@circle-fin/usdckit 0.24.2 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,4 +14,4 @@ exports.VERSION = void 0;
14
14
  /**
15
15
  * The current semantic version of the SDK
16
16
  */
17
- exports.VERSION = `${'0.24.2' || '0.0.0'}`;
17
+ exports.VERSION = `${'0.25.1' || '0.0.0'}`;
@@ -10418,6 +10418,194 @@ export declare const SUPPORTED_CONTRACT_QUERY_CHAINS: readonly [{
10418
10418
  readonly blockchainId: "ARB-SEPOLIA";
10419
10419
  readonly blockchainVm: "EVM";
10420
10420
  readonly formatters?: undefined | undefined;
10421
+ }, {
10422
+ blockExplorers: {
10423
+ readonly default: {
10424
+ readonly name: "ArcScan";
10425
+ readonly url: "https://testnet.arcscan.app";
10426
+ readonly apiUrl: "https://testnet.arcscan.app/api";
10427
+ };
10428
+ };
10429
+ blockTime?: number | undefined | undefined;
10430
+ contracts: {
10431
+ readonly USDC: {
10432
+ readonly address: "0x3600000000000000000000000000000000000000";
10433
+ readonly abi: readonly [{
10434
+ readonly type: "event";
10435
+ readonly name: "Approval";
10436
+ readonly inputs: readonly [{
10437
+ readonly indexed: true;
10438
+ readonly name: "owner";
10439
+ readonly type: "address";
10440
+ }, {
10441
+ readonly indexed: true;
10442
+ readonly name: "spender";
10443
+ readonly type: "address";
10444
+ }, {
10445
+ readonly indexed: false;
10446
+ readonly name: "value";
10447
+ readonly type: "uint256";
10448
+ }];
10449
+ }, {
10450
+ readonly type: "event";
10451
+ readonly name: "Transfer";
10452
+ readonly inputs: readonly [{
10453
+ readonly indexed: true;
10454
+ readonly name: "from";
10455
+ readonly type: "address";
10456
+ }, {
10457
+ readonly indexed: true;
10458
+ readonly name: "to";
10459
+ readonly type: "address";
10460
+ }, {
10461
+ readonly indexed: false;
10462
+ readonly name: "value";
10463
+ readonly type: "uint256";
10464
+ }];
10465
+ }, {
10466
+ readonly type: "function";
10467
+ readonly name: "allowance";
10468
+ readonly stateMutability: "view";
10469
+ readonly inputs: readonly [{
10470
+ readonly name: "owner";
10471
+ readonly type: "address";
10472
+ }, {
10473
+ readonly name: "spender";
10474
+ readonly type: "address";
10475
+ }];
10476
+ readonly outputs: readonly [{
10477
+ readonly type: "uint256";
10478
+ }];
10479
+ }, {
10480
+ readonly type: "function";
10481
+ readonly name: "approve";
10482
+ readonly stateMutability: "nonpayable";
10483
+ readonly inputs: readonly [{
10484
+ readonly name: "spender";
10485
+ readonly type: "address";
10486
+ }, {
10487
+ readonly name: "amount";
10488
+ readonly type: "uint256";
10489
+ }];
10490
+ readonly outputs: readonly [{
10491
+ readonly type: "bool";
10492
+ }];
10493
+ }, {
10494
+ readonly type: "function";
10495
+ readonly name: "balanceOf";
10496
+ readonly stateMutability: "view";
10497
+ readonly inputs: readonly [{
10498
+ readonly name: "account";
10499
+ readonly type: "address";
10500
+ }];
10501
+ readonly outputs: readonly [{
10502
+ readonly type: "uint256";
10503
+ }];
10504
+ }, {
10505
+ readonly type: "function";
10506
+ readonly name: "decimals";
10507
+ readonly stateMutability: "view";
10508
+ readonly inputs: readonly [];
10509
+ readonly outputs: readonly [{
10510
+ readonly type: "uint8";
10511
+ }];
10512
+ }, {
10513
+ readonly type: "function";
10514
+ readonly name: "name";
10515
+ readonly stateMutability: "view";
10516
+ readonly inputs: readonly [];
10517
+ readonly outputs: readonly [{
10518
+ readonly type: "string";
10519
+ }];
10520
+ }, {
10521
+ readonly type: "function";
10522
+ readonly name: "symbol";
10523
+ readonly stateMutability: "view";
10524
+ readonly inputs: readonly [];
10525
+ readonly outputs: readonly [{
10526
+ readonly type: "string";
10527
+ }];
10528
+ }, {
10529
+ readonly type: "function";
10530
+ readonly name: "totalSupply";
10531
+ readonly stateMutability: "view";
10532
+ readonly inputs: readonly [];
10533
+ readonly outputs: readonly [{
10534
+ readonly type: "uint256";
10535
+ }];
10536
+ }, {
10537
+ readonly type: "function";
10538
+ readonly name: "transfer";
10539
+ readonly stateMutability: "nonpayable";
10540
+ readonly inputs: readonly [{
10541
+ readonly name: "recipient";
10542
+ readonly type: "address";
10543
+ }, {
10544
+ readonly name: "amount";
10545
+ readonly type: "uint256";
10546
+ }];
10547
+ readonly outputs: readonly [{
10548
+ readonly type: "bool";
10549
+ }];
10550
+ }, {
10551
+ readonly type: "function";
10552
+ readonly name: "transferFrom";
10553
+ readonly stateMutability: "nonpayable";
10554
+ readonly inputs: readonly [{
10555
+ readonly name: "sender";
10556
+ readonly type: "address";
10557
+ }, {
10558
+ readonly name: "recipient";
10559
+ readonly type: "address";
10560
+ }, {
10561
+ readonly name: "amount";
10562
+ readonly type: "uint256";
10563
+ }];
10564
+ readonly outputs: readonly [{
10565
+ readonly type: "bool";
10566
+ }];
10567
+ }];
10568
+ readonly read: {
10569
+ readonly decimals: () => 6;
10570
+ };
10571
+ };
10572
+ readonly multicall3: {
10573
+ readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11";
10574
+ readonly blockCreated: 0;
10575
+ };
10576
+ };
10577
+ ensTlds?: readonly string[] | undefined;
10578
+ id: 5042002;
10579
+ name: "Arc Testnet";
10580
+ nativeCurrency: {
10581
+ readonly name: "USDC";
10582
+ readonly symbol: "USDC";
10583
+ readonly decimals: 18;
10584
+ };
10585
+ experimental_preconfirmationTime?: number | undefined | undefined;
10586
+ rpcUrls: {
10587
+ readonly default: {
10588
+ readonly http: readonly ["https://rpc.testnet.arc.network", "https://rpc.quicknode.testnet.arc.network", "https://rpc.blockdaemon.testnet.arc.network"];
10589
+ readonly webSocket: readonly ["wss://rpc.testnet.arc.network", "wss://rpc.quicknode.testnet.arc.network"];
10590
+ };
10591
+ };
10592
+ sourceId?: number | undefined | undefined;
10593
+ testnet: true;
10594
+ custom?: Record<string, unknown> | undefined;
10595
+ extendSchema?: Record<string, unknown> | undefined;
10596
+ fees?: import("viem").ChainFees<undefined> | undefined;
10597
+ prepareTransactionRequest?: ((args: import("viem").PrepareTransactionRequestParameters, options: {
10598
+ phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
10599
+ }) => Promise<import("viem").PrepareTransactionRequestParameters>) | [fn: ((args: import("viem").PrepareTransactionRequestParameters, options: {
10600
+ phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
10601
+ }) => Promise<import("viem").PrepareTransactionRequestParameters>) | undefined, options: {
10602
+ runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
10603
+ }] | undefined;
10604
+ serializers?: import("viem").ChainSerializers<undefined, import("viem").TransactionSerializable<bigint, number>> | undefined;
10605
+ verifyHash?: ((client: import("viem").Client, parameters: import("viem").VerifyHashActionParameters) => Promise<import("viem").VerifyHashActionReturnType>) | undefined;
10606
+ readonly blockchainId: "ARC-TESTNET";
10607
+ readonly blockchainVm: "EVM";
10608
+ readonly formatters?: undefined | undefined;
10421
10609
  }, {
10422
10610
  blockExplorers: {
10423
10611
  readonly default: {
@@ -73,6 +73,7 @@ exports.SUPPORTED_CONTRACT_QUERY_CHAINS = [
73
73
  index_js_1.MATIC_AMOY,
74
74
  index_js_1.ARB,
75
75
  index_js_1.ARB_SEPOLIA,
76
+ index_js_1.ARC_TESTNET,
76
77
  index_js_1.UNI,
77
78
  index_js_1.UNI_SEPOLIA,
78
79
  index_js_1.BASE,
@@ -120,17 +120,47 @@ class Provider extends index_js_2.Provider {
120
120
  }
121
121
  async eth_call(params, blockIdentifier = 'latest') {
122
122
  // NOTE: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call
123
- // In JSON-RPC `eth_call` spec, blockIdentifier is a required parameter.
124
- // It is a hex string of the block number, 'latest', 'earliest', 'pending', 'safe' or 'finalized'.
125
- // Although Circle's query contract API does not support it, we still add it to be compatible with the JSON-RPC spec.
123
+ // Circle's query contract API has a narrow surface (blockchain, to, data, from). For anything
124
+ // it cannot represent, throw `MethodNotFoundRpcError` so `Provider.request` falls back to the
125
+ // embedded viem RPC transport instead of hard-failing the caller. This matters in particular
126
+ // for viem-based adapters (e.g. the Circle Wallets adapter's pre-flight `publicClient.call`
127
+ // during a swap), which forward `gas`/`value` into `eth_call` as a matter of course.
126
128
  if (blockIdentifier !== 'latest') {
127
- throw new index_js_1.ErrorWithCause('Only `latest` block identifier is supported');
129
+ throw new viem_1.MethodNotFoundRpcError(new index_js_1.ErrorWithCause('Only `latest` block identifier is supported by Circle queryContract'), {
130
+ method: 'eth_call',
131
+ });
132
+ }
133
+ // gas / gasPrice / maxFeePerGas / maxPriorityFeePerGas are EVM-visible during the target
134
+ // call: `gas` bounds `gasleft()` and affects out-of-gas behavior; the fee fields determine
135
+ // `tx.gasprice`, which contracts can read. Circle's queryContract cannot thread these
136
+ // through, so surface `MethodNotFoundRpcError` to trigger fallback rather than silently
137
+ // returning an SCP result that ignored the caller's explicit simulation constraints.
138
+ const gasPricingParams = ['gas', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas'];
139
+ if (gasPricingParams.some((param) => params[param] !== undefined)) {
140
+ throw new viem_1.MethodNotFoundRpcError(new index_js_1.ErrorWithCause('gas / gasPrice / maxFeePerGas / maxPriorityFeePerGas are not supported by Circle queryContract'), { method: 'eth_call' });
141
+ }
142
+ // `nonce` is transaction-level and not EVM-visible inside eth_call, so silently drop it —
143
+ // forwarding would be a no-op and throwing would break callers that forward nonce for parity.
144
+ // `value` is the one remaining param with read semantics (payable view calls). `0x0` / `0n`
145
+ // mean "no native value" and are accepted; anything else cannot be expressed through
146
+ // queryContract, so fall back to the RPC transport. Accept both the typed `bigint` and the
147
+ // runtime JSON-RPC `Hex` shape (viem's `TransactionRequest.value` is typed `bigint`, but
148
+ // transports deliver hex).
149
+ const rawValue = params.value;
150
+ let valueAsBigInt;
151
+ if (rawValue === undefined) {
152
+ valueAsBigInt = 0n;
128
153
  }
129
- // Since Circle's query contract API does not support some transaction params,
130
- // to prevent misuse, we throw an error if they are provided.
131
- const unsupportedTransactionParams = ['gas', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'nonce', 'value'];
132
- if (unsupportedTransactionParams.some((param) => params[param] !== undefined)) {
133
- throw new index_js_1.ErrorWithCause(`Unsupported transaction params: ${unsupportedTransactionParams.join(', ')}`);
154
+ else if (typeof rawValue === 'bigint') {
155
+ valueAsBigInt = rawValue;
156
+ }
157
+ else {
158
+ valueAsBigInt = (0, viem_1.hexToBigInt)(rawValue);
159
+ }
160
+ if (valueAsBigInt !== 0n) {
161
+ throw new viem_1.MethodNotFoundRpcError(new index_js_1.ErrorWithCause('Non-zero `value` is not supported by Circle queryContract'), {
162
+ method: 'eth_call',
163
+ });
134
164
  }
135
165
  // Provider's chainId is used to determine the blockchain
136
166
  // When the chain is not supported by Circle's query contract API, throw MethodNotFoundRpcError to fallback.
@@ -1886,12 +1886,21 @@ vitest_1.vi.mock('@circle-fin/developer-controlled-wallets', () => ({
1886
1886
  });
1887
1887
  });
1888
1888
  (0, vitest_1.describe)('eth_call', () => {
1889
+ // Build a provider with `fallbackTransport: null` so the inner `eth_call` errors
1890
+ // surface raw instead of being silently retried on the fallback RPC transport.
1891
+ // The production default IS to fall back; these tests pin the inner contract.
1892
+ const makeProviderWithoutFallback = (chainId) => new index_js_4.Provider({
1893
+ apiKey: 'test-api-key',
1894
+ entitySecret: 'test-entity-secret',
1895
+ chainId,
1896
+ fallbackTransport: null,
1897
+ });
1898
+ const baseParams = {
1899
+ to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1900
+ data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d0c8c0c8d8d8d8d8',
1901
+ from: '0x123456789abcdef123456789abcdef123456789a',
1902
+ };
1889
1903
  (0, vitest_1.it)('should perform contract query successfully', async () => {
1890
- const params = {
1891
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1892
- data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d0c8c0c8d8d8d8d8',
1893
- from: '0x123456789abcdef123456789abcdef123456789a',
1894
- };
1895
1904
  mockScp.queryContract.mockResolvedValue({
1896
1905
  data: {
1897
1906
  outputData: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
@@ -1899,36 +1908,18 @@ vitest_1.vi.mock('@circle-fin/developer-controlled-wallets', () => ({
1899
1908
  });
1900
1909
  const result = await provider.request({
1901
1910
  method: 'eth_call',
1902
- params: [params],
1911
+ params: [baseParams],
1903
1912
  });
1904
1913
  (0, vitest_1.expect)(result).toBe('0x0000000000000000000000000000000000000000000000000de0b6b3a7640000');
1905
1914
  (0, vitest_1.expect)(mockScp.queryContract).toHaveBeenCalledWith({
1906
1915
  blockchain: 'ETH',
1907
- address: params.to,
1908
- callData: params.data,
1909
- fromAddress: params.from,
1916
+ address: baseParams.to,
1917
+ callData: baseParams.data,
1918
+ fromAddress: baseParams.from,
1910
1919
  });
1911
1920
  });
1912
- (0, vitest_1.it)('should throw UnsupportedChainError when chainId is not supported for contract queries', async () => {
1913
- const provider = new index_js_4.Provider({
1914
- apiKey: 'test-api-key',
1915
- entitySecret: 'test-entity-secret',
1916
- chainId: 999999, // Unsupported chain
1917
- });
1918
- const params = {
1919
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1920
- data: '0x70a08231',
1921
- };
1922
- await (0, vitest_1.expect)(provider.request({
1923
- method: 'eth_call',
1924
- params: [params],
1925
- })).rejects.toThrow();
1926
- });
1927
1921
  (0, vitest_1.it)('should work without from parameter', async () => {
1928
- const params = {
1929
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1930
- data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d0c8c0c8d8d8d8d8',
1931
- };
1922
+ const params = { to: baseParams.to, data: baseParams.data };
1932
1923
  mockScp.queryContract.mockResolvedValue({
1933
1924
  data: {
1934
1925
  outputData: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
@@ -1946,52 +1937,111 @@ vitest_1.vi.mock('@circle-fin/developer-controlled-wallets', () => ({
1946
1937
  fromAddress: undefined,
1947
1938
  });
1948
1939
  });
1949
- (0, vitest_1.it)('should throw error when blockIdentifier is not "latest"', async () => {
1950
- const params = {
1951
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1952
- data: '0x70a08231',
1953
- };
1940
+ // gas / gasPrice / maxFeePerGas / maxPriorityFeePerGas are EVM-visible during the call:
1941
+ // `gas` affects `gasleft()` and the fee fields determine `tx.gasprice`. Circle's
1942
+ // queryContract cannot represent them, so surface MethodNotFoundRpcError to trigger
1943
+ // fallback rather than silently returning an SCP result that ignored the constraints.
1944
+ vitest_1.it.each([
1945
+ { label: 'gas', param: { gas: (0, viem_1.numberToHex)(1000000n) } },
1946
+ { label: 'gasPrice', param: { gasPrice: (0, viem_1.numberToHex)((0, viem_1.parseEther)('0.00000001')) } },
1947
+ { label: 'maxFeePerGas', param: { maxFeePerGas: (0, viem_1.numberToHex)((0, viem_1.parseEther)('0.00000001')) } },
1948
+ {
1949
+ label: 'maxPriorityFeePerGas',
1950
+ param: { maxPriorityFeePerGas: (0, viem_1.numberToHex)((0, viem_1.parseEther)('0.000000001')) },
1951
+ },
1952
+ ])('should throw MethodNotFoundRpcError when $label is set (fallback trigger)', async ({ param }) => {
1953
+ const provider = makeProviderWithoutFallback(1);
1954
1954
  await (0, vitest_1.expect)(provider.request({
1955
1955
  method: 'eth_call',
1956
- params: [params, 'earliest'],
1957
- })).rejects.toThrow('Only `latest` block identifier is supported');
1956
+ params: [{ ...baseParams, ...param }],
1957
+ })).rejects.toBeInstanceOf(viem_1.MethodNotFoundRpcError);
1958
+ (0, vitest_1.expect)(mockScp.queryContract).not.toHaveBeenCalled();
1959
+ });
1960
+ // `nonce` is transaction-level and not visible inside eth_call, so it's fine to silently
1961
+ // drop — forwarding would be a no-op and throwing would break callers (e.g. viem's
1962
+ // publicClient.call) that may surface nonce for parity with standard JSON-RPC shapes.
1963
+ (0, vitest_1.it)('should silently ignore nonce', async () => {
1964
+ mockScp.queryContract.mockResolvedValue({ data: { outputData: '0xok' } });
1965
+ const result = await provider.request({
1966
+ method: 'eth_call',
1967
+ params: [{ ...baseParams, nonce: (0, viem_1.numberToHex)(42n) }],
1968
+ });
1969
+ (0, vitest_1.expect)(result).toBe('0xok');
1970
+ // nonce must not be forwarded — queryContract only accepts {blockchain, to, data, from}.
1971
+ (0, vitest_1.expect)(mockScp.queryContract).toHaveBeenCalledWith({
1972
+ blockchain: 'ETH',
1973
+ address: baseParams.to,
1974
+ callData: baseParams.data,
1975
+ fromAddress: baseParams.from,
1976
+ });
1977
+ });
1978
+ // `value` arrives as a `Hex` at runtime from JSON-RPC transports, but the
1979
+ // TypeScript surface (`ExactPartial<TransactionRequest>`) admits `bigint`.
1980
+ // Exercise both shapes so the dual-type contract is regression-tested.
1981
+ vitest_1.it.each([
1982
+ { label: 'hex 0x0', value: '0x0' },
1983
+ { label: 'bigint 0n', value: 0n },
1984
+ ])('should accept zero value ($label) as equivalent to no native value', async ({ value }) => {
1985
+ mockScp.queryContract.mockResolvedValue({ data: { outputData: '0xzero' } });
1986
+ const result = await provider.request({
1987
+ method: 'eth_call',
1988
+ params: [{ ...baseParams, value }],
1989
+ });
1990
+ (0, vitest_1.expect)(result).toBe('0xzero');
1991
+ (0, vitest_1.expect)(mockScp.queryContract).toHaveBeenCalledTimes(1);
1992
+ });
1993
+ // `value` is the one param with read semantics (payable view calls). Non-zero must
1994
+ // surface `MethodNotFoundRpcError` so `Provider.request` falls back to the RPC transport.
1995
+ // Again, cover both the hex and bigint shapes.
1996
+ vitest_1.it.each([
1997
+ { label: 'hex 0x1', value: (0, viem_1.numberToHex)(1n) },
1998
+ { label: 'bigint 1n', value: 1n },
1999
+ ])('should throw MethodNotFoundRpcError for non-zero value ($label) (fallback trigger)', async ({ value }) => {
2000
+ const provider = makeProviderWithoutFallback(1);
1958
2001
  await (0, vitest_1.expect)(provider.request({
1959
2002
  method: 'eth_call',
1960
- params: [params, '0x1234'],
1961
- })).rejects.toThrow('Only `latest` block identifier is supported');
2003
+ params: [{ ...baseParams, value }],
2004
+ })).rejects.toBeInstanceOf(viem_1.MethodNotFoundRpcError);
2005
+ (0, vitest_1.expect)(mockScp.queryContract).not.toHaveBeenCalled();
1962
2006
  });
1963
- (0, vitest_1.it)('should throw error when unsupported transaction params are provided', async () => {
1964
- const baseParams = {
1965
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1966
- data: '0x70a08231',
1967
- };
1968
- // Test each unsupported parameter individually
1969
- const unsupportedParams = [
1970
- { gas: '0x186A0' },
1971
- { gasPrice: '0x3B9ACA00' },
1972
- { maxFeePerGas: '0x3B9ACA00' },
1973
- { maxPriorityFeePerGas: '0x3B9ACA00' },
1974
- { nonce: '0x1' },
1975
- { value: '0x0' },
1976
- ];
1977
- for (const param of unsupportedParams) {
1978
- const testParams = { ...baseParams, ...param };
1979
- await (0, vitest_1.expect)(provider.request({
1980
- method: 'eth_call',
1981
- params: [testParams],
1982
- })).rejects.toThrow();
1983
- }
1984
- // Test multiple unsupported params together
1985
- const multipleUnsupportedParams = {
1986
- ...baseParams,
1987
- gas: '0x186A0',
1988
- value: '0x0',
1989
- nonce: '0x1',
1990
- };
2007
+ (0, vitest_1.it)('should throw MethodNotFoundRpcError when blockIdentifier is not "latest" (fallback trigger)', async () => {
2008
+ const provider = makeProviderWithoutFallback(1);
1991
2009
  await (0, vitest_1.expect)(provider.request({
1992
2010
  method: 'eth_call',
1993
- params: [multipleUnsupportedParams],
1994
- })).rejects.toThrow();
2011
+ params: [baseParams, 'earliest'],
2012
+ })).rejects.toBeInstanceOf(viem_1.MethodNotFoundRpcError);
2013
+ await (0, vitest_1.expect)(provider.request({
2014
+ method: 'eth_call',
2015
+ params: [baseParams, '0x1234'],
2016
+ })).rejects.toBeInstanceOf(viem_1.MethodNotFoundRpcError);
2017
+ (0, vitest_1.expect)(mockScp.queryContract).not.toHaveBeenCalled();
2018
+ });
2019
+ // AVAX_FUJI (43113) is in SUPPORTED_CHAINS but NOT SUPPORTED_CONTRACT_QUERY_CHAINS.
2020
+ (0, vitest_1.it)('should throw MethodNotFoundRpcError when chain is not supported by queryContract (fallback trigger)', async () => {
2021
+ const provider = makeProviderWithoutFallback(43113);
2022
+ await (0, vitest_1.expect)(provider.request({ method: 'eth_call', params: [baseParams] })).rejects.toBeInstanceOf(viem_1.MethodNotFoundRpcError);
2023
+ (0, vitest_1.expect)(mockScp.queryContract).not.toHaveBeenCalled();
2024
+ });
2025
+ // ARC_TESTNET (5042002) IS in SUPPORTED_CONTRACT_QUERY_CHAINS — reads flow through Circle's SCP.
2026
+ (0, vitest_1.it)('should route Arc Testnet eth_call through queryContract', async () => {
2027
+ // `vi.restoreAllMocks` in the outer beforeEach clears the module-level mock
2028
+ // return; re-prime it before constructing a second Provider for Arc Testnet.
2029
+ ;
2030
+ smart_contract_platform_1.initiateSmartContractPlatformClient.mockReturnValue(mockScp);
2031
+ const arcProvider = new index_js_4.Provider({
2032
+ apiKey: 'test-api-key',
2033
+ entitySecret: 'test-entity-secret',
2034
+ chainId: 5042002, // ARC_TESTNET
2035
+ });
2036
+ mockScp.queryContract.mockResolvedValue({ data: { outputData: '0xarc' } });
2037
+ const result = await arcProvider.request({ method: 'eth_call', params: [baseParams] });
2038
+ (0, vitest_1.expect)(result).toBe('0xarc');
2039
+ (0, vitest_1.expect)(mockScp.queryContract).toHaveBeenCalledWith({
2040
+ blockchain: 'ARC-TESTNET',
2041
+ address: baseParams.to,
2042
+ callData: baseParams.data,
2043
+ fromAddress: baseParams.from,
2044
+ });
1995
2045
  });
1996
2046
  });
1997
2047
  (0, vitest_1.describe)('eth_estimateGas', () => {
@@ -11,4 +11,4 @@
11
11
  /**
12
12
  * The current semantic version of the SDK
13
13
  */
14
- export const VERSION = `${'0.24.2' || '0.0.0'}`;
14
+ export const VERSION = `${'0.25.1' || '0.0.0'}`;
@@ -10418,6 +10418,194 @@ export declare const SUPPORTED_CONTRACT_QUERY_CHAINS: readonly [{
10418
10418
  readonly blockchainId: "ARB-SEPOLIA";
10419
10419
  readonly blockchainVm: "EVM";
10420
10420
  readonly formatters?: undefined | undefined;
10421
+ }, {
10422
+ blockExplorers: {
10423
+ readonly default: {
10424
+ readonly name: "ArcScan";
10425
+ readonly url: "https://testnet.arcscan.app";
10426
+ readonly apiUrl: "https://testnet.arcscan.app/api";
10427
+ };
10428
+ };
10429
+ blockTime?: number | undefined | undefined;
10430
+ contracts: {
10431
+ readonly USDC: {
10432
+ readonly address: "0x3600000000000000000000000000000000000000";
10433
+ readonly abi: readonly [{
10434
+ readonly type: "event";
10435
+ readonly name: "Approval";
10436
+ readonly inputs: readonly [{
10437
+ readonly indexed: true;
10438
+ readonly name: "owner";
10439
+ readonly type: "address";
10440
+ }, {
10441
+ readonly indexed: true;
10442
+ readonly name: "spender";
10443
+ readonly type: "address";
10444
+ }, {
10445
+ readonly indexed: false;
10446
+ readonly name: "value";
10447
+ readonly type: "uint256";
10448
+ }];
10449
+ }, {
10450
+ readonly type: "event";
10451
+ readonly name: "Transfer";
10452
+ readonly inputs: readonly [{
10453
+ readonly indexed: true;
10454
+ readonly name: "from";
10455
+ readonly type: "address";
10456
+ }, {
10457
+ readonly indexed: true;
10458
+ readonly name: "to";
10459
+ readonly type: "address";
10460
+ }, {
10461
+ readonly indexed: false;
10462
+ readonly name: "value";
10463
+ readonly type: "uint256";
10464
+ }];
10465
+ }, {
10466
+ readonly type: "function";
10467
+ readonly name: "allowance";
10468
+ readonly stateMutability: "view";
10469
+ readonly inputs: readonly [{
10470
+ readonly name: "owner";
10471
+ readonly type: "address";
10472
+ }, {
10473
+ readonly name: "spender";
10474
+ readonly type: "address";
10475
+ }];
10476
+ readonly outputs: readonly [{
10477
+ readonly type: "uint256";
10478
+ }];
10479
+ }, {
10480
+ readonly type: "function";
10481
+ readonly name: "approve";
10482
+ readonly stateMutability: "nonpayable";
10483
+ readonly inputs: readonly [{
10484
+ readonly name: "spender";
10485
+ readonly type: "address";
10486
+ }, {
10487
+ readonly name: "amount";
10488
+ readonly type: "uint256";
10489
+ }];
10490
+ readonly outputs: readonly [{
10491
+ readonly type: "bool";
10492
+ }];
10493
+ }, {
10494
+ readonly type: "function";
10495
+ readonly name: "balanceOf";
10496
+ readonly stateMutability: "view";
10497
+ readonly inputs: readonly [{
10498
+ readonly name: "account";
10499
+ readonly type: "address";
10500
+ }];
10501
+ readonly outputs: readonly [{
10502
+ readonly type: "uint256";
10503
+ }];
10504
+ }, {
10505
+ readonly type: "function";
10506
+ readonly name: "decimals";
10507
+ readonly stateMutability: "view";
10508
+ readonly inputs: readonly [];
10509
+ readonly outputs: readonly [{
10510
+ readonly type: "uint8";
10511
+ }];
10512
+ }, {
10513
+ readonly type: "function";
10514
+ readonly name: "name";
10515
+ readonly stateMutability: "view";
10516
+ readonly inputs: readonly [];
10517
+ readonly outputs: readonly [{
10518
+ readonly type: "string";
10519
+ }];
10520
+ }, {
10521
+ readonly type: "function";
10522
+ readonly name: "symbol";
10523
+ readonly stateMutability: "view";
10524
+ readonly inputs: readonly [];
10525
+ readonly outputs: readonly [{
10526
+ readonly type: "string";
10527
+ }];
10528
+ }, {
10529
+ readonly type: "function";
10530
+ readonly name: "totalSupply";
10531
+ readonly stateMutability: "view";
10532
+ readonly inputs: readonly [];
10533
+ readonly outputs: readonly [{
10534
+ readonly type: "uint256";
10535
+ }];
10536
+ }, {
10537
+ readonly type: "function";
10538
+ readonly name: "transfer";
10539
+ readonly stateMutability: "nonpayable";
10540
+ readonly inputs: readonly [{
10541
+ readonly name: "recipient";
10542
+ readonly type: "address";
10543
+ }, {
10544
+ readonly name: "amount";
10545
+ readonly type: "uint256";
10546
+ }];
10547
+ readonly outputs: readonly [{
10548
+ readonly type: "bool";
10549
+ }];
10550
+ }, {
10551
+ readonly type: "function";
10552
+ readonly name: "transferFrom";
10553
+ readonly stateMutability: "nonpayable";
10554
+ readonly inputs: readonly [{
10555
+ readonly name: "sender";
10556
+ readonly type: "address";
10557
+ }, {
10558
+ readonly name: "recipient";
10559
+ readonly type: "address";
10560
+ }, {
10561
+ readonly name: "amount";
10562
+ readonly type: "uint256";
10563
+ }];
10564
+ readonly outputs: readonly [{
10565
+ readonly type: "bool";
10566
+ }];
10567
+ }];
10568
+ readonly read: {
10569
+ readonly decimals: () => 6;
10570
+ };
10571
+ };
10572
+ readonly multicall3: {
10573
+ readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11";
10574
+ readonly blockCreated: 0;
10575
+ };
10576
+ };
10577
+ ensTlds?: readonly string[] | undefined;
10578
+ id: 5042002;
10579
+ name: "Arc Testnet";
10580
+ nativeCurrency: {
10581
+ readonly name: "USDC";
10582
+ readonly symbol: "USDC";
10583
+ readonly decimals: 18;
10584
+ };
10585
+ experimental_preconfirmationTime?: number | undefined | undefined;
10586
+ rpcUrls: {
10587
+ readonly default: {
10588
+ readonly http: readonly ["https://rpc.testnet.arc.network", "https://rpc.quicknode.testnet.arc.network", "https://rpc.blockdaemon.testnet.arc.network"];
10589
+ readonly webSocket: readonly ["wss://rpc.testnet.arc.network", "wss://rpc.quicknode.testnet.arc.network"];
10590
+ };
10591
+ };
10592
+ sourceId?: number | undefined | undefined;
10593
+ testnet: true;
10594
+ custom?: Record<string, unknown> | undefined;
10595
+ extendSchema?: Record<string, unknown> | undefined;
10596
+ fees?: import("viem").ChainFees<undefined> | undefined;
10597
+ prepareTransactionRequest?: ((args: import("viem").PrepareTransactionRequestParameters, options: {
10598
+ phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
10599
+ }) => Promise<import("viem").PrepareTransactionRequestParameters>) | [fn: ((args: import("viem").PrepareTransactionRequestParameters, options: {
10600
+ phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
10601
+ }) => Promise<import("viem").PrepareTransactionRequestParameters>) | undefined, options: {
10602
+ runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
10603
+ }] | undefined;
10604
+ serializers?: import("viem").ChainSerializers<undefined, import("viem").TransactionSerializable<bigint, number>> | undefined;
10605
+ verifyHash?: ((client: import("viem").Client, parameters: import("viem").VerifyHashActionParameters) => Promise<import("viem").VerifyHashActionReturnType>) | undefined;
10606
+ readonly blockchainId: "ARC-TESTNET";
10607
+ readonly blockchainVm: "EVM";
10608
+ readonly formatters?: undefined | undefined;
10421
10609
  }, {
10422
10610
  blockExplorers: {
10423
10611
  readonly default: {
@@ -52,6 +52,7 @@ export const SUPPORTED_CONTRACT_QUERY_CHAINS = [
52
52
  MATIC_AMOY,
53
53
  ARB,
54
54
  ARB_SEPOLIA,
55
+ ARC_TESTNET,
55
56
  UNI,
56
57
  UNI_SEPOLIA,
57
58
  BASE,
@@ -116,17 +116,47 @@ export class Provider extends BaseProvider {
116
116
  }
117
117
  async eth_call(params, blockIdentifier = 'latest') {
118
118
  // NOTE: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call
119
- // In JSON-RPC `eth_call` spec, blockIdentifier is a required parameter.
120
- // It is a hex string of the block number, 'latest', 'earliest', 'pending', 'safe' or 'finalized'.
121
- // Although Circle's query contract API does not support it, we still add it to be compatible with the JSON-RPC spec.
119
+ // Circle's query contract API has a narrow surface (blockchain, to, data, from). For anything
120
+ // it cannot represent, throw `MethodNotFoundRpcError` so `Provider.request` falls back to the
121
+ // embedded viem RPC transport instead of hard-failing the caller. This matters in particular
122
+ // for viem-based adapters (e.g. the Circle Wallets adapter's pre-flight `publicClient.call`
123
+ // during a swap), which forward `gas`/`value` into `eth_call` as a matter of course.
122
124
  if (blockIdentifier !== 'latest') {
123
- throw new Error('Only `latest` block identifier is supported');
125
+ throw new MethodNotFoundRpcError(new Error('Only `latest` block identifier is supported by Circle queryContract'), {
126
+ method: 'eth_call',
127
+ });
128
+ }
129
+ // gas / gasPrice / maxFeePerGas / maxPriorityFeePerGas are EVM-visible during the target
130
+ // call: `gas` bounds `gasleft()` and affects out-of-gas behavior; the fee fields determine
131
+ // `tx.gasprice`, which contracts can read. Circle's queryContract cannot thread these
132
+ // through, so surface `MethodNotFoundRpcError` to trigger fallback rather than silently
133
+ // returning an SCP result that ignored the caller's explicit simulation constraints.
134
+ const gasPricingParams = ['gas', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas'];
135
+ if (gasPricingParams.some((param) => params[param] !== undefined)) {
136
+ throw new MethodNotFoundRpcError(new Error('gas / gasPrice / maxFeePerGas / maxPriorityFeePerGas are not supported by Circle queryContract'), { method: 'eth_call' });
137
+ }
138
+ // `nonce` is transaction-level and not EVM-visible inside eth_call, so silently drop it —
139
+ // forwarding would be a no-op and throwing would break callers that forward nonce for parity.
140
+ // `value` is the one remaining param with read semantics (payable view calls). `0x0` / `0n`
141
+ // mean "no native value" and are accepted; anything else cannot be expressed through
142
+ // queryContract, so fall back to the RPC transport. Accept both the typed `bigint` and the
143
+ // runtime JSON-RPC `Hex` shape (viem's `TransactionRequest.value` is typed `bigint`, but
144
+ // transports deliver hex).
145
+ const rawValue = params.value;
146
+ let valueAsBigInt;
147
+ if (rawValue === undefined) {
148
+ valueAsBigInt = 0n;
124
149
  }
125
- // Since Circle's query contract API does not support some transaction params,
126
- // to prevent misuse, we throw an error if they are provided.
127
- const unsupportedTransactionParams = ['gas', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'nonce', 'value'];
128
- if (unsupportedTransactionParams.some((param) => params[param] !== undefined)) {
129
- throw new Error(`Unsupported transaction params: ${unsupportedTransactionParams.join(', ')}`);
150
+ else if (typeof rawValue === 'bigint') {
151
+ valueAsBigInt = rawValue;
152
+ }
153
+ else {
154
+ valueAsBigInt = hexToBigInt(rawValue);
155
+ }
156
+ if (valueAsBigInt !== 0n) {
157
+ throw new MethodNotFoundRpcError(new Error('Non-zero `value` is not supported by Circle queryContract'), {
158
+ method: 'eth_call',
159
+ });
130
160
  }
131
161
  // Provider's chainId is used to determine the blockchain
132
162
  // When the chain is not supported by Circle's query contract API, throw MethodNotFoundRpcError to fallback.
@@ -1851,12 +1851,21 @@ describe('Provider', () => {
1851
1851
  });
1852
1852
  });
1853
1853
  describe('eth_call', () => {
1854
+ // Build a provider with `fallbackTransport: null` so the inner `eth_call` errors
1855
+ // surface raw instead of being silently retried on the fallback RPC transport.
1856
+ // The production default IS to fall back; these tests pin the inner contract.
1857
+ const makeProviderWithoutFallback = (chainId) => new Provider({
1858
+ apiKey: 'test-api-key',
1859
+ entitySecret: 'test-entity-secret',
1860
+ chainId,
1861
+ fallbackTransport: null,
1862
+ });
1863
+ const baseParams = {
1864
+ to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1865
+ data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d0c8c0c8d8d8d8d8',
1866
+ from: '0x123456789abcdef123456789abcdef123456789a',
1867
+ };
1854
1868
  it('should perform contract query successfully', async () => {
1855
- const params = {
1856
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1857
- data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d0c8c0c8d8d8d8d8',
1858
- from: '0x123456789abcdef123456789abcdef123456789a',
1859
- };
1860
1869
  mockScp.queryContract.mockResolvedValue({
1861
1870
  data: {
1862
1871
  outputData: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
@@ -1864,36 +1873,18 @@ describe('Provider', () => {
1864
1873
  });
1865
1874
  const result = await provider.request({
1866
1875
  method: 'eth_call',
1867
- params: [params],
1876
+ params: [baseParams],
1868
1877
  });
1869
1878
  expect(result).toBe('0x0000000000000000000000000000000000000000000000000de0b6b3a7640000');
1870
1879
  expect(mockScp.queryContract).toHaveBeenCalledWith({
1871
1880
  blockchain: 'ETH',
1872
- address: params.to,
1873
- callData: params.data,
1874
- fromAddress: params.from,
1881
+ address: baseParams.to,
1882
+ callData: baseParams.data,
1883
+ fromAddress: baseParams.from,
1875
1884
  });
1876
1885
  });
1877
- it('should throw UnsupportedChainError when chainId is not supported for contract queries', async () => {
1878
- const provider = new Provider({
1879
- apiKey: 'test-api-key',
1880
- entitySecret: 'test-entity-secret',
1881
- chainId: 999999, // Unsupported chain
1882
- });
1883
- const params = {
1884
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1885
- data: '0x70a08231',
1886
- };
1887
- await expect(provider.request({
1888
- method: 'eth_call',
1889
- params: [params],
1890
- })).rejects.toThrow();
1891
- });
1892
1886
  it('should work without from parameter', async () => {
1893
- const params = {
1894
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1895
- data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d0c8c0c8d8d8d8d8',
1896
- };
1887
+ const params = { to: baseParams.to, data: baseParams.data };
1897
1888
  mockScp.queryContract.mockResolvedValue({
1898
1889
  data: {
1899
1890
  outputData: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
@@ -1911,52 +1902,111 @@ describe('Provider', () => {
1911
1902
  fromAddress: undefined,
1912
1903
  });
1913
1904
  });
1914
- it('should throw error when blockIdentifier is not "latest"', async () => {
1915
- const params = {
1916
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1917
- data: '0x70a08231',
1918
- };
1905
+ // gas / gasPrice / maxFeePerGas / maxPriorityFeePerGas are EVM-visible during the call:
1906
+ // `gas` affects `gasleft()` and the fee fields determine `tx.gasprice`. Circle's
1907
+ // queryContract cannot represent them, so surface MethodNotFoundRpcError to trigger
1908
+ // fallback rather than silently returning an SCP result that ignored the constraints.
1909
+ it.each([
1910
+ { label: 'gas', param: { gas: numberToHex(1000000n) } },
1911
+ { label: 'gasPrice', param: { gasPrice: numberToHex(parseEther('0.00000001')) } },
1912
+ { label: 'maxFeePerGas', param: { maxFeePerGas: numberToHex(parseEther('0.00000001')) } },
1913
+ {
1914
+ label: 'maxPriorityFeePerGas',
1915
+ param: { maxPriorityFeePerGas: numberToHex(parseEther('0.000000001')) },
1916
+ },
1917
+ ])('should throw MethodNotFoundRpcError when $label is set (fallback trigger)', async ({ param }) => {
1918
+ const provider = makeProviderWithoutFallback(1);
1919
1919
  await expect(provider.request({
1920
1920
  method: 'eth_call',
1921
- params: [params, 'earliest'],
1922
- })).rejects.toThrow('Only `latest` block identifier is supported');
1921
+ params: [{ ...baseParams, ...param }],
1922
+ })).rejects.toBeInstanceOf(MethodNotFoundRpcError);
1923
+ expect(mockScp.queryContract).not.toHaveBeenCalled();
1924
+ });
1925
+ // `nonce` is transaction-level and not visible inside eth_call, so it's fine to silently
1926
+ // drop — forwarding would be a no-op and throwing would break callers (e.g. viem's
1927
+ // publicClient.call) that may surface nonce for parity with standard JSON-RPC shapes.
1928
+ it('should silently ignore nonce', async () => {
1929
+ mockScp.queryContract.mockResolvedValue({ data: { outputData: '0xok' } });
1930
+ const result = await provider.request({
1931
+ method: 'eth_call',
1932
+ params: [{ ...baseParams, nonce: numberToHex(42n) }],
1933
+ });
1934
+ expect(result).toBe('0xok');
1935
+ // nonce must not be forwarded — queryContract only accepts {blockchain, to, data, from}.
1936
+ expect(mockScp.queryContract).toHaveBeenCalledWith({
1937
+ blockchain: 'ETH',
1938
+ address: baseParams.to,
1939
+ callData: baseParams.data,
1940
+ fromAddress: baseParams.from,
1941
+ });
1942
+ });
1943
+ // `value` arrives as a `Hex` at runtime from JSON-RPC transports, but the
1944
+ // TypeScript surface (`ExactPartial<TransactionRequest>`) admits `bigint`.
1945
+ // Exercise both shapes so the dual-type contract is regression-tested.
1946
+ it.each([
1947
+ { label: 'hex 0x0', value: '0x0' },
1948
+ { label: 'bigint 0n', value: 0n },
1949
+ ])('should accept zero value ($label) as equivalent to no native value', async ({ value }) => {
1950
+ mockScp.queryContract.mockResolvedValue({ data: { outputData: '0xzero' } });
1951
+ const result = await provider.request({
1952
+ method: 'eth_call',
1953
+ params: [{ ...baseParams, value }],
1954
+ });
1955
+ expect(result).toBe('0xzero');
1956
+ expect(mockScp.queryContract).toHaveBeenCalledTimes(1);
1957
+ });
1958
+ // `value` is the one param with read semantics (payable view calls). Non-zero must
1959
+ // surface `MethodNotFoundRpcError` so `Provider.request` falls back to the RPC transport.
1960
+ // Again, cover both the hex and bigint shapes.
1961
+ it.each([
1962
+ { label: 'hex 0x1', value: numberToHex(1n) },
1963
+ { label: 'bigint 1n', value: 1n },
1964
+ ])('should throw MethodNotFoundRpcError for non-zero value ($label) (fallback trigger)', async ({ value }) => {
1965
+ const provider = makeProviderWithoutFallback(1);
1923
1966
  await expect(provider.request({
1924
1967
  method: 'eth_call',
1925
- params: [params, '0x1234'],
1926
- })).rejects.toThrow('Only `latest` block identifier is supported');
1968
+ params: [{ ...baseParams, value }],
1969
+ })).rejects.toBeInstanceOf(MethodNotFoundRpcError);
1970
+ expect(mockScp.queryContract).not.toHaveBeenCalled();
1927
1971
  });
1928
- it('should throw error when unsupported transaction params are provided', async () => {
1929
- const baseParams = {
1930
- to: '0x742d35cc6634C0532925a3b8D0c8c0C8D8d8d8d8',
1931
- data: '0x70a08231',
1932
- };
1933
- // Test each unsupported parameter individually
1934
- const unsupportedParams = [
1935
- { gas: '0x186A0' },
1936
- { gasPrice: '0x3B9ACA00' },
1937
- { maxFeePerGas: '0x3B9ACA00' },
1938
- { maxPriorityFeePerGas: '0x3B9ACA00' },
1939
- { nonce: '0x1' },
1940
- { value: '0x0' },
1941
- ];
1942
- for (const param of unsupportedParams) {
1943
- const testParams = { ...baseParams, ...param };
1944
- await expect(provider.request({
1945
- method: 'eth_call',
1946
- params: [testParams],
1947
- })).rejects.toThrow();
1948
- }
1949
- // Test multiple unsupported params together
1950
- const multipleUnsupportedParams = {
1951
- ...baseParams,
1952
- gas: '0x186A0',
1953
- value: '0x0',
1954
- nonce: '0x1',
1955
- };
1972
+ it('should throw MethodNotFoundRpcError when blockIdentifier is not "latest" (fallback trigger)', async () => {
1973
+ const provider = makeProviderWithoutFallback(1);
1956
1974
  await expect(provider.request({
1957
1975
  method: 'eth_call',
1958
- params: [multipleUnsupportedParams],
1959
- })).rejects.toThrow();
1976
+ params: [baseParams, 'earliest'],
1977
+ })).rejects.toBeInstanceOf(MethodNotFoundRpcError);
1978
+ await expect(provider.request({
1979
+ method: 'eth_call',
1980
+ params: [baseParams, '0x1234'],
1981
+ })).rejects.toBeInstanceOf(MethodNotFoundRpcError);
1982
+ expect(mockScp.queryContract).not.toHaveBeenCalled();
1983
+ });
1984
+ // AVAX_FUJI (43113) is in SUPPORTED_CHAINS but NOT SUPPORTED_CONTRACT_QUERY_CHAINS.
1985
+ it('should throw MethodNotFoundRpcError when chain is not supported by queryContract (fallback trigger)', async () => {
1986
+ const provider = makeProviderWithoutFallback(43113);
1987
+ await expect(provider.request({ method: 'eth_call', params: [baseParams] })).rejects.toBeInstanceOf(MethodNotFoundRpcError);
1988
+ expect(mockScp.queryContract).not.toHaveBeenCalled();
1989
+ });
1990
+ // ARC_TESTNET (5042002) IS in SUPPORTED_CONTRACT_QUERY_CHAINS — reads flow through Circle's SCP.
1991
+ it('should route Arc Testnet eth_call through queryContract', async () => {
1992
+ // `vi.restoreAllMocks` in the outer beforeEach clears the module-level mock
1993
+ // return; re-prime it before constructing a second Provider for Arc Testnet.
1994
+ ;
1995
+ initiateSmartContractPlatformClient.mockReturnValue(mockScp);
1996
+ const arcProvider = new Provider({
1997
+ apiKey: 'test-api-key',
1998
+ entitySecret: 'test-entity-secret',
1999
+ chainId: 5042002, // ARC_TESTNET
2000
+ });
2001
+ mockScp.queryContract.mockResolvedValue({ data: { outputData: '0xarc' } });
2002
+ const result = await arcProvider.request({ method: 'eth_call', params: [baseParams] });
2003
+ expect(result).toBe('0xarc');
2004
+ expect(mockScp.queryContract).toHaveBeenCalledWith({
2005
+ blockchain: 'ARC-TESTNET',
2006
+ address: baseParams.to,
2007
+ callData: baseParams.data,
2008
+ fromAddress: baseParams.from,
2009
+ });
1960
2010
  });
1961
2011
  });
1962
2012
  describe('eth_estimateGas', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@circle-fin/usdckit",
3
- "version": "0.24.2",
3
+ "version": "0.25.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/crcl-main/w3s-node-sdk.git"
@@ -19,7 +19,7 @@
19
19
  "@solana-program/token-2022": "^0.4.2",
20
20
  "@solana/kit": "^2.3.0",
21
21
  "@wagmi/core": "^2.20.3",
22
- "axios": "^1.9.0",
22
+ "axios": "^1.13.5",
23
23
  "commander": "^14.0.0",
24
24
  "pino": "^9.9.0",
25
25
  "uuid": "^11.0.3",