@circle-fin/app-kit 1.11.0 → 1.12.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/CHANGELOG.md +53 -0
- package/README.md +56 -25
- package/bridge.cjs +1696 -1348
- package/bridge.d.cts +979 -2
- package/bridge.d.mts +979 -2
- package/bridge.d.ts +979 -2
- package/bridge.mjs +1696 -1348
- package/chains.cjs +106 -1
- package/chains.d.cts +96 -2
- package/chains.d.mts +96 -2
- package/chains.d.ts +96 -2
- package/chains.mjs +105 -2
- package/context.d.cts +978 -1
- package/context.d.mts +978 -1
- package/context.d.ts +978 -1
- package/earn.cjs +1728 -65
- package/earn.d.cts +978 -1
- package/earn.d.mts +978 -1
- package/earn.d.ts +978 -1
- package/earn.mjs +1728 -65
- package/estimateBridge.cjs +1695 -1347
- package/estimateBridge.d.cts +978 -1
- package/estimateBridge.d.mts +978 -1
- package/estimateBridge.d.ts +978 -1
- package/estimateBridge.mjs +1695 -1347
- package/estimateSwap.cjs +312 -17
- package/estimateSwap.d.cts +977 -1
- package/estimateSwap.d.mts +977 -1
- package/estimateSwap.d.ts +977 -1
- package/estimateSwap.mjs +312 -17
- package/index.cjs +3608 -1603
- package/index.d.cts +4181 -3426
- package/index.d.mts +4181 -3426
- package/index.d.ts +4181 -3426
- package/index.mjs +3607 -1604
- package/package.json +6 -6
- package/swap.cjs +312 -17
- package/swap.d.cts +977 -1
- package/swap.d.mts +977 -1
- package/swap.d.ts +977 -1
- package/swap.mjs +312 -17
- package/unifiedBalance.cjs +436 -122
- package/unifiedBalance.d.cts +132 -31
- package/unifiedBalance.d.mts +132 -31
- package/unifiedBalance.d.ts +132 -31
- package/unifiedBalance.mjs +436 -122
package/unifiedBalance.cjs
CHANGED
|
@@ -2325,6 +2325,8 @@ class KitError extends Error {
|
|
|
2325
2325
|
Blockchain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
|
|
2326
2326
|
Blockchain["XDC"] = "XDC";
|
|
2327
2327
|
Blockchain["XDC_Apothem"] = "XDC_Apothem";
|
|
2328
|
+
Blockchain["X_Layer"] = "X_Layer";
|
|
2329
|
+
Blockchain["X_Layer_Testnet"] = "X_Layer_Testnet";
|
|
2328
2330
|
Blockchain["ZKSync_Era"] = "ZKSync_Era";
|
|
2329
2331
|
Blockchain["ZKSync_Sepolia"] = "ZKSync_Sepolia";
|
|
2330
2332
|
})(Blockchain || (Blockchain = {}));
|
|
@@ -2378,6 +2380,7 @@ var BridgeChain;
|
|
|
2378
2380
|
BridgeChain["Unichain"] = "Unichain";
|
|
2379
2381
|
BridgeChain["World_Chain"] = "World_Chain";
|
|
2380
2382
|
BridgeChain["XDC"] = "XDC";
|
|
2383
|
+
BridgeChain["X_Layer"] = "X_Layer";
|
|
2381
2384
|
// Testnet chains with CCTPv2 support
|
|
2382
2385
|
BridgeChain["Arc_Testnet"] = "Arc_Testnet";
|
|
2383
2386
|
BridgeChain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
|
|
@@ -2403,6 +2406,7 @@ var BridgeChain;
|
|
|
2403
2406
|
BridgeChain["Unichain_Sepolia"] = "Unichain_Sepolia";
|
|
2404
2407
|
BridgeChain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
|
|
2405
2408
|
BridgeChain["XDC_Apothem"] = "XDC_Apothem";
|
|
2409
|
+
BridgeChain["X_Layer_Testnet"] = "X_Layer_Testnet";
|
|
2406
2410
|
})(BridgeChain || (BridgeChain = {}));
|
|
2407
2411
|
var UnifiedBalanceChain;
|
|
2408
2412
|
(function(UnifiedBalanceChain) {
|
|
@@ -4950,7 +4954,8 @@ var EarnChain;
|
|
|
4950
4954
|
isTestnet: true,
|
|
4951
4955
|
explorerUrl: 'https://amoy.polygonscan.com/tx/{hash}',
|
|
4952
4956
|
rpcEndpoints: [
|
|
4953
|
-
'https://
|
|
4957
|
+
'https://polygon-amoy-bor-rpc.publicnode.com',
|
|
4958
|
+
'https://polygon-amoy.drpc.org'
|
|
4954
4959
|
],
|
|
4955
4960
|
eurcAddress: null,
|
|
4956
4961
|
usdcAddress: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
|
|
@@ -5815,6 +5820,104 @@ var EarnChain;
|
|
|
5815
5820
|
}
|
|
5816
5821
|
});
|
|
5817
5822
|
|
|
5823
|
+
/**
|
|
5824
|
+
* X Layer Mainnet chain definition
|
|
5825
|
+
* @remarks
|
|
5826
|
+
* This represents the official production network for the X Layer blockchain.
|
|
5827
|
+
* X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
|
|
5828
|
+
* using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
|
|
5829
|
+
* OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
|
|
5830
|
+
*/ const XLayer = defineChain({
|
|
5831
|
+
type: 'evm',
|
|
5832
|
+
chain: Blockchain.X_Layer,
|
|
5833
|
+
name: 'X Layer',
|
|
5834
|
+
title: 'X Layer Mainnet',
|
|
5835
|
+
nativeCurrency: {
|
|
5836
|
+
name: 'OKB',
|
|
5837
|
+
symbol: 'OKB',
|
|
5838
|
+
decimals: 18
|
|
5839
|
+
},
|
|
5840
|
+
chainId: 196,
|
|
5841
|
+
isTestnet: false,
|
|
5842
|
+
explorerUrl: 'https://www.oklink.com/xlayer/tx/{hash}',
|
|
5843
|
+
rpcEndpoints: [
|
|
5844
|
+
'https://xlayerrpc.okx.com'
|
|
5845
|
+
],
|
|
5846
|
+
eurcAddress: null,
|
|
5847
|
+
usdcAddress: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
|
|
5848
|
+
usdtAddress: null,
|
|
5849
|
+
cctp: {
|
|
5850
|
+
domain: 37,
|
|
5851
|
+
contracts: {
|
|
5852
|
+
v2: {
|
|
5853
|
+
type: 'split',
|
|
5854
|
+
tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
|
|
5855
|
+
messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
|
|
5856
|
+
confirmations: 65,
|
|
5857
|
+
fastConfirmations: 1
|
|
5858
|
+
}
|
|
5859
|
+
},
|
|
5860
|
+
forwarderSupported: {
|
|
5861
|
+
source: false,
|
|
5862
|
+
destination: false
|
|
5863
|
+
}
|
|
5864
|
+
},
|
|
5865
|
+
kitContracts: {
|
|
5866
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
5867
|
+
}
|
|
5868
|
+
});
|
|
5869
|
+
|
|
5870
|
+
/**
|
|
5871
|
+
* X Layer Testnet chain definition
|
|
5872
|
+
* @remarks
|
|
5873
|
+
* This represents the official test network for the X Layer blockchain.
|
|
5874
|
+
* X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
|
|
5875
|
+
* using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
|
|
5876
|
+
* OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
|
|
5877
|
+
*/ const XLayerTestnet = defineChain({
|
|
5878
|
+
type: 'evm',
|
|
5879
|
+
chain: Blockchain.X_Layer_Testnet,
|
|
5880
|
+
name: 'X Layer Testnet',
|
|
5881
|
+
title: 'X Layer Testnet',
|
|
5882
|
+
nativeCurrency: {
|
|
5883
|
+
name: 'OKB',
|
|
5884
|
+
symbol: 'OKB',
|
|
5885
|
+
decimals: 18
|
|
5886
|
+
},
|
|
5887
|
+
chainId: 1952,
|
|
5888
|
+
isTestnet: true,
|
|
5889
|
+
// Deliberately not oklink.com (used for mainnet): viem's bundled OKLink
|
|
5890
|
+
// testnet URL targets the deprecated pre-rebrand chain ID 195, not this
|
|
5891
|
+
// chain's ID (1952). Verified against the internal chain-expansion-scripts
|
|
5892
|
+
// config (`v2config.sandbox.yml`) — do not "normalize" this to match mainnet.
|
|
5893
|
+
explorerUrl: 'https://web3.okx.com/explorer/x-layer-testnet/tx/{hash}',
|
|
5894
|
+
rpcEndpoints: [
|
|
5895
|
+
'https://testrpc.xlayer.tech'
|
|
5896
|
+
],
|
|
5897
|
+
eurcAddress: null,
|
|
5898
|
+
usdcAddress: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
|
|
5899
|
+
usdtAddress: null,
|
|
5900
|
+
cctp: {
|
|
5901
|
+
domain: 37,
|
|
5902
|
+
contracts: {
|
|
5903
|
+
v2: {
|
|
5904
|
+
type: 'split',
|
|
5905
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
5906
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
5907
|
+
confirmations: 65,
|
|
5908
|
+
fastConfirmations: 1
|
|
5909
|
+
}
|
|
5910
|
+
},
|
|
5911
|
+
forwarderSupported: {
|
|
5912
|
+
source: false,
|
|
5913
|
+
destination: false
|
|
5914
|
+
}
|
|
5915
|
+
},
|
|
5916
|
+
kitContracts: {
|
|
5917
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET
|
|
5918
|
+
}
|
|
5919
|
+
});
|
|
5920
|
+
|
|
5818
5921
|
/**
|
|
5819
5922
|
* ZKSync Era Mainnet chain definition
|
|
5820
5923
|
* @remarks
|
|
@@ -5934,6 +6037,8 @@ var Chains = {
|
|
|
5934
6037
|
WorldChainSepolia: WorldChainSepolia,
|
|
5935
6038
|
XDC: XDC,
|
|
5936
6039
|
XDCApothem: XDCApothem,
|
|
6040
|
+
XLayer: XLayer,
|
|
6041
|
+
XLayerTestnet: XLayerTestnet,
|
|
5937
6042
|
ZKSyncEra: ZKSyncEra,
|
|
5938
6043
|
ZKSyncEraSepolia: ZKSyncEraSepolia
|
|
5939
6044
|
};
|
|
@@ -7886,6 +7991,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
7886
7991
|
[Blockchain.Unichain]: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
|
|
7887
7992
|
[Blockchain.World_Chain]: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
|
|
7888
7993
|
[Blockchain.XDC]: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
|
|
7994
|
+
[Blockchain.X_Layer]: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
|
|
7889
7995
|
[Blockchain.ZKSync_Era]: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
|
|
7890
7996
|
// =========================================================================
|
|
7891
7997
|
// Testnets (alphabetically sorted)
|
|
@@ -7894,6 +8000,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
7894
8000
|
[Blockchain.Arbitrum_Sepolia]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
|
|
7895
8001
|
[Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
7896
8002
|
[Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
8003
|
+
[Blockchain.Celo_Alfajores_Testnet]: '0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B',
|
|
7897
8004
|
[Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
7898
8005
|
[Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
7899
8006
|
[Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
|
|
@@ -7920,6 +8027,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
7920
8027
|
[Blockchain.Unichain_Sepolia]: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
|
|
7921
8028
|
[Blockchain.World_Chain_Sepolia]: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
|
|
7922
8029
|
[Blockchain.XDC_Apothem]: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
|
|
8030
|
+
[Blockchain.X_Layer_Testnet]: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
|
|
7923
8031
|
[Blockchain.ZKSync_Sepolia]: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853'
|
|
7924
8032
|
}
|
|
7925
8033
|
};
|
|
@@ -8785,7 +8893,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
8785
8893
|
}
|
|
8786
8894
|
|
|
8787
8895
|
var name = "@circle-fin/unified-balance-kit";
|
|
8788
|
-
var version = "1.
|
|
8896
|
+
var version = "1.4.1";
|
|
8789
8897
|
var pkg = {
|
|
8790
8898
|
name: name,
|
|
8791
8899
|
version: version};
|
|
@@ -9068,6 +9176,110 @@ var pkg = {
|
|
|
9068
9176
|
* ```
|
|
9069
9177
|
*/ const USDC_DECIMALS$1 = 6;
|
|
9070
9178
|
|
|
9179
|
+
/**
|
|
9180
|
+
* Canonical list of actions that do not prepare or submit transactions.
|
|
9181
|
+
*
|
|
9182
|
+
* @internal
|
|
9183
|
+
*/ const READ_ACTION_KEYS = [
|
|
9184
|
+
'token.allowance',
|
|
9185
|
+
'token.balanceOf',
|
|
9186
|
+
'token.name',
|
|
9187
|
+
'native.balanceOf',
|
|
9188
|
+
'usdc.allowance',
|
|
9189
|
+
'usdc.balanceOf',
|
|
9190
|
+
'usdc.name',
|
|
9191
|
+
'gateway.v1.isDelegate',
|
|
9192
|
+
'gateway.v1.withdrawingBalance',
|
|
9193
|
+
'gateway.v1.withdrawalBlock',
|
|
9194
|
+
'gateway.v1.signBurnIntents'
|
|
9195
|
+
];
|
|
9196
|
+
const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
|
|
9197
|
+
/**
|
|
9198
|
+
* Check whether a runtime value identifies a read action.
|
|
9199
|
+
*
|
|
9200
|
+
* @param action - The value to classify.
|
|
9201
|
+
* @returns Whether the value is a registered read-action key.
|
|
9202
|
+
*
|
|
9203
|
+
* @example
|
|
9204
|
+
* ```typescript
|
|
9205
|
+
* import { isReadActionKey } from '@core/adapter'
|
|
9206
|
+
*
|
|
9207
|
+
* if (isReadActionKey(value)) {
|
|
9208
|
+
* await adapter.readAction(value, params, context)
|
|
9209
|
+
* }
|
|
9210
|
+
* ```
|
|
9211
|
+
*
|
|
9212
|
+
* @internal
|
|
9213
|
+
*/ function isReadActionKey(action) {
|
|
9214
|
+
return typeof action === 'string' && READ_ACTION_KEY_SET.has(action);
|
|
9215
|
+
}
|
|
9216
|
+
|
|
9217
|
+
/**
|
|
9218
|
+
* Create the standard error for a missing or non-read action.
|
|
9219
|
+
*
|
|
9220
|
+
* @param action - The unsupported action value.
|
|
9221
|
+
* @returns A fatal unsupported-action error.
|
|
9222
|
+
*
|
|
9223
|
+
* @internal
|
|
9224
|
+
*/ function createUnsupportedReadActionError(action) {
|
|
9225
|
+
return new KitError({
|
|
9226
|
+
...InputError.UNSUPPORTED_ACTION,
|
|
9227
|
+
recoverability: 'FATAL',
|
|
9228
|
+
message: `Read action "${String(action)}" is not registered in this adapter.`
|
|
9229
|
+
});
|
|
9230
|
+
}
|
|
9231
|
+
/**
|
|
9232
|
+
* Execute a read through the adapter's dedicated read seam when available.
|
|
9233
|
+
*
|
|
9234
|
+
* @remarks
|
|
9235
|
+
* Fall back to the legacy `prepareAction().execute()` contract so providers
|
|
9236
|
+
* remain runtime-compatible with adapter versions released before `readAction`.
|
|
9237
|
+
* Consumers must upgrade their adapter package for reads to bypass custom
|
|
9238
|
+
* `prepareAction` wrappers.
|
|
9239
|
+
*
|
|
9240
|
+
* @typeParam TAdapterCapabilities - The adapter capabilities type.
|
|
9241
|
+
* @typeParam TActionKey - The read action key.
|
|
9242
|
+
* @param adapter - The adapter that owns the read action.
|
|
9243
|
+
* @param action - The read action to execute.
|
|
9244
|
+
* @param params - The parameters for the read action.
|
|
9245
|
+
* @param ctx - The operation context.
|
|
9246
|
+
* @returns The raw read-action result.
|
|
9247
|
+
* @throws {KitError} When `action` is not a supported read-action key.
|
|
9248
|
+
*
|
|
9249
|
+
* @example
|
|
9250
|
+
* ```typescript
|
|
9251
|
+
* import { executeAdapterReadAction } from '@core/adapter'
|
|
9252
|
+
* import { Ethereum } from '@core/chains'
|
|
9253
|
+
*
|
|
9254
|
+
* const allowance = await executeAdapterReadAction(
|
|
9255
|
+
* adapter,
|
|
9256
|
+
* 'token.allowance',
|
|
9257
|
+
* { tokenAddress, delegate },
|
|
9258
|
+
* { chain: Ethereum },
|
|
9259
|
+
* )
|
|
9260
|
+
* ```
|
|
9261
|
+
*
|
|
9262
|
+
* @internal
|
|
9263
|
+
*/ async function executeAdapterReadAction(adapter, action, params, ctx) {
|
|
9264
|
+
if (!isReadActionKey(action)) {
|
|
9265
|
+
throw createUnsupportedReadActionError(action);
|
|
9266
|
+
}
|
|
9267
|
+
const runtimeAdapter = adapter;
|
|
9268
|
+
if (typeof runtimeAdapter.readAction === 'function') {
|
|
9269
|
+
return runtimeAdapter.readAction(action, params, ctx);
|
|
9270
|
+
}
|
|
9271
|
+
let request;
|
|
9272
|
+
try {
|
|
9273
|
+
request = await adapter.prepareAction(action, params, ctx);
|
|
9274
|
+
} catch (error) {
|
|
9275
|
+
if (error instanceof Error && error.message === `Action ${action} is not supported`) {
|
|
9276
|
+
throw createUnsupportedReadActionError(action);
|
|
9277
|
+
}
|
|
9278
|
+
throw error;
|
|
9279
|
+
}
|
|
9280
|
+
return request.execute();
|
|
9281
|
+
}
|
|
9282
|
+
|
|
9071
9283
|
/**
|
|
9072
9284
|
* Schema for validating hexadecimal strings with '0x' prefix.
|
|
9073
9285
|
*
|
|
@@ -9277,16 +9489,15 @@ var pkg = {
|
|
|
9277
9489
|
* ```
|
|
9278
9490
|
*/ const validateBalanceForTransaction = async (params)=>{
|
|
9279
9491
|
const { amount, adapter, token, tokenAddress, operationContext } = params;
|
|
9280
|
-
const
|
|
9492
|
+
const balance = await executeAdapterReadAction(adapter, 'usdc.balanceOf', {
|
|
9281
9493
|
walletAddress: operationContext.address
|
|
9282
9494
|
}, operationContext);
|
|
9283
|
-
|
|
9284
|
-
if (BigInt(balance) < BigInt(amount)) {
|
|
9495
|
+
if (BigInt(String(balance)) < BigInt(amount)) {
|
|
9285
9496
|
// Extract chain name from operationContext
|
|
9286
9497
|
const chainName = extractChainInfo(operationContext.chain).name;
|
|
9287
9498
|
// Create KitError with rich context in trace
|
|
9288
9499
|
throw createInsufficientTokenBalanceError(chainName, token, {
|
|
9289
|
-
balance: balance
|
|
9500
|
+
balance: String(balance),
|
|
9290
9501
|
amount,
|
|
9291
9502
|
tokenAddress,
|
|
9292
9503
|
walletAddress: operationContext.address
|
|
@@ -12066,72 +12277,55 @@ function evmSigningData(burnIntent) {
|
|
|
12066
12277
|
* `0xef0100` followed by the 20-byte delegate address (23 bytes total).
|
|
12067
12278
|
* The underlying secp256k1 key still produces `ecrecover`-verifiable
|
|
12068
12279
|
* signatures, so for Gateway's purposes a 7702-delegated address is
|
|
12069
|
-
* an EOA, not
|
|
12280
|
+
* an EOA, not a contract signer.
|
|
12070
12281
|
*
|
|
12071
12282
|
* Spec: https://eips.ethereum.org/EIPS/eip-7702
|
|
12072
12283
|
*/ const EIP_7702_DELEGATION_PREFIX = '0xef0100';
|
|
12073
12284
|
/**
|
|
12074
|
-
*
|
|
12075
|
-
*
|
|
12076
|
-
* Gateway verifies burn-intent signatures with plain `ecrecover` (see
|
|
12077
|
-
* `evm-gateway-contracts/src/lib/EIP712Domain.sol`). Smart-contract
|
|
12078
|
-
* accounts (SCAs) produce signatures over wrapped hashes (ERC-1271 /
|
|
12079
|
-
* ERC-6492 / ERC-6900 replay-safe hashes) that Gateway cannot verify.
|
|
12080
|
-
* Additionally, the Circle Wallets backend rejects SCA typed-data signing
|
|
12081
|
-
* against Gateway's chainId-less domain with an opaque
|
|
12082
|
-
* `invalid integer value <nil>/<nil> for type uint256` error.
|
|
12285
|
+
* Determine whether `address` on `chain` signs as a contract (ERC-1271)
|
|
12286
|
+
* rather than as an EOA.
|
|
12083
12287
|
*
|
|
12084
|
-
*
|
|
12085
|
-
*
|
|
12086
|
-
* `
|
|
12288
|
+
* Gateway validates burn-intent signatures two ways: a static `ecrecover`
|
|
12289
|
+
* check for EOAs, and — for requests that carry `contractSigner: true` —
|
|
12290
|
+
* an offchain `isValidSignature` simulation against the signing contract
|
|
12291
|
+
* (ERC-1271). Gateway does not infer which one to use, so the caller must
|
|
12292
|
+
* declare it. This detects the contract case from on-chain bytecode.
|
|
12087
12293
|
*
|
|
12088
|
-
*
|
|
12089
|
-
*
|
|
12090
|
-
*
|
|
12091
|
-
*
|
|
12092
|
-
* docs for the exact API.
|
|
12294
|
+
* EIP-7702-delegated EOAs are treated as EOAs: they expose non-empty
|
|
12295
|
+
* bytecode (`0xef0100<delegate>`) but the underlying secp256k1 key still
|
|
12296
|
+
* produces `ecrecover`-verifiable signatures, so the cheaper EOA path
|
|
12297
|
+
* stays correct for them.
|
|
12093
12298
|
*
|
|
12094
|
-
* If bytecode cannot be read (RPC failure, etc.) the
|
|
12095
|
-
*
|
|
12096
|
-
*
|
|
12299
|
+
* If bytecode cannot be read (RPC failure, etc.) the address is reported
|
|
12300
|
+
* as an EOA and a warning is logged so the fallback is diagnosable. A
|
|
12301
|
+
* genuine contract signer misreported this way is rejected by Gateway with
|
|
12302
|
+
* an invalid-signature error rather than silently mis-attested.
|
|
12097
12303
|
*
|
|
12098
12304
|
* @param adapter - Anything exposing {@link EvmAdapterLike.readBytecode}.
|
|
12099
|
-
* @param address - Signer address to
|
|
12305
|
+
* @param address - Signer address to classify.
|
|
12100
12306
|
* @param chain - EVM chain where the signer lives.
|
|
12101
|
-
* @
|
|
12307
|
+
* @returns `true` when the signer is a contract account and the transfer
|
|
12308
|
+
* request must set `contractSigner: true`; `false` otherwise.
|
|
12102
12309
|
*
|
|
12103
12310
|
* @example
|
|
12104
12311
|
* ```typescript
|
|
12105
|
-
* import {
|
|
12312
|
+
* import { isContractSigner } from '@core/adapter-evm'
|
|
12106
12313
|
* import { Ethereum } from '@core/chains'
|
|
12107
12314
|
*
|
|
12108
|
-
* await
|
|
12315
|
+
* const useErc1271 = await isContractSigner(adapter, '0xabc...', Ethereum)
|
|
12109
12316
|
* ```
|
|
12110
|
-
*/ async function
|
|
12317
|
+
*/ async function isContractSigner(adapter, address, chain) {
|
|
12111
12318
|
let code;
|
|
12112
12319
|
try {
|
|
12113
12320
|
code = await adapter.readBytecode(address, chain);
|
|
12114
12321
|
} catch (err) {
|
|
12115
|
-
console.warn(`[gateway]
|
|
12116
|
-
return;
|
|
12322
|
+
console.warn(`[gateway] isContractSigner defaulting to EOA (readBytecode failed ` + `for ${address} on ${chain.name}): ` + (err instanceof Error ? err.message : String(err)));
|
|
12323
|
+
return false;
|
|
12117
12324
|
}
|
|
12118
12325
|
if (code === undefined || code === '0x' || code.toLowerCase().startsWith(EIP_7702_DELEGATION_PREFIX)) {
|
|
12119
|
-
return;
|
|
12326
|
+
return false;
|
|
12120
12327
|
}
|
|
12121
|
-
|
|
12122
|
-
...InputError.UNSUPPORTED_ACTION,
|
|
12123
|
-
recoverability: 'FATAL',
|
|
12124
|
-
message: `Gateway burn-intent signing requires an EOA signer (Gateway ` + `verifies signatures with ecrecover and does not support ERC-1271). ` + `The signer ${address} on ${chain.name} has on-chain bytecode, ` + `indicating it is a smart-contract account (SCA). Register an EOA ` + `delegate against the SCA, then submit the spend with the delegate ` + `EOA as the signer and the SCA as the source account. See DEVX-2774.`,
|
|
12125
|
-
cause: {
|
|
12126
|
-
trace: {
|
|
12127
|
-
operation: 'signEvmIntentGroup.assertSignerIsEoa',
|
|
12128
|
-
address,
|
|
12129
|
-
chain: chain.name,
|
|
12130
|
-
bytecodeBytes: (code.length - 2) / 2,
|
|
12131
|
-
bytecodePrefix: code.slice(0, 12)
|
|
12132
|
-
}
|
|
12133
|
-
}
|
|
12134
|
-
});
|
|
12328
|
+
return true;
|
|
12135
12329
|
}
|
|
12136
12330
|
|
|
12137
12331
|
/**
|
|
@@ -12158,78 +12352,177 @@ function evmSigningData(burnIntent) {
|
|
|
12158
12352
|
return typeof value === 'object' && value !== null && 'readBytecode' in value && typeof value.readBytecode === 'function';
|
|
12159
12353
|
}
|
|
12160
12354
|
|
|
12355
|
+
function resolveIntentChain(group, intent) {
|
|
12356
|
+
const sourceDomain = intent.spec.sourceDomain;
|
|
12357
|
+
const chain = group.chainsByDomain.get(sourceDomain);
|
|
12358
|
+
if (chain !== undefined) return chain;
|
|
12359
|
+
throw createValidationFailedError$1('intent.spec.sourceDomain', sourceDomain, `No source chain found for Gateway domain ${String(sourceDomain)}`);
|
|
12360
|
+
}
|
|
12361
|
+
function normalizeSignatureResult(result) {
|
|
12362
|
+
if (typeof result === 'string') {
|
|
12363
|
+
return {
|
|
12364
|
+
signature: result,
|
|
12365
|
+
contractSigner: false
|
|
12366
|
+
};
|
|
12367
|
+
}
|
|
12368
|
+
if (typeof result === 'object' && result !== null && 'signature' in result && typeof result.signature === 'string') {
|
|
12369
|
+
return {
|
|
12370
|
+
signature: result.signature,
|
|
12371
|
+
contractSigner: 'contractSigner' in result && result.contractSigner === true
|
|
12372
|
+
};
|
|
12373
|
+
}
|
|
12374
|
+
throw createValidationFailedError$1('signature', result, 'must be a signature string or an object containing a signature string');
|
|
12375
|
+
}
|
|
12376
|
+
function validateGroupIntents(intents) {
|
|
12377
|
+
evmSigningData(intents);
|
|
12378
|
+
}
|
|
12379
|
+
function collectChainsByDomain(group) {
|
|
12380
|
+
const chainsByDomain = new Map();
|
|
12381
|
+
for (const intent of group.intents){
|
|
12382
|
+
chainsByDomain.set(intent.spec.sourceDomain, resolveIntentChain(group, intent));
|
|
12383
|
+
}
|
|
12384
|
+
return chainsByDomain;
|
|
12385
|
+
}
|
|
12386
|
+
async function classifySignerTypes(group, chainsByDomain) {
|
|
12387
|
+
const { adapter, address } = group;
|
|
12388
|
+
// Duck-typed on readBytecode rather than `instanceof EvmAdapter` because
|
|
12389
|
+
// each consumer package bundles its own copy of the base class and the
|
|
12390
|
+
// `instanceof` identity check fails across package boundaries.
|
|
12391
|
+
// Empty strings are rejected to avoid calling eth_getCode('') on the RPC.
|
|
12392
|
+
const hasResolvedSigner = typeof address === 'string' && address.length > 0;
|
|
12393
|
+
const signerTypes = await Promise.all([
|
|
12394
|
+
...chainsByDomain
|
|
12395
|
+
].map(async ([sourceDomain, sourceChain])=>{
|
|
12396
|
+
const contractSigner = hasResolvedSigner && sourceChain.type === 'evm' && isEvmAdapterLike(adapter) ? await isContractSigner(adapter, address, sourceChain) : false;
|
|
12397
|
+
return [
|
|
12398
|
+
sourceDomain,
|
|
12399
|
+
contractSigner
|
|
12400
|
+
];
|
|
12401
|
+
}));
|
|
12402
|
+
return new Map(signerTypes);
|
|
12403
|
+
}
|
|
12404
|
+
function createSigningUnits(group, signerTypeByDomain) {
|
|
12405
|
+
const contractUnitsByDomain = new Map();
|
|
12406
|
+
let eoaUnit;
|
|
12407
|
+
for (const [index, intent] of group.intents.entries()){
|
|
12408
|
+
const sourceDomain = intent.spec.sourceDomain;
|
|
12409
|
+
const contractSigner = signerTypeByDomain.get(sourceDomain) ?? false;
|
|
12410
|
+
if (contractSigner) {
|
|
12411
|
+
const existingUnit = contractUnitsByDomain.get(sourceDomain);
|
|
12412
|
+
if (existingUnit === undefined) {
|
|
12413
|
+
contractUnitsByDomain.set(sourceDomain, {
|
|
12414
|
+
intents: [
|
|
12415
|
+
intent
|
|
12416
|
+
],
|
|
12417
|
+
chain: resolveIntentChain(group, intent),
|
|
12418
|
+
contractSigner: true,
|
|
12419
|
+
firstIntentIndex: index
|
|
12420
|
+
});
|
|
12421
|
+
} else {
|
|
12422
|
+
existingUnit.intents.push(intent);
|
|
12423
|
+
}
|
|
12424
|
+
} else {
|
|
12425
|
+
eoaUnit ??= {
|
|
12426
|
+
intents: [],
|
|
12427
|
+
chain: resolveIntentChain(group, intent),
|
|
12428
|
+
contractSigner: false,
|
|
12429
|
+
firstIntentIndex: index
|
|
12430
|
+
};
|
|
12431
|
+
eoaUnit.intents.push(intent);
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12434
|
+
const signingUnits = [
|
|
12435
|
+
...contractUnitsByDomain.values()
|
|
12436
|
+
];
|
|
12437
|
+
if (eoaUnit !== undefined) signingUnits.push(eoaUnit);
|
|
12438
|
+
signingUnits.sort((a, b)=>a.firstIntentIndex - b.firstIntentIndex);
|
|
12439
|
+
return signingUnits;
|
|
12440
|
+
}
|
|
12441
|
+
async function signUnit(group, unit) {
|
|
12442
|
+
const { adapter, address } = group;
|
|
12443
|
+
const firstIntent = unit.intents[0];
|
|
12444
|
+
const typedData = unit.intents.length === 1 && firstIntent !== undefined ? evmSigningData(firstIntent) : evmSigningData(unit.intents);
|
|
12445
|
+
const operationContext = address === undefined ? {
|
|
12446
|
+
chain: unit.chain
|
|
12447
|
+
} : {
|
|
12448
|
+
chain: unit.chain,
|
|
12449
|
+
address
|
|
12450
|
+
};
|
|
12451
|
+
const signRequest = await adapter.prepareAction('gateway.v1.signBurnIntents', {
|
|
12452
|
+
typedData,
|
|
12453
|
+
chain: unit.chain
|
|
12454
|
+
}, operationContext);
|
|
12455
|
+
const result = normalizeSignatureResult(await signRequest.execute());
|
|
12456
|
+
return {
|
|
12457
|
+
intents: unit.intents,
|
|
12458
|
+
signature: result.signature,
|
|
12459
|
+
contractSigner: result.contractSigner || unit.contractSigner
|
|
12460
|
+
};
|
|
12461
|
+
}
|
|
12462
|
+
async function signUnits(group, signingUnits) {
|
|
12463
|
+
const signedSets = [];
|
|
12464
|
+
// Keep wallet prompts deterministic. Multiple adapter groups can still sign
|
|
12465
|
+
// in parallel, but one signer is asked for its chain-bound signatures in
|
|
12466
|
+
// source-intent order.
|
|
12467
|
+
for (const unit of signingUnits){
|
|
12468
|
+
signedSets.push(await signUnit(group, unit));
|
|
12469
|
+
}
|
|
12470
|
+
return signedSets;
|
|
12471
|
+
}
|
|
12161
12472
|
/**
|
|
12162
|
-
* Sign an EVM adapter group
|
|
12163
|
-
* EIP-712 ECDSA signature.
|
|
12473
|
+
* Sign an EVM adapter group.
|
|
12164
12474
|
*
|
|
12165
|
-
*
|
|
12166
|
-
*
|
|
12475
|
+
* EOA intents remain batched into one EIP-712 `BurnIntentSet`. ERC-1271
|
|
12476
|
+
* intents are grouped and signed per source chain because smart accounts
|
|
12477
|
+
* commonly include `chainId` in their replay-safe signature hash.
|
|
12478
|
+
* All returned entries can still be submitted together in one atomic Gateway
|
|
12479
|
+
* transfer request.
|
|
12167
12480
|
*
|
|
12168
|
-
* Before signing,
|
|
12169
|
-
*
|
|
12170
|
-
*
|
|
12171
|
-
*
|
|
12172
|
-
*
|
|
12481
|
+
* Before signing, classifies the signer as an EOA or a contract account.
|
|
12482
|
+
* Gateway validates EOA signatures with `ecrecover` and contract-account
|
|
12483
|
+
* signatures with ERC-1271, but it does not infer which one applies — the
|
|
12484
|
+
* transfer request has to declare it. The returned `contractSigner` flag
|
|
12485
|
+
* carries that decision through to `buildTransferRequestBody`.
|
|
12173
12486
|
*
|
|
12174
12487
|
* @param group - The adapter group containing the adapter, chain, and
|
|
12175
12488
|
* burn intents to sign.
|
|
12176
|
-
* @returns
|
|
12489
|
+
* @returns Signed entries with their intents, signatures, and Gateway signer
|
|
12490
|
+
* validation mode.
|
|
12491
|
+
* @throws KitError when an intent has no source-chain mapping or a signing
|
|
12492
|
+
* action returns an invalid signature shape.
|
|
12177
12493
|
*
|
|
12178
12494
|
* @example
|
|
12179
12495
|
* ```typescript
|
|
12180
12496
|
* import { signEvmIntentGroup } from '@core/adapter-evm'
|
|
12181
12497
|
*
|
|
12182
|
-
* const
|
|
12498
|
+
* const signedSets = await signEvmIntentGroup({
|
|
12183
12499
|
* adapter: evmAdapter,
|
|
12184
12500
|
* chain: ethereumChain,
|
|
12185
12501
|
* intents: [burnIntent1, burnIntent2],
|
|
12502
|
+
* chainsByDomain: new Map([
|
|
12503
|
+
* [0, ethereumChain],
|
|
12504
|
+
* [6, baseChain],
|
|
12505
|
+
* ]),
|
|
12186
12506
|
* address: '0x...',
|
|
12187
12507
|
* })
|
|
12188
|
-
* console.log(
|
|
12508
|
+
* console.log(signedSets)
|
|
12189
12509
|
* ```
|
|
12190
12510
|
*/ async function signEvmIntentGroup(group) {
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
// Gateway verifies burn-intent signatures with plain ecrecover. An SCA
|
|
12199
|
-
// signer silently produces a signature over a wrapped hash that Gateway
|
|
12200
|
-
// cannot verify, and Circle Wallets' KMS rejects the typed data up front
|
|
12201
|
-
// with an opaque `<nil>/<nil>` error. Short-circuit with a clear message
|
|
12202
|
-
// when we can detect bytecode at the signer address. See DEVX-2774.
|
|
12203
|
-
//
|
|
12204
|
-
// Duck-typed on readBytecode rather than `instanceof EvmAdapter` because
|
|
12205
|
-
// each consumer package bundles its own copy of the base class and the
|
|
12206
|
-
// `instanceof` identity check fails across package boundaries.
|
|
12207
|
-
//
|
|
12208
|
-
// Empty string is defended against because assertSignerIsEoa would
|
|
12209
|
-
// otherwise call eth_getCode('') on the RPC.
|
|
12210
|
-
const hasResolvedSigner = typeof address === 'string' && address.length > 0;
|
|
12211
|
-
if (hasResolvedSigner && chain.type === 'evm' && isEvmAdapterLike(adapter)) {
|
|
12212
|
-
await assertSignerIsEoa(adapter, address, chain);
|
|
12213
|
-
}
|
|
12214
|
-
const firstIntent = groupIntents[0];
|
|
12215
|
-
const typedData = groupIntents.length === 1 && firstIntent ? evmSigningData(firstIntent) : evmSigningData(groupIntents);
|
|
12216
|
-
const signRequest = await adapter.prepareAction('gateway.v1.signBurnIntents', {
|
|
12217
|
-
typedData,
|
|
12218
|
-
chain
|
|
12219
|
-
}, operationContext);
|
|
12220
|
-
const sig = await signRequest.execute();
|
|
12221
|
-
return {
|
|
12222
|
-
intents: groupIntents,
|
|
12223
|
-
signature: sig
|
|
12224
|
-
};
|
|
12511
|
+
// Validate the collection before doing bytecode reads or asking a wallet
|
|
12512
|
+
// to sign. evmSigningData owns the canonical BurnIntent validation.
|
|
12513
|
+
validateGroupIntents(group.intents);
|
|
12514
|
+
const chainsByDomain = collectChainsByDomain(group);
|
|
12515
|
+
const signerTypeByDomain = await classifySignerTypes(group, chainsByDomain);
|
|
12516
|
+
const signingUnits = createSigningUnits(group, signerTypeByDomain);
|
|
12517
|
+
return await signUnits(group, signingUnits);
|
|
12225
12518
|
}
|
|
12226
12519
|
|
|
12227
12520
|
/**
|
|
12228
12521
|
* Add an EVM intent into the batched EVM group map.
|
|
12229
12522
|
*
|
|
12230
12523
|
* On EVM, all intents for the same adapter are batched into a single
|
|
12231
|
-
* group
|
|
12232
|
-
*
|
|
12524
|
+
* group. The signing step uses `chainsByDomain` to preserve EOA batching
|
|
12525
|
+
* while signing ERC-1271 intents separately on their source chains.
|
|
12233
12526
|
*
|
|
12234
12527
|
* @param intent - The burn intent to group.
|
|
12235
12528
|
* @param alloc - The allocation that resolved to this intent.
|
|
@@ -12246,6 +12539,7 @@ function evmSigningData(burnIntent) {
|
|
|
12246
12539
|
const existing = evmGroups.get(alloc.adapter);
|
|
12247
12540
|
if (existing) {
|
|
12248
12541
|
existing.intents.push(intent);
|
|
12542
|
+
existing.chainsByDomain.set(alloc.chain.gateway.domain, alloc.chain);
|
|
12249
12543
|
} else {
|
|
12250
12544
|
evmGroups.set(alloc.adapter, {
|
|
12251
12545
|
adapter: alloc.adapter,
|
|
@@ -12253,6 +12547,12 @@ function evmSigningData(burnIntent) {
|
|
|
12253
12547
|
intents: [
|
|
12254
12548
|
intent
|
|
12255
12549
|
],
|
|
12550
|
+
chainsByDomain: new Map([
|
|
12551
|
+
[
|
|
12552
|
+
alloc.chain.gateway.domain,
|
|
12553
|
+
alloc.chain
|
|
12554
|
+
]
|
|
12555
|
+
]),
|
|
12256
12556
|
address: alloc.sourceSigner
|
|
12257
12557
|
});
|
|
12258
12558
|
}
|
|
@@ -13778,7 +14078,8 @@ function throwNetworkMismatch(expected, actual) {
|
|
|
13778
14078
|
};
|
|
13779
14079
|
}
|
|
13780
14080
|
/**
|
|
13781
|
-
* Group intents
|
|
14081
|
+
* Group intents for signing (Solana one-per-intent, EVM batched by adapter
|
|
14082
|
+
* with every source chain retained by Gateway domain).
|
|
13782
14083
|
*
|
|
13783
14084
|
* @param intents - Burn intents from estimate response.
|
|
13784
14085
|
* @param allocations - Normalized allocations used to map domain → adapter/chain.
|
|
@@ -16671,22 +16972,32 @@ const DEFAULT_GAS_FEE = parseUnits('0.1', USDC_DECIMALS);
|
|
|
16671
16972
|
*
|
|
16672
16973
|
* Single-intent sets become one burnIntent + signature; multi-intent sets become burnIntentSet + signature.
|
|
16673
16974
|
*
|
|
16975
|
+
* Sets flagged `contractSigner` carry `contractSigner: true`, which tells
|
|
16976
|
+
* Gateway to validate the signature with ERC-1271 (an offchain
|
|
16977
|
+
* `isValidSignature` simulation) instead of `ecrecover`. The flag is
|
|
16978
|
+
* omitted for EOA signers so their payloads stay byte-identical.
|
|
16979
|
+
*
|
|
16674
16980
|
* @param signedSets - Signed intent sets (intents + signature per signer).
|
|
16675
16981
|
* @returns Array of transfer payloads for POST /v1/transfer.
|
|
16676
16982
|
*/ function buildTransferRequestBody(signedSets) {
|
|
16677
16983
|
return signedSets.map((set)=>{
|
|
16678
16984
|
const firstIntent = set.intents[0];
|
|
16985
|
+
const contractSigner = set.contractSigner === true ? {
|
|
16986
|
+
contractSigner: true
|
|
16987
|
+
} : {};
|
|
16679
16988
|
if (set.intents.length === 1 && firstIntent) {
|
|
16680
16989
|
return {
|
|
16681
16990
|
burnIntent: serializeBurnIntent(firstIntent),
|
|
16682
|
-
signature: set.signature
|
|
16991
|
+
signature: set.signature,
|
|
16992
|
+
...contractSigner
|
|
16683
16993
|
};
|
|
16684
16994
|
}
|
|
16685
16995
|
return {
|
|
16686
16996
|
burnIntentSet: {
|
|
16687
16997
|
intents: set.intents.map(serializeBurnIntent)
|
|
16688
16998
|
},
|
|
16689
|
-
signature: set.signature
|
|
16999
|
+
signature: set.signature,
|
|
17000
|
+
...contractSigner
|
|
16690
17001
|
};
|
|
16691
17002
|
});
|
|
16692
17003
|
}
|
|
@@ -17049,11 +17360,16 @@ const BPS_DIVISOR = 100_000n;
|
|
|
17049
17360
|
return required;
|
|
17050
17361
|
}
|
|
17051
17362
|
|
|
17363
|
+
function requireEvmChainsByDomain(group) {
|
|
17364
|
+
if (group.chainsByDomain !== undefined) return group.chainsByDomain;
|
|
17365
|
+
throw createValidationFailedError$1('adapterGroup.chainsByDomain', group.chainsByDomain, 'must be provided for an EVM adapter group');
|
|
17366
|
+
}
|
|
17052
17367
|
/**
|
|
17053
|
-
* Sign each adapter group: Solana one intent per signature, EVM
|
|
17368
|
+
* Sign each adapter group: Solana one intent per signature, and EVM either
|
|
17369
|
+
* batched for EOAs or split by source chain for ERC-1271 signers.
|
|
17054
17370
|
*
|
|
17055
17371
|
* @param adapterGroups - Groups from groupIntentsByAdapter.
|
|
17056
|
-
* @returns Promise of signed sets
|
|
17372
|
+
* @returns Promise of signed sets for buildTransferRequestBody.
|
|
17057
17373
|
*
|
|
17058
17374
|
* @example
|
|
17059
17375
|
* ```typescript
|
|
@@ -17066,9 +17382,10 @@ const BPS_DIVISOR = 100_000n;
|
|
|
17066
17382
|
if (group.chain.type === 'solana') {
|
|
17067
17383
|
return signSolanaIntentGroup(group);
|
|
17068
17384
|
}
|
|
17069
|
-
return
|
|
17070
|
-
|
|
17071
|
-
|
|
17385
|
+
return await signEvmIntentGroup({
|
|
17386
|
+
...group,
|
|
17387
|
+
chainsByDomain: requireEvmChainsByDomain(group)
|
|
17388
|
+
});
|
|
17072
17389
|
}));
|
|
17073
17390
|
return nested.flat();
|
|
17074
17391
|
}
|
|
@@ -17718,7 +18035,8 @@ async function runSpendNormalPath(params, destChain, useForwarder, dispatcher, s
|
|
|
17718
18035
|
signedSetCount: signedSets.length,
|
|
17719
18036
|
signatures: signedSets.map((s)=>({
|
|
17720
18037
|
intentCount: s.intents.length,
|
|
17721
|
-
signature: s.signature
|
|
18038
|
+
signature: s.signature,
|
|
18039
|
+
contractSigner: s.contractSigner === true
|
|
17722
18040
|
}))
|
|
17723
18041
|
}
|
|
17724
18042
|
});
|
|
@@ -18173,8 +18491,7 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18173
18491
|
chain
|
|
18174
18492
|
};
|
|
18175
18493
|
// Step 1: Quick check at latest block (no HTTP call)
|
|
18176
|
-
const
|
|
18177
|
-
const latestResult = await latestRequest.execute();
|
|
18494
|
+
const latestResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', baseActionParams, operationContext);
|
|
18178
18495
|
if (String(latestResult).toLowerCase() !== 'true') {
|
|
18179
18496
|
return 'none';
|
|
18180
18497
|
}
|
|
@@ -18183,13 +18500,12 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18183
18500
|
// Solana uses confirmed vs finalized commitment as a proxy for
|
|
18184
18501
|
// Gateway finality. This is conservative — can only over-report
|
|
18185
18502
|
// 'pending', never falsely report 'ready'.
|
|
18186
|
-
const finalizedRequest = await adapter.prepareAction('gateway.v1.isDelegate', {
|
|
18187
|
-
...baseActionParams,
|
|
18188
|
-
commitment: 'finalized'
|
|
18189
|
-
}, operationContext);
|
|
18190
18503
|
let finalizedResult;
|
|
18191
18504
|
try {
|
|
18192
|
-
finalizedResult = await
|
|
18505
|
+
finalizedResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', {
|
|
18506
|
+
...baseActionParams,
|
|
18507
|
+
commitment: 'finalized'
|
|
18508
|
+
}, operationContext);
|
|
18193
18509
|
} catch (error) {
|
|
18194
18510
|
if (isBlockRangeError(error)) {
|
|
18195
18511
|
return 'pending';
|
|
@@ -18200,10 +18516,6 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18200
18516
|
}
|
|
18201
18517
|
// EVM: use processedHeight from /v1/info
|
|
18202
18518
|
const processedHeight = await getProcessedHeight(chain.isTestnet, chain.gateway.domain);
|
|
18203
|
-
const finalizedRequest = await adapter.prepareAction('gateway.v1.isDelegate', {
|
|
18204
|
-
...baseActionParams,
|
|
18205
|
-
blockNumber: processedHeight
|
|
18206
|
-
}, operationContext);
|
|
18207
18519
|
// If the RPC node lags Gateway's indexer view, the historical read at
|
|
18208
18520
|
// processedHeight may throw a block-range error. This is safe to treat
|
|
18209
18521
|
// as 'pending' because processedHeight comes from Gateway's /v1/info
|
|
@@ -18212,7 +18524,10 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18212
18524
|
// Re-throw structural errors to avoid masking real bugs.
|
|
18213
18525
|
let finalizedResult;
|
|
18214
18526
|
try {
|
|
18215
|
-
finalizedResult = await
|
|
18527
|
+
finalizedResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', {
|
|
18528
|
+
...baseActionParams,
|
|
18529
|
+
blockNumber: processedHeight
|
|
18530
|
+
}, operationContext);
|
|
18216
18531
|
} catch (error) {
|
|
18217
18532
|
if (isBlockRangeError(error)) {
|
|
18218
18533
|
return 'pending';
|
|
@@ -18273,8 +18588,8 @@ function parseAmountSafe(amount) {
|
|
|
18273
18588
|
chain
|
|
18274
18589
|
};
|
|
18275
18590
|
const [withdrawingRaw, withdrawalBlockRaw] = await Promise.all([
|
|
18276
|
-
adapter
|
|
18277
|
-
adapter
|
|
18591
|
+
executeAdapterReadAction(adapter, 'gateway.v1.withdrawingBalance', readParams, operationContext),
|
|
18592
|
+
executeAdapterReadAction(adapter, 'gateway.v1.withdrawalBlock', readParams, operationContext)
|
|
18278
18593
|
]);
|
|
18279
18594
|
const withdrawingValue = safeBigInt(String(withdrawingRaw), 'withdrawingBalance');
|
|
18280
18595
|
const withdrawalBlockValue = safeBigInt(String(withdrawalBlockRaw), 'withdrawalBlock');
|
|
@@ -18314,12 +18629,11 @@ function parseAmountSafe(amount) {
|
|
|
18314
18629
|
const tokenAddress = getTokenAddress(chain, params.token);
|
|
18315
18630
|
// Read the pending balance before withdrawing — the contract resets it to 0
|
|
18316
18631
|
// after withdraw() executes, so this is the only way to capture the amount.
|
|
18317
|
-
const
|
|
18632
|
+
const withdrawingRaw = await executeAdapterReadAction(adapter, 'gateway.v1.withdrawingBalance', {
|
|
18318
18633
|
token: tokenAddress,
|
|
18319
18634
|
depositor: signerAddress,
|
|
18320
18635
|
chain
|
|
18321
18636
|
}, operationContext);
|
|
18322
|
-
const withdrawingRaw = await withdrawingBalanceReq.execute();
|
|
18323
18637
|
const withdrawingValue = safeBigInt(String(withdrawingRaw), 'withdrawingBalance');
|
|
18324
18638
|
if (withdrawingValue === 0n) {
|
|
18325
18639
|
throw new KitError({
|