@circle-fin/app-kit 1.15.1 → 1.15.3
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 +12 -0
- package/bridge.cjs +61 -18
- package/bridge.d.cts +71 -0
- package/bridge.d.mts +71 -0
- package/bridge.d.ts +71 -0
- package/bridge.mjs +61 -18
- package/context.d.cts +71 -0
- package/context.d.mts +71 -0
- package/context.d.ts +71 -0
- package/earn.cjs +383 -43
- package/earn.d.cts +19 -26
- package/earn.d.mts +19 -26
- package/earn.d.ts +19 -26
- package/earn.mjs +383 -43
- package/estimateBridge.cjs +61 -18
- package/estimateBridge.d.cts +71 -0
- package/estimateBridge.d.mts +71 -0
- package/estimateBridge.d.ts +71 -0
- package/estimateBridge.mjs +61 -18
- package/estimateSwap.cjs +59 -16
- package/estimateSwap.d.cts +71 -0
- package/estimateSwap.d.mts +71 -0
- package/estimateSwap.d.ts +71 -0
- package/estimateSwap.mjs +59 -16
- package/index.cjs +291 -63
- package/index.d.cts +184 -91
- package/index.d.mts +184 -91
- package/index.d.ts +184 -91
- package/index.mjs +291 -63
- package/package.json +4 -4
- package/server.cjs +13 -17
- package/server.d.cts +10 -9
- package/server.d.mts +10 -9
- package/server.d.ts +10 -9
- package/server.mjs +13 -17
- package/swap.cjs +59 -16
- package/swap.d.cts +71 -0
- package/swap.d.mts +71 -0
- package/swap.d.ts +71 -0
- package/swap.mjs +59 -16
package/earn.mjs
CHANGED
|
@@ -698,6 +698,29 @@ class KitError extends Error {
|
|
|
698
698
|
name: 'INPUT_VALIDATION_FAILED',
|
|
699
699
|
type: 'INPUT'
|
|
700
700
|
}};
|
|
701
|
+
/**
|
|
702
|
+
* Standardized error definitions for BALANCE type errors.
|
|
703
|
+
*
|
|
704
|
+
* BALANCE errors indicate insufficient funds or allowance issues
|
|
705
|
+
* that prevent transaction execution.
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* ```typescript
|
|
709
|
+
* import { BalanceError } from '@core/errors'
|
|
710
|
+
*
|
|
711
|
+
* const error = new KitError({
|
|
712
|
+
* ...BalanceError.INSUFFICIENT_TOKEN,
|
|
713
|
+
* recoverability: 'FATAL',
|
|
714
|
+
* message: 'Insufficient USDC balance on Ethereum',
|
|
715
|
+
* cause: { trace: { required: '100', available: '50' } }
|
|
716
|
+
* })
|
|
717
|
+
* ```
|
|
718
|
+
*/ const BalanceError = {
|
|
719
|
+
/** Insufficient token balance for transaction */ INSUFFICIENT_TOKEN: {
|
|
720
|
+
code: 9001,
|
|
721
|
+
name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
722
|
+
type: 'BALANCE'
|
|
723
|
+
}};
|
|
701
724
|
/**
|
|
702
725
|
* Standardized error definitions for ONCHAIN type errors.
|
|
703
726
|
*
|
|
@@ -9129,7 +9152,7 @@ const swapTokenEnumSchema = z.enum([
|
|
|
9129
9152
|
}
|
|
9130
9153
|
|
|
9131
9154
|
var name$3 = "@circle-fin/bridge-kit";
|
|
9132
|
-
var version$3 = "1.15.
|
|
9155
|
+
var version$3 = "1.15.1";
|
|
9133
9156
|
var pkg$3 = {
|
|
9134
9157
|
name: name$3,
|
|
9135
9158
|
version: version$3};
|
|
@@ -9217,7 +9240,7 @@ const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
|
|
|
9217
9240
|
*
|
|
9218
9241
|
* @internal
|
|
9219
9242
|
*/ function isReadActionKey(action) {
|
|
9220
|
-
return READ_ACTION_KEY_SET.has(action);
|
|
9243
|
+
return typeof action === 'string' && READ_ACTION_KEY_SET.has(action);
|
|
9221
9244
|
}
|
|
9222
9245
|
|
|
9223
9246
|
/**
|
|
@@ -12541,6 +12564,86 @@ const abiParameterSchema = z.object({
|
|
|
12541
12564
|
};
|
|
12542
12565
|
}
|
|
12543
12566
|
|
|
12567
|
+
/**
|
|
12568
|
+
* EIP-7702 delegation indicator. A 7702-delegated EOA's bytecode is
|
|
12569
|
+
* `0xef0100` followed by the 20-byte delegate address (23 bytes total).
|
|
12570
|
+
* The underlying secp256k1 key still produces `ecrecover`-verifiable
|
|
12571
|
+
* signatures, so for Gateway's purposes a 7702-delegated address is
|
|
12572
|
+
* an EOA, not a contract signer.
|
|
12573
|
+
*
|
|
12574
|
+
* Spec: https://eips.ethereum.org/EIPS/eip-7702
|
|
12575
|
+
*/ const EIP_7702_DELEGATION_PREFIX = '0xef0100';
|
|
12576
|
+
/**
|
|
12577
|
+
* Determine whether `address` on `chain` signs as a contract (ERC-1271)
|
|
12578
|
+
* rather than as an EOA.
|
|
12579
|
+
*
|
|
12580
|
+
* Gateway validates burn-intent signatures two ways: a static `ecrecover`
|
|
12581
|
+
* check for EOAs, and — for requests that carry `contractSigner: true` —
|
|
12582
|
+
* an offchain `isValidSignature` simulation against the signing contract
|
|
12583
|
+
* (ERC-1271). Gateway does not infer which one to use, so the caller must
|
|
12584
|
+
* declare it. This detects the contract case from on-chain bytecode.
|
|
12585
|
+
*
|
|
12586
|
+
* EIP-7702-delegated EOAs are treated as EOAs: they expose non-empty
|
|
12587
|
+
* bytecode (`0xef0100<delegate>`) but the underlying secp256k1 key still
|
|
12588
|
+
* produces `ecrecover`-verifiable signatures, so the cheaper EOA path
|
|
12589
|
+
* stays correct for them.
|
|
12590
|
+
*
|
|
12591
|
+
* If bytecode cannot be read (RPC failure, etc.) the address is reported
|
|
12592
|
+
* as an EOA and a warning is logged so the fallback is diagnosable. A
|
|
12593
|
+
* genuine contract signer misreported this way is rejected by Gateway with
|
|
12594
|
+
* an invalid-signature error rather than silently mis-attested.
|
|
12595
|
+
*
|
|
12596
|
+
* @param adapter - Anything exposing {@link EvmAdapterLike.readBytecode}.
|
|
12597
|
+
* @param address - Signer address to classify.
|
|
12598
|
+
* @param chain - EVM chain where the signer lives.
|
|
12599
|
+
* @returns `true` when the signer is a contract account and the transfer
|
|
12600
|
+
* request must set `contractSigner: true`; `false` otherwise.
|
|
12601
|
+
*
|
|
12602
|
+
* @example
|
|
12603
|
+
* ```typescript
|
|
12604
|
+
* import { isContractSigner } from '@core/adapter-evm'
|
|
12605
|
+
* import { Ethereum } from '@core/chains'
|
|
12606
|
+
*
|
|
12607
|
+
* const useErc1271 = await isContractSigner(adapter, '0xabc...', Ethereum)
|
|
12608
|
+
* ```
|
|
12609
|
+
*/ async function isContractSigner(adapter, address, chain) {
|
|
12610
|
+
let code;
|
|
12611
|
+
try {
|
|
12612
|
+
code = await adapter.readBytecode(address, chain);
|
|
12613
|
+
} catch (err) {
|
|
12614
|
+
console.warn(`[gateway] isContractSigner defaulting to EOA (readBytecode failed ` + `for ${address} on ${chain.name}): ` + (err instanceof Error ? err.message : String(err)));
|
|
12615
|
+
return false;
|
|
12616
|
+
}
|
|
12617
|
+
if (code === undefined || code === '0x' || code.toLowerCase().startsWith(EIP_7702_DELEGATION_PREFIX)) {
|
|
12618
|
+
return false;
|
|
12619
|
+
}
|
|
12620
|
+
return true;
|
|
12621
|
+
}
|
|
12622
|
+
|
|
12623
|
+
/**
|
|
12624
|
+
* Duck-type test for an EVM adapter that exposes `readBytecode`.
|
|
12625
|
+
*
|
|
12626
|
+
* Used as a structural guard at package boundaries (e.g. the Circle Wallets
|
|
12627
|
+
* hybrid adapter calling into a chain adapter, or the Gateway sign path
|
|
12628
|
+
* receiving an arbitrary adapter implementation) where `instanceof EvmAdapter`
|
|
12629
|
+
* is unreliable because each consumer bundles its own copy of the base class.
|
|
12630
|
+
*
|
|
12631
|
+
* @param value - The value to test.
|
|
12632
|
+
* @returns `true` when `value` is an object exposing a callable
|
|
12633
|
+
* `readBytecode` method, narrowed to {@link EvmAdapterLike}.
|
|
12634
|
+
*
|
|
12635
|
+
* @example
|
|
12636
|
+
* ```typescript
|
|
12637
|
+
* import { isEvmAdapterLike } from '@core/adapter-evm'
|
|
12638
|
+
*
|
|
12639
|
+
* if (isEvmAdapterLike(adapter)) {
|
|
12640
|
+
* const code = await adapter.readBytecode('0xabc...', chain)
|
|
12641
|
+
* }
|
|
12642
|
+
* ```
|
|
12643
|
+
*/ function isEvmAdapterLike(value) {
|
|
12644
|
+
return typeof value === 'object' && value !== null && 'readBytecode' in value && typeof value.readBytecode === 'function';
|
|
12645
|
+
}
|
|
12646
|
+
|
|
12544
12647
|
const hexBytesSchema$1 = z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
|
|
12545
12648
|
message: 'value must be a 0x-prefixed even-length hex string'
|
|
12546
12649
|
}).transform((value)=>value);
|
|
@@ -14435,7 +14538,7 @@ new Set(Object.values(Blockchain));
|
|
|
14435
14538
|
registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
14436
14539
|
|
|
14437
14540
|
var name$1 = "@circle-fin/earn-kit";
|
|
14438
|
-
var version$1 = "1.
|
|
14541
|
+
var version$1 = "1.8.0";
|
|
14439
14542
|
var pkg$1 = {
|
|
14440
14543
|
name: name$1,
|
|
14441
14544
|
version: version$1};
|
|
@@ -14974,6 +15077,142 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
14974
15077
|
};
|
|
14975
15078
|
}
|
|
14976
15079
|
|
|
15080
|
+
/**
|
|
15081
|
+
* Fallback gas reserve expressed in ERC-20 deposit token base units when no
|
|
15082
|
+
* quote gas fees are available. 50_000 = 0.05 USDC at the 6-decimal ERC-20
|
|
15083
|
+
* USDC scale used on Arc (and the scale of `balance` / `depositAmountRaw`).
|
|
15084
|
+
*
|
|
15085
|
+
* @internal
|
|
15086
|
+
*/ const DEFAULT_GAS_RESERVE = 50_000n;
|
|
15087
|
+
/**
|
|
15088
|
+
* Standard ERC-20 decimal precision for USDC and EURC. Both tokens use 6
|
|
15089
|
+
* decimals on all EVM chains, regardless of how many decimals the native
|
|
15090
|
+
* gas-token representation uses.
|
|
15091
|
+
*
|
|
15092
|
+
* @internal
|
|
15093
|
+
*/ const STABLECOIN_ERC20_DECIMALS = 6;
|
|
15094
|
+
/**
|
|
15095
|
+
* Compute the gas reserve in ERC-20 deposit token base units.
|
|
15096
|
+
*
|
|
15097
|
+
* `EstimatedGas.fee` is denominated in the chain's native currency base units
|
|
15098
|
+
* (e.g. 18-decimal USDC on Arc). The deposit token balance and
|
|
15099
|
+
* `depositAmountRaw` are in ERC-20 token base units (e.g. 6-decimal USDC).
|
|
15100
|
+
* This function converts the native fee total to the same scale so both sides
|
|
15101
|
+
* of the `balance < required` comparison are commensurable.
|
|
15102
|
+
*
|
|
15103
|
+
* Conversion: `fee_token_units = fee_native_units / 10^(nativeDecimals - tokenDecimals)`
|
|
15104
|
+
*
|
|
15105
|
+
* Falls back to {@link DEFAULT_GAS_RESERVE} when:
|
|
15106
|
+
* - `gasFees` is absent or empty
|
|
15107
|
+
* - all fee entries have `fees: null` (estimation failed)
|
|
15108
|
+
* - the converted total rounds to zero (native fee smaller than 1 token unit)
|
|
15109
|
+
*
|
|
15110
|
+
* @internal
|
|
15111
|
+
*/ function computeGasReserve(gasFees, nativeDecimals, tokenDecimals) {
|
|
15112
|
+
if (gasFees === undefined || gasFees.length === 0) {
|
|
15113
|
+
return DEFAULT_GAS_RESERVE;
|
|
15114
|
+
}
|
|
15115
|
+
let totalNative = 0n;
|
|
15116
|
+
let hasNullEntry = false;
|
|
15117
|
+
for (const entry of gasFees){
|
|
15118
|
+
if (entry.fees === null) {
|
|
15119
|
+
hasNullEntry = true;
|
|
15120
|
+
} else {
|
|
15121
|
+
totalNative += BigInt(entry.fees.fee);
|
|
15122
|
+
}
|
|
15123
|
+
}
|
|
15124
|
+
const decimalDiff = nativeDecimals - tokenDecimals;
|
|
15125
|
+
// Use ceiling division when reducing precision so we never under-estimate.
|
|
15126
|
+
const scale = decimalDiff > 0 ? 10n ** BigInt(decimalDiff) : 1n;
|
|
15127
|
+
let knownToken;
|
|
15128
|
+
if (totalNative === 0n) {
|
|
15129
|
+
knownToken = 0n;
|
|
15130
|
+
} else if (decimalDiff > 0) {
|
|
15131
|
+
knownToken = (totalNative + scale - 1n) / scale;
|
|
15132
|
+
} else {
|
|
15133
|
+
knownToken = totalNative * 10n ** BigInt(-decimalDiff);
|
|
15134
|
+
}
|
|
15135
|
+
// When any estimate is missing the true total is unknown, so reserve at
|
|
15136
|
+
// least DEFAULT_GAS_RESERVE on top of whatever was already known.
|
|
15137
|
+
// BigInt is not supported by Math.max, so we use an explicit comparison.
|
|
15138
|
+
if (hasNullEntry) {
|
|
15139
|
+
return knownToken > DEFAULT_GAS_RESERVE ? knownToken : DEFAULT_GAS_RESERVE // NOSONAR
|
|
15140
|
+
;
|
|
15141
|
+
}
|
|
15142
|
+
return knownToken > 0n ? knownToken : DEFAULT_GAS_RESERVE;
|
|
15143
|
+
}
|
|
15144
|
+
/**
|
|
15145
|
+
* Build a symbol → ERC-20 address map from the chain's known token addresses.
|
|
15146
|
+
* Used to resolve whether the native gas token corresponds to the deposit
|
|
15147
|
+
* token's address.
|
|
15148
|
+
*
|
|
15149
|
+
* @internal
|
|
15150
|
+
*/ function buildSymbolAddressMap(chain) {
|
|
15151
|
+
const map = new Map();
|
|
15152
|
+
if (chain.usdcAddress != null) {
|
|
15153
|
+
map.set('USDC', chain.usdcAddress.toLowerCase());
|
|
15154
|
+
}
|
|
15155
|
+
if (chain.eurcAddress != null) {
|
|
15156
|
+
map.set('EURC', chain.eurcAddress.toLowerCase());
|
|
15157
|
+
}
|
|
15158
|
+
return map;
|
|
15159
|
+
}
|
|
15160
|
+
/**
|
|
15161
|
+
* Pre-broadcast balance check for EOA wallets on chains where the native gas
|
|
15162
|
+
* token and the deposit token are the same asset (e.g. Arc: USDC gas + USDC
|
|
15163
|
+
* deposit).
|
|
15164
|
+
*
|
|
15165
|
+
* When the deposit would consume the entire balance, the adapter locks gas
|
|
15166
|
+
* before `transferFrom`, causing an `ERC20: transfer amount exceeds balance`
|
|
15167
|
+
* revert on-chain. This function reads the wallet balance before broadcast and
|
|
15168
|
+
* throws {@link BalanceError.INSUFFICIENT_TOKEN} (code 9001) when
|
|
15169
|
+
* `balance < depositAmount + gasReserve`, giving callers a clean pre-broadcast
|
|
15170
|
+
* error instead of an opaque chain revert.
|
|
15171
|
+
*
|
|
15172
|
+
* The check is skipped when the chain's native gas currency symbol does not
|
|
15173
|
+
* match the deposit token symbol (derived by address lookup). Examples of
|
|
15174
|
+
* chains where the check is skipped: EURC deposit on Arc, any ETH-gas chain.
|
|
15175
|
+
*
|
|
15176
|
+
* Gas fees from the quote (`gasFees`) are in native-currency base units
|
|
15177
|
+
* (18-decimal on Arc) and are scaled down to ERC-20 token units (6-decimal)
|
|
15178
|
+
* before being added to `depositAmountRaw`.
|
|
15179
|
+
*
|
|
15180
|
+
* @param params - Adapter context, chain, deposit token, amount, and optional
|
|
15181
|
+
* quote gas fees.
|
|
15182
|
+
* @throws {@link KitError} (code 9001) when the balance is insufficient to
|
|
15183
|
+
* cover both the deposit and the gas reserve.
|
|
15184
|
+
*
|
|
15185
|
+
* @internal
|
|
15186
|
+
*/ async function checkGasReserveForDeposit(params) {
|
|
15187
|
+
const { adapter, chain, address, depositTokenAddress, depositAmountRaw, gasFees } = params;
|
|
15188
|
+
const nativeSymbol = chain.nativeCurrency.symbol.toUpperCase();
|
|
15189
|
+
const symbolAddressMap = buildSymbolAddressMap(chain);
|
|
15190
|
+
const nativeTokenAddress = symbolAddressMap.get(nativeSymbol);
|
|
15191
|
+
// Only applies when native gas currency and deposit token are the same asset.
|
|
15192
|
+
if (nativeTokenAddress === undefined || depositTokenAddress.toLowerCase() !== nativeTokenAddress) {
|
|
15193
|
+
return;
|
|
15194
|
+
}
|
|
15195
|
+
const rawBalance = await executeAdapterReadAction(adapter, 'token.balanceOf', {
|
|
15196
|
+
tokenAddress: depositTokenAddress,
|
|
15197
|
+
walletAddress: address
|
|
15198
|
+
}, {
|
|
15199
|
+
chain,
|
|
15200
|
+
address
|
|
15201
|
+
});
|
|
15202
|
+
const balance = BigInt(String(rawBalance));
|
|
15203
|
+
// Both USDC and EURC use 6 ERC-20 decimals; the check only reaches here when
|
|
15204
|
+
// the deposit token is the chain's native gas asset (USDC or EURC).
|
|
15205
|
+
const gasReserve = computeGasReserve(gasFees, chain.nativeCurrency.decimals, STABLECOIN_ERC20_DECIMALS);
|
|
15206
|
+
const required = depositAmountRaw + gasReserve;
|
|
15207
|
+
if (balance < required) {
|
|
15208
|
+
throw new KitError({
|
|
15209
|
+
...BalanceError.INSUFFICIENT_TOKEN,
|
|
15210
|
+
recoverability: 'FATAL',
|
|
15211
|
+
message: `Insufficient ${chain.nativeCurrency.symbol} balance to cover deposit and gas. Balance: ${balance.toString()}, required: ${required.toString()} (deposit: ${depositAmountRaw.toString()} + gas reserve: ${gasReserve.toString()})`
|
|
15212
|
+
});
|
|
15213
|
+
}
|
|
15214
|
+
}
|
|
15215
|
+
|
|
14977
15216
|
/** @internal */ function isSameAddress(actual, expected) {
|
|
14978
15217
|
return actual.toLowerCase() === expected.toLowerCase();
|
|
14979
15218
|
}
|
|
@@ -16325,12 +16564,19 @@ const EARN_OPERATIONS = new Set([
|
|
|
16325
16564
|
writable: true,
|
|
16326
16565
|
configurable: true
|
|
16327
16566
|
});
|
|
16567
|
+
// Surface the tx hash in the message so callers can look it up without
|
|
16568
|
+
// digging into cause.trace. Only applies to on-chain reverts — other errors
|
|
16569
|
+
// either have no tx hash or already embed it in their message.
|
|
16570
|
+
let message = base.message;
|
|
16571
|
+
if (base.name === 'ONCHAIN_TRANSACTION_REVERTED' && typeof existingTrace['txHash'] === 'string' && existingTrace['txHash'] !== '') {
|
|
16572
|
+
message = `${message} (txHash: ${existingTrace['txHash']})`;
|
|
16573
|
+
}
|
|
16328
16574
|
return new KitError({
|
|
16329
16575
|
code: base.code,
|
|
16330
16576
|
name: base.name,
|
|
16331
16577
|
type: base.type,
|
|
16332
16578
|
recoverability,
|
|
16333
|
-
message
|
|
16579
|
+
message,
|
|
16334
16580
|
cause: {
|
|
16335
16581
|
trace
|
|
16336
16582
|
}
|
|
@@ -16446,10 +16692,8 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
16446
16692
|
assetAddress: z.string(),
|
|
16447
16693
|
lltv: z.number(),
|
|
16448
16694
|
supplyUsd: z.number(),
|
|
16449
|
-
//
|
|
16450
|
-
|
|
16451
|
-
// schema; `null` when the product exposes no per-market allocation (V2).
|
|
16452
|
-
allocationPct: z.number().nullable().optional()
|
|
16695
|
+
// `null` when the product exposes no per-market allocation (V2).
|
|
16696
|
+
allocationPct: z.number().nullable()
|
|
16453
16697
|
});
|
|
16454
16698
|
/**
|
|
16455
16699
|
* Zod schema for a Morpho vault warning in the API response.
|
|
@@ -16556,19 +16800,20 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
16556
16800
|
earnKitWarnings: z.array(z.string()).optional()
|
|
16557
16801
|
});
|
|
16558
16802
|
/**
|
|
16559
|
-
* Shared base schema: existing flat fields (kept
|
|
16560
|
-
* facets
|
|
16561
|
-
*
|
|
16803
|
+
* Shared base schema: existing flat fields (kept as deprecated aliases) plus
|
|
16804
|
+
* the nested facets. Zenith emits all facets on every response; required here
|
|
16805
|
+
* so a partial emission is caught at parse time rather than surfacing as a
|
|
16806
|
+
* runtime `undefined` access.
|
|
16562
16807
|
*
|
|
16563
16808
|
* @internal
|
|
16564
16809
|
*/ const earnBaseSchema = vaultInfoResponseSchema.extend({
|
|
16565
|
-
address: z.string()
|
|
16566
|
-
asOf: z.string()
|
|
16567
|
-
manager: managerSchema.nullable()
|
|
16568
|
-
apyProfile: apyProfileSchema
|
|
16569
|
-
fee: feeInfoSchema
|
|
16570
|
-
liquidityProfile: liquidityProfileSchema
|
|
16571
|
-
riskSignals: riskSignalsSchema
|
|
16810
|
+
address: z.string(),
|
|
16811
|
+
asOf: z.string(),
|
|
16812
|
+
manager: managerSchema.nullable(),
|
|
16813
|
+
apyProfile: apyProfileSchema,
|
|
16814
|
+
fee: feeInfoSchema,
|
|
16815
|
+
liquidityProfile: liquidityProfileSchema,
|
|
16816
|
+
riskSignals: riskSignalsSchema
|
|
16572
16817
|
});
|
|
16573
16818
|
/**
|
|
16574
16819
|
* Zod schema for the `vault` opportunity variant.
|
|
@@ -17356,7 +17601,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
17356
17601
|
}
|
|
17357
17602
|
|
|
17358
17603
|
var name = "@circle-fin/provider-earn-service";
|
|
17359
|
-
var version = "1.
|
|
17604
|
+
var version = "1.7.0";
|
|
17360
17605
|
var pkg = {
|
|
17361
17606
|
name: name,
|
|
17362
17607
|
version: version};
|
|
@@ -17504,23 +17749,16 @@ var pkg = {
|
|
|
17504
17749
|
if (chain === undefined) {
|
|
17505
17750
|
throw createInvalidChainError(vault.chain, 'Chain returned by the Earn Service is not supported by the SDK');
|
|
17506
17751
|
}
|
|
17507
|
-
// The nested facets are `.optional()` in the schema (a backend that predates
|
|
17508
|
-
// them omits them) and are typed optional on `EarnOpportunity` to match.
|
|
17509
|
-
// Convert the nested liquidity amounts when present and pass the remaining
|
|
17510
|
-
// facets straight through; each absent facet stays absent rather than being
|
|
17511
|
-
// asserted present by a cast.
|
|
17512
17752
|
return {
|
|
17513
17753
|
...vault,
|
|
17514
17754
|
chain,
|
|
17515
17755
|
totalDeposits: Amount.fromJSON(totalDeposits),
|
|
17516
17756
|
liquidity: Amount.fromJSON(liquidity),
|
|
17517
|
-
|
|
17518
|
-
liquidityProfile
|
|
17519
|
-
|
|
17520
|
-
|
|
17521
|
-
|
|
17522
|
-
totalSupply: Amount.fromJSON(liquidityProfile.totalSupply)
|
|
17523
|
-
}
|
|
17757
|
+
liquidityProfile: {
|
|
17758
|
+
...liquidityProfile,
|
|
17759
|
+
totalDeposits: Amount.fromJSON(liquidityProfile.totalDeposits),
|
|
17760
|
+
available: Amount.fromJSON(liquidityProfile.available),
|
|
17761
|
+
totalSupply: Amount.fromJSON(liquidityProfile.totalSupply)
|
|
17524
17762
|
}
|
|
17525
17763
|
};
|
|
17526
17764
|
}
|
|
@@ -19026,6 +19264,24 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
19026
19264
|
amount: params.amount
|
|
19027
19265
|
};
|
|
19028
19266
|
}
|
|
19267
|
+
// Only EOA wallets pay native gas from their own balance — SCAs have
|
|
19268
|
+
// their gas sponsored externally. Detect EOA by checking that the
|
|
19269
|
+
// adapter supports readBytecode and the wallet address has no on-chain
|
|
19270
|
+
// bytecode (empty code ⟹ EOA; non-empty ⟹ smart-contract account).
|
|
19271
|
+
// EIP-7702-delegated addresses are treated as EOA by isContractSigner.
|
|
19272
|
+
// If the adapter does not expose readBytecode (non-EVM adapter) the
|
|
19273
|
+
// check is skipped conservatively.
|
|
19274
|
+
if (approvalToken !== undefined && requiredAllowance > 0n && chain.type === 'evm' && isEvmAdapterLike(adapter) && !await isContractSigner(adapter, address, chain)) {
|
|
19275
|
+
const sameChainParams = params;
|
|
19276
|
+
await checkGasReserveForDeposit({
|
|
19277
|
+
adapter,
|
|
19278
|
+
chain,
|
|
19279
|
+
address,
|
|
19280
|
+
depositTokenAddress: approvalToken,
|
|
19281
|
+
depositAmountRaw: requiredAllowance,
|
|
19282
|
+
gasFees: sameChainParams.gasFees
|
|
19283
|
+
});
|
|
19284
|
+
}
|
|
19029
19285
|
if (!options.skipApprove && approvalToken !== undefined && requiredAllowance > 0n) {
|
|
19030
19286
|
await this.runPhase(ctx, 'approve', 'approve', async ()=>{
|
|
19031
19287
|
try {
|
|
@@ -19432,7 +19688,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
19432
19688
|
// Gas is estimated server-side by the Earn Service and returned on the quote, so the
|
|
19433
19689
|
// SDK no longer simulates it locally. `chainDefinition` lets the fetch fill
|
|
19434
19690
|
// the native token symbol / blockchain on each gas entry.
|
|
19435
|
-
|
|
19691
|
+
const quote = await fetchDepositQuote({
|
|
19436
19692
|
vaultAddress: params.vaultAddress,
|
|
19437
19693
|
amount: params.amount,
|
|
19438
19694
|
address,
|
|
@@ -19440,6 +19696,34 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
19440
19696
|
config,
|
|
19441
19697
|
chainDefinition
|
|
19442
19698
|
});
|
|
19699
|
+
// Run the same pre-broadcast gas reserve check here so callers get an early
|
|
19700
|
+
// warning at quote time — before they call deposit() and hit the real check.
|
|
19701
|
+
// Only applies on stablecoin-gas chains (e.g. Arc) where the deposit token
|
|
19702
|
+
// is also the native gas token, and only for EOA wallets that pay native gas
|
|
19703
|
+
// from their own balance. Skips silently on all other chains and wallet types.
|
|
19704
|
+
if (chainDefinition?.type === 'evm' && isEvmAdapterLike(params.from.adapter) && !await isContractSigner(params.from.adapter, address, chainDefinition)) {
|
|
19705
|
+
const symbol = quote.deposit.symbol.toUpperCase();
|
|
19706
|
+
let depositTokenAddress = null;
|
|
19707
|
+
if (symbol === 'USDC') {
|
|
19708
|
+
depositTokenAddress = chainDefinition.usdcAddress;
|
|
19709
|
+
} else if (symbol === 'EURC') {
|
|
19710
|
+
depositTokenAddress = chainDefinition.eurcAddress;
|
|
19711
|
+
}
|
|
19712
|
+
if (depositTokenAddress != null) {
|
|
19713
|
+
const amountObj = Amount.parse(params.amount, {
|
|
19714
|
+
decimals: 6
|
|
19715
|
+
});
|
|
19716
|
+
await checkGasReserveForDeposit({
|
|
19717
|
+
adapter: params.from.adapter,
|
|
19718
|
+
chain: chainDefinition,
|
|
19719
|
+
address,
|
|
19720
|
+
depositTokenAddress,
|
|
19721
|
+
depositAmountRaw: amountObj.raw,
|
|
19722
|
+
gasFees: quote.gasFees
|
|
19723
|
+
});
|
|
19724
|
+
}
|
|
19725
|
+
}
|
|
19726
|
+
return quote;
|
|
19443
19727
|
}
|
|
19444
19728
|
/** {@inheritdoc} */ async getWithdrawalQuote(params) {
|
|
19445
19729
|
const config = this.resolveConfig(params.config);
|
|
@@ -19739,21 +20023,15 @@ function formatPositionPnL(pnl) {
|
|
|
19739
20023
|
// consumers keep receiving them until Contract.
|
|
19740
20024
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
19741
20025
|
const { totalDeposits, liquidity, liquidityProfile, ...rest } = vault;
|
|
19742
|
-
// `liquidityProfile` is `.optional()` in the response schema during the
|
|
19743
|
-
// expand/contract window (an old backend that predates the nested facets
|
|
19744
|
-
// omits it), so only format and re-attach it when present — matching the
|
|
19745
|
-
// provider-side `toVaultInfo` mapper.
|
|
19746
20026
|
return {
|
|
19747
20027
|
...rest,
|
|
19748
20028
|
totalDeposits: formatAmount(totalDeposits),
|
|
19749
20029
|
liquidity: formatAmount(liquidity),
|
|
19750
|
-
|
|
19751
|
-
liquidityProfile
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
totalSupply: formatAmount(liquidityProfile.totalSupply)
|
|
19756
|
-
}
|
|
20030
|
+
liquidityProfile: {
|
|
20031
|
+
...liquidityProfile,
|
|
20032
|
+
totalDeposits: formatAmount(liquidityProfile.totalDeposits),
|
|
20033
|
+
available: formatAmount(liquidityProfile.available),
|
|
20034
|
+
totalSupply: formatAmount(liquidityProfile.totalSupply)
|
|
19757
20035
|
}
|
|
19758
20036
|
};
|
|
19759
20037
|
}
|
|
@@ -20239,6 +20517,49 @@ const sourceAdapterContextSchema = z.object({
|
|
|
20239
20517
|
vaultAddress: vaultAddressSchema,
|
|
20240
20518
|
config: earnConfigSchema.optional()
|
|
20241
20519
|
});
|
|
20520
|
+
/**
|
|
20521
|
+
* Schema for a successful gas fee entry (fees is non-null).
|
|
20522
|
+
*
|
|
20523
|
+
* Validates the kit shape — {@link EarnEstimatedGas} uses decimal strings for
|
|
20524
|
+
* `gas`, `gasPrice`, and `fee`. Only digits are accepted so that the deposit
|
|
20525
|
+
* operation can safely convert them to `bigint` before passing them to the
|
|
20526
|
+
* provider's `computeGasReserve`.
|
|
20527
|
+
*
|
|
20528
|
+
* @internal
|
|
20529
|
+
*/ const earnGasFeeEstimateSuccessSchema = z.object({
|
|
20530
|
+
name: z.string(),
|
|
20531
|
+
token: z.string(),
|
|
20532
|
+
blockchain: z.string(),
|
|
20533
|
+
fees: z.object({
|
|
20534
|
+
gas: z.string().regex(/^\d+$/, 'gas must be a non-negative integer string'),
|
|
20535
|
+
gasPrice: z.string().regex(/^\d+$/, 'gasPrice must be a non-negative integer string'),
|
|
20536
|
+
fee: z.string().regex(/^\d+$/, 'fee must be a non-negative integer string')
|
|
20537
|
+
}),
|
|
20538
|
+
error: z.undefined().optional()
|
|
20539
|
+
}).strict();
|
|
20540
|
+
/**
|
|
20541
|
+
* Schema for a failed gas fee entry (fees is null, error is a string).
|
|
20542
|
+
*
|
|
20543
|
+
* @internal
|
|
20544
|
+
*/ const earnGasFeeEstimateFailureSchema = z.object({
|
|
20545
|
+
name: z.string(),
|
|
20546
|
+
token: z.string(),
|
|
20547
|
+
blockchain: z.string(),
|
|
20548
|
+
fees: z.null(),
|
|
20549
|
+
error: z.string()
|
|
20550
|
+
}).strict();
|
|
20551
|
+
/**
|
|
20552
|
+
* Schema for a single kit-level gas fee estimate entry.
|
|
20553
|
+
*
|
|
20554
|
+
* Rejects arbitrary inputs (null, numbers, empty objects, non-numeric fee
|
|
20555
|
+
* strings) so callers receive a descriptive validation error instead of a
|
|
20556
|
+
* raw `BigInt` conversion error inside `computeGasReserve`.
|
|
20557
|
+
*
|
|
20558
|
+
* @internal
|
|
20559
|
+
*/ const earnGasFeeEstimateSchema = z.union([
|
|
20560
|
+
earnGasFeeEstimateSuccessSchema,
|
|
20561
|
+
earnGasFeeEstimateFailureSchema
|
|
20562
|
+
]);
|
|
20242
20563
|
/**
|
|
20243
20564
|
* Validation schema for the same-chain variant of DepositParams.
|
|
20244
20565
|
*
|
|
@@ -20251,6 +20572,7 @@ const sourceAdapterContextSchema = z.object({
|
|
|
20251
20572
|
to: z.undefined().optional(),
|
|
20252
20573
|
vaultAddress: vaultAddressSchema,
|
|
20253
20574
|
amount: amountSchema,
|
|
20575
|
+
gasFees: z.array(earnGasFeeEstimateSchema).optional(),
|
|
20254
20576
|
config: earnConfigSchema.optional()
|
|
20255
20577
|
}).strict();
|
|
20256
20578
|
const crossChainDepositDestinationSchema = z.object({
|
|
@@ -20871,6 +21193,9 @@ async function deposit$1(context, params) {
|
|
|
20871
21193
|
},
|
|
20872
21194
|
vaultAddress: params.vaultAddress,
|
|
20873
21195
|
amount: params.amount,
|
|
21196
|
+
...!hasExplicitDestination && params.gasFees !== undefined && {
|
|
21197
|
+
gasFees: toProviderGasFees(params.gasFees)
|
|
21198
|
+
},
|
|
20874
21199
|
...params.config !== undefined && {
|
|
20875
21200
|
config: params.config
|
|
20876
21201
|
}
|
|
@@ -20893,6 +21218,21 @@ async function deposit$1(context, params) {
|
|
|
20893
21218
|
}
|
|
20894
21219
|
return provider.deposit(baseParams);
|
|
20895
21220
|
}
|
|
21221
|
+
function toProviderGasFees(gasFees) {
|
|
21222
|
+
return gasFees.map((entry)=>{
|
|
21223
|
+
if (entry.fees === null) {
|
|
21224
|
+
return entry; // NOSONAR
|
|
21225
|
+
}
|
|
21226
|
+
return {
|
|
21227
|
+
...entry,
|
|
21228
|
+
fees: {
|
|
21229
|
+
...entry.fees,
|
|
21230
|
+
gas: BigInt(entry.fees.gas),
|
|
21231
|
+
gasPrice: BigInt(entry.fees.gasPrice)
|
|
21232
|
+
}
|
|
21233
|
+
};
|
|
21234
|
+
});
|
|
21235
|
+
}
|
|
20896
21236
|
function hasCrossChainDestination(params) {
|
|
20897
21237
|
// Runs before schema validation, so guard against non-object params from
|
|
20898
21238
|
// untyped callers; assertEarnParams reports those with a structured error.
|