@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.
- package/dist/cjs/metadata.js +1 -1
- package/dist/cjs/providers/circle-wallets/index.d.ts +188 -0
- package/dist/cjs/providers/circle-wallets/index.js +1 -0
- package/dist/cjs/providers/circle-wallets/transports/index.js +39 -9
- package/dist/cjs/providers/circle-wallets/transports/index.test.js +117 -67
- package/dist/esm/metadata.js +1 -1
- package/dist/esm/providers/circle-wallets/index.d.ts +188 -0
- package/dist/esm/providers/circle-wallets/index.js +1 -0
- package/dist/esm/providers/circle-wallets/transports/index.js +39 -9
- package/dist/esm/providers/circle-wallets/transports/index.test.js +117 -67
- package/package.json +2 -2
package/dist/cjs/metadata.js
CHANGED
|
@@ -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: {
|
|
@@ -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
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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: [
|
|
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:
|
|
1908
|
-
callData:
|
|
1909
|
-
fromAddress:
|
|
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
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
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: [
|
|
1957
|
-
})).rejects.
|
|
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: [
|
|
1961
|
-
})).rejects.
|
|
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
|
|
1964
|
-
const
|
|
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: [
|
|
1994
|
-
})).rejects.
|
|
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', () => {
|
package/dist/esm/metadata.js
CHANGED
|
@@ -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: {
|
|
@@ -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
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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: [
|
|
1876
|
+
params: [baseParams],
|
|
1868
1877
|
});
|
|
1869
1878
|
expect(result).toBe('0x0000000000000000000000000000000000000000000000000de0b6b3a7640000');
|
|
1870
1879
|
expect(mockScp.queryContract).toHaveBeenCalledWith({
|
|
1871
1880
|
blockchain: 'ETH',
|
|
1872
|
-
address:
|
|
1873
|
-
callData:
|
|
1874
|
-
fromAddress:
|
|
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
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
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: [
|
|
1922
|
-
})).rejects.
|
|
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: [
|
|
1926
|
-
})).rejects.
|
|
1968
|
+
params: [{ ...baseParams, value }],
|
|
1969
|
+
})).rejects.toBeInstanceOf(MethodNotFoundRpcError);
|
|
1970
|
+
expect(mockScp.queryContract).not.toHaveBeenCalled();
|
|
1927
1971
|
});
|
|
1928
|
-
it('should throw
|
|
1929
|
-
const
|
|
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: [
|
|
1959
|
-
})).rejects.
|
|
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.
|
|
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.
|
|
22
|
+
"axios": "^1.13.5",
|
|
23
23
|
"commander": "^14.0.0",
|
|
24
24
|
"pino": "^9.9.0",
|
|
25
25
|
"uuid": "^11.0.3",
|