@circle-fin/app-kit 1.9.0 → 1.11.0
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 +60 -0
- package/bridge.cjs +691 -43
- package/bridge.d.cts +159 -11
- package/bridge.d.mts +159 -11
- package/bridge.d.ts +159 -11
- package/bridge.mjs +691 -43
- package/chains.cjs +19 -2
- package/chains.d.cts +1 -0
- package/chains.d.mts +1 -0
- package/chains.d.ts +1 -0
- package/chains.mjs +19 -2
- package/context.cjs +12 -0
- package/context.d.cts +164 -13
- package/context.d.mts +164 -13
- package/context.d.ts +164 -13
- package/context.mjs +12 -0
- package/earn.cjs +1559 -468
- package/earn.d.cts +533 -95
- package/earn.d.mts +533 -95
- package/earn.d.ts +533 -95
- package/earn.mjs +1559 -469
- package/estimateBridge.cjs +691 -43
- package/estimateBridge.d.cts +159 -11
- package/estimateBridge.d.mts +159 -11
- package/estimateBridge.d.ts +159 -11
- package/estimateBridge.mjs +691 -43
- package/estimateSwap.cjs +1065 -118
- package/estimateSwap.d.cts +159 -11
- package/estimateSwap.d.mts +159 -11
- package/estimateSwap.d.ts +159 -11
- package/estimateSwap.mjs +1065 -118
- package/index.cjs +2981 -718
- package/index.d.cts +1104 -133
- package/index.d.mts +1104 -133
- package/index.d.ts +1104 -133
- package/index.mjs +2981 -718
- package/package.json +7 -6
- package/swap.cjs +1065 -118
- package/swap.d.cts +159 -11
- package/swap.d.mts +159 -11
- package/swap.d.ts +159 -11
- package/swap.mjs +1065 -118
- package/unifiedBalance.cjs +817 -140
- package/unifiedBalance.d.cts +250 -9
- package/unifiedBalance.d.mts +250 -9
- package/unifiedBalance.d.ts +250 -9
- package/unifiedBalance.mjs +817 -140
package/estimateBridge.mjs
CHANGED
|
@@ -16,13 +16,24 @@
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
// Buffer polyfill setup - executes before any other code
|
|
20
|
+
// Ensures globalThis.Buffer is available for Solana libraries
|
|
21
|
+
import { Buffer } from 'buffer';
|
|
22
|
+
if (typeof globalThis !== 'undefined' && typeof globalThis.Buffer === 'undefined') {
|
|
23
|
+
globalThis.Buffer = Buffer;
|
|
24
|
+
}
|
|
25
|
+
if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
|
|
26
|
+
window.Buffer = Buffer;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
19
30
|
import { z } from 'zod';
|
|
20
31
|
import pino from 'pino';
|
|
21
32
|
import { formatUnits as formatUnits$1, parseUnits as parseUnits$1 } from '@ethersproject/units';
|
|
22
33
|
import { hexlify, hexZeroPad } from '@ethersproject/bytes';
|
|
34
|
+
import '@ethersproject/abi';
|
|
23
35
|
import { getAddress } from '@ethersproject/address';
|
|
24
36
|
import bs58 from 'bs58';
|
|
25
|
-
import '@ethersproject/abi';
|
|
26
37
|
import { PublicKey } from '@solana/web3.js';
|
|
27
38
|
import 'bn.js';
|
|
28
39
|
import '@coral-xyz/anchor';
|
|
@@ -44,6 +55,27 @@ import { keccak256 } from '@ethersproject/keccak256';
|
|
|
44
55
|
* }
|
|
45
56
|
* ```
|
|
46
57
|
*/ const isNodeEnvironment = ()=>typeof process !== 'undefined' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
|
|
58
|
+
/**
|
|
59
|
+
* Return the SDK User-Agent request header only when running in Node.js.
|
|
60
|
+
*
|
|
61
|
+
* Browsers forbid manually setting `User-Agent`, and a custom fallback header
|
|
62
|
+
* can trigger CORS preflight. Non-Node server runtimes also omit this optional
|
|
63
|
+
* attribution header because they cannot set it reliably.
|
|
64
|
+
*
|
|
65
|
+
* @returns A User-Agent header in Node.js, or an empty object otherwise.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* import { getNodeUserAgentHeader } from '@core/utils'
|
|
70
|
+
*
|
|
71
|
+
* const headers = {
|
|
72
|
+
* 'Content-Type': 'application/json',
|
|
73
|
+
* ...getNodeUserAgentHeader(),
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/ const getNodeUserAgentHeader = ()=>isNodeEnvironment() ? {
|
|
77
|
+
'User-Agent': getUserAgent()
|
|
78
|
+
} : {};
|
|
47
79
|
/**
|
|
48
80
|
* Detect the runtime environment and return a shortened identifier.
|
|
49
81
|
*
|
|
@@ -3094,7 +3126,10 @@ var EarnChain;
|
|
|
3094
3126
|
contracts: {
|
|
3095
3127
|
v1: {
|
|
3096
3128
|
wallet: GATEWAY_WALLET_EVM_TESTNET,
|
|
3097
|
-
minter: GATEWAY_MINTER_EVM_TESTNET
|
|
3129
|
+
minter: GATEWAY_MINTER_EVM_TESTNET,
|
|
3130
|
+
// DepositForHandler the GenericExecutor calls to run a fast cross-chain
|
|
3131
|
+
// deposit into the GatewayWallet above.
|
|
3132
|
+
depositForHandler: '0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48'
|
|
3098
3133
|
}
|
|
3099
3134
|
},
|
|
3100
3135
|
forwarderSupported: {
|
|
@@ -6212,7 +6247,10 @@ var Chains = /*#__PURE__*/Object.freeze({
|
|
|
6212
6247
|
minter: z.string({
|
|
6213
6248
|
required_error: 'Gateway minter address is required. Please provide a valid contract address.',
|
|
6214
6249
|
invalid_type_error: 'Gateway minter address must be a string.'
|
|
6215
|
-
}).min(1, 'Gateway minter address cannot be empty.')
|
|
6250
|
+
}).min(1, 'Gateway minter address cannot be empty.'),
|
|
6251
|
+
depositForHandler: z.string({
|
|
6252
|
+
invalid_type_error: 'Gateway depositForHandler address must be a string.'
|
|
6253
|
+
}).min(1, 'Gateway depositForHandler address cannot be empty.').optional()
|
|
6216
6254
|
}).strict() // Reject any additional properties not defined in the schema
|
|
6217
6255
|
;
|
|
6218
6256
|
/**
|
|
@@ -6734,21 +6772,31 @@ const swapTokenEnumSchema = z.enum([
|
|
|
6734
6772
|
* returning the appropriate address based on the requested contract type.
|
|
6735
6773
|
*
|
|
6736
6774
|
* @param chain - The chain definition to resolve the contract address for
|
|
6737
|
-
* @param contractType - The type of contract address to resolve ('tokenMessenger' or '
|
|
6775
|
+
* @param contractType - The type of contract address to resolve ('tokenMessenger', 'messageTransmitter', or 'tokenMessengerWithFees')
|
|
6738
6776
|
* @returns The contract address for the specified contract type
|
|
6739
6777
|
* @throws Error when chain does not support CCTP v2 or has unsupported contract configuration
|
|
6778
|
+
* @throws Error when 'tokenMessengerWithFees' is requested but not configured on the chain
|
|
6740
6779
|
*/ const resolveCCTPV2ContractAddress = (chain, contractType)=>{
|
|
6741
6780
|
// Handle custom bridge contract for tokenMessenger (burn transaction)
|
|
6742
|
-
if (hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
|
|
6781
|
+
if (contractType === 'tokenMessenger' && hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
|
|
6743
6782
|
return chain.kitContracts.bridge;
|
|
6744
6783
|
}
|
|
6745
6784
|
// At this point we know CCTP v2 is supported, so contracts exist
|
|
6746
6785
|
const cctpConfig = chain.cctp;
|
|
6747
6786
|
const contracts = cctpConfig.contracts.v2;
|
|
6787
|
+
// The `TokenMessengerWithFees` wrapper (prepaid FORWARD path) is an optional
|
|
6788
|
+
// deployment carried alongside both split and merged configurations.
|
|
6789
|
+
if (contractType === 'tokenMessengerWithFees') {
|
|
6790
|
+
const wrapper = contracts.tokenMessengerWithFees;
|
|
6791
|
+
if (wrapper === undefined || wrapper === '') {
|
|
6792
|
+
throw new Error(`TokenMessengerWithFees is not configured on chain ${chain.name}. The prepaid FORWARD path is unavailable on this chain.`);
|
|
6793
|
+
}
|
|
6794
|
+
return wrapper;
|
|
6795
|
+
}
|
|
6748
6796
|
// Handle different contract types with explicit type checking
|
|
6749
6797
|
switch(contracts.type){
|
|
6750
6798
|
case 'split':
|
|
6751
|
-
return contracts.tokenMessenger ;
|
|
6799
|
+
return contractType === 'tokenMessenger' ? contracts.tokenMessenger : contracts.messageTransmitter;
|
|
6752
6800
|
case 'merged':
|
|
6753
6801
|
return contracts.contract;
|
|
6754
6802
|
default:
|
|
@@ -7454,13 +7502,12 @@ const swapTokenEnumSchema = z.enum([
|
|
|
7454
7502
|
headers: {
|
|
7455
7503
|
...DEFAULT_CONFIG$1.headers,
|
|
7456
7504
|
...config.headers ?? {},
|
|
7457
|
-
//
|
|
7458
|
-
//
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
}
|
|
7505
|
+
// Browsers forbid setting a user-agent request header, and the custom
|
|
7506
|
+
// fallback header the SDK used instead trips CORS preflight against the
|
|
7507
|
+
// Circle APIs (it isn't in their `Access-Control-Allow-Headers`),
|
|
7508
|
+
// blocking the request. So send the SDK user agent only in Node;
|
|
7509
|
+
// browsers omit it entirely.
|
|
7510
|
+
...getNodeUserAgentHeader()
|
|
7464
7511
|
}
|
|
7465
7512
|
};
|
|
7466
7513
|
let lastError;
|
|
@@ -9246,6 +9293,7 @@ function resolveOptions(options) {
|
|
|
9246
9293
|
if (payload.tokenIn !== undefined) safe['tokenIn'] = payload.tokenIn;
|
|
9247
9294
|
if (payload.tokenOut !== undefined) safe['tokenOut'] = payload.tokenOut;
|
|
9248
9295
|
if (payload.txHash !== undefined) safe['txHash'] = payload.txHash;
|
|
9296
|
+
if (payload.correlationId !== undefined) safe['correlationId'] = payload.correlationId;
|
|
9249
9297
|
if (payload.errorDetails !== undefined) {
|
|
9250
9298
|
const errorDetails = {
|
|
9251
9299
|
...payload.errorDetails.errorCode !== undefined && {
|
|
@@ -9316,18 +9364,15 @@ function resolveOptions(options) {
|
|
|
9316
9364
|
timeoutHandle.unref();
|
|
9317
9365
|
}
|
|
9318
9366
|
try {
|
|
9319
|
-
const isNode = isNodeEnvironment();
|
|
9320
|
-
const userAgent = getUserAgent();
|
|
9321
9367
|
await fetch(getLogsUrl(), {
|
|
9322
9368
|
method: 'POST',
|
|
9323
9369
|
headers: {
|
|
9324
9370
|
'Content-Type': 'application/json',
|
|
9325
|
-
//
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
}
|
|
9371
|
+
// Browsers forbid setting a user-agent request header, and the custom
|
|
9372
|
+
// fallback header the SDK used instead trips CORS preflight (it isn't
|
|
9373
|
+
// in the telemetry endpoint's `Access-Control-Allow-Headers`), so send
|
|
9374
|
+
// it only in Node; browsers omit it entirely.
|
|
9375
|
+
...getNodeUserAgentHeader()
|
|
9331
9376
|
},
|
|
9332
9377
|
body: JSON.stringify(toSafePayload(payload)),
|
|
9333
9378
|
signal: controller.signal
|
|
@@ -9458,7 +9503,7 @@ function resolveOptions(options) {
|
|
|
9458
9503
|
// discards the stack trace, nested `cause`, and any custom Error
|
|
9459
9504
|
// properties — exactly the context an on-call needs when a
|
|
9460
9505
|
// resolver-closure regression triggers this path.
|
|
9461
|
-
console.warn(`[stablecoin-kits telemetry] dropped
|
|
9506
|
+
console.warn(`[stablecoin-kits telemetry] dropped event '${eventType}':`, cause);
|
|
9462
9507
|
} catch {
|
|
9463
9508
|
// console.warn itself throwing is the user's environment; nothing more we
|
|
9464
9509
|
// can do without risking the original operation error.
|
|
@@ -9474,7 +9519,9 @@ function resolveOptions(options) {
|
|
|
9474
9519
|
sdkVersion: config.sdkVersion,
|
|
9475
9520
|
eventType,
|
|
9476
9521
|
timestamp: new Date().toISOString(),
|
|
9477
|
-
errorDetails
|
|
9522
|
+
...errorDetails !== undefined && {
|
|
9523
|
+
errorDetails
|
|
9524
|
+
},
|
|
9478
9525
|
clientContext: buildClientContext(),
|
|
9479
9526
|
...context?.sourceChain != null && {
|
|
9480
9527
|
sourceChain: context.sourceChain
|
|
@@ -9490,6 +9537,9 @@ function resolveOptions(options) {
|
|
|
9490
9537
|
},
|
|
9491
9538
|
...context?.txHash != null && {
|
|
9492
9539
|
txHash: context.txHash
|
|
9540
|
+
},
|
|
9541
|
+
...context?.correlationId != null && {
|
|
9542
|
+
correlationId: context.correlationId
|
|
9493
9543
|
}
|
|
9494
9544
|
};
|
|
9495
9545
|
}
|
|
@@ -9603,7 +9653,7 @@ function resolveOptions(options) {
|
|
|
9603
9653
|
}
|
|
9604
9654
|
|
|
9605
9655
|
var name$2 = "@circle-fin/bridge-kit";
|
|
9606
|
-
var version$3 = "1.12.
|
|
9656
|
+
var version$3 = "1.12.2";
|
|
9607
9657
|
var pkg$3 = {
|
|
9608
9658
|
name: name$2,
|
|
9609
9659
|
version: version$3};
|
|
@@ -12107,6 +12157,150 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
|
|
|
12107
12157
|
return false;
|
|
12108
12158
|
};
|
|
12109
12159
|
|
|
12160
|
+
/**
|
|
12161
|
+
* The zero address, denoting a native-currency fee in a signed quote.
|
|
12162
|
+
*/ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
12163
|
+
/**
|
|
12164
|
+
* Resolve the fee payment channel for a prepaid-FORWARD burn via `TokenMessengerWithFees`.
|
|
12165
|
+
*
|
|
12166
|
+
* Determines the native `msg.value` and the ERC-20 approvals required, honouring
|
|
12167
|
+
* the quote's `feeToken`:
|
|
12168
|
+
* - Native fee (`feeToken` is the zero address): attach exactly `feeTotalAmount`
|
|
12169
|
+
* as `msg.value`; approve only the burn amount.
|
|
12170
|
+
* - ERC-20 fee equal to the burn token (both USDC — the `isBurnTokenFee` case):
|
|
12171
|
+
* approve a single combined `amount + feeTotalAmount` and skip the redundant
|
|
12172
|
+
* second approval.
|
|
12173
|
+
* - ERC-20 fee different from the burn token: approve the burn amount and the fee
|
|
12174
|
+
* amount separately.
|
|
12175
|
+
*
|
|
12176
|
+
* This encodes only balance/allowance intent; it does not fetch balances. The
|
|
12177
|
+
* caller is responsible for a balance preflight against the fresh quote.
|
|
12178
|
+
*
|
|
12179
|
+
* @param params - The fee token, burn token, burn amount, and total fee amount.
|
|
12180
|
+
* @returns The resolved fee payment plan.
|
|
12181
|
+
* @throws KitError if `amount` or `feeTotalAmount` is negative.
|
|
12182
|
+
*
|
|
12183
|
+
* @example
|
|
12184
|
+
* ```typescript
|
|
12185
|
+
* // Native fee
|
|
12186
|
+
* resolveFeePayment({
|
|
12187
|
+
* feeToken: '0x0000000000000000000000000000000000000000',
|
|
12188
|
+
* burnToken: '0xUSDC...',
|
|
12189
|
+
* amount: 1_000_000n,
|
|
12190
|
+
* feeTotalAmount: 3_500_000n,
|
|
12191
|
+
* })
|
|
12192
|
+
* // → { isNativeFee: true, isBurnTokenFee: false, nativeValue: 3_500_000n,
|
|
12193
|
+
* // approvals: [{ token: '0xUSDC...', amount: 1_000_000n }] }
|
|
12194
|
+
* ```
|
|
12195
|
+
*/ const resolveFeePayment = (params)=>{
|
|
12196
|
+
const { feeToken, burnToken, amount, feeTotalAmount } = params;
|
|
12197
|
+
if (typeof amount !== 'bigint' || amount < 0n) {
|
|
12198
|
+
throw createValidationFailedError$1('amount', amount, 'Must be a non-negative bigint');
|
|
12199
|
+
}
|
|
12200
|
+
if (typeof feeTotalAmount !== 'bigint' || feeTotalAmount < 0n) {
|
|
12201
|
+
throw createValidationFailedError$1('feeTotalAmount', feeTotalAmount, 'Must be a non-negative bigint');
|
|
12202
|
+
}
|
|
12203
|
+
const isNativeFee = feeToken.toLowerCase() === ZERO_ADDRESS;
|
|
12204
|
+
const isBurnTokenFee = !isNativeFee && feeToken.toLowerCase() === burnToken.toLowerCase();
|
|
12205
|
+
if (isNativeFee) {
|
|
12206
|
+
return {
|
|
12207
|
+
isNativeFee: true,
|
|
12208
|
+
isBurnTokenFee: false,
|
|
12209
|
+
nativeValue: feeTotalAmount,
|
|
12210
|
+
approvals: [
|
|
12211
|
+
{
|
|
12212
|
+
token: burnToken,
|
|
12213
|
+
amount
|
|
12214
|
+
}
|
|
12215
|
+
]
|
|
12216
|
+
};
|
|
12217
|
+
}
|
|
12218
|
+
if (isBurnTokenFee) {
|
|
12219
|
+
// Fee and burn draw on the same token — a single combined approval covers
|
|
12220
|
+
// both; the redundant second approval is skipped.
|
|
12221
|
+
return {
|
|
12222
|
+
isNativeFee: false,
|
|
12223
|
+
isBurnTokenFee: true,
|
|
12224
|
+
nativeValue: 0n,
|
|
12225
|
+
approvals: [
|
|
12226
|
+
{
|
|
12227
|
+
token: burnToken,
|
|
12228
|
+
amount: amount + feeTotalAmount
|
|
12229
|
+
}
|
|
12230
|
+
]
|
|
12231
|
+
};
|
|
12232
|
+
}
|
|
12233
|
+
return {
|
|
12234
|
+
isNativeFee: false,
|
|
12235
|
+
isBurnTokenFee: false,
|
|
12236
|
+
nativeValue: 0n,
|
|
12237
|
+
approvals: [
|
|
12238
|
+
{
|
|
12239
|
+
token: burnToken,
|
|
12240
|
+
amount
|
|
12241
|
+
},
|
|
12242
|
+
{
|
|
12243
|
+
token: feeToken,
|
|
12244
|
+
amount: feeTotalAmount
|
|
12245
|
+
}
|
|
12246
|
+
]
|
|
12247
|
+
};
|
|
12248
|
+
};
|
|
12249
|
+
|
|
12250
|
+
/**
|
|
12251
|
+
* The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
|
|
12252
|
+
* hookData must start with.
|
|
12253
|
+
*
|
|
12254
|
+
* Encoded with `TextEncoder` (a browser-safe Web API) rather than `Buffer.from`
|
|
12255
|
+
* so this module-level constant does not reference the Node `Buffer` global at
|
|
12256
|
+
* import time. App Kit inlines this provider into its bundle without a Buffer polyfill, and a
|
|
12257
|
+
* bare `Buffer` here crashes browser bundles (e.g. Vite) on load — even for apps
|
|
12258
|
+
* that never touch the prepaid FORWARD path. Mirrors `buildForwardingHookData`.
|
|
12259
|
+
*/ const CCTP_FORWARD_MAGIC_HEX = Array.from(new TextEncoder().encode(CCTP_FORWARD_MAGIC_PREFIX)).map((byte)=>byte.toString(16).padStart(2, '0')).join('');
|
|
12260
|
+
/**
|
|
12261
|
+
* Determine whether a hookData blob begins with the `cctp-forward` envelope.
|
|
12262
|
+
*
|
|
12263
|
+
* The prepaid FORWARD path requires the GenericExecutor blob to be wrapped in a
|
|
12264
|
+
* `cctp-forward` frame; without it the wrapper reverts `ForwardFeeWithoutHook`.
|
|
12265
|
+
*
|
|
12266
|
+
* @param hookData - The 0x-prefixed hookData hex string.
|
|
12267
|
+
* @returns True when the hookData starts with the `cctp-forward` magic.
|
|
12268
|
+
*
|
|
12269
|
+
* @example
|
|
12270
|
+
* ```typescript
|
|
12271
|
+
* hasForwardHook('0x636374702d666f7277617264...') // true
|
|
12272
|
+
* hasForwardHook('0xdeadbeef') // false
|
|
12273
|
+
* ```
|
|
12274
|
+
*/ const hasForwardHook = (hookData)=>{
|
|
12275
|
+
if (typeof hookData !== 'string') {
|
|
12276
|
+
return false;
|
|
12277
|
+
}
|
|
12278
|
+
const normalized = (hookData.startsWith('0x') ? hookData.slice(2) : hookData).toLowerCase();
|
|
12279
|
+
return normalized.startsWith(CCTP_FORWARD_MAGIC_HEX);
|
|
12280
|
+
};
|
|
12281
|
+
/**
|
|
12282
|
+
* Assert that a hookData blob is forward-friendly for the prepaid FORWARD path.
|
|
12283
|
+
*
|
|
12284
|
+
* The prepaid FORWARD path always requests a FORWARD fee item, so the wrapper
|
|
12285
|
+
* requires the hookData to start with a `cctp-forward` frame. Validating this
|
|
12286
|
+
* before the burn surfaces the guaranteed `ForwardFeeWithoutHook` revert as a
|
|
12287
|
+
* typed input error instead of an on-chain failure.
|
|
12288
|
+
*
|
|
12289
|
+
* @param hookData - The 0x-prefixed hookData hex string.
|
|
12290
|
+
* @throws KitError (`INPUT_VALIDATION_FAILED`) if the hookData is missing or lacks
|
|
12291
|
+
* the `cctp-forward` frame.
|
|
12292
|
+
*
|
|
12293
|
+
* @example
|
|
12294
|
+
* ```typescript
|
|
12295
|
+
* assertForwardHookData(geForwardHookData) // ok
|
|
12296
|
+
* assertForwardHookData('0xdeadbeef') // throws — would revert ForwardFeeWithoutHook
|
|
12297
|
+
* ```
|
|
12298
|
+
*/ const assertForwardHookData = (hookData)=>{
|
|
12299
|
+
if (!hasForwardHook(hookData)) {
|
|
12300
|
+
throw createValidationFailedError$1('hookData', hookData, 'Prepaid FORWARD burns require a cctp-forward-wrapped hookData; without it the TokenMessengerWithFees wrapper reverts ForwardFeeWithoutHook');
|
|
12301
|
+
}
|
|
12302
|
+
};
|
|
12303
|
+
|
|
12110
12304
|
/**
|
|
12111
12305
|
* Type guard to validate the forwardFee object structure.
|
|
12112
12306
|
*
|
|
@@ -13038,6 +13232,109 @@ const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
|
|
|
13038
13232
|
}
|
|
13039
13233
|
}
|
|
13040
13234
|
|
|
13235
|
+
/**
|
|
13236
|
+
* Assert that `params` is a well-formed {@link BurnWithFeesParams} object.
|
|
13237
|
+
*
|
|
13238
|
+
* Validates the full public-boundary input before any field destructuring,
|
|
13239
|
+
* `BigInt()` coercion, or adapter preparation runs, so malformed JS-caller
|
|
13240
|
+
* inputs always produce typed `KitError` validation failures.
|
|
13241
|
+
*
|
|
13242
|
+
* Checks performed (in order):
|
|
13243
|
+
* - `params` must be a non-null plain object
|
|
13244
|
+
* - `source` — valid CCTP v2 wallet context (via `assertCCTPv2WalletContext`)
|
|
13245
|
+
* - `destinationChain` — present and supports CCTP v2
|
|
13246
|
+
* - source and destination chains must both be testnet or both mainnet
|
|
13247
|
+
* - source and destination chains must differ
|
|
13248
|
+
* - `executor` — non-empty string
|
|
13249
|
+
* - `amount` — bigint or non-empty string coercible to bigint
|
|
13250
|
+
* - `feeTotalAmount` — bigint or non-empty string coercible to bigint
|
|
13251
|
+
* - `feeToken` — valid EVM address (`0x` + 40 hex chars)
|
|
13252
|
+
* - `claim.signedQuote` — valid `0x`-prefixed hex string
|
|
13253
|
+
* - `claim.refundAddress` — valid EVM address
|
|
13254
|
+
* - `hookData` — valid `0x`-prefixed hex string when present
|
|
13255
|
+
*
|
|
13256
|
+
* @param params - The value to validate.
|
|
13257
|
+
* @throws {KitError} If any field is missing or invalid.
|
|
13258
|
+
*
|
|
13259
|
+
* @example
|
|
13260
|
+
* ```typescript
|
|
13261
|
+
* assertBurnWithFeesParams(params)
|
|
13262
|
+
* // params is now typed as BurnWithFeesParams and safe to use
|
|
13263
|
+
* const { source, destinationChain, amount } = params
|
|
13264
|
+
* ```
|
|
13265
|
+
*/ function assertBurnWithFeesParams(params) {
|
|
13266
|
+
if (params === null || typeof params !== 'object' || Array.isArray(params)) {
|
|
13267
|
+
throw createValidationFailedError$1('params', params, 'Must be a non-null plain object');
|
|
13268
|
+
}
|
|
13269
|
+
const p = params;
|
|
13270
|
+
// Source wallet context
|
|
13271
|
+
assertCCTPv2WalletContext(p['source']);
|
|
13272
|
+
const source = p['source'];
|
|
13273
|
+
// destinationChain
|
|
13274
|
+
const destinationChain = p['destinationChain'];
|
|
13275
|
+
if (destinationChain === null || destinationChain === undefined || typeof destinationChain !== 'object') {
|
|
13276
|
+
throw createValidationFailedError$1('destinationChain', destinationChain, 'Must be a chain definition object');
|
|
13277
|
+
}
|
|
13278
|
+
if (!isCCTPV2Supported(destinationChain)) {
|
|
13279
|
+
throw createValidationFailedError$1('destinationChain', destinationChain, 'destinationChain must support CCTP v2');
|
|
13280
|
+
}
|
|
13281
|
+
const dest = destinationChain;
|
|
13282
|
+
// Testnet / mainnet mismatch
|
|
13283
|
+
if (source.chain.isTestnet !== dest.isTestnet) {
|
|
13284
|
+
throw createNetworkMismatchError(source.chain, dest);
|
|
13285
|
+
}
|
|
13286
|
+
// Same-chain guard
|
|
13287
|
+
if (source.chain.name === dest.name) {
|
|
13288
|
+
throw createUnsupportedRouteError(source.chain.name, dest.name);
|
|
13289
|
+
}
|
|
13290
|
+
// executor
|
|
13291
|
+
const executor = p['executor'];
|
|
13292
|
+
if (typeof executor !== 'string' || executor === '') {
|
|
13293
|
+
throw createValidationFailedError$1('executor', executor, 'A GenericExecutor address is required (used for both mintRecipient and destinationCaller)');
|
|
13294
|
+
}
|
|
13295
|
+
// amount
|
|
13296
|
+
const rawAmount = p['amount'];
|
|
13297
|
+
if (typeof rawAmount !== 'bigint' && typeof rawAmount !== 'string' || rawAmount === '') {
|
|
13298
|
+
throw createValidationFailedError$1('amount', rawAmount, 'Must be a bigint or a numeric string');
|
|
13299
|
+
}
|
|
13300
|
+
try {
|
|
13301
|
+
BigInt(rawAmount);
|
|
13302
|
+
} catch {
|
|
13303
|
+
throw createValidationFailedError$1('amount', rawAmount, 'Must be a valid integer value coercible to bigint');
|
|
13304
|
+
}
|
|
13305
|
+
// feeTotalAmount
|
|
13306
|
+
const rawFee = p['feeTotalAmount'];
|
|
13307
|
+
if (typeof rawFee !== 'bigint' && typeof rawFee !== 'string' || rawFee === '') {
|
|
13308
|
+
throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a bigint or a numeric string');
|
|
13309
|
+
}
|
|
13310
|
+
try {
|
|
13311
|
+
BigInt(rawFee);
|
|
13312
|
+
} catch {
|
|
13313
|
+
throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a valid integer value coercible to bigint');
|
|
13314
|
+
}
|
|
13315
|
+
// feeToken
|
|
13316
|
+
if (!evmAddressSchema.safeParse(p['feeToken']).success) {
|
|
13317
|
+
throw createValidationFailedError$1('feeToken', p['feeToken'], 'Must be a valid EVM address (use the zero address for a native fee)');
|
|
13318
|
+
}
|
|
13319
|
+
// claim
|
|
13320
|
+
const rawClaim = p['claim'];
|
|
13321
|
+
if (rawClaim === null || typeof rawClaim !== 'object' || Array.isArray(rawClaim)) {
|
|
13322
|
+
throw createValidationFailedError$1('claim', rawClaim, 'Must be an object with signedQuote and refundAddress');
|
|
13323
|
+
}
|
|
13324
|
+
const claim = rawClaim;
|
|
13325
|
+
if (!hexStringSchema.safeParse(claim['signedQuote']).success) {
|
|
13326
|
+
throw createValidationFailedError$1('claim.signedQuote', claim['signedQuote'], 'Must be a valid hex string starting with 0x');
|
|
13327
|
+
}
|
|
13328
|
+
if (!evmAddressSchema.safeParse(claim['refundAddress']).success) {
|
|
13329
|
+
throw createValidationFailedError$1('claim.refundAddress', claim['refundAddress'], 'Must be a valid EVM address');
|
|
13330
|
+
}
|
|
13331
|
+
// hookData (optional)
|
|
13332
|
+
const hookData = p['hookData'];
|
|
13333
|
+
if (hookData !== undefined && hookData !== '' && !hexStringSchema.safeParse(hookData).success) {
|
|
13334
|
+
throw createValidationFailedError$1('hookData', hookData, 'Must be a valid hex string starting with 0x');
|
|
13335
|
+
}
|
|
13336
|
+
}
|
|
13337
|
+
|
|
13041
13338
|
/**
|
|
13042
13339
|
* CCTP bridge step names that can occur in the bridging flow.
|
|
13043
13340
|
*
|
|
@@ -14246,10 +14543,15 @@ const mockAttestationMessage = {
|
|
|
14246
14543
|
const burnCallData = burnRequest.getCallData();
|
|
14247
14544
|
// batchExecute may throw before submission (wallet declined) but never
|
|
14248
14545
|
// after — post-submission errors are returned as empty receipts.
|
|
14546
|
+
// The sender is threaded for adapters whose execution is routed through a
|
|
14547
|
+
// signing strategy (which has no wallet account to read it from); the
|
|
14548
|
+
// wallet-client path ignores it.
|
|
14249
14549
|
const batchResult = await adapter.batchExecute([
|
|
14250
14550
|
approveCallData,
|
|
14251
14551
|
burnCallData
|
|
14252
|
-
], chain
|
|
14552
|
+
], chain, {
|
|
14553
|
+
fromAddress: params.source.address
|
|
14554
|
+
});
|
|
14253
14555
|
const approveReceipt = batchResult.receipts[0];
|
|
14254
14556
|
const burnReceipt = batchResult.receipts[1];
|
|
14255
14557
|
const approveStep = await buildBatchedStep('approve', approveReceipt, batchResult.batchId, adapter, chain, batchResult.statusCode, batchResult.error);
|
|
@@ -14392,7 +14694,7 @@ const mockAttestationMessage = {
|
|
|
14392
14694
|
return step;
|
|
14393
14695
|
}
|
|
14394
14696
|
|
|
14395
|
-
var version$2 = "1.
|
|
14697
|
+
var version$2 = "1.10.1";
|
|
14396
14698
|
var pkg$2 = {
|
|
14397
14699
|
version: version$2};
|
|
14398
14700
|
|
|
@@ -15547,7 +15849,7 @@ function assertCCTPV2Config(config) {
|
|
|
15547
15849
|
throw new Error(`Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
|
|
15548
15850
|
}
|
|
15549
15851
|
// Resolve spender address with proper error handling
|
|
15550
|
-
const spenderAddress = resolveCCTPV2ContractAddress(chain);
|
|
15852
|
+
const spenderAddress = resolveCCTPV2ContractAddress(chain, 'tokenMessenger');
|
|
15551
15853
|
// Prepare action parameters
|
|
15552
15854
|
const actionParams = {
|
|
15553
15855
|
amount: BigInt(amount),
|
|
@@ -16027,6 +16329,106 @@ function assertCCTPV2Config(config) {
|
|
|
16027
16329
|
return await source.adapter.prepareAction('cctp.v2.depositForBurn', actionParams, resolvedContext);
|
|
16028
16330
|
}
|
|
16029
16331
|
/**
|
|
16332
|
+
* Prepare a prepaid-FORWARD burn through the `TokenMessengerWithFees` wrapper.
|
|
16333
|
+
*
|
|
16334
|
+
* Builds the source-chain `depositForBurnWithHookAndFees` call for the
|
|
16335
|
+
* GenericExecutor FORWARD path: fees are collected up front on the source chain
|
|
16336
|
+
* against a signed quote, `mintRecipient` and `destinationCaller` are both set to
|
|
16337
|
+
* the GenericExecutor, and the GE `hookData` is passed through unchanged.
|
|
16338
|
+
*
|
|
16339
|
+
* This is the low-level on-chain primitive behind the UBK `fastCrossChainDeposit`
|
|
16340
|
+
* and Bridge Kit `bridge({ deposit })` flows. The `hookData` and signed-quote
|
|
16341
|
+
* `claim` are produced elsewhere and passed in here:
|
|
16342
|
+
* - `hookData`: `buildForwardingHookDataWithPayload(version,
|
|
16343
|
+
* buildDepositForGenericExecutorPayload(...).hookData)` from `@core/utils`.
|
|
16344
|
+
* - `claim.signedQuote` / `feeToken` / `feeTotalAmount`: from `fetchFeeQuote`
|
|
16345
|
+
* (`@circle-fin/provider-fee-v1`), whose FORWARD item must be bound to the
|
|
16346
|
+
* SAME `hookData` and executor `destinationCaller` used here.
|
|
16347
|
+
*
|
|
16348
|
+
* The returned approvals and burn are NOT executed — the caller executes the
|
|
16349
|
+
* approvals first (in order) and then the burn. The fee payment channel matches
|
|
16350
|
+
* the quote's `feeToken`:
|
|
16351
|
+
* - Native fee: exactly `feeTotalAmount` is attached as the burn's `msg.value`;
|
|
16352
|
+
* only the burn amount is approved.
|
|
16353
|
+
* - USDC fee (same token as the burn): a single combined `amount + feeTotalAmount`
|
|
16354
|
+
* approval covers both; the redundant second approval is skipped.
|
|
16355
|
+
*
|
|
16356
|
+
* @typeParam TFromAdapterCapabilities - The source adapter's capabilities.
|
|
16357
|
+
* @param params - The burn amount, executor, hookData, signed-quote claim, and fee.
|
|
16358
|
+
* @returns The prepared approvals, the prepared burn, and the resolved fee plan.
|
|
16359
|
+
* @throws {KitError} If the wallet context is invalid, `destinationChain` does not
|
|
16360
|
+
* support CCTP v2, the executor is missing, `amount` or `feeTotalAmount` is not
|
|
16361
|
+
* a bigint or a numeric string coercible to bigint, the hookData lacks a
|
|
16362
|
+
* `cctp-forward` frame (guaranteed `ForwardFeeWithoutHook`), or the operation
|
|
16363
|
+
* context cannot be resolved.
|
|
16364
|
+
*
|
|
16365
|
+
* @example
|
|
16366
|
+
* ```typescript
|
|
16367
|
+
* const { approvals, burn } = await provider.burnWithFees({
|
|
16368
|
+
* source,
|
|
16369
|
+
* destinationChain: Arc,
|
|
16370
|
+
* amount: 1_000_000n,
|
|
16371
|
+
* executor: genericExecutorAddress,
|
|
16372
|
+
* hookData: geForwardHookData,
|
|
16373
|
+
* claim: { signedQuote: '0x01...', refundAddress: userAddress },
|
|
16374
|
+
* feeToken: '0x0000000000000000000000000000000000000000', // native
|
|
16375
|
+
* feeTotalAmount: 3_500_000n,
|
|
16376
|
+
* })
|
|
16377
|
+
* for (const approval of approvals) await approval.execute()
|
|
16378
|
+
* const txHash = await burn.execute()
|
|
16379
|
+
* ```
|
|
16380
|
+
*/ async burnWithFees(params) {
|
|
16381
|
+
assertBurnWithFeesParams(params);
|
|
16382
|
+
const { source, destinationChain, executor, hookData, claim, feeToken } = params;
|
|
16383
|
+
const amount = BigInt(params.amount);
|
|
16384
|
+
const feeTotalAmount = BigInt(params.feeTotalAmount);
|
|
16385
|
+
// Coupling guard: the prepaid FORWARD path always requests a FORWARD fee item,
|
|
16386
|
+
// so the hookData must carry a cctp-forward frame; otherwise the wrapper
|
|
16387
|
+
// reverts ForwardFeeWithoutHook. Surface it as a typed input error up front.
|
|
16388
|
+
assertForwardHookData(hookData);
|
|
16389
|
+
const burnToken = source.chain.usdcAddress;
|
|
16390
|
+
const feePayment = resolveFeePayment({
|
|
16391
|
+
feeToken,
|
|
16392
|
+
burnToken,
|
|
16393
|
+
amount,
|
|
16394
|
+
feeTotalAmount
|
|
16395
|
+
});
|
|
16396
|
+
// Resolve operation context from the source wallet context.
|
|
16397
|
+
const operationContext = this.extractOperationContext(source);
|
|
16398
|
+
let resolvedContext;
|
|
16399
|
+
try {
|
|
16400
|
+
resolvedContext = await resolveOperationContext(source.adapter, operationContext);
|
|
16401
|
+
} catch (error) {
|
|
16402
|
+
throw createValidationFailedError$1('source.adapter', undefined, `Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
|
|
16403
|
+
}
|
|
16404
|
+
const context = resolvedContext;
|
|
16405
|
+
const wrapperAddress = resolveCCTPV2ContractAddress(source.chain, 'tokenMessengerWithFees');
|
|
16406
|
+
// Build the ERC-20 approvals to the wrapper (burn token, plus a distinct fee
|
|
16407
|
+
// token only when the fee is not paid in the burn token).
|
|
16408
|
+
const approvals = await Promise.all(feePayment.approvals.map(async (approval)=>source.adapter.prepareAction('token.approve', {
|
|
16409
|
+
tokenAddress: approval.token,
|
|
16410
|
+
delegate: wrapperAddress,
|
|
16411
|
+
amount: approval.amount
|
|
16412
|
+
}, context)));
|
|
16413
|
+
// Build the burn: mintRecipient AND destinationCaller are both the executor.
|
|
16414
|
+
const burn = await source.adapter.prepareAction('cctp.v2.depositForBurnWithFees', {
|
|
16415
|
+
fromChain: source.chain,
|
|
16416
|
+
toChain: destinationChain,
|
|
16417
|
+
amount,
|
|
16418
|
+
mintRecipient: executor,
|
|
16419
|
+
destinationCaller: executor,
|
|
16420
|
+
hookData,
|
|
16421
|
+
claim,
|
|
16422
|
+
feeToken,
|
|
16423
|
+
feeTotalAmount
|
|
16424
|
+
}, context);
|
|
16425
|
+
return {
|
|
16426
|
+
approvals,
|
|
16427
|
+
burn,
|
|
16428
|
+
feePayment
|
|
16429
|
+
};
|
|
16430
|
+
}
|
|
16431
|
+
/**
|
|
16030
16432
|
* Waits for a transaction to be mined and confirmed on the blockchain.
|
|
16031
16433
|
*
|
|
16032
16434
|
* This method should block until the transaction is confirmed on the blockchain.
|
|
@@ -16906,7 +17308,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
|
|
|
16906
17308
|
};
|
|
16907
17309
|
|
|
16908
17310
|
var name$1 = "@circle-fin/swap-kit";
|
|
16909
|
-
var version$1 = "1.
|
|
17311
|
+
var version$1 = "1.5.0";
|
|
16910
17312
|
var pkg$1 = {
|
|
16911
17313
|
name: name$1,
|
|
16912
17314
|
version: version$1};
|
|
@@ -16971,7 +17373,10 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
|
|
|
16971
17373
|
}).min(1, 'kitKey must be a non-empty string').optional(),
|
|
16972
17374
|
provider: z.string({
|
|
16973
17375
|
invalid_type_error: 'provider must be a string'
|
|
16974
|
-
}).min(1, 'provider must be a non-empty string').optional()
|
|
17376
|
+
}).min(1, 'provider must be a non-empty string').optional(),
|
|
17377
|
+
batchTransactions: z.boolean({
|
|
17378
|
+
invalid_type_error: 'batchTransactions must be a boolean'
|
|
17379
|
+
}).optional()
|
|
16975
17380
|
});
|
|
16976
17381
|
/**
|
|
16977
17382
|
* Zod schema for adapter context.
|
|
@@ -17410,7 +17815,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
17410
17815
|
/**
|
|
17411
17816
|
* Circle Stablecoin Service API Key.
|
|
17412
17817
|
* Must be a valid API key format.
|
|
17413
|
-
*/ apiKey: apiKeySchema
|
|
17818
|
+
*/ apiKey: apiKeySchema.optional()
|
|
17414
17819
|
}).superRefine(requireCrossChainQuoteToAddress);
|
|
17415
17820
|
/**
|
|
17416
17821
|
* Zod schema for validating CreateSwapRequest parameters.
|
|
@@ -17468,7 +17873,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
17468
17873
|
/**
|
|
17469
17874
|
* Circle Stablecoin Service API Key.
|
|
17470
17875
|
* Must be a valid API key format.
|
|
17471
|
-
*/ apiKey: apiKeySchema
|
|
17876
|
+
*/ apiKey: apiKeySchema.optional()
|
|
17472
17877
|
});
|
|
17473
17878
|
/**
|
|
17474
17879
|
* Zod schema for validating GetSwapStatusResponse data.
|
|
@@ -17504,7 +17909,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
17504
17909
|
toChain: z.string({
|
|
17505
17910
|
invalid_type_error: 'toChain must be a string'
|
|
17506
17911
|
}).min(1, 'toChain must be a non-empty string if provided').optional(),
|
|
17507
|
-
apiKey: apiKeySchema
|
|
17912
|
+
apiKey: apiKeySchema.optional()
|
|
17508
17913
|
});
|
|
17509
17914
|
/**
|
|
17510
17915
|
* Zod schema for validating CreateSwapResponse payloads.
|
|
@@ -17513,13 +17918,15 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
17513
17918
|
required_error: 'fee token is required',
|
|
17514
17919
|
invalid_type_error: 'fee token must be a string'
|
|
17515
17920
|
}).min(1, 'fee token must be a non-empty string'),
|
|
17516
|
-
amount: feeAmountSchema
|
|
17921
|
+
amount: feeAmountSchema,
|
|
17922
|
+
decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
|
|
17923
|
+
symbol: z.string({
|
|
17924
|
+
invalid_type_error: 'fee token symbol must be a string'
|
|
17925
|
+
}).min(1, 'fee token symbol must be a non-empty string').optional()
|
|
17517
17926
|
});
|
|
17518
17927
|
/**
|
|
17519
17928
|
* Developer fee item schema with basis field.
|
|
17520
|
-
*/ const createSwapDeveloperFeeItemSchema =
|
|
17521
|
-
token: z.string().min(1, 'fee token must be a non-empty string'),
|
|
17522
|
-
amount: feeAmountSchema,
|
|
17929
|
+
*/ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
|
|
17523
17930
|
basis: z.enum([
|
|
17524
17931
|
'inputAmount',
|
|
17525
17932
|
'estimatedAmount'
|
|
@@ -17611,7 +18018,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
17611
18018
|
addresses: z.array(z.string({
|
|
17612
18019
|
invalid_type_error: 'addresses entries must be strings'
|
|
17613
18020
|
}).min(1, 'addresses entries must be non-empty strings')).min(1, 'addresses must contain at least one entry when provided').max(MAX_RATE_ADDRESSES_PER_REQUEST, `addresses supports at most ${String(MAX_RATE_ADDRESSES_PER_REQUEST)} values per request`).optional(),
|
|
17614
|
-
apiKey: apiKeySchema
|
|
18021
|
+
apiKey: apiKeySchema.optional()
|
|
17615
18022
|
});
|
|
17616
18023
|
/**
|
|
17617
18024
|
* Zod schema for validating GetTokenRatesResponse payloads.
|
|
@@ -17642,6 +18049,16 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
17642
18049
|
required_error: 'estimatedAmount is required',
|
|
17643
18050
|
invalid_type_error: 'estimatedAmount must be a string'
|
|
17644
18051
|
}).min(1, 'estimatedAmount must be a non-empty string'),
|
|
18052
|
+
// Per-swap join key echoed back verbatim on success telemetry. Optional so a
|
|
18053
|
+
// not-yet-upgraded service (no field) still validates during rollout. A
|
|
18054
|
+
// malformed/non-UUID value is coerced to `undefined` (no telemetry id) rather
|
|
18055
|
+
// than throwing: this is a telemetry-only field (stripped from the developer
|
|
18056
|
+
// result, never used for control flow), so it must not be able to abort the
|
|
18057
|
+
// swap via `parseCreateSwapResponse().parse()`. Mirrors the best-effort,
|
|
18058
|
+
// never-throw contract of the rest of the telemetry stack. Implemented with
|
|
18059
|
+
// `preprocess` rather than Zod's `.catch()` because static analysis misreads
|
|
18060
|
+
// `.catch` on the schema chain as an unhandled Promise (S7785).
|
|
18061
|
+
correlationId: z.preprocess((value)=>z.string().uuid().safeParse(value).success ? value : undefined, z.string().optional()),
|
|
17645
18062
|
config: createSwapRequestBaseSchema.shape.config.optional(),
|
|
17646
18063
|
fees: createSwapFeesSchema.optional(),
|
|
17647
18064
|
transaction: createSwapTransactionSchema
|
|
@@ -18834,7 +19251,7 @@ new Set(Object.values(Blockchain));
|
|
|
18834
19251
|
registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
18835
19252
|
|
|
18836
19253
|
var name = "@circle-fin/earn-kit";
|
|
18837
|
-
var version = "1.
|
|
19254
|
+
var version = "1.4.0";
|
|
18838
19255
|
var pkg = {
|
|
18839
19256
|
name: name,
|
|
18840
19257
|
version: version};
|
|
@@ -18964,7 +19381,11 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
18964
19381
|
asset: z.string(),
|
|
18965
19382
|
assetAddress: z.string(),
|
|
18966
19383
|
lltv: z.number(),
|
|
18967
|
-
supplyUsd: z.number()
|
|
19384
|
+
supplyUsd: z.number(),
|
|
19385
|
+
// Optional during the expand/contract window (a backend that predates the
|
|
19386
|
+
// field omits the key), mirroring the `.optional()` facets on the base
|
|
19387
|
+
// schema; `null` when the product exposes no per-market allocation (V2).
|
|
19388
|
+
allocationPct: z.number().nullable().optional()
|
|
18968
19389
|
});
|
|
18969
19390
|
/**
|
|
18970
19391
|
* Zod schema for a Morpho vault warning in the API response.
|
|
@@ -18978,7 +19399,74 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
18978
19399
|
])
|
|
18979
19400
|
});
|
|
18980
19401
|
/**
|
|
18981
|
-
* Zod schema for
|
|
19402
|
+
* Zod schema for the manager (curator) facet in the API response.
|
|
19403
|
+
*
|
|
19404
|
+
* @internal
|
|
19405
|
+
*/ const managerSchema = z.object({
|
|
19406
|
+
name: z.string(),
|
|
19407
|
+
address: z.string().optional(),
|
|
19408
|
+
// Only 'curator' is emitted today (Morpho V1/V2). Additional manager roles
|
|
19409
|
+
// are added here as the providers that emit them land, rather than shipped
|
|
19410
|
+
// speculatively.
|
|
19411
|
+
type: z.enum([
|
|
19412
|
+
'curator'
|
|
19413
|
+
])
|
|
19414
|
+
});
|
|
19415
|
+
/**
|
|
19416
|
+
* Zod schema for the APY profile facet in the API response.
|
|
19417
|
+
*
|
|
19418
|
+
* @internal
|
|
19419
|
+
*/ const apyProfileSchema = z.object({
|
|
19420
|
+
current: z.number(),
|
|
19421
|
+
native: z.number().nullable(),
|
|
19422
|
+
d7: z.number().nullable(),
|
|
19423
|
+
d30: z.number().nullable(),
|
|
19424
|
+
d90: z.number().nullable(),
|
|
19425
|
+
rewardShare: z.number().nullable(),
|
|
19426
|
+
source: z.string().optional(),
|
|
19427
|
+
asOf: z.string().optional()
|
|
19428
|
+
});
|
|
19429
|
+
/**
|
|
19430
|
+
* Zod schema for the fee split facet in the API response.
|
|
19431
|
+
*
|
|
19432
|
+
* @internal
|
|
19433
|
+
*/ const feeInfoSchema = z.object({
|
|
19434
|
+
performance: z.number().nullable(),
|
|
19435
|
+
management: z.number().nullable()
|
|
19436
|
+
});
|
|
19437
|
+
/**
|
|
19438
|
+
* Zod schema for the liquidity profile facet in the API response.
|
|
19439
|
+
*
|
|
19440
|
+
* `totalSupply` is the outstanding vault share tokens (ERC4626 totalSupply);
|
|
19441
|
+
* it is validated as a raw JSON amount, like `totalDeposits`/`available`.
|
|
19442
|
+
*
|
|
19443
|
+
* @internal
|
|
19444
|
+
*/ const liquidityProfileSchema = z.object({
|
|
19445
|
+
totalDeposits: amountJsonSchema,
|
|
19446
|
+
available: amountJsonSchema,
|
|
19447
|
+
totalSupply: amountJsonSchema,
|
|
19448
|
+
status: z.enum([
|
|
19449
|
+
'active',
|
|
19450
|
+
'low_liquidity'
|
|
19451
|
+
])
|
|
19452
|
+
});
|
|
19453
|
+
/**
|
|
19454
|
+
* Zod schema for the risk signals facet in the API response.
|
|
19455
|
+
*
|
|
19456
|
+
* @internal
|
|
19457
|
+
*/ const riskSignalsSchema = z.object({
|
|
19458
|
+
circleSentinel: z.boolean(),
|
|
19459
|
+
warnings: z.array(vaultWarningSchema).optional(),
|
|
19460
|
+
earnKitWarnings: z.array(z.string()).optional()
|
|
19461
|
+
});
|
|
19462
|
+
/**
|
|
19463
|
+
* Zod schema for the universal earn-opportunity base in the API response.
|
|
19464
|
+
*
|
|
19465
|
+
* Retains every existing deprecated flat field (kept validated through the
|
|
19466
|
+
* expand/contract window so default-strip does not drop them) and adds the
|
|
19467
|
+
* new nested facets. The nested facets are `.optional()` during the
|
|
19468
|
+
* transition so the SDK still validates against a not-yet-fully-deployed
|
|
19469
|
+
* backend; they become required after Expand ships.
|
|
18982
19470
|
*
|
|
18983
19471
|
* @internal
|
|
18984
19472
|
*/ const vaultInfoResponseSchema = z.object({
|
|
@@ -19003,6 +19491,96 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
19003
19491
|
warnings: z.array(vaultWarningSchema).optional(),
|
|
19004
19492
|
earnKitWarnings: z.array(z.string()).optional()
|
|
19005
19493
|
});
|
|
19494
|
+
/**
|
|
19495
|
+
* Shared base schema: existing flat fields (kept) plus the new nested
|
|
19496
|
+
* facets and neutral identity. Facets are `.optional()` during the
|
|
19497
|
+
* transition; flip to required once the backend is confirmed emitting.
|
|
19498
|
+
*
|
|
19499
|
+
* @internal
|
|
19500
|
+
*/ const earnBaseSchema = vaultInfoResponseSchema.extend({
|
|
19501
|
+
address: z.string().optional(),
|
|
19502
|
+
asOf: z.string().optional(),
|
|
19503
|
+
manager: managerSchema.nullable().optional(),
|
|
19504
|
+
apyProfile: apyProfileSchema.optional(),
|
|
19505
|
+
fee: feeInfoSchema.optional(),
|
|
19506
|
+
liquidityProfile: liquidityProfileSchema.optional(),
|
|
19507
|
+
riskSignals: riskSignalsSchema.optional()
|
|
19508
|
+
});
|
|
19509
|
+
/**
|
|
19510
|
+
* Zod schema for the `vault` opportunity variant.
|
|
19511
|
+
*
|
|
19512
|
+
* @internal
|
|
19513
|
+
*/ const vaultOpportunitySchema = earnBaseSchema.extend({
|
|
19514
|
+
productType: z.literal('vault'),
|
|
19515
|
+
collateral: z.array(collateralSchema)
|
|
19516
|
+
});
|
|
19517
|
+
/**
|
|
19518
|
+
* Discriminated union over `productType`. Add union members here as new
|
|
19519
|
+
* product types (e.g. `lending_market`, `rwa_token`) land.
|
|
19520
|
+
*
|
|
19521
|
+
* @internal
|
|
19522
|
+
*/ const earnOpportunityVariants = [
|
|
19523
|
+
vaultOpportunitySchema
|
|
19524
|
+
];
|
|
19525
|
+
/** @internal */ const earnOpportunitySchema = z.discriminatedUnion('productType', earnOpportunityVariants);
|
|
19526
|
+
/** Product types this SDK version knows how to parse. */ const knownProductTypes = new Set(earnOpportunityVariants.map((variant)=>variant.shape.productType.value));
|
|
19527
|
+
/**
|
|
19528
|
+
* Tolerant list parser for earn opportunities.
|
|
19529
|
+
*
|
|
19530
|
+
* `z.discriminatedUnion` throws on an unrecognized discriminant and
|
|
19531
|
+
* `z.array` fails the whole array if any element fails. Two migration-window
|
|
19532
|
+
* cases are smoothed over here so neither breaks an already-shipped SDK:
|
|
19533
|
+
*
|
|
19534
|
+
* - A backend that predates `productType` omits it entirely. `'vault'` was the
|
|
19535
|
+
* only opportunity type then, so default a missing discriminant to `'vault'`
|
|
19536
|
+
* rather than dropping every vault the backend returns.
|
|
19537
|
+
* - A future backend adds a *second* `productType` this SDK version does not
|
|
19538
|
+
* know. Drop those elements (a present-but-unrecognized discriminant) instead
|
|
19539
|
+
* of rejecting the whole list.
|
|
19540
|
+
*
|
|
19541
|
+
* Only the drop above is a *tolerant* case. Anything that is not a plain object
|
|
19542
|
+
* with a present-but-unknown string `productType` — `null`, `undefined`,
|
|
19543
|
+
* primitives, or an object whose `productType` is malformed — is passed through
|
|
19544
|
+
* untouched so `z.array(earnOpportunitySchema)` reports it as a normal
|
|
19545
|
+
* validation failure. It is deliberately not silently dropped (which would hide
|
|
19546
|
+
* malformed backend data) and never throws here (an unguarded property read on
|
|
19547
|
+
* a non-object would escape `safeParse` as a raw `TypeError` instead of a
|
|
19548
|
+
* `ZodError`).
|
|
19549
|
+
*
|
|
19550
|
+
* @internal
|
|
19551
|
+
*/ const earnOpportunityListSchema = z.preprocess((raw)=>{
|
|
19552
|
+
if (!Array.isArray(raw)) {
|
|
19553
|
+
return raw;
|
|
19554
|
+
}
|
|
19555
|
+
// Array.isArray narrows `raw` to `any[]`; view it as `unknown[]` so the
|
|
19556
|
+
// map/filter chain stays type-safe and no `any` leaks into the return.
|
|
19557
|
+
const entries = raw;
|
|
19558
|
+
return entries.map((entry)=>{
|
|
19559
|
+
// Only touch plain objects; non-objects fall through to fail validation.
|
|
19560
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
19561
|
+
return entry;
|
|
19562
|
+
}
|
|
19563
|
+
const record = entry;
|
|
19564
|
+
// Older backend predating productType: default to the only type then.
|
|
19565
|
+
return record.productType === undefined ? {
|
|
19566
|
+
...record,
|
|
19567
|
+
productType: 'vault'
|
|
19568
|
+
} : record;
|
|
19569
|
+
}).filter((entry)=>{
|
|
19570
|
+
// Drop ONLY a present-but-unknown string discriminant (a future
|
|
19571
|
+
// productType this SDK version doesn't know). Everything else —
|
|
19572
|
+
// non-objects, a non-string productType — flows through to
|
|
19573
|
+
// z.array(earnOpportunitySchema) and fails/passes validation normally.
|
|
19574
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
19575
|
+
return true;
|
|
19576
|
+
}
|
|
19577
|
+
const productType = entry.productType;
|
|
19578
|
+
if (typeof productType !== 'string') {
|
|
19579
|
+
return true;
|
|
19580
|
+
}
|
|
19581
|
+
return knownProductTypes.has(productType);
|
|
19582
|
+
});
|
|
19583
|
+
}, z.array(earnOpportunitySchema));
|
|
19006
19584
|
// ---------------------------------------------------------------------------
|
|
19007
19585
|
// Position response schema
|
|
19008
19586
|
// ---------------------------------------------------------------------------
|
|
@@ -19132,6 +19710,7 @@ const positionPnlSchema = z.discriminatedUnion('status', [
|
|
|
19132
19710
|
*
|
|
19133
19711
|
* @internal
|
|
19134
19712
|
*/ const depositPayloadSchema = z.object({
|
|
19713
|
+
execId: bridgeDepositExecIdSchema,
|
|
19135
19714
|
executionParams: depositExecutionParamsSchema,
|
|
19136
19715
|
signature: hexSignatureSchema
|
|
19137
19716
|
});
|
|
@@ -19223,6 +19802,21 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19223
19802
|
amount: amountJsonSchema,
|
|
19224
19803
|
vaultAddress: hexAddressSchema
|
|
19225
19804
|
}).passthrough();
|
|
19805
|
+
/** @internal */ const bridgeQuoteExpirySchema = z.discriminatedUnion('mode', [
|
|
19806
|
+
z.object({
|
|
19807
|
+
mode: z.literal('TIMESTAMP'),
|
|
19808
|
+
expiresAt: z.string().datetime({
|
|
19809
|
+
offset: true
|
|
19810
|
+
})
|
|
19811
|
+
}),
|
|
19812
|
+
z.object({
|
|
19813
|
+
mode: z.literal('BLOCK_NUMBER'),
|
|
19814
|
+
expiresAtBlock: z.number().int(),
|
|
19815
|
+
blockEstimatedAt: z.string().datetime({
|
|
19816
|
+
offset: true
|
|
19817
|
+
}).optional()
|
|
19818
|
+
})
|
|
19819
|
+
]).optional().catch(undefined);
|
|
19226
19820
|
/**
|
|
19227
19821
|
* Zod schema for the bridge deposit prepare payload.
|
|
19228
19822
|
*
|
|
@@ -19234,6 +19828,10 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19234
19828
|
execId: bridgeDepositExecIdSchema,
|
|
19235
19829
|
erc3009TypedData: bridgeDepositPreparedBundleSchema,
|
|
19236
19830
|
expiresAt: z.string().datetime(),
|
|
19831
|
+
quoteIssuedAt: z.string().datetime({
|
|
19832
|
+
offset: true
|
|
19833
|
+
}).optional().catch(undefined),
|
|
19834
|
+
quoteExpiry: bridgeQuoteExpirySchema,
|
|
19237
19835
|
review: bridgeDepositPrepareReviewSchema
|
|
19238
19836
|
});
|
|
19239
19837
|
/**
|
|
@@ -19299,6 +19897,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19299
19897
|
*
|
|
19300
19898
|
* @internal
|
|
19301
19899
|
*/ const withdrawPayloadSchema = z.object({
|
|
19900
|
+
execId: bridgeDepositExecIdSchema,
|
|
19302
19901
|
executionParams: withdrawExecutionParamsSchema,
|
|
19303
19902
|
signature: hexSignatureSchema
|
|
19304
19903
|
});
|
|
@@ -19312,6 +19911,27 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19312
19911
|
data: withdrawPayloadSchema
|
|
19313
19912
|
});
|
|
19314
19913
|
// ---------------------------------------------------------------------------
|
|
19914
|
+
// Transaction report response schema
|
|
19915
|
+
// ---------------------------------------------------------------------------
|
|
19916
|
+
/**
|
|
19917
|
+
* Zod schema for the transaction report payload inside the API `data` envelope.
|
|
19918
|
+
*
|
|
19919
|
+
* The Earn Service returns an empty payload (`{"data":{}}`) on success, so the
|
|
19920
|
+
* schema accepts any object shape and does not require specific fields.
|
|
19921
|
+
*
|
|
19922
|
+
* @internal
|
|
19923
|
+
*/ const transactionReportPayloadSchema = z.object({}).passthrough();
|
|
19924
|
+
/**
|
|
19925
|
+
* Zod schema for the `POST /v1/earnKit/transactions/report` API response.
|
|
19926
|
+
*
|
|
19927
|
+
* The Earn Service API wraps the transaction report payload in a `data`
|
|
19928
|
+
* envelope.
|
|
19929
|
+
*
|
|
19930
|
+
* @internal
|
|
19931
|
+
*/ z.object({
|
|
19932
|
+
data: transactionReportPayloadSchema
|
|
19933
|
+
});
|
|
19934
|
+
// ---------------------------------------------------------------------------
|
|
19315
19935
|
// Claim rewards response schema
|
|
19316
19936
|
// ---------------------------------------------------------------------------
|
|
19317
19937
|
/**
|
|
@@ -19372,6 +19992,30 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19372
19992
|
token: z.string(),
|
|
19373
19993
|
amount: amountJsonSchema
|
|
19374
19994
|
});
|
|
19995
|
+
/**
|
|
19996
|
+
* Zod schema for a native gas-fee entry in an EarnKit quote response.
|
|
19997
|
+
*
|
|
19998
|
+
* The Earn Service backend estimates gas server-side and returns one entry per
|
|
19999
|
+
* action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
|
|
20000
|
+
* `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
|
|
20001
|
+
* integer string in the chain's native base units. When the backend cannot
|
|
20002
|
+
* estimate an action it returns `fees: null` with an `error` message instead.
|
|
20003
|
+
*
|
|
20004
|
+
* The schema deliberately validates almost nothing beyond the envelope: `name`
|
|
20005
|
+
* is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
|
|
20006
|
+
* of `fees` — that it is an object at all, and that `gas`, `gasPrice`, and
|
|
20007
|
+
* `fee` are parseable integer strings — is deferred to {@link toQuoteGasFees},
|
|
20008
|
+
* which degrades a malformed entry to a `fees: null` soft failure. This is
|
|
20009
|
+
* intentional: gas is best-effort, so a single unparseable gas entry (a wrong
|
|
20010
|
+
* type such as `fees: 123` or `fees: 'bad'`, a missing field, or a non-numeric
|
|
20011
|
+
* `fee`) must never fail Zod validation and reject the entire quote.
|
|
20012
|
+
*
|
|
20013
|
+
* @internal
|
|
20014
|
+
*/ const quoteGasFeeSchema = z.object({
|
|
20015
|
+
name: z.string().optional(),
|
|
20016
|
+
fees: z.unknown(),
|
|
20017
|
+
error: z.string().optional()
|
|
20018
|
+
}).passthrough();
|
|
19375
20019
|
/**
|
|
19376
20020
|
* Zod schema for the inner deposit quote payload.
|
|
19377
20021
|
*
|
|
@@ -19387,7 +20031,8 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19387
20031
|
expectedShares: amountJsonSchema,
|
|
19388
20032
|
sharePrice: z.string(),
|
|
19389
20033
|
currentApy: z.number(),
|
|
19390
|
-
fees: z.array(feeSchema).optional()
|
|
20034
|
+
fees: z.array(feeSchema).optional(),
|
|
20035
|
+
gasFees: z.array(quoteGasFeeSchema).optional()
|
|
19391
20036
|
});
|
|
19392
20037
|
/**
|
|
19393
20038
|
* Zod schema for the `POST /v1/earnKit/deposit/quote` API response.
|
|
@@ -19414,6 +20059,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19414
20059
|
sharePrice: z.string(),
|
|
19415
20060
|
maxWithdrawable: amountJsonSchema,
|
|
19416
20061
|
fees: z.array(feeSchema),
|
|
20062
|
+
gasFees: z.array(quoteGasFeeSchema).optional(),
|
|
19417
20063
|
warnings: z.array(z.string()).optional()
|
|
19418
20064
|
});
|
|
19419
20065
|
/**
|
|
@@ -19471,7 +20117,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19471
20117
|
*
|
|
19472
20118
|
* @internal
|
|
19473
20119
|
*/ const getVaultsPayloadSchema = z.object({
|
|
19474
|
-
vaults:
|
|
20120
|
+
vaults: earnOpportunityListSchema,
|
|
19475
20121
|
errors: z.array(vaultErrorSchema)
|
|
19476
20122
|
});
|
|
19477
20123
|
/**
|
|
@@ -19501,7 +20147,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
19501
20147
|
*
|
|
19502
20148
|
* @internal
|
|
19503
20149
|
*/ const exploreVaultsPayloadSchema = z.object({
|
|
19504
|
-
vaults:
|
|
20150
|
+
vaults: earnOpportunityListSchema,
|
|
19505
20151
|
pagination: explorePaginationSchema
|
|
19506
20152
|
});
|
|
19507
20153
|
/**
|
|
@@ -20069,6 +20715,8 @@ function hasCrossChainDepositQuoteShape(params) {
|
|
|
20069
20715
|
config: earnConfigSchema.optional()
|
|
20070
20716
|
});
|
|
20071
20717
|
|
|
20718
|
+
/** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
|
|
20719
|
+
|
|
20072
20720
|
// Auto-register this kit for user agent tracking
|
|
20073
20721
|
registerKit(`${pkg.name}/${pkg.version}`);
|
|
20074
20722
|
|