@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.mjs
CHANGED
|
@@ -2319,6 +2319,8 @@ class KitError extends Error {
|
|
|
2319
2319
|
Blockchain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
|
|
2320
2320
|
Blockchain["XDC"] = "XDC";
|
|
2321
2321
|
Blockchain["XDC_Apothem"] = "XDC_Apothem";
|
|
2322
|
+
Blockchain["X_Layer"] = "X_Layer";
|
|
2323
|
+
Blockchain["X_Layer_Testnet"] = "X_Layer_Testnet";
|
|
2322
2324
|
Blockchain["ZKSync_Era"] = "ZKSync_Era";
|
|
2323
2325
|
Blockchain["ZKSync_Sepolia"] = "ZKSync_Sepolia";
|
|
2324
2326
|
})(Blockchain || (Blockchain = {}));
|
|
@@ -2372,6 +2374,7 @@ var BridgeChain;
|
|
|
2372
2374
|
BridgeChain["Unichain"] = "Unichain";
|
|
2373
2375
|
BridgeChain["World_Chain"] = "World_Chain";
|
|
2374
2376
|
BridgeChain["XDC"] = "XDC";
|
|
2377
|
+
BridgeChain["X_Layer"] = "X_Layer";
|
|
2375
2378
|
// Testnet chains with CCTPv2 support
|
|
2376
2379
|
BridgeChain["Arc_Testnet"] = "Arc_Testnet";
|
|
2377
2380
|
BridgeChain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
|
|
@@ -2397,6 +2400,7 @@ var BridgeChain;
|
|
|
2397
2400
|
BridgeChain["Unichain_Sepolia"] = "Unichain_Sepolia";
|
|
2398
2401
|
BridgeChain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
|
|
2399
2402
|
BridgeChain["XDC_Apothem"] = "XDC_Apothem";
|
|
2403
|
+
BridgeChain["X_Layer_Testnet"] = "X_Layer_Testnet";
|
|
2400
2404
|
})(BridgeChain || (BridgeChain = {}));
|
|
2401
2405
|
var UnifiedBalanceChain;
|
|
2402
2406
|
(function(UnifiedBalanceChain) {
|
|
@@ -4944,7 +4948,8 @@ var EarnChain;
|
|
|
4944
4948
|
isTestnet: true,
|
|
4945
4949
|
explorerUrl: 'https://amoy.polygonscan.com/tx/{hash}',
|
|
4946
4950
|
rpcEndpoints: [
|
|
4947
|
-
'https://
|
|
4951
|
+
'https://polygon-amoy-bor-rpc.publicnode.com',
|
|
4952
|
+
'https://polygon-amoy.drpc.org'
|
|
4948
4953
|
],
|
|
4949
4954
|
eurcAddress: null,
|
|
4950
4955
|
usdcAddress: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
|
|
@@ -5809,6 +5814,104 @@ var EarnChain;
|
|
|
5809
5814
|
}
|
|
5810
5815
|
});
|
|
5811
5816
|
|
|
5817
|
+
/**
|
|
5818
|
+
* X Layer Mainnet chain definition
|
|
5819
|
+
* @remarks
|
|
5820
|
+
* This represents the official production network for the X Layer blockchain.
|
|
5821
|
+
* X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
|
|
5822
|
+
* using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
|
|
5823
|
+
* OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
|
|
5824
|
+
*/ const XLayer = defineChain({
|
|
5825
|
+
type: 'evm',
|
|
5826
|
+
chain: Blockchain.X_Layer,
|
|
5827
|
+
name: 'X Layer',
|
|
5828
|
+
title: 'X Layer Mainnet',
|
|
5829
|
+
nativeCurrency: {
|
|
5830
|
+
name: 'OKB',
|
|
5831
|
+
symbol: 'OKB',
|
|
5832
|
+
decimals: 18
|
|
5833
|
+
},
|
|
5834
|
+
chainId: 196,
|
|
5835
|
+
isTestnet: false,
|
|
5836
|
+
explorerUrl: 'https://www.oklink.com/xlayer/tx/{hash}',
|
|
5837
|
+
rpcEndpoints: [
|
|
5838
|
+
'https://xlayerrpc.okx.com'
|
|
5839
|
+
],
|
|
5840
|
+
eurcAddress: null,
|
|
5841
|
+
usdcAddress: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
|
|
5842
|
+
usdtAddress: null,
|
|
5843
|
+
cctp: {
|
|
5844
|
+
domain: 37,
|
|
5845
|
+
contracts: {
|
|
5846
|
+
v2: {
|
|
5847
|
+
type: 'split',
|
|
5848
|
+
tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
|
|
5849
|
+
messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
|
|
5850
|
+
confirmations: 65,
|
|
5851
|
+
fastConfirmations: 1
|
|
5852
|
+
}
|
|
5853
|
+
},
|
|
5854
|
+
forwarderSupported: {
|
|
5855
|
+
source: false,
|
|
5856
|
+
destination: false
|
|
5857
|
+
}
|
|
5858
|
+
},
|
|
5859
|
+
kitContracts: {
|
|
5860
|
+
bridge: BRIDGE_CONTRACT_EVM_MAINNET
|
|
5861
|
+
}
|
|
5862
|
+
});
|
|
5863
|
+
|
|
5864
|
+
/**
|
|
5865
|
+
* X Layer Testnet chain definition
|
|
5866
|
+
* @remarks
|
|
5867
|
+
* This represents the official test network for the X Layer blockchain.
|
|
5868
|
+
* X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
|
|
5869
|
+
* using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
|
|
5870
|
+
* OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
|
|
5871
|
+
*/ const XLayerTestnet = defineChain({
|
|
5872
|
+
type: 'evm',
|
|
5873
|
+
chain: Blockchain.X_Layer_Testnet,
|
|
5874
|
+
name: 'X Layer Testnet',
|
|
5875
|
+
title: 'X Layer Testnet',
|
|
5876
|
+
nativeCurrency: {
|
|
5877
|
+
name: 'OKB',
|
|
5878
|
+
symbol: 'OKB',
|
|
5879
|
+
decimals: 18
|
|
5880
|
+
},
|
|
5881
|
+
chainId: 1952,
|
|
5882
|
+
isTestnet: true,
|
|
5883
|
+
// Deliberately not oklink.com (used for mainnet): viem's bundled OKLink
|
|
5884
|
+
// testnet URL targets the deprecated pre-rebrand chain ID 195, not this
|
|
5885
|
+
// chain's ID (1952). Verified against the internal chain-expansion-scripts
|
|
5886
|
+
// config (`v2config.sandbox.yml`) — do not "normalize" this to match mainnet.
|
|
5887
|
+
explorerUrl: 'https://web3.okx.com/explorer/x-layer-testnet/tx/{hash}',
|
|
5888
|
+
rpcEndpoints: [
|
|
5889
|
+
'https://testrpc.xlayer.tech'
|
|
5890
|
+
],
|
|
5891
|
+
eurcAddress: null,
|
|
5892
|
+
usdcAddress: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
|
|
5893
|
+
usdtAddress: null,
|
|
5894
|
+
cctp: {
|
|
5895
|
+
domain: 37,
|
|
5896
|
+
contracts: {
|
|
5897
|
+
v2: {
|
|
5898
|
+
type: 'split',
|
|
5899
|
+
tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
|
|
5900
|
+
messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
|
|
5901
|
+
confirmations: 65,
|
|
5902
|
+
fastConfirmations: 1
|
|
5903
|
+
}
|
|
5904
|
+
},
|
|
5905
|
+
forwarderSupported: {
|
|
5906
|
+
source: false,
|
|
5907
|
+
destination: false
|
|
5908
|
+
}
|
|
5909
|
+
},
|
|
5910
|
+
kitContracts: {
|
|
5911
|
+
bridge: BRIDGE_CONTRACT_EVM_TESTNET
|
|
5912
|
+
}
|
|
5913
|
+
});
|
|
5914
|
+
|
|
5812
5915
|
/**
|
|
5813
5916
|
* ZKSync Era Mainnet chain definition
|
|
5814
5917
|
* @remarks
|
|
@@ -5928,6 +6031,8 @@ var Chains = /*#__PURE__*/Object.freeze({
|
|
|
5928
6031
|
WorldChainSepolia: WorldChainSepolia,
|
|
5929
6032
|
XDC: XDC,
|
|
5930
6033
|
XDCApothem: XDCApothem,
|
|
6034
|
+
XLayer: XLayer,
|
|
6035
|
+
XLayerTestnet: XLayerTestnet,
|
|
5931
6036
|
ZKSyncEra: ZKSyncEra,
|
|
5932
6037
|
ZKSyncEraSepolia: ZKSyncEraSepolia
|
|
5933
6038
|
});
|
|
@@ -7880,6 +7985,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
7880
7985
|
[Blockchain.Unichain]: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
|
|
7881
7986
|
[Blockchain.World_Chain]: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
|
|
7882
7987
|
[Blockchain.XDC]: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
|
|
7988
|
+
[Blockchain.X_Layer]: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
|
|
7883
7989
|
[Blockchain.ZKSync_Era]: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
|
|
7884
7990
|
// =========================================================================
|
|
7885
7991
|
// Testnets (alphabetically sorted)
|
|
@@ -7888,6 +7994,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
7888
7994
|
[Blockchain.Arbitrum_Sepolia]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
|
|
7889
7995
|
[Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
7890
7996
|
[Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
7997
|
+
[Blockchain.Celo_Alfajores_Testnet]: '0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B',
|
|
7891
7998
|
[Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
7892
7999
|
[Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
|
|
7893
8000
|
[Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
|
|
@@ -7914,6 +8021,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
7914
8021
|
[Blockchain.Unichain_Sepolia]: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
|
|
7915
8022
|
[Blockchain.World_Chain_Sepolia]: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
|
|
7916
8023
|
[Blockchain.XDC_Apothem]: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
|
|
8024
|
+
[Blockchain.X_Layer_Testnet]: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
|
|
7917
8025
|
[Blockchain.ZKSync_Sepolia]: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853'
|
|
7918
8026
|
}
|
|
7919
8027
|
};
|
|
@@ -8779,7 +8887,7 @@ function parseOrThrow(value, schema, context) {
|
|
|
8779
8887
|
}
|
|
8780
8888
|
|
|
8781
8889
|
var name = "@circle-fin/unified-balance-kit";
|
|
8782
|
-
var version = "1.
|
|
8890
|
+
var version = "1.4.1";
|
|
8783
8891
|
var pkg = {
|
|
8784
8892
|
name: name,
|
|
8785
8893
|
version: version};
|
|
@@ -9062,6 +9170,110 @@ var pkg = {
|
|
|
9062
9170
|
* ```
|
|
9063
9171
|
*/ const USDC_DECIMALS$1 = 6;
|
|
9064
9172
|
|
|
9173
|
+
/**
|
|
9174
|
+
* Canonical list of actions that do not prepare or submit transactions.
|
|
9175
|
+
*
|
|
9176
|
+
* @internal
|
|
9177
|
+
*/ const READ_ACTION_KEYS = [
|
|
9178
|
+
'token.allowance',
|
|
9179
|
+
'token.balanceOf',
|
|
9180
|
+
'token.name',
|
|
9181
|
+
'native.balanceOf',
|
|
9182
|
+
'usdc.allowance',
|
|
9183
|
+
'usdc.balanceOf',
|
|
9184
|
+
'usdc.name',
|
|
9185
|
+
'gateway.v1.isDelegate',
|
|
9186
|
+
'gateway.v1.withdrawingBalance',
|
|
9187
|
+
'gateway.v1.withdrawalBlock',
|
|
9188
|
+
'gateway.v1.signBurnIntents'
|
|
9189
|
+
];
|
|
9190
|
+
const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
|
|
9191
|
+
/**
|
|
9192
|
+
* Check whether a runtime value identifies a read action.
|
|
9193
|
+
*
|
|
9194
|
+
* @param action - The value to classify.
|
|
9195
|
+
* @returns Whether the value is a registered read-action key.
|
|
9196
|
+
*
|
|
9197
|
+
* @example
|
|
9198
|
+
* ```typescript
|
|
9199
|
+
* import { isReadActionKey } from '@core/adapter'
|
|
9200
|
+
*
|
|
9201
|
+
* if (isReadActionKey(value)) {
|
|
9202
|
+
* await adapter.readAction(value, params, context)
|
|
9203
|
+
* }
|
|
9204
|
+
* ```
|
|
9205
|
+
*
|
|
9206
|
+
* @internal
|
|
9207
|
+
*/ function isReadActionKey(action) {
|
|
9208
|
+
return typeof action === 'string' && READ_ACTION_KEY_SET.has(action);
|
|
9209
|
+
}
|
|
9210
|
+
|
|
9211
|
+
/**
|
|
9212
|
+
* Create the standard error for a missing or non-read action.
|
|
9213
|
+
*
|
|
9214
|
+
* @param action - The unsupported action value.
|
|
9215
|
+
* @returns A fatal unsupported-action error.
|
|
9216
|
+
*
|
|
9217
|
+
* @internal
|
|
9218
|
+
*/ function createUnsupportedReadActionError(action) {
|
|
9219
|
+
return new KitError({
|
|
9220
|
+
...InputError.UNSUPPORTED_ACTION,
|
|
9221
|
+
recoverability: 'FATAL',
|
|
9222
|
+
message: `Read action "${String(action)}" is not registered in this adapter.`
|
|
9223
|
+
});
|
|
9224
|
+
}
|
|
9225
|
+
/**
|
|
9226
|
+
* Execute a read through the adapter's dedicated read seam when available.
|
|
9227
|
+
*
|
|
9228
|
+
* @remarks
|
|
9229
|
+
* Fall back to the legacy `prepareAction().execute()` contract so providers
|
|
9230
|
+
* remain runtime-compatible with adapter versions released before `readAction`.
|
|
9231
|
+
* Consumers must upgrade their adapter package for reads to bypass custom
|
|
9232
|
+
* `prepareAction` wrappers.
|
|
9233
|
+
*
|
|
9234
|
+
* @typeParam TAdapterCapabilities - The adapter capabilities type.
|
|
9235
|
+
* @typeParam TActionKey - The read action key.
|
|
9236
|
+
* @param adapter - The adapter that owns the read action.
|
|
9237
|
+
* @param action - The read action to execute.
|
|
9238
|
+
* @param params - The parameters for the read action.
|
|
9239
|
+
* @param ctx - The operation context.
|
|
9240
|
+
* @returns The raw read-action result.
|
|
9241
|
+
* @throws {KitError} When `action` is not a supported read-action key.
|
|
9242
|
+
*
|
|
9243
|
+
* @example
|
|
9244
|
+
* ```typescript
|
|
9245
|
+
* import { executeAdapterReadAction } from '@core/adapter'
|
|
9246
|
+
* import { Ethereum } from '@core/chains'
|
|
9247
|
+
*
|
|
9248
|
+
* const allowance = await executeAdapterReadAction(
|
|
9249
|
+
* adapter,
|
|
9250
|
+
* 'token.allowance',
|
|
9251
|
+
* { tokenAddress, delegate },
|
|
9252
|
+
* { chain: Ethereum },
|
|
9253
|
+
* )
|
|
9254
|
+
* ```
|
|
9255
|
+
*
|
|
9256
|
+
* @internal
|
|
9257
|
+
*/ async function executeAdapterReadAction(adapter, action, params, ctx) {
|
|
9258
|
+
if (!isReadActionKey(action)) {
|
|
9259
|
+
throw createUnsupportedReadActionError(action);
|
|
9260
|
+
}
|
|
9261
|
+
const runtimeAdapter = adapter;
|
|
9262
|
+
if (typeof runtimeAdapter.readAction === 'function') {
|
|
9263
|
+
return runtimeAdapter.readAction(action, params, ctx);
|
|
9264
|
+
}
|
|
9265
|
+
let request;
|
|
9266
|
+
try {
|
|
9267
|
+
request = await adapter.prepareAction(action, params, ctx);
|
|
9268
|
+
} catch (error) {
|
|
9269
|
+
if (error instanceof Error && error.message === `Action ${action} is not supported`) {
|
|
9270
|
+
throw createUnsupportedReadActionError(action);
|
|
9271
|
+
}
|
|
9272
|
+
throw error;
|
|
9273
|
+
}
|
|
9274
|
+
return request.execute();
|
|
9275
|
+
}
|
|
9276
|
+
|
|
9065
9277
|
/**
|
|
9066
9278
|
* Schema for validating hexadecimal strings with '0x' prefix.
|
|
9067
9279
|
*
|
|
@@ -9271,16 +9483,15 @@ var pkg = {
|
|
|
9271
9483
|
* ```
|
|
9272
9484
|
*/ const validateBalanceForTransaction = async (params)=>{
|
|
9273
9485
|
const { amount, adapter, token, tokenAddress, operationContext } = params;
|
|
9274
|
-
const
|
|
9486
|
+
const balance = await executeAdapterReadAction(adapter, 'usdc.balanceOf', {
|
|
9275
9487
|
walletAddress: operationContext.address
|
|
9276
9488
|
}, operationContext);
|
|
9277
|
-
|
|
9278
|
-
if (BigInt(balance) < BigInt(amount)) {
|
|
9489
|
+
if (BigInt(String(balance)) < BigInt(amount)) {
|
|
9279
9490
|
// Extract chain name from operationContext
|
|
9280
9491
|
const chainName = extractChainInfo(operationContext.chain).name;
|
|
9281
9492
|
// Create KitError with rich context in trace
|
|
9282
9493
|
throw createInsufficientTokenBalanceError(chainName, token, {
|
|
9283
|
-
balance: balance
|
|
9494
|
+
balance: String(balance),
|
|
9284
9495
|
amount,
|
|
9285
9496
|
tokenAddress,
|
|
9286
9497
|
walletAddress: operationContext.address
|
|
@@ -12060,72 +12271,55 @@ function evmSigningData(burnIntent) {
|
|
|
12060
12271
|
* `0xef0100` followed by the 20-byte delegate address (23 bytes total).
|
|
12061
12272
|
* The underlying secp256k1 key still produces `ecrecover`-verifiable
|
|
12062
12273
|
* signatures, so for Gateway's purposes a 7702-delegated address is
|
|
12063
|
-
* an EOA, not
|
|
12274
|
+
* an EOA, not a contract signer.
|
|
12064
12275
|
*
|
|
12065
12276
|
* Spec: https://eips.ethereum.org/EIPS/eip-7702
|
|
12066
12277
|
*/ const EIP_7702_DELEGATION_PREFIX = '0xef0100';
|
|
12067
12278
|
/**
|
|
12068
|
-
*
|
|
12069
|
-
*
|
|
12070
|
-
* Gateway verifies burn-intent signatures with plain `ecrecover` (see
|
|
12071
|
-
* `evm-gateway-contracts/src/lib/EIP712Domain.sol`). Smart-contract
|
|
12072
|
-
* accounts (SCAs) produce signatures over wrapped hashes (ERC-1271 /
|
|
12073
|
-
* ERC-6492 / ERC-6900 replay-safe hashes) that Gateway cannot verify.
|
|
12074
|
-
* Additionally, the Circle Wallets backend rejects SCA typed-data signing
|
|
12075
|
-
* against Gateway's chainId-less domain with an opaque
|
|
12076
|
-
* `invalid integer value <nil>/<nil> for type uint256` error.
|
|
12279
|
+
* Determine whether `address` on `chain` signs as a contract (ERC-1271)
|
|
12280
|
+
* rather than as an EOA.
|
|
12077
12281
|
*
|
|
12078
|
-
*
|
|
12079
|
-
*
|
|
12080
|
-
* `
|
|
12282
|
+
* Gateway validates burn-intent signatures two ways: a static `ecrecover`
|
|
12283
|
+
* check for EOAs, and — for requests that carry `contractSigner: true` —
|
|
12284
|
+
* an offchain `isValidSignature` simulation against the signing contract
|
|
12285
|
+
* (ERC-1271). Gateway does not infer which one to use, so the caller must
|
|
12286
|
+
* declare it. This detects the contract case from on-chain bytecode.
|
|
12081
12287
|
*
|
|
12082
|
-
*
|
|
12083
|
-
*
|
|
12084
|
-
*
|
|
12085
|
-
*
|
|
12086
|
-
* docs for the exact API.
|
|
12288
|
+
* EIP-7702-delegated EOAs are treated as EOAs: they expose non-empty
|
|
12289
|
+
* bytecode (`0xef0100<delegate>`) but the underlying secp256k1 key still
|
|
12290
|
+
* produces `ecrecover`-verifiable signatures, so the cheaper EOA path
|
|
12291
|
+
* stays correct for them.
|
|
12087
12292
|
*
|
|
12088
|
-
* If bytecode cannot be read (RPC failure, etc.) the
|
|
12089
|
-
*
|
|
12090
|
-
*
|
|
12293
|
+
* If bytecode cannot be read (RPC failure, etc.) the address is reported
|
|
12294
|
+
* as an EOA and a warning is logged so the fallback is diagnosable. A
|
|
12295
|
+
* genuine contract signer misreported this way is rejected by Gateway with
|
|
12296
|
+
* an invalid-signature error rather than silently mis-attested.
|
|
12091
12297
|
*
|
|
12092
12298
|
* @param adapter - Anything exposing {@link EvmAdapterLike.readBytecode}.
|
|
12093
|
-
* @param address - Signer address to
|
|
12299
|
+
* @param address - Signer address to classify.
|
|
12094
12300
|
* @param chain - EVM chain where the signer lives.
|
|
12095
|
-
* @
|
|
12301
|
+
* @returns `true` when the signer is a contract account and the transfer
|
|
12302
|
+
* request must set `contractSigner: true`; `false` otherwise.
|
|
12096
12303
|
*
|
|
12097
12304
|
* @example
|
|
12098
12305
|
* ```typescript
|
|
12099
|
-
* import {
|
|
12306
|
+
* import { isContractSigner } from '@core/adapter-evm'
|
|
12100
12307
|
* import { Ethereum } from '@core/chains'
|
|
12101
12308
|
*
|
|
12102
|
-
* await
|
|
12309
|
+
* const useErc1271 = await isContractSigner(adapter, '0xabc...', Ethereum)
|
|
12103
12310
|
* ```
|
|
12104
|
-
*/ async function
|
|
12311
|
+
*/ async function isContractSigner(adapter, address, chain) {
|
|
12105
12312
|
let code;
|
|
12106
12313
|
try {
|
|
12107
12314
|
code = await adapter.readBytecode(address, chain);
|
|
12108
12315
|
} catch (err) {
|
|
12109
|
-
console.warn(`[gateway]
|
|
12110
|
-
return;
|
|
12316
|
+
console.warn(`[gateway] isContractSigner defaulting to EOA (readBytecode failed ` + `for ${address} on ${chain.name}): ` + (err instanceof Error ? err.message : String(err)));
|
|
12317
|
+
return false;
|
|
12111
12318
|
}
|
|
12112
12319
|
if (code === undefined || code === '0x' || code.toLowerCase().startsWith(EIP_7702_DELEGATION_PREFIX)) {
|
|
12113
|
-
return;
|
|
12320
|
+
return false;
|
|
12114
12321
|
}
|
|
12115
|
-
|
|
12116
|
-
...InputError.UNSUPPORTED_ACTION,
|
|
12117
|
-
recoverability: 'FATAL',
|
|
12118
|
-
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.`,
|
|
12119
|
-
cause: {
|
|
12120
|
-
trace: {
|
|
12121
|
-
operation: 'signEvmIntentGroup.assertSignerIsEoa',
|
|
12122
|
-
address,
|
|
12123
|
-
chain: chain.name,
|
|
12124
|
-
bytecodeBytes: (code.length - 2) / 2,
|
|
12125
|
-
bytecodePrefix: code.slice(0, 12)
|
|
12126
|
-
}
|
|
12127
|
-
}
|
|
12128
|
-
});
|
|
12322
|
+
return true;
|
|
12129
12323
|
}
|
|
12130
12324
|
|
|
12131
12325
|
/**
|
|
@@ -12152,78 +12346,177 @@ function evmSigningData(burnIntent) {
|
|
|
12152
12346
|
return typeof value === 'object' && value !== null && 'readBytecode' in value && typeof value.readBytecode === 'function';
|
|
12153
12347
|
}
|
|
12154
12348
|
|
|
12349
|
+
function resolveIntentChain(group, intent) {
|
|
12350
|
+
const sourceDomain = intent.spec.sourceDomain;
|
|
12351
|
+
const chain = group.chainsByDomain.get(sourceDomain);
|
|
12352
|
+
if (chain !== undefined) return chain;
|
|
12353
|
+
throw createValidationFailedError$1('intent.spec.sourceDomain', sourceDomain, `No source chain found for Gateway domain ${String(sourceDomain)}`);
|
|
12354
|
+
}
|
|
12355
|
+
function normalizeSignatureResult(result) {
|
|
12356
|
+
if (typeof result === 'string') {
|
|
12357
|
+
return {
|
|
12358
|
+
signature: result,
|
|
12359
|
+
contractSigner: false
|
|
12360
|
+
};
|
|
12361
|
+
}
|
|
12362
|
+
if (typeof result === 'object' && result !== null && 'signature' in result && typeof result.signature === 'string') {
|
|
12363
|
+
return {
|
|
12364
|
+
signature: result.signature,
|
|
12365
|
+
contractSigner: 'contractSigner' in result && result.contractSigner === true
|
|
12366
|
+
};
|
|
12367
|
+
}
|
|
12368
|
+
throw createValidationFailedError$1('signature', result, 'must be a signature string or an object containing a signature string');
|
|
12369
|
+
}
|
|
12370
|
+
function validateGroupIntents(intents) {
|
|
12371
|
+
evmSigningData(intents);
|
|
12372
|
+
}
|
|
12373
|
+
function collectChainsByDomain(group) {
|
|
12374
|
+
const chainsByDomain = new Map();
|
|
12375
|
+
for (const intent of group.intents){
|
|
12376
|
+
chainsByDomain.set(intent.spec.sourceDomain, resolveIntentChain(group, intent));
|
|
12377
|
+
}
|
|
12378
|
+
return chainsByDomain;
|
|
12379
|
+
}
|
|
12380
|
+
async function classifySignerTypes(group, chainsByDomain) {
|
|
12381
|
+
const { adapter, address } = group;
|
|
12382
|
+
// Duck-typed on readBytecode rather than `instanceof EvmAdapter` because
|
|
12383
|
+
// each consumer package bundles its own copy of the base class and the
|
|
12384
|
+
// `instanceof` identity check fails across package boundaries.
|
|
12385
|
+
// Empty strings are rejected to avoid calling eth_getCode('') on the RPC.
|
|
12386
|
+
const hasResolvedSigner = typeof address === 'string' && address.length > 0;
|
|
12387
|
+
const signerTypes = await Promise.all([
|
|
12388
|
+
...chainsByDomain
|
|
12389
|
+
].map(async ([sourceDomain, sourceChain])=>{
|
|
12390
|
+
const contractSigner = hasResolvedSigner && sourceChain.type === 'evm' && isEvmAdapterLike(adapter) ? await isContractSigner(adapter, address, sourceChain) : false;
|
|
12391
|
+
return [
|
|
12392
|
+
sourceDomain,
|
|
12393
|
+
contractSigner
|
|
12394
|
+
];
|
|
12395
|
+
}));
|
|
12396
|
+
return new Map(signerTypes);
|
|
12397
|
+
}
|
|
12398
|
+
function createSigningUnits(group, signerTypeByDomain) {
|
|
12399
|
+
const contractUnitsByDomain = new Map();
|
|
12400
|
+
let eoaUnit;
|
|
12401
|
+
for (const [index, intent] of group.intents.entries()){
|
|
12402
|
+
const sourceDomain = intent.spec.sourceDomain;
|
|
12403
|
+
const contractSigner = signerTypeByDomain.get(sourceDomain) ?? false;
|
|
12404
|
+
if (contractSigner) {
|
|
12405
|
+
const existingUnit = contractUnitsByDomain.get(sourceDomain);
|
|
12406
|
+
if (existingUnit === undefined) {
|
|
12407
|
+
contractUnitsByDomain.set(sourceDomain, {
|
|
12408
|
+
intents: [
|
|
12409
|
+
intent
|
|
12410
|
+
],
|
|
12411
|
+
chain: resolveIntentChain(group, intent),
|
|
12412
|
+
contractSigner: true,
|
|
12413
|
+
firstIntentIndex: index
|
|
12414
|
+
});
|
|
12415
|
+
} else {
|
|
12416
|
+
existingUnit.intents.push(intent);
|
|
12417
|
+
}
|
|
12418
|
+
} else {
|
|
12419
|
+
eoaUnit ??= {
|
|
12420
|
+
intents: [],
|
|
12421
|
+
chain: resolveIntentChain(group, intent),
|
|
12422
|
+
contractSigner: false,
|
|
12423
|
+
firstIntentIndex: index
|
|
12424
|
+
};
|
|
12425
|
+
eoaUnit.intents.push(intent);
|
|
12426
|
+
}
|
|
12427
|
+
}
|
|
12428
|
+
const signingUnits = [
|
|
12429
|
+
...contractUnitsByDomain.values()
|
|
12430
|
+
];
|
|
12431
|
+
if (eoaUnit !== undefined) signingUnits.push(eoaUnit);
|
|
12432
|
+
signingUnits.sort((a, b)=>a.firstIntentIndex - b.firstIntentIndex);
|
|
12433
|
+
return signingUnits;
|
|
12434
|
+
}
|
|
12435
|
+
async function signUnit(group, unit) {
|
|
12436
|
+
const { adapter, address } = group;
|
|
12437
|
+
const firstIntent = unit.intents[0];
|
|
12438
|
+
const typedData = unit.intents.length === 1 && firstIntent !== undefined ? evmSigningData(firstIntent) : evmSigningData(unit.intents);
|
|
12439
|
+
const operationContext = address === undefined ? {
|
|
12440
|
+
chain: unit.chain
|
|
12441
|
+
} : {
|
|
12442
|
+
chain: unit.chain,
|
|
12443
|
+
address
|
|
12444
|
+
};
|
|
12445
|
+
const signRequest = await adapter.prepareAction('gateway.v1.signBurnIntents', {
|
|
12446
|
+
typedData,
|
|
12447
|
+
chain: unit.chain
|
|
12448
|
+
}, operationContext);
|
|
12449
|
+
const result = normalizeSignatureResult(await signRequest.execute());
|
|
12450
|
+
return {
|
|
12451
|
+
intents: unit.intents,
|
|
12452
|
+
signature: result.signature,
|
|
12453
|
+
contractSigner: result.contractSigner || unit.contractSigner
|
|
12454
|
+
};
|
|
12455
|
+
}
|
|
12456
|
+
async function signUnits(group, signingUnits) {
|
|
12457
|
+
const signedSets = [];
|
|
12458
|
+
// Keep wallet prompts deterministic. Multiple adapter groups can still sign
|
|
12459
|
+
// in parallel, but one signer is asked for its chain-bound signatures in
|
|
12460
|
+
// source-intent order.
|
|
12461
|
+
for (const unit of signingUnits){
|
|
12462
|
+
signedSets.push(await signUnit(group, unit));
|
|
12463
|
+
}
|
|
12464
|
+
return signedSets;
|
|
12465
|
+
}
|
|
12155
12466
|
/**
|
|
12156
|
-
* Sign an EVM adapter group
|
|
12157
|
-
* EIP-712 ECDSA signature.
|
|
12467
|
+
* Sign an EVM adapter group.
|
|
12158
12468
|
*
|
|
12159
|
-
*
|
|
12160
|
-
*
|
|
12469
|
+
* EOA intents remain batched into one EIP-712 `BurnIntentSet`. ERC-1271
|
|
12470
|
+
* intents are grouped and signed per source chain because smart accounts
|
|
12471
|
+
* commonly include `chainId` in their replay-safe signature hash.
|
|
12472
|
+
* All returned entries can still be submitted together in one atomic Gateway
|
|
12473
|
+
* transfer request.
|
|
12161
12474
|
*
|
|
12162
|
-
* Before signing,
|
|
12163
|
-
*
|
|
12164
|
-
*
|
|
12165
|
-
*
|
|
12166
|
-
*
|
|
12475
|
+
* Before signing, classifies the signer as an EOA or a contract account.
|
|
12476
|
+
* Gateway validates EOA signatures with `ecrecover` and contract-account
|
|
12477
|
+
* signatures with ERC-1271, but it does not infer which one applies — the
|
|
12478
|
+
* transfer request has to declare it. The returned `contractSigner` flag
|
|
12479
|
+
* carries that decision through to `buildTransferRequestBody`.
|
|
12167
12480
|
*
|
|
12168
12481
|
* @param group - The adapter group containing the adapter, chain, and
|
|
12169
12482
|
* burn intents to sign.
|
|
12170
|
-
* @returns
|
|
12483
|
+
* @returns Signed entries with their intents, signatures, and Gateway signer
|
|
12484
|
+
* validation mode.
|
|
12485
|
+
* @throws KitError when an intent has no source-chain mapping or a signing
|
|
12486
|
+
* action returns an invalid signature shape.
|
|
12171
12487
|
*
|
|
12172
12488
|
* @example
|
|
12173
12489
|
* ```typescript
|
|
12174
12490
|
* import { signEvmIntentGroup } from '@core/adapter-evm'
|
|
12175
12491
|
*
|
|
12176
|
-
* const
|
|
12492
|
+
* const signedSets = await signEvmIntentGroup({
|
|
12177
12493
|
* adapter: evmAdapter,
|
|
12178
12494
|
* chain: ethereumChain,
|
|
12179
12495
|
* intents: [burnIntent1, burnIntent2],
|
|
12496
|
+
* chainsByDomain: new Map([
|
|
12497
|
+
* [0, ethereumChain],
|
|
12498
|
+
* [6, baseChain],
|
|
12499
|
+
* ]),
|
|
12180
12500
|
* address: '0x...',
|
|
12181
12501
|
* })
|
|
12182
|
-
* console.log(
|
|
12502
|
+
* console.log(signedSets)
|
|
12183
12503
|
* ```
|
|
12184
12504
|
*/ async function signEvmIntentGroup(group) {
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
// Gateway verifies burn-intent signatures with plain ecrecover. An SCA
|
|
12193
|
-
// signer silently produces a signature over a wrapped hash that Gateway
|
|
12194
|
-
// cannot verify, and Circle Wallets' KMS rejects the typed data up front
|
|
12195
|
-
// with an opaque `<nil>/<nil>` error. Short-circuit with a clear message
|
|
12196
|
-
// when we can detect bytecode at the signer address. See DEVX-2774.
|
|
12197
|
-
//
|
|
12198
|
-
// Duck-typed on readBytecode rather than `instanceof EvmAdapter` because
|
|
12199
|
-
// each consumer package bundles its own copy of the base class and the
|
|
12200
|
-
// `instanceof` identity check fails across package boundaries.
|
|
12201
|
-
//
|
|
12202
|
-
// Empty string is defended against because assertSignerIsEoa would
|
|
12203
|
-
// otherwise call eth_getCode('') on the RPC.
|
|
12204
|
-
const hasResolvedSigner = typeof address === 'string' && address.length > 0;
|
|
12205
|
-
if (hasResolvedSigner && chain.type === 'evm' && isEvmAdapterLike(adapter)) {
|
|
12206
|
-
await assertSignerIsEoa(adapter, address, chain);
|
|
12207
|
-
}
|
|
12208
|
-
const firstIntent = groupIntents[0];
|
|
12209
|
-
const typedData = groupIntents.length === 1 && firstIntent ? evmSigningData(firstIntent) : evmSigningData(groupIntents);
|
|
12210
|
-
const signRequest = await adapter.prepareAction('gateway.v1.signBurnIntents', {
|
|
12211
|
-
typedData,
|
|
12212
|
-
chain
|
|
12213
|
-
}, operationContext);
|
|
12214
|
-
const sig = await signRequest.execute();
|
|
12215
|
-
return {
|
|
12216
|
-
intents: groupIntents,
|
|
12217
|
-
signature: sig
|
|
12218
|
-
};
|
|
12505
|
+
// Validate the collection before doing bytecode reads or asking a wallet
|
|
12506
|
+
// to sign. evmSigningData owns the canonical BurnIntent validation.
|
|
12507
|
+
validateGroupIntents(group.intents);
|
|
12508
|
+
const chainsByDomain = collectChainsByDomain(group);
|
|
12509
|
+
const signerTypeByDomain = await classifySignerTypes(group, chainsByDomain);
|
|
12510
|
+
const signingUnits = createSigningUnits(group, signerTypeByDomain);
|
|
12511
|
+
return await signUnits(group, signingUnits);
|
|
12219
12512
|
}
|
|
12220
12513
|
|
|
12221
12514
|
/**
|
|
12222
12515
|
* Add an EVM intent into the batched EVM group map.
|
|
12223
12516
|
*
|
|
12224
12517
|
* On EVM, all intents for the same adapter are batched into a single
|
|
12225
|
-
* group
|
|
12226
|
-
*
|
|
12518
|
+
* group. The signing step uses `chainsByDomain` to preserve EOA batching
|
|
12519
|
+
* while signing ERC-1271 intents separately on their source chains.
|
|
12227
12520
|
*
|
|
12228
12521
|
* @param intent - The burn intent to group.
|
|
12229
12522
|
* @param alloc - The allocation that resolved to this intent.
|
|
@@ -12240,6 +12533,7 @@ function evmSigningData(burnIntent) {
|
|
|
12240
12533
|
const existing = evmGroups.get(alloc.adapter);
|
|
12241
12534
|
if (existing) {
|
|
12242
12535
|
existing.intents.push(intent);
|
|
12536
|
+
existing.chainsByDomain.set(alloc.chain.gateway.domain, alloc.chain);
|
|
12243
12537
|
} else {
|
|
12244
12538
|
evmGroups.set(alloc.adapter, {
|
|
12245
12539
|
adapter: alloc.adapter,
|
|
@@ -12247,6 +12541,12 @@ function evmSigningData(burnIntent) {
|
|
|
12247
12541
|
intents: [
|
|
12248
12542
|
intent
|
|
12249
12543
|
],
|
|
12544
|
+
chainsByDomain: new Map([
|
|
12545
|
+
[
|
|
12546
|
+
alloc.chain.gateway.domain,
|
|
12547
|
+
alloc.chain
|
|
12548
|
+
]
|
|
12549
|
+
]),
|
|
12250
12550
|
address: alloc.sourceSigner
|
|
12251
12551
|
});
|
|
12252
12552
|
}
|
|
@@ -13772,7 +14072,8 @@ function throwNetworkMismatch(expected, actual) {
|
|
|
13772
14072
|
};
|
|
13773
14073
|
}
|
|
13774
14074
|
/**
|
|
13775
|
-
* Group intents
|
|
14075
|
+
* Group intents for signing (Solana one-per-intent, EVM batched by adapter
|
|
14076
|
+
* with every source chain retained by Gateway domain).
|
|
13776
14077
|
*
|
|
13777
14078
|
* @param intents - Burn intents from estimate response.
|
|
13778
14079
|
* @param allocations - Normalized allocations used to map domain → adapter/chain.
|
|
@@ -16665,22 +16966,32 @@ const DEFAULT_GAS_FEE = parseUnits('0.1', USDC_DECIMALS);
|
|
|
16665
16966
|
*
|
|
16666
16967
|
* Single-intent sets become one burnIntent + signature; multi-intent sets become burnIntentSet + signature.
|
|
16667
16968
|
*
|
|
16969
|
+
* Sets flagged `contractSigner` carry `contractSigner: true`, which tells
|
|
16970
|
+
* Gateway to validate the signature with ERC-1271 (an offchain
|
|
16971
|
+
* `isValidSignature` simulation) instead of `ecrecover`. The flag is
|
|
16972
|
+
* omitted for EOA signers so their payloads stay byte-identical.
|
|
16973
|
+
*
|
|
16668
16974
|
* @param signedSets - Signed intent sets (intents + signature per signer).
|
|
16669
16975
|
* @returns Array of transfer payloads for POST /v1/transfer.
|
|
16670
16976
|
*/ function buildTransferRequestBody(signedSets) {
|
|
16671
16977
|
return signedSets.map((set)=>{
|
|
16672
16978
|
const firstIntent = set.intents[0];
|
|
16979
|
+
const contractSigner = set.contractSigner === true ? {
|
|
16980
|
+
contractSigner: true
|
|
16981
|
+
} : {};
|
|
16673
16982
|
if (set.intents.length === 1 && firstIntent) {
|
|
16674
16983
|
return {
|
|
16675
16984
|
burnIntent: serializeBurnIntent(firstIntent),
|
|
16676
|
-
signature: set.signature
|
|
16985
|
+
signature: set.signature,
|
|
16986
|
+
...contractSigner
|
|
16677
16987
|
};
|
|
16678
16988
|
}
|
|
16679
16989
|
return {
|
|
16680
16990
|
burnIntentSet: {
|
|
16681
16991
|
intents: set.intents.map(serializeBurnIntent)
|
|
16682
16992
|
},
|
|
16683
|
-
signature: set.signature
|
|
16993
|
+
signature: set.signature,
|
|
16994
|
+
...contractSigner
|
|
16684
16995
|
};
|
|
16685
16996
|
});
|
|
16686
16997
|
}
|
|
@@ -17043,11 +17354,16 @@ const BPS_DIVISOR = 100_000n;
|
|
|
17043
17354
|
return required;
|
|
17044
17355
|
}
|
|
17045
17356
|
|
|
17357
|
+
function requireEvmChainsByDomain(group) {
|
|
17358
|
+
if (group.chainsByDomain !== undefined) return group.chainsByDomain;
|
|
17359
|
+
throw createValidationFailedError$1('adapterGroup.chainsByDomain', group.chainsByDomain, 'must be provided for an EVM adapter group');
|
|
17360
|
+
}
|
|
17046
17361
|
/**
|
|
17047
|
-
* Sign each adapter group: Solana one intent per signature, EVM
|
|
17362
|
+
* Sign each adapter group: Solana one intent per signature, and EVM either
|
|
17363
|
+
* batched for EOAs or split by source chain for ERC-1271 signers.
|
|
17048
17364
|
*
|
|
17049
17365
|
* @param adapterGroups - Groups from groupIntentsByAdapter.
|
|
17050
|
-
* @returns Promise of signed sets
|
|
17366
|
+
* @returns Promise of signed sets for buildTransferRequestBody.
|
|
17051
17367
|
*
|
|
17052
17368
|
* @example
|
|
17053
17369
|
* ```typescript
|
|
@@ -17060,9 +17376,10 @@ const BPS_DIVISOR = 100_000n;
|
|
|
17060
17376
|
if (group.chain.type === 'solana') {
|
|
17061
17377
|
return signSolanaIntentGroup(group);
|
|
17062
17378
|
}
|
|
17063
|
-
return
|
|
17064
|
-
|
|
17065
|
-
|
|
17379
|
+
return await signEvmIntentGroup({
|
|
17380
|
+
...group,
|
|
17381
|
+
chainsByDomain: requireEvmChainsByDomain(group)
|
|
17382
|
+
});
|
|
17066
17383
|
}));
|
|
17067
17384
|
return nested.flat();
|
|
17068
17385
|
}
|
|
@@ -17712,7 +18029,8 @@ async function runSpendNormalPath(params, destChain, useForwarder, dispatcher, s
|
|
|
17712
18029
|
signedSetCount: signedSets.length,
|
|
17713
18030
|
signatures: signedSets.map((s)=>({
|
|
17714
18031
|
intentCount: s.intents.length,
|
|
17715
|
-
signature: s.signature
|
|
18032
|
+
signature: s.signature,
|
|
18033
|
+
contractSigner: s.contractSigner === true
|
|
17716
18034
|
}))
|
|
17717
18035
|
}
|
|
17718
18036
|
});
|
|
@@ -18167,8 +18485,7 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18167
18485
|
chain
|
|
18168
18486
|
};
|
|
18169
18487
|
// Step 1: Quick check at latest block (no HTTP call)
|
|
18170
|
-
const
|
|
18171
|
-
const latestResult = await latestRequest.execute();
|
|
18488
|
+
const latestResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', baseActionParams, operationContext);
|
|
18172
18489
|
if (String(latestResult).toLowerCase() !== 'true') {
|
|
18173
18490
|
return 'none';
|
|
18174
18491
|
}
|
|
@@ -18177,13 +18494,12 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18177
18494
|
// Solana uses confirmed vs finalized commitment as a proxy for
|
|
18178
18495
|
// Gateway finality. This is conservative — can only over-report
|
|
18179
18496
|
// 'pending', never falsely report 'ready'.
|
|
18180
|
-
const finalizedRequest = await adapter.prepareAction('gateway.v1.isDelegate', {
|
|
18181
|
-
...baseActionParams,
|
|
18182
|
-
commitment: 'finalized'
|
|
18183
|
-
}, operationContext);
|
|
18184
18497
|
let finalizedResult;
|
|
18185
18498
|
try {
|
|
18186
|
-
finalizedResult = await
|
|
18499
|
+
finalizedResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', {
|
|
18500
|
+
...baseActionParams,
|
|
18501
|
+
commitment: 'finalized'
|
|
18502
|
+
}, operationContext);
|
|
18187
18503
|
} catch (error) {
|
|
18188
18504
|
if (isBlockRangeError(error)) {
|
|
18189
18505
|
return 'pending';
|
|
@@ -18194,10 +18510,6 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18194
18510
|
}
|
|
18195
18511
|
// EVM: use processedHeight from /v1/info
|
|
18196
18512
|
const processedHeight = await getProcessedHeight(chain.isTestnet, chain.gateway.domain);
|
|
18197
|
-
const finalizedRequest = await adapter.prepareAction('gateway.v1.isDelegate', {
|
|
18198
|
-
...baseActionParams,
|
|
18199
|
-
blockNumber: processedHeight
|
|
18200
|
-
}, operationContext);
|
|
18201
18513
|
// If the RPC node lags Gateway's indexer view, the historical read at
|
|
18202
18514
|
// processedHeight may throw a block-range error. This is safe to treat
|
|
18203
18515
|
// as 'pending' because processedHeight comes from Gateway's /v1/info
|
|
@@ -18206,7 +18518,10 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
|
|
|
18206
18518
|
// Re-throw structural errors to avoid masking real bugs.
|
|
18207
18519
|
let finalizedResult;
|
|
18208
18520
|
try {
|
|
18209
|
-
finalizedResult = await
|
|
18521
|
+
finalizedResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', {
|
|
18522
|
+
...baseActionParams,
|
|
18523
|
+
blockNumber: processedHeight
|
|
18524
|
+
}, operationContext);
|
|
18210
18525
|
} catch (error) {
|
|
18211
18526
|
if (isBlockRangeError(error)) {
|
|
18212
18527
|
return 'pending';
|
|
@@ -18267,8 +18582,8 @@ function parseAmountSafe(amount) {
|
|
|
18267
18582
|
chain
|
|
18268
18583
|
};
|
|
18269
18584
|
const [withdrawingRaw, withdrawalBlockRaw] = await Promise.all([
|
|
18270
|
-
adapter
|
|
18271
|
-
adapter
|
|
18585
|
+
executeAdapterReadAction(adapter, 'gateway.v1.withdrawingBalance', readParams, operationContext),
|
|
18586
|
+
executeAdapterReadAction(adapter, 'gateway.v1.withdrawalBlock', readParams, operationContext)
|
|
18272
18587
|
]);
|
|
18273
18588
|
const withdrawingValue = safeBigInt(String(withdrawingRaw), 'withdrawingBalance');
|
|
18274
18589
|
const withdrawalBlockValue = safeBigInt(String(withdrawalBlockRaw), 'withdrawalBlock');
|
|
@@ -18308,12 +18623,11 @@ function parseAmountSafe(amount) {
|
|
|
18308
18623
|
const tokenAddress = getTokenAddress(chain, params.token);
|
|
18309
18624
|
// Read the pending balance before withdrawing — the contract resets it to 0
|
|
18310
18625
|
// after withdraw() executes, so this is the only way to capture the amount.
|
|
18311
|
-
const
|
|
18626
|
+
const withdrawingRaw = await executeAdapterReadAction(adapter, 'gateway.v1.withdrawingBalance', {
|
|
18312
18627
|
token: tokenAddress,
|
|
18313
18628
|
depositor: signerAddress,
|
|
18314
18629
|
chain
|
|
18315
18630
|
}, operationContext);
|
|
18316
|
-
const withdrawingRaw = await withdrawingBalanceReq.execute();
|
|
18317
18631
|
const withdrawingValue = safeBigInt(String(withdrawingRaw), 'withdrawingBalance');
|
|
18318
18632
|
if (withdrawingValue === 0n) {
|
|
18319
18633
|
throw new KitError({
|