From 18f33e228b776700d3156466a72166905bb7bbd0 Mon Sep 17 00:00:00 2001 From: Tolgahan Date: Mon, 15 Jun 2026 19:39:24 +0300 Subject: [PATCH 1/4] Update WaaS sandbox client --- API.md | 4 +- README.md | 2 + src/clients/walletClient.ts | 24 +++--- src/generated/waas.gen.ts | 141 +++++++++++++++++++++++++------ src/omsEnvironment.ts | 2 +- src/signedFetch.ts | 2 +- src/utils/requestUtils.ts | 2 +- tests/credentialSigner.test.ts | 6 +- tests/oidcRedirectAuth.test.ts | 2 +- tests/walletSigning.test.ts | 6 +- tests/walletTransactions.test.ts | 42 +++++++++ 11 files changed, 184 insertions(+), 49 deletions(-) diff --git a/API.md b/API.md index 382be2d..2cd857d 100644 --- a/API.md +++ b/API.md @@ -96,7 +96,7 @@ new OMSClient(params: { | Name | Type | Required | Description | |---|---|---|---| -| `publishableKey` | `string` | Yes | Your OMS publishable key. | +| `publishableKey` | `string` | Yes | Your OMS publishable key. Wallet requests send this as the WaaS `Api-Key` header. | | `projectId` | `string` | Yes | Your OMS project ID. Used as the WaaS signing scope for wallet requests and OIDC redirect state. | | `environment` | `OmsEnvironment` | No | API endpoint configuration. Defaults to the SDK's configured OMS endpoints. | | `storage` | `StorageManager` | No | Storage backend for wallet metadata. Defaults to `LocalStorageManager` when browser `localStorage` is available, otherwise `MemoryStorageManager`. | @@ -887,7 +887,7 @@ interface OmsEnvironment { | `indexerUrlTemplate` | `string` | URL template for the Indexer API. `{value}` is replaced with the selected network name, e.g. `"https://indexer.example.com/{value}"`. | | `auth.oidcProviders` | `Record` | OIDC provider configurations addressable by provider key. | -The default is exported as `defaultOmsEnvironment` and includes the `google` OIDC provider. +The default is exported as `defaultOmsEnvironment`, uses `https://sandbox-api.dev.polygon-dev.technology` as the WaaS API base URL, and includes the `google` OIDC provider. Use `defineOmsEnvironment` to preserve typed custom OIDC provider keys: diff --git a/README.md b/README.md index 8807bdb..7ea10e9 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ const oms = new OMSClient({ }) ``` +By default, wallet requests use the sandbox WaaS API at `https://sandbox-api.dev.polygon-dev.technology`. + In Vite browser apps, keep those values in local environment variables: ```typescript diff --git a/src/clients/walletClient.ts b/src/clients/walletClient.ts index 18ec12a..da00f5e 100644 --- a/src/clients/walletClient.ts +++ b/src/clients/walletClient.ts @@ -27,8 +27,8 @@ import { import {OmsSessionError, OmsTransactionError, OmsWalletSelectionError, toOmsSdkError} from "../errors.js"; import { - Wallet as Walletclient, - WalletPublic as WalletPublicclient, + Waas as WaasClient, + WaasPublic as WaasPublicClient, WalletType, TransactionMode, TransactionStatus, @@ -331,8 +331,8 @@ class PendingWalletSelectionImpl implements PendingWalletSelection { } export class WalletClient { - private readonly client: Walletclient - private readonly publicClient: WalletPublicclient + private readonly client: WaasClient + private readonly publicClient: WaasPublicClient private readonly storage: StorageManager private readonly redirectAuthStorage?: StorageManager private readonly credentialSigner: CredentialSigner @@ -407,10 +407,10 @@ export class WalletClient { } const signedFetch = createSignedFetch(params.publishableKey, this.credentialSigner, this.projectId) - this.client = new Walletclient(params.environment.walletApiUrl, signedFetch) - this.publicClient = new WalletPublicclient( + this.client = new WaasClient(params.environment.walletApiUrl, signedFetch) + this.publicClient = new WaasPublicClient( params.environment.walletApiUrl, - createAccessKeyFetch(params.publishableKey), + createApiKeyFetch(params.publishableKey), ) this.indexerClient = new IndexerClient({ publishableKey: params.publishableKey, @@ -1620,7 +1620,11 @@ export class WalletClient { do { lastStatus = await this.client.transactionStatus({txnId: txnId} as TransactionStatusRequest) completedPolls += 1 - if (lastStatus.status === TransactionStatus.Executed || lastStatus.txnHash) { + if ( + lastStatus.status === TransactionStatus.Executed || + lastStatus.status === TransactionStatus.Failed || + lastStatus.txnHash + ) { return lastStatus } const pollDelayMs = this.transactionStatusPollDelayMs(completedPolls, options) @@ -1854,12 +1858,12 @@ function defaultRedirectAuthStorage(): StorageManager | undefined { : undefined } -function createAccessKeyFetch(publishableKey: string): Fetch { +function createApiKeyFetch(publishableKey: string): Fetch { return async (input: RequestInfo, init?: RequestInit): Promise => { const existingHeaders = (init?.headers ?? {}) as Record const headers: Record = { ...existingHeaders, - 'X-Access-Key': publishableKey, + 'Api-Key': publishableKey, } return globalThis.fetch(input, {...init, headers}) diff --git a/src/generated/waas.gen.ts b/src/generated/waas.gen.ts index 7bd46ad..6d30c21 100644 --- a/src/generated/waas.gen.ts +++ b/src/generated/waas.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// waas v0.1.0 c7e8530922bd8ff83b9338cd080808244d972a3a +// waas v1-26.5.25-cc1eed0 f038c851ab6eacfa03422c6c4ed3c069c28e78ea // -- // Code generated by Webrpc-gen@v0.37.2 with typescript generator. DO NOT EDIT. // @@ -9,16 +9,16 @@ export const WebrpcVersion = "v1" // Schema version of your RIDL schema -export const WebrpcSchemaVersion = "v0.1.0" +export const WebrpcSchemaVersion = "v1-26.5.25-cc1eed0" // Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = "c7e8530922bd8ff83b9338cd080808244d972a3a" +export const WebrpcSchemaHash = "f038c851ab6eacfa03422c6c4ed3c069c28e78ea" // // Client interface // -export interface WalletClient { +export interface WaasClient { commitVerifier(req: CommitVerifierRequest, headers?: object, signal?: AbortSignal): Promise completeAuth(req: CompleteAuthRequest, headers?: object, signal?: AbortSignal): Promise @@ -47,7 +47,11 @@ export interface WalletClient { getIDToken(req: GetIDTokenRequest, headers?: object, signal?: AbortSignal): Promise } -export interface WalletPublicClient { +export interface WaasPublicClient { + status(headers?: object, signal?: AbortSignal): Promise + + getJWKS(headers?: object, signal?: AbortSignal): Promise + isValidMessageSignature(req: IsValidMessageSignatureRequest, headers?: object, signal?: AbortSignal): Promise isValidTypedDataSignature(req: IsValidTypedDataSignatureRequest, headers?: object, signal?: AbortSignal): Promise @@ -99,7 +103,8 @@ export enum SigningAlgorithm { export enum TransactionStatus { Quoted = 'quoted', Pending = 'pending', - Executed = 'executed' + Executed = 'executed', + Failed = 'failed' } export interface Identity { @@ -337,6 +342,17 @@ export interface GetIDTokenResponse { idToken: string } +export interface JWK { + alg: string + crv: string + kid: string + kty: string + use: string + x: string + y: string +} + + @@ -351,6 +367,21 @@ export interface GetIDTokenResponse { +export interface StatusRequest { +} + +export interface StatusResponse { + ver: string + env: string + pcr0: string +} + +export interface GetJWKSRequest { +} + +export interface GetJWKSResponse { + keys: Array +} @@ -360,10 +391,10 @@ export interface GetIDTokenResponse { // Client // -export class Wallet implements WalletClient { +export class Waas implements WaasClient { protected hostname: string protected fetch: Fetch - protected path = '/rpc/Wallet/' + protected path = '/v1/Waas/' constructor(hostname: string, fetch: Fetch) { this.hostname = hostname.replace(/\/*$/, '') @@ -375,20 +406,20 @@ export class Wallet implements WalletClient { } queryKey = { - commitVerifier: (req: CommitVerifierRequest) => ['Wallet', 'commitVerifier', req] as const, - completeAuth: (req: CompleteAuthRequest) => ['Wallet', 'completeAuth', req] as const, - createWallet: (req: CreateWalletRequest) => ['Wallet', 'createWallet', req] as const, - useWallet: (req: UseWalletRequest) => ['Wallet', 'useWallet', req] as const, - signMessage: (req: SignMessageRequest) => ['Wallet', 'signMessage', req] as const, - signTypedData: (req: SignTypedDataRequest) => ['Wallet', 'signTypedData', req] as const, - prepareEthereumTransaction: (req: PrepareEthereumTransactionRequest) => ['Wallet', 'prepareEthereumTransaction', req] as const, - prepareEthereumContractCall: (req: PrepareEthereumContractCallRequest) => ['Wallet', 'prepareEthereumContractCall', req] as const, - execute: (req: ExecuteRequest) => ['Wallet', 'execute', req] as const, - transactionStatus: (req: TransactionStatusRequest) => ['Wallet', 'transactionStatus', req] as const, - listAccess: (req: ListAccessRequest) => ['Wallet', 'listAccess', req] as const, - revokeAccess: (req: RevokeAccessRequest) => ['Wallet', 'revokeAccess', req] as const, - listWallets: (req: ListWalletsRequest) => ['Wallet', 'listWallets', req] as const, - getIDToken: (req: GetIDTokenRequest) => ['Wallet', 'getIDToken', req] as const, + commitVerifier: (req: CommitVerifierRequest) => ['Waas', 'commitVerifier', req] as const, + completeAuth: (req: CompleteAuthRequest) => ['Waas', 'completeAuth', req] as const, + createWallet: (req: CreateWalletRequest) => ['Waas', 'createWallet', req] as const, + useWallet: (req: UseWalletRequest) => ['Waas', 'useWallet', req] as const, + signMessage: (req: SignMessageRequest) => ['Waas', 'signMessage', req] as const, + signTypedData: (req: SignTypedDataRequest) => ['Waas', 'signTypedData', req] as const, + prepareEthereumTransaction: (req: PrepareEthereumTransactionRequest) => ['Waas', 'prepareEthereumTransaction', req] as const, + prepareEthereumContractCall: (req: PrepareEthereumContractCallRequest) => ['Waas', 'prepareEthereumContractCall', req] as const, + execute: (req: ExecuteRequest) => ['Waas', 'execute', req] as const, + transactionStatus: (req: TransactionStatusRequest) => ['Waas', 'transactionStatus', req] as const, + listAccess: (req: ListAccessRequest) => ['Waas', 'listAccess', req] as const, + revokeAccess: (req: RevokeAccessRequest) => ['Waas', 'revokeAccess', req] as const, + listWallets: (req: ListWalletsRequest) => ['Waas', 'listWallets', req] as const, + getIDToken: (req: GetIDTokenRequest) => ['Waas', 'getIDToken', req] as const, } commitVerifier = (req: CommitVerifierRequest, headers?: object, signal?: AbortSignal): Promise => { @@ -560,10 +591,10 @@ export class Wallet implements WalletClient { } } -export class WalletPublic implements WalletPublicClient { +export class WaasPublic implements WaasPublicClient { protected hostname: string protected fetch: Fetch - protected path = '/rpc/WalletPublic/' + protected path = '/v1/WaasPublic/' constructor(hostname: string, fetch: Fetch) { this.hostname = hostname.replace(/\/*$/, '') @@ -575,8 +606,34 @@ export class WalletPublic implements WalletPublicClient { } queryKey = { - isValidMessageSignature: (req: IsValidMessageSignatureRequest) => ['WalletPublic', 'isValidMessageSignature', req] as const, - isValidTypedDataSignature: (req: IsValidTypedDataSignatureRequest) => ['WalletPublic', 'isValidTypedDataSignature', req] as const, + status: () => ['WaasPublic', 'status'] as const, + getJWKS: () => ['WaasPublic', 'getJWKS'] as const, + isValidMessageSignature: (req: IsValidMessageSignatureRequest) => ['WaasPublic', 'isValidMessageSignature', req] as const, + isValidTypedDataSignature: (req: IsValidTypedDataSignatureRequest) => ['WaasPublic', 'isValidTypedDataSignature', req] as const, + } + + status = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('Status'), + createHttpRequest('{}', headers, signal)).then((res) => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'StatusResponse') + }) + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + }) + } + + getJWKS = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('GetJWKS'), + createHttpRequest('{}', headers, signal)).then((res) => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetJWKSResponse') + }) + }, (error) => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + }) } isValidMessageSignature = (req: IsValidMessageSignatureRequest, headers?: object, signal?: AbortSignal): Promise => { @@ -1066,6 +1123,18 @@ export class AuthMethodUnavailableError extends WebrpcError { } } +export class OTPRateLimitedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'OTPRateLimited' + this.code = typeof error.code === 'number' ? error.code : 7010 + this.message = error.message || `Too many OTP requests, try again later` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, OTPRateLimitedError.prototype) + } +} + export class WalletNotFoundError extends WebrpcError { constructor(error: WebrpcErrorParams = {}) { super(error) @@ -1198,6 +1267,18 @@ export class InvalidFeeOptionError extends WebrpcError { } } +export class TransactionBroadcastFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'TransactionBroadcastFailed' + this.code = typeof error.code === 'number' ? error.code : 7311 + this.message = error.message || `Transaction broadcast attempt failed; poll TransactionStatus to determine on-chain outcome` + this.status = typeof error.status === 'number' ? error.status : 502 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, TransactionBroadcastFailedError.prototype) + } +} + export enum errors { WebrpcEndpoint = 'WebrpcEndpoint', @@ -1232,6 +1313,7 @@ export enum errors { OAuthError = 'OAuthError', AccessError = 'AccessError', AuthMethodUnavailable = 'AuthMethodUnavailable', + OTPRateLimited = 'OTPRateLimited', WalletNotFound = 'WalletNotFound', AccountNotFound = 'AccountNotFound', AccountAlreadyExists = 'AccountAlreadyExists', @@ -1243,6 +1325,7 @@ export enum errors { TransactionNotFound = 'TransactionNotFound', TransactionExpired = 'TransactionExpired', InvalidFeeOption = 'InvalidFeeOption', + TransactionBroadcastFailed = 'TransactionBroadcastFailed', } export enum WebrpcErrorCodes { @@ -1278,6 +1361,7 @@ export enum WebrpcErrorCodes { OAuthError = 7006, AccessError = 7007, AuthMethodUnavailable = 7009, + OTPRateLimited = 7010, WalletNotFound = 7300, AccountNotFound = 7301, AccountAlreadyExists = 7302, @@ -1289,6 +1373,7 @@ export enum WebrpcErrorCodes { TransactionNotFound = 7308, TransactionExpired = 7309, InvalidFeeOption = 7310, + TransactionBroadcastFailed = 7311, } export const webrpcErrorByCode: { [code: number]: any } = { @@ -1324,6 +1409,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { [7006]: OAuthErrorError, [7007]: AccessErrorError, [7009]: AuthMethodUnavailableError, + [7010]: OTPRateLimitedError, [7300]: WalletNotFoundError, [7301]: AccountNotFoundError, [7302]: AccountAlreadyExistsError, @@ -1335,6 +1421,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { [7308]: TransactionNotFoundError, [7309]: TransactionExpiredError, [7310]: InvalidFeeOptionError, + [7311]: TransactionBroadcastFailedError, } @@ -1345,7 +1432,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { export const WebrpcHeader = "Webrpc" -export const WebrpcHeaderValue = "webrpc@v0.37.2;gen-typescript@v0.26.0;waas@v0.1.0" +export const WebrpcHeaderValue = "webrpc@v0.37.2;gen-typescript@v0.26.0;waas@v1-26.5.25-cc1eed0" type WebrpcGenVersions = { WebrpcGenVersion: string; diff --git a/src/omsEnvironment.ts b/src/omsEnvironment.ts index 85eaff2..b1b641c 100644 --- a/src/omsEnvironment.ts +++ b/src/omsEnvironment.ts @@ -24,7 +24,7 @@ export interface OmsEnvironment< } export const defaultOmsEnvironment = { - walletApiUrl: "https://d26giflyqapd29.cloudfront.net", + walletApiUrl: "https://sandbox-api.dev.polygon-dev.technology", indexerUrlTemplate: "https://dev-{value}-indexer.sequence.app/rpc/Indexer/", auth: { oidcProviders: { diff --git a/src/signedFetch.ts b/src/signedFetch.ts index 3ad4df0..a956047 100644 --- a/src/signedFetch.ts +++ b/src/signedFetch.ts @@ -32,7 +32,7 @@ export function createSignedFetch( const existingHeaders = (init?.headers ?? {}) as Record const headers: Record = { ...existingHeaders, - 'X-Access-Key': publishableKey, + 'Api-Key': publishableKey, 'OMS-Wallet-Signature': signatureHeader, } diff --git a/src/utils/requestUtils.ts b/src/utils/requestUtils.ts index 117c095..f788904 100644 --- a/src/utils/requestUtils.ts +++ b/src/utils/requestUtils.ts @@ -7,7 +7,7 @@ export class RequestUtils { scope: string, payload: string, ): string { - return `POST /rpc/Wallet${endpoint}\nnonce: ${nonce}\nscope: ${scope}\n\n${payload}`; + return `POST /v1/Waas${endpoint}\nnonce: ${nonce}\nscope: ${scope}\n\n${payload}`; } static buildWalletSignatureHeader( diff --git a/tests/credentialSigner.test.ts b/tests/credentialSigner.test.ts index de097f7..cb40c08 100644 --- a/tests/credentialSigner.test.ts +++ b/tests/credentialSigner.test.ts @@ -34,13 +34,13 @@ describe("wallet request signing", () => { vi.stubGlobal("fetch", fetchMock); const signedFetch = createSignedFetch("publishable-key", signer, "project-id"); - await signedFetch("https://wallet.example/rpc/Wallet/CommitVerifier", { + await signedFetch("https://wallet.example/v1/Waas/CommitVerifier", { method: "POST", body, }); expect(signer.preimages).toEqual([ - "POST /rpc/Wallet/CommitVerifier\n" + + "POST /v1/Waas/CommitVerifier\n" + "nonce: 42\n" + "scope: project-id\n\n" + body, @@ -48,7 +48,7 @@ describe("wallet request signing", () => { const headers = fetchMock.mock.calls[0][1]?.headers as Record; expect(headers).toMatchObject({ - "X-Access-Key": "publishable-key", + "Api-Key": "publishable-key", "OMS-Wallet-Signature": `alg="ecdsa-p256-sha256", scope="project-id", cred="0x04${"11".repeat(64)}", nonce=42, sig="0x${"22".repeat(64)}"`, }); }); diff --git a/tests/oidcRedirectAuth.test.ts b/tests/oidcRedirectAuth.test.ts index 3a13f69..66ad33b 100644 --- a/tests/oidcRedirectAuth.test.ts +++ b/tests/oidcRedirectAuth.test.ts @@ -51,7 +51,7 @@ describe("WalletClient OIDC redirect auth", () => { const url = input.toString(); const body = JSON.parse(init?.body as string); - expect(url).toBe("https://wallet.example/rpc/Wallet/CommitVerifier"); + expect(url).toBe("https://wallet.example/v1/Waas/CommitVerifier"); expect(body).toMatchObject({ identityType: "oidc", authMode: "auth-code-pkce", diff --git a/tests/walletSigning.test.ts b/tests/walletSigning.test.ts index 431c44b..c6bc1a5 100644 --- a/tests/walletSigning.test.ts +++ b/tests/walletSigning.test.ts @@ -37,7 +37,7 @@ describe("WalletClient signing", () => { const body = JSON.parse(init?.body as string); const headers = init?.headers as Record; - expect(headers["X-Access-Key"]).toBe("publishable-key"); + expect(headers["Api-Key"]).toBe("publishable-key"); expect(headers["OMS-Wallet-Signature"]).toContain('alg="ecdsa-p256-sha256"'); expect(headers.Authorization).toBeUndefined(); @@ -95,7 +95,7 @@ describe("WalletClient signing", () => { const url = input.toString(); const body = JSON.parse(init?.body as string); - expect((init?.headers as Record)["X-Access-Key"]).toBe("publishable-key"); + expect((init?.headers as Record)["Api-Key"]).toBe("publishable-key"); const headers = init?.headers as Record; expect(headers["OMS-Wallet-Signature"]).toContain('alg="ecdsa-p256-sha256"'); expect(headers.Authorization).toBeUndefined(); @@ -136,7 +136,7 @@ describe("WalletClient signing", () => { const body = JSON.parse(init?.body as string); const headers = init?.headers as Record; - expect(headers["X-Access-Key"]).toBe("publishable-key"); + expect(headers["Api-Key"]).toBe("publishable-key"); expect(headers["OMS-Wallet-Signature"]).toBeUndefined(); expect(headers.Authorization).toBeUndefined(); diff --git a/tests/walletTransactions.test.ts b/tests/walletTransactions.test.ts index 0926e06..7a83aae 100644 --- a/tests/walletTransactions.test.ts +++ b/tests/walletTransactions.test.ts @@ -524,6 +524,48 @@ describe("WalletClient transactions", () => { }); }); + it("returns failed transaction status as terminal", async () => { + const fetchMock = vi.fn(async (input: RequestInfo | URL) => { + const url = input.toString(); + + if (url.endsWith("/PrepareEthereumTransaction")) { + return jsonResponse({ + txnId: "txn-failed", + status: "quoted", + feeOptions: [], + sponsored: true, + expiresAt: "2099-01-01T00:00:00Z", + }); + } + + if (url.endsWith("/Execute")) { + return jsonResponse({status: "pending"}); + } + + if (url.endsWith("/TransactionStatus")) { + return jsonResponse({status: "failed"}); + } + + throw new Error(`Unexpected request: ${url}`); + }); + vi.stubGlobal("fetch", fetchMock); + + const wallet = createWalletWithSession( + new MemoryStorageManager(), + "0x9999999999999999999999999999999999999999", + ); + + await expect(wallet.sendTransaction({ + network: Networks.polygon, + to: "0x1111111111111111111111111111111111111111", + value: 0n, + })).resolves.toEqual({ + txnId: "txn-failed", + status: TransactionStatus.Failed, + }); + expect(fetchMock.mock.calls.filter(([input]) => input.toString().endsWith("/TransactionStatus"))).toHaveLength(1); + }); + it("exposes transaction status lookup by transaction id", async () => { const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => { const url = input.toString(); From b46d7e5a508fd8c3344685769bcf66bdf3bebc10 Mon Sep 17 00:00:00 2001 From: Tolgahan Date: Mon, 15 Jun 2026 19:52:52 +0300 Subject: [PATCH 2/4] Require separate indexer API key --- API.md | 3 ++ README.md | 13 +++++-- .../node-contract-deploy-example/.env.example | 1 + .../node-contract-deploy-example/README.md | 2 +- .../deployErc20.ts | 2 ++ examples/node/README.md | 2 +- examples/node/signInFlow.ts | 2 ++ examples/react/.env.example | 1 + examples/react/README.md | 4 +-- examples/react/src/config.ts | 1 + examples/react/src/omsClient.ts | 3 +- examples/react/src/vite-env.d.ts | 1 + examples/trails-actions/.env.example | 5 +-- examples/trails-actions/README.md | 2 +- examples/trails-actions/src/config.ts | 1 + examples/trails-actions/src/omsClient.ts | 3 +- examples/trails-actions/src/vite-env.d.ts | 1 + examples/wagmi/.env.example | 1 + examples/wagmi/README.md | 2 +- examples/wagmi/src/config.ts | 1 + examples/wagmi/src/omsClient.ts | 3 +- examples/wagmi/src/vite-env.d.ts | 1 + packages/oms-wallet-wagmi-connector/README.md | 1 + src/clients/indexerClient.ts | 8 ++--- src/clients/walletClient.ts | 3 +- src/omsClient.ts | 4 ++- tests/errorContracts.test.ts | 1 + tests/indexerClient.test.ts | 6 ++-- tests/networks.test.ts | 1 + tests/oidcRedirectAuth.test.ts | 2 ++ tests/walletAccess.test.ts | 1 + tests/walletErrors.test.ts | 3 ++ tests/walletSession.test.ts | 34 +++++++++++++++++++ tests/walletSigning.test.ts | 1 + tests/walletTransactions.test.ts | 2 ++ type-tests/oidcProviderTypes.ts | 26 ++++++++++---- 36 files changed, 118 insertions(+), 30 deletions(-) diff --git a/API.md b/API.md index 2cd857d..1802266 100644 --- a/API.md +++ b/API.md @@ -75,6 +75,7 @@ import { OMSClient } from '@0xsequence/typescript-sdk' const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) ``` @@ -84,6 +85,7 @@ const oms = new OMSClient({ ```typescript new OMSClient(params: { publishableKey: string + indexerApiKey: string projectId: string environment?: OmsEnvironment storage?: StorageManager @@ -97,6 +99,7 @@ new OMSClient(params: { | Name | Type | Required | Description | |---|---|---|---| | `publishableKey` | `string` | Yes | Your OMS publishable key. Wallet requests send this as the WaaS `Api-Key` header. | +| `indexerApiKey` | `string` | Yes | Your Indexer API key. Indexer requests send this as the `X-Access-Key` header. | | `projectId` | `string` | Yes | Your OMS project ID. Used as the WaaS signing scope for wallet requests and OIDC redirect state. | | `environment` | `OmsEnvironment` | No | API endpoint configuration. Defaults to the SDK's configured OMS endpoints. | | `storage` | `StorageManager` | No | Storage backend for wallet metadata. Defaults to `LocalStorageManager` when browser `localStorage` is available, otherwise `MemoryStorageManager`. | diff --git a/README.md b/README.md index 7ea10e9..2925159 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ import { OMSClient } from '@0xsequence/typescript-sdk' const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) ``` @@ -42,6 +43,7 @@ function requiredEnv(name: string, value: string | undefined): string { const oms = new OMSClient({ publishableKey: requiredEnv('VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY), + indexerApiKey: requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY), projectId: requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID), }) ``` @@ -69,7 +71,7 @@ To run it locally from the repository root: ```bash cp examples/react/.env.example examples/react/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID in examples/react/.env.local +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID in examples/react/.env.local pnpm dev:example ``` @@ -95,7 +97,7 @@ To run it locally from the repository root: ```bash cp examples/wagmi/.env.example examples/wagmi/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID in examples/wagmi/.env.local +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID in examples/wagmi/.env.local pnpm dev:wagmi-example ``` @@ -109,7 +111,7 @@ To run it locally from the repository root: ```bash cp examples/trails-actions/.env.example examples/trails-actions/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID in examples/trails-actions/.env.local +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID in examples/trails-actions/.env.local pnpm dev:trails-actions-example ``` @@ -121,6 +123,7 @@ import { parseUnits } from 'viem' const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) @@ -188,6 +191,7 @@ Google redirect auth is configured on the default environment. The redirect auth ```typescript const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) ``` @@ -243,6 +247,7 @@ The SDK makes expired sessions inactive before protected wallet operations and t ```typescript const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) @@ -423,6 +428,7 @@ const tx = await oms.wallet.sendTransaction({ ```typescript const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', environment: { walletApiUrl: 'https://staging-wallet.example.com', @@ -442,6 +448,7 @@ import { MemoryStorageManager, OMSClient } from '@0xsequence/typescript-sdk' const oms = new OMSClient({ publishableKey: 'your-publishable-key', + indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', storage: new MemoryStorageManager(), }) diff --git a/examples/node-contract-deploy-example/.env.example b/examples/node-contract-deploy-example/.env.example index 08c0ba0..2680918 100644 --- a/examples/node-contract-deploy-example/.env.example +++ b/examples/node-contract-deploy-example/.env.example @@ -1,3 +1,4 @@ OMS_PUBLISHABLE_KEY=your-publishable-key +OMS_INDEXER_API_KEY=your-indexer-api-key OMS_PROJECT_ID=your-oms-project-id # DEPLOY_SALT=0x0000000000000000000000000000000000000000000000000000000000000001 diff --git a/examples/node-contract-deploy-example/README.md b/examples/node-contract-deploy-example/README.md index fd5a793..b23154f 100644 --- a/examples/node-contract-deploy-example/README.md +++ b/examples/node-contract-deploy-example/README.md @@ -32,7 +32,7 @@ From the repository root: pnpm install pnpm build cp examples/node-contract-deploy-example/.env.example examples/node-contract-deploy-example/.env.local -# Fill OMS_PUBLISHABLE_KEY and OMS_PROJECT_ID in .env.local +# Fill OMS_PUBLISHABLE_KEY, OMS_INDEXER_API_KEY, and OMS_PROJECT_ID in .env.local pnpm dev:node-contract-deploy-example ``` diff --git a/examples/node-contract-deploy-example/deployErc20.ts b/examples/node-contract-deploy-example/deployErc20.ts index d71f84b..2386238 100644 --- a/examples/node-contract-deploy-example/deployErc20.ts +++ b/examples/node-contract-deploy-example/deployErc20.ts @@ -14,6 +14,7 @@ loadDotenv({path: join(exampleDir, ".env.local"), quiet: true}); loadDotenv({path: join(exampleDir, ".env"), quiet: true}); const publishableKey = requiredEnv("OMS_PUBLISHABLE_KEY", process.env.OMS_PUBLISHABLE_KEY); +const indexerApiKey = requiredEnv("OMS_INDEXER_API_KEY", process.env.OMS_INDEXER_API_KEY); const projectId = requiredEnv("OMS_PROJECT_ID", process.env.OMS_PROJECT_ID); const defaultDeployerAddress = "0xce0042B868300000d44A59004Da54A005ffdcf9f" as const satisfies Address; const deployerAddress = optionalAddress("DEPLOYER_ADDRESS", process.env.DEPLOYER_ADDRESS) ?? defaultDeployerAddress; @@ -45,6 +46,7 @@ async function main() { const client = new OMSClient({ publishableKey, + indexerApiKey, projectId, storage: new MemoryStorageManager(), }); diff --git a/examples/node/README.md b/examples/node/README.md index 0c6474a..f95c086 100644 --- a/examples/node/README.md +++ b/examples/node/README.md @@ -11,7 +11,7 @@ Run it from the repository root: ```bash pnpm install pnpm build -OMS_PUBLISHABLE_KEY=your-publishable-key OMS_PROJECT_ID=your-project-id pnpm dev:node-example +OMS_PUBLISHABLE_KEY=your-publishable-key OMS_INDEXER_API_KEY=your-indexer-api-key OMS_PROJECT_ID=your-project-id pnpm dev:node-example ``` The example prompts for an email address, sends an OTP code, then prompts for the code. diff --git a/examples/node/signInFlow.ts b/examples/node/signInFlow.ts index 14bfd81..395a46b 100644 --- a/examples/node/signInFlow.ts +++ b/examples/node/signInFlow.ts @@ -2,6 +2,7 @@ import readline from "node:readline/promises"; import {MemoryStorageManager, Networks, OMSClient} from "@0xsequence/typescript-sdk"; const publishableKey = requiredEnv("OMS_PUBLISHABLE_KEY", process.env.OMS_PUBLISHABLE_KEY); +const indexerApiKey = requiredEnv("OMS_INDEXER_API_KEY", process.env.OMS_INDEXER_API_KEY); const projectId = requiredEnv("OMS_PROJECT_ID", process.env.OMS_PROJECT_ID); async function main() { @@ -18,6 +19,7 @@ async function main() { const client = new OMSClient({ publishableKey, + indexerApiKey, projectId, storage: new MemoryStorageManager(), }); diff --git a/examples/react/.env.example b/examples/react/.env.example index 6316db9..8289e05 100644 --- a/examples/react/.env.example +++ b/examples/react/.env.example @@ -1,2 +1,3 @@ VITE_OMS_PUBLISHABLE_KEY=your-publishable-key +VITE_OMS_INDEXER_API_KEY=your-indexer-api-key VITE_OMS_PROJECT_ID=your-oms-project-id diff --git a/examples/react/README.md b/examples/react/README.md index aea350f..6cd9f30 100644 --- a/examples/react/README.md +++ b/examples/react/README.md @@ -12,7 +12,7 @@ Run it from the repository root: pnpm install pnpm build cp examples/react/.env.example examples/react/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID pnpm dev:example ``` @@ -24,7 +24,7 @@ The example requires a publishable key and project ID. Configure them locally be ```bash cp examples/react/.env.example examples/react/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID ``` The Amoy-only "ERC20 example" panel includes a WalletKit Dollar example using diff --git a/examples/react/src/config.ts b/examples/react/src/config.ts index 503b55b..973a717 100644 --- a/examples/react/src/config.ts +++ b/examples/react/src/config.ts @@ -2,6 +2,7 @@ export const PUBLISHABLE_KEY = requiredEnv( 'VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY, ) +export const INDEXER_API_KEY = requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY) export const PROJECT_ID = requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID) function requiredEnv(name: string, value: string | undefined): string { diff --git a/examples/react/src/omsClient.ts b/examples/react/src/omsClient.ts index 2b5c90d..ca2cec0 100644 --- a/examples/react/src/omsClient.ts +++ b/examples/react/src/omsClient.ts @@ -1,9 +1,10 @@ import { OMSClient } from '@0xsequence/typescript-sdk' -import { PROJECT_ID, PUBLISHABLE_KEY } from './config' +import { INDEXER_API_KEY, PROJECT_ID, PUBLISHABLE_KEY } from './config' export const TEST_SESSION_LIFETIME_SECONDS = 604_800 export const oms = new OMSClient({ publishableKey: PUBLISHABLE_KEY, + indexerApiKey: INDEXER_API_KEY, projectId: PROJECT_ID, }) diff --git a/examples/react/src/vite-env.d.ts b/examples/react/src/vite-env.d.ts index 37fee95..80d9569 100644 --- a/examples/react/src/vite-env.d.ts +++ b/examples/react/src/vite-env.d.ts @@ -2,6 +2,7 @@ interface ImportMetaEnv { readonly VITE_OMS_PUBLISHABLE_KEY?: string + readonly VITE_OMS_INDEXER_API_KEY?: string readonly VITE_OMS_PROJECT_ID?: string } diff --git a/examples/trails-actions/.env.example b/examples/trails-actions/.env.example index 83de793..8289e05 100644 --- a/examples/trails-actions/.env.example +++ b/examples/trails-actions/.env.example @@ -1,2 +1,3 @@ -VITE_OMS_PUBLISHABLE_KEY= -VITE_OMS_PROJECT_ID= +VITE_OMS_PUBLISHABLE_KEY=your-publishable-key +VITE_OMS_INDEXER_API_KEY=your-indexer-api-key +VITE_OMS_PROJECT_ID=your-oms-project-id diff --git a/examples/trails-actions/README.md b/examples/trails-actions/README.md index d096842..99812a5 100644 --- a/examples/trails-actions/README.md +++ b/examples/trails-actions/README.md @@ -12,7 +12,7 @@ Run it from the repository root: pnpm install pnpm build cp examples/trails-actions/.env.example examples/trails-actions/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID pnpm dev:trails-actions-example ``` diff --git a/examples/trails-actions/src/config.ts b/examples/trails-actions/src/config.ts index 09ca60c..444d19f 100644 --- a/examples/trails-actions/src/config.ts +++ b/examples/trails-actions/src/config.ts @@ -1,4 +1,5 @@ export const PUBLISHABLE_KEY = requiredEnv('VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY) +export const INDEXER_API_KEY = requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY) export const PROJECT_ID = requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID) function requiredEnv(name: string, value: string | undefined): string { diff --git a/examples/trails-actions/src/omsClient.ts b/examples/trails-actions/src/omsClient.ts index 2b5c90d..ca2cec0 100644 --- a/examples/trails-actions/src/omsClient.ts +++ b/examples/trails-actions/src/omsClient.ts @@ -1,9 +1,10 @@ import { OMSClient } from '@0xsequence/typescript-sdk' -import { PROJECT_ID, PUBLISHABLE_KEY } from './config' +import { INDEXER_API_KEY, PROJECT_ID, PUBLISHABLE_KEY } from './config' export const TEST_SESSION_LIFETIME_SECONDS = 604_800 export const oms = new OMSClient({ publishableKey: PUBLISHABLE_KEY, + indexerApiKey: INDEXER_API_KEY, projectId: PROJECT_ID, }) diff --git a/examples/trails-actions/src/vite-env.d.ts b/examples/trails-actions/src/vite-env.d.ts index 37fee95..80d9569 100644 --- a/examples/trails-actions/src/vite-env.d.ts +++ b/examples/trails-actions/src/vite-env.d.ts @@ -2,6 +2,7 @@ interface ImportMetaEnv { readonly VITE_OMS_PUBLISHABLE_KEY?: string + readonly VITE_OMS_INDEXER_API_KEY?: string readonly VITE_OMS_PROJECT_ID?: string } diff --git a/examples/wagmi/.env.example b/examples/wagmi/.env.example index 9d0ccee..9347af6 100644 --- a/examples/wagmi/.env.example +++ b/examples/wagmi/.env.example @@ -1,3 +1,4 @@ VITE_OMS_PUBLISHABLE_KEY=your-publishable-key +VITE_OMS_INDEXER_API_KEY=your-indexer-api-key VITE_OMS_PROJECT_ID=your-oms-project-id VITE_TRAILS_API_KEY=AQAAAAAAAMDoWz-avqIIjXGH7JJlBSormpo diff --git a/examples/wagmi/README.md b/examples/wagmi/README.md index acccdb2..814bdf0 100644 --- a/examples/wagmi/README.md +++ b/examples/wagmi/README.md @@ -8,7 +8,7 @@ Run it from the repository root: pnpm install pnpm build cp examples/wagmi/.env.example examples/wagmi/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID pnpm dev:wagmi-example ``` diff --git a/examples/wagmi/src/config.ts b/examples/wagmi/src/config.ts index 5f26fec..9e9907a 100644 --- a/examples/wagmi/src/config.ts +++ b/examples/wagmi/src/config.ts @@ -2,6 +2,7 @@ export const PUBLISHABLE_KEY = requiredEnv( 'VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY, ) +export const INDEXER_API_KEY = requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY) export const PROJECT_ID = requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID) export const TRAILS_API_KEY = import.meta.env.VITE_TRAILS_API_KEY diff --git a/examples/wagmi/src/omsClient.ts b/examples/wagmi/src/omsClient.ts index 2b5c90d..ca2cec0 100644 --- a/examples/wagmi/src/omsClient.ts +++ b/examples/wagmi/src/omsClient.ts @@ -1,9 +1,10 @@ import { OMSClient } from '@0xsequence/typescript-sdk' -import { PROJECT_ID, PUBLISHABLE_KEY } from './config' +import { INDEXER_API_KEY, PROJECT_ID, PUBLISHABLE_KEY } from './config' export const TEST_SESSION_LIFETIME_SECONDS = 604_800 export const oms = new OMSClient({ publishableKey: PUBLISHABLE_KEY, + indexerApiKey: INDEXER_API_KEY, projectId: PROJECT_ID, }) diff --git a/examples/wagmi/src/vite-env.d.ts b/examples/wagmi/src/vite-env.d.ts index fd73e38..2225909 100644 --- a/examples/wagmi/src/vite-env.d.ts +++ b/examples/wagmi/src/vite-env.d.ts @@ -2,6 +2,7 @@ interface ImportMetaEnv { readonly VITE_OMS_PUBLISHABLE_KEY?: string + readonly VITE_OMS_INDEXER_API_KEY?: string readonly VITE_OMS_PROJECT_ID?: string readonly VITE_TRAILS_API_KEY?: string } diff --git a/packages/oms-wallet-wagmi-connector/README.md b/packages/oms-wallet-wagmi-connector/README.md index 73e22c1..81382fb 100644 --- a/packages/oms-wallet-wagmi-connector/README.md +++ b/packages/oms-wallet-wagmi-connector/README.md @@ -12,6 +12,7 @@ import { omsWalletConnector } from '@0xsequence/oms-wallet-wagmi-connector' const oms = new OMSClient({ publishableKey: import.meta.env.VITE_OMS_PUBLISHABLE_KEY, + indexerApiKey: import.meta.env.VITE_OMS_INDEXER_API_KEY, projectId: import.meta.env.VITE_OMS_PROJECT_ID, }) diff --git a/src/clients/indexerClient.ts b/src/clients/indexerClient.ts index 3f7fb96..c9c7633 100644 --- a/src/clients/indexerClient.ts +++ b/src/clients/indexerClient.ts @@ -155,15 +155,15 @@ export interface OmsEnvironment { } export class IndexerClient { - private readonly publishableKey: string; + private readonly indexerApiKey: string; private readonly environment: OmsEnvironment; private readonly client: HttpClient; constructor(params: { - publishableKey: string, + indexerApiKey: string, environment: OmsEnvironment }) { - this.publishableKey = params.publishableKey; + this.indexerApiKey = params.indexerApiKey; this.environment = params.environment; this.client = new HttpClient(); } @@ -293,7 +293,7 @@ export class IndexerClient { private defaultHeaders(): Record { return { - "X-Access-Key": this.publishableKey, + "X-Access-Key": this.indexerApiKey, Accept: "application/json", }; } diff --git a/src/clients/walletClient.ts b/src/clients/walletClient.ts index da00f5e..5bbf6de 100644 --- a/src/clients/walletClient.ts +++ b/src/clients/walletClient.ts @@ -360,6 +360,7 @@ export class WalletClient { constructor(params: { publishableKey: string, + indexerApiKey: string, projectId: string, environment: Env, storage?: StorageManager @@ -413,7 +414,7 @@ export class WalletClient { createApiKeyFetch(params.publishableKey), ) this.indexerClient = new IndexerClient({ - publishableKey: params.publishableKey, + indexerApiKey: params.indexerApiKey, environment: params.environment, }) } diff --git a/src/omsClient.ts b/src/omsClient.ts index 6aa124c..844a624 100644 --- a/src/omsClient.ts +++ b/src/omsClient.ts @@ -7,6 +7,7 @@ import {supportedNetworks} from "./networks.js"; interface OMSClientBaseParams { publishableKey: string; + indexerApiKey: string; projectId: string; storage?: StorageManager; redirectAuthStorage?: StorageManager; @@ -29,6 +30,7 @@ class OMSClientImpl { this.wallet = new WalletClient({ publishableKey: params.publishableKey, + indexerApiKey: params.indexerApiKey, projectId: params.projectId, environment, storage, @@ -37,7 +39,7 @@ class OMSClientImpl { }); this.indexer = new IndexerClient({ - publishableKey: params.publishableKey, + indexerApiKey: params.indexerApiKey, environment }); } diff --git a/tests/errorContracts.test.ts b/tests/errorContracts.test.ts index 3f73afc..6227e8a 100644 --- a/tests/errorContracts.test.ts +++ b/tests/errorContracts.test.ts @@ -1573,6 +1573,7 @@ function createOmsClient(params: { } = {}): OMSClient { const clientParams: ConstructorParameters[0] = { publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", storage: new MemoryStorageManager(), credentialSigner: params.credentialSigner ?? new MockSigner(), diff --git a/tests/indexerClient.test.ts b/tests/indexerClient.test.ts index ebde7e2..7323437 100644 --- a/tests/indexerClient.test.ts +++ b/tests/indexerClient.test.ts @@ -31,7 +31,7 @@ describe("IndexerClient", () => { vi.stubGlobal("fetch", fetchMock); const indexer = new IndexerClient({ - publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", environment: testEnvironment(), }); @@ -68,7 +68,7 @@ describe("IndexerClient", () => { vi.stubGlobal("fetch", fetchMock); const indexer = new IndexerClient({ - publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", environment: testEnvironment(), }); @@ -89,7 +89,7 @@ describe("IndexerClient", () => { vi.stubGlobal("fetch", vi.fn(async () => new Response("Bad Gateway", {status: 502}))); const indexer = new IndexerClient({ - publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", environment: testEnvironment(), }); diff --git a/tests/networks.test.ts b/tests/networks.test.ts index a7ffbc5..7101f50 100644 --- a/tests/networks.test.ts +++ b/tests/networks.test.ts @@ -51,6 +51,7 @@ describe("Networks", () => { it("is available from OMSClient", () => { const oms = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", }); diff --git a/tests/oidcRedirectAuth.test.ts b/tests/oidcRedirectAuth.test.ts index 66ad33b..92dbb91 100644 --- a/tests/oidcRedirectAuth.test.ts +++ b/tests/oidcRedirectAuth.test.ts @@ -142,6 +142,7 @@ describe("WalletClient OIDC redirect auth", () => { const storage = new MemoryStorageManager(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -810,6 +811,7 @@ function createWalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: params.projectId ?? "project-id", environment, storage: new MemoryStorageManager(), diff --git a/tests/walletAccess.test.ts b/tests/walletAccess.test.ts index b204715..a861576 100644 --- a/tests/walletAccess.test.ts +++ b/tests/walletAccess.test.ts @@ -94,6 +94,7 @@ describe("WalletClient access management", () => { function createWalletWithSession(): WalletClient { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), diff --git a/tests/walletErrors.test.ts b/tests/walletErrors.test.ts index 2b85563..ae04d66 100644 --- a/tests/walletErrors.test.ts +++ b/tests/walletErrors.test.ts @@ -33,6 +33,7 @@ describe("WalletClient errors", () => { it("wraps local validation failures separately from request failures", async () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -54,6 +55,7 @@ describe("WalletClient errors", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -86,6 +88,7 @@ describe("WalletClient errors", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), diff --git a/tests/walletSession.test.ts b/tests/walletSession.test.ts index a3ce14c..032cd29 100644 --- a/tests/walletSession.test.ts +++ b/tests/walletSession.test.ts @@ -52,6 +52,7 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), credentialSigner: new MockSigner(), @@ -70,6 +71,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -94,6 +96,7 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -153,6 +156,7 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -199,6 +203,7 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -235,6 +240,7 @@ describe("WalletClient session storage", () => { const nextOnSessionExpired = vi.fn(); const nextClient = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -269,6 +275,7 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -297,6 +304,7 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -328,6 +336,7 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -377,6 +386,7 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -406,6 +416,7 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -478,6 +489,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -522,6 +534,7 @@ describe("WalletClient session storage", () => { redirectAuthStorage.set(Constants.redirectAuthStorageKey, JSON.stringify({verifier: "old-verifier"})); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -582,6 +595,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -611,6 +625,7 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -667,6 +682,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -726,6 +742,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -760,6 +777,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -827,6 +845,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -888,6 +907,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -943,6 +963,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1002,6 +1023,7 @@ describe("WalletClient session storage", () => { const storage = new MemoryStorageManager(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -1070,6 +1092,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1112,6 +1135,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1167,6 +1191,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1219,6 +1244,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1264,6 +1290,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1315,6 +1342,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1366,6 +1394,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1417,6 +1446,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1448,6 +1478,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1489,6 +1520,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -1541,6 +1573,7 @@ describe("WalletClient session storage", () => { const storage = new MemoryStorageManager(); const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -1586,6 +1619,7 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), diff --git a/tests/walletSigning.test.ts b/tests/walletSigning.test.ts index c6bc1a5..c524008 100644 --- a/tests/walletSigning.test.ts +++ b/tests/walletSigning.test.ts @@ -184,6 +184,7 @@ describe("WalletClient signing", () => { function createWalletWithSession(walletAddress: string): WalletClient { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), diff --git a/tests/walletTransactions.test.ts b/tests/walletTransactions.test.ts index 7a83aae..75863e9 100644 --- a/tests/walletTransactions.test.ts +++ b/tests/walletTransactions.test.ts @@ -585,6 +585,7 @@ describe("WalletClient transactions", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -644,6 +645,7 @@ describe("WalletClient transactions", () => { function createWalletWithSession(storage: MemoryStorageManager, walletAddress: string): WalletClient { const wallet = new WalletClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, diff --git a/type-tests/oidcProviderTypes.ts b/type-tests/oidcProviderTypes.ts index 6d2e6f2..1df13fa 100644 --- a/type-tests/oidcProviderTypes.ts +++ b/type-tests/oidcProviderTypes.ts @@ -71,20 +71,28 @@ if (false) { const defaultClient = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", }); // @ts-expect-error publishableKey is required. -new OMSClient({projectId: "project-id"}); +new OMSClient({indexerApiKey: "indexer-api-key", projectId: "project-id"}); +// @ts-expect-error indexerApiKey is required. +new OMSClient({publishableKey: "publishable-key", projectId: "project-id"}); // @ts-expect-error projectId is required. -new OMSClient({publishableKey: "publishable-key"}); +new OMSClient({publishableKey: "publishable-key", indexerApiKey: "indexer-api-key"}); // @ts-expect-error old projectAccessKey initializer name is not supported. -new OMSClient({projectAccessKey: "publishable-key", projectId: "project-id"}); +new OMSClient({projectAccessKey: "publishable-key", indexerApiKey: "indexer-api-key", projectId: "project-id"}); // @ts-expect-error old publicApiKey initializer name is not supported. -new OMSClient({publicApiKey: "publishable-key", projectId: "project-id"}); +new OMSClient({publicApiKey: "publishable-key", indexerApiKey: "indexer-api-key", projectId: "project-id"}); // @ts-expect-error old authorizationScope initializer name is not supported. -new OMSClient({publishableKey: "publishable-key", authorizationScope: "project-id"}); -// @ts-expect-error session expiry is subscribed through wallet.onSessionExpired, not constructor params. -new OMSClient({publishableKey: "publishable-key", projectId: "project-id", onSessionExpired: () => {}}); +new OMSClient({publishableKey: "publishable-key", indexerApiKey: "indexer-api-key", authorizationScope: "project-id"}); +new OMSClient({ + publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", + projectId: "project-id", + // @ts-expect-error session expiry is subscribed through wallet.onSessionExpired, not constructor params. + onSessionExpired: () => {}, +}); const session: OMSClientSessionState = defaultClient.wallet.session; const unsubscribeSessionExpired: () => void = defaultClient.wallet.onSessionExpired(({session}) => { void session.sessionEmail; @@ -194,6 +202,7 @@ const customEnvironmentWithoutProviders = defineOmsEnvironment({ }); const customClient = new OMSClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: customEnvironmentWithoutProviders, }); @@ -208,6 +217,7 @@ void customClient.wallet.startOidcRedirectAuth({ function createClient(params: { publishableKey: string; + indexerApiKey: string; projectId: string; environment?: OmsEnvironment; }) { @@ -216,10 +226,12 @@ function createClient(params: { void createClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", }); void createClient({ publishableKey: "publishable-key", + indexerApiKey: "indexer-api-key", projectId: "project-id", environment: customEnvironmentWithoutProviders, }); From a2a14c4cb5e01605c3c232a7f2d751ec2b4b91b5 Mon Sep 17 00:00:00 2001 From: Tolgahan Date: Thu, 18 Jun 2026 14:05:04 +0300 Subject: [PATCH 3/4] Use IndexerGateway for balances and history --- API.md | 117 +++-- README.md | 36 +- docs/error-contracts.md | 4 +- .../node-contract-deploy-example/.env.example | 1 - .../node-contract-deploy-example/README.md | 2 +- .../deployErc20.ts | 2 - examples/node/README.md | 2 +- examples/node/signInFlow.ts | 2 - examples/react/.env.example | 1 - examples/react/README.md | 4 +- examples/react/src/config.ts | 1 - examples/react/src/omsClient.ts | 3 +- examples/react/src/vite-env.d.ts | 1 - examples/trails-actions/.env.example | 1 - examples/trails-actions/README.md | 2 +- examples/trails-actions/src/config.ts | 1 - examples/trails-actions/src/omsClient.ts | 3 +- examples/trails-actions/src/trailsActions.ts | 24 +- examples/trails-actions/src/vite-env.d.ts | 1 - examples/wagmi/.env.example | 1 - examples/wagmi/README.md | 2 +- examples/wagmi/src/config.ts | 1 - examples/wagmi/src/omsClient.ts | 3 +- examples/wagmi/src/vite-env.d.ts | 1 - packages/oms-wallet-wagmi-connector/README.md | 1 - src/clients/indexerClient.ts | 407 ++++++++++++++---- src/clients/walletClient.ts | 69 ++- src/index.ts | 11 +- src/omsClient.ts | 4 +- src/omsEnvironment.ts | 4 +- src/operations.ts | 4 +- tests/errorContracts.test.ts | 65 ++- tests/indexerClient.test.ts | 236 ++++++++-- tests/networks.test.ts | 1 - tests/oidcRedirectAuth.test.ts | 10 +- tests/walletAccess.test.ts | 3 +- tests/walletErrors.test.ts | 5 +- tests/walletSession.test.ts | 36 +- tests/walletSigning.test.ts | 3 +- tests/walletTransactions.test.ts | 94 ++-- type-tests/oidcProviderTypes.ts | 80 ++-- 41 files changed, 817 insertions(+), 432 deletions(-) diff --git a/API.md b/API.md index 1802266..fbac41f 100644 --- a/API.md +++ b/API.md @@ -33,8 +33,8 @@ - [listAccessPages](#listaccesspages) - [revokeAccess](#revokeaccess) - [IndexerClient](#indexerclient) - - [getTokenBalances](#gettokenbalances) - - [getNativeTokenBalance](#getnativetokenbalance) + - [getBalances](#getbalances) + - [getTransactionHistory](#gettransactionhistory) - [Errors](#errors) - [Types](#types) - [Network](#network) @@ -55,7 +55,8 @@ - [SendTransactionResponse](#sendtransactionresponse) - [TransactionStatusPollingOptions](#transactionstatuspollingoptions) - [FeeOptionSelector](#feeoptionselector) - - [TokenBalancesResult](#tokenbalancesresult) + - [BalancesResult](#balancesresult) + - [TransactionHistoryResult](#transactionhistoryresult) - [TokenBalancesPage](#tokenbalancespage) - [TokenBalance](#tokenbalance) - [TokenContractInfo](#tokencontractinfo) @@ -75,7 +76,6 @@ import { OMSClient } from '@0xsequence/typescript-sdk' const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) ``` @@ -85,7 +85,6 @@ const oms = new OMSClient({ ```typescript new OMSClient(params: { publishableKey: string - indexerApiKey: string projectId: string environment?: OmsEnvironment storage?: StorageManager @@ -98,8 +97,7 @@ new OMSClient(params: { | Name | Type | Required | Description | |---|---|---|---| -| `publishableKey` | `string` | Yes | Your OMS publishable key. Wallet requests send this as the WaaS `Api-Key` header. | -| `indexerApiKey` | `string` | Yes | Your Indexer API key. Indexer requests send this as the `X-Access-Key` header. | +| `publishableKey` | `string` | Yes | Your OMS publishable key. Wallet and IndexerGateway requests send this as the `Api-Key` header. | | `projectId` | `string` | Yes | Your OMS project ID. Used as the WaaS signing scope for wallet requests and OIDC redirect state. | | `environment` | `OmsEnvironment` | No | API endpoint configuration. Defaults to the SDK's configured OMS endpoints. | | `storage` | `StorageManager` | No | Storage backend for wallet metadata. Defaults to `LocalStorageManager` when browser `localStorage` is available, otherwise `MemoryStorageManager`. | @@ -703,36 +701,39 @@ if (other) { ## IndexerClient -Accessed via `oms.indexer`. Queries on-chain token balances through the OMS Indexer API. +Accessed via `oms.indexer`. Queries on-chain balances and transaction history through IndexerGateway. -### getTokenBalances +### getBalances ```typescript -getTokenBalances(params: { - network: Network - contractAddress?: string +getBalances(params: { walletAddress: string - includeMetadata: boolean - page?: { - page?: number - pageSize?: number - } -}): Promise + networks?: Network[] + networkType?: 'MAINNETS' | 'TESTNETS' | 'ALL' + contractAddresses?: string[] + includeMetadata?: boolean + omitPrices?: boolean + tokenIds?: string[] + page?: TokenBalancesPage +}): Promise ``` -Fetches token balances for a wallet on a given network. Omit `contractAddress` to query balances across contracts; provide it to filter to one token contract. The default request returns page `0` with up to `40` entries. When `includeMetadata` is `true`, token display data is returned on `contractInfo` and `tokenMetadata`; ERC-20 decimals are available as `contractInfo.decimals`. +Fetches native and token balances for a wallet. Pass `networks` to query explicit SDK network objects. If `networks` is omitted, the request defaults to `networkType: 'MAINNETS'`. The default request returns page `0` with up to `40` entries. `includeMetadata` defaults to `true`; token display data is returned on `contractInfo` and `tokenMetadata`, and ERC-20 decimals are available as `contractInfo.decimals`. **Parameters** | Name | Type | Description | |---|---|---| -| `network` | `Network` | The network to query. Use an exported registry value such as `Networks.polygon`. | -| `contractAddress` | `string` | Optional token contract filter. Omit to query balances across contracts. | | `walletAddress` | `string` | The wallet address whose balances to fetch. Use `oms.wallet.walletAddress` after checking it is defined. | -| `includeMetadata` | `boolean` | When `true`, the response includes token metadata such as name, symbol, and decimals. | -| `page` | `{ page?: number; pageSize?: number }` | Optional pagination request. Defaults to `{ page: 0, pageSize: 40 }`. | +| `networks` | `Network[]` | Optional explicit networks to query. Use exported registry values such as `Networks.polygon`. | +| `networkType` | `'MAINNETS' \| 'TESTNETS' \| 'ALL'` | Optional gateway network group when `networks` is omitted. Defaults to `'MAINNETS'`. | +| `contractAddresses` | `string[]` | Optional token contract filter. Omit to query balances across contracts. | +| `includeMetadata` | `boolean` | Optional metadata flag. Defaults to `true`. | +| `omitPrices` | `boolean` | Optional price exclusion flag. | +| `tokenIds` | `string[]` | Optional token ID filter. | +| `page` | `TokenBalancesPage` | Optional pagination request. Defaults to `{ page: 0, pageSize: 40 }`. | -**Returns** `Promise` — see [TokenBalancesResult](#tokenbalancesresult). +**Returns** `Promise` — see [BalancesResult](#balancesresult). **Example** @@ -740,12 +741,16 @@ Fetches token balances for a wallet on a given network. Omit `contractAddress` t const { walletAddress } = oms.wallet if (!walletAddress) throw new Error('No active wallet session') -const result = await oms.indexer.getTokenBalances({ - network: Networks.polygon, +const result = await oms.indexer.getBalances({ + networks: [Networks.polygon], walletAddress, includeMetadata: true, }) +for (const b of result.nativeBalances) { + console.log(b.symbol, b.balance) +} + for (const b of result.balances) { console.log(b.contractAddress, b.balance, b.tokenId) } @@ -753,16 +758,26 @@ for (const b of result.balances) { --- -### getNativeTokenBalance +### getTransactionHistory ```typescript -getNativeTokenBalance(params: { - network: Network +getTransactionHistory(params: { walletAddress: string -}): Promise + networks?: Network[] + networkType?: 'MAINNETS' | 'TESTNETS' | 'ALL' + contractAddresses?: string[] + transactionHashes?: string[] + metaTransactionIds?: string[] + fromBlock?: number + toBlock?: number + tokenId?: string + includeMetadata?: boolean + omitPrices?: boolean + page?: TokenBalancesPage +}): Promise ``` -Fetches the native token balance for a wallet. This is also used internally to enrich transaction fee options. +Fetches mined transaction history for a wallet. Pass `networks` to query explicit SDK network objects. If `networks` is omitted, the request defaults to `networkType: 'MAINNETS'`. `includeMetadata` defaults to `true`. --- @@ -877,7 +892,7 @@ findNetworkByName(name: string): Network | undefined ```typescript interface OmsEnvironment { walletApiUrl: string - indexerUrlTemplate: string + indexerGatewayUrl: string auth?: { oidcProviders?: Record } @@ -887,7 +902,7 @@ interface OmsEnvironment { | Field | Type | Description | |---|---|---| | `walletApiUrl` | `string` | Base URL of the WaaS Wallet RPC host. | -| `indexerUrlTemplate` | `string` | URL template for the Indexer API. `{value}` is replaced with the selected network name, e.g. `"https://indexer.example.com/{value}"`. | +| `indexerGatewayUrl` | `string` | Base URL of the IndexerGateway host, e.g. `"https://api.example.com/v1/IndexerGateway/"`. | | `auth.oidcProviders` | `Record` | OIDC provider configurations addressable by provider key. | The default is exported as `defaultOmsEnvironment`, uses `https://sandbox-api.dev.polygon-dev.technology` as the WaaS API base URL, and includes the `google` OIDC provider. @@ -1185,12 +1200,13 @@ that fee option. --- -### TokenBalancesResult +### BalancesResult ```typescript -interface TokenBalancesResult { +interface BalancesResult { status: number page?: TokenBalancesPage + nativeBalances: TokenBalance[] balances: TokenBalance[] } ``` @@ -1199,25 +1215,48 @@ interface TokenBalancesResult { |---|---|---| | `status` | `number` | HTTP status code of the indexer response. | | `page` | `TokenBalancesPage` | Pagination metadata, if present. | +| `nativeBalances` | `TokenBalance[]` | Native token balances for the requested address. | | `balances` | `TokenBalance[]` | Array of token balance entries for the requested address. | --- +### TransactionHistoryResult + +```typescript +interface TransactionHistoryResult { + status: number + page?: TokenBalancesPage + transactions: Transaction[] +} +``` + +| Field | Type | Description | +|---|---|---| +| `status` | `number` | HTTP status code of the indexer response. | +| `page` | `TokenBalancesPage` | Pagination metadata, if present. | +| `transactions` | `Transaction[]` | Flattened transaction entries across the requested networks. | + +--- + ### TokenBalancesPage ```typescript interface TokenBalancesPage { - page: number - pageSize: number - more: boolean + page?: number + pageSize?: number + more?: boolean + before?: unknown + after?: unknown } ``` | Field | Type | Description | |---|---|---| | `page` | `number` | Current page index (zero-based). | -| `pageSize` | `number` | Number of entries per page (up to 40). | +| `pageSize` | `number` | Number of entries per page. | | `more` | `boolean` | `true` if additional pages are available. | +| `before` | `unknown` | Gateway cursor before the current page, when returned. | +| `after` | `unknown` | Gateway cursor after the current page, when returned. | --- diff --git a/README.md b/README.md index 2925159..696f96c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ import { OMSClient } from '@0xsequence/typescript-sdk' const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) ``` @@ -43,7 +42,6 @@ function requiredEnv(name: string, value: string | undefined): string { const oms = new OMSClient({ publishableKey: requiredEnv('VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY), - indexerApiKey: requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY), projectId: requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID), }) ``` @@ -71,7 +69,7 @@ To run it locally from the repository root: ```bash cp examples/react/.env.example examples/react/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID in examples/react/.env.local +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID in examples/react/.env.local pnpm dev:example ``` @@ -97,7 +95,7 @@ To run it locally from the repository root: ```bash cp examples/wagmi/.env.example examples/wagmi/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID in examples/wagmi/.env.local +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID in examples/wagmi/.env.local pnpm dev:wagmi-example ``` @@ -111,7 +109,7 @@ To run it locally from the repository root: ```bash cp examples/trails-actions/.env.example examples/trails-actions/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID in examples/trails-actions/.env.local +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID in examples/trails-actions/.env.local pnpm dev:trails-actions-example ``` @@ -123,7 +121,6 @@ import { parseUnits } from 'viem' const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) @@ -191,7 +188,6 @@ Google redirect auth is configured on the default environment. The redirect auth ```typescript const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) ``` @@ -247,7 +243,6 @@ The SDK makes expired sessions inactive before protected wallet operations and t ```typescript const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', }) @@ -428,16 +423,15 @@ const tx = await oms.wallet.sendTransaction({ ```typescript const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', environment: { walletApiUrl: 'https://staging-wallet.example.com', - indexerUrlTemplate: 'https://staging-indexer.example.com/{value}', + indexerGatewayUrl: 'https://staging-indexer.example.com/v1/IndexerGateway/', }, }) ``` -For indexer requests, `{value}` is replaced with the selected `Network.name`, such as `polygon` or `amoy`. +For indexer requests, `indexerGatewayUrl` points at the IndexerGateway base URL. ### Custom Storage and Signing @@ -448,7 +442,6 @@ import { MemoryStorageManager, OMSClient } from '@0xsequence/typescript-sdk' const oms = new OMSClient({ publishableKey: 'your-publishable-key', - indexerApiKey: 'your-indexer-api-key', projectId: 'your-project-id', storage: new MemoryStorageManager(), }) @@ -506,31 +499,28 @@ const tx = await oms.wallet.callContract({ }) ``` -### Query Token and Native Balances +### Query Balances ```typescript const { walletAddress } = oms.wallet if (!walletAddress) throw new Error('No active wallet session') -const result = await oms.indexer.getTokenBalances({ - network: Networks.polygon, +const result = await oms.indexer.getBalances({ + networks: [Networks.polygon], walletAddress, includeMetadata: true, }) +for (const b of result.nativeBalances) { + console.log(b.symbol, b.balance) +} + for (const b of result.balances) { console.log(b.contractInfo?.symbol, b.balance, b.contractInfo?.decimals) } - -const nativeBalance = await oms.indexer.getNativeTokenBalance({ - network: Networks.polygon, - walletAddress, -}) - -console.log(nativeBalance?.balance) ``` -Pass `contractAddress` to filter balances to one token contract. With `includeMetadata: true`, ERC-20 decimals are available as `contractInfo.decimals`. The response is paginated; pass `page` when requesting later pages. +Pass `contractAddresses` to filter balances to specific token contracts. Omit `networks` to query mainnets by default, or pass `networkType: 'TESTNETS'` / `'ALL'`. With `includeMetadata: true`, ERC-20 decimals are available as `contractInfo.decimals`. The response is paginated; pass `page` when requesting later pages. ### Manage Access diff --git a/docs/error-contracts.md b/docs/error-contracts.md index b47ac67..67e4579 100644 --- a/docs/error-contracts.md +++ b/docs/error-contracts.md @@ -33,8 +33,8 @@ supports, whether `upstreamError` should be present, and which test owns the con | `oms.wallet.sendTransaction`, `callContract` | Submitted transaction status polling fails | `OmsTransactionError`, `OMS_TRANSACTION_STATUS_LOOKUP_FAILED`, `retryable: true`, `txnId` | Retry status lookup, not the original write | Present when polling crossed transport/upstream boundary | `snapshots transaction status polling failures with txn and upstream details`; `snapshots transaction status polling backend errors as retryable` | | `oms.wallet.getTransactionStatus` | Direct status lookup backend failure | `OmsRequestError` or `OmsResponseError` with status operation | Retry status lookup or surface backend status to the user | Present | `snapshots direct transaction status backend errors with upstream details` | | `oms.wallet.listAccess`, `listAccessPages`, `revokeAccess` | WaaS access backend failure | `OmsRequestError` or `OmsResponseError` with access operation | Retry based on SDK code/status; log upstream detail | Present | `snapshots access backend errors with upstream details` | -| `oms.indexer.getTokenBalances`, `getNativeTokenBalance` | Indexer backend, transport, malformed JSON, or malformed payload | `OmsRequestError` or `OmsResponseError` with indexer operation | Retry based on SDK code/status; log upstream detail | Present for remote/transport response failures | `snapshots indexer backend errors with upstream details`; `snapshots native balance indexer errors with upstream details`; `snapshots indexer transport failures with upstream details`; `snapshots indexer malformed response errors with upstream details` | -| `oms.indexer.getTokenBalances`, `getNativeTokenBalance` | Indexer non-JSON HTTP body | `OmsRequestError`, `OMS_HTTP_ERROR`, sanitized message | Do not expose raw upstream HTML/text bodies; log normalized detail | Present, sanitized | `snapshots indexer non-JSON HTTP errors without raw upstream bodies` | +| `oms.indexer.getBalances`, `getTransactionHistory` | Indexer backend, transport, malformed JSON, or malformed payload | `OmsRequestError` or `OmsResponseError` with indexer operation | Retry based on SDK code/status; log upstream detail | Present for remote/transport response failures | `snapshots indexer backend errors with upstream details`; `snapshots transaction history indexer errors with upstream details`; `snapshots indexer transport failures with upstream details`; `snapshots indexer malformed response errors with upstream details` | +| `oms.indexer.getBalances`, `getTransactionHistory` | Indexer non-JSON HTTP body | `OmsRequestError`, `OMS_HTTP_ERROR`, sanitized message | Do not expose raw upstream HTML/text bodies; log normalized detail | Present, sanitized | `snapshots indexer non-JSON HTTP errors without raw upstream bodies` | | Exported storage managers and credential signers | Local runtime capability or storage failure | Native `Error` or signer/storage-specific runtime error | Fix local runtime support or storage availability | Absent | `snapshots exported storage and signer runtime errors` | | Exported `OmsSdkError` classes and `isOmsSdkError` | Error class/helper field contract | Stable public fields on constructed errors | Use only when the error class/helper is the unit under test | As constructed | `snapshots exported error helper and subclass fields` | diff --git a/examples/node-contract-deploy-example/.env.example b/examples/node-contract-deploy-example/.env.example index 2680918..08c0ba0 100644 --- a/examples/node-contract-deploy-example/.env.example +++ b/examples/node-contract-deploy-example/.env.example @@ -1,4 +1,3 @@ OMS_PUBLISHABLE_KEY=your-publishable-key -OMS_INDEXER_API_KEY=your-indexer-api-key OMS_PROJECT_ID=your-oms-project-id # DEPLOY_SALT=0x0000000000000000000000000000000000000000000000000000000000000001 diff --git a/examples/node-contract-deploy-example/README.md b/examples/node-contract-deploy-example/README.md index b23154f..fd5a793 100644 --- a/examples/node-contract-deploy-example/README.md +++ b/examples/node-contract-deploy-example/README.md @@ -32,7 +32,7 @@ From the repository root: pnpm install pnpm build cp examples/node-contract-deploy-example/.env.example examples/node-contract-deploy-example/.env.local -# Fill OMS_PUBLISHABLE_KEY, OMS_INDEXER_API_KEY, and OMS_PROJECT_ID in .env.local +# Fill OMS_PUBLISHABLE_KEY and OMS_PROJECT_ID in .env.local pnpm dev:node-contract-deploy-example ``` diff --git a/examples/node-contract-deploy-example/deployErc20.ts b/examples/node-contract-deploy-example/deployErc20.ts index 2386238..d71f84b 100644 --- a/examples/node-contract-deploy-example/deployErc20.ts +++ b/examples/node-contract-deploy-example/deployErc20.ts @@ -14,7 +14,6 @@ loadDotenv({path: join(exampleDir, ".env.local"), quiet: true}); loadDotenv({path: join(exampleDir, ".env"), quiet: true}); const publishableKey = requiredEnv("OMS_PUBLISHABLE_KEY", process.env.OMS_PUBLISHABLE_KEY); -const indexerApiKey = requiredEnv("OMS_INDEXER_API_KEY", process.env.OMS_INDEXER_API_KEY); const projectId = requiredEnv("OMS_PROJECT_ID", process.env.OMS_PROJECT_ID); const defaultDeployerAddress = "0xce0042B868300000d44A59004Da54A005ffdcf9f" as const satisfies Address; const deployerAddress = optionalAddress("DEPLOYER_ADDRESS", process.env.DEPLOYER_ADDRESS) ?? defaultDeployerAddress; @@ -46,7 +45,6 @@ async function main() { const client = new OMSClient({ publishableKey, - indexerApiKey, projectId, storage: new MemoryStorageManager(), }); diff --git a/examples/node/README.md b/examples/node/README.md index f95c086..0c6474a 100644 --- a/examples/node/README.md +++ b/examples/node/README.md @@ -11,7 +11,7 @@ Run it from the repository root: ```bash pnpm install pnpm build -OMS_PUBLISHABLE_KEY=your-publishable-key OMS_INDEXER_API_KEY=your-indexer-api-key OMS_PROJECT_ID=your-project-id pnpm dev:node-example +OMS_PUBLISHABLE_KEY=your-publishable-key OMS_PROJECT_ID=your-project-id pnpm dev:node-example ``` The example prompts for an email address, sends an OTP code, then prompts for the code. diff --git a/examples/node/signInFlow.ts b/examples/node/signInFlow.ts index 395a46b..14bfd81 100644 --- a/examples/node/signInFlow.ts +++ b/examples/node/signInFlow.ts @@ -2,7 +2,6 @@ import readline from "node:readline/promises"; import {MemoryStorageManager, Networks, OMSClient} from "@0xsequence/typescript-sdk"; const publishableKey = requiredEnv("OMS_PUBLISHABLE_KEY", process.env.OMS_PUBLISHABLE_KEY); -const indexerApiKey = requiredEnv("OMS_INDEXER_API_KEY", process.env.OMS_INDEXER_API_KEY); const projectId = requiredEnv("OMS_PROJECT_ID", process.env.OMS_PROJECT_ID); async function main() { @@ -19,7 +18,6 @@ async function main() { const client = new OMSClient({ publishableKey, - indexerApiKey, projectId, storage: new MemoryStorageManager(), }); diff --git a/examples/react/.env.example b/examples/react/.env.example index 8289e05..6316db9 100644 --- a/examples/react/.env.example +++ b/examples/react/.env.example @@ -1,3 +1,2 @@ VITE_OMS_PUBLISHABLE_KEY=your-publishable-key -VITE_OMS_INDEXER_API_KEY=your-indexer-api-key VITE_OMS_PROJECT_ID=your-oms-project-id diff --git a/examples/react/README.md b/examples/react/README.md index 6cd9f30..aea350f 100644 --- a/examples/react/README.md +++ b/examples/react/README.md @@ -12,7 +12,7 @@ Run it from the repository root: pnpm install pnpm build cp examples/react/.env.example examples/react/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID pnpm dev:example ``` @@ -24,7 +24,7 @@ The example requires a publishable key and project ID. Configure them locally be ```bash cp examples/react/.env.example examples/react/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID ``` The Amoy-only "ERC20 example" panel includes a WalletKit Dollar example using diff --git a/examples/react/src/config.ts b/examples/react/src/config.ts index 973a717..503b55b 100644 --- a/examples/react/src/config.ts +++ b/examples/react/src/config.ts @@ -2,7 +2,6 @@ export const PUBLISHABLE_KEY = requiredEnv( 'VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY, ) -export const INDEXER_API_KEY = requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY) export const PROJECT_ID = requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID) function requiredEnv(name: string, value: string | undefined): string { diff --git a/examples/react/src/omsClient.ts b/examples/react/src/omsClient.ts index ca2cec0..2b5c90d 100644 --- a/examples/react/src/omsClient.ts +++ b/examples/react/src/omsClient.ts @@ -1,10 +1,9 @@ import { OMSClient } from '@0xsequence/typescript-sdk' -import { INDEXER_API_KEY, PROJECT_ID, PUBLISHABLE_KEY } from './config' +import { PROJECT_ID, PUBLISHABLE_KEY } from './config' export const TEST_SESSION_LIFETIME_SECONDS = 604_800 export const oms = new OMSClient({ publishableKey: PUBLISHABLE_KEY, - indexerApiKey: INDEXER_API_KEY, projectId: PROJECT_ID, }) diff --git a/examples/react/src/vite-env.d.ts b/examples/react/src/vite-env.d.ts index 80d9569..37fee95 100644 --- a/examples/react/src/vite-env.d.ts +++ b/examples/react/src/vite-env.d.ts @@ -2,7 +2,6 @@ interface ImportMetaEnv { readonly VITE_OMS_PUBLISHABLE_KEY?: string - readonly VITE_OMS_INDEXER_API_KEY?: string readonly VITE_OMS_PROJECT_ID?: string } diff --git a/examples/trails-actions/.env.example b/examples/trails-actions/.env.example index 8289e05..6316db9 100644 --- a/examples/trails-actions/.env.example +++ b/examples/trails-actions/.env.example @@ -1,3 +1,2 @@ VITE_OMS_PUBLISHABLE_KEY=your-publishable-key -VITE_OMS_INDEXER_API_KEY=your-indexer-api-key VITE_OMS_PROJECT_ID=your-oms-project-id diff --git a/examples/trails-actions/README.md b/examples/trails-actions/README.md index 99812a5..d096842 100644 --- a/examples/trails-actions/README.md +++ b/examples/trails-actions/README.md @@ -12,7 +12,7 @@ Run it from the repository root: pnpm install pnpm build cp examples/trails-actions/.env.example examples/trails-actions/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID pnpm dev:trails-actions-example ``` diff --git a/examples/trails-actions/src/config.ts b/examples/trails-actions/src/config.ts index 444d19f..09ca60c 100644 --- a/examples/trails-actions/src/config.ts +++ b/examples/trails-actions/src/config.ts @@ -1,5 +1,4 @@ export const PUBLISHABLE_KEY = requiredEnv('VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY) -export const INDEXER_API_KEY = requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY) export const PROJECT_ID = requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID) function requiredEnv(name: string, value: string | undefined): string { diff --git a/examples/trails-actions/src/omsClient.ts b/examples/trails-actions/src/omsClient.ts index ca2cec0..2b5c90d 100644 --- a/examples/trails-actions/src/omsClient.ts +++ b/examples/trails-actions/src/omsClient.ts @@ -1,10 +1,9 @@ import { OMSClient } from '@0xsequence/typescript-sdk' -import { INDEXER_API_KEY, PROJECT_ID, PUBLISHABLE_KEY } from './config' +import { PROJECT_ID, PUBLISHABLE_KEY } from './config' export const TEST_SESSION_LIFETIME_SECONDS = 604_800 export const oms = new OMSClient({ publishableKey: PUBLISHABLE_KEY, - indexerApiKey: INDEXER_API_KEY, projectId: PROJECT_ID, }) diff --git a/examples/trails-actions/src/trailsActions.ts b/examples/trails-actions/src/trailsActions.ts index 449a2d5..eb3578b 100644 --- a/examples/trails-actions/src/trailsActions.ts +++ b/examples/trails-actions/src/trailsActions.ts @@ -183,20 +183,18 @@ export function requirePreparedYieldTransactions( } export async function getPolygonBalances(walletAddress: Address): Promise { - const [polBalance, usdcResult] = await Promise.all([ - oms.indexer.getNativeTokenBalance({ - network: POLYGON_NETWORK, - walletAddress, - }), - oms.indexer.getTokenBalances({ - network: POLYGON_NETWORK, - contractAddress: POLYGON_USDC, - walletAddress, - includeMetadata: false, - }), - ]) + const balances = await oms.indexer.getBalances({ + networks: [POLYGON_NETWORK], + contractAddresses: [POLYGON_USDC], + walletAddress, + includeMetadata: false, + }) + const polBalance = balances.nativeBalances.find(balance => balance.chainId === POLYGON_NETWORK.id) + const usdcBalance = balances.balances.find(balance => + balance.contractAddress?.toLowerCase() === POLYGON_USDC.toLowerCase(), + ) const polRaw = polBalance?.balance ?? '0' - const usdcRaw = usdcResult.balances[0]?.balance ?? '0' + const usdcRaw = usdcBalance?.balance ?? '0' return { pol: formatTokenAmount(polRaw, 18, 'POL'), diff --git a/examples/trails-actions/src/vite-env.d.ts b/examples/trails-actions/src/vite-env.d.ts index 80d9569..37fee95 100644 --- a/examples/trails-actions/src/vite-env.d.ts +++ b/examples/trails-actions/src/vite-env.d.ts @@ -2,7 +2,6 @@ interface ImportMetaEnv { readonly VITE_OMS_PUBLISHABLE_KEY?: string - readonly VITE_OMS_INDEXER_API_KEY?: string readonly VITE_OMS_PROJECT_ID?: string } diff --git a/examples/wagmi/.env.example b/examples/wagmi/.env.example index 9347af6..9d0ccee 100644 --- a/examples/wagmi/.env.example +++ b/examples/wagmi/.env.example @@ -1,4 +1,3 @@ VITE_OMS_PUBLISHABLE_KEY=your-publishable-key -VITE_OMS_INDEXER_API_KEY=your-indexer-api-key VITE_OMS_PROJECT_ID=your-oms-project-id VITE_TRAILS_API_KEY=AQAAAAAAAMDoWz-avqIIjXGH7JJlBSormpo diff --git a/examples/wagmi/README.md b/examples/wagmi/README.md index 814bdf0..acccdb2 100644 --- a/examples/wagmi/README.md +++ b/examples/wagmi/README.md @@ -8,7 +8,7 @@ Run it from the repository root: pnpm install pnpm build cp examples/wagmi/.env.example examples/wagmi/.env.local -# Fill VITE_OMS_PUBLISHABLE_KEY, VITE_OMS_INDEXER_API_KEY, and VITE_OMS_PROJECT_ID +# Fill VITE_OMS_PUBLISHABLE_KEY and VITE_OMS_PROJECT_ID pnpm dev:wagmi-example ``` diff --git a/examples/wagmi/src/config.ts b/examples/wagmi/src/config.ts index 9e9907a..5f26fec 100644 --- a/examples/wagmi/src/config.ts +++ b/examples/wagmi/src/config.ts @@ -2,7 +2,6 @@ export const PUBLISHABLE_KEY = requiredEnv( 'VITE_OMS_PUBLISHABLE_KEY', import.meta.env.VITE_OMS_PUBLISHABLE_KEY, ) -export const INDEXER_API_KEY = requiredEnv('VITE_OMS_INDEXER_API_KEY', import.meta.env.VITE_OMS_INDEXER_API_KEY) export const PROJECT_ID = requiredEnv('VITE_OMS_PROJECT_ID', import.meta.env.VITE_OMS_PROJECT_ID) export const TRAILS_API_KEY = import.meta.env.VITE_TRAILS_API_KEY diff --git a/examples/wagmi/src/omsClient.ts b/examples/wagmi/src/omsClient.ts index ca2cec0..2b5c90d 100644 --- a/examples/wagmi/src/omsClient.ts +++ b/examples/wagmi/src/omsClient.ts @@ -1,10 +1,9 @@ import { OMSClient } from '@0xsequence/typescript-sdk' -import { INDEXER_API_KEY, PROJECT_ID, PUBLISHABLE_KEY } from './config' +import { PROJECT_ID, PUBLISHABLE_KEY } from './config' export const TEST_SESSION_LIFETIME_SECONDS = 604_800 export const oms = new OMSClient({ publishableKey: PUBLISHABLE_KEY, - indexerApiKey: INDEXER_API_KEY, projectId: PROJECT_ID, }) diff --git a/examples/wagmi/src/vite-env.d.ts b/examples/wagmi/src/vite-env.d.ts index 2225909..fd73e38 100644 --- a/examples/wagmi/src/vite-env.d.ts +++ b/examples/wagmi/src/vite-env.d.ts @@ -2,7 +2,6 @@ interface ImportMetaEnv { readonly VITE_OMS_PUBLISHABLE_KEY?: string - readonly VITE_OMS_INDEXER_API_KEY?: string readonly VITE_OMS_PROJECT_ID?: string readonly VITE_TRAILS_API_KEY?: string } diff --git a/packages/oms-wallet-wagmi-connector/README.md b/packages/oms-wallet-wagmi-connector/README.md index 81382fb..73e22c1 100644 --- a/packages/oms-wallet-wagmi-connector/README.md +++ b/packages/oms-wallet-wagmi-connector/README.md @@ -12,7 +12,6 @@ import { omsWalletConnector } from '@0xsequence/oms-wallet-wagmi-connector' const oms = new OMSClient({ publishableKey: import.meta.env.VITE_OMS_PUBLISHABLE_KEY, - indexerApiKey: import.meta.env.VITE_OMS_INDEXER_API_KEY, projectId: import.meta.env.VITE_OMS_PROJECT_ID, }) diff --git a/src/clients/indexerClient.ts b/src/clients/indexerClient.ts index c9c7633..7223128 100644 --- a/src/clients/indexerClient.ts +++ b/src/clients/indexerClient.ts @@ -1,14 +1,28 @@ -// Converted from Swift IndexerClient. +// Minimal hand-written IndexerGateway adapter for the SDK surface we expose. import {HttpClient} from "../httpClient.js"; import {errorMessage, OmsRequestError, OmsResponseError, type OmsUpstreamError} from "../errors.js"; import type {Network} from "../networks.js"; import {IndexerOperation} from "../operations.js"; +const WebrpcHeaderValue = "webrpc@v0.31.2;gen-typescript@v0.23.1;sequence-indexer@v0.4.0"; + +export type IndexerNetworkType = "MAINNETS" | "TESTNETS" | "ALL"; +export type ContractVerificationStatus = "VERIFIED" | "UNVERIFIED" | "ALL"; + export interface TokenBalancesPage { - page: number; - pageSize: number; - more: boolean; + page?: number; + column?: string; + before?: unknown; + after?: unknown; + sort?: SortBy[]; + pageSize?: number; + more?: boolean; +} + +export interface SortBy { + column: string; + order: "DESC" | "ASC"; } export interface TokenContractInfo { @@ -72,6 +86,8 @@ export interface TokenBalance { accountAddress?: string; /** Wire format uses `tokenID`; this field is re-mapped during decoding. */ tokenId?: string; + name?: string; + symbol?: string; balance?: string; balanceUSD?: string; priceUSD?: string; @@ -85,26 +101,106 @@ export interface TokenBalance { tokenMetadata?: TokenMetadata; } -export interface TokenBalancesResult { +export interface MetadataOptions { + verifiedOnly?: boolean; + unverifiedOnly?: boolean; + includeContracts?: string[]; +} + +export interface GetBalancesParams { + walletAddress: string; + networks?: Network[]; + networkType?: IndexerNetworkType; + contractAddresses?: string[]; + includeMetadata?: boolean; + omitPrices?: boolean; + tokenIds?: string[]; + contractStatus?: ContractVerificationStatus; + page?: TokenBalancesPage; +} + +export interface BalancesResult { status: number; page?: TokenBalancesPage; + nativeBalances: TokenBalance[]; balances: TokenBalance[]; } -interface NativeTokenBalancePayloadRaw { - balance?: NativeTokenBalanceRaw; +export interface TransactionTransfer { + transferType?: string; + contractAddress?: string; + contractType?: string; + from?: string; + to?: string; + tokenIds?: string[]; + amounts?: string[]; + logIndex?: number; + amountsUSD?: string[]; + pricesUSD?: string[]; + contractInfo?: TokenContractInfo; + tokenMetadata?: Record; } -interface NativeTokenBalanceRaw { - accountAddress?: string; - balance?: string; - balanceWei?: string; - chainId?: number; +export interface Transaction { + txnHash: string; + blockNumber: number; + blockHash: string; + chainId: number; + metaTxnId?: string; + transfers?: TransactionTransfer[]; + timestamp: string; +} + +export interface GetTransactionHistoryParams { + walletAddress: string; + networks?: Network[]; + networkType?: IndexerNetworkType; + contractAddresses?: string[]; + transactionHashes?: string[]; + metaTransactionIds?: string[]; + fromBlock?: number; + toBlock?: number; + tokenId?: string; + includeMetadata?: boolean; + omitPrices?: boolean; + metadataOptions?: MetadataOptions; + page?: TokenBalancesPage; } -interface TokenBalancesPayloadRaw { +export interface TransactionHistoryResult { + status: number; page?: TokenBalancesPage; - balances?: TokenBalanceRaw[]; + transactions: Transaction[]; +} + +interface GatewayNativeTokenBalances { + chainId: number; + errorReason?: string; + results?: NativeTokenBalanceRaw[]; +} + +interface GatewayTokenBalance { + chainId: number; + errorReason?: string; + results?: TokenBalanceRaw[]; +} + +interface GatewayTransaction { + chainId: number; + errorReason?: string; + results?: TransactionRaw[]; +} + +interface NativeTokenBalanceRaw { + accountAddress?: string; + chainId?: number; + name?: string; + symbol?: string; + balance?: string; + balanceUSD?: string; + priceUSD?: string; + priceUpdatedAt?: string; + errorReason?: string; } interface TokenBalanceRaw { @@ -136,102 +232,150 @@ interface TokenMetadataAssetRaw extends Omit { tokenID?: string; } -interface RequestPage { - page: number; - pageSize: number; - more: boolean; +interface TransactionRaw { + txnHash: string; + blockNumber: number; + blockHash: string; + chainId: number; + metaTxnID?: string; + transfers?: TransactionTransferRaw[]; + timestamp: string; } -interface TokenBalancesRequest { - page: RequestPage; - contractAddress?: string; - accountAddress: string; - includeMetadata: boolean; +interface TransactionTransferRaw extends Omit { + tokenIds?: string[]; + tokenIDs?: string[]; + tokenMetadata?: Record; +} + +interface TokenBalancesFilter { + accountAddresses: string[]; + contractStatus?: ContractVerificationStatus; + contractTypes?: string[]; + contractWhitelist?: string[]; + contractBlacklist?: string[]; + omitNativeBalances: boolean; + omitPrices?: boolean; + tokenIDs?: string[]; +} + +interface GetTokenBalancesDetailsRequest { + chainIds?: number[]; + networkType?: IndexerNetworkType; + filter: TokenBalancesFilter; + omitMetadata?: boolean; + page?: TokenBalancesPage; +} + +interface GetTokenBalancesDetailsResponse { + page?: TokenBalancesPage; + nativeBalances?: GatewayNativeTokenBalances[]; + balances?: GatewayTokenBalance[]; +} + +interface TransactionHistoryFilter { + accountAddresses: string[]; + contractAddresses?: string[]; + transactionHashes?: string[]; + metaTransactionIDs?: string[]; + fromBlock?: number; + toBlock?: number; + tokenID?: string; + omitPrices?: boolean; +} + +interface GetTransactionHistoryRequest { + chainIds?: number[]; + networkType?: IndexerNetworkType; + filter: TransactionHistoryFilter; + includeMetadata?: boolean; + metadataOptions?: MetadataOptions; + page?: TokenBalancesPage; +} + +interface GetTransactionHistoryResponse { + page?: TokenBalancesPage; + transactions?: GatewayTransaction[]; } // Matches the Swift `OmsEnvironment` shape used by IndexerClient. export interface OmsEnvironment { - indexerUrlTemplate: string; + indexerGatewayUrl: string; } export class IndexerClient { - private readonly indexerApiKey: string; + private readonly publishableKey: string; private readonly environment: OmsEnvironment; private readonly client: HttpClient; constructor(params: { - indexerApiKey: string, + publishableKey: string, environment: OmsEnvironment }) { - this.indexerApiKey = params.indexerApiKey; + this.publishableKey = params.publishableKey; this.environment = params.environment; this.client = new HttpClient(); } - async getTokenBalances(params: { - network: Network - contractAddress?: string - walletAddress: string - includeMetadata: boolean - page?: { - page?: number - pageSize?: number - } - }): Promise { - const request: TokenBalancesRequest = { - page: { - page: params.page?.page ?? 0, - pageSize: params.page?.pageSize ?? 40, - more: false, + async getBalances(params: GetBalancesParams): Promise { + const request: GetTokenBalancesDetailsRequest = { + ...this.chainScope(params), + filter: { + accountAddresses: [params.walletAddress], + contractWhitelist: nonEmpty(params.contractAddresses), + contractStatus: params.contractStatus, + omitNativeBalances: false, + omitPrices: params.omitPrices, + tokenIDs: nonEmpty(params.tokenIds), }, - accountAddress: params.walletAddress, - includeMetadata: params.includeMetadata, + omitMetadata: params.includeMetadata === false, + page: this.requestPage(params.page), }; - if (params.contractAddress) { - request.contractAddress = params.contractAddress; - } - const bodyString = JSON.stringify(request); - const baseUrl = this.indexerUrl(params.network); - - const response = await this.postJson(IndexerOperation.getTokenBalances, { - baseUrl, - path: "/GetTokenBalances", - body: bodyString, + const response = await this.postJson(IndexerOperation.getBalances, { + baseUrl: this.indexerGatewayUrl(), + path: "/GetTokenBalancesDetails", + body: JSON.stringify(request), headers: this.defaultHeaders(), }); return { status: response.statusCode, page: response.payload.page, - balances: (response.payload.balances ?? []).map(mapTokenBalance), + nativeBalances: flattenGatewayResults(response.payload.nativeBalances).map(mapNativeTokenBalance), + balances: flattenGatewayResults(response.payload.balances).map(mapTokenBalance), }; } - async getNativeTokenBalance(params: { - network: Network - walletAddress: string - }): Promise { - const response = await this.postJson(IndexerOperation.getNativeTokenBalance, { - baseUrl: this.indexerUrl(params.network), - path: "/GetNativeTokenBalance", - body: JSON.stringify({ accountAddress: params.walletAddress }), + async getTransactionHistory(params: GetTransactionHistoryParams): Promise { + const request: GetTransactionHistoryRequest = { + ...this.chainScope(params), + filter: { + accountAddresses: [params.walletAddress], + contractAddresses: nonEmpty(params.contractAddresses), + transactionHashes: nonEmpty(params.transactionHashes), + metaTransactionIDs: nonEmpty(params.metaTransactionIds), + fromBlock: params.fromBlock, + toBlock: params.toBlock, + tokenID: params.tokenId, + omitPrices: params.omitPrices, + }, + includeMetadata: params.includeMetadata ?? true, + metadataOptions: params.metadataOptions, + page: this.requestPage(params.page), + }; + + const response = await this.postJson(IndexerOperation.getTransactionHistory, { + baseUrl: this.indexerGatewayUrl(), + path: "/GetTransactionHistory", + body: JSON.stringify(request), headers: this.defaultHeaders(), }); - if (!response.payload.balance) { - return undefined; - } - return { - contractType: "NATIVE", - contractAddress: undefined, - accountAddress: response.payload.balance.accountAddress, - tokenId: undefined, - balance: response.payload.balance.balance ?? response.payload.balance.balanceWei, - blockHash: undefined, - blockNumber: undefined, - chainId: response.payload.balance.chainId, + status: response.statusCode, + page: response.payload.page, + transactions: flattenGatewayResults(response.payload.transactions).map(mapTransaction), }; } @@ -287,16 +431,78 @@ export class IndexerClient { return {statusCode: response.statusCode, payload}; } - private indexerUrl(network: Network): string { - return this.environment.indexerUrlTemplate.replace("{value}", network.name); + private chainScope(params: { + networks?: Network[] + networkType?: IndexerNetworkType + }): {chainIds?: number[], networkType?: IndexerNetworkType} { + if (params.networks && params.networks.length > 0) { + return {chainIds: params.networks.map(network => network.id)}; + } + return {networkType: params.networkType ?? "MAINNETS"}; } - private defaultHeaders(): Record { + private requestPage(page: TokenBalancesPage | undefined): TokenBalancesPage { return { - "X-Access-Key": this.indexerApiKey, + ...page, + page: page?.page ?? 0, + pageSize: page?.pageSize ?? 40, + }; + } + + private indexerGatewayUrl(): string { + return this.environment.indexerGatewayUrl; + } + + private defaultHeaders(): Record { + const headers: Record = { + "Api-Key": this.publishableKey, Accept: "application/json", + Webrpc: WebrpcHeaderValue, }; + + const origin = this.originHeader(); + if (origin) { + headers.Origin = origin; + } + + return headers; } + + private originHeader(): string | undefined { + if (typeof globalThis.location?.origin === "string") { + return undefined; + } + + // The dev IndexerGateway currently rejects originless publishable-key requests. + // Send the local dev origin from originless runtimes until the gateway accepts them. + return "http://localhost:5173"; + } +} + +function flattenGatewayResults(groups: Array<{results?: T[]}> | undefined): T[] { + return groups?.flatMap(group => group.results ?? []) ?? []; +} + +function nonEmpty(values: T[] | undefined): T[] | undefined { + return values && values.length > 0 ? values : undefined; +} + +function mapNativeTokenBalance(raw: NativeTokenBalanceRaw): TokenBalance { + return { + contractType: "NATIVE", + contractAddress: undefined, + accountAddress: raw.accountAddress, + tokenId: undefined, + name: raw.name, + symbol: raw.symbol, + balance: raw.balance, + balanceUSD: raw.balanceUSD, + priceUSD: raw.priceUSD, + priceUpdatedAt: raw.priceUpdatedAt, + blockHash: undefined, + blockNumber: undefined, + chainId: raw.chainId, + }; } /** Re-maps the wire key `tokenID` onto the camelCase `tokenId` field. */ @@ -320,6 +526,33 @@ function mapTokenBalance(raw: TokenBalanceRaw): TokenBalance { }; } +function mapTransaction(raw: TransactionRaw): Transaction { + return { + txnHash: raw.txnHash, + blockNumber: raw.blockNumber, + blockHash: raw.blockHash, + chainId: raw.chainId, + metaTxnId: raw.metaTxnID, + transfers: raw.transfers?.map(mapTransactionTransfer), + timestamp: raw.timestamp, + }; +} + +function mapTransactionTransfer(raw: TransactionTransferRaw): TransactionTransfer { + const {tokenIDs, tokenMetadata, ...transfer} = raw; + return { + ...transfer, + tokenIds: raw.tokenIds ?? tokenIDs, + tokenMetadata: tokenMetadata ? mapTokenMetadataRecord(tokenMetadata) : undefined, + }; +} + +function mapTokenMetadataRecord(raw: Record): Record { + return Object.fromEntries( + Object.entries(raw).map(([tokenId, metadata]) => [tokenId, mapTokenMetadata(metadata)]), + ); +} + function mapTokenMetadata(raw: TokenMetadataRaw): TokenMetadata { const {tokenID, assets, ...metadata} = raw; return { @@ -336,11 +569,9 @@ function mapTokenMetadata(raw: TokenMetadataRaw): TokenMetadata { } function responseErrorMessage(payload: unknown, operation: IndexerOperation, status: number): string { - if (payload && typeof payload === "object" && "message" in payload) { - const message = (payload as {message?: unknown}).message; - if (typeof message === "string" && message) { - return message; - } + const message = gatewayErrorMessage(payload); + if (message) { + return message; } return `${operation} failed with HTTP ${status}`; } @@ -369,11 +600,17 @@ function indexerResponseError(payload: unknown, status: number, fallbackMessage: service: "indexer", name: stringField(payload, "name") ?? stringField(payload, "error"), code: numberOrStringField(payload, "code"), - message: stringField(payload, "message") ?? fallbackMessage, + message: gatewayErrorMessage(payload) ?? fallbackMessage, status, }; } +function gatewayErrorMessage(payload: unknown): string | undefined { + return stringField(payload, "message") + ?? stringField(payload, "cause") + ?? stringField(payload, "msg"); +} + function stringField(source: unknown, key: string): string | undefined { const value = objectField(source, key); return typeof value === "string" ? value : undefined; diff --git a/src/clients/walletClient.ts b/src/clients/walletClient.ts index 5bbf6de..694e333 100644 --- a/src/clients/walletClient.ts +++ b/src/clients/walletClient.ts @@ -360,7 +360,6 @@ export class WalletClient { constructor(params: { publishableKey: string, - indexerApiKey: string, projectId: string, environment: Env, storage?: StorageManager @@ -414,7 +413,7 @@ export class WalletClient { createApiKeyFetch(params.publishableKey), ) this.indexerClient = new IndexerClient({ - indexerApiKey: params.indexerApiKey, + publishableKey: params.publishableKey, environment: params.environment, }) } @@ -1535,20 +1534,38 @@ export class WalletClient { throw new Error('No active wallet session') } - const nativeBalance = feeOptions.some(option => this.isNativeToken(option)) - ? await this.loadNativeTokenBalance(network, walletAddress) - : undefined - const contractAddresses = Array.from(new Set( feeOptions .map(option => this.normalizeAddress(option.token.contractAddress)) .filter((address): address is string => Boolean(address)), )) + const balances = await this.indexerClient.getBalances({ + networks: [network], + contractAddresses, + walletAddress, + includeMetadata: false, + }).catch(() => undefined) + const nativeBalance = feeOptions.some(option => this.isNativeToken(option)) + ? balances?.nativeBalances.find(balance => balance.chainId === network.id) + : undefined const tokenBalances = new Map( - await Promise.all(contractAddresses.map(async contractAddress => [ + contractAddresses.map(contractAddress => [ contractAddress, - await this.loadTokenBalanceOrZero(network, contractAddress, walletAddress), - ] as const)), + balances + ? balances.balances.find(balance => + this.normalizeAddress(balance.contractAddress) === contractAddress, + ) ?? { + contractType: 'ERC20', + contractAddress, + accountAddress: walletAddress, + tokenId: undefined, + balance: '0', + blockHash: undefined, + blockNumber: undefined, + chainId: network.id, + } + : undefined, + ]), ) return feeOptions.map(feeOption => { @@ -1568,40 +1585,6 @@ export class WalletClient { }) } - private async loadNativeTokenBalance( - network: Network, - walletAddress: Address, - ): Promise { - return this.indexerClient.getNativeTokenBalance({ - network, - walletAddress, - }).catch(() => undefined) - } - - private async loadTokenBalanceOrZero( - network: Network, - contractAddress: string, - walletAddress: Address, - ): Promise { - return this.indexerClient.getTokenBalances({ - network, - contractAddress, - walletAddress, - includeMetadata: false, - }).then(result => result.balances.find(balance => - this.normalizeAddress(balance.contractAddress) === contractAddress, - ) ?? { - contractType: 'ERC20', - contractAddress, - accountAddress: walletAddress, - tokenId: undefined, - balance: '0', - blockHash: undefined, - blockNumber: undefined, - chainId: network.id, - }).catch(() => undefined) - } - private defaultFeeOptionSelection( feeOptions: FeeOption[], ): FeeOptionSelection { diff --git a/src/index.ts b/src/index.ts index 2d67cde..8918acf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -73,12 +73,21 @@ export type { WalletSelectionBehavior, } from './clients/walletClient.js' export type { + BalancesResult, + ContractVerificationStatus, + GetBalancesParams, + GetTransactionHistoryParams, + IndexerNetworkType, + MetadataOptions, + SortBy, TokenContractInfo, TokenBalance, TokenBalancesPage, - TokenBalancesResult, TokenMetadata, TokenMetadataAsset, + Transaction, + TransactionHistoryResult, + TransactionTransfer, } from './clients/indexerClient.js' export type { AccessGrant, diff --git a/src/omsClient.ts b/src/omsClient.ts index 844a624..6aa124c 100644 --- a/src/omsClient.ts +++ b/src/omsClient.ts @@ -7,7 +7,6 @@ import {supportedNetworks} from "./networks.js"; interface OMSClientBaseParams { publishableKey: string; - indexerApiKey: string; projectId: string; storage?: StorageManager; redirectAuthStorage?: StorageManager; @@ -30,7 +29,6 @@ class OMSClientImpl { this.wallet = new WalletClient({ publishableKey: params.publishableKey, - indexerApiKey: params.indexerApiKey, projectId: params.projectId, environment, storage, @@ -39,7 +37,7 @@ class OMSClientImpl { }); this.indexer = new IndexerClient({ - indexerApiKey: params.indexerApiKey, + publishableKey: params.publishableKey, environment }); } diff --git a/src/omsEnvironment.ts b/src/omsEnvironment.ts index b1b641c..207636b 100644 --- a/src/omsEnvironment.ts +++ b/src/omsEnvironment.ts @@ -19,13 +19,13 @@ export interface OmsEnvironment< OidcProviders extends Record = Record, > { walletApiUrl: string; - indexerUrlTemplate: string; + indexerGatewayUrl: string; auth?: OmsAuthConfig; } export const defaultOmsEnvironment = { walletApiUrl: "https://sandbox-api.dev.polygon-dev.technology", - indexerUrlTemplate: "https://dev-{value}-indexer.sequence.app/rpc/Indexer/", + indexerGatewayUrl: "https://api.dev.polygon-dev.technology/v1/IndexerGateway/", auth: { oidcProviders: { google: googleOidcProvider(), diff --git a/src/operations.ts b/src/operations.ts index 1d16633..3493cf5 100644 --- a/src/operations.ts +++ b/src/operations.ts @@ -28,8 +28,8 @@ export const WalletOperation = { export type WalletOperation = typeof WalletOperation[keyof typeof WalletOperation] export const IndexerOperation = { - getTokenBalances: "indexer.getTokenBalances", - getNativeTokenBalance: "indexer.getNativeTokenBalance", + getBalances: "indexer.getBalances", + getTransactionHistory: "indexer.getTransactionHistory", } as const export type IndexerOperation = typeof IndexerOperation[keyof typeof IndexerOperation] diff --git a/tests/errorContracts.test.ts b/tests/errorContracts.test.ts index 6227e8a..5288b7f 100644 --- a/tests/errorContracts.test.ts +++ b/tests/errorContracts.test.ts @@ -1168,8 +1168,8 @@ describe("public API error contracts", () => { const oms = createOmsClient(); await expect(publicError(() => - oms.indexer.getTokenBalances({ - network: Networks.polygon, + oms.indexer.getBalances({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }), @@ -1178,7 +1178,7 @@ describe("public API error contracts", () => { "code": "OMS_HTTP_ERROR", "message": "Indexer is unavailable", "name": "OmsRequestError", - "operation": "indexer.getTokenBalances", + "operation": "indexer.getBalances", "retryable": true, "status": 503, "txnId": null, @@ -1204,23 +1204,23 @@ describe("public API error contracts", () => { const oms = createOmsClient(); await expect(publicError(() => - oms.indexer.getTokenBalances({ - network: Networks.polygon, + oms.indexer.getBalances({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }), )).resolves.toMatchInlineSnapshot(` { "code": "OMS_HTTP_ERROR", - "message": "indexer.getTokenBalances failed with HTTP 502", + "message": "indexer.getBalances failed with HTTP 502", "name": "OmsRequestError", - "operation": "indexer.getTokenBalances", + "operation": "indexer.getBalances", "retryable": true, "status": 502, "txnId": null, "upstreamError": { "code": null, - "message": "indexer.getTokenBalances failed with HTTP 502", + "message": "indexer.getBalances failed with HTTP 502", "name": null, "service": "indexer", "status": 502, @@ -1229,7 +1229,7 @@ describe("public API error contracts", () => { `); }); - it("snapshots native balance indexer errors with upstream details", async () => { + it("snapshots transaction history indexer errors with upstream details", async () => { let callCount = 0; vi.stubGlobal("fetch", vi.fn(async () => { callCount += 1; @@ -1249,16 +1249,16 @@ describe("public API error contracts", () => { const oms = createOmsClient(); await expect(publicErrors([ - ["indexer.getNativeTokenBalance.http", () => oms.indexer.getNativeTokenBalance({ - network: Networks.polygon, + ["indexer.getTransactionHistory.http", () => oms.indexer.getTransactionHistory({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", })], - ["indexer.getNativeTokenBalance.transport", () => oms.indexer.getNativeTokenBalance({ - network: Networks.polygon, + ["indexer.getTransactionHistory.transport", () => oms.indexer.getTransactionHistory({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", })], - ["indexer.getNativeTokenBalance.malformed", () => oms.indexer.getNativeTokenBalance({ - network: Networks.polygon, + ["indexer.getTransactionHistory.malformed", () => oms.indexer.getTransactionHistory({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", })], ])).resolves.toMatchInlineSnapshot(` @@ -1268,7 +1268,7 @@ describe("public API error contracts", () => { "code": "OMS_HTTP_ERROR", "message": "Indexer is unavailable", "name": "OmsRequestError", - "operation": "indexer.getNativeTokenBalance", + "operation": "indexer.getTransactionHistory", "retryable": true, "status": 503, "txnId": null, @@ -1280,14 +1280,14 @@ describe("public API error contracts", () => { "status": 503, }, }, - "label": "indexer.getNativeTokenBalance.http", + "label": "indexer.getTransactionHistory.http", }, { "error": { "code": "OMS_REQUEST_FAILED", "message": "fetch failed", "name": "OmsRequestError", - "operation": "indexer.getNativeTokenBalance", + "operation": "indexer.getTransactionHistory", "retryable": true, "status": null, "txnId": null, @@ -1299,26 +1299,26 @@ describe("public API error contracts", () => { "status": null, }, }, - "label": "indexer.getNativeTokenBalance.transport", + "label": "indexer.getTransactionHistory.transport", }, { "error": { "code": "OMS_INVALID_RESPONSE", - "message": "Invalid JSON response from indexer.getNativeTokenBalance", + "message": "Invalid JSON response from indexer.getTransactionHistory", "name": "OmsResponseError", - "operation": "indexer.getNativeTokenBalance", + "operation": "indexer.getTransactionHistory", "retryable": null, "status": 200, "txnId": null, "upstreamError": { "code": null, - "message": "Invalid JSON response from indexer.getNativeTokenBalance", + "message": "Invalid JSON response from indexer.getTransactionHistory", "name": null, "service": "indexer", "status": 200, }, }, - "label": "indexer.getNativeTokenBalance.malformed", + "label": "indexer.getTransactionHistory.malformed", }, ] `); @@ -1332,8 +1332,8 @@ describe("public API error contracts", () => { const oms = createOmsClient(); await expect(publicError(() => - oms.indexer.getTokenBalances({ - network: Networks.polygon, + oms.indexer.getBalances({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }), @@ -1342,7 +1342,7 @@ describe("public API error contracts", () => { "code": "OMS_REQUEST_FAILED", "message": "fetch failed", "name": "OmsRequestError", - "operation": "indexer.getTokenBalances", + "operation": "indexer.getBalances", "retryable": true, "status": null, "txnId": null, @@ -1363,23 +1363,23 @@ describe("public API error contracts", () => { const oms = createOmsClient(); await expect(publicError(() => - oms.indexer.getTokenBalances({ - network: Networks.polygon, + oms.indexer.getBalances({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }), )).resolves.toMatchInlineSnapshot(` { "code": "OMS_INVALID_RESPONSE", - "message": "Invalid JSON response from indexer.getTokenBalances", + "message": "Invalid JSON response from indexer.getBalances", "name": "OmsResponseError", - "operation": "indexer.getTokenBalances", + "operation": "indexer.getBalances", "retryable": null, "status": 200, "txnId": null, "upstreamError": { "code": null, - "message": "Invalid JSON response from indexer.getTokenBalances", + "message": "Invalid JSON response from indexer.getBalances", "name": null, "service": "indexer", "status": 200, @@ -1573,13 +1573,12 @@ function createOmsClient(params: { } = {}): OMSClient { const clientParams: ConstructorParameters[0] = { publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", storage: new MemoryStorageManager(), credentialSigner: params.credentialSigner ?? new MockSigner(), environment: { walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }, }; diff --git a/tests/indexerClient.test.ts b/tests/indexerClient.test.ts index 7323437..0926f99 100644 --- a/tests/indexerClient.test.ts +++ b/tests/indexerClient.test.ts @@ -9,40 +9,63 @@ afterEach(() => { }); describe("IndexerClient", () => { - it("omits contractAddress when querying balances across contracts", async () => { + it("requests balances through IndexerGateway and flattens grouped chain results", async () => { const fetchMock = vi.fn(async () => new Response(JSON.stringify({ page: {page: 1, pageSize: 25, more: false}, + nativeBalances: [{ + chainId: 137, + results: [{ + accountAddress: "0x9999999999999999999999999999999999999999", + chainId: 137, + name: "POL", + symbol: "POL", + balance: "1000000000000000000", + balanceUSD: "0.20", + priceUSD: "0.20", + }], + }], balances: [{ - contractType: "ERC20", - contractAddress: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", - accountAddress: "0x9999999999999999999999999999999999999999", - tokenID: "0", - balance: "141799", - balanceUSD: "0.141799", - priceUSD: "1", chainId: 137, - contractInfo: { - name: "USDC", - symbol: "USDC", - decimals: 6, - }, + results: [{ + contractType: "ERC20", + contractAddress: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", + accountAddress: "0x9999999999999999999999999999999999999999", + tokenID: "0", + balance: "141799", + balanceUSD: "0.141799", + priceUSD: "1", + chainId: 137, + contractInfo: { + name: "USDC", + symbol: "USDC", + decimals: 6, + }, + }], }], }), {status: 200})); vi.stubGlobal("fetch", fetchMock); const indexer = new IndexerClient({ - indexerApiKey: "indexer-api-key", + publishableKey: "publishable-key", environment: testEnvironment(), }); - await expect(indexer.getTokenBalances({ - network: Networks.polygon, + await expect(indexer.getBalances({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: true, + contractAddresses: ["0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"], page: {page: 1, pageSize: 25}, })).resolves.toMatchObject({ status: 200, page: {page: 1, pageSize: 25, more: false}, + nativeBalances: [{ + contractType: "NATIVE", + symbol: "POL", + balance: "1000000000000000000", + balanceUSD: "0.20", + priceUSD: "0.20", + }], balances: [{ tokenId: "0", balance: "141799", @@ -55,11 +78,162 @@ describe("IndexerClient", () => { }], }); - expect(fetchMock.mock.calls[0][0].toString()).toBe("https://indexer.example/polygon/GetTokenBalances"); + expect(fetchMock.mock.calls[0][0].toString()).toBe("https://indexer.example/GetTokenBalancesDetails"); + expect(fetchMock.mock.calls[0][1]?.headers).toMatchObject({ + "Api-Key": "publishable-key", + Origin: "http://localhost:5173", + Webrpc: "webrpc@v0.31.2;gen-typescript@v0.23.1;sequence-indexer@v0.4.0", + }); expect(JSON.parse(fetchMock.mock.calls[0][1]?.body as string)).toEqual({ - page: {page: 1, pageSize: 25, more: false}, - accountAddress: "0x9999999999999999999999999999999999999999", + chainIds: [137], + filter: { + accountAddresses: ["0x9999999999999999999999999999999999999999"], + contractWhitelist: ["0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"], + omitNativeBalances: false, + }, + omitMetadata: false, + page: {page: 1, pageSize: 25}, + }); + }); + + it("defaults balance queries to mainnets when networks are omitted", async () => { + const fetchMock = vi.fn(async () => new Response(JSON.stringify({ + page: {page: 0, pageSize: 40, more: false}, + nativeBalances: [], + balances: [], + }), {status: 200})); + vi.stubGlobal("fetch", fetchMock); + + const indexer = new IndexerClient({ + publishableKey: "publishable-key", + environment: testEnvironment(), + }); + + await indexer.getBalances({ + walletAddress: "0x9999999999999999999999999999999999999999", + }); + + expect(JSON.parse(fetchMock.mock.calls[0][1]?.body as string)).toEqual({ + networkType: "MAINNETS", + filter: { + accountAddresses: ["0x9999999999999999999999999999999999999999"], + omitNativeBalances: false, + }, + omitMetadata: false, + page: {page: 0, pageSize: 40}, + }); + }); + + it("requests transaction history through IndexerGateway", async () => { + const fetchMock = vi.fn(async () => new Response(JSON.stringify({ + page: {page: 0, pageSize: 1, more: true}, + transactions: [{ + chainId: 1, + results: [{ + txnHash: "0xabc", + blockNumber: 123, + blockHash: "0xdef", + chainId: 1, + metaTxnID: "meta-1", + transfers: [{ + transferType: "RECEIVE", + contractAddress: "0x0000000000000000000000000000000000000000", + contractType: "NATIVE", + from: "0x1111111111111111111111111111111111111111", + to: "0x9999999999999999999999999999999999999999", + tokenIDs: ["0"], + amounts: ["1"], + logIndex: 0, + }], + timestamp: "2026-06-17T00:00:00Z", + }], + }], + }), {status: 200})); + vi.stubGlobal("fetch", fetchMock); + + const indexer = new IndexerClient({ + publishableKey: "publishable-key", + environment: testEnvironment(), + }); + + await expect(indexer.getTransactionHistory({ + networks: [Networks.mainnet], + walletAddress: "0x9999999999999999999999999999999999999999", + includeMetadata: true, + page: {pageSize: 1}, + })).resolves.toMatchObject({ + status: 200, + page: {page: 0, pageSize: 1, more: true}, + transactions: [{ + txnHash: "0xabc", + metaTxnId: "meta-1", + transfers: [{ + tokenIds: ["0"], + amounts: ["1"], + }], + }], + }); + + expect(fetchMock.mock.calls[0][0].toString()).toBe("https://indexer.example/GetTransactionHistory"); + expect(JSON.parse(fetchMock.mock.calls[0][1]?.body as string)).toEqual({ + chainIds: [1], + filter: { + accountAddresses: ["0x9999999999999999999999999999999999999999"], + }, includeMetadata: true, + page: {pageSize: 1, page: 0}, + }); + }); + + it("does not set a synthetic Origin when the runtime already has one", async () => { + const fetchMock = vi.fn(async () => new Response(JSON.stringify({ + page: {page: 0, pageSize: 40, more: false}, + nativeBalances: [], + balances: [], + }), {status: 200})); + vi.stubGlobal("fetch", fetchMock); + vi.stubGlobal("location", {origin: "http://app.example"}); + + const indexer = new IndexerClient({ + publishableKey: "publishable-key", + environment: testEnvironment(), + }); + + await indexer.getBalances({ + walletAddress: "0x9999999999999999999999999999999999999999", + }); + + expect(fetchMock.mock.calls[0][1]?.headers).not.toHaveProperty("Origin"); + }); + + it("preserves WebRPC gateway error causes", async () => { + vi.stubGlobal("fetch", vi.fn(async () => new Response(JSON.stringify({ + error: "WebrpcEndpoint", + code: 0, + msg: "endpoint error", + cause: "InvalidCredentials 1003: omsx-api: invalid credentials, requestId: req_123", + status: 400, + }), {status: 400}))); + + const indexer = new IndexerClient({ + publishableKey: "publishable-key", + environment: testEnvironment(), + }); + + await expect(indexer.getBalances({ + walletAddress: "0x9999999999999999999999999999999999999999", + })).rejects.toMatchObject({ + code: "OMS_HTTP_ERROR", + message: "InvalidCredentials 1003: omsx-api: invalid credentials, requestId: req_123", + operation: "indexer.getBalances", + status: 400, + upstreamError: { + name: "WebrpcEndpoint", + code: 0, + message: "InvalidCredentials 1003: omsx-api: invalid credentials, requestId: req_123", + service: "indexer", + status: 400, + }, }); }); @@ -68,39 +242,39 @@ describe("IndexerClient", () => { vi.stubGlobal("fetch", fetchMock); const indexer = new IndexerClient({ - indexerApiKey: "indexer-api-key", + publishableKey: "publishable-key", environment: testEnvironment(), }); - await expect(indexer.getTokenBalances({ - network: Networks.polygon, - contractAddress: "0x2222222222222222222222222222222222222222", + await expect(indexer.getBalances({ + networks: [Networks.polygon], + contractAddresses: ["0x2222222222222222222222222222222222222222"], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, })).rejects.toMatchObject({ code: "OMS_INVALID_RESPONSE", - operation: "indexer.getTokenBalances", + operation: "indexer.getBalances", status: 200, }); - expect(fetchMock.mock.calls[0][0].toString()).toBe("https://indexer.example/polygon/GetTokenBalances"); + expect(fetchMock.mock.calls[0][0].toString()).toBe("https://indexer.example/GetTokenBalancesDetails"); }); it("wraps non-JSON HTTP responses as retryable HTTP errors", async () => { vi.stubGlobal("fetch", vi.fn(async () => new Response("Bad Gateway", {status: 502}))); const indexer = new IndexerClient({ - indexerApiKey: "indexer-api-key", + publishableKey: "publishable-key", environment: testEnvironment(), }); - await expect(indexer.getTokenBalances({ - network: Networks.polygon, - contractAddress: "0x2222222222222222222222222222222222222222", + await expect(indexer.getBalances({ + networks: [Networks.polygon], + contractAddresses: ["0x2222222222222222222222222222222222222222"], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, })).rejects.toMatchObject({ code: "OMS_HTTP_ERROR", - operation: "indexer.getTokenBalances", + operation: "indexer.getBalances", status: 502, retryable: true, }); @@ -111,6 +285,6 @@ function testEnvironment() { return { walletApiUrl: "https://wallet.example", apiRpcUrl: "https://api.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }; } diff --git a/tests/networks.test.ts b/tests/networks.test.ts index 7101f50..a7ffbc5 100644 --- a/tests/networks.test.ts +++ b/tests/networks.test.ts @@ -51,7 +51,6 @@ describe("Networks", () => { it("is available from OMSClient", () => { const oms = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", }); diff --git a/tests/oidcRedirectAuth.test.ts b/tests/oidcRedirectAuth.test.ts index 92dbb91..99376f0 100644 --- a/tests/oidcRedirectAuth.test.ts +++ b/tests/oidcRedirectAuth.test.ts @@ -142,7 +142,6 @@ describe("WalletClient OIDC redirect auth", () => { const storage = new MemoryStorageManager(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -177,7 +176,7 @@ describe("WalletClient OIDC redirect auth", () => { redirectAuthStorage: new MemoryStorageManager(), environment: defineOmsEnvironment({ walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", auth: { oidcProviders: { custom: { @@ -249,7 +248,7 @@ describe("WalletClient OIDC redirect auth", () => { projectId: "proj_custom", environment: defineOmsEnvironment({ walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", auth: { oidcProviders: { google: googleOidcProvider({ @@ -330,7 +329,7 @@ describe("WalletClient OIDC redirect auth", () => { redirectAuthStorage: new MemoryStorageManager(), environment: defineOmsEnvironment({ walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", auth: { oidcProviders: { custom: { @@ -811,7 +810,6 @@ function createWalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: params.projectId ?? "project-id", environment, storage: new MemoryStorageManager(), @@ -823,7 +821,7 @@ function createWalletClient { function createWalletWithSession(): WalletClient { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -122,6 +121,6 @@ function testCredential(seed = "11", isCaller = true) { function testEnvironment() { return { walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }; } diff --git a/tests/walletErrors.test.ts b/tests/walletErrors.test.ts index ae04d66..72d2772 100644 --- a/tests/walletErrors.test.ts +++ b/tests/walletErrors.test.ts @@ -33,7 +33,6 @@ describe("WalletClient errors", () => { it("wraps local validation failures separately from request failures", async () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -55,7 +54,6 @@ describe("WalletClient errors", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -88,7 +86,6 @@ describe("WalletClient errors", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -128,6 +125,6 @@ function jsonResponse(body: unknown, status = 200): Response { function testEnvironment() { return { walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }; } diff --git a/tests/walletSession.test.ts b/tests/walletSession.test.ts index 032cd29..509136c 100644 --- a/tests/walletSession.test.ts +++ b/tests/walletSession.test.ts @@ -52,7 +52,6 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), credentialSigner: new MockSigner(), @@ -71,7 +70,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -96,7 +94,6 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -156,7 +153,6 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -203,7 +199,6 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -240,7 +235,6 @@ describe("WalletClient session storage", () => { const nextOnSessionExpired = vi.fn(); const nextClient = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -275,7 +269,6 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -304,7 +297,6 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -336,7 +328,6 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -386,7 +377,6 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -416,7 +406,6 @@ describe("WalletClient session storage", () => { const onSessionExpired = vi.fn(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -489,7 +478,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -534,7 +522,6 @@ describe("WalletClient session storage", () => { redirectAuthStorage.set(Constants.redirectAuthStorageKey, JSON.stringify({verifier: "old-verifier"})); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -595,7 +582,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -625,7 +611,6 @@ describe("WalletClient session storage", () => { const client = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -682,7 +667,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -742,7 +726,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -777,7 +760,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -845,7 +827,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -907,7 +888,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -963,7 +943,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1023,7 +1002,6 @@ describe("WalletClient session storage", () => { const storage = new MemoryStorageManager(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -1092,7 +1070,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1135,7 +1112,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1191,7 +1167,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1244,7 +1219,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1290,7 +1264,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1342,7 +1315,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1394,7 +1366,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1446,7 +1417,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1478,7 +1448,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1520,7 +1489,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -1573,7 +1541,6 @@ describe("WalletClient session storage", () => { const storage = new MemoryStorageManager(); const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -1619,7 +1586,6 @@ describe("WalletClient session storage", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -1644,7 +1610,7 @@ describe("WalletClient session storage", () => { function testEnvironment() { return { walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }; } diff --git a/tests/walletSigning.test.ts b/tests/walletSigning.test.ts index c524008..a536900 100644 --- a/tests/walletSigning.test.ts +++ b/tests/walletSigning.test.ts @@ -184,7 +184,6 @@ describe("WalletClient signing", () => { function createWalletWithSession(walletAddress: string): WalletClient { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -204,6 +203,6 @@ function jsonResponse(body: unknown): Response { function testEnvironment() { return { walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }; } diff --git a/tests/walletTransactions.test.ts b/tests/walletTransactions.test.ts index 75863e9..5c4d0ad 100644 --- a/tests/walletTransactions.test.ts +++ b/tests/walletTransactions.test.ts @@ -82,34 +82,36 @@ describe("WalletClient transactions", () => { }); } - if (url.endsWith("/GetNativeTokenBalance")) { - expect(body).toEqual({ - accountAddress: "0x9999999999999999999999999999999999999999", - }); - return jsonResponse({ - balance: { - accountAddress: "0x9999999999999999999999999999999999999999", - balanceWei: "1000000000000000000", - chainId: 137, - }, - }); - } - - if (url.endsWith("/GetTokenBalances")) { + if (url.endsWith("/GetTokenBalancesDetails")) { expect(body).toMatchObject({ - contractAddress: "0x2222222222222222222222222222222222222222", - accountAddress: "0x9999999999999999999999999999999999999999", - includeMetadata: false, + chainIds: [137], + filter: { + accountAddresses: ["0x9999999999999999999999999999999999999999"], + contractWhitelist: ["0x2222222222222222222222222222222222222222"], + omitNativeBalances: false, + }, + omitMetadata: true, }); return jsonResponse({ page: {page: 0, pageSize: 40, more: false}, + nativeBalances: [{ + chainId: 137, + results: [{ + accountAddress: "0x9999999999999999999999999999999999999999", + balance: "1000000000000000000", + chainId: 137, + }], + }], balances: [{ - contractType: "ERC20", - contractAddress: "0x2222222222222222222222222222222222222222", - accountAddress: "0x9999999999999999999999999999999999999999", - tokenID: null, - balance: "2500000", chainId: 137, + results: [{ + contractType: "ERC20", + contractAddress: "0x2222222222222222222222222222222222222222", + accountAddress: "0x9999999999999999999999999999999999999999", + tokenID: null, + balance: "2500000", + chainId: 137, + }], }], }); } @@ -186,7 +188,7 @@ describe("WalletClient transactions", () => { }); } - if (url.endsWith("/GetNativeTokenBalance") || url.endsWith("/GetTokenBalances")) { + if (url.endsWith("/GetTokenBalancesDetails")) { throw new Error("default fee selection should not load balances"); } @@ -324,17 +326,30 @@ describe("WalletClient transactions", () => { }); } - if (url.endsWith("/GetTokenBalances")) { - const balance = body.contractAddress === daiAddress ? "100" : "2000"; + if (url.endsWith("/GetTokenBalancesDetails")) { + expect(body.filter.contractWhitelist).toEqual([daiAddress, usdcAddress]); return jsonResponse({ page: {page: 0, pageSize: 40, more: false}, balances: [{ - contractType: "ERC20", - contractAddress: body.contractAddress, - accountAddress: body.accountAddress, - tokenID: null, - balance, chainId: 137, + results: [ + { + contractType: "ERC20", + contractAddress: daiAddress, + accountAddress: "0x9999999999999999999999999999999999999999", + tokenID: null, + balance: "100", + chainId: 137, + }, + { + contractType: "ERC20", + contractAddress: usdcAddress, + accountAddress: "0x9999999999999999999999999999999999999999", + tokenID: null, + balance: "2000", + chainId: 137, + }, + ], }], }); } @@ -399,16 +414,19 @@ describe("WalletClient transactions", () => { }); } - if (url.endsWith("/GetTokenBalances")) { + if (url.endsWith("/GetTokenBalancesDetails")) { return jsonResponse({ page: {page: 0, pageSize: 40, more: false}, balances: [{ - contractType: "ERC20", - contractAddress: body.contractAddress, - accountAddress: body.accountAddress, - tokenID: null, - balance: "100", chainId: 137, + results: [{ + contractType: "ERC20", + contractAddress: usdcAddress, + accountAddress: "0x9999999999999999999999999999999999999999", + tokenID: null, + balance: "100", + chainId: 137, + }], }], }); } @@ -585,7 +603,6 @@ describe("WalletClient transactions", () => { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage: new MemoryStorageManager(), @@ -645,7 +662,6 @@ describe("WalletClient transactions", () => { function createWalletWithSession(storage: MemoryStorageManager, walletAddress: string): WalletClient { const wallet = new WalletClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: testEnvironment(), storage, @@ -665,6 +681,6 @@ function jsonResponse(body: unknown): Response { function testEnvironment() { return { walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }; } diff --git a/type-tests/oidcProviderTypes.ts b/type-tests/oidcProviderTypes.ts index 1df13fa..cb1d827 100644 --- a/type-tests/oidcProviderTypes.ts +++ b/type-tests/oidcProviderTypes.ts @@ -13,18 +13,22 @@ import { type OmsSdkError, type OmsSdkErrorCode, type OmsUpstreamError, + type BalancesResult, + type GetBalancesParams, + type GetTransactionHistoryParams, + type IndexerNetworkType, type TokenBalance, type TokenBalancesPage, - type TokenBalancesResult, type TokenContractInfo, type TokenMetadata, + type TransactionHistoryResult, } from "../src/index"; import {defineOmsEnvironment, type OmsEnvironment} from "../src/omsEnvironment"; import {googleOidcProvider} from "../src/oidc"; const environment = defineOmsEnvironment({ walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", auth: { oidcProviders: { google: googleOidcProvider(), @@ -71,24 +75,20 @@ if (false) { const defaultClient = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", }); // @ts-expect-error publishableKey is required. -new OMSClient({indexerApiKey: "indexer-api-key", projectId: "project-id"}); -// @ts-expect-error indexerApiKey is required. -new OMSClient({publishableKey: "publishable-key", projectId: "project-id"}); +new OMSClient({projectId: "project-id"}); // @ts-expect-error projectId is required. -new OMSClient({publishableKey: "publishable-key", indexerApiKey: "indexer-api-key"}); +new OMSClient({publishableKey: "publishable-key"}); // @ts-expect-error old projectAccessKey initializer name is not supported. -new OMSClient({projectAccessKey: "publishable-key", indexerApiKey: "indexer-api-key", projectId: "project-id"}); +new OMSClient({projectAccessKey: "publishable-key", projectId: "project-id"}); // @ts-expect-error old publicApiKey initializer name is not supported. -new OMSClient({publicApiKey: "publishable-key", indexerApiKey: "indexer-api-key", projectId: "project-id"}); +new OMSClient({publicApiKey: "publishable-key", projectId: "project-id"}); // @ts-expect-error old authorizationScope initializer name is not supported. -new OMSClient({publishableKey: "publishable-key", indexerApiKey: "indexer-api-key", authorizationScope: "project-id"}); +new OMSClient({publishableKey: "publishable-key", authorizationScope: "project-id"}); new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", // @ts-expect-error session expiry is subscribed through wallet.onSessionExpired, not constructor params. onSessionExpired: () => {}, @@ -119,7 +119,14 @@ const tokenBalance: TokenBalance = { priceUSD: "1", }; const tokenBalancesPage: TokenBalancesPage = {page: 0, pageSize: 40, more: false}; -const tokenBalancesResult: TokenBalancesResult = {status: 200, page: tokenBalancesPage, balances: [tokenBalance]}; +const tokenBalancesResult: BalancesResult = { + status: 200, + page: tokenBalancesPage, + nativeBalances: [tokenBalance], + balances: [tokenBalance], +}; +const indexerNetworkType: IndexerNetworkType = "MAINNETS"; +const transactionHistoryResult: TransactionHistoryResult = {status: 200, page: tokenBalancesPage, transactions: []}; const upstreamError: OmsUpstreamError = { service: "waas", name: "CommitmentConsumed", @@ -140,52 +147,51 @@ void allNetworks; void tokenContractInfo; void tokenMetadata; void tokenBalancesResult; +void indexerNetworkType; +void transactionHistoryResult; void upstreamError; void maybeUpstreamError; void transactionExecutionCode; void defaultClient.supportedNetworks; // @ts-expect-error findNetworkById accepts numeric chain IDs only. findNetworkById("80002"); -void defaultClient.indexer.getTokenBalances({ - network: Networks.polygon, - contractAddress: "0x2222222222222222222222222222222222222222", +void defaultClient.indexer.getBalances({ + networks: [Networks.polygon], + contractAddresses: ["0x2222222222222222222222222222222222222222"], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }); -void defaultClient.indexer.getTokenBalances({ - network: Networks.polygon, +void defaultClient.indexer.getBalances({ + networks: [Networks.polygon], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: true, page: {page: 1, pageSize: 25}, }); -void defaultClient.indexer.getTokenBalances({ +void defaultClient.indexer.getBalances({ // @ts-expect-error Indexer public methods accept Network objects, not numeric chain IDs. - network: 137, - contractAddress: "0x2222222222222222222222222222222222222222", + networks: [137], + contractAddresses: ["0x2222222222222222222222222222222222222222"], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }); -void defaultClient.indexer.getTokenBalances({ +void defaultClient.indexer.getBalances({ // @ts-expect-error chainId is not a public indexer parameter. chainId: 137, - contractAddress: "0x2222222222222222222222222222222222222222", + contractAddresses: ["0x2222222222222222222222222222222222222222"], walletAddress: "0x9999999999999999999999999999999999999999", includeMetadata: false, }); -void defaultClient.indexer.getNativeTokenBalance({ - network: Networks.polygon, - walletAddress: "0x9999999999999999999999999999999999999999", -}); -void defaultClient.indexer.getNativeTokenBalance({ - // @ts-expect-error Indexer public methods accept Network objects, not numeric chain IDs. - network: 137, +const getBalancesParams: GetBalancesParams = { walletAddress: "0x9999999999999999999999999999999999999999", -}); -void defaultClient.indexer.getNativeTokenBalance({ - // @ts-expect-error chainId is not a public indexer parameter. - chainId: 137, + networkType: "TESTNETS", +}; +void defaultClient.indexer.getBalances(getBalancesParams); +const transactionHistoryParams: GetTransactionHistoryParams = { walletAddress: "0x9999999999999999999999999999999999999999", -}); + networks: [Networks.polygon], + includeMetadata: true, +}; +void defaultClient.indexer.getTransactionHistory(transactionHistoryParams); void defaultClient.wallet.startOidcRedirectAuth({ provider: "google", redirectUri: "https://app.example/auth/callback", @@ -198,11 +204,10 @@ void defaultClient.wallet.startOidcRedirectAuth({ const customEnvironmentWithoutProviders = defineOmsEnvironment({ walletApiUrl: "https://wallet.example", - indexerUrlTemplate: "https://indexer.example/{value}", + indexerGatewayUrl: "https://indexer.example", }); const customClient = new OMSClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: customEnvironmentWithoutProviders, }); @@ -217,7 +222,6 @@ void customClient.wallet.startOidcRedirectAuth({ function createClient(params: { publishableKey: string; - indexerApiKey: string; projectId: string; environment?: OmsEnvironment; }) { @@ -226,12 +230,10 @@ function createClient(params: { void createClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", }); void createClient({ publishableKey: "publishable-key", - indexerApiKey: "indexer-api-key", projectId: "project-id", environment: customEnvironmentWithoutProviders, }); From d0543439e5c638c4a332e823a31741073ce73084 Mon Sep 17 00:00:00 2001 From: Tolgahan Date: Thu, 18 Jun 2026 14:09:50 +0300 Subject: [PATCH 4/4] Update generated WaaS client --- src/generated/waas.gen.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/generated/waas.gen.ts b/src/generated/waas.gen.ts index 6d30c21..9ef6a20 100644 --- a/src/generated/waas.gen.ts +++ b/src/generated/waas.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// waas v1-26.5.25-cc1eed0 f038c851ab6eacfa03422c6c4ed3c069c28e78ea +// waas v1-26.6.17-061733f 2279592e720c50a33130cd3fd935b3e2e74ff67d // -- // Code generated by Webrpc-gen@v0.37.2 with typescript generator. DO NOT EDIT. // @@ -9,10 +9,10 @@ export const WebrpcVersion = "v1" // Schema version of your RIDL schema -export const WebrpcSchemaVersion = "v1-26.5.25-cc1eed0" +export const WebrpcSchemaVersion = "v1-26.6.17-061733f" // Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = "f038c851ab6eacfa03422c6c4ed3c069c28e78ea" +export const WebrpcSchemaHash = "2279592e720c50a33130cd3fd935b3e2e74ff67d" // // Client interface @@ -139,7 +139,7 @@ export interface FeeToken { symbol: string type: string decimals?: number - logoURL: string + logoURL?: string contractAddress?: string tokenID?: string } @@ -1432,7 +1432,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { export const WebrpcHeader = "Webrpc" -export const WebrpcHeaderValue = "webrpc@v0.37.2;gen-typescript@v0.26.0;waas@v1-26.5.25-cc1eed0" +export const WebrpcHeaderValue = "webrpc@v0.37.2;gen-typescript@v0.26.0;waas@v1-26.6.17-061733f" type WebrpcGenVersions = { WebrpcGenVersion: string;