@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/earn.mjs
CHANGED
|
@@ -16,15 +16,29 @@
|
|
|
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';
|
|
32
|
+
import '@ethersproject/bytes';
|
|
33
|
+
import '@ethersproject/abi';
|
|
34
|
+
import '@ethersproject/address';
|
|
21
35
|
import { PublicKey } from '@solana/web3.js';
|
|
22
36
|
import 'bn.js';
|
|
23
37
|
import '@coral-xyz/anchor';
|
|
24
38
|
import 'bs58';
|
|
25
39
|
import '@noble/curves/ed25519';
|
|
26
|
-
import { keccak256 } from '@ethersproject/keccak256';
|
|
27
40
|
import { formatUnits as formatUnits$1 } from '@ethersproject/units';
|
|
41
|
+
import { keccak256 } from '@ethersproject/keccak256';
|
|
28
42
|
|
|
29
43
|
// Import global type declarations
|
|
30
44
|
/**
|
|
@@ -41,6 +55,51 @@ import { formatUnits as formatUnits$1 } from '@ethersproject/units';
|
|
|
41
55
|
* }
|
|
42
56
|
* ```
|
|
43
57
|
*/ const isNodeEnvironment = ()=>typeof process !== 'undefined' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
|
|
58
|
+
/**
|
|
59
|
+
* Check whether the current runtime exposes a browser DOM.
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* This intentionally does not treat every non-Node runtime as a browser.
|
|
63
|
+
* Server-side edge runtimes such as Cloudflare Workers, Deno, and Supabase
|
|
64
|
+
* Edge Functions do not expose Node globals but can safely use server
|
|
65
|
+
* credentials. A Node.js runtime remains server-side even when a test or SSR
|
|
66
|
+
* environment provides a DOM shim.
|
|
67
|
+
*
|
|
68
|
+
* @returns `true` when running in a browser window, `false` otherwise.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* import { isBrowserEnvironment } from '@core/utils'
|
|
73
|
+
*
|
|
74
|
+
* if (isBrowserEnvironment()) {
|
|
75
|
+
* throw new Error('Server-only secrets must not be used in the browser')
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/ const isBrowserEnvironment = ()=>{
|
|
79
|
+
const browserWindow = globalThis.window;
|
|
80
|
+
return !isNodeEnvironment() && browserWindow?.document !== undefined;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Return the SDK User-Agent request header only when running in Node.js.
|
|
84
|
+
*
|
|
85
|
+
* Browsers forbid manually setting `User-Agent`, and a custom fallback header
|
|
86
|
+
* can trigger CORS preflight. Non-Node server runtimes also omit this optional
|
|
87
|
+
* attribution header because they cannot set it reliably.
|
|
88
|
+
*
|
|
89
|
+
* @returns A User-Agent header in Node.js, or an empty object otherwise.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* import { getNodeUserAgentHeader } from '@core/utils'
|
|
94
|
+
*
|
|
95
|
+
* const headers = {
|
|
96
|
+
* 'Content-Type': 'application/json',
|
|
97
|
+
* ...getNodeUserAgentHeader(),
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/ const getNodeUserAgentHeader = ()=>isNodeEnvironment() ? {
|
|
101
|
+
'User-Agent': getUserAgent()
|
|
102
|
+
} : {};
|
|
44
103
|
/**
|
|
45
104
|
* Detect the runtime environment and return a shortened identifier.
|
|
46
105
|
*
|
|
@@ -1759,14 +1818,14 @@ class KitError extends Error {
|
|
|
1759
1818
|
}
|
|
1760
1819
|
|
|
1761
1820
|
/**
|
|
1762
|
-
* Standardized error definitions for Earn
|
|
1821
|
+
* Standardized error definitions for Earn operations.
|
|
1763
1822
|
*
|
|
1764
1823
|
* These error codes provide fine-grained categorization of failures
|
|
1765
|
-
* from the
|
|
1824
|
+
* from the Earn service, enabling SDK consumers to distinguish
|
|
1766
1825
|
* between input errors (fix your request) and service errors (retry later).
|
|
1767
1826
|
*
|
|
1768
1827
|
* Error code ranges:
|
|
1769
|
-
* - 1100-
|
|
1828
|
+
* - 1100-1106: INPUT errors — invalid, unsupported, or stale request state
|
|
1770
1829
|
* - 8100-8105: SERVICE errors — retryable backend/provider failures
|
|
1771
1830
|
*
|
|
1772
1831
|
* @example
|
|
@@ -1819,6 +1878,14 @@ class KitError extends Error {
|
|
|
1819
1878
|
name: 'EARN_UNSUPPORTED_BRIDGE_ROUTE',
|
|
1820
1879
|
type: 'INPUT'
|
|
1821
1880
|
},
|
|
1881
|
+
/**
|
|
1882
|
+
* The bridge quote expired. This is an INPUT error because the prepared
|
|
1883
|
+
* request is stale and must be replaced instead of retried.
|
|
1884
|
+
*/ BRIDGE_QUOTE_EXPIRED: {
|
|
1885
|
+
code: 1106,
|
|
1886
|
+
name: 'EARN_BRIDGE_QUOTE_EXPIRED',
|
|
1887
|
+
type: 'INPUT'
|
|
1888
|
+
},
|
|
1822
1889
|
/** The proxy signing call failed — retryable. */ SIGNING_FAILED: {
|
|
1823
1890
|
code: 8100,
|
|
1824
1891
|
name: 'EARN_SIGNING_FAILED',
|
|
@@ -1878,6 +1945,9 @@ function getOptionalString(value) {
|
|
|
1878
1945
|
* internal-error, vault-refresh-busy, off-chain-paused, position-PnL-pending,
|
|
1879
1946
|
* bridge failures/status lookup failures
|
|
1880
1947
|
*
|
|
1948
|
+
* Quote expiry is INPUT/FATAL because callers must start a fresh bridge prepare
|
|
1949
|
+
* flow rather than retry the stale prepared bundle.
|
|
1950
|
+
*
|
|
1881
1951
|
* Unrecognized codes fall through to `parseApiError` for HTTP-status-based
|
|
1882
1952
|
* handling.
|
|
1883
1953
|
*
|
|
@@ -2111,6 +2181,13 @@ function getOptionalString(value) {
|
|
|
2111
2181
|
errorDef: EarnError.PROVIDER_ERROR,
|
|
2112
2182
|
recoverability: 'FATAL'
|
|
2113
2183
|
}
|
|
2184
|
+
],
|
|
2185
|
+
[
|
|
2186
|
+
380506,
|
|
2187
|
+
{
|
|
2188
|
+
errorDef: EarnError.BRIDGE_QUOTE_EXPIRED,
|
|
2189
|
+
recoverability: 'FATAL'
|
|
2190
|
+
}
|
|
2114
2191
|
]
|
|
2115
2192
|
]);
|
|
2116
2193
|
/**
|
|
@@ -2834,7 +2911,10 @@ var EarnChain;
|
|
|
2834
2911
|
contracts: {
|
|
2835
2912
|
v1: {
|
|
2836
2913
|
wallet: GATEWAY_WALLET_EVM_TESTNET,
|
|
2837
|
-
minter: GATEWAY_MINTER_EVM_TESTNET
|
|
2914
|
+
minter: GATEWAY_MINTER_EVM_TESTNET,
|
|
2915
|
+
// DepositForHandler the GenericExecutor calls to run a fast cross-chain
|
|
2916
|
+
// deposit into the GatewayWallet above.
|
|
2917
|
+
depositForHandler: '0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48'
|
|
2838
2918
|
}
|
|
2839
2919
|
},
|
|
2840
2920
|
forwarderSupported: {
|
|
@@ -5902,7 +5982,10 @@ var Chains = /*#__PURE__*/Object.freeze({
|
|
|
5902
5982
|
minter: z.string({
|
|
5903
5983
|
required_error: 'Gateway minter address is required. Please provide a valid contract address.',
|
|
5904
5984
|
invalid_type_error: 'Gateway minter address must be a string.'
|
|
5905
|
-
}).min(1, 'Gateway minter address cannot be empty.')
|
|
5985
|
+
}).min(1, 'Gateway minter address cannot be empty.'),
|
|
5986
|
+
depositForHandler: z.string({
|
|
5987
|
+
invalid_type_error: 'Gateway depositForHandler address must be a string.'
|
|
5988
|
+
}).min(1, 'Gateway depositForHandler address cannot be empty.').optional()
|
|
5906
5989
|
}).strict() // Reject any additional properties not defined in the schema
|
|
5907
5990
|
;
|
|
5908
5991
|
/**
|
|
@@ -6365,6 +6448,39 @@ const swapTokenEnumSchema = z.enum([
|
|
|
6365
6448
|
throw new Error(`Invalid chain identifier type: ${typeof chainIdentifier}. Expected ChainDefinition object, Blockchain enum, or string literal.`);
|
|
6366
6449
|
}
|
|
6367
6450
|
|
|
6451
|
+
/**
|
|
6452
|
+
* Resolve a chain identifier to a plain chain-name string.
|
|
6453
|
+
*
|
|
6454
|
+
* Accept a string literal (`'Ethereum'`), a `ChainDefinition`-like
|
|
6455
|
+
* object (`{ chain: 'Ethereum' }`), or `null`/`undefined` and return
|
|
6456
|
+
* the chain name as a string. Return `undefined` when the value
|
|
6457
|
+
* cannot be resolved.
|
|
6458
|
+
*
|
|
6459
|
+
* @remarks
|
|
6460
|
+
* Unlike `resolveChainIdentifier` (which returns a full `ChainDefinition`
|
|
6461
|
+
* and throws on invalid input), this helper is intentionally lenient and
|
|
6462
|
+
* never throws — it is safe to call in error-handling and telemetry paths.
|
|
6463
|
+
*
|
|
6464
|
+
* @param value - A string, chain-definition object, or nullish value.
|
|
6465
|
+
* @returns The chain name string, or `undefined`.
|
|
6466
|
+
*
|
|
6467
|
+
* @example
|
|
6468
|
+
* ```typescript
|
|
6469
|
+
* import { resolveChainName } from '@core/chains'
|
|
6470
|
+
*
|
|
6471
|
+
* resolveChainName('Ethereum') // 'Ethereum'
|
|
6472
|
+
* resolveChainName({ chain: 'Ethereum' }) // 'Ethereum'
|
|
6473
|
+
* resolveChainName(undefined) // undefined
|
|
6474
|
+
* ```
|
|
6475
|
+
*/ function resolveChainName(value) {
|
|
6476
|
+
if (value == null) return undefined;
|
|
6477
|
+
if (typeof value === 'string') return value;
|
|
6478
|
+
if (typeof value === 'object' && 'chain' in value && typeof value.chain === 'string') {
|
|
6479
|
+
return value.chain;
|
|
6480
|
+
}
|
|
6481
|
+
return undefined;
|
|
6482
|
+
}
|
|
6483
|
+
|
|
6368
6484
|
/**
|
|
6369
6485
|
* Extracts chain information including name, display name, and expected address format.
|
|
6370
6486
|
*
|
|
@@ -6749,13 +6865,12 @@ const swapTokenEnumSchema = z.enum([
|
|
|
6749
6865
|
headers: {
|
|
6750
6866
|
...DEFAULT_CONFIG$1.headers,
|
|
6751
6867
|
...config.headers ?? {},
|
|
6752
|
-
//
|
|
6753
|
-
//
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
}
|
|
6868
|
+
// Browsers forbid setting a user-agent request header, and the custom
|
|
6869
|
+
// fallback header the SDK used instead trips CORS preflight against the
|
|
6870
|
+
// Circle APIs (it isn't in their `Access-Control-Allow-Headers`),
|
|
6871
|
+
// blocking the request. So send the SDK user agent only in Node;
|
|
6872
|
+
// browsers omit it entirely.
|
|
6873
|
+
...getNodeUserAgentHeader()
|
|
6759
6874
|
}
|
|
6760
6875
|
};
|
|
6761
6876
|
let lastError;
|
|
@@ -8014,6 +8129,223 @@ const swapTokenEnumSchema = z.enum([
|
|
|
8014
8129
|
return explorerUrl;
|
|
8015
8130
|
}
|
|
8016
8131
|
|
|
8132
|
+
/**
|
|
8133
|
+
* CCTP forwarding magic bytes prefix.
|
|
8134
|
+
*
|
|
8135
|
+
* The ASCII string "cctp-forward" (12 bytes) that identifies a forwarding request.
|
|
8136
|
+
* This prefix is right-padded to 24 bytes in the final hookData.
|
|
8137
|
+
*/ const CCTP_FORWARD_MAGIC_PREFIX = 'cctp-forward';
|
|
8138
|
+
|
|
8139
|
+
/**
|
|
8140
|
+
* Project an arbitrary payload onto the exact set of fields the telemetry
|
|
8141
|
+
* endpoint accepts.
|
|
8142
|
+
*
|
|
8143
|
+
* @remarks
|
|
8144
|
+
* Defense-in-depth before the last network hop: rather than
|
|
8145
|
+
* `JSON.stringify`-ing the caller's object verbatim, only the
|
|
8146
|
+
* allowlisted {@link ClientLogPayload} fields (and the allowlisted
|
|
8147
|
+
* sub-fields of `errorDetails` / `clientContext`) are copied across.
|
|
8148
|
+
* A regressing upstream mapper — or a plain-JS caller that bypasses the
|
|
8149
|
+
* type — therefore cannot exfiltrate stray properties (secrets, PII,
|
|
8150
|
+
* raw error stacks) through the analytics channel. Optional fields are
|
|
8151
|
+
* only included when present so the serialised shape matches the
|
|
8152
|
+
* server's strict schema.
|
|
8153
|
+
*
|
|
8154
|
+
* @internal
|
|
8155
|
+
*/ function toSafePayload(payload) {
|
|
8156
|
+
const clientContext = {
|
|
8157
|
+
platform: payload.clientContext.platform,
|
|
8158
|
+
os: payload.clientContext.os,
|
|
8159
|
+
runtimeName: payload.clientContext.runtimeName
|
|
8160
|
+
};
|
|
8161
|
+
const safe = {
|
|
8162
|
+
sdkName: payload.sdkName,
|
|
8163
|
+
sdkVersion: payload.sdkVersion,
|
|
8164
|
+
eventType: payload.eventType,
|
|
8165
|
+
timestamp: payload.timestamp,
|
|
8166
|
+
clientContext
|
|
8167
|
+
};
|
|
8168
|
+
if (payload.sourceChain !== undefined) safe['sourceChain'] = payload.sourceChain;
|
|
8169
|
+
if (payload.destinationChain !== undefined) safe['destinationChain'] = payload.destinationChain;
|
|
8170
|
+
if (payload.tokenIn !== undefined) safe['tokenIn'] = payload.tokenIn;
|
|
8171
|
+
if (payload.tokenOut !== undefined) safe['tokenOut'] = payload.tokenOut;
|
|
8172
|
+
if (payload.txHash !== undefined) safe['txHash'] = payload.txHash;
|
|
8173
|
+
if (payload.correlationId !== undefined) safe['correlationId'] = payload.correlationId;
|
|
8174
|
+
if (payload.errorDetails !== undefined) {
|
|
8175
|
+
const errorDetails = {
|
|
8176
|
+
...payload.errorDetails.errorCode !== undefined && {
|
|
8177
|
+
errorCode: payload.errorDetails.errorCode
|
|
8178
|
+
},
|
|
8179
|
+
...payload.errorDetails.errorType !== undefined && {
|
|
8180
|
+
errorType: payload.errorDetails.errorType
|
|
8181
|
+
}
|
|
8182
|
+
};
|
|
8183
|
+
safe['errorDetails'] = errorDetails;
|
|
8184
|
+
}
|
|
8185
|
+
return safe;
|
|
8186
|
+
}
|
|
8187
|
+
/**
|
|
8188
|
+
* Default telemetry endpoint.
|
|
8189
|
+
*
|
|
8190
|
+
* Override via the `STABLECOIN_KITS_TELEMETRY_URL` environment variable
|
|
8191
|
+
* (e.g. for staging or local development).
|
|
8192
|
+
*
|
|
8193
|
+
* @internal
|
|
8194
|
+
*/ const DEFAULT_LOGS_URL = 'https://api.circle.com/v1/stablecoinKits/logs';
|
|
8195
|
+
/**
|
|
8196
|
+
* Resolve the telemetry endpoint URL.
|
|
8197
|
+
*
|
|
8198
|
+
* @internal
|
|
8199
|
+
*/ function getLogsUrl() {
|
|
8200
|
+
if (isNodeEnvironment() && typeof process.env['STABLECOIN_KITS_TELEMETRY_URL'] === 'string' && process.env['STABLECOIN_KITS_TELEMETRY_URL'].length > 0) {
|
|
8201
|
+
return process.env['STABLECOIN_KITS_TELEMETRY_URL'];
|
|
8202
|
+
}
|
|
8203
|
+
return DEFAULT_LOGS_URL;
|
|
8204
|
+
}
|
|
8205
|
+
/**
|
|
8206
|
+
* Send a telemetry event to the proxy service.
|
|
8207
|
+
*
|
|
8208
|
+
* @remarks
|
|
8209
|
+
* Fire-and-forget: the returned promise is intentionally not awaited
|
|
8210
|
+
* by the caller. A fetch failure (network error, non-2xx, timeout)
|
|
8211
|
+
* is silently swallowed so telemetry never blocks or fails user
|
|
8212
|
+
* operations.
|
|
8213
|
+
*
|
|
8214
|
+
* @param payload - The structured log payload matching the server schema.
|
|
8215
|
+
*
|
|
8216
|
+
* @example
|
|
8217
|
+
* ```typescript
|
|
8218
|
+
* import { emitAnalyticsLog } from '@core/utils'
|
|
8219
|
+
*
|
|
8220
|
+
* // Fire-and-forget — do not await
|
|
8221
|
+
* void emitAnalyticsLog(payload)
|
|
8222
|
+
* ```
|
|
8223
|
+
*/ async function emitAnalyticsLog(payload) {
|
|
8224
|
+
// Hand-rolled timeout via `AbortController` + `setTimeout` rather than
|
|
8225
|
+
// `AbortSignal.timeout(...)` so we can `clearTimeout` the handle in a
|
|
8226
|
+
// `finally`. `AbortSignal.timeout` registers a timer that stays on the
|
|
8227
|
+
// event loop until it fires even if the fetch already settled, which
|
|
8228
|
+
// manifests as spurious `TimeoutError` unhandled rejections during
|
|
8229
|
+
// process teardown (notably between e2e test fork lifecycles). See
|
|
8230
|
+
// nodejs/node#48298 for the underlying issue.
|
|
8231
|
+
const controller = new AbortController();
|
|
8232
|
+
const timeoutHandle = setTimeout(()=>{
|
|
8233
|
+
controller.abort(new DOMException('Telemetry request timed out', 'TimeoutError'));
|
|
8234
|
+
}, 5_000);
|
|
8235
|
+
// Don't let the timer keep the Node event loop alive in short-lived
|
|
8236
|
+
// CLIs / test processes; telemetry is best-effort and must never
|
|
8237
|
+
// block clean process exit. `unref` only exists on Node's `Timeout`
|
|
8238
|
+
// object, not on the `number` returned by the browser's `setTimeout`,
|
|
8239
|
+
// so we feature-detect rather than call unconditionally.
|
|
8240
|
+
if (typeof timeoutHandle.unref === 'function') {
|
|
8241
|
+
timeoutHandle.unref();
|
|
8242
|
+
}
|
|
8243
|
+
try {
|
|
8244
|
+
await fetch(getLogsUrl(), {
|
|
8245
|
+
method: 'POST',
|
|
8246
|
+
headers: {
|
|
8247
|
+
'Content-Type': 'application/json',
|
|
8248
|
+
// Browsers forbid setting a user-agent request header, and the custom
|
|
8249
|
+
// fallback header the SDK used instead trips CORS preflight (it isn't
|
|
8250
|
+
// in the telemetry endpoint's `Access-Control-Allow-Headers`), so send
|
|
8251
|
+
// it only in Node; browsers omit it entirely.
|
|
8252
|
+
...getNodeUserAgentHeader()
|
|
8253
|
+
},
|
|
8254
|
+
body: JSON.stringify(toSafePayload(payload)),
|
|
8255
|
+
signal: controller.signal
|
|
8256
|
+
});
|
|
8257
|
+
} catch {
|
|
8258
|
+
// Silently swallow — telemetry must never break user operations.
|
|
8259
|
+
} finally{
|
|
8260
|
+
clearTimeout(timeoutHandle);
|
|
8261
|
+
}
|
|
8262
|
+
}
|
|
8263
|
+
|
|
8264
|
+
/**
|
|
8265
|
+
* Build the `clientContext` object for telemetry payloads.
|
|
8266
|
+
*
|
|
8267
|
+
* @remarks
|
|
8268
|
+
* Use the exported `getRuntime()` and `isNodeEnvironment()` from
|
|
8269
|
+
* `@core/utils` to detect the runtime environment. The returned
|
|
8270
|
+
* string is parsed into the structured `ClientContext` fields
|
|
8271
|
+
* expected by the server schema.
|
|
8272
|
+
*
|
|
8273
|
+
* @returns A {@link ClientContext} with platform, OS, and runtime name
|
|
8274
|
+
* populated from the current environment.
|
|
8275
|
+
*
|
|
8276
|
+
* @example
|
|
8277
|
+
* ```typescript
|
|
8278
|
+
* import { buildClientContext } from '@core/utils'
|
|
8279
|
+
*
|
|
8280
|
+
* const ctx = buildClientContext()
|
|
8281
|
+
* // Node: { platform: 'node', os: 'darwin', runtimeName: null }
|
|
8282
|
+
* // Browser: { platform: 'browser', os: null, runtimeName: 'chrome' }
|
|
8283
|
+
* ```
|
|
8284
|
+
*/ function buildClientContext() {
|
|
8285
|
+
const runtime = getRuntime();
|
|
8286
|
+
if (runtime.startsWith('browser/')) {
|
|
8287
|
+
return {
|
|
8288
|
+
platform: 'browser',
|
|
8289
|
+
os: null,
|
|
8290
|
+
runtimeName: runtime.slice('browser/'.length).toLowerCase()
|
|
8291
|
+
};
|
|
8292
|
+
}
|
|
8293
|
+
if (runtime.startsWith('node/')) {
|
|
8294
|
+
return {
|
|
8295
|
+
platform: 'node',
|
|
8296
|
+
os: isNodeEnvironment() ? process.platform : null,
|
|
8297
|
+
runtimeName: null
|
|
8298
|
+
};
|
|
8299
|
+
}
|
|
8300
|
+
return {
|
|
8301
|
+
platform: 'node',
|
|
8302
|
+
os: null,
|
|
8303
|
+
runtimeName: null
|
|
8304
|
+
};
|
|
8305
|
+
}
|
|
8306
|
+
|
|
8307
|
+
/**
|
|
8308
|
+
* Extract structured error details from an unknown error value.
|
|
8309
|
+
*
|
|
8310
|
+
* @remarks
|
|
8311
|
+
* Handle three cases:
|
|
8312
|
+
* - `KitError` — extract `code` and `name`.
|
|
8313
|
+
* - `Error` — extract `name`.
|
|
8314
|
+
* - Anything else — return empty details.
|
|
8315
|
+
*
|
|
8316
|
+
* Only structured, bounded fields (`errorCode`, `errorType`) are
|
|
8317
|
+
* included. Free-text fields (`message`, `stack`) are intentionally
|
|
8318
|
+
* omitted to avoid leaking secrets or PII through vendor telemetry.
|
|
8319
|
+
*
|
|
8320
|
+
* @param error - The thrown value to extract details from.
|
|
8321
|
+
* @returns A {@link ErrorDetails} object suitable for telemetry payloads.
|
|
8322
|
+
*
|
|
8323
|
+
* @example
|
|
8324
|
+
* ```typescript
|
|
8325
|
+
* import { extractErrorDetails } from '@core/utils'
|
|
8326
|
+
*
|
|
8327
|
+
* try {
|
|
8328
|
+
* await riskyOperation()
|
|
8329
|
+
* } catch (error) {
|
|
8330
|
+
* const details = extractErrorDetails(error)
|
|
8331
|
+
* // { errorCode: '1001', errorType: 'INPUT_NETWORK_MISMATCH' }
|
|
8332
|
+
* }
|
|
8333
|
+
* ```
|
|
8334
|
+
*/ function extractErrorDetails(error) {
|
|
8335
|
+
if (error instanceof KitError) {
|
|
8336
|
+
return {
|
|
8337
|
+
errorCode: String(error.code),
|
|
8338
|
+
errorType: error.name
|
|
8339
|
+
};
|
|
8340
|
+
}
|
|
8341
|
+
if (error instanceof Error) {
|
|
8342
|
+
return {
|
|
8343
|
+
errorType: error.name
|
|
8344
|
+
};
|
|
8345
|
+
}
|
|
8346
|
+
return {};
|
|
8347
|
+
}
|
|
8348
|
+
|
|
8017
8349
|
/**
|
|
8018
8350
|
* Strip the `@circle-fin/` scope from a kit package name to produce the
|
|
8019
8351
|
* short SDK name used in telemetry payloads.
|
|
@@ -8032,8 +8364,154 @@ const swapTokenEnumSchema = z.enum([
|
|
|
8032
8364
|
return pkgName.replace('@circle-fin/', '');
|
|
8033
8365
|
}
|
|
8034
8366
|
|
|
8367
|
+
/**
|
|
8368
|
+
* Soft signal for the case where building or emitting a telemetry payload
|
|
8369
|
+
* threw — for example, a buggy `TelemetryContextResolver`, a regression in
|
|
8370
|
+
* `extractErrorDetails`, or a synchronous failure inside `emitAnalyticsLog`
|
|
8371
|
+
* before it could swallow the error itself. Logged with a stable prefix so
|
|
8372
|
+
* consumers can grep for it. We deliberately do not re-throw: the caller's
|
|
8373
|
+
* original operation error must always win.
|
|
8374
|
+
*
|
|
8375
|
+
* @internal
|
|
8376
|
+
*/ function warnTelemetryDrop(eventType, cause) {
|
|
8377
|
+
try {
|
|
8378
|
+
// Pass `cause` as the second console.warn argument rather than
|
|
8379
|
+
// string-coercing it. `String(err)` (and `err.message` alone)
|
|
8380
|
+
// discards the stack trace, nested `cause`, and any custom Error
|
|
8381
|
+
// properties — exactly the context an on-call needs when a
|
|
8382
|
+
// resolver-closure regression triggers this path.
|
|
8383
|
+
console.warn(`[stablecoin-kits telemetry] dropped event '${eventType}':`, cause);
|
|
8384
|
+
} catch {
|
|
8385
|
+
// console.warn itself throwing is the user's environment; nothing more we
|
|
8386
|
+
// can do without risking the original operation error.
|
|
8387
|
+
}
|
|
8388
|
+
}
|
|
8389
|
+
/**
|
|
8390
|
+
* Build a telemetry payload from common fields.
|
|
8391
|
+
*
|
|
8392
|
+
* @internal
|
|
8393
|
+
*/ function buildPayload(config, eventType, errorDetails, context) {
|
|
8394
|
+
return {
|
|
8395
|
+
sdkName: config.sdkName,
|
|
8396
|
+
sdkVersion: config.sdkVersion,
|
|
8397
|
+
eventType,
|
|
8398
|
+
timestamp: new Date().toISOString(),
|
|
8399
|
+
...errorDetails !== undefined && {
|
|
8400
|
+
errorDetails
|
|
8401
|
+
},
|
|
8402
|
+
clientContext: buildClientContext(),
|
|
8403
|
+
...context?.sourceChain != null && {
|
|
8404
|
+
sourceChain: context.sourceChain
|
|
8405
|
+
},
|
|
8406
|
+
...context?.destinationChain != null && {
|
|
8407
|
+
destinationChain: context.destinationChain
|
|
8408
|
+
},
|
|
8409
|
+
...context?.tokenIn != null && {
|
|
8410
|
+
tokenIn: context.tokenIn
|
|
8411
|
+
},
|
|
8412
|
+
...context?.tokenOut != null && {
|
|
8413
|
+
tokenOut: context.tokenOut
|
|
8414
|
+
},
|
|
8415
|
+
...context?.txHash != null && {
|
|
8416
|
+
txHash: context.txHash
|
|
8417
|
+
},
|
|
8418
|
+
...context?.correlationId != null && {
|
|
8419
|
+
correlationId: context.correlationId
|
|
8420
|
+
}
|
|
8421
|
+
};
|
|
8422
|
+
}
|
|
8423
|
+
/**
|
|
8424
|
+
* Emit telemetry for a completed operation without affecting its caller.
|
|
8425
|
+
*
|
|
8426
|
+
* No-ops when `config.disabled` is `true`. Like {@link withErrorTelemetry},
|
|
8427
|
+
* failures while constructing or submitting the telemetry payload are reported
|
|
8428
|
+
* as a soft warning and never change a completed operation's result.
|
|
8429
|
+
*
|
|
8430
|
+
* @param eventType - The telemetry event type for the completed operation.
|
|
8431
|
+
* @param config - Per-kit SDK identity and disabled flag.
|
|
8432
|
+
* @param context - Optional chain, token, and transaction context.
|
|
8433
|
+
* @returns Nothing.
|
|
8434
|
+
* @throws Never — telemetry failures are reported as warnings.
|
|
8435
|
+
*
|
|
8436
|
+
* @example
|
|
8437
|
+
* ```typescript
|
|
8438
|
+
* import { emitSuccessTelemetry } from '@core/utils'
|
|
8439
|
+
*
|
|
8440
|
+
* emitSuccessTelemetry(
|
|
8441
|
+
* 'bridge_bridge',
|
|
8442
|
+
* { sdkName: 'bridge-kit', sdkVersion: '1.0.0', disabled: false },
|
|
8443
|
+
* { sourceChain: 'Ethereum', destinationChain: 'Base', tokenIn: 'USDC' },
|
|
8444
|
+
* )
|
|
8445
|
+
* ```
|
|
8446
|
+
*/ function emitSuccessTelemetry(eventType, config, context) {
|
|
8447
|
+
if (config.disabled) {
|
|
8448
|
+
return;
|
|
8449
|
+
}
|
|
8450
|
+
try {
|
|
8451
|
+
void emitAnalyticsLog(buildPayload(config, eventType, undefined, context));
|
|
8452
|
+
} catch (telemetryError) {
|
|
8453
|
+
warnTelemetryDrop(eventType, telemetryError);
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
/**
|
|
8457
|
+
* Wrap an async operation with error telemetry.
|
|
8458
|
+
*
|
|
8459
|
+
* Execute `fn` and, if it throws, emit an error telemetry payload
|
|
8460
|
+
* before re-throwing. No-ops when `config.disabled` is `true`.
|
|
8461
|
+
*
|
|
8462
|
+
* `context` may be a static {@link TelemetryContext} or a
|
|
8463
|
+
* {@link TelemetryContextResolver}. The resolver is invoked in the
|
|
8464
|
+
* catch branch, so it can read state — most importantly `txHash` —
|
|
8465
|
+
* that the wrapped operation set after a successful broadcast. The
|
|
8466
|
+
* resolver must close over per-call locals only; passing instance
|
|
8467
|
+
* state would break isolation between concurrent invocations.
|
|
8468
|
+
*
|
|
8469
|
+
* @param fn - The async operation to execute.
|
|
8470
|
+
* @param eventType - The telemetry event type for this operation.
|
|
8471
|
+
* @param config - Per-kit SDK identity and disabled flag.
|
|
8472
|
+
* @param context - Optional context, static or lazily resolved.
|
|
8473
|
+
* @returns The result of the operation.
|
|
8474
|
+
* @throws Re-throws any error after emitting telemetry.
|
|
8475
|
+
*
|
|
8476
|
+
* @example
|
|
8477
|
+
* ```typescript
|
|
8478
|
+
* import { withErrorTelemetry } from '@core/utils'
|
|
8479
|
+
*
|
|
8480
|
+
* let txHash: string | undefined
|
|
8481
|
+
* const result = await withErrorTelemetry(
|
|
8482
|
+
* () => provider.swap(params, h => { txHash = h }),
|
|
8483
|
+
* 'swap_swap',
|
|
8484
|
+
* { sdkName: 'swap-kit', sdkVersion: '1.0.0', disabled: false },
|
|
8485
|
+
* () => ({
|
|
8486
|
+
* sourceChain: 'Ethereum',
|
|
8487
|
+
* tokenIn: 'USDC',
|
|
8488
|
+
* tokenOut: 'EURC',
|
|
8489
|
+
* ...(txHash != null && { txHash }),
|
|
8490
|
+
* }),
|
|
8491
|
+
* )
|
|
8492
|
+
* ```
|
|
8493
|
+
*/ async function withErrorTelemetry(fn, eventType, config, context) {
|
|
8494
|
+
try {
|
|
8495
|
+
return await fn();
|
|
8496
|
+
} catch (error) {
|
|
8497
|
+
if (!config.disabled) {
|
|
8498
|
+
try {
|
|
8499
|
+
const resolved = typeof context === 'function' ? context() : context;
|
|
8500
|
+
void emitAnalyticsLog(buildPayload(config, eventType, extractErrorDetails(error), resolved));
|
|
8501
|
+
} catch (telemetryError) {
|
|
8502
|
+
// Never let telemetry emission mask the original operation error.
|
|
8503
|
+
// But surface a soft signal so silent telemetry drops are
|
|
8504
|
+
// discoverable (e.g. a regression in a resolver closure or in
|
|
8505
|
+
// `extractErrorDetails`) instead of vanishing without any trace.
|
|
8506
|
+
warnTelemetryDrop(eventType, telemetryError);
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8509
|
+
throw error;
|
|
8510
|
+
}
|
|
8511
|
+
}
|
|
8512
|
+
|
|
8035
8513
|
var name$3 = "@circle-fin/bridge-kit";
|
|
8036
|
-
var version$3 = "1.12.
|
|
8514
|
+
var version$3 = "1.12.2";
|
|
8037
8515
|
var pkg$3 = {
|
|
8038
8516
|
name: name$3,
|
|
8039
8517
|
version: version$3};
|
|
@@ -8858,6 +9336,17 @@ var TransferSpeed;
|
|
|
8858
9336
|
clock: z.any().optional()
|
|
8859
9337
|
}).passthrough();
|
|
8860
9338
|
|
|
9339
|
+
/**
|
|
9340
|
+
* The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
|
|
9341
|
+
* hookData must start with.
|
|
9342
|
+
*
|
|
9343
|
+
* Encoded with `TextEncoder` (a browser-safe Web API) rather than `Buffer.from`
|
|
9344
|
+
* so this module-level constant does not reference the Node `Buffer` global at
|
|
9345
|
+
* import time. App Kit inlines this provider into its bundle without a Buffer polyfill, and a
|
|
9346
|
+
* bare `Buffer` here crashes browser bundles (e.g. Vite) on load — even for apps
|
|
9347
|
+
* that never touch the prepaid FORWARD path. Mirrors `buildForwardingHookData`.
|
|
9348
|
+
*/ Array.from(new TextEncoder().encode(CCTP_FORWARD_MAGIC_PREFIX)).map((byte)=>byte.toString(16).padStart(2, '0')).join('');
|
|
9349
|
+
|
|
8861
9350
|
/**
|
|
8862
9351
|
* The minimum finality threshold for CCTPv2 transfers.
|
|
8863
9352
|
*
|
|
@@ -8890,7 +9379,7 @@ var TransferSpeed;
|
|
|
8890
9379
|
registerKit(`${pkg$3.name}/${pkg$3.version}`);
|
|
8891
9380
|
|
|
8892
9381
|
var name$2 = "@circle-fin/swap-kit";
|
|
8893
|
-
var version$2 = "1.
|
|
9382
|
+
var version$2 = "1.5.0";
|
|
8894
9383
|
var pkg$2 = {
|
|
8895
9384
|
name: name$2,
|
|
8896
9385
|
version: version$2};
|
|
@@ -8955,7 +9444,10 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
|
|
|
8955
9444
|
}).min(1, 'kitKey must be a non-empty string').optional(),
|
|
8956
9445
|
provider: z.string({
|
|
8957
9446
|
invalid_type_error: 'provider must be a string'
|
|
8958
|
-
}).min(1, 'provider must be a non-empty string').optional()
|
|
9447
|
+
}).min(1, 'provider must be a non-empty string').optional(),
|
|
9448
|
+
batchTransactions: z.boolean({
|
|
9449
|
+
invalid_type_error: 'batchTransactions must be a boolean'
|
|
9450
|
+
}).optional()
|
|
8959
9451
|
});
|
|
8960
9452
|
/**
|
|
8961
9453
|
* Zod schema for adapter context.
|
|
@@ -9394,7 +9886,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
9394
9886
|
/**
|
|
9395
9887
|
* Circle Stablecoin Service API Key.
|
|
9396
9888
|
* Must be a valid API key format.
|
|
9397
|
-
*/ apiKey: apiKeySchema
|
|
9889
|
+
*/ apiKey: apiKeySchema.optional()
|
|
9398
9890
|
}).superRefine(requireCrossChainQuoteToAddress);
|
|
9399
9891
|
/**
|
|
9400
9892
|
* Zod schema for validating CreateSwapRequest parameters.
|
|
@@ -9452,7 +9944,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
9452
9944
|
/**
|
|
9453
9945
|
* Circle Stablecoin Service API Key.
|
|
9454
9946
|
* Must be a valid API key format.
|
|
9455
|
-
*/ apiKey: apiKeySchema
|
|
9947
|
+
*/ apiKey: apiKeySchema.optional()
|
|
9456
9948
|
});
|
|
9457
9949
|
/**
|
|
9458
9950
|
* Zod schema for validating GetSwapStatusResponse data.
|
|
@@ -9488,7 +9980,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
9488
9980
|
toChain: z.string({
|
|
9489
9981
|
invalid_type_error: 'toChain must be a string'
|
|
9490
9982
|
}).min(1, 'toChain must be a non-empty string if provided').optional(),
|
|
9491
|
-
apiKey: apiKeySchema
|
|
9983
|
+
apiKey: apiKeySchema.optional()
|
|
9492
9984
|
});
|
|
9493
9985
|
/**
|
|
9494
9986
|
* Zod schema for validating CreateSwapResponse payloads.
|
|
@@ -9497,13 +9989,15 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
9497
9989
|
required_error: 'fee token is required',
|
|
9498
9990
|
invalid_type_error: 'fee token must be a string'
|
|
9499
9991
|
}).min(1, 'fee token must be a non-empty string'),
|
|
9500
|
-
amount: feeAmountSchema
|
|
9992
|
+
amount: feeAmountSchema,
|
|
9993
|
+
decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
|
|
9994
|
+
symbol: z.string({
|
|
9995
|
+
invalid_type_error: 'fee token symbol must be a string'
|
|
9996
|
+
}).min(1, 'fee token symbol must be a non-empty string').optional()
|
|
9501
9997
|
});
|
|
9502
9998
|
/**
|
|
9503
9999
|
* Developer fee item schema with basis field.
|
|
9504
|
-
*/ const createSwapDeveloperFeeItemSchema =
|
|
9505
|
-
token: z.string().min(1, 'fee token must be a non-empty string'),
|
|
9506
|
-
amount: feeAmountSchema,
|
|
10000
|
+
*/ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
|
|
9507
10001
|
basis: z.enum([
|
|
9508
10002
|
'inputAmount',
|
|
9509
10003
|
'estimatedAmount'
|
|
@@ -9595,7 +10089,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
9595
10089
|
addresses: z.array(z.string({
|
|
9596
10090
|
invalid_type_error: 'addresses entries must be strings'
|
|
9597
10091
|
}).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(),
|
|
9598
|
-
apiKey: apiKeySchema
|
|
10092
|
+
apiKey: apiKeySchema.optional()
|
|
9599
10093
|
});
|
|
9600
10094
|
/**
|
|
9601
10095
|
* Zod schema for validating GetTokenRatesResponse payloads.
|
|
@@ -9626,6 +10120,16 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
|
|
|
9626
10120
|
required_error: 'estimatedAmount is required',
|
|
9627
10121
|
invalid_type_error: 'estimatedAmount must be a string'
|
|
9628
10122
|
}).min(1, 'estimatedAmount must be a non-empty string'),
|
|
10123
|
+
// Per-swap join key echoed back verbatim on success telemetry. Optional so a
|
|
10124
|
+
// not-yet-upgraded service (no field) still validates during rollout. A
|
|
10125
|
+
// malformed/non-UUID value is coerced to `undefined` (no telemetry id) rather
|
|
10126
|
+
// than throwing: this is a telemetry-only field (stripped from the developer
|
|
10127
|
+
// result, never used for control flow), so it must not be able to abort the
|
|
10128
|
+
// swap via `parseCreateSwapResponse().parse()`. Mirrors the best-effort,
|
|
10129
|
+
// never-throw contract of the rest of the telemetry stack. Implemented with
|
|
10130
|
+
// `preprocess` rather than Zod's `.catch()` because static analysis misreads
|
|
10131
|
+
// `.catch` on the schema chain as an unhandled Promise (S7785).
|
|
10132
|
+
correlationId: z.preprocess((value)=>z.string().uuid().safeParse(value).success ? value : undefined, z.string().optional()),
|
|
9629
10133
|
config: createSwapRequestBaseSchema.shape.config.optional(),
|
|
9630
10134
|
fees: createSwapFeesSchema.optional(),
|
|
9631
10135
|
transaction: createSwapTransactionSchema
|
|
@@ -12184,7 +12688,7 @@ new Set(Object.values(Blockchain));
|
|
|
12184
12688
|
registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
12185
12689
|
|
|
12186
12690
|
var name$1 = "@circle-fin/earn-kit";
|
|
12187
|
-
var version$1 = "1.
|
|
12691
|
+
var version$1 = "1.4.0";
|
|
12188
12692
|
var pkg$1 = {
|
|
12189
12693
|
name: name$1,
|
|
12190
12694
|
version: version$1};
|
|
@@ -12651,7 +13155,7 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12651
13155
|
*
|
|
12652
13156
|
* @param params - Adapter, chain, token/delegate/wallet addresses, the required
|
|
12653
13157
|
* allowance for the signed payload, and a revert message for on-chain failure.
|
|
12654
|
-
* @returns The approval transaction
|
|
13158
|
+
* @returns The approval transaction result when an approval was submitted, or
|
|
12655
13159
|
* `undefined` when the existing allowance already covers `requiredAllowance`
|
|
12656
13160
|
* (or `requiredAllowance` is zero).
|
|
12657
13161
|
* @throws {@link KitError} If the `token.allowance` response is malformed.
|
|
@@ -12659,7 +13163,7 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12659
13163
|
*
|
|
12660
13164
|
* @example
|
|
12661
13165
|
* ```typescript
|
|
12662
|
-
* const
|
|
13166
|
+
* const approval = await approveAllowanceIfNeeded({
|
|
12663
13167
|
* adapter,
|
|
12664
13168
|
* chain,
|
|
12665
13169
|
* tokenAddress: usdcAddress,
|
|
@@ -12721,7 +13225,15 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12721
13225
|
maxAttempts: params.allowancePropagation?.maxAttempts ?? DEFAULT_PROPAGATION_ATTEMPTS,
|
|
12722
13226
|
delayMs: params.allowancePropagation?.delayMs ?? DEFAULT_PROPAGATION_DELAY_MS
|
|
12723
13227
|
});
|
|
12724
|
-
return
|
|
13228
|
+
return {
|
|
13229
|
+
txHash: approvalTxHash,
|
|
13230
|
+
...approvalReceipt.gasUsed !== undefined && {
|
|
13231
|
+
gasUsed: approvalReceipt.gasUsed
|
|
13232
|
+
},
|
|
13233
|
+
...approvalReceipt.effectiveGasPrice !== undefined && {
|
|
13234
|
+
effectiveGasPrice: approvalReceipt.effectiveGasPrice
|
|
13235
|
+
}
|
|
13236
|
+
};
|
|
12725
13237
|
}
|
|
12726
13238
|
|
|
12727
13239
|
/** @internal */ function isSameAddress(actual, expected) {
|
|
@@ -12869,7 +13381,13 @@ const GAS_SAFETY_MULTIPLIER_DENOMINATOR = 10n;
|
|
|
12869
13381
|
}
|
|
12870
13382
|
return {
|
|
12871
13383
|
txHash,
|
|
12872
|
-
explorerUrl
|
|
13384
|
+
explorerUrl,
|
|
13385
|
+
...receipt.gasUsed !== undefined && {
|
|
13386
|
+
gasUsed: receipt.gasUsed
|
|
13387
|
+
},
|
|
13388
|
+
...receipt.effectiveGasPrice !== undefined && {
|
|
13389
|
+
effectiveGasPrice: receipt.effectiveGasPrice
|
|
13390
|
+
}
|
|
12873
13391
|
};
|
|
12874
13392
|
}
|
|
12875
13393
|
|
|
@@ -13181,112 +13699,6 @@ const EARN_OPERATIONS = new Set([
|
|
|
13181
13699
|
return hasEarnServiceParamsShape(operation, candidate['params']);
|
|
13182
13700
|
}
|
|
13183
13701
|
|
|
13184
|
-
function buildGasFeeBase(name, chain) {
|
|
13185
|
-
return {
|
|
13186
|
-
name,
|
|
13187
|
-
token: chain.nativeCurrency.symbol,
|
|
13188
|
-
blockchain: chain.chain
|
|
13189
|
-
};
|
|
13190
|
-
}
|
|
13191
|
-
function buildGasFeeSuccess(name, chain, fees) {
|
|
13192
|
-
return {
|
|
13193
|
-
...buildGasFeeBase(name, chain),
|
|
13194
|
-
fees
|
|
13195
|
-
};
|
|
13196
|
-
}
|
|
13197
|
-
function buildGasFeeFailure(name, chain, error) {
|
|
13198
|
-
return {
|
|
13199
|
-
...buildGasFeeBase(name, chain),
|
|
13200
|
-
fees: null,
|
|
13201
|
-
error: getErrorMessage(error)
|
|
13202
|
-
};
|
|
13203
|
-
}
|
|
13204
|
-
async function estimatePreparedGasFee(name, chain, prepared) {
|
|
13205
|
-
try {
|
|
13206
|
-
const estimate = bufferEstimatedGas(await prepared.estimate());
|
|
13207
|
-
if (estimate.gas <= 0n) {
|
|
13208
|
-
throw createValidationFailedError('estimate.gas', estimate.gas.toString(), 'gas estimate must be greater than zero');
|
|
13209
|
-
}
|
|
13210
|
-
return buildGasFeeSuccess(name, chain, estimate);
|
|
13211
|
-
} catch (error) {
|
|
13212
|
-
return buildGasFeeFailure(name, chain, error);
|
|
13213
|
-
}
|
|
13214
|
-
}
|
|
13215
|
-
async function estimateApprovalGasFeeIfNeeded(params) {
|
|
13216
|
-
const { adapter, chain, address, tokenAddress, delegate, requiredAllowance } = params;
|
|
13217
|
-
if (requiredAllowance <= 0n) {
|
|
13218
|
-
return undefined;
|
|
13219
|
-
}
|
|
13220
|
-
try {
|
|
13221
|
-
const allowancePrepared = await adapter.prepareAction('token.allowance', {
|
|
13222
|
-
tokenAddress,
|
|
13223
|
-
delegate
|
|
13224
|
-
}, {
|
|
13225
|
-
chain,
|
|
13226
|
-
address
|
|
13227
|
-
});
|
|
13228
|
-
const allowanceRaw = await allowancePrepared.execute();
|
|
13229
|
-
const currentAllowance = parseAllowanceResponse(allowanceRaw);
|
|
13230
|
-
if (currentAllowance >= requiredAllowance) {
|
|
13231
|
-
return undefined;
|
|
13232
|
-
}
|
|
13233
|
-
// Reuse the execute path's approval builder so the estimate simulates the
|
|
13234
|
-
// exact approval (action, amount, and WARM_SLOT_RESIDUAL) that
|
|
13235
|
-
// approveAllowanceIfNeeded later submits.
|
|
13236
|
-
const approvalPrepared = await prepareApprovalAction({
|
|
13237
|
-
adapter,
|
|
13238
|
-
chain,
|
|
13239
|
-
address,
|
|
13240
|
-
tokenAddress,
|
|
13241
|
-
delegate,
|
|
13242
|
-
currentAllowance,
|
|
13243
|
-
requiredAllowance
|
|
13244
|
-
});
|
|
13245
|
-
return await estimatePreparedGasFee('Approve', chain, approvalPrepared);
|
|
13246
|
-
} catch (error) {
|
|
13247
|
-
return buildGasFeeFailure('Approve', chain, error);
|
|
13248
|
-
}
|
|
13249
|
-
}
|
|
13250
|
-
/**
|
|
13251
|
-
* Estimate gas fee entries for an earn quote without submitting transactions.
|
|
13252
|
-
*
|
|
13253
|
-
* Each entry is produced by simulating the prepared transaction against
|
|
13254
|
-
* current chain state. When an approval is required (allowance below the
|
|
13255
|
-
* signed payload's required amount), the subsequent action simulation runs
|
|
13256
|
-
* without that approval in place and is expected to revert — the action entry
|
|
13257
|
-
* then carries `fees: null` with the revert message while the approval entry
|
|
13258
|
-
* still estimates normally. Quote consumers must treat that as "estimate
|
|
13259
|
-
* pending approval", not a hard failure.
|
|
13260
|
-
*
|
|
13261
|
-
* @internal
|
|
13262
|
-
*/ async function estimateEarnQuoteGasFees(params) {
|
|
13263
|
-
const { adapter, chain, address, actionName, actionKey, actionParams, approval } = params;
|
|
13264
|
-
const gasFees = [];
|
|
13265
|
-
if (approval !== undefined) {
|
|
13266
|
-
const approvalEstimate = await estimateApprovalGasFeeIfNeeded({
|
|
13267
|
-
adapter,
|
|
13268
|
-
chain,
|
|
13269
|
-
address,
|
|
13270
|
-
tokenAddress: approval.token,
|
|
13271
|
-
delegate: approval.delegate,
|
|
13272
|
-
requiredAllowance: approval.requiredAllowance
|
|
13273
|
-
});
|
|
13274
|
-
if (approvalEstimate !== undefined) {
|
|
13275
|
-
gasFees.push(approvalEstimate);
|
|
13276
|
-
}
|
|
13277
|
-
}
|
|
13278
|
-
try {
|
|
13279
|
-
const actionPrepared = await adapter.prepareAction(actionKey, actionParams, {
|
|
13280
|
-
chain,
|
|
13281
|
-
address
|
|
13282
|
-
});
|
|
13283
|
-
gasFees.push(await estimatePreparedGasFee(actionName, chain, actionPrepared));
|
|
13284
|
-
} catch (error) {
|
|
13285
|
-
gasFees.push(buildGasFeeFailure(actionName, chain, error));
|
|
13286
|
-
}
|
|
13287
|
-
return gasFees;
|
|
13288
|
-
}
|
|
13289
|
-
|
|
13290
13702
|
// ---------------------------------------------------------------------------
|
|
13291
13703
|
// Shared primitives
|
|
13292
13704
|
// ---------------------------------------------------------------------------
|
|
@@ -13364,7 +13776,11 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
13364
13776
|
asset: z.string(),
|
|
13365
13777
|
assetAddress: z.string(),
|
|
13366
13778
|
lltv: z.number(),
|
|
13367
|
-
supplyUsd: z.number()
|
|
13779
|
+
supplyUsd: z.number(),
|
|
13780
|
+
// Optional during the expand/contract window (a backend that predates the
|
|
13781
|
+
// field omits the key), mirroring the `.optional()` facets on the base
|
|
13782
|
+
// schema; `null` when the product exposes no per-market allocation (V2).
|
|
13783
|
+
allocationPct: z.number().nullable().optional()
|
|
13368
13784
|
});
|
|
13369
13785
|
/**
|
|
13370
13786
|
* Zod schema for a Morpho vault warning in the API response.
|
|
@@ -13378,15 +13794,82 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
13378
13794
|
])
|
|
13379
13795
|
});
|
|
13380
13796
|
/**
|
|
13381
|
-
* Zod schema for
|
|
13797
|
+
* Zod schema for the manager (curator) facet in the API response.
|
|
13382
13798
|
*
|
|
13383
13799
|
* @internal
|
|
13384
|
-
*/ const
|
|
13385
|
-
vaultAddress: z.string(),
|
|
13386
|
-
chain: z.string(),
|
|
13800
|
+
*/ const managerSchema = z.object({
|
|
13387
13801
|
name: z.string(),
|
|
13388
|
-
|
|
13389
|
-
|
|
13802
|
+
address: z.string().optional(),
|
|
13803
|
+
// Only 'curator' is emitted today (Morpho V1/V2). Additional manager roles
|
|
13804
|
+
// are added here as the providers that emit them land, rather than shipped
|
|
13805
|
+
// speculatively.
|
|
13806
|
+
type: z.enum([
|
|
13807
|
+
'curator'
|
|
13808
|
+
])
|
|
13809
|
+
});
|
|
13810
|
+
/**
|
|
13811
|
+
* Zod schema for the APY profile facet in the API response.
|
|
13812
|
+
*
|
|
13813
|
+
* @internal
|
|
13814
|
+
*/ const apyProfileSchema = z.object({
|
|
13815
|
+
current: z.number(),
|
|
13816
|
+
native: z.number().nullable(),
|
|
13817
|
+
d7: z.number().nullable(),
|
|
13818
|
+
d30: z.number().nullable(),
|
|
13819
|
+
d90: z.number().nullable(),
|
|
13820
|
+
rewardShare: z.number().nullable(),
|
|
13821
|
+
source: z.string().optional(),
|
|
13822
|
+
asOf: z.string().optional()
|
|
13823
|
+
});
|
|
13824
|
+
/**
|
|
13825
|
+
* Zod schema for the fee split facet in the API response.
|
|
13826
|
+
*
|
|
13827
|
+
* @internal
|
|
13828
|
+
*/ const feeInfoSchema = z.object({
|
|
13829
|
+
performance: z.number().nullable(),
|
|
13830
|
+
management: z.number().nullable()
|
|
13831
|
+
});
|
|
13832
|
+
/**
|
|
13833
|
+
* Zod schema for the liquidity profile facet in the API response.
|
|
13834
|
+
*
|
|
13835
|
+
* `totalSupply` is the outstanding vault share tokens (ERC4626 totalSupply);
|
|
13836
|
+
* it is validated as a raw JSON amount, like `totalDeposits`/`available`.
|
|
13837
|
+
*
|
|
13838
|
+
* @internal
|
|
13839
|
+
*/ const liquidityProfileSchema = z.object({
|
|
13840
|
+
totalDeposits: amountJsonSchema,
|
|
13841
|
+
available: amountJsonSchema,
|
|
13842
|
+
totalSupply: amountJsonSchema,
|
|
13843
|
+
status: z.enum([
|
|
13844
|
+
'active',
|
|
13845
|
+
'low_liquidity'
|
|
13846
|
+
])
|
|
13847
|
+
});
|
|
13848
|
+
/**
|
|
13849
|
+
* Zod schema for the risk signals facet in the API response.
|
|
13850
|
+
*
|
|
13851
|
+
* @internal
|
|
13852
|
+
*/ const riskSignalsSchema = z.object({
|
|
13853
|
+
circleSentinel: z.boolean(),
|
|
13854
|
+
warnings: z.array(vaultWarningSchema).optional(),
|
|
13855
|
+
earnKitWarnings: z.array(z.string()).optional()
|
|
13856
|
+
});
|
|
13857
|
+
/**
|
|
13858
|
+
* Zod schema for the universal earn-opportunity base in the API response.
|
|
13859
|
+
*
|
|
13860
|
+
* Retains every existing deprecated flat field (kept validated through the
|
|
13861
|
+
* expand/contract window so default-strip does not drop them) and adds the
|
|
13862
|
+
* new nested facets. The nested facets are `.optional()` during the
|
|
13863
|
+
* transition so the SDK still validates against a not-yet-fully-deployed
|
|
13864
|
+
* backend; they become required after Expand ships.
|
|
13865
|
+
*
|
|
13866
|
+
* @internal
|
|
13867
|
+
*/ const vaultInfoResponseSchema = z.object({
|
|
13868
|
+
vaultAddress: z.string(),
|
|
13869
|
+
chain: z.string(),
|
|
13870
|
+
name: z.string(),
|
|
13871
|
+
protocol: z.string(),
|
|
13872
|
+
asset: z.string(),
|
|
13390
13873
|
assetAddress: z.string(),
|
|
13391
13874
|
currentApy: z.number(),
|
|
13392
13875
|
nativeApy: z.number(),
|
|
@@ -13403,6 +13886,96 @@ const bridgeFeeTokenSchema = hexAddressSchema;
|
|
|
13403
13886
|
warnings: z.array(vaultWarningSchema).optional(),
|
|
13404
13887
|
earnKitWarnings: z.array(z.string()).optional()
|
|
13405
13888
|
});
|
|
13889
|
+
/**
|
|
13890
|
+
* Shared base schema: existing flat fields (kept) plus the new nested
|
|
13891
|
+
* facets and neutral identity. Facets are `.optional()` during the
|
|
13892
|
+
* transition; flip to required once the backend is confirmed emitting.
|
|
13893
|
+
*
|
|
13894
|
+
* @internal
|
|
13895
|
+
*/ const earnBaseSchema = vaultInfoResponseSchema.extend({
|
|
13896
|
+
address: z.string().optional(),
|
|
13897
|
+
asOf: z.string().optional(),
|
|
13898
|
+
manager: managerSchema.nullable().optional(),
|
|
13899
|
+
apyProfile: apyProfileSchema.optional(),
|
|
13900
|
+
fee: feeInfoSchema.optional(),
|
|
13901
|
+
liquidityProfile: liquidityProfileSchema.optional(),
|
|
13902
|
+
riskSignals: riskSignalsSchema.optional()
|
|
13903
|
+
});
|
|
13904
|
+
/**
|
|
13905
|
+
* Zod schema for the `vault` opportunity variant.
|
|
13906
|
+
*
|
|
13907
|
+
* @internal
|
|
13908
|
+
*/ const vaultOpportunitySchema = earnBaseSchema.extend({
|
|
13909
|
+
productType: z.literal('vault'),
|
|
13910
|
+
collateral: z.array(collateralSchema)
|
|
13911
|
+
});
|
|
13912
|
+
/**
|
|
13913
|
+
* Discriminated union over `productType`. Add union members here as new
|
|
13914
|
+
* product types (e.g. `lending_market`, `rwa_token`) land.
|
|
13915
|
+
*
|
|
13916
|
+
* @internal
|
|
13917
|
+
*/ const earnOpportunityVariants = [
|
|
13918
|
+
vaultOpportunitySchema
|
|
13919
|
+
];
|
|
13920
|
+
/** @internal */ const earnOpportunitySchema = z.discriminatedUnion('productType', earnOpportunityVariants);
|
|
13921
|
+
/** Product types this SDK version knows how to parse. */ const knownProductTypes = new Set(earnOpportunityVariants.map((variant)=>variant.shape.productType.value));
|
|
13922
|
+
/**
|
|
13923
|
+
* Tolerant list parser for earn opportunities.
|
|
13924
|
+
*
|
|
13925
|
+
* `z.discriminatedUnion` throws on an unrecognized discriminant and
|
|
13926
|
+
* `z.array` fails the whole array if any element fails. Two migration-window
|
|
13927
|
+
* cases are smoothed over here so neither breaks an already-shipped SDK:
|
|
13928
|
+
*
|
|
13929
|
+
* - A backend that predates `productType` omits it entirely. `'vault'` was the
|
|
13930
|
+
* only opportunity type then, so default a missing discriminant to `'vault'`
|
|
13931
|
+
* rather than dropping every vault the backend returns.
|
|
13932
|
+
* - A future backend adds a *second* `productType` this SDK version does not
|
|
13933
|
+
* know. Drop those elements (a present-but-unrecognized discriminant) instead
|
|
13934
|
+
* of rejecting the whole list.
|
|
13935
|
+
*
|
|
13936
|
+
* Only the drop above is a *tolerant* case. Anything that is not a plain object
|
|
13937
|
+
* with a present-but-unknown string `productType` — `null`, `undefined`,
|
|
13938
|
+
* primitives, or an object whose `productType` is malformed — is passed through
|
|
13939
|
+
* untouched so `z.array(earnOpportunitySchema)` reports it as a normal
|
|
13940
|
+
* validation failure. It is deliberately not silently dropped (which would hide
|
|
13941
|
+
* malformed backend data) and never throws here (an unguarded property read on
|
|
13942
|
+
* a non-object would escape `safeParse` as a raw `TypeError` instead of a
|
|
13943
|
+
* `ZodError`).
|
|
13944
|
+
*
|
|
13945
|
+
* @internal
|
|
13946
|
+
*/ const earnOpportunityListSchema = z.preprocess((raw)=>{
|
|
13947
|
+
if (!Array.isArray(raw)) {
|
|
13948
|
+
return raw;
|
|
13949
|
+
}
|
|
13950
|
+
// Array.isArray narrows `raw` to `any[]`; view it as `unknown[]` so the
|
|
13951
|
+
// map/filter chain stays type-safe and no `any` leaks into the return.
|
|
13952
|
+
const entries = raw;
|
|
13953
|
+
return entries.map((entry)=>{
|
|
13954
|
+
// Only touch plain objects; non-objects fall through to fail validation.
|
|
13955
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
13956
|
+
return entry;
|
|
13957
|
+
}
|
|
13958
|
+
const record = entry;
|
|
13959
|
+
// Older backend predating productType: default to the only type then.
|
|
13960
|
+
return record.productType === undefined ? {
|
|
13961
|
+
...record,
|
|
13962
|
+
productType: 'vault'
|
|
13963
|
+
} : record;
|
|
13964
|
+
}).filter((entry)=>{
|
|
13965
|
+
// Drop ONLY a present-but-unknown string discriminant (a future
|
|
13966
|
+
// productType this SDK version doesn't know). Everything else —
|
|
13967
|
+
// non-objects, a non-string productType — flows through to
|
|
13968
|
+
// z.array(earnOpportunitySchema) and fails/passes validation normally.
|
|
13969
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
13970
|
+
return true;
|
|
13971
|
+
}
|
|
13972
|
+
const productType = entry.productType;
|
|
13973
|
+
if (typeof productType !== 'string') {
|
|
13974
|
+
return true;
|
|
13975
|
+
}
|
|
13976
|
+
return knownProductTypes.has(productType);
|
|
13977
|
+
});
|
|
13978
|
+
}, z.array(earnOpportunitySchema));
|
|
13406
13979
|
// ---------------------------------------------------------------------------
|
|
13407
13980
|
// Position response schema
|
|
13408
13981
|
// ---------------------------------------------------------------------------
|
|
@@ -13532,6 +14105,7 @@ const positionPnlSchema = z.discriminatedUnion('status', [
|
|
|
13532
14105
|
*
|
|
13533
14106
|
* @internal
|
|
13534
14107
|
*/ const depositPayloadSchema = z.object({
|
|
14108
|
+
execId: bridgeDepositExecIdSchema,
|
|
13535
14109
|
executionParams: depositExecutionParamsSchema,
|
|
13536
14110
|
signature: hexSignatureSchema
|
|
13537
14111
|
});
|
|
@@ -13623,6 +14197,21 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13623
14197
|
amount: amountJsonSchema,
|
|
13624
14198
|
vaultAddress: hexAddressSchema
|
|
13625
14199
|
}).passthrough();
|
|
14200
|
+
/** @internal */ const bridgeQuoteExpirySchema = z.discriminatedUnion('mode', [
|
|
14201
|
+
z.object({
|
|
14202
|
+
mode: z.literal('TIMESTAMP'),
|
|
14203
|
+
expiresAt: z.string().datetime({
|
|
14204
|
+
offset: true
|
|
14205
|
+
})
|
|
14206
|
+
}),
|
|
14207
|
+
z.object({
|
|
14208
|
+
mode: z.literal('BLOCK_NUMBER'),
|
|
14209
|
+
expiresAtBlock: z.number().int(),
|
|
14210
|
+
blockEstimatedAt: z.string().datetime({
|
|
14211
|
+
offset: true
|
|
14212
|
+
}).optional()
|
|
14213
|
+
})
|
|
14214
|
+
]).optional().catch(undefined);
|
|
13626
14215
|
/**
|
|
13627
14216
|
* Zod schema for the bridge deposit prepare payload.
|
|
13628
14217
|
*
|
|
@@ -13634,6 +14223,10 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13634
14223
|
execId: bridgeDepositExecIdSchema,
|
|
13635
14224
|
erc3009TypedData: bridgeDepositPreparedBundleSchema,
|
|
13636
14225
|
expiresAt: z.string().datetime(),
|
|
14226
|
+
quoteIssuedAt: z.string().datetime({
|
|
14227
|
+
offset: true
|
|
14228
|
+
}).optional().catch(undefined),
|
|
14229
|
+
quoteExpiry: bridgeQuoteExpirySchema,
|
|
13637
14230
|
review: bridgeDepositPrepareReviewSchema
|
|
13638
14231
|
});
|
|
13639
14232
|
/**
|
|
@@ -13699,6 +14292,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13699
14292
|
*
|
|
13700
14293
|
* @internal
|
|
13701
14294
|
*/ const withdrawPayloadSchema = z.object({
|
|
14295
|
+
execId: bridgeDepositExecIdSchema,
|
|
13702
14296
|
executionParams: withdrawExecutionParamsSchema,
|
|
13703
14297
|
signature: hexSignatureSchema
|
|
13704
14298
|
});
|
|
@@ -13712,6 +14306,27 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13712
14306
|
data: withdrawPayloadSchema
|
|
13713
14307
|
});
|
|
13714
14308
|
// ---------------------------------------------------------------------------
|
|
14309
|
+
// Transaction report response schema
|
|
14310
|
+
// ---------------------------------------------------------------------------
|
|
14311
|
+
/**
|
|
14312
|
+
* Zod schema for the transaction report payload inside the API `data` envelope.
|
|
14313
|
+
*
|
|
14314
|
+
* The Earn Service returns an empty payload (`{"data":{}}`) on success, so the
|
|
14315
|
+
* schema accepts any object shape and does not require specific fields.
|
|
14316
|
+
*
|
|
14317
|
+
* @internal
|
|
14318
|
+
*/ const transactionReportPayloadSchema = z.object({}).passthrough();
|
|
14319
|
+
/**
|
|
14320
|
+
* Zod schema for the `POST /v1/earnKit/transactions/report` API response.
|
|
14321
|
+
*
|
|
14322
|
+
* The Earn Service API wraps the transaction report payload in a `data`
|
|
14323
|
+
* envelope.
|
|
14324
|
+
*
|
|
14325
|
+
* @internal
|
|
14326
|
+
*/ const transactionReportResponseSchema = z.object({
|
|
14327
|
+
data: transactionReportPayloadSchema
|
|
14328
|
+
});
|
|
14329
|
+
// ---------------------------------------------------------------------------
|
|
13715
14330
|
// Claim rewards response schema
|
|
13716
14331
|
// ---------------------------------------------------------------------------
|
|
13717
14332
|
/**
|
|
@@ -13772,6 +14387,30 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13772
14387
|
token: z.string(),
|
|
13773
14388
|
amount: amountJsonSchema
|
|
13774
14389
|
});
|
|
14390
|
+
/**
|
|
14391
|
+
* Zod schema for a native gas-fee entry in an EarnKit quote response.
|
|
14392
|
+
*
|
|
14393
|
+
* The Earn Service backend estimates gas server-side and returns one entry per
|
|
14394
|
+
* action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
|
|
14395
|
+
* `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
|
|
14396
|
+
* integer string in the chain's native base units. When the backend cannot
|
|
14397
|
+
* estimate an action it returns `fees: null` with an `error` message instead.
|
|
14398
|
+
*
|
|
14399
|
+
* The schema deliberately validates almost nothing beyond the envelope: `name`
|
|
14400
|
+
* is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
|
|
14401
|
+
* of `fees` — that it is an object at all, and that `gas`, `gasPrice`, and
|
|
14402
|
+
* `fee` are parseable integer strings — is deferred to {@link toQuoteGasFees},
|
|
14403
|
+
* which degrades a malformed entry to a `fees: null` soft failure. This is
|
|
14404
|
+
* intentional: gas is best-effort, so a single unparseable gas entry (a wrong
|
|
14405
|
+
* type such as `fees: 123` or `fees: 'bad'`, a missing field, or a non-numeric
|
|
14406
|
+
* `fee`) must never fail Zod validation and reject the entire quote.
|
|
14407
|
+
*
|
|
14408
|
+
* @internal
|
|
14409
|
+
*/ const quoteGasFeeSchema = z.object({
|
|
14410
|
+
name: z.string().optional(),
|
|
14411
|
+
fees: z.unknown(),
|
|
14412
|
+
error: z.string().optional()
|
|
14413
|
+
}).passthrough();
|
|
13775
14414
|
/**
|
|
13776
14415
|
* Zod schema for the inner deposit quote payload.
|
|
13777
14416
|
*
|
|
@@ -13787,7 +14426,8 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13787
14426
|
expectedShares: amountJsonSchema,
|
|
13788
14427
|
sharePrice: z.string(),
|
|
13789
14428
|
currentApy: z.number(),
|
|
13790
|
-
fees: z.array(feeSchema).optional()
|
|
14429
|
+
fees: z.array(feeSchema).optional(),
|
|
14430
|
+
gasFees: z.array(quoteGasFeeSchema).optional()
|
|
13791
14431
|
});
|
|
13792
14432
|
/**
|
|
13793
14433
|
* Zod schema for the `POST /v1/earnKit/deposit/quote` API response.
|
|
@@ -13814,6 +14454,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13814
14454
|
sharePrice: z.string(),
|
|
13815
14455
|
maxWithdrawable: amountJsonSchema,
|
|
13816
14456
|
fees: z.array(feeSchema),
|
|
14457
|
+
gasFees: z.array(quoteGasFeeSchema).optional(),
|
|
13817
14458
|
warnings: z.array(z.string()).optional()
|
|
13818
14459
|
});
|
|
13819
14460
|
/**
|
|
@@ -13871,7 +14512,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13871
14512
|
*
|
|
13872
14513
|
* @internal
|
|
13873
14514
|
*/ const getVaultsPayloadSchema = z.object({
|
|
13874
|
-
vaults:
|
|
14515
|
+
vaults: earnOpportunityListSchema,
|
|
13875
14516
|
errors: z.array(vaultErrorSchema)
|
|
13876
14517
|
});
|
|
13877
14518
|
/**
|
|
@@ -13901,7 +14542,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13901
14542
|
*
|
|
13902
14543
|
* @internal
|
|
13903
14544
|
*/ const exploreVaultsPayloadSchema = z.object({
|
|
13904
|
-
vaults:
|
|
14545
|
+
vaults: earnOpportunityListSchema,
|
|
13905
14546
|
pagination: explorePaginationSchema
|
|
13906
14547
|
});
|
|
13907
14548
|
/**
|
|
@@ -13994,6 +14635,16 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
13994
14635
|
*/ function isWithdrawResponse(value) {
|
|
13995
14636
|
return withdrawResponseSchema.safeParse(value).success;
|
|
13996
14637
|
}
|
|
14638
|
+
/**
|
|
14639
|
+
* Type guard for the transaction report API response.
|
|
14640
|
+
*
|
|
14641
|
+
* @param value - Unknown response value to validate
|
|
14642
|
+
* @returns True when the value matches the transaction report response shape
|
|
14643
|
+
*
|
|
14644
|
+
* @internal
|
|
14645
|
+
*/ function isTransactionReportResponse(value) {
|
|
14646
|
+
return transactionReportResponseSchema.safeParse(value).success;
|
|
14647
|
+
}
|
|
13997
14648
|
/**
|
|
13998
14649
|
* Type guard for the claim rewards API response.
|
|
13999
14650
|
*
|
|
@@ -14036,7 +14687,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
|
|
|
14036
14687
|
}
|
|
14037
14688
|
|
|
14038
14689
|
var name = "@circle-fin/provider-earn-service";
|
|
14039
|
-
var version = "1.
|
|
14690
|
+
var version = "1.3.1";
|
|
14040
14691
|
var pkg = {
|
|
14041
14692
|
name: name,
|
|
14042
14693
|
version: version};
|
|
@@ -14098,15 +14749,25 @@ var pkg = {
|
|
|
14098
14749
|
*
|
|
14099
14750
|
* @internal
|
|
14100
14751
|
*/ function buildConfig(serviceConfig) {
|
|
14752
|
+
// The kit key is a server-only secret. Reject it in the browser so it cannot
|
|
14753
|
+
// leak into a client bundle (no-op in Node.js). Keyless usage stays allowed.
|
|
14754
|
+
if (serviceConfig?.kitKey !== undefined && isBrowserEnvironment()) {
|
|
14755
|
+
throw createValidationFailedError('kitKey', '[redacted]', 'kitKey must not be provided in a browser environment — it is a server-only secret. Run EarnKit operations that use a kit key on your server, or omit kitKey to use the permissionless (keyless) client path');
|
|
14756
|
+
}
|
|
14101
14757
|
const baseUrl = serviceConfig?.baseUrl ?? EARN_SERVICE_BASE_URL;
|
|
14102
|
-
|
|
14758
|
+
// The API CORS policy does not allow this custom header. Keep the existing
|
|
14759
|
+
// per-request version attribution for Node callers, but omit it in browsers
|
|
14760
|
+
// so public EarnKit endpoints do not fail at CORS preflight.
|
|
14761
|
+
const sdkVersionHeader = isNodeEnvironment() ? {
|
|
14762
|
+
[SDK_VERSION_HEADER]: resolveSdkVersionHeader()
|
|
14763
|
+
} : {};
|
|
14103
14764
|
if (serviceConfig?.kitKey === undefined) {
|
|
14104
14765
|
return {
|
|
14105
14766
|
pollingConfig: {
|
|
14106
14767
|
...DEFAULT_CONFIG,
|
|
14107
14768
|
headers: {
|
|
14108
14769
|
...DEFAULT_CONFIG.headers,
|
|
14109
|
-
|
|
14770
|
+
...sdkVersionHeader
|
|
14110
14771
|
}
|
|
14111
14772
|
},
|
|
14112
14773
|
baseUrl
|
|
@@ -14124,7 +14785,7 @@ var pkg = {
|
|
|
14124
14785
|
...DEFAULT_CONFIG,
|
|
14125
14786
|
headers: {
|
|
14126
14787
|
...DEFAULT_CONFIG.headers,
|
|
14127
|
-
|
|
14788
|
+
...sdkVersionHeader,
|
|
14128
14789
|
Authorization: `Bearer ${serviceConfig.kitKey}`
|
|
14129
14790
|
}
|
|
14130
14791
|
},
|
|
@@ -14156,7 +14817,7 @@ var pkg = {
|
|
|
14156
14817
|
}
|
|
14157
14818
|
|
|
14158
14819
|
/**
|
|
14159
|
-
* Convert an API vault info object into the SDK {@link
|
|
14820
|
+
* Convert an API vault info object into the SDK {@link EarnOpportunity} shape.
|
|
14160
14821
|
*
|
|
14161
14822
|
* Map the API chain code back to the SDK chain identifier and hydrate the
|
|
14162
14823
|
* amount payloads into {@link Amount} instances.
|
|
@@ -14167,16 +14828,29 @@ var pkg = {
|
|
|
14167
14828
|
*
|
|
14168
14829
|
* @internal
|
|
14169
14830
|
*/ function toVaultInfo(data) {
|
|
14170
|
-
const { totalDeposits, liquidity, ...vault } = data;
|
|
14831
|
+
const { totalDeposits, liquidity, liquidityProfile, ...vault } = data;
|
|
14171
14832
|
const chain = toSdkChain(vault.chain);
|
|
14172
14833
|
if (chain === undefined) {
|
|
14173
14834
|
throw createInvalidChainError(vault.chain, 'Chain returned by the Earn Service is not supported by the SDK');
|
|
14174
14835
|
}
|
|
14836
|
+
// The nested facets are `.optional()` in the schema (a backend that predates
|
|
14837
|
+
// them omits them) and are typed optional on `EarnOpportunity` to match.
|
|
14838
|
+
// Convert the nested liquidity amounts when present and pass the remaining
|
|
14839
|
+
// facets straight through; each absent facet stays absent rather than being
|
|
14840
|
+
// asserted present by a cast.
|
|
14175
14841
|
return {
|
|
14176
14842
|
...vault,
|
|
14177
14843
|
chain,
|
|
14178
14844
|
totalDeposits: Amount.fromJSON(totalDeposits),
|
|
14179
|
-
liquidity: Amount.fromJSON(liquidity)
|
|
14845
|
+
liquidity: Amount.fromJSON(liquidity),
|
|
14846
|
+
...liquidityProfile !== undefined && {
|
|
14847
|
+
liquidityProfile: {
|
|
14848
|
+
...liquidityProfile,
|
|
14849
|
+
totalDeposits: Amount.fromJSON(liquidityProfile.totalDeposits),
|
|
14850
|
+
available: Amount.fromJSON(liquidityProfile.available),
|
|
14851
|
+
totalSupply: Amount.fromJSON(liquidityProfile.totalSupply)
|
|
14852
|
+
}
|
|
14853
|
+
}
|
|
14180
14854
|
};
|
|
14181
14855
|
}
|
|
14182
14856
|
|
|
@@ -14211,8 +14885,11 @@ function toVaultError(error) {
|
|
|
14211
14885
|
}
|
|
14212
14886
|
try {
|
|
14213
14887
|
const response = await pollApiGet(url.toString(), isGetVaultsResponse, pollingConfig);
|
|
14888
|
+
// `pollApiGet` validates via a boolean guard and returns the raw JSON — it
|
|
14889
|
+
// does not run the schema's preprocess. Parse explicitly so unknown
|
|
14890
|
+
// `productType` values are dropped before `toVaultInfo`.
|
|
14214
14891
|
return {
|
|
14215
|
-
vaults: response.data.vaults.map(toVaultInfo),
|
|
14892
|
+
vaults: earnOpportunityListSchema.parse(response.data.vaults).map(toVaultInfo),
|
|
14216
14893
|
errors: response.data.errors.map(toVaultError)
|
|
14217
14894
|
};
|
|
14218
14895
|
} catch (error) {
|
|
@@ -14261,8 +14938,11 @@ function toVaultError(error) {
|
|
|
14261
14938
|
}
|
|
14262
14939
|
try {
|
|
14263
14940
|
const response = await pollApiGet(url.toString(), isExploreVaultsResponse, pollingConfig);
|
|
14941
|
+
// `pollApiGet` validates via a boolean guard and returns the raw JSON — it
|
|
14942
|
+
// does not run the schema's preprocess. Parse explicitly so unknown
|
|
14943
|
+
// `productType` values are dropped before `toVaultInfo`.
|
|
14264
14944
|
return {
|
|
14265
|
-
vaults: response.data.vaults.map(toVaultInfo),
|
|
14945
|
+
vaults: earnOpportunityListSchema.parse(response.data.vaults).map(toVaultInfo),
|
|
14266
14946
|
pagination: response.data.pagination
|
|
14267
14947
|
};
|
|
14268
14948
|
} catch (error) {
|
|
@@ -14426,6 +15106,12 @@ function toPositionInfo(data) {
|
|
|
14426
15106
|
execId: response.data.execId,
|
|
14427
15107
|
preparedBundle,
|
|
14428
15108
|
expiresAt: response.data.expiresAt,
|
|
15109
|
+
...response.data.quoteIssuedAt !== undefined && {
|
|
15110
|
+
quoteIssuedAt: response.data.quoteIssuedAt
|
|
15111
|
+
},
|
|
15112
|
+
...response.data.quoteExpiry !== undefined && {
|
|
15113
|
+
quoteExpiry: response.data.quoteExpiry
|
|
15114
|
+
},
|
|
14429
15115
|
review: response.data.review
|
|
14430
15116
|
};
|
|
14431
15117
|
} catch (error) {
|
|
@@ -14767,7 +15453,110 @@ function toClaimedAmount(reward) {
|
|
|
14767
15453
|
}
|
|
14768
15454
|
}
|
|
14769
15455
|
|
|
14770
|
-
|
|
15456
|
+
/**
|
|
15457
|
+
* Map the Earn Service's server-side quote gas estimates into the SDK
|
|
15458
|
+
* {@link EarnGasFeeEstimate} shape.
|
|
15459
|
+
*
|
|
15460
|
+
* The Earn Service estimates gas for each action (`Approve`, `Deposit`,
|
|
15461
|
+
* `Withdraw`) and returns `{ name, fees: { gas, gasPrice, fee } }` with raw
|
|
15462
|
+
* integer strings.
|
|
15463
|
+
* The SDK type additionally carries `token` (the chain's native currency
|
|
15464
|
+
* symbol) and `blockchain`, which are filled in here from the chain
|
|
15465
|
+
* definition.
|
|
15466
|
+
*
|
|
15467
|
+
* Gas reporting is best-effort: a malformed entry (e.g. a non-integer string
|
|
15468
|
+
* that fails `BigInt` parsing) degrades to a `{ fees: null, error }` estimate
|
|
15469
|
+
* rather than throwing, so one bad entry never fails the whole quote.
|
|
15470
|
+
*
|
|
15471
|
+
* @param gasFees - Backend gas-fee entries from the quote response, if any.
|
|
15472
|
+
* @param chain - Chain definition, used for the native token symbol and
|
|
15473
|
+
* blockchain identifier.
|
|
15474
|
+
* @returns One {@link EarnGasFeeEstimate} per backend entry (empty when the
|
|
15475
|
+
* backend returned none).
|
|
15476
|
+
*
|
|
15477
|
+
* @example
|
|
15478
|
+
* ```typescript
|
|
15479
|
+
* toQuoteGasFees(
|
|
15480
|
+
* [{ name: 'Deposit', fees: { gas: '364142', gasPrice: '21000000000', fee: '7646982000000000' } }],
|
|
15481
|
+
* arcTestnet,
|
|
15482
|
+
* )
|
|
15483
|
+
* // [{ name: 'Deposit', token: 'USDC', blockchain: 'Arc_Testnet',
|
|
15484
|
+
* // fees: { gas: 364142n, gasPrice: 21000000000n, fee: '7646982000000000' } }]
|
|
15485
|
+
* ```
|
|
15486
|
+
*
|
|
15487
|
+
* @internal
|
|
15488
|
+
*/ function toQuoteGasFees(gasFees, chain) {
|
|
15489
|
+
if (gasFees === undefined) {
|
|
15490
|
+
return [];
|
|
15491
|
+
}
|
|
15492
|
+
return gasFees.map((entry)=>{
|
|
15493
|
+
const base = {
|
|
15494
|
+
// `name` is optional on the wire; label an unnamed entry rather than
|
|
15495
|
+
// emitting `name: undefined`.
|
|
15496
|
+
name: entry.name ?? 'Unknown',
|
|
15497
|
+
token: chain.nativeCurrency.symbol,
|
|
15498
|
+
blockchain: chain.chain
|
|
15499
|
+
};
|
|
15500
|
+
// The Earn Service itself reports a failed estimate as `fees: null` with
|
|
15501
|
+
// an error; propagate that soft failure verbatim.
|
|
15502
|
+
if (entry.fees === null || entry.fees === undefined) {
|
|
15503
|
+
return {
|
|
15504
|
+
...base,
|
|
15505
|
+
fees: null,
|
|
15506
|
+
error: entry.error ?? 'gas estimate unavailable'
|
|
15507
|
+
};
|
|
15508
|
+
}
|
|
15509
|
+
// `fees` is `unknown` at the schema layer, so ALL validation happens here:
|
|
15510
|
+
// that it is an object at all, and that `gas`, `gasPrice`, and `fee` are
|
|
15511
|
+
// each parseable integer strings (including `fee`, which the SDK contract
|
|
15512
|
+
// requires be a numeric base-unit string). Any failure — a wrong type
|
|
15513
|
+
// (`fees: 123`), a missing field, or a non-numeric value — degrades the
|
|
15514
|
+
// whole entry to a `fees: null` soft failure rather than surfacing a
|
|
15515
|
+
// malformed "successful" estimate or rejecting the quote.
|
|
15516
|
+
try {
|
|
15517
|
+
if (typeof entry.fees !== 'object') {
|
|
15518
|
+
throw new TypeError(`gas fees must be an object (got ${typeof entry.fees})`);
|
|
15519
|
+
}
|
|
15520
|
+
const { gas, gasPrice, fee } = entry.fees;
|
|
15521
|
+
return {
|
|
15522
|
+
...base,
|
|
15523
|
+
fees: {
|
|
15524
|
+
gas: toBigInt('gas', gas),
|
|
15525
|
+
gasPrice: toBigInt('gasPrice', gasPrice),
|
|
15526
|
+
fee: toBigInt('fee', fee).toString()
|
|
15527
|
+
}
|
|
15528
|
+
};
|
|
15529
|
+
} catch (error) {
|
|
15530
|
+
return {
|
|
15531
|
+
...base,
|
|
15532
|
+
fees: null,
|
|
15533
|
+
error: getErrorMessage(error)
|
|
15534
|
+
};
|
|
15535
|
+
}
|
|
15536
|
+
});
|
|
15537
|
+
}
|
|
15538
|
+
/**
|
|
15539
|
+
* Parse an unknown value into a `bigint`, rejecting anything that is not a
|
|
15540
|
+
* non-empty integer string. `BigInt` alone is too permissive for this path —
|
|
15541
|
+
* it accepts numbers, booleans, and empty strings — so guard the type first.
|
|
15542
|
+
*
|
|
15543
|
+
* @param field - Field name, used in the thrown error message.
|
|
15544
|
+
* @param value - Raw value from the backend gas entry.
|
|
15545
|
+
* @returns The parsed `bigint`.
|
|
15546
|
+
* @throws {TypeError} When `value` is not a non-empty integer string.
|
|
15547
|
+
*/ function toBigInt(field, value) {
|
|
15548
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
15549
|
+
throw new TypeError(`gas fee field "${field}" must be an integer string`);
|
|
15550
|
+
}
|
|
15551
|
+
try {
|
|
15552
|
+
// BigInt throws on non-integer strings (e.g. "1.5", "not-a-number").
|
|
15553
|
+
return BigInt(value);
|
|
15554
|
+
} catch {
|
|
15555
|
+
throw new Error(`gas fee field "${field}" is not a valid integer string: ${value}`);
|
|
15556
|
+
}
|
|
15557
|
+
}
|
|
15558
|
+
|
|
15559
|
+
function toDepositQuoteInfo(data, chain) {
|
|
14771
15560
|
const fees = (data.fees ?? []).map(({ token: feeTokenSymbol, ...fee })=>{
|
|
14772
15561
|
// Earn Service returns fee.token as a display symbol, for example "USDC".
|
|
14773
15562
|
return {
|
|
@@ -14796,7 +15585,10 @@ function toDepositQuoteInfo(data) {
|
|
|
14796
15585
|
sharePrice: data.sharePrice,
|
|
14797
15586
|
currentApy: data.currentApy,
|
|
14798
15587
|
fees,
|
|
14799
|
-
|
|
15588
|
+
// The Earn Service estimates gas server-side; the chain fills token/blockchain.
|
|
15589
|
+
// Cross-chain quotes resolve no local chain definition, so gasFees stays
|
|
15590
|
+
// empty there (unchanged behavior).
|
|
15591
|
+
gasFees: chain === undefined ? [] : toQuoteGasFees(data.gasFees, chain)
|
|
14800
15592
|
};
|
|
14801
15593
|
}
|
|
14802
15594
|
/**
|
|
@@ -14831,7 +15623,7 @@ function toDepositQuoteInfo(data) {
|
|
|
14831
15623
|
};
|
|
14832
15624
|
try {
|
|
14833
15625
|
const response = await pollApiPost(url.toString(), requestBody, isDepositQuoteResponse, pollingConfig);
|
|
14834
|
-
return toDepositQuoteInfo(response.data);
|
|
15626
|
+
return toDepositQuoteInfo(response.data, params.chainDefinition);
|
|
14835
15627
|
} catch (error) {
|
|
14836
15628
|
throw parseEarnApiError(error, {
|
|
14837
15629
|
operation: 'getDepositQuote'
|
|
@@ -14839,7 +15631,7 @@ function toDepositQuoteInfo(data) {
|
|
|
14839
15631
|
}
|
|
14840
15632
|
}
|
|
14841
15633
|
|
|
14842
|
-
function toWithdrawalQuoteInfo(data) {
|
|
15634
|
+
function toWithdrawalQuoteInfo(data, chain) {
|
|
14843
15635
|
return {
|
|
14844
15636
|
vaultAddress: data.vaultAddress,
|
|
14845
15637
|
vaultName: data.vaultName,
|
|
@@ -14867,7 +15659,7 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
14867
15659
|
status
|
|
14868
15660
|
}
|
|
14869
15661
|
})),
|
|
14870
|
-
gasFees: [],
|
|
15662
|
+
gasFees: chain === undefined ? [] : toQuoteGasFees(data.gasFees, chain),
|
|
14871
15663
|
// Wire format uses `warnings`, but the SDK surface uses
|
|
14872
15664
|
// `earnKitWarnings` to match the precedent set by `VaultInfo` —
|
|
14873
15665
|
// `warnings` is reserved for the structured `VaultWarning` shape.
|
|
@@ -14899,7 +15691,7 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
14899
15691
|
};
|
|
14900
15692
|
try {
|
|
14901
15693
|
const response = await pollApiPost(url.toString(), requestBody, isWithdrawalQuoteResponse, pollingConfig);
|
|
14902
|
-
return toWithdrawalQuoteInfo(response.data);
|
|
15694
|
+
return toWithdrawalQuoteInfo(response.data, params.chainDefinition);
|
|
14903
15695
|
} catch (error) {
|
|
14904
15696
|
throw parseEarnApiError(error, {
|
|
14905
15697
|
operation: 'getWithdrawalQuote'
|
|
@@ -14935,6 +15727,8 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
14935
15727
|
amount: Amount.fromJSON(r.amount),
|
|
14936
15728
|
address: r.token
|
|
14937
15729
|
})),
|
|
15730
|
+
// The claimRewards/quote response does not carry a gas estimate (unlike
|
|
15731
|
+
// deposit/withdrawal quotes), so there is nothing to surface here.
|
|
14938
15732
|
gasFees: []
|
|
14939
15733
|
};
|
|
14940
15734
|
} catch (error) {
|
|
@@ -14944,6 +15738,83 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
14944
15738
|
}
|
|
14945
15739
|
}
|
|
14946
15740
|
|
|
15741
|
+
/**
|
|
15742
|
+
* Build the native gas triple the backend expects for `gasUsed`.
|
|
15743
|
+
*
|
|
15744
|
+
* Returns `undefined` unless both receipt components are present, so the
|
|
15745
|
+
* caller can omit the field entirely — the Earn Service treats a missing
|
|
15746
|
+
* triple as "skip the gas cache write, still return 200".
|
|
15747
|
+
*
|
|
15748
|
+
* @param gasUsed - Receipt gas units used.
|
|
15749
|
+
* @param effectiveGasPrice - Receipt effective gas price.
|
|
15750
|
+
* @returns The `{ gas, gasPrice, fee }` triple, or `undefined` when either
|
|
15751
|
+
* component is missing.
|
|
15752
|
+
*
|
|
15753
|
+
* @example
|
|
15754
|
+
* ```typescript
|
|
15755
|
+
* buildReportedGasUsed(362454n, 29466364605n)
|
|
15756
|
+
* // { gas: '362454', gasPrice: '29466364605', fee: '10680201716540670' }
|
|
15757
|
+
* ```
|
|
15758
|
+
*
|
|
15759
|
+
* @internal
|
|
15760
|
+
*/ function buildReportedGasUsed(gasUsed, effectiveGasPrice) {
|
|
15761
|
+
if (gasUsed === undefined || effectiveGasPrice === undefined) {
|
|
15762
|
+
return undefined;
|
|
15763
|
+
}
|
|
15764
|
+
return {
|
|
15765
|
+
gas: gasUsed.toString(),
|
|
15766
|
+
gasPrice: effectiveGasPrice.toString(),
|
|
15767
|
+
fee: (gasUsed * effectiveGasPrice).toString()
|
|
15768
|
+
};
|
|
15769
|
+
}
|
|
15770
|
+
/**
|
|
15771
|
+
* Report the outcome of an SDK-submitted same-chain Earn transaction.
|
|
15772
|
+
*
|
|
15773
|
+
* @param params - Transaction report parameters.
|
|
15774
|
+
* @throws {@link KitError} When the API call fails.
|
|
15775
|
+
*
|
|
15776
|
+
* @internal
|
|
15777
|
+
*/ async function reportEarnTransaction(params) {
|
|
15778
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
15779
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/transactions/report`, baseUrl);
|
|
15780
|
+
// The report endpoint is not idempotent: success reports refresh the gas
|
|
15781
|
+
// cache and failure reports increment counts. If the first request succeeds
|
|
15782
|
+
// server-side but the client times out or sees a transient 5xx, retrying
|
|
15783
|
+
// would duplicate the report (double-writing an outcome or inflating failure
|
|
15784
|
+
// counts). Reporting is best-effort (see the fire-and-forget caller), so
|
|
15785
|
+
// make exactly one attempt and never retry — a single dropped report is
|
|
15786
|
+
// preferable to a duplicated one. `maxRetries` here is the total attempt
|
|
15787
|
+
// count in pollApiWithValidation (loop runs `attempt <= maxRetries`), so 1
|
|
15788
|
+
// means one request with no retry; 0 would skip the request entirely.
|
|
15789
|
+
const reportConfig = {
|
|
15790
|
+
...pollingConfig,
|
|
15791
|
+
maxRetries: 1
|
|
15792
|
+
};
|
|
15793
|
+
const gasUsed = buildReportedGasUsed(params.gasUsed, params.effectiveGasPrice);
|
|
15794
|
+
const requestBody = {
|
|
15795
|
+
execId: params.execId,
|
|
15796
|
+
chain: params.chain,
|
|
15797
|
+
status: params.status,
|
|
15798
|
+
action: params.action,
|
|
15799
|
+
...params.txHash !== undefined && {
|
|
15800
|
+
txHash: params.txHash
|
|
15801
|
+
},
|
|
15802
|
+
...gasUsed !== undefined && {
|
|
15803
|
+
gasUsed
|
|
15804
|
+
},
|
|
15805
|
+
...params.errorCode !== undefined && {
|
|
15806
|
+
errorCode: params.errorCode
|
|
15807
|
+
}
|
|
15808
|
+
};
|
|
15809
|
+
try {
|
|
15810
|
+
await pollApiPost(url.toString(), requestBody, isTransactionReportResponse, reportConfig);
|
|
15811
|
+
} catch (error) {
|
|
15812
|
+
throw parseEarnApiError(error, {
|
|
15813
|
+
operation: 'transactionReport'
|
|
15814
|
+
});
|
|
15815
|
+
}
|
|
15816
|
+
}
|
|
15817
|
+
|
|
14947
15818
|
/**
|
|
14948
15819
|
* Sum the amounts across every token input to size the allowance approval.
|
|
14949
15820
|
*
|
|
@@ -15054,6 +15925,59 @@ function toWithdrawalQuoteInfo(data) {
|
|
|
15054
15925
|
// Intentionally built-ins-only: Earn bridge support is limited to SDK-known
|
|
15055
15926
|
// token contracts plus the explicit ERC-3009 domain allowlist below.
|
|
15056
15927
|
const TOKEN_REGISTRY = createTokenRegistry();
|
|
15928
|
+
function submitTransactionReport(reportContext, action, status, details) {
|
|
15929
|
+
void reportEarnTransaction({
|
|
15930
|
+
execId: reportContext.execId,
|
|
15931
|
+
chain: reportContext.chain,
|
|
15932
|
+
config: reportContext.config,
|
|
15933
|
+
action,
|
|
15934
|
+
status,
|
|
15935
|
+
...details
|
|
15936
|
+
}).catch(()=>undefined);
|
|
15937
|
+
}
|
|
15938
|
+
function reportTransactionSuccess(reportContext, action, result) {
|
|
15939
|
+
if (result === undefined) {
|
|
15940
|
+
return;
|
|
15941
|
+
}
|
|
15942
|
+
submitTransactionReport(reportContext, action, 'success', {
|
|
15943
|
+
txHash: result.txHash,
|
|
15944
|
+
gasUsed: result.gasUsed,
|
|
15945
|
+
effectiveGasPrice: result.effectiveGasPrice
|
|
15946
|
+
});
|
|
15947
|
+
}
|
|
15948
|
+
function reportTransactionFailure(reportContext, action, error) {
|
|
15949
|
+
submitTransactionReport(reportContext, action, 'failure', {
|
|
15950
|
+
txHash: transactionReportTxHash(error),
|
|
15951
|
+
errorCode: transactionReportErrorCode(error)
|
|
15952
|
+
});
|
|
15953
|
+
}
|
|
15954
|
+
function transactionReportErrorCode(error) {
|
|
15955
|
+
if (isKitError(error)) {
|
|
15956
|
+
return error.name;
|
|
15957
|
+
}
|
|
15958
|
+
const message = getErrorMessage(error);
|
|
15959
|
+
if (/user (rejected|denied)|rejected by user/i.test(message)) {
|
|
15960
|
+
return 'USER_REJECTED';
|
|
15961
|
+
}
|
|
15962
|
+
if (/insufficient funds/i.test(message)) {
|
|
15963
|
+
return 'INSUFFICIENT_FUNDS';
|
|
15964
|
+
}
|
|
15965
|
+
if (/timeout|timed out/i.test(message)) {
|
|
15966
|
+
return 'TIMEOUT';
|
|
15967
|
+
}
|
|
15968
|
+
return 'UNKNOWN_ERROR';
|
|
15969
|
+
}
|
|
15970
|
+
function transactionReportTxHash(error) {
|
|
15971
|
+
if (!isKitError(error)) {
|
|
15972
|
+
return undefined;
|
|
15973
|
+
}
|
|
15974
|
+
const trace = error.cause?.trace;
|
|
15975
|
+
if (typeof trace !== 'object' || trace === null) {
|
|
15976
|
+
return undefined;
|
|
15977
|
+
}
|
|
15978
|
+
const txHash = trace['txHash'];
|
|
15979
|
+
return typeof txHash === 'string' && txHash !== '' ? txHash : undefined;
|
|
15980
|
+
}
|
|
15057
15981
|
/**
|
|
15058
15982
|
* Build the typed error raised when a cross-chain wait is cancelled via its
|
|
15059
15983
|
* `AbortSignal`. Mirrors `@core/adapter-base`'s `createAbortError` (same
|
|
@@ -15375,7 +16299,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15375
16299
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
15376
16300
|
const { adapter } = params.from;
|
|
15377
16301
|
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15378
|
-
const { executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
|
|
16302
|
+
const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
|
|
15379
16303
|
vaultAddress,
|
|
15380
16304
|
amount: params.amount,
|
|
15381
16305
|
address,
|
|
@@ -15383,32 +16307,55 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15383
16307
|
config
|
|
15384
16308
|
}), ()=>undefined);
|
|
15385
16309
|
validateExecutionDeadline(executionParams);
|
|
16310
|
+
const transactionReportContext = {
|
|
16311
|
+
execId,
|
|
16312
|
+
chain: apiChain,
|
|
16313
|
+
config
|
|
16314
|
+
};
|
|
15386
16315
|
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
15387
16316
|
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
15388
16317
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
15389
16318
|
if (!options.skipApprove && approvalToken !== undefined && requiredAllowance > 0n) {
|
|
15390
|
-
await this.runPhase(ctx, 'approve', 'approve', async ()=>
|
|
16319
|
+
await this.runPhase(ctx, 'approve', 'approve', async ()=>{
|
|
16320
|
+
try {
|
|
16321
|
+
const approval = await approveAllowanceIfNeeded({
|
|
16322
|
+
adapter,
|
|
16323
|
+
chain,
|
|
16324
|
+
tokenAddress: approvalToken,
|
|
16325
|
+
delegate: adapterContractAddress,
|
|
16326
|
+
address,
|
|
16327
|
+
requiredAllowance,
|
|
16328
|
+
revertMessage: 'Earn deposit token approval reverted on-chain'
|
|
16329
|
+
});
|
|
16330
|
+
reportTransactionSuccess(transactionReportContext, 'Approve', approval);
|
|
16331
|
+
return approval;
|
|
16332
|
+
} catch (error) {
|
|
16333
|
+
reportTransactionFailure(transactionReportContext, 'Approve', error);
|
|
16334
|
+
throw error;
|
|
16335
|
+
}
|
|
16336
|
+
}, (approval)=>approval?.txHash);
|
|
16337
|
+
}
|
|
16338
|
+
const { txHash, explorerUrl } = await this.runPhase(ctx, 'deposit', 'execute', async ()=>{
|
|
16339
|
+
try {
|
|
16340
|
+
const result = await executeEarnAction({
|
|
15391
16341
|
adapter,
|
|
15392
16342
|
chain,
|
|
15393
|
-
tokenAddress: approvalToken,
|
|
15394
|
-
delegate: adapterContractAddress,
|
|
15395
16343
|
address,
|
|
15396
|
-
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
}), ({ txHash })=>txHash);
|
|
16344
|
+
actionKey: 'earn.deposit',
|
|
16345
|
+
actionParams: {
|
|
16346
|
+
executeParams: executionParams,
|
|
16347
|
+
tokenInputs,
|
|
16348
|
+
signature
|
|
16349
|
+
},
|
|
16350
|
+
revertMessage: 'Earn deposit reverted on-chain'
|
|
16351
|
+
});
|
|
16352
|
+
reportTransactionSuccess(transactionReportContext, 'Deposit', result);
|
|
16353
|
+
return result;
|
|
16354
|
+
} catch (error) {
|
|
16355
|
+
reportTransactionFailure(transactionReportContext, 'Deposit', error);
|
|
16356
|
+
throw error;
|
|
16357
|
+
}
|
|
16358
|
+
}, ({ txHash })=>txHash);
|
|
15412
16359
|
return {
|
|
15413
16360
|
kind: 'same-chain',
|
|
15414
16361
|
txHash,
|
|
@@ -15488,7 +16435,13 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15488
16435
|
amount: params.amount,
|
|
15489
16436
|
sourceChain: sourceChain.chain,
|
|
15490
16437
|
destinationChain: destinationChain.chain,
|
|
15491
|
-
expiresAt: prepared.expiresAt
|
|
16438
|
+
expiresAt: prepared.expiresAt,
|
|
16439
|
+
...prepared.quoteIssuedAt !== undefined && {
|
|
16440
|
+
quoteIssuedAt: prepared.quoteIssuedAt
|
|
16441
|
+
},
|
|
16442
|
+
...prepared.quoteExpiry !== undefined && {
|
|
16443
|
+
quoteExpiry: prepared.quoteExpiry
|
|
16444
|
+
}
|
|
15492
16445
|
};
|
|
15493
16446
|
}
|
|
15494
16447
|
/** {@inheritdoc} */ async withdraw(params) {
|
|
@@ -15509,7 +16462,7 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15509
16462
|
const adapterContractAddress = requireAdapterContract(chain);
|
|
15510
16463
|
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15511
16464
|
const { adapter } = params.from;
|
|
15512
|
-
const { executionParams, signature } = await this.runPhase(ctx, 'withdraw', 'fetchParams', async ()=>fetchWithdraw({
|
|
16465
|
+
const { execId, executionParams, signature } = await this.runPhase(ctx, 'withdraw', 'fetchParams', async ()=>fetchWithdraw({
|
|
15513
16466
|
vaultAddress,
|
|
15514
16467
|
amount: params.amount,
|
|
15515
16468
|
address,
|
|
@@ -15517,32 +16470,55 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15517
16470
|
config
|
|
15518
16471
|
}), ()=>undefined);
|
|
15519
16472
|
validateExecutionDeadline(executionParams);
|
|
16473
|
+
const transactionReportContext = {
|
|
16474
|
+
execId,
|
|
16475
|
+
chain: apiChain,
|
|
16476
|
+
config
|
|
16477
|
+
};
|
|
15520
16478
|
const tokenInputs = buildEarnTokenInputs(executionParams, vaultAddress);
|
|
15521
16479
|
const approvalToken = tokenInputs[0]?.token;
|
|
15522
16480
|
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
15523
16481
|
if (!options.skipApprove && approvalToken !== undefined) {
|
|
15524
|
-
await this.runPhase(ctx, 'approve', 'approve', async ()=>
|
|
16482
|
+
await this.runPhase(ctx, 'approve', 'approve', async ()=>{
|
|
16483
|
+
try {
|
|
16484
|
+
const approval = await approveAllowanceIfNeeded({
|
|
16485
|
+
adapter,
|
|
16486
|
+
chain,
|
|
16487
|
+
tokenAddress: approvalToken,
|
|
16488
|
+
delegate: adapterContractAddress,
|
|
16489
|
+
address,
|
|
16490
|
+
requiredAllowance,
|
|
16491
|
+
revertMessage: 'Vault share token approval reverted on-chain'
|
|
16492
|
+
});
|
|
16493
|
+
reportTransactionSuccess(transactionReportContext, 'Approve', approval);
|
|
16494
|
+
return approval;
|
|
16495
|
+
} catch (error) {
|
|
16496
|
+
reportTransactionFailure(transactionReportContext, 'Approve', error);
|
|
16497
|
+
throw error;
|
|
16498
|
+
}
|
|
16499
|
+
}, (approval)=>approval?.txHash);
|
|
16500
|
+
}
|
|
16501
|
+
const { txHash, explorerUrl } = await this.runPhase(ctx, 'withdraw', 'execute', async ()=>{
|
|
16502
|
+
try {
|
|
16503
|
+
const result = await executeEarnAction({
|
|
15525
16504
|
adapter,
|
|
15526
16505
|
chain,
|
|
15527
|
-
tokenAddress: approvalToken,
|
|
15528
|
-
delegate: adapterContractAddress,
|
|
15529
16506
|
address,
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
}), ({ txHash })=>txHash);
|
|
16507
|
+
actionKey: 'earn.withdraw',
|
|
16508
|
+
actionParams: {
|
|
16509
|
+
executeParams: executionParams,
|
|
16510
|
+
tokenInputs,
|
|
16511
|
+
signature
|
|
16512
|
+
},
|
|
16513
|
+
revertMessage: 'Earn withdraw reverted on-chain'
|
|
16514
|
+
});
|
|
16515
|
+
reportTransactionSuccess(transactionReportContext, 'Withdraw', result);
|
|
16516
|
+
return result;
|
|
16517
|
+
} catch (error) {
|
|
16518
|
+
reportTransactionFailure(transactionReportContext, 'Withdraw', error);
|
|
16519
|
+
throw error;
|
|
16520
|
+
}
|
|
16521
|
+
}, ({ txHash })=>txHash);
|
|
15546
16522
|
return {
|
|
15547
16523
|
txHash,
|
|
15548
16524
|
explorerUrl,
|
|
@@ -15676,141 +16652,6 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15676
16652
|
}
|
|
15677
16653
|
}
|
|
15678
16654
|
}
|
|
15679
|
-
gasEstimateFailure(name, chain, error) {
|
|
15680
|
-
return {
|
|
15681
|
-
name,
|
|
15682
|
-
token: chain.nativeCurrency.symbol,
|
|
15683
|
-
blockchain: chain.chain,
|
|
15684
|
-
fees: null,
|
|
15685
|
-
error: getErrorMessage(error)
|
|
15686
|
-
};
|
|
15687
|
-
}
|
|
15688
|
-
async estimateDepositQuoteGasFees(params) {
|
|
15689
|
-
const { adapter, chain, apiChain, address, vaultAddress, amount, config } = params;
|
|
15690
|
-
try {
|
|
15691
|
-
const adapterContractAddress = requireAdapterContract(chain);
|
|
15692
|
-
const normalizedVaultAddress = assertHexAddress('vaultAddress', vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15693
|
-
const { executionParams, signature } = await fetchDeposit({
|
|
15694
|
-
vaultAddress: normalizedVaultAddress,
|
|
15695
|
-
amount,
|
|
15696
|
-
address,
|
|
15697
|
-
chain: apiChain,
|
|
15698
|
-
config
|
|
15699
|
-
});
|
|
15700
|
-
validateExecutionDeadline(executionParams);
|
|
15701
|
-
const approvalToken = resolveEarnApprovalToken(executionParams);
|
|
15702
|
-
const tokenInputs = approvalToken === undefined ? [] : buildEarnTokenInputs(executionParams, approvalToken);
|
|
15703
|
-
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
15704
|
-
return await estimateEarnQuoteGasFees({
|
|
15705
|
-
adapter,
|
|
15706
|
-
chain,
|
|
15707
|
-
address,
|
|
15708
|
-
actionName: 'Deposit',
|
|
15709
|
-
actionKey: 'earn.deposit',
|
|
15710
|
-
actionParams: {
|
|
15711
|
-
executeParams: executionParams,
|
|
15712
|
-
tokenInputs,
|
|
15713
|
-
signature
|
|
15714
|
-
},
|
|
15715
|
-
approval: approvalToken !== undefined && requiredAllowance > 0n ? {
|
|
15716
|
-
token: approvalToken,
|
|
15717
|
-
delegate: adapterContractAddress,
|
|
15718
|
-
requiredAllowance
|
|
15719
|
-
} : undefined
|
|
15720
|
-
});
|
|
15721
|
-
} catch (error) {
|
|
15722
|
-
return [
|
|
15723
|
-
this.gasEstimateFailure('Deposit', chain, error)
|
|
15724
|
-
];
|
|
15725
|
-
}
|
|
15726
|
-
}
|
|
15727
|
-
async estimateWithdrawalQuoteGasFees(params) {
|
|
15728
|
-
const { adapter, chain, apiChain, address, vaultAddress, amount, config } = params;
|
|
15729
|
-
try {
|
|
15730
|
-
const adapterContractAddress = requireAdapterContract(chain);
|
|
15731
|
-
const normalizedVaultAddress = assertHexAddress('vaultAddress', vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
15732
|
-
const { executionParams, signature } = await fetchWithdraw({
|
|
15733
|
-
vaultAddress: normalizedVaultAddress,
|
|
15734
|
-
amount,
|
|
15735
|
-
address,
|
|
15736
|
-
chain: apiChain,
|
|
15737
|
-
config
|
|
15738
|
-
});
|
|
15739
|
-
validateExecutionDeadline(executionParams);
|
|
15740
|
-
const tokenInputs = buildEarnTokenInputs(executionParams, normalizedVaultAddress);
|
|
15741
|
-
const approvalToken = tokenInputs[0]?.token;
|
|
15742
|
-
const requiredAllowance = sumTokenInputAmounts(tokenInputs);
|
|
15743
|
-
return await estimateEarnQuoteGasFees({
|
|
15744
|
-
adapter,
|
|
15745
|
-
chain,
|
|
15746
|
-
address,
|
|
15747
|
-
actionName: 'Withdraw',
|
|
15748
|
-
actionKey: 'earn.withdraw',
|
|
15749
|
-
actionParams: {
|
|
15750
|
-
executeParams: executionParams,
|
|
15751
|
-
tokenInputs,
|
|
15752
|
-
signature
|
|
15753
|
-
},
|
|
15754
|
-
approval: approvalToken !== undefined ? {
|
|
15755
|
-
token: approvalToken,
|
|
15756
|
-
delegate: adapterContractAddress,
|
|
15757
|
-
requiredAllowance
|
|
15758
|
-
} : undefined
|
|
15759
|
-
});
|
|
15760
|
-
} catch (error) {
|
|
15761
|
-
return [
|
|
15762
|
-
this.gasEstimateFailure('Withdraw', chain, error)
|
|
15763
|
-
];
|
|
15764
|
-
}
|
|
15765
|
-
}
|
|
15766
|
-
async estimateClaimRewardsQuoteGasFees(params) {
|
|
15767
|
-
const { adapter, chain, apiChain, address, vaultAddress, config } = params;
|
|
15768
|
-
try {
|
|
15769
|
-
requireAdapterContract(chain);
|
|
15770
|
-
const { rewards, executionParams, signature } = await fetchClaimRewards({
|
|
15771
|
-
address,
|
|
15772
|
-
chain: apiChain,
|
|
15773
|
-
vaultAddress,
|
|
15774
|
-
config
|
|
15775
|
-
});
|
|
15776
|
-
if (rewards.length === 0) {
|
|
15777
|
-
return [];
|
|
15778
|
-
}
|
|
15779
|
-
const missingExecutionParams = executionParams === undefined;
|
|
15780
|
-
const missingSignature = signature === undefined;
|
|
15781
|
-
if (missingExecutionParams || missingSignature) {
|
|
15782
|
-
throw new KitError({
|
|
15783
|
-
...EarnError.INTERNAL_ERROR,
|
|
15784
|
-
recoverability: 'RETRYABLE',
|
|
15785
|
-
message: 'Claim rewards response must include executionParams and signature when rewards are claimable',
|
|
15786
|
-
cause: {
|
|
15787
|
-
trace: {
|
|
15788
|
-
rewardsCount: rewards.length,
|
|
15789
|
-
missingExecutionParams,
|
|
15790
|
-
missingSignature
|
|
15791
|
-
}
|
|
15792
|
-
}
|
|
15793
|
-
});
|
|
15794
|
-
}
|
|
15795
|
-
validateExecutionDeadline(executionParams);
|
|
15796
|
-
return await estimateEarnQuoteGasFees({
|
|
15797
|
-
adapter,
|
|
15798
|
-
chain,
|
|
15799
|
-
address,
|
|
15800
|
-
actionName: 'Claim Rewards',
|
|
15801
|
-
actionKey: 'earn.claimRewards',
|
|
15802
|
-
actionParams: {
|
|
15803
|
-
executeParams: executionParams,
|
|
15804
|
-
tokenInputs: [],
|
|
15805
|
-
signature
|
|
15806
|
-
}
|
|
15807
|
-
});
|
|
15808
|
-
} catch (error) {
|
|
15809
|
-
return [
|
|
15810
|
-
this.gasEstimateFailure('Claim Rewards', chain, error)
|
|
15811
|
-
];
|
|
15812
|
-
}
|
|
15813
|
-
}
|
|
15814
16655
|
/** {@inheritdoc} */ async getDepositQuote(params) {
|
|
15815
16656
|
const config = this.resolveConfig(params.config);
|
|
15816
16657
|
if (hasQuoteDestinationChain(params)) {
|
|
@@ -15833,96 +16674,43 @@ function finishElapsedWait(lastStatus, lastError) {
|
|
|
15833
16674
|
throw createValidationFailedError('chain', destinationChain.chain, 'chain is only supported for cross-chain Earn deposit quotes; omit chain/address when quoting on the source chain');
|
|
15834
16675
|
}
|
|
15835
16676
|
const { address, chain, chainDefinition } = await resolveAdapterContext(params.from);
|
|
15836
|
-
//
|
|
15837
|
-
//
|
|
15838
|
-
//
|
|
15839
|
-
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
|
|
15843
|
-
|
|
15844
|
-
|
|
15845
|
-
|
|
15846
|
-
|
|
15847
|
-
this.estimateDepositQuoteGasFees({
|
|
15848
|
-
adapter: params.from.adapter,
|
|
15849
|
-
chain: chainDefinition,
|
|
15850
|
-
apiChain: chain,
|
|
15851
|
-
address,
|
|
15852
|
-
vaultAddress: params.vaultAddress,
|
|
15853
|
-
amount: params.amount,
|
|
15854
|
-
config
|
|
15855
|
-
})
|
|
15856
|
-
]);
|
|
15857
|
-
return {
|
|
15858
|
-
...quote,
|
|
15859
|
-
gasFees
|
|
15860
|
-
};
|
|
16677
|
+
// Gas is estimated server-side by the Earn Service and returned on the quote, so the
|
|
16678
|
+
// SDK no longer simulates it locally. `chainDefinition` lets the fetch fill
|
|
16679
|
+
// the native token symbol / blockchain on each gas entry.
|
|
16680
|
+
return fetchDepositQuote({
|
|
16681
|
+
vaultAddress: params.vaultAddress,
|
|
16682
|
+
amount: params.amount,
|
|
16683
|
+
address,
|
|
16684
|
+
chain,
|
|
16685
|
+
config,
|
|
16686
|
+
chainDefinition
|
|
16687
|
+
});
|
|
15861
16688
|
}
|
|
15862
16689
|
/** {@inheritdoc} */ async getWithdrawalQuote(params) {
|
|
15863
16690
|
const config = this.resolveConfig(params.config);
|
|
15864
16691
|
const { address, chain, chainDefinition } = await resolveAdapterContext(params.from);
|
|
15865
|
-
//
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
config
|
|
15875
|
-
}),
|
|
15876
|
-
this.estimateWithdrawalQuoteGasFees({
|
|
15877
|
-
adapter: params.from.adapter,
|
|
15878
|
-
chain: chainDefinition,
|
|
15879
|
-
apiChain: chain,
|
|
15880
|
-
address,
|
|
15881
|
-
vaultAddress: params.vaultAddress,
|
|
15882
|
-
amount: params.amount,
|
|
15883
|
-
config
|
|
15884
|
-
})
|
|
15885
|
-
]);
|
|
15886
|
-
return {
|
|
15887
|
-
...quote,
|
|
15888
|
-
gasFees
|
|
15889
|
-
};
|
|
16692
|
+
// Gas is estimated server-side by the Earn Service and returned on the quote.
|
|
16693
|
+
return fetchWithdrawalQuote({
|
|
16694
|
+
vaultAddress: params.vaultAddress,
|
|
16695
|
+
amount: params.amount,
|
|
16696
|
+
address,
|
|
16697
|
+
chain,
|
|
16698
|
+
config,
|
|
16699
|
+
chainDefinition
|
|
16700
|
+
});
|
|
15890
16701
|
}
|
|
15891
16702
|
/** {@inheritdoc} */ async getClaimRewardsQuote(params) {
|
|
15892
16703
|
const config = this.resolveConfig(params.config);
|
|
15893
|
-
const { address, chain
|
|
15894
|
-
|
|
16704
|
+
const { address, chain } = await resolveAdapterContext(params.from);
|
|
16705
|
+
// The claimRewards/quote response carries no gas estimate (unlike
|
|
16706
|
+
// deposit/withdrawal quotes), and the SDK no longer estimates gas locally,
|
|
16707
|
+
// so gasFees is always empty for claim rewards.
|
|
16708
|
+
return fetchClaimRewardsQuote({
|
|
15895
16709
|
vaultAddress: params.vaultAddress,
|
|
15896
16710
|
address,
|
|
15897
16711
|
chain,
|
|
15898
16712
|
config
|
|
15899
16713
|
});
|
|
15900
|
-
// No claimable rewards means there is nothing to execute, so there is no
|
|
15901
|
-
// gas to estimate. Short-circuit on the already-fetched quote rather than
|
|
15902
|
-
// calling the (heavier) claim execution endpoint again — this also keeps
|
|
15903
|
-
// `gasFees` empty as documented, instead of risking a `{ fees: null }`
|
|
15904
|
-
// estimation-error entry when the adapter/RPC is unavailable. This
|
|
15905
|
-
// short-circuit is why the claim path stays sequential instead of using
|
|
15906
|
-
// the Promise.all pattern of the deposit/withdrawal quotes: estimating in
|
|
15907
|
-
// parallel would hit the signing endpoint even when nothing is claimable.
|
|
15908
|
-
if (quote.rewards.length === 0) {
|
|
15909
|
-
return {
|
|
15910
|
-
...quote,
|
|
15911
|
-
gasFees: []
|
|
15912
|
-
};
|
|
15913
|
-
}
|
|
15914
|
-
const gasFees = await this.estimateClaimRewardsQuoteGasFees({
|
|
15915
|
-
adapter: params.from.adapter,
|
|
15916
|
-
chain: chainDefinition,
|
|
15917
|
-
apiChain: chain,
|
|
15918
|
-
address,
|
|
15919
|
-
vaultAddress: params.vaultAddress,
|
|
15920
|
-
config
|
|
15921
|
-
});
|
|
15922
|
-
return {
|
|
15923
|
-
...quote,
|
|
15924
|
-
gasFees
|
|
15925
|
-
};
|
|
15926
16714
|
}
|
|
15927
16715
|
}
|
|
15928
16716
|
function hasDepositDestination(params) {
|
|
@@ -16101,6 +16889,12 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
|
16101
16889
|
if (config.providers !== undefined && !Array.isArray(config.providers)) {
|
|
16102
16890
|
throw createValidationFailedError('config.providers', config.providers, 'providers must be an array of earn providers when provided');
|
|
16103
16891
|
}
|
|
16892
|
+
if (config.disableAnalytics !== undefined && typeof config.disableAnalytics !== 'boolean') {
|
|
16893
|
+
throw createValidationFailedError('config.disableAnalytics', config.disableAnalytics, 'disableAnalytics must be a boolean when provided');
|
|
16894
|
+
}
|
|
16895
|
+
if (config.disableErrorReporting !== undefined && typeof config.disableErrorReporting !== 'boolean') {
|
|
16896
|
+
throw createValidationFailedError('config.disableErrorReporting', config.disableErrorReporting, 'disableErrorReporting must be a boolean when provided');
|
|
16897
|
+
}
|
|
16104
16898
|
const defaultProviders = getDefaultProviders();
|
|
16105
16899
|
const providers = [
|
|
16106
16900
|
...config.providers ?? [],
|
|
@@ -16112,6 +16906,31 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
|
|
|
16112
16906
|
return context;
|
|
16113
16907
|
}
|
|
16114
16908
|
|
|
16909
|
+
/**
|
|
16910
|
+
* Register Earn Kit telemetry event type strings with the shared registry so
|
|
16911
|
+
* error telemetry helpers remain compile-time checked.
|
|
16912
|
+
*
|
|
16913
|
+
* @internal
|
|
16914
|
+
*/ /**
|
|
16915
|
+
* Telemetry event type identifiers for Earn Kit operations.
|
|
16916
|
+
*
|
|
16917
|
+
* @internal
|
|
16918
|
+
*/ const EARN_EVENT_TYPES = {
|
|
16919
|
+
GET_VAULTS: 'earn_get_vaults',
|
|
16920
|
+
EXPLORE_VAULTS: 'earn_explore_vaults',
|
|
16921
|
+
GET_POSITION: 'earn_get_position',
|
|
16922
|
+
GET_CROSS_CHAIN_DEPOSIT_STATUS: 'earn_get_cross_chain_deposit_status',
|
|
16923
|
+
WAIT_FOR_CROSS_CHAIN_DEPOSIT: 'earn_wait_for_cross_chain_deposit',
|
|
16924
|
+
DEPOSIT: 'earn_deposit',
|
|
16925
|
+
CROSS_CHAIN_DEPOSIT: 'earn_cross_chain_deposit',
|
|
16926
|
+
WITHDRAW: 'earn_withdraw',
|
|
16927
|
+
CLAIM_REWARDS: 'earn_claim_rewards',
|
|
16928
|
+
GET_DEPOSIT_QUOTE: 'earn_get_deposit_quote',
|
|
16929
|
+
GET_WITHDRAWAL_QUOTE: 'earn_get_withdrawal_quote',
|
|
16930
|
+
GET_CLAIM_REWARDS_QUOTE: 'earn_get_claim_rewards_quote',
|
|
16931
|
+
RETRY: 'earn_retry'
|
|
16932
|
+
};
|
|
16933
|
+
|
|
16115
16934
|
/**
|
|
16116
16935
|
* Format a provider amount object as a human-readable decimal string.
|
|
16117
16936
|
*
|
|
@@ -16160,11 +16979,27 @@ function formatPositionPnL(pnl) {
|
|
|
16160
16979
|
* @param vault - Provider vault info with raw amount objects
|
|
16161
16980
|
* @returns Vault info with total deposits and liquidity formatted as strings
|
|
16162
16981
|
*/ function formatVaultInfo(vault) {
|
|
16163
|
-
|
|
16982
|
+
// The flat `totalDeposits`/`liquidity` are deprecated aliases that are
|
|
16983
|
+
// intentionally dual-read through the migration window so existing
|
|
16984
|
+
// consumers keep receiving them until Contract.
|
|
16985
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
16986
|
+
const { totalDeposits, liquidity, liquidityProfile, ...rest } = vault;
|
|
16987
|
+
// `liquidityProfile` is `.optional()` in the response schema during the
|
|
16988
|
+
// expand/contract window (an old backend that predates the nested facets
|
|
16989
|
+
// omits it), so only format and re-attach it when present — matching the
|
|
16990
|
+
// provider-side `toVaultInfo` mapper.
|
|
16164
16991
|
return {
|
|
16165
16992
|
...rest,
|
|
16166
16993
|
totalDeposits: formatAmount(totalDeposits),
|
|
16167
|
-
liquidity: formatAmount(liquidity)
|
|
16994
|
+
liquidity: formatAmount(liquidity),
|
|
16995
|
+
...liquidityProfile !== undefined && {
|
|
16996
|
+
liquidityProfile: {
|
|
16997
|
+
...liquidityProfile,
|
|
16998
|
+
totalDeposits: formatAmount(liquidityProfile.totalDeposits),
|
|
16999
|
+
available: formatAmount(liquidityProfile.available),
|
|
17000
|
+
totalSupply: formatAmount(liquidityProfile.totalSupply)
|
|
17001
|
+
}
|
|
17002
|
+
}
|
|
16168
17003
|
};
|
|
16169
17004
|
}
|
|
16170
17005
|
/**
|
|
@@ -17611,6 +18446,14 @@ function hasCrossChainDestination(params) {
|
|
|
17611
18446
|
return formatClaimRewardsQuoteInfo(result);
|
|
17612
18447
|
}
|
|
17613
18448
|
|
|
18449
|
+
/** SDK name used in telemetry payloads. */ const SDK_NAME = resolveKitSdkName(pkg$1.name);
|
|
18450
|
+
/**
|
|
18451
|
+
* Determine whether deposit parameters target a destination chain.
|
|
18452
|
+
*
|
|
18453
|
+
* @internal
|
|
18454
|
+
*/ function isCrossChainDeposit(params) {
|
|
18455
|
+
return 'to' in params && params.to !== undefined;
|
|
18456
|
+
}
|
|
17614
18457
|
function formatRetryResult(operation, result) {
|
|
17615
18458
|
switch(operation){
|
|
17616
18459
|
case 'deposit':
|
|
@@ -17625,6 +18468,70 @@ function formatRetryResult(operation, result) {
|
|
|
17625
18468
|
}
|
|
17626
18469
|
}
|
|
17627
18470
|
}
|
|
18471
|
+
/**
|
|
18472
|
+
* Emit the success event corresponding to a completed retry.
|
|
18473
|
+
*
|
|
18474
|
+
* @internal
|
|
18475
|
+
*/ function emitRetrySuccessTelemetry(trace, result, config) {
|
|
18476
|
+
switch(trace.operation){
|
|
18477
|
+
case 'deposit':
|
|
18478
|
+
{
|
|
18479
|
+
const sourceChain = resolveChainName(trace.params.from.chain);
|
|
18480
|
+
if ('to' in trace.params && trace.params.to !== undefined) {
|
|
18481
|
+
const destinationChain = resolveChainName(trace.params.to.chain);
|
|
18482
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.CROSS_CHAIN_DEPOSIT, config, {
|
|
18483
|
+
...sourceChain != null && {
|
|
18484
|
+
sourceChain
|
|
18485
|
+
},
|
|
18486
|
+
...destinationChain != null && {
|
|
18487
|
+
destinationChain
|
|
18488
|
+
}
|
|
18489
|
+
});
|
|
18490
|
+
return;
|
|
18491
|
+
}
|
|
18492
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.DEPOSIT, config, {
|
|
18493
|
+
...sourceChain != null && {
|
|
18494
|
+
sourceChain
|
|
18495
|
+
},
|
|
18496
|
+
...'txHash' in result && {
|
|
18497
|
+
txHash: result.txHash
|
|
18498
|
+
}
|
|
18499
|
+
});
|
|
18500
|
+
return;
|
|
18501
|
+
}
|
|
18502
|
+
case 'withdraw':
|
|
18503
|
+
{
|
|
18504
|
+
const sourceChain = resolveChainName(trace.params.from.chain);
|
|
18505
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.WITHDRAW, config, {
|
|
18506
|
+
...sourceChain != null && {
|
|
18507
|
+
sourceChain
|
|
18508
|
+
},
|
|
18509
|
+
...'txHash' in result && {
|
|
18510
|
+
txHash: result.txHash
|
|
18511
|
+
}
|
|
18512
|
+
});
|
|
18513
|
+
return;
|
|
18514
|
+
}
|
|
18515
|
+
case 'claimRewards':
|
|
18516
|
+
{
|
|
18517
|
+
if ('rewards' in result && result.status === 'claimed') {
|
|
18518
|
+
const sourceChain = resolveChainName(trace.params.from.chain);
|
|
18519
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.CLAIM_REWARDS, config, {
|
|
18520
|
+
...sourceChain != null && {
|
|
18521
|
+
sourceChain
|
|
18522
|
+
},
|
|
18523
|
+
txHash: result.txHash
|
|
18524
|
+
});
|
|
18525
|
+
}
|
|
18526
|
+
return;
|
|
18527
|
+
}
|
|
18528
|
+
default:
|
|
18529
|
+
{
|
|
18530
|
+
const exhaustive = trace;
|
|
18531
|
+
throw createValidationFailedError('error.cause.trace', exhaustive, 'EarnKit.retry() does not support this earn operation');
|
|
18532
|
+
}
|
|
18533
|
+
}
|
|
18534
|
+
}
|
|
17628
18535
|
/**
|
|
17629
18536
|
* A high-level class-based interface for DeFi lending vault operations.
|
|
17630
18537
|
*
|
|
@@ -17683,6 +18590,8 @@ function formatRetryResult(operation, result) {
|
|
|
17683
18590
|
* ```
|
|
17684
18591
|
*/ class EarnKit {
|
|
17685
18592
|
context;
|
|
18593
|
+
/** Per-kit identity and opt-out state for error telemetry. */ telemetryConfig;
|
|
18594
|
+
/** Per-kit identity and opt-out state for success telemetry. */ analyticsTelemetryConfig;
|
|
17686
18595
|
/**
|
|
17687
18596
|
* Event dispatcher for step-level events emitted during multi-phase earn
|
|
17688
18597
|
* operations. Prefer {@link EarnKit.on} / {@link EarnKit.off} over using
|
|
@@ -17707,6 +18616,16 @@ function formatRetryResult(operation, result) {
|
|
|
17707
18616
|
*/ constructor(config = {}){
|
|
17708
18617
|
this.context = createEarnKitContext(config);
|
|
17709
18618
|
this.actionDispatcher = new Actionable();
|
|
18619
|
+
this.telemetryConfig = {
|
|
18620
|
+
sdkName: SDK_NAME,
|
|
18621
|
+
sdkVersion: pkg$1.version,
|
|
18622
|
+
disabled: config.disableErrorReporting === true
|
|
18623
|
+
};
|
|
18624
|
+
this.analyticsTelemetryConfig = {
|
|
18625
|
+
sdkName: SDK_NAME,
|
|
18626
|
+
sdkVersion: pkg$1.version,
|
|
18627
|
+
disabled: config.disableAnalytics === true
|
|
18628
|
+
};
|
|
17710
18629
|
for (const provider of this.context.providers){
|
|
17711
18630
|
provider.registerDispatcher(this.actionDispatcher);
|
|
17712
18631
|
}
|
|
@@ -17767,29 +18686,36 @@ function formatRetryResult(operation, result) {
|
|
|
17767
18686
|
* }
|
|
17768
18687
|
* ```
|
|
17769
18688
|
*/ async retry(error) {
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
18689
|
+
const result = await withErrorTelemetry(async ()=>{
|
|
18690
|
+
if (!isKitError(error)) {
|
|
18691
|
+
throw createValidationFailedError('error', error, 'EarnKit.retry() requires a KitError thrown by a previous earn operation');
|
|
18692
|
+
}
|
|
18693
|
+
if (!isRetryableError$1(error)) {
|
|
18694
|
+
throw createValidationFailedError('error.recoverability', error.recoverability, 'EarnKit.retry() requires a retryable or resumable error — check isRetryableError(error) first');
|
|
18695
|
+
}
|
|
18696
|
+
const trace = error.cause?.trace;
|
|
18697
|
+
if (!isEarnErrorTrace(trace)) {
|
|
18698
|
+
throw createValidationFailedError('error.cause.trace', trace, 'EarnKit.retry() requires a KitError carrying earn retry context (operation, steps, provider, params)');
|
|
18699
|
+
}
|
|
18700
|
+
const provider = this.context.providers.find((candidate)=>candidate.name === trace.provider);
|
|
18701
|
+
if (provider === undefined) {
|
|
18702
|
+
throw createValidationFailedError('error.cause.trace.provider', trace.provider, `No earn provider named "${trace.provider}" is registered with this kit`);
|
|
18703
|
+
}
|
|
18704
|
+
const result = await provider.retry(error);
|
|
18705
|
+
// `provider.retry` returns a flat result union with no compile-time link to
|
|
18706
|
+
// `trace.operation`, so narrow the operation here to select the matching
|
|
18707
|
+
// overload. The result cast in each branch is sound: the provider always
|
|
18708
|
+
// returns the result type corresponding to the resumed operation.
|
|
18709
|
+
if (trace.operation === 'claimRewards') {
|
|
18710
|
+
return formatRetryResult(trace.operation, result);
|
|
18711
|
+
}
|
|
17790
18712
|
return formatRetryResult(trace.operation, result);
|
|
18713
|
+
}, EARN_EVENT_TYPES.RETRY, this.telemetryConfig);
|
|
18714
|
+
const trace = isKitError(error) ? error.cause?.trace : undefined;
|
|
18715
|
+
if (isEarnErrorTrace(trace)) {
|
|
18716
|
+
emitRetrySuccessTelemetry(trace, result, this.analyticsTelemetryConfig);
|
|
17791
18717
|
}
|
|
17792
|
-
return
|
|
18718
|
+
return result;
|
|
17793
18719
|
}
|
|
17794
18720
|
/**
|
|
17795
18721
|
* Return the chains supported by configured earn providers.
|
|
@@ -17823,7 +18749,9 @@ function formatRetryResult(operation, result) {
|
|
|
17823
18749
|
* result.vaults.forEach(v => console.log(`${v.name}: ${(v.currentApy * 100).toFixed(2)}% APY`))
|
|
17824
18750
|
* ```
|
|
17825
18751
|
*/ async getVaults(params) {
|
|
17826
|
-
|
|
18752
|
+
const result = await withErrorTelemetry(async ()=>getVaults$1(this.context, params), EARN_EVENT_TYPES.GET_VAULTS, this.telemetryConfig);
|
|
18753
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.GET_VAULTS, this.analyticsTelemetryConfig, {});
|
|
18754
|
+
return result;
|
|
17827
18755
|
}
|
|
17828
18756
|
/**
|
|
17829
18757
|
* Discover vaults available on a chain.
|
|
@@ -17848,7 +18776,12 @@ function formatRetryResult(operation, result) {
|
|
|
17848
18776
|
* const guarded = result.vaults.filter(v => v.circleGuarded) // Circle-guarded vaults only
|
|
17849
18777
|
* ```
|
|
17850
18778
|
*/ async exploreVaults(params) {
|
|
17851
|
-
|
|
18779
|
+
const context = {
|
|
18780
|
+
sourceChain: resolveChainName(params.chain)
|
|
18781
|
+
};
|
|
18782
|
+
const result = await withErrorTelemetry(async ()=>exploreVaults$1(this.context, params), EARN_EVENT_TYPES.EXPLORE_VAULTS, this.telemetryConfig, context);
|
|
18783
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.EXPLORE_VAULTS, this.analyticsTelemetryConfig, context);
|
|
18784
|
+
return result;
|
|
17852
18785
|
}
|
|
17853
18786
|
/**
|
|
17854
18787
|
* Lazily iterate every vault available on a chain.
|
|
@@ -17895,7 +18828,9 @@ function formatRetryResult(operation, result) {
|
|
|
17895
18828
|
* }
|
|
17896
18829
|
* ```
|
|
17897
18830
|
*/ async getPosition(params) {
|
|
17898
|
-
return getPosition$1(this.context, params)
|
|
18831
|
+
return withErrorTelemetry(async ()=>getPosition$1(this.context, params), EARN_EVENT_TYPES.GET_POSITION, this.telemetryConfig, {
|
|
18832
|
+
sourceChain: resolveChainName(params.from.chain)
|
|
18833
|
+
});
|
|
17899
18834
|
}
|
|
17900
18835
|
/**
|
|
17901
18836
|
* Fetch the current status of a cross-chain deposit by execution ID.
|
|
@@ -17920,7 +18855,7 @@ function formatRetryResult(operation, result) {
|
|
|
17920
18855
|
* console.log(`Bridge ${status.execId} is ${status.status}`)
|
|
17921
18856
|
* ```
|
|
17922
18857
|
*/ async getCrossChainDepositStatus(params) {
|
|
17923
|
-
return getCrossChainDepositStatus$1(this.context, params);
|
|
18858
|
+
return withErrorTelemetry(async ()=>getCrossChainDepositStatus$1(this.context, params), EARN_EVENT_TYPES.GET_CROSS_CHAIN_DEPOSIT_STATUS, this.telemetryConfig);
|
|
17924
18859
|
}
|
|
17925
18860
|
/**
|
|
17926
18861
|
* Poll a cross-chain deposit until it reaches a terminal bridge state.
|
|
@@ -17947,10 +18882,30 @@ function formatRetryResult(operation, result) {
|
|
|
17947
18882
|
* console.log(`Bridge ended as ${result.outcome}`)
|
|
17948
18883
|
* ```
|
|
17949
18884
|
*/ async waitForCrossChainDeposit(params) {
|
|
17950
|
-
return waitForCrossChainDeposit$1(this.context, params);
|
|
18885
|
+
return withErrorTelemetry(async ()=>waitForCrossChainDeposit$1(this.context, params), EARN_EVENT_TYPES.WAIT_FOR_CROSS_CHAIN_DEPOSIT, this.telemetryConfig);
|
|
17951
18886
|
}
|
|
17952
18887
|
async deposit(params) {
|
|
17953
|
-
|
|
18888
|
+
const isCrossChain = isCrossChainDeposit(params);
|
|
18889
|
+
const context = {
|
|
18890
|
+
sourceChain: resolveChainName(params.from.chain),
|
|
18891
|
+
...isCrossChain && {
|
|
18892
|
+
destinationChain: resolveChainName(params.to.chain)
|
|
18893
|
+
}
|
|
18894
|
+
};
|
|
18895
|
+
const eventType = isCrossChain ? EARN_EVENT_TYPES.CROSS_CHAIN_DEPOSIT : EARN_EVENT_TYPES.DEPOSIT;
|
|
18896
|
+
const result = await withErrorTelemetry(async ()=>deposit$1(this.context, params), eventType, this.telemetryConfig, context);
|
|
18897
|
+
if (result.kind === 'cross-chain') {
|
|
18898
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.CROSS_CHAIN_DEPOSIT, this.analyticsTelemetryConfig, {
|
|
18899
|
+
sourceChain: resolveChainName(result.sourceChain),
|
|
18900
|
+
destinationChain: resolveChainName(result.destinationChain)
|
|
18901
|
+
});
|
|
18902
|
+
} else {
|
|
18903
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.DEPOSIT, this.analyticsTelemetryConfig, {
|
|
18904
|
+
...context,
|
|
18905
|
+
txHash: result.txHash
|
|
18906
|
+
});
|
|
18907
|
+
}
|
|
18908
|
+
return result;
|
|
17954
18909
|
}
|
|
17955
18910
|
/**
|
|
17956
18911
|
* Execute a withdrawal from a DeFi lending vault.
|
|
@@ -17976,7 +18931,15 @@ function formatRetryResult(operation, result) {
|
|
|
17976
18931
|
* console.log(`Withdrew ${result.amount} from ${result.vaultAddress}, tx: ${result.txHash}`)
|
|
17977
18932
|
* ```
|
|
17978
18933
|
*/ async withdraw(params) {
|
|
17979
|
-
|
|
18934
|
+
const context = {
|
|
18935
|
+
sourceChain: resolveChainName(params.from.chain)
|
|
18936
|
+
};
|
|
18937
|
+
const result = await withErrorTelemetry(async ()=>withdraw$1(this.context, params), EARN_EVENT_TYPES.WITHDRAW, this.telemetryConfig, context);
|
|
18938
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.WITHDRAW, this.analyticsTelemetryConfig, {
|
|
18939
|
+
...context,
|
|
18940
|
+
txHash: result.txHash
|
|
18941
|
+
});
|
|
18942
|
+
return result;
|
|
17980
18943
|
}
|
|
17981
18944
|
/**
|
|
17982
18945
|
* Claim rewards from earn vaults.
|
|
@@ -18003,7 +18966,17 @@ function formatRetryResult(operation, result) {
|
|
|
18003
18966
|
*
|
|
18004
18967
|
* @internal
|
|
18005
18968
|
*/ async claimRewards(params) {
|
|
18006
|
-
|
|
18969
|
+
const context = {
|
|
18970
|
+
sourceChain: resolveChainName(params.from.chain)
|
|
18971
|
+
};
|
|
18972
|
+
const result = await withErrorTelemetry(async ()=>claimRewards$1(this.context, params), EARN_EVENT_TYPES.CLAIM_REWARDS, this.telemetryConfig, context);
|
|
18973
|
+
if (result.status === 'claimed') {
|
|
18974
|
+
emitSuccessTelemetry(EARN_EVENT_TYPES.CLAIM_REWARDS, this.analyticsTelemetryConfig, {
|
|
18975
|
+
...context,
|
|
18976
|
+
txHash: result.txHash
|
|
18977
|
+
});
|
|
18978
|
+
}
|
|
18979
|
+
return result;
|
|
18007
18980
|
}
|
|
18008
18981
|
/**
|
|
18009
18982
|
* Get an informational quote for a deposit into a vault.
|
|
@@ -18025,7 +18998,9 @@ function formatRetryResult(operation, result) {
|
|
|
18025
18998
|
* console.log(`Expected shares: ${quote.expectedShares.amount}`)
|
|
18026
18999
|
* ```
|
|
18027
19000
|
*/ async getDepositQuote(params) {
|
|
18028
|
-
return getDepositQuote$1(this.context, params)
|
|
19001
|
+
return withErrorTelemetry(async ()=>getDepositQuote$1(this.context, params), EARN_EVENT_TYPES.GET_DEPOSIT_QUOTE, this.telemetryConfig, {
|
|
19002
|
+
sourceChain: resolveChainName(params.from.chain)
|
|
19003
|
+
});
|
|
18029
19004
|
}
|
|
18030
19005
|
/**
|
|
18031
19006
|
* Get an informational quote for a withdrawal from a vault.
|
|
@@ -18047,7 +19022,9 @@ function formatRetryResult(operation, result) {
|
|
|
18047
19022
|
* console.log(`Shares to redeem: ${quote.sharesToRedeem.amount}`)
|
|
18048
19023
|
* ```
|
|
18049
19024
|
*/ async getWithdrawalQuote(params) {
|
|
18050
|
-
return getWithdrawalQuote$1(this.context, params)
|
|
19025
|
+
return withErrorTelemetry(async ()=>getWithdrawalQuote$1(this.context, params), EARN_EVENT_TYPES.GET_WITHDRAWAL_QUOTE, this.telemetryConfig, {
|
|
19026
|
+
sourceChain: resolveChainName(params.from.chain)
|
|
19027
|
+
});
|
|
18051
19028
|
}
|
|
18052
19029
|
/**
|
|
18053
19030
|
* Get an informational quote for claiming rewards.
|
|
@@ -18069,34 +19046,111 @@ function formatRetryResult(operation, result) {
|
|
|
18069
19046
|
*
|
|
18070
19047
|
* @internal
|
|
18071
19048
|
*/ async getClaimRewardsQuote(params) {
|
|
18072
|
-
return getClaimRewardsQuote$1(this.context, params)
|
|
19049
|
+
return withErrorTelemetry(async ()=>getClaimRewardsQuote$1(this.context, params), EARN_EVENT_TYPES.GET_CLAIM_REWARDS_QUOTE, this.telemetryConfig, {
|
|
19050
|
+
sourceChain: resolveChainName(params.from.chain)
|
|
19051
|
+
});
|
|
18073
19052
|
}
|
|
18074
19053
|
}
|
|
18075
19054
|
|
|
18076
19055
|
// Auto-register this kit for user agent tracking
|
|
18077
19056
|
registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
18078
19057
|
|
|
19058
|
+
/**
|
|
19059
|
+
* Register event handlers from a context actions map to a kit instance.
|
|
19060
|
+
*
|
|
19061
|
+
* This utility function registers event handlers stored in a context actions map
|
|
19062
|
+
* with a kit instance that supports event handling via an `on` method. It handles
|
|
19063
|
+
* wildcard handlers ('*') and prefixed action handlers, stripping the prefix
|
|
19064
|
+
* before registration.
|
|
19065
|
+
*
|
|
19066
|
+
* The function is designed to be reusable across different operation types
|
|
19067
|
+
* (bridge, swap, stake, etc.) by accepting a configurable prefix parameter.
|
|
19068
|
+
*
|
|
19069
|
+
* @param kit - The kit instance to register handlers with (must have an `on` method)
|
|
19070
|
+
* @param handlers - Map of action names to arrays of handler functions
|
|
19071
|
+
* @param prefix - Optional prefix to strip from action names (e.g., 'bridge.')
|
|
19072
|
+
*
|
|
19073
|
+
* @example
|
|
19074
|
+
* ```typescript
|
|
19075
|
+
* import { registerActionHandlers } from '@circle-fin/app-kit/utils'
|
|
19076
|
+
* import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
19077
|
+
*
|
|
19078
|
+
* const kit = new BridgeKit()
|
|
19079
|
+
* const handlers = {
|
|
19080
|
+
* '*': [(payload) => console.log('All actions:', payload)],
|
|
19081
|
+
* 'bridge.approve': [(payload) => console.log('Approved:', payload)],
|
|
19082
|
+
* 'bridge.burn': [(payload) => console.log('Burned:', payload)],
|
|
19083
|
+
* }
|
|
19084
|
+
*
|
|
19085
|
+
* registerActionHandlers(kit, handlers, 'bridge.')
|
|
19086
|
+
* ```
|
|
19087
|
+
*
|
|
19088
|
+
* @example
|
|
19089
|
+
* ```typescript
|
|
19090
|
+
* import { registerActionHandlers } from '@circle-fin/app-kit/utils'
|
|
19091
|
+
* import { SwapKit } from '@circle-fin/swap-kit'
|
|
19092
|
+
*
|
|
19093
|
+
* const kit = new SwapKit()
|
|
19094
|
+
* const handlers = {
|
|
19095
|
+
* 'swap.initiate': [(payload) => console.log('Swap initiated:', payload)],
|
|
19096
|
+
* }
|
|
19097
|
+
*
|
|
19098
|
+
* registerActionHandlers(kit, handlers, 'swap.')
|
|
19099
|
+
* ```
|
|
19100
|
+
*/ const registerActionHandlers = (kit, handlers, prefix = '')=>{
|
|
19101
|
+
for (const [action, handlerArray] of Object.entries(handlers)){
|
|
19102
|
+
// Register all handlers for this action
|
|
19103
|
+
for (const handler of handlerArray){
|
|
19104
|
+
if (action === '*') {
|
|
19105
|
+
// Wildcard handlers are registered as-is
|
|
19106
|
+
kit.on('*', handler);
|
|
19107
|
+
} else if (prefix && action.startsWith(prefix)) {
|
|
19108
|
+
// Remove prefix to get the actual kit action name
|
|
19109
|
+
const kitAction = action.split('.').at(1);
|
|
19110
|
+
if (kitAction) {
|
|
19111
|
+
kit.on(kitAction, handler);
|
|
19112
|
+
}
|
|
19113
|
+
} else if (!prefix) {
|
|
19114
|
+
// No prefix configured, register action as-is
|
|
19115
|
+
kit.on(action, handler);
|
|
19116
|
+
}
|
|
19117
|
+
// Actions that don't match the prefix are silently ignored
|
|
19118
|
+
}
|
|
19119
|
+
}
|
|
19120
|
+
};
|
|
19121
|
+
|
|
18079
19122
|
/**
|
|
18080
19123
|
* Create an EarnKit instance for AppKit earn operations.
|
|
18081
19124
|
*
|
|
18082
|
-
*
|
|
18083
|
-
*
|
|
18084
|
-
*
|
|
18085
|
-
* reserved until EarnKit fee support ships.
|
|
19125
|
+
* Attaches any earn event handlers previously registered on the AppKit
|
|
19126
|
+
* context (via `kit.on('earn.*', …)` or `kit.on('*', …)`) so step events
|
|
19127
|
+
* fire during the returned kit's earn operations.
|
|
18086
19128
|
*
|
|
18087
|
-
*
|
|
19129
|
+
* @remarks Developer fee hooks from the AppKit context are not applied.
|
|
19130
|
+
* EarnKit does not yet support custom fee policies.
|
|
18088
19131
|
*
|
|
18089
|
-
* @param context - AppKit context
|
|
18090
|
-
* @returns
|
|
19132
|
+
* @param context - AppKit context with earn event handlers and kit options
|
|
19133
|
+
* @returns An EarnKit instance ready for AppKit earn operations
|
|
18091
19134
|
*
|
|
18092
19135
|
* @example
|
|
18093
19136
|
* ```typescript
|
|
18094
19137
|
* const earnKit = createEarnKit(context)
|
|
18095
19138
|
* ```
|
|
18096
|
-
*/ const createEarnKit = ()=>
|
|
19139
|
+
*/ const createEarnKit = (context)=>{
|
|
19140
|
+
const kit = new EarnKit({
|
|
19141
|
+
...context.disableErrorReporting != null && {
|
|
19142
|
+
disableErrorReporting: context.disableErrorReporting
|
|
19143
|
+
},
|
|
19144
|
+
...context.disableAnalytics != null && {
|
|
19145
|
+
disableAnalytics: context.disableAnalytics
|
|
19146
|
+
}
|
|
19147
|
+
});
|
|
19148
|
+
registerActionHandlers(kit, context.actions.earn, 'earn');
|
|
19149
|
+
return kit;
|
|
19150
|
+
};
|
|
18097
19151
|
|
|
18098
19152
|
async function deposit(context, params) {
|
|
18099
|
-
return createEarnKit().deposit(params);
|
|
19153
|
+
return createEarnKit(context).deposit(params);
|
|
18100
19154
|
}
|
|
18101
19155
|
/**
|
|
18102
19156
|
* Execute an earn withdrawal operation.
|
|
@@ -18120,7 +19174,7 @@ async function deposit(context, params) {
|
|
|
18120
19174
|
* })
|
|
18121
19175
|
* ```
|
|
18122
19176
|
*/ async function withdraw(context, params) {
|
|
18123
|
-
return createEarnKit().withdraw(params);
|
|
19177
|
+
return createEarnKit(context).withdraw(params);
|
|
18124
19178
|
}
|
|
18125
19179
|
/**
|
|
18126
19180
|
* Claim earn rewards.
|
|
@@ -18143,7 +19197,7 @@ async function deposit(context, params) {
|
|
|
18143
19197
|
* })
|
|
18144
19198
|
* ```
|
|
18145
19199
|
*/ async function claimRewards(context, params) {
|
|
18146
|
-
return createEarnKit().claimRewards(params);
|
|
19200
|
+
return createEarnKit(context).claimRewards(params);
|
|
18147
19201
|
}
|
|
18148
19202
|
/**
|
|
18149
19203
|
* Fetch vault information.
|
|
@@ -18165,7 +19219,7 @@ async function deposit(context, params) {
|
|
|
18165
19219
|
* })
|
|
18166
19220
|
* ```
|
|
18167
19221
|
*/ async function getVaults(context, params) {
|
|
18168
|
-
return createEarnKit().getVaults(params);
|
|
19222
|
+
return createEarnKit(context).getVaults(params);
|
|
18169
19223
|
}
|
|
18170
19224
|
/**
|
|
18171
19225
|
* Discover vaults available on a chain.
|
|
@@ -18189,7 +19243,7 @@ async function deposit(context, params) {
|
|
|
18189
19243
|
* })
|
|
18190
19244
|
* ```
|
|
18191
19245
|
*/ async function exploreVaults(context, params) {
|
|
18192
|
-
return createEarnKit().exploreVaults(params);
|
|
19246
|
+
return createEarnKit(context).exploreVaults(params);
|
|
18193
19247
|
}
|
|
18194
19248
|
/**
|
|
18195
19249
|
* Lazily iterate every vault available on a chain.
|
|
@@ -18213,7 +19267,7 @@ async function deposit(context, params) {
|
|
|
18213
19267
|
* }
|
|
18214
19268
|
* ```
|
|
18215
19269
|
*/ function exploreVaultsIterator(context, params) {
|
|
18216
|
-
return createEarnKit().exploreVaultsIterator(params);
|
|
19270
|
+
return createEarnKit(context).exploreVaultsIterator(params);
|
|
18217
19271
|
}
|
|
18218
19272
|
/**
|
|
18219
19273
|
* Fetch a wallet position in a vault.
|
|
@@ -18236,7 +19290,7 @@ async function deposit(context, params) {
|
|
|
18236
19290
|
* })
|
|
18237
19291
|
* ```
|
|
18238
19292
|
*/ async function getPosition(context, params) {
|
|
18239
|
-
return createEarnKit().getPosition(params);
|
|
19293
|
+
return createEarnKit(context).getPosition(params);
|
|
18240
19294
|
}
|
|
18241
19295
|
/**
|
|
18242
19296
|
* Fetch the current status of a cross-chain Earn deposit.
|
|
@@ -18258,7 +19312,7 @@ async function deposit(context, params) {
|
|
|
18258
19312
|
* console.log(status.status)
|
|
18259
19313
|
* ```
|
|
18260
19314
|
*/ async function getCrossChainDepositStatus(context, params) {
|
|
18261
|
-
return createEarnKit().getCrossChainDepositStatus(params);
|
|
19315
|
+
return createEarnKit(context).getCrossChainDepositStatus(params);
|
|
18262
19316
|
}
|
|
18263
19317
|
/**
|
|
18264
19318
|
* Poll a cross-chain Earn deposit until it reaches a terminal bridge state.
|
|
@@ -18281,7 +19335,7 @@ async function deposit(context, params) {
|
|
|
18281
19335
|
* console.log(result.outcome)
|
|
18282
19336
|
* ```
|
|
18283
19337
|
*/ async function waitForCrossChainDeposit(context, params) {
|
|
18284
|
-
return createEarnKit().waitForCrossChainDeposit(params);
|
|
19338
|
+
return createEarnKit(context).waitForCrossChainDeposit(params);
|
|
18285
19339
|
}
|
|
18286
19340
|
/**
|
|
18287
19341
|
* Fetch a deposit quote.
|
|
@@ -18305,7 +19359,7 @@ async function deposit(context, params) {
|
|
|
18305
19359
|
* })
|
|
18306
19360
|
* ```
|
|
18307
19361
|
*/ async function getDepositQuote(context, params) {
|
|
18308
|
-
return createEarnKit().getDepositQuote(params);
|
|
19362
|
+
return createEarnKit(context).getDepositQuote(params);
|
|
18309
19363
|
}
|
|
18310
19364
|
/**
|
|
18311
19365
|
* Fetch a withdrawal quote.
|
|
@@ -18329,7 +19383,7 @@ async function deposit(context, params) {
|
|
|
18329
19383
|
* })
|
|
18330
19384
|
* ```
|
|
18331
19385
|
*/ async function getWithdrawalQuote(context, params) {
|
|
18332
|
-
return createEarnKit().getWithdrawalQuote(params);
|
|
19386
|
+
return createEarnKit(context).getWithdrawalQuote(params);
|
|
18333
19387
|
}
|
|
18334
19388
|
/**
|
|
18335
19389
|
* Fetch a claim rewards quote.
|
|
@@ -18352,8 +19406,44 @@ async function deposit(context, params) {
|
|
|
18352
19406
|
* })
|
|
18353
19407
|
* ```
|
|
18354
19408
|
*/ async function getClaimRewardsQuote(context, params) {
|
|
18355
|
-
return createEarnKit().getClaimRewardsQuote(params);
|
|
19409
|
+
return createEarnKit(context).getClaimRewardsQuote(params);
|
|
19410
|
+
}
|
|
19411
|
+
/**
|
|
19412
|
+
* Resume a multi-phase earn operation that previously failed.
|
|
19413
|
+
*
|
|
19414
|
+
* Pass the {@link KitError} caught from `deposit`, `withdraw`, or
|
|
19415
|
+
* `claimRewards`. Completed phases can be skipped when the error carries
|
|
19416
|
+
* earn retry context. Call `isRetryableError(error)` first.
|
|
19417
|
+
*
|
|
19418
|
+
* @remarks
|
|
19419
|
+
* Retry re-fetches execution params and may re-submit the execute
|
|
19420
|
+
* transaction. Treat this as best-effort recovery if a prior execute
|
|
19421
|
+
* broadcast may still be in flight.
|
|
19422
|
+
*
|
|
19423
|
+
* @param context - AppKit context
|
|
19424
|
+
* @param error - The error caught from a previous multi-phase earn operation
|
|
19425
|
+
* @returns Promise resolving to the result of the resumed operation
|
|
19426
|
+
* @throws If the error is not retryable or lacks earn retry context
|
|
19427
|
+
*
|
|
19428
|
+
* @example
|
|
19429
|
+
* ```typescript
|
|
19430
|
+
* import { isRetryableError } from '@circle-fin/app-kit'
|
|
19431
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
19432
|
+
* import { retry } from '@circle-fin/app-kit/earn'
|
|
19433
|
+
*
|
|
19434
|
+
* const context = createContext()
|
|
19435
|
+
*
|
|
19436
|
+
* try {
|
|
19437
|
+
* await deposit(context, params)
|
|
19438
|
+
* } catch (error) {
|
|
19439
|
+
* if (isRetryableError(error)) {
|
|
19440
|
+
* const result = await retry(context, error)
|
|
19441
|
+
* }
|
|
19442
|
+
* }
|
|
19443
|
+
* ```
|
|
19444
|
+
*/ async function retry(context, error) {
|
|
19445
|
+
return createEarnKit(context).retry(error);
|
|
18356
19446
|
}
|
|
18357
19447
|
|
|
18358
|
-
export { claimRewards, deposit, exploreVaults, exploreVaultsIterator, getClaimRewardsQuote, getCrossChainDepositStatus, getDepositQuote, getPosition, getVaults, getWithdrawalQuote, waitForCrossChainDeposit, withdraw };
|
|
19448
|
+
export { claimRewards, deposit, exploreVaults, exploreVaultsIterator, getClaimRewardsQuote, getCrossChainDepositStatus, getDepositQuote, getPosition, getVaults, getWithdrawalQuote, retry, waitForCrossChainDeposit, withdraw };
|
|
18359
19449
|
//# sourceMappingURL=earn.mjs.map
|