-
Notifications
You must be signed in to change notification settings - Fork 915
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
createHttpTransportForSolanaRpc
function
- Loading branch information
1 parent
26af60a
commit e5a3363
Showing
5 changed files
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@solana/rpc-transport-http': patch | ||
--- | ||
|
||
Add new `createHttpTransportForSolanaRpc` function that creates a new HTTP transport specific to the Solana RPC API. This transport uses custom JSON parsing and stringifying strategies on both the request and response of Solana RPC API requests in order to prevents loss of precision for large integers. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
packages/rpc-transport-http/src/__tests__/http-transport-for-solana-rpc-test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { RpcTransport } from '@solana/rpc-spec'; | ||
|
||
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); | ||
const MAX_SAFE_INTEGER_PLUS_ONE = BigInt(Number.MAX_SAFE_INTEGER) + 1n; | ||
|
||
describe('createHttpTransportForSolanaRpc', () => { | ||
let fetchSpy: jest.SpyInstance; | ||
let makeHttpRequest: RpcTransport; | ||
beforeEach(async () => { | ||
await jest.isolateModulesAsync(async () => { | ||
fetchSpy = jest.spyOn(globalThis, 'fetch'); | ||
const { createHttpTransportForSolanaRpc } = | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
await import('../http-transport-for-solana-rpc'); | ||
makeHttpRequest = createHttpTransportForSolanaRpc({ url: 'http://localhost' }); | ||
}); | ||
}); | ||
describe('when the request is from the Solana RPC API', () => { | ||
it('passes all bigints as large numerical values in the request body', async () => { | ||
expect.assertions(1); | ||
fetchSpy.mockResolvedValue({ ok: true, text: () => `{"ok":true}` }); | ||
await makeHttpRequest({ | ||
methodName: 'getBalance', | ||
params: { | ||
numbersInString: 'He said: "1, 2, 3, Soleil!"', | ||
safeNumber: MAX_SAFE_INTEGER, | ||
unsafeNumber: MAX_SAFE_INTEGER_PLUS_ONE, | ||
}, | ||
}); | ||
expect(fetchSpy).toHaveBeenCalledWith( | ||
expect.anything(), | ||
expect.objectContaining({ | ||
body: expect.stringContaining( | ||
`"params":{` + | ||
`"numbersInString":"He said: \\"1, 2, 3, Soleil!\\"",` + | ||
`"safeNumber":${MAX_SAFE_INTEGER},` + | ||
`"unsafeNumber":${MAX_SAFE_INTEGER_PLUS_ONE}}`, | ||
), | ||
}), | ||
); | ||
}); | ||
it('gets all integers as bigints within the response', async () => { | ||
expect.assertions(1); | ||
fetchSpy.mockResolvedValue({ | ||
ok: true, | ||
text: () => | ||
`{"safeNumber": ${MAX_SAFE_INTEGER}, ` + | ||
`"unsafeNumber": ${MAX_SAFE_INTEGER_PLUS_ONE}, ` + | ||
`"numbersInString": "He said: \\"1, 2, 3, Soleil!\\""}`, | ||
}); | ||
const requestPromise = makeHttpRequest({ methodName: 'getBalance', params: ['1234..5678'] }); | ||
await expect(requestPromise).resolves.toStrictEqual({ | ||
numbersInString: 'He said: "1, 2, 3, Soleil!"', | ||
safeNumber: MAX_SAFE_INTEGER, | ||
unsafeNumber: MAX_SAFE_INTEGER_PLUS_ONE, | ||
}); | ||
}); | ||
}); | ||
describe('when the request is not from the Solana RPC API', () => { | ||
it('fails to stringify bigints in requests', async () => { | ||
expect.assertions(1); | ||
const promise = makeHttpRequest({ | ||
methodName: 'getAssetsByOwner', | ||
params: [MAX_SAFE_INTEGER_PLUS_ONE], | ||
}); | ||
await expect(promise).rejects.toThrow(new TypeError('Do not know how to serialize a BigInt')); | ||
}); | ||
it('downcasts bigints to numbers in responses', async () => { | ||
expect.assertions(1); | ||
fetchSpy.mockResolvedValue({ | ||
ok: true, | ||
text: () => | ||
`{"safeNumber": ${MAX_SAFE_INTEGER}, ` + | ||
`"unsafeNumber": ${MAX_SAFE_INTEGER_PLUS_ONE}, ` + | ||
`"numbersInString": "He said: \\"1, 2, 3, Soleil!\\""}`, | ||
}); | ||
const requestPromise = makeHttpRequest({ methodName: 'getAssetsByOwner', params: ['1234..5678'] }); | ||
await expect(requestPromise).resolves.toStrictEqual({ | ||
numbersInString: 'He said: "1, 2, 3, Soleil!"', | ||
safeNumber: Number(MAX_SAFE_INTEGER), | ||
unsafeNumber: Number(MAX_SAFE_INTEGER_PLUS_ONE), | ||
}); | ||
}); | ||
}); | ||
}); |
24 changes: 24 additions & 0 deletions
24
packages/rpc-transport-http/src/http-transport-for-solana-rpc.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { RpcRequest, RpcTransport } from '@solana/rpc-spec'; | ||
import type Dispatcher from 'undici-types/dispatcher'; | ||
|
||
import { createHttpTransport } from './http-transport'; | ||
import { AllowedHttpRequestHeaders } from './http-transport-headers'; | ||
import { isSolanaRequest } from './is-solana-request'; | ||
import { parseJsonWithBigInts } from './parse-json-with-bigints'; | ||
import { stringifyJsonWithBigints } from './stringify-json-with-bigints'; | ||
|
||
type Config = Readonly<{ | ||
dispatcher_NODE_ONLY?: Dispatcher; | ||
headers?: AllowedHttpRequestHeaders; | ||
url: string; | ||
}>; | ||
|
||
export function createHttpTransportForSolanaRpc(config: Config): RpcTransport { | ||
return createHttpTransport({ | ||
...config, | ||
fromJson: (rawResponse: string, request: RpcRequest) => | ||
isSolanaRequest(request) ? parseJsonWithBigInts(rawResponse) : JSON.parse(rawResponse), | ||
toJson: (payload: unknown, request: RpcRequest) => | ||
isSolanaRequest(request) ? stringifyJsonWithBigints(payload) : JSON.stringify(payload), | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './http-transport'; | ||
export * from './http-transport-for-solana-rpc'; |