@circle-fin/app-kit 1.5.0 → 1.6.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 +12 -0
- package/README.md +81 -33
- package/chains.cjs +8 -10
- package/chains.mjs +8 -10
- package/earn.cjs +14828 -0
- package/earn.cjs.map +1 -0
- package/earn.d.ts +7438 -0
- package/earn.mjs +14819 -0
- package/earn.mjs.map +1 -0
- package/index.cjs +3857 -261
- package/index.d.ts +6750 -2849
- package/index.mjs +3831 -255
- package/package.json +12 -5
package/index.mjs
CHANGED
|
@@ -1067,6 +1067,35 @@ function createUnsupportedSwapRouteError(tokenIn, tokenOut, chain) {
|
|
|
1067
1067
|
};
|
|
1068
1068
|
return new KitError(errorDetails);
|
|
1069
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Creates error for unsupported earn route.
|
|
1072
|
+
*
|
|
1073
|
+
* This error is thrown when no available earn provider supports the requested
|
|
1074
|
+
* operation on the specified chain.
|
|
1075
|
+
*
|
|
1076
|
+
* @param operation - Earn operation name
|
|
1077
|
+
* @param chain - Chain name where the earn operation was attempted
|
|
1078
|
+
* @returns KitError with specific earn route details
|
|
1079
|
+
*
|
|
1080
|
+
* @example
|
|
1081
|
+
* ```typescript
|
|
1082
|
+
* import { createUnsupportedEarnRouteError } from '@core/errors'
|
|
1083
|
+
*
|
|
1084
|
+
* throw createUnsupportedEarnRouteError('deposit', 'Ethereum')
|
|
1085
|
+
* // Message: "Earn deposit on Ethereum is not supported by any available provider."
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
function createUnsupportedEarnRouteError(operation, chain) {
|
|
1089
|
+
const errorDetails = {
|
|
1090
|
+
...InputError.UNSUPPORTED_ROUTE,
|
|
1091
|
+
recoverability: 'FATAL',
|
|
1092
|
+
message: `Earn ${operation} on ${chain} is not supported by any available provider.`,
|
|
1093
|
+
cause: {
|
|
1094
|
+
trace: { operation, chain },
|
|
1095
|
+
},
|
|
1096
|
+
};
|
|
1097
|
+
return new KitError(errorDetails);
|
|
1098
|
+
}
|
|
1070
1099
|
/**
|
|
1071
1100
|
* Creates error for unsupported token on chain.
|
|
1072
1101
|
*
|
|
@@ -3210,7 +3239,7 @@ function handleTimeoutError(serviceName, operation, error) {
|
|
|
3210
3239
|
* @returns `true` when `error.responseBody` is a non-null object
|
|
3211
3240
|
* @internal
|
|
3212
3241
|
*/
|
|
3213
|
-
function hasResponseBody(error) {
|
|
3242
|
+
function hasResponseBody$1(error) {
|
|
3214
3243
|
return (typeof error === 'object' &&
|
|
3215
3244
|
error !== null &&
|
|
3216
3245
|
'responseBody' in error &&
|
|
@@ -3223,10 +3252,12 @@ function hasResponseBody(error) {
|
|
|
3223
3252
|
*
|
|
3224
3253
|
* @param error - The raw error from the HTTP layer
|
|
3225
3254
|
* @returns The parsed body cast to {@link ApiErrorResponseBody}, or undefined
|
|
3255
|
+
* @throws Never. Returns `undefined` when the error does not carry a valid
|
|
3256
|
+
* `responseBody`.
|
|
3226
3257
|
* @internal
|
|
3227
3258
|
*/
|
|
3228
3259
|
function extractResponseBody(error) {
|
|
3229
|
-
if (!hasResponseBody(error)) {
|
|
3260
|
+
if (!hasResponseBody$1(error)) {
|
|
3230
3261
|
return undefined;
|
|
3231
3262
|
}
|
|
3232
3263
|
return error.responseBody;
|
|
@@ -3358,6 +3389,224 @@ function extractHttpStatusCode(msg) {
|
|
|
3358
3389
|
return null;
|
|
3359
3390
|
}
|
|
3360
3391
|
|
|
3392
|
+
/**
|
|
3393
|
+
* Standardized error definitions for Earn/Zenith operations.
|
|
3394
|
+
*
|
|
3395
|
+
* These error codes provide fine-grained categorization of failures
|
|
3396
|
+
* from the Zenith earn service, enabling SDK consumers to distinguish
|
|
3397
|
+
* between input errors (fix your request) and service errors (retry later).
|
|
3398
|
+
*
|
|
3399
|
+
* Error code ranges:
|
|
3400
|
+
* - 1100-1104: INPUT errors — invalid inputs, unsupported configurations
|
|
3401
|
+
* - 8100-8103: SERVICE errors — retryable backend/provider failures
|
|
3402
|
+
*
|
|
3403
|
+
* @example
|
|
3404
|
+
* ```typescript
|
|
3405
|
+
* import { EarnError, isInputError, isRetryableError } from '@circle-fin/earn-kit'
|
|
3406
|
+
*
|
|
3407
|
+
* try {
|
|
3408
|
+
* await earnKit.deposit(params)
|
|
3409
|
+
* } catch (error) {
|
|
3410
|
+
* if (isInputError(error)) {
|
|
3411
|
+
* // Fix the request: vault doesn't exist, chain not supported, etc.
|
|
3412
|
+
* }
|
|
3413
|
+
* if (isRetryableError(error)) {
|
|
3414
|
+
* // Try again: signing timed out, provider temporarily unavailable, etc.
|
|
3415
|
+
* }
|
|
3416
|
+
* }
|
|
3417
|
+
* ```
|
|
3418
|
+
*/
|
|
3419
|
+
const EarnError = {
|
|
3420
|
+
/** The requested vault does not exist for the provided chain. */
|
|
3421
|
+
VAULT_NOT_FOUND: {
|
|
3422
|
+
code: 1100,
|
|
3423
|
+
name: 'EARN_VAULT_NOT_FOUND',
|
|
3424
|
+
type: 'INPUT',
|
|
3425
|
+
},
|
|
3426
|
+
/** The specified blockchain is not supported for earn operations. */
|
|
3427
|
+
UNSUPPORTED_CHAIN: {
|
|
3428
|
+
code: 1101,
|
|
3429
|
+
name: 'EARN_UNSUPPORTED_CHAIN',
|
|
3430
|
+
type: 'INPUT',
|
|
3431
|
+
},
|
|
3432
|
+
/** The vault's underlying asset is not supported. */
|
|
3433
|
+
UNSUPPORTED_VAULT: {
|
|
3434
|
+
code: 1102,
|
|
3435
|
+
name: 'EARN_UNSUPPORTED_VAULT',
|
|
3436
|
+
type: 'INPUT',
|
|
3437
|
+
},
|
|
3438
|
+
/** The submitted signature could not be verified. */
|
|
3439
|
+
SIGNATURE_REJECTED: {
|
|
3440
|
+
code: 1103,
|
|
3441
|
+
name: 'EARN_SIGNATURE_REJECTED',
|
|
3442
|
+
type: 'INPUT',
|
|
3443
|
+
},
|
|
3444
|
+
/** General input validation failure (invalid ID, batch size, etc.). */
|
|
3445
|
+
INVALID_INPUT: {
|
|
3446
|
+
code: 1104,
|
|
3447
|
+
name: 'EARN_INVALID_INPUT',
|
|
3448
|
+
type: 'INPUT',
|
|
3449
|
+
},
|
|
3450
|
+
/** The proxy signing call failed — retryable. */
|
|
3451
|
+
SIGNING_FAILED: {
|
|
3452
|
+
code: 8100,
|
|
3453
|
+
name: 'EARN_SIGNING_FAILED',
|
|
3454
|
+
type: 'SERVICE',
|
|
3455
|
+
},
|
|
3456
|
+
/** An external provider API request failed — retryable. */
|
|
3457
|
+
PROVIDER_ERROR: {
|
|
3458
|
+
code: 8101,
|
|
3459
|
+
name: 'EARN_PROVIDER_ERROR',
|
|
3460
|
+
type: 'SERVICE',
|
|
3461
|
+
},
|
|
3462
|
+
/** Failed to fetch reward data — retryable. */
|
|
3463
|
+
REWARDS_FETCH_FAILED: {
|
|
3464
|
+
code: 8102,
|
|
3465
|
+
name: 'EARN_REWARDS_FETCH_FAILED',
|
|
3466
|
+
type: 'SERVICE',
|
|
3467
|
+
},
|
|
3468
|
+
/** An internal earn service error occurred — retryable. */
|
|
3469
|
+
INTERNAL_ERROR: {
|
|
3470
|
+
code: 8103,
|
|
3471
|
+
name: 'EARN_INTERNAL_ERROR',
|
|
3472
|
+
type: 'SERVICE',
|
|
3473
|
+
},
|
|
3474
|
+
};
|
|
3475
|
+
|
|
3476
|
+
function hasResponseBody(error) {
|
|
3477
|
+
return (typeof error === 'object' &&
|
|
3478
|
+
error !== null &&
|
|
3479
|
+
'responseBody' in error &&
|
|
3480
|
+
typeof error['responseBody'] === 'object' &&
|
|
3481
|
+
error['responseBody'] !== null);
|
|
3482
|
+
}
|
|
3483
|
+
function extractEarnResponseBody(error) {
|
|
3484
|
+
if (!hasResponseBody(error)) {
|
|
3485
|
+
return undefined;
|
|
3486
|
+
}
|
|
3487
|
+
return error.responseBody;
|
|
3488
|
+
}
|
|
3489
|
+
function getOptionalString(value) {
|
|
3490
|
+
return typeof value === 'string' ? value : undefined;
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Maps earn service backend error codes (380xxx) to typed EarnError constants
|
|
3494
|
+
* with correct recoverability.
|
|
3495
|
+
*
|
|
3496
|
+
* INPUT errors (FATAL) — fix your request:
|
|
3497
|
+
* - vault-not-found, unsupported-chain, unsupported-vault,
|
|
3498
|
+
* signature-rejected, invalid-id, invalid-batch-size, position-not-registered,
|
|
3499
|
+
* withdrawal-max-exceeded, insufficient-balance
|
|
3500
|
+
*
|
|
3501
|
+
* SERVICE errors (RETRYABLE) — try again later:
|
|
3502
|
+
* - signing-failed, provider-error, rewards-fetch-failed,
|
|
3503
|
+
* internal-error, vault-refresh-busy, bridge failures
|
|
3504
|
+
*
|
|
3505
|
+
* Unrecognized codes fall through to `parseApiError` for HTTP-status-based
|
|
3506
|
+
* handling.
|
|
3507
|
+
*
|
|
3508
|
+
* @internal
|
|
3509
|
+
*/
|
|
3510
|
+
const EARN_CODE_MAP = new Map([
|
|
3511
|
+
// Common (380_0XX)
|
|
3512
|
+
[380001, { errorDef: EarnError.INVALID_INPUT, recoverability: 'FATAL' }],
|
|
3513
|
+
[
|
|
3514
|
+
380002,
|
|
3515
|
+
{
|
|
3516
|
+
errorDef: RateLimitError.RATE_LIMIT_EXCEEDED,
|
|
3517
|
+
recoverability: 'RETRYABLE',
|
|
3518
|
+
},
|
|
3519
|
+
],
|
|
3520
|
+
// Shared (380_1XX)
|
|
3521
|
+
[380100, { errorDef: EarnError.VAULT_NOT_FOUND, recoverability: 'FATAL' }],
|
|
3522
|
+
[380101, { errorDef: EarnError.SIGNATURE_REJECTED, recoverability: 'FATAL' }],
|
|
3523
|
+
[380102, { errorDef: EarnError.UNSUPPORTED_CHAIN, recoverability: 'FATAL' }],
|
|
3524
|
+
// Vault Discovery (380_3XX)
|
|
3525
|
+
[380300, { errorDef: EarnError.VAULT_NOT_FOUND, recoverability: 'FATAL' }],
|
|
3526
|
+
[380301, { errorDef: EarnError.INVALID_INPUT, recoverability: 'FATAL' }],
|
|
3527
|
+
[380302, { errorDef: EarnError.PROVIDER_ERROR, recoverability: 'RETRYABLE' }],
|
|
3528
|
+
[380303, { errorDef: EarnError.UNSUPPORTED_VAULT, recoverability: 'FATAL' }],
|
|
3529
|
+
// EarnKit (380_4XX)
|
|
3530
|
+
[380400, { errorDef: EarnError.UNSUPPORTED_CHAIN, recoverability: 'FATAL' }],
|
|
3531
|
+
[380401, { errorDef: EarnError.SIGNING_FAILED, recoverability: 'RETRYABLE' }],
|
|
3532
|
+
[
|
|
3533
|
+
380402,
|
|
3534
|
+
{ errorDef: EarnError.REWARDS_FETCH_FAILED, recoverability: 'RETRYABLE' },
|
|
3535
|
+
],
|
|
3536
|
+
[380403, { errorDef: EarnError.INTERNAL_ERROR, recoverability: 'RETRYABLE' }],
|
|
3537
|
+
[380404, { errorDef: EarnError.PROVIDER_ERROR, recoverability: 'RETRYABLE' }],
|
|
3538
|
+
[380405, { errorDef: EarnError.PROVIDER_ERROR, recoverability: 'RETRYABLE' }],
|
|
3539
|
+
[380406, { errorDef: EarnError.INTERNAL_ERROR, recoverability: 'RETRYABLE' }],
|
|
3540
|
+
[380407, { errorDef: EarnError.INVALID_INPUT, recoverability: 'FATAL' }],
|
|
3541
|
+
[380408, { errorDef: EarnError.INVALID_INPUT, recoverability: 'FATAL' }],
|
|
3542
|
+
[380409, { errorDef: EarnError.INVALID_INPUT, recoverability: 'FATAL' }],
|
|
3543
|
+
// Bridge (380_5XX)
|
|
3544
|
+
[380500, { errorDef: EarnError.PROVIDER_ERROR, recoverability: 'RETRYABLE' }],
|
|
3545
|
+
[380501, { errorDef: EarnError.SIGNING_FAILED, recoverability: 'RETRYABLE' }],
|
|
3546
|
+
[380502, { errorDef: EarnError.PROVIDER_ERROR, recoverability: 'RETRYABLE' }],
|
|
3547
|
+
]);
|
|
3548
|
+
/**
|
|
3549
|
+
* Parses Earn Service API errors into typed KitError instances using
|
|
3550
|
+
* earn service backend error codes for fine-grained categorization.
|
|
3551
|
+
*
|
|
3552
|
+
* When the response body contains a recognized earn service backend code
|
|
3553
|
+
* (380xxx), the error is mapped to a specific EarnError constant with the
|
|
3554
|
+
* correct recoverability. The original backend error code is preserved in
|
|
3555
|
+
* `error.cause.trace.earnServiceCode`.
|
|
3556
|
+
*
|
|
3557
|
+
* When the code is unrecognized or the response body is unavailable,
|
|
3558
|
+
* falls through to the generic `parseApiError` for HTTP-status-based handling.
|
|
3559
|
+
*
|
|
3560
|
+
* @param error - The raw error from the API call
|
|
3561
|
+
* @param context - Context information (operation name)
|
|
3562
|
+
* @returns A structured KitError instance
|
|
3563
|
+
* @throws Never — all error paths are caught and returned as a `KitError`
|
|
3564
|
+
* instance
|
|
3565
|
+
*
|
|
3566
|
+
* @example
|
|
3567
|
+
* ```typescript
|
|
3568
|
+
* try {
|
|
3569
|
+
* await earnServiceApi.deposit(params)
|
|
3570
|
+
* } catch (error) {
|
|
3571
|
+
* throw parseEarnApiError(error, { operation: 'deposit' })
|
|
3572
|
+
* }
|
|
3573
|
+
* ```
|
|
3574
|
+
*/
|
|
3575
|
+
function parseEarnApiError(error, context) {
|
|
3576
|
+
// If it's already a KitError, return it as-is
|
|
3577
|
+
if (error instanceof KitError) {
|
|
3578
|
+
return error;
|
|
3579
|
+
}
|
|
3580
|
+
const responseBody = extractEarnResponseBody(error);
|
|
3581
|
+
const earnServiceCode = typeof responseBody?.['code'] === 'number'
|
|
3582
|
+
? responseBody['code']
|
|
3583
|
+
: undefined;
|
|
3584
|
+
if (earnServiceCode !== undefined) {
|
|
3585
|
+
const mapping = EARN_CODE_MAP.get(earnServiceCode);
|
|
3586
|
+
if (mapping !== undefined) {
|
|
3587
|
+
const message = getOptionalString(responseBody?.['externalMessage']) ??
|
|
3588
|
+
getOptionalString(responseBody?.['message']) ??
|
|
3589
|
+
`Earn Service ${context.operation ?? 'API'} failed`;
|
|
3590
|
+
return new KitError({
|
|
3591
|
+
...mapping.errorDef,
|
|
3592
|
+
recoverability: mapping.recoverability,
|
|
3593
|
+
message,
|
|
3594
|
+
cause: {
|
|
3595
|
+
trace: {
|
|
3596
|
+
earnServiceCode,
|
|
3597
|
+
originalError: error,
|
|
3598
|
+
},
|
|
3599
|
+
},
|
|
3600
|
+
});
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
// Fallthrough: unrecognized code or no response body — use HTTP-status-based parsing
|
|
3604
|
+
return parseApiError(error, {
|
|
3605
|
+
...context,
|
|
3606
|
+
service: context.service ?? 'Earn Service',
|
|
3607
|
+
});
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3361
3610
|
// -----------------------------------------------------------------------------
|
|
3362
3611
|
// Blockchain Enum
|
|
3363
3612
|
// -----------------------------------------------------------------------------
|
|
@@ -3717,23 +3966,21 @@ var UnifiedBalanceChain;
|
|
|
3717
3966
|
* Enumeration of blockchains that support earn (vault deposit/withdraw)
|
|
3718
3967
|
* operations through the Earn Kit.
|
|
3719
3968
|
*
|
|
3720
|
-
* Currently only Ethereum mainnet is supported. Additional chains
|
|
3721
|
-
* will be added as vault protocol support expands.
|
|
3722
|
-
*
|
|
3723
3969
|
* @example
|
|
3724
3970
|
* ```typescript
|
|
3725
3971
|
* import { EarnChain } from '@core/chains'
|
|
3726
3972
|
*
|
|
3727
3973
|
* const result = await earnKit.deposit({
|
|
3728
|
-
* from: { adapter, chain: EarnChain.
|
|
3974
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
3729
3975
|
* vaultAddress: '0x...',
|
|
3730
3976
|
* amount: '100',
|
|
3731
3977
|
* })
|
|
3732
3978
|
* ```
|
|
3733
3979
|
*/
|
|
3980
|
+
// Values must match Blockchain enum members exactly.
|
|
3734
3981
|
var EarnChain;
|
|
3735
3982
|
(function (EarnChain) {
|
|
3736
|
-
EarnChain["
|
|
3983
|
+
EarnChain["Arc_Testnet"] = "Arc_Testnet";
|
|
3737
3984
|
})(EarnChain || (EarnChain = {}));
|
|
3738
3985
|
|
|
3739
3986
|
/**
|
|
@@ -7474,7 +7721,7 @@ const bridgeChainIdentifierSchema = z.union([
|
|
|
7474
7721
|
* Zod schema for validating earn-specific chain identifiers.
|
|
7475
7722
|
*
|
|
7476
7723
|
* Validate that the provided chain is supported for earn (vault
|
|
7477
|
-
* deposit/withdraw) operations. Currently only
|
|
7724
|
+
* deposit/withdraw) operations. Currently only Arc Testnet is
|
|
7478
7725
|
* supported.
|
|
7479
7726
|
*
|
|
7480
7727
|
* Accept an EarnChain enum value, a matching string literal, or
|
|
@@ -7483,18 +7730,18 @@ const bridgeChainIdentifierSchema = z.union([
|
|
|
7483
7730
|
* @example
|
|
7484
7731
|
* ```typescript
|
|
7485
7732
|
* import { earnChainIdentifierSchema } from '@core/chains'
|
|
7486
|
-
* import {
|
|
7733
|
+
* import { ArcTestnet, EarnChain } from '@core/chains'
|
|
7487
7734
|
*
|
|
7488
7735
|
* // Valid
|
|
7489
|
-
* earnChainIdentifierSchema.parse(EarnChain.
|
|
7490
|
-
* earnChainIdentifierSchema.parse('
|
|
7491
|
-
* earnChainIdentifierSchema.parse(
|
|
7736
|
+
* earnChainIdentifierSchema.parse(EarnChain.Arc_Testnet)
|
|
7737
|
+
* earnChainIdentifierSchema.parse('Arc_Testnet')
|
|
7738
|
+
* earnChainIdentifierSchema.parse(ArcTestnet)
|
|
7492
7739
|
*
|
|
7493
7740
|
* // Invalid (throws ZodError)
|
|
7494
7741
|
* earnChainIdentifierSchema.parse('Solana')
|
|
7495
7742
|
* ```
|
|
7496
7743
|
*/
|
|
7497
|
-
z.union([
|
|
7744
|
+
const earnChainIdentifierSchema = z.union([
|
|
7498
7745
|
z.string().refine((val) => val in EarnChain, (val) => ({
|
|
7499
7746
|
message: `"${val}" is not a supported earn chain. ` +
|
|
7500
7747
|
`Supported chains: ${Object.values(EarnChain).join(', ')}`,
|
|
@@ -8352,7 +8599,7 @@ function handleAddressError(path, _code, _message, paramsObj) {
|
|
|
8352
8599
|
* Default configuration values for the API polling utility.
|
|
8353
8600
|
* @internal
|
|
8354
8601
|
*/
|
|
8355
|
-
const DEFAULT_CONFIG$
|
|
8602
|
+
const DEFAULT_CONFIG$3 = {
|
|
8356
8603
|
timeout: 2_000,
|
|
8357
8604
|
maxRetries: 10,
|
|
8358
8605
|
retryDelay: 200,
|
|
@@ -8561,10 +8808,10 @@ const isRetryableError = (error) => {
|
|
|
8561
8808
|
const pollApiWithValidation = async (url, method, isValidType, config = {}, body) => {
|
|
8562
8809
|
// Merge headers so that 'User-Agent' is always present and not overwritten
|
|
8563
8810
|
const effectiveConfig = {
|
|
8564
|
-
...DEFAULT_CONFIG$
|
|
8811
|
+
...DEFAULT_CONFIG$3,
|
|
8565
8812
|
...config,
|
|
8566
8813
|
headers: {
|
|
8567
|
-
...DEFAULT_CONFIG$
|
|
8814
|
+
...DEFAULT_CONFIG$3.headers,
|
|
8568
8815
|
...(config.headers ?? {}),
|
|
8569
8816
|
// In browser environments, directly setting the 'User-Agent' or similar headers is restricted and may be ignored or cause errors.
|
|
8570
8817
|
// This is why we use the 'X-User-Agent' header instead.
|
|
@@ -10071,7 +10318,7 @@ z.custom((value) => {
|
|
|
10071
10318
|
* formatAmount({ value: '3141592000000000000', token: 'NATIVE', chain: Ethereum }) // "3.141592"
|
|
10072
10319
|
* ```
|
|
10073
10320
|
*/
|
|
10074
|
-
const formatAmount$
|
|
10321
|
+
const formatAmount$2 = (params) => {
|
|
10075
10322
|
const { value, token, tokens } = params;
|
|
10076
10323
|
// Handle NATIVE token first (chain-specific decimals)
|
|
10077
10324
|
if (token === 'NATIVE') {
|
|
@@ -10791,11 +11038,11 @@ function emitResultStepErrorTelemetry(failedStep, stepEventMap, fallbackEventTyp
|
|
|
10791
11038
|
void emitAnalyticsLog(buildPayload$1(config, stepEntry?.[1] ?? fallbackEventType, errorDetails, context));
|
|
10792
11039
|
}
|
|
10793
11040
|
|
|
10794
|
-
var name$
|
|
10795
|
-
var version$
|
|
10796
|
-
var pkg$
|
|
10797
|
-
name: name$
|
|
10798
|
-
version: version$
|
|
11041
|
+
var name$3 = "@circle-fin/bridge-kit";
|
|
11042
|
+
var version$4 = "1.10.1";
|
|
11043
|
+
var pkg$4 = {
|
|
11044
|
+
name: name$3,
|
|
11045
|
+
version: version$4};
|
|
10799
11046
|
|
|
10800
11047
|
const assertCustomFeePolicySymbol$2 = Symbol('assertCustomFeePolicy');
|
|
10801
11048
|
/**
|
|
@@ -11091,7 +11338,9 @@ const hexStringSchema = z
|
|
|
11091
11338
|
* console.log(result.success) // true
|
|
11092
11339
|
* ```
|
|
11093
11340
|
*/
|
|
11094
|
-
const evmAddressSchema = hexStringSchema
|
|
11341
|
+
const evmAddressSchema = hexStringSchema
|
|
11342
|
+
.refine((value) => value.length === 42, 'EVM address must be exactly 42 characters long (0x + 40 hex characters)')
|
|
11343
|
+
.transform((value) => value);
|
|
11095
11344
|
/**
|
|
11096
11345
|
* Schema for validating transaction hashes.
|
|
11097
11346
|
*
|
|
@@ -11112,6 +11361,29 @@ const evmAddressSchema = hexStringSchema.refine((value) => value.length === 42,
|
|
|
11112
11361
|
* ```
|
|
11113
11362
|
*/
|
|
11114
11363
|
hexStringSchema.refine((value) => value.length === 66, 'Transaction hash must be exactly 66 characters long (0x + 64 hex characters)');
|
|
11364
|
+
/**
|
|
11365
|
+
* Schema for validating EVM signatures.
|
|
11366
|
+
*
|
|
11367
|
+
* This schema validates that a string is a properly formatted 65-byte EVM
|
|
11368
|
+
* signature:
|
|
11369
|
+
* - Must be a valid hex string with '0x' prefix
|
|
11370
|
+
* - Must be exactly 132 characters long (0x + 130 hex characters)
|
|
11371
|
+
*
|
|
11372
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
11373
|
+
*
|
|
11374
|
+
* @example
|
|
11375
|
+
* ```typescript
|
|
11376
|
+
* import { evmSignatureSchema } from '@core/adapter'
|
|
11377
|
+
*
|
|
11378
|
+
* const validSignature = `0x${'ab'.repeat(65)}`
|
|
11379
|
+
*
|
|
11380
|
+
* const result = evmSignatureSchema.safeParse(validSignature)
|
|
11381
|
+
* console.log(result.success) // true
|
|
11382
|
+
* ```
|
|
11383
|
+
*/
|
|
11384
|
+
const evmSignatureSchema = hexStringSchema
|
|
11385
|
+
.refine((value) => value.length === 132, 'EVM signature must be exactly 132 characters long (0x + 130 hex characters)')
|
|
11386
|
+
.transform((value) => value);
|
|
11115
11387
|
/**
|
|
11116
11388
|
* Schema for validating base58-encoded strings.
|
|
11117
11389
|
*
|
|
@@ -11766,7 +12038,7 @@ function createRecipientAddressValidator() {
|
|
|
11766
12038
|
*
|
|
11767
12039
|
* Optionally includes address for developer-controlled adapters.
|
|
11768
12040
|
*/
|
|
11769
|
-
const adapterContextSchema$
|
|
12041
|
+
const adapterContextSchema$7 = z.object({
|
|
11770
12042
|
adapter: adapterSchema$1,
|
|
11771
12043
|
chain: bridgeChainIdentifierSchema,
|
|
11772
12044
|
address: z.string().optional(),
|
|
@@ -11776,7 +12048,7 @@ const adapterContextSchema$6 = z.object({
|
|
|
11776
12048
|
* Contains an explicit recipientAddress along with adapter and chain.
|
|
11777
12049
|
* The address format is validated based on the chain type (EVM or Solana).
|
|
11778
12050
|
*/
|
|
11779
|
-
const bridgeDestinationWithAddressSchema = adapterContextSchema$
|
|
12051
|
+
const bridgeDestinationWithAddressSchema = adapterContextSchema$7
|
|
11780
12052
|
.extend({
|
|
11781
12053
|
recipientAddress: z.string().min(1, 'Recipient address is required'),
|
|
11782
12054
|
useForwarder: z.boolean().optional(),
|
|
@@ -11786,7 +12058,7 @@ const bridgeDestinationWithAddressSchema = adapterContextSchema$6
|
|
|
11786
12058
|
* Schema for validating AdapterContext with optional useForwarder.
|
|
11787
12059
|
* Extends adapterContextSchema with the useForwarder flag.
|
|
11788
12060
|
*/
|
|
11789
|
-
const adapterContextWithForwarderSchema = adapterContextSchema$
|
|
12061
|
+
const adapterContextWithForwarderSchema = adapterContextSchema$7.extend({
|
|
11790
12062
|
useForwarder: z.boolean().optional(),
|
|
11791
12063
|
});
|
|
11792
12064
|
/**
|
|
@@ -11867,7 +12139,7 @@ const bridgeDestinationSchema = z.union([
|
|
|
11867
12139
|
* ```
|
|
11868
12140
|
*/
|
|
11869
12141
|
const bridgeParamsWithChainIdentifierSchema = z.object({
|
|
11870
|
-
from: adapterContextSchema$
|
|
12142
|
+
from: adapterContextSchema$7.strict(),
|
|
11871
12143
|
to: bridgeDestinationSchema,
|
|
11872
12144
|
amount: z
|
|
11873
12145
|
.string()
|
|
@@ -12933,7 +13205,7 @@ function resolveBridgeInvocation$1(invocationMeta) {
|
|
|
12933
13205
|
const bridgeKitCaller = {
|
|
12934
13206
|
type: 'kit',
|
|
12935
13207
|
name: 'BridgeKit',
|
|
12936
|
-
version: pkg$
|
|
13208
|
+
version: pkg$4.version,
|
|
12937
13209
|
};
|
|
12938
13210
|
// Create default runtime and tokens for invocation context resolution
|
|
12939
13211
|
const defaults = {
|
|
@@ -13203,7 +13475,7 @@ async function fetchUsdcFastBurnFee(sourceDomain, destinationDomain, isTestnet)
|
|
|
13203
13475
|
}
|
|
13204
13476
|
// Fetch the fee tiers from the API
|
|
13205
13477
|
const url = buildFastBurnFeeUrl(sourceDomain, destinationDomain, isTestnet);
|
|
13206
|
-
const response = await pollApiGet(url, isFastBurnFeeResponse, DEFAULT_CONFIG$
|
|
13478
|
+
const response = await pollApiGet(url, isFastBurnFeeResponse, DEFAULT_CONFIG$3);
|
|
13207
13479
|
// Validate that we have at least one fee tier
|
|
13208
13480
|
if (response.length === 0) {
|
|
13209
13481
|
throw new KitError({
|
|
@@ -13552,7 +13824,7 @@ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, fi
|
|
|
13552
13824
|
}
|
|
13553
13825
|
// Fetch the fee tiers from the API with forward=true
|
|
13554
13826
|
const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet);
|
|
13555
|
-
const response = await pollApiGet(url, isForwardingFeeResponse, DEFAULT_CONFIG$
|
|
13827
|
+
const response = await pollApiGet(url, isForwardingFeeResponse, DEFAULT_CONFIG$3);
|
|
13556
13828
|
// Validate that we have at least one fee tier
|
|
13557
13829
|
if (response.length === 0) {
|
|
13558
13830
|
throw new KitError({
|
|
@@ -13619,7 +13891,7 @@ const CCTPv2MinFinalityThreshold = {
|
|
|
13619
13891
|
* Default configuration values for the attestation fetcher.
|
|
13620
13892
|
* @internal
|
|
13621
13893
|
*/
|
|
13622
|
-
const DEFAULT_CONFIG$
|
|
13894
|
+
const DEFAULT_CONFIG$2 = {
|
|
13623
13895
|
timeout: 2_000, // 2 seconds
|
|
13624
13896
|
maxRetries: 30 * 20, // 30 * 20 * 2 seconds (from the retry delay) = 20 minutes (to account for slow transfers maximum time based on confirmations)
|
|
13625
13897
|
retryDelay: 2_000, // 2 seconds
|
|
@@ -13790,7 +14062,7 @@ const buildIrisUrl = (sourceDomainId, transactionHash, isTestnet) => {
|
|
|
13790
14062
|
*/
|
|
13791
14063
|
const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {}) => {
|
|
13792
14064
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
13793
|
-
const effectiveConfig = { ...DEFAULT_CONFIG$
|
|
14065
|
+
const effectiveConfig = { ...DEFAULT_CONFIG$2, ...config };
|
|
13794
14066
|
return await pollApiGet(url, isAttestationResponse, effectiveConfig);
|
|
13795
14067
|
};
|
|
13796
14068
|
/**
|
|
@@ -13836,7 +14108,7 @@ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHas
|
|
|
13836
14108
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
13837
14109
|
// Use minimal retries since we're just fetching existing data
|
|
13838
14110
|
const effectiveConfig = {
|
|
13839
|
-
...DEFAULT_CONFIG$
|
|
14111
|
+
...DEFAULT_CONFIG$2,
|
|
13840
14112
|
maxRetries: 3,
|
|
13841
14113
|
...config,
|
|
13842
14114
|
};
|
|
@@ -13901,7 +14173,7 @@ const isReAttestedAttestationResponse = (obj) => {
|
|
|
13901
14173
|
*/
|
|
13902
14174
|
const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {}) => {
|
|
13903
14175
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
13904
|
-
const effectiveConfig = { ...DEFAULT_CONFIG$
|
|
14176
|
+
const effectiveConfig = { ...DEFAULT_CONFIG$2, ...config };
|
|
13905
14177
|
return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
|
|
13906
14178
|
};
|
|
13907
14179
|
/**
|
|
@@ -13976,7 +14248,7 @@ const requestReAttestation = async (nonce, isTestnet, config = {}) => {
|
|
|
13976
14248
|
const url = buildReAttestUrl(nonce, isTestnet);
|
|
13977
14249
|
// Use minimal retries since we're just submitting a request, not polling for state
|
|
13978
14250
|
const effectiveConfig = {
|
|
13979
|
-
...DEFAULT_CONFIG$
|
|
14251
|
+
...DEFAULT_CONFIG$2,
|
|
13980
14252
|
maxRetries: 3,
|
|
13981
14253
|
...config,
|
|
13982
14254
|
};
|
|
@@ -15231,7 +15503,7 @@ const isRelayerMintConfirmed = (obj) => {
|
|
|
15231
15503
|
*/
|
|
15232
15504
|
const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {}) => {
|
|
15233
15505
|
const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
|
|
15234
|
-
const effectiveConfig = { ...DEFAULT_CONFIG$
|
|
15506
|
+
const effectiveConfig = { ...DEFAULT_CONFIG$2, ...config };
|
|
15235
15507
|
let response;
|
|
15236
15508
|
try {
|
|
15237
15509
|
response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
|
|
@@ -15743,9 +16015,9 @@ async function buildBatchedStep(name, receipt, batchId, adapter, chain, statusCo
|
|
|
15743
16015
|
return step;
|
|
15744
16016
|
}
|
|
15745
16017
|
|
|
15746
|
-
var version$
|
|
15747
|
-
var pkg$
|
|
15748
|
-
version: version$
|
|
16018
|
+
var version$3 = "1.8.2";
|
|
16019
|
+
var pkg$3 = {
|
|
16020
|
+
version: version$3};
|
|
15749
16021
|
|
|
15750
16022
|
/**
|
|
15751
16023
|
* Provider caller component for bridge operations.
|
|
@@ -15753,7 +16025,7 @@ var pkg$2 = {
|
|
|
15753
16025
|
const BRIDGE_CALLER = {
|
|
15754
16026
|
type: 'provider',
|
|
15755
16027
|
name: 'CCTPV2BridgingProvider.bridge',
|
|
15756
|
-
version: pkg$
|
|
16028
|
+
version: pkg$3.version,
|
|
15757
16029
|
};
|
|
15758
16030
|
/**
|
|
15759
16031
|
* Resolve invocation context for bridge operations.
|
|
@@ -16133,7 +16405,7 @@ async function bridgeReAttest({ params, provider, }, burnTxHash) {
|
|
|
16133
16405
|
const RETRY_CALLER = {
|
|
16134
16406
|
type: 'provider',
|
|
16135
16407
|
name: 'CCTPV2BridgingProvider.retry',
|
|
16136
|
-
version: pkg$
|
|
16408
|
+
version: pkg$3.version,
|
|
16137
16409
|
};
|
|
16138
16410
|
/**
|
|
16139
16411
|
* Resolve invocation context for retry operations.
|
|
@@ -17339,7 +17611,7 @@ class CCTPV2BridgingProvider extends BridgingProvider {
|
|
|
17339
17611
|
* The default providers that will be used in addition to the providers provided
|
|
17340
17612
|
* to the BridgeKit constructor.
|
|
17341
17613
|
*/
|
|
17342
|
-
const getDefaultProviders$
|
|
17614
|
+
const getDefaultProviders$3 = () => [new CCTPV2BridgingProvider()];
|
|
17343
17615
|
|
|
17344
17616
|
/**
|
|
17345
17617
|
* A helper function to get a function that transforms an amount into a human-readable string or a bigint string.
|
|
@@ -17347,7 +17619,7 @@ const getDefaultProviders$2 = () => [new CCTPV2BridgingProvider()];
|
|
|
17347
17619
|
* @returns A function that transforms an amount into a human-readable string or a bigint string.
|
|
17348
17620
|
*/
|
|
17349
17621
|
const getAmountTransformer = (formatDirection) => formatDirection === 'to-human-readable'
|
|
17350
|
-
? (params) => formatAmount$
|
|
17622
|
+
? (params) => formatAmount$2(params)
|
|
17351
17623
|
: (params) => parseAmount(params).toString();
|
|
17352
17624
|
/**
|
|
17353
17625
|
* Format the bridge result into human-readable string values for the user or bigint string values for internal use.
|
|
@@ -17437,14 +17709,14 @@ const BRIDGE_STEP_EVENT_MAP = [
|
|
|
17437
17709
|
];
|
|
17438
17710
|
|
|
17439
17711
|
/** SDK name used in telemetry payloads. */
|
|
17440
|
-
const SDK_NAME$2 = resolveKitSdkName(pkg$
|
|
17712
|
+
const SDK_NAME$2 = resolveKitSdkName(pkg$4.name);
|
|
17441
17713
|
/**
|
|
17442
17714
|
* BridgeKit caller component for retry and estimate operations.
|
|
17443
17715
|
*/
|
|
17444
17716
|
const BRIDGE_KIT_CALLER = {
|
|
17445
17717
|
type: 'kit',
|
|
17446
17718
|
name: 'BridgeKit',
|
|
17447
|
-
version: pkg$
|
|
17719
|
+
version: pkg$4.version,
|
|
17448
17720
|
};
|
|
17449
17721
|
/**
|
|
17450
17722
|
* Merge BridgeKit's caller into the invocation metadata for retry operations.
|
|
@@ -17539,13 +17811,13 @@ class BridgeKit {
|
|
|
17539
17811
|
*/
|
|
17540
17812
|
constructor(config = {}) {
|
|
17541
17813
|
// Handle provider configuration
|
|
17542
|
-
const defaultProviders = getDefaultProviders$
|
|
17814
|
+
const defaultProviders = getDefaultProviders$3();
|
|
17543
17815
|
this.providers = [...defaultProviders, ...(config.providers ?? [])];
|
|
17544
17816
|
this.actionDispatcher = new Actionable();
|
|
17545
17817
|
this.disableErrorReporting = config.disableErrorReporting === true;
|
|
17546
17818
|
this.telemetryConfig = {
|
|
17547
17819
|
sdkName: SDK_NAME$2,
|
|
17548
|
-
sdkVersion: pkg$
|
|
17820
|
+
sdkVersion: pkg$4.version,
|
|
17549
17821
|
disabled: this.disableErrorReporting,
|
|
17550
17822
|
};
|
|
17551
17823
|
for (const provider of this.providers) {
|
|
@@ -18074,7 +18346,7 @@ class BridgeKit {
|
|
|
18074
18346
|
* @packageDocumentation
|
|
18075
18347
|
*/
|
|
18076
18348
|
// Auto-register this kit for user agent tracking
|
|
18077
|
-
registerKit(`${pkg$
|
|
18349
|
+
registerKit(`${pkg$4.name}/${pkg$4.version}`);
|
|
18078
18350
|
|
|
18079
18351
|
/**
|
|
18080
18352
|
* Create a BridgeKit instance with optional developer fee configuration.
|
|
@@ -18141,11 +18413,11 @@ const createBridgeKit = (context) => {
|
|
|
18141
18413
|
return kit;
|
|
18142
18414
|
};
|
|
18143
18415
|
|
|
18144
|
-
var name$
|
|
18145
|
-
var version$
|
|
18146
|
-
var pkg$
|
|
18147
|
-
name: name$
|
|
18148
|
-
version: version$
|
|
18416
|
+
var name$2 = "@circle-fin/swap-kit";
|
|
18417
|
+
var version$2 = "1.2.2";
|
|
18418
|
+
var pkg$2 = {
|
|
18419
|
+
name: name$2,
|
|
18420
|
+
version: version$2};
|
|
18149
18421
|
|
|
18150
18422
|
/**
|
|
18151
18423
|
* @packageDocumentation
|
|
@@ -18242,7 +18514,7 @@ const serviceSwapConfigSchema = z.object({
|
|
|
18242
18514
|
* @remarks
|
|
18243
18515
|
* Optionally includes address for developer-controlled adapters.
|
|
18244
18516
|
*/
|
|
18245
|
-
const adapterContextSchema$
|
|
18517
|
+
const adapterContextSchema$6 = z.object({
|
|
18246
18518
|
adapter: adapterSchema$1,
|
|
18247
18519
|
chain: chainIdentifierSchema,
|
|
18248
18520
|
address: z.string().optional(),
|
|
@@ -18264,7 +18536,7 @@ const adapterContextSchema$5 = z.object({
|
|
|
18264
18536
|
* ```
|
|
18265
18537
|
*/
|
|
18266
18538
|
const serviceSwapParamsSchema = z.object({
|
|
18267
|
-
from: adapterContextSchema$
|
|
18539
|
+
from: adapterContextSchema$6,
|
|
18268
18540
|
tokenIn: z
|
|
18269
18541
|
.string({
|
|
18270
18542
|
required_error: 'tokenIn is required',
|
|
@@ -18479,7 +18751,7 @@ const validateServiceSwapParams = (params) => {
|
|
|
18479
18751
|
*
|
|
18480
18752
|
* @internal
|
|
18481
18753
|
*/
|
|
18482
|
-
const DEFAULT_CONFIG = {
|
|
18754
|
+
const DEFAULT_CONFIG$1 = {
|
|
18483
18755
|
timeout: 30_000, // 30 seconds - matches load balancer timeout
|
|
18484
18756
|
maxRetries: 3, // 3 retries as per requirements
|
|
18485
18757
|
retryDelay: 200, // 200ms between retries
|
|
@@ -19144,6 +19416,46 @@ const parseCreateSwapResponse = (obj) => createSwapResponseSchema.parse(obj);
|
|
|
19144
19416
|
* ```
|
|
19145
19417
|
*/
|
|
19146
19418
|
const isGetSwapStatusResponse = (obj) => getSwapStatusResponseSchema.safeParse(obj).success;
|
|
19419
|
+
/**
|
|
19420
|
+
* Validates that an API key is properly formatted for Circle Stablecoin Service.
|
|
19421
|
+
*
|
|
19422
|
+
* This function performs validation on API keys to ensure they conform to the
|
|
19423
|
+
* expected format before making API requests. The key must follow the structure:
|
|
19424
|
+
* `KIT_KEY:keyId:keySecret`
|
|
19425
|
+
*
|
|
19426
|
+
* @param apiKey - The API key to validate (can be any type for graceful handling).
|
|
19427
|
+
* @returns True if the API key is valid, false otherwise.
|
|
19428
|
+
*
|
|
19429
|
+
* @remarks
|
|
19430
|
+
* This function handles invalid inputs gracefully by returning false rather than
|
|
19431
|
+
* throwing errors. It validates:
|
|
19432
|
+
* - The key starts with the `KIT_KEY:` prefix
|
|
19433
|
+
* - Contains exactly three colon-separated parts
|
|
19434
|
+
* - keyId and keySecret contain only valid characters (alphanumeric, `-`, `_`, `.`)
|
|
19435
|
+
* - keyId and keySecret are non-empty
|
|
19436
|
+
* - No whitespace anywhere in the API key (including leading, trailing, or internal)
|
|
19437
|
+
*
|
|
19438
|
+
* Valid characters in keyId and keySecret: `a-z`, `A-Z`, `0-9`, `-`, `_`, `.`
|
|
19439
|
+
*
|
|
19440
|
+
* @example
|
|
19441
|
+
* ```typescript
|
|
19442
|
+
* import { isValidApiKey } from '@core/service-client'
|
|
19443
|
+
*
|
|
19444
|
+
* // Valid API key
|
|
19445
|
+
* const isValid = isValidApiKey('KIT_KEY:e84d2546d4e321b2ff427dc988c89503:f84d2548d4e322b2ff427fc989c87503')
|
|
19446
|
+
* console.log(isValid) // true
|
|
19447
|
+
*
|
|
19448
|
+
* ```
|
|
19449
|
+
*/
|
|
19450
|
+
const isValidApiKey = (apiKey) => {
|
|
19451
|
+
// Handle invalid input types
|
|
19452
|
+
if (typeof apiKey !== 'string') {
|
|
19453
|
+
return false;
|
|
19454
|
+
}
|
|
19455
|
+
const apiKeyPattern = /^KIT_KEY:[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+$/;
|
|
19456
|
+
// Validate without trimming - any whitespace will cause validation to fail
|
|
19457
|
+
return apiKeyPattern.test(apiKey);
|
|
19458
|
+
};
|
|
19147
19459
|
|
|
19148
19460
|
/**
|
|
19149
19461
|
* @packageDocumentation
|
|
@@ -19214,9 +19526,9 @@ const createSwap = async (params) => {
|
|
|
19214
19526
|
// Remove the API key from the request body
|
|
19215
19527
|
const { apiKey, ...requestBody } = validatedParams;
|
|
19216
19528
|
const effectiveConfig = {
|
|
19217
|
-
...DEFAULT_CONFIG,
|
|
19529
|
+
...DEFAULT_CONFIG$1,
|
|
19218
19530
|
headers: {
|
|
19219
|
-
...DEFAULT_CONFIG.headers,
|
|
19531
|
+
...DEFAULT_CONFIG$1.headers,
|
|
19220
19532
|
Authorization: `Bearer ${apiKey}`,
|
|
19221
19533
|
},
|
|
19222
19534
|
};
|
|
@@ -19369,9 +19681,9 @@ const getQuote = async (params) => {
|
|
|
19369
19681
|
const url = buildQuoteUrl(validatedParams);
|
|
19370
19682
|
// Merge default config with Authorization header
|
|
19371
19683
|
const effectiveConfig = {
|
|
19372
|
-
...DEFAULT_CONFIG,
|
|
19684
|
+
...DEFAULT_CONFIG$1,
|
|
19373
19685
|
headers: {
|
|
19374
|
-
...DEFAULT_CONFIG.headers,
|
|
19686
|
+
...DEFAULT_CONFIG$1.headers,
|
|
19375
19687
|
Authorization: `Bearer ${validatedParams.apiKey}`,
|
|
19376
19688
|
},
|
|
19377
19689
|
};
|
|
@@ -19424,9 +19736,9 @@ const getSwapStatus = async (params) => {
|
|
|
19424
19736
|
}
|
|
19425
19737
|
const url = buildSwapStatusUrl(result.data);
|
|
19426
19738
|
const effectiveConfig = {
|
|
19427
|
-
...DEFAULT_CONFIG,
|
|
19739
|
+
...DEFAULT_CONFIG$1,
|
|
19428
19740
|
headers: {
|
|
19429
|
-
...DEFAULT_CONFIG.headers,
|
|
19741
|
+
...DEFAULT_CONFIG$1.headers,
|
|
19430
19742
|
Authorization: `Bearer ${result.data.apiKey}`,
|
|
19431
19743
|
},
|
|
19432
19744
|
};
|
|
@@ -22022,6 +22334,119 @@ function evmSigningData(burnIntent) {
|
|
|
22022
22334
|
};
|
|
22023
22335
|
}
|
|
22024
22336
|
|
|
22337
|
+
/**
|
|
22338
|
+
* EIP-7702 delegation indicator. A 7702-delegated EOA's bytecode is
|
|
22339
|
+
* `0xef0100` followed by the 20-byte delegate address (23 bytes total).
|
|
22340
|
+
* The underlying secp256k1 key still produces `ecrecover`-verifiable
|
|
22341
|
+
* signatures, so for Gateway's purposes a 7702-delegated address is
|
|
22342
|
+
* an EOA, not an SCA.
|
|
22343
|
+
*
|
|
22344
|
+
* Spec: https://eips.ethereum.org/EIPS/eip-7702
|
|
22345
|
+
*/
|
|
22346
|
+
const EIP_7702_DELEGATION_PREFIX = '0xef0100';
|
|
22347
|
+
/**
|
|
22348
|
+
* Assert that `address` on `chain` can sign Gateway burn intents.
|
|
22349
|
+
*
|
|
22350
|
+
* Gateway verifies burn-intent signatures with plain `ecrecover` (see
|
|
22351
|
+
* `evm-gateway-contracts/src/lib/EIP712Domain.sol`). Smart-contract
|
|
22352
|
+
* accounts (SCAs) produce signatures over wrapped hashes (ERC-1271 /
|
|
22353
|
+
* ERC-6492 / ERC-6900 replay-safe hashes) that Gateway cannot verify.
|
|
22354
|
+
* Additionally, the Circle Wallets backend rejects SCA typed-data signing
|
|
22355
|
+
* against Gateway's chainId-less domain with an opaque
|
|
22356
|
+
* `invalid integer value <nil>/<nil> for type uint256` error.
|
|
22357
|
+
*
|
|
22358
|
+
* EIP-7702-delegated EOAs are exempt: they expose non-empty bytecode
|
|
22359
|
+
* (`0xef0100<delegate>`) but the underlying secp256k1 key still produces
|
|
22360
|
+
* `ecrecover`-verifiable signatures, so Gateway accepts them.
|
|
22361
|
+
*
|
|
22362
|
+
* When the signer is a true SCA, raises an `INPUT_UNSUPPORTED_ACTION`
|
|
22363
|
+
* error directing the caller to register an EOA delegate against the
|
|
22364
|
+
* SCA and then submit the spend with the delegate EOA as the signer
|
|
22365
|
+
* and the SCA as the source account. See the unified-balance / Gateway
|
|
22366
|
+
* docs for the exact API.
|
|
22367
|
+
*
|
|
22368
|
+
* If bytecode cannot be read (RPC failure, etc.) the pre-check is
|
|
22369
|
+
* skipped and downstream signing surfaces its own error — a warning is
|
|
22370
|
+
* logged so the skip is diagnosable.
|
|
22371
|
+
*
|
|
22372
|
+
* @param adapter - Anything exposing {@link EvmAdapterLike.readBytecode}.
|
|
22373
|
+
* @param address - Signer address to validate.
|
|
22374
|
+
* @param chain - EVM chain where the signer lives.
|
|
22375
|
+
* @throws {KitError} INPUT_UNSUPPORTED_ACTION when `address` is an SCA.
|
|
22376
|
+
*
|
|
22377
|
+
* @example
|
|
22378
|
+
* ```typescript
|
|
22379
|
+
* import { assertSignerIsEoa } from '@core/adapter-evm'
|
|
22380
|
+
* import { Ethereum } from '@core/chains'
|
|
22381
|
+
*
|
|
22382
|
+
* await assertSignerIsEoa(adapter, '0xabc...', Ethereum)
|
|
22383
|
+
* ```
|
|
22384
|
+
*/
|
|
22385
|
+
async function assertSignerIsEoa(adapter, address, chain) {
|
|
22386
|
+
let code;
|
|
22387
|
+
try {
|
|
22388
|
+
code = await adapter.readBytecode(address, chain);
|
|
22389
|
+
}
|
|
22390
|
+
catch (err) {
|
|
22391
|
+
console.warn(`[gateway] assertSignerIsEoa skipped (readBytecode failed for ` +
|
|
22392
|
+
`${address} on ${chain.name}): ` +
|
|
22393
|
+
(err instanceof Error ? err.message : String(err)));
|
|
22394
|
+
return;
|
|
22395
|
+
}
|
|
22396
|
+
if (code === undefined ||
|
|
22397
|
+
code === '0x' ||
|
|
22398
|
+
code.toLowerCase().startsWith(EIP_7702_DELEGATION_PREFIX)) {
|
|
22399
|
+
return;
|
|
22400
|
+
}
|
|
22401
|
+
throw new KitError({
|
|
22402
|
+
...InputError.UNSUPPORTED_ACTION,
|
|
22403
|
+
recoverability: 'FATAL',
|
|
22404
|
+
message: `Gateway burn-intent signing requires an EOA signer (Gateway ` +
|
|
22405
|
+
`verifies signatures with ecrecover and does not support ERC-1271). ` +
|
|
22406
|
+
`The signer ${address} on ${chain.name} has on-chain bytecode, ` +
|
|
22407
|
+
`indicating it is a smart-contract account (SCA). Register an EOA ` +
|
|
22408
|
+
`delegate against the SCA, then submit the spend with the delegate ` +
|
|
22409
|
+
`EOA as the signer and the SCA as the source account. See DEVX-2774.`,
|
|
22410
|
+
cause: {
|
|
22411
|
+
trace: {
|
|
22412
|
+
operation: 'signEvmIntentGroup.assertSignerIsEoa',
|
|
22413
|
+
address,
|
|
22414
|
+
chain: chain.name,
|
|
22415
|
+
bytecodeBytes: (code.length - 2) / 2,
|
|
22416
|
+
bytecodePrefix: code.slice(0, 12),
|
|
22417
|
+
},
|
|
22418
|
+
},
|
|
22419
|
+
});
|
|
22420
|
+
}
|
|
22421
|
+
|
|
22422
|
+
/**
|
|
22423
|
+
* Duck-type test for an EVM adapter that exposes `readBytecode`.
|
|
22424
|
+
*
|
|
22425
|
+
* Used as a structural guard at package boundaries (e.g. the Circle Wallets
|
|
22426
|
+
* hybrid adapter calling into a chain adapter, or the Gateway sign path
|
|
22427
|
+
* receiving an arbitrary adapter implementation) where `instanceof EvmAdapter`
|
|
22428
|
+
* is unreliable because each consumer bundles its own copy of the base class.
|
|
22429
|
+
*
|
|
22430
|
+
* @param value - The value to test.
|
|
22431
|
+
* @returns `true` when `value` is an object exposing a callable
|
|
22432
|
+
* `readBytecode` method, narrowed to {@link EvmAdapterLike}.
|
|
22433
|
+
*
|
|
22434
|
+
* @example
|
|
22435
|
+
* ```typescript
|
|
22436
|
+
* import { isEvmAdapterLike } from '@core/adapter-evm'
|
|
22437
|
+
*
|
|
22438
|
+
* if (isEvmAdapterLike(adapter)) {
|
|
22439
|
+
* const code = await adapter.readBytecode('0xabc...', chain)
|
|
22440
|
+
* }
|
|
22441
|
+
* ```
|
|
22442
|
+
*/
|
|
22443
|
+
function isEvmAdapterLike(value) {
|
|
22444
|
+
return (typeof value === 'object' &&
|
|
22445
|
+
value !== null &&
|
|
22446
|
+
'readBytecode' in value &&
|
|
22447
|
+
typeof value.readBytecode === 'function');
|
|
22448
|
+
}
|
|
22449
|
+
|
|
22025
22450
|
/**
|
|
22026
22451
|
* Sign an EVM adapter group: batches all intents and produces a single
|
|
22027
22452
|
* EIP-712 ECDSA signature.
|
|
@@ -22029,6 +22454,12 @@ function evmSigningData(burnIntent) {
|
|
|
22029
22454
|
* For a single-intent group, `primaryType` is `'BurnIntent'`.
|
|
22030
22455
|
* For multi-intent groups, `primaryType` is `'BurnIntentSet'`.
|
|
22031
22456
|
*
|
|
22457
|
+
* Before signing, asserts that the signer address is an EOA. Gateway
|
|
22458
|
+
* verifies burn-intent signatures with plain `ecrecover` (no ERC-1271
|
|
22459
|
+
* fallback), so signatures produced by smart-contract accounts (SCAs)
|
|
22460
|
+
* cannot be verified. When an SCA is detected, a clear error is raised
|
|
22461
|
+
* directing the caller to the delegate workflow (DEVX-2774).
|
|
22462
|
+
*
|
|
22032
22463
|
* @param group - The adapter group containing the adapter, chain, and
|
|
22033
22464
|
* burn intents to sign.
|
|
22034
22465
|
* @returns A signed set with the intents and the ECDSA signature.
|
|
@@ -22049,6 +22480,22 @@ function evmSigningData(burnIntent) {
|
|
|
22049
22480
|
async function signEvmIntentGroup(group) {
|
|
22050
22481
|
const { adapter, intents: groupIntents, chain, address } = group;
|
|
22051
22482
|
const operationContext = address === undefined ? { chain } : { chain, address };
|
|
22483
|
+
// Gateway verifies burn-intent signatures with plain ecrecover. An SCA
|
|
22484
|
+
// signer silently produces a signature over a wrapped hash that Gateway
|
|
22485
|
+
// cannot verify, and Circle Wallets' KMS rejects the typed data up front
|
|
22486
|
+
// with an opaque `<nil>/<nil>` error. Short-circuit with a clear message
|
|
22487
|
+
// when we can detect bytecode at the signer address. See DEVX-2774.
|
|
22488
|
+
//
|
|
22489
|
+
// Duck-typed on readBytecode rather than `instanceof EvmAdapter` because
|
|
22490
|
+
// each consumer package bundles its own copy of the base class and the
|
|
22491
|
+
// `instanceof` identity check fails across package boundaries.
|
|
22492
|
+
//
|
|
22493
|
+
// Empty string is defended against because assertSignerIsEoa would
|
|
22494
|
+
// otherwise call eth_getCode('') on the RPC.
|
|
22495
|
+
const hasResolvedSigner = typeof address === 'string' && address.length > 0;
|
|
22496
|
+
if (hasResolvedSigner && chain.type === 'evm' && isEvmAdapterLike(adapter)) {
|
|
22497
|
+
await assertSignerIsEoa(adapter, address, chain);
|
|
22498
|
+
}
|
|
22052
22499
|
const firstIntent = groupIntents[0];
|
|
22053
22500
|
const typedData = groupIntents.length === 1 && firstIntent
|
|
22054
22501
|
? evmSigningData(firstIntent)
|
|
@@ -22166,6 +22613,27 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
|
22166
22613
|
*/
|
|
22167
22614
|
const NATIVE_TOKEN_ADDRESS$1 = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
|
22168
22615
|
|
|
22616
|
+
const hexBytesSchema$1 = z
|
|
22617
|
+
.string()
|
|
22618
|
+
.regex(/^0x([0-9a-fA-F]{2})*$/, {
|
|
22619
|
+
message: 'value must be a 0x-prefixed even-length hex string',
|
|
22620
|
+
})
|
|
22621
|
+
.transform((value) => value);
|
|
22622
|
+
z
|
|
22623
|
+
.object({
|
|
22624
|
+
executeParams: z.object({}).passthrough(),
|
|
22625
|
+
signature: evmSignatureSchema,
|
|
22626
|
+
tokenInputs: z.array(z.object({
|
|
22627
|
+
permitType: z.nativeEnum(PermitType),
|
|
22628
|
+
token: evmAddressSchema,
|
|
22629
|
+
amount: z.bigint().refine((value) => value >= 0n, {
|
|
22630
|
+
message: 'amount must be a non-negative bigint',
|
|
22631
|
+
}),
|
|
22632
|
+
permitCalldata: hexBytesSchema$1,
|
|
22633
|
+
})),
|
|
22634
|
+
})
|
|
22635
|
+
.passthrough();
|
|
22636
|
+
|
|
22169
22637
|
/**
|
|
22170
22638
|
* Solana mainnet genesis hash
|
|
22171
22639
|
*/
|
|
@@ -22318,9 +22786,10 @@ function getSolanaFeeRecipient() {
|
|
|
22318
22786
|
return CIRCLE_FEE_RECIPIENT_SOLANA;
|
|
22319
22787
|
}
|
|
22320
22788
|
|
|
22321
|
-
|
|
22322
|
-
|
|
22323
|
-
|
|
22789
|
+
const enc = new TextEncoder();
|
|
22790
|
+
enc.encode('gateway_minter');
|
|
22791
|
+
enc.encode('gateway_minter_custody');
|
|
22792
|
+
enc.encode('used_transfer_spec_hash');
|
|
22324
22793
|
|
|
22325
22794
|
async function executeAndWait$1(adapter, request, chain) {
|
|
22326
22795
|
if (request.type === 'noop') {
|
|
@@ -24375,21 +24844,21 @@ function buildInsufficientSwapBalanceError(tokenSymbol, walletAddress, currentBa
|
|
|
24375
24844
|
const displaySymbol = resolvedSymbol ?? tokenSymbol;
|
|
24376
24845
|
// Format human-readable amounts if we can resolve the token, otherwise use base units only
|
|
24377
24846
|
const currentFormatted = canFormat
|
|
24378
|
-
? formatAmount$
|
|
24847
|
+
? formatAmount$2({
|
|
24379
24848
|
value: currentBalance.toString(),
|
|
24380
24849
|
token: resolvedSymbol,
|
|
24381
24850
|
chain,
|
|
24382
24851
|
})
|
|
24383
24852
|
: null;
|
|
24384
24853
|
const requiredFormatted = canFormat
|
|
24385
|
-
? formatAmount$
|
|
24854
|
+
? formatAmount$2({
|
|
24386
24855
|
value: requiredAmount.toString(),
|
|
24387
24856
|
token: resolvedSymbol,
|
|
24388
24857
|
chain,
|
|
24389
24858
|
})
|
|
24390
24859
|
: null;
|
|
24391
24860
|
const shortfallFormatted = canFormat
|
|
24392
|
-
? formatAmount$
|
|
24861
|
+
? formatAmount$2({
|
|
24393
24862
|
value: shortfall.toString(),
|
|
24394
24863
|
token: resolvedSymbol,
|
|
24395
24864
|
chain,
|
|
@@ -24475,7 +24944,7 @@ async function formatTokenValue(value, token, chain, adapter) {
|
|
|
24475
24944
|
if (resolvedSymbol) {
|
|
24476
24945
|
if (isSwapToken(resolvedSymbol)) {
|
|
24477
24946
|
return {
|
|
24478
|
-
amount: formatAmount$
|
|
24947
|
+
amount: formatAmount$2({
|
|
24479
24948
|
value,
|
|
24480
24949
|
token: resolvedSymbol,
|
|
24481
24950
|
chain,
|
|
@@ -24516,7 +24985,7 @@ async function formatTokenValue(value, token, chain, adapter) {
|
|
|
24516
24985
|
* i.e. 1 + 0.2 + 0.1 = 1.3. Use the greater of (local estimate × this multiplier)
|
|
24517
24986
|
* or the proxy's gasLimit.
|
|
24518
24987
|
*/
|
|
24519
|
-
const GAS_SAFETY_MULTIPLIER = 1.3;
|
|
24988
|
+
const GAS_SAFETY_MULTIPLIER$1 = 1.3;
|
|
24520
24989
|
const TOKEN_REGISTRY = createTokenRegistry();
|
|
24521
24990
|
/**
|
|
24522
24991
|
* Enhances a KitError with transaction details (txHash and explorerUrl).
|
|
@@ -24949,7 +25418,7 @@ class StablecoinServiceSwapProvider {
|
|
|
24949
25418
|
const gasEstimate = await params.from.adapter.calculateTransactionFee(gasLimitUnits, undefined, // Use default 5% buffer
|
|
24950
25419
|
chain);
|
|
24951
25420
|
// Format the fee from wei to human-readable native currency amount
|
|
24952
|
-
estimatedGasFee = formatAmount$
|
|
25421
|
+
estimatedGasFee = formatAmount$2({
|
|
24953
25422
|
value: gasEstimate.fee,
|
|
24954
25423
|
token: 'NATIVE',
|
|
24955
25424
|
chain,
|
|
@@ -24962,7 +25431,7 @@ class StablecoinServiceSwapProvider {
|
|
|
24962
25431
|
const gasEstimate = await params.from.adapter.calculateTransactionFee(300000n, undefined, // Use default buffer
|
|
24963
25432
|
chain);
|
|
24964
25433
|
// Format the fee from lamports to human-readable SOL amount
|
|
24965
|
-
estimatedGasFee = formatAmount$
|
|
25434
|
+
estimatedGasFee = formatAmount$2({
|
|
24966
25435
|
value: gasEstimate.fee,
|
|
24967
25436
|
token: 'NATIVE',
|
|
24968
25437
|
chain,
|
|
@@ -25496,7 +25965,7 @@ class StablecoinServiceSwapProvider {
|
|
|
25496
25965
|
const localEstimate = await preparedAction.estimate();
|
|
25497
25966
|
const localGas = Number(localEstimate?.gas);
|
|
25498
25967
|
if (Number.isFinite(localGas) && localGas > 0) {
|
|
25499
|
-
const localWithBuffer = Math.ceil(localGas * GAS_SAFETY_MULTIPLIER);
|
|
25968
|
+
const localWithBuffer = Math.ceil(localGas * GAS_SAFETY_MULTIPLIER$1);
|
|
25500
25969
|
if (Number.isFinite(localWithBuffer) && localWithBuffer > 0) {
|
|
25501
25970
|
effectiveGasLimit = Math.max(localWithBuffer, proxyGasLimit);
|
|
25502
25971
|
}
|
|
@@ -25592,7 +26061,7 @@ class StablecoinServiceSwapProvider {
|
|
|
25592
26061
|
* Must always contain both adapter and chain explicitly.
|
|
25593
26062
|
* Optionally includes address for developer-controlled adapters.
|
|
25594
26063
|
*/
|
|
25595
|
-
const adapterContextSchema$
|
|
26064
|
+
const adapterContextSchema$5 = z.object({
|
|
25596
26065
|
adapter: adapterSchema$1,
|
|
25597
26066
|
chain: swapChainIdentifierSchema,
|
|
25598
26067
|
address: z.string().optional(),
|
|
@@ -25808,7 +26277,7 @@ const amountInSchema = z
|
|
|
25808
26277
|
* ```
|
|
25809
26278
|
*/
|
|
25810
26279
|
const swapParamsSchema = z.object({
|
|
25811
|
-
from: adapterContextSchema$
|
|
26280
|
+
from: adapterContextSchema$5,
|
|
25812
26281
|
tokenIn: swapTokenSchema,
|
|
25813
26282
|
tokenOut: swapTokenSchema,
|
|
25814
26283
|
amountIn: amountInSchema,
|
|
@@ -28339,7 +28808,7 @@ async function swap$1(context, params) {
|
|
|
28339
28808
|
* )
|
|
28340
28809
|
* ```
|
|
28341
28810
|
*/
|
|
28342
|
-
function getSupportedChains$
|
|
28811
|
+
function getSupportedChains$4(context) {
|
|
28343
28812
|
const swapChains = new Set(Object.values(SwapChain));
|
|
28344
28813
|
// Collect all supported chains from all providers
|
|
28345
28814
|
// Use optional chaining to gracefully handle providers without supportedChains
|
|
@@ -28526,7 +28995,7 @@ function removeCustomFeePolicy(context) {
|
|
|
28526
28995
|
* @returns An array containing the default StablecoinServiceSwapProvider
|
|
28527
28996
|
* @internal
|
|
28528
28997
|
*/
|
|
28529
|
-
const getDefaultProviders$
|
|
28998
|
+
const getDefaultProviders$2 = () => [new StablecoinServiceSwapProvider()];
|
|
28530
28999
|
/**
|
|
28531
29000
|
* Create a SwapKit context with validated configuration.
|
|
28532
29001
|
*
|
|
@@ -28586,7 +29055,7 @@ const getDefaultProviders$1 = () => [new StablecoinServiceSwapProvider()];
|
|
|
28586
29055
|
*/
|
|
28587
29056
|
function createSwapKitContext(config = {}) {
|
|
28588
29057
|
// Initialize default providers
|
|
28589
|
-
const defaultProviders = getDefaultProviders$
|
|
29058
|
+
const defaultProviders = getDefaultProviders$2();
|
|
28590
29059
|
// Merge default and custom providers
|
|
28591
29060
|
const providers = [...defaultProviders, ...(config.providers ?? [])];
|
|
28592
29061
|
// Build base context without fee policy
|
|
@@ -28614,7 +29083,7 @@ const SWAP_EVENT_TYPES = {
|
|
|
28614
29083
|
};
|
|
28615
29084
|
|
|
28616
29085
|
/** SDK name used in telemetry payloads. */
|
|
28617
|
-
const SDK_NAME$1 = resolveKitSdkName(pkg$
|
|
29086
|
+
const SDK_NAME$1 = resolveKitSdkName(pkg$2.name);
|
|
28618
29087
|
/**
|
|
28619
29088
|
* A high-level class-based interface for single-chain token swap operations.
|
|
28620
29089
|
*
|
|
@@ -28741,7 +29210,7 @@ class SwapKit {
|
|
|
28741
29210
|
this.disableErrorReporting = config.disableErrorReporting === true;
|
|
28742
29211
|
this.telemetryConfig = {
|
|
28743
29212
|
sdkName: SDK_NAME$1,
|
|
28744
|
-
sdkVersion: pkg$
|
|
29213
|
+
sdkVersion: pkg$2.version,
|
|
28745
29214
|
disabled: this.disableErrorReporting,
|
|
28746
29215
|
};
|
|
28747
29216
|
}
|
|
@@ -28885,7 +29354,7 @@ class SwapKit {
|
|
|
28885
29354
|
* ```
|
|
28886
29355
|
*/
|
|
28887
29356
|
getSupportedChains() {
|
|
28888
|
-
return getSupportedChains$
|
|
29357
|
+
return getSupportedChains$4(this.context);
|
|
28889
29358
|
}
|
|
28890
29359
|
/**
|
|
28891
29360
|
* Set a custom fee policy for all swap operations.
|
|
@@ -29009,7 +29478,7 @@ class SwapKit {
|
|
|
29009
29478
|
* @packageDocumentation
|
|
29010
29479
|
*/
|
|
29011
29480
|
// Auto-register this kit for user agent tracking
|
|
29012
|
-
registerKit(`${pkg$
|
|
29481
|
+
registerKit(`${pkg$2.name}/${pkg$2.version}`);
|
|
29013
29482
|
|
|
29014
29483
|
/**
|
|
29015
29484
|
* Create a SwapKit instance with optional developer fee configuration.
|
|
@@ -29107,221 +29576,3071 @@ const createSwapKit = (context) => {
|
|
|
29107
29576
|
return kit;
|
|
29108
29577
|
};
|
|
29109
29578
|
|
|
29579
|
+
var name$1 = "@circle-fin/earn-kit";
|
|
29580
|
+
var version$1 = "1.0.0";
|
|
29581
|
+
var pkg$1 = {
|
|
29582
|
+
name: name$1,
|
|
29583
|
+
version: version$1};
|
|
29584
|
+
|
|
29110
29585
|
/**
|
|
29111
|
-
*
|
|
29586
|
+
* Default base URL for the Earn Service API.
|
|
29112
29587
|
*
|
|
29113
|
-
*
|
|
29114
|
-
|
|
29115
|
-
|
|
29116
|
-
|
|
29588
|
+
* @internal
|
|
29589
|
+
*/
|
|
29590
|
+
const EARN_SERVICE_BASE_URL = 'https://api.circle.com';
|
|
29591
|
+
/**
|
|
29592
|
+
* API path prefix for all EarnKit endpoints.
|
|
29117
29593
|
*
|
|
29118
|
-
*
|
|
29119
|
-
|
|
29594
|
+
* @internal
|
|
29595
|
+
*/
|
|
29596
|
+
const EARN_KIT_API_PREFIX = '/v1/earnKit';
|
|
29597
|
+
/**
|
|
29598
|
+
* Map SDK chain identifiers to Zenith API chain strings.
|
|
29120
29599
|
*
|
|
29121
|
-
*
|
|
29122
|
-
*
|
|
29123
|
-
*
|
|
29600
|
+
* The Zenith backend uses short-form blockchain identifiers (e.g., 'ARC-TESTNET')
|
|
29601
|
+
* while the SDK uses the {@link Blockchain} enum (e.g., 'Arc_Testnet').
|
|
29602
|
+
* Adding a new {@link EarnSupportedBlockchain} without a corresponding
|
|
29603
|
+
* entry here produces a compile error.
|
|
29604
|
+
*
|
|
29605
|
+
* @internal
|
|
29606
|
+
*/
|
|
29607
|
+
const CHAIN_TO_API = {
|
|
29608
|
+
[Blockchain.Arc_Testnet]: 'ARC-TESTNET',
|
|
29609
|
+
};
|
|
29610
|
+
/**
|
|
29611
|
+
* Display symbol for vault share tokens.
|
|
29612
|
+
*
|
|
29613
|
+
* @internal
|
|
29614
|
+
*/
|
|
29615
|
+
const VAULT_SHARE_SYMBOL = 'shares';
|
|
29616
|
+
/**
|
|
29617
|
+
* Default polling configuration for Earn Service API calls.
|
|
29618
|
+
*
|
|
29619
|
+
* Match the load-balancer timeout (30 s) with retry settings consistent
|
|
29620
|
+
* with `@core/service-client` defaults.
|
|
29621
|
+
*
|
|
29622
|
+
* @internal
|
|
29623
|
+
*/
|
|
29624
|
+
const DEFAULT_CONFIG = {
|
|
29625
|
+
timeout: 30_000,
|
|
29626
|
+
maxRetries: 3,
|
|
29627
|
+
retryDelay: 200,
|
|
29628
|
+
headers: {
|
|
29629
|
+
'Content-Type': 'application/json',
|
|
29630
|
+
},
|
|
29631
|
+
};
|
|
29632
|
+
|
|
29633
|
+
const API_TO_CHAIN = Object.fromEntries(Object.entries(CHAIN_TO_API).map(([sdkChain, apiChain]) => [
|
|
29634
|
+
apiChain,
|
|
29635
|
+
sdkChain,
|
|
29636
|
+
]));
|
|
29637
|
+
function toApiChain(chain) {
|
|
29638
|
+
if (!Object.hasOwn(CHAIN_TO_API, chain)) {
|
|
29639
|
+
return undefined;
|
|
29640
|
+
}
|
|
29641
|
+
return CHAIN_TO_API[chain];
|
|
29642
|
+
}
|
|
29643
|
+
function toSdkChain(chain) {
|
|
29644
|
+
if (Object.hasOwn(CHAIN_TO_API, chain)) {
|
|
29645
|
+
return chain;
|
|
29646
|
+
}
|
|
29647
|
+
if (!Object.hasOwn(API_TO_CHAIN, chain)) {
|
|
29648
|
+
return undefined;
|
|
29649
|
+
}
|
|
29650
|
+
return API_TO_CHAIN[chain];
|
|
29651
|
+
}
|
|
29652
|
+
|
|
29653
|
+
/**
|
|
29654
|
+
* Extract address and chain string from an adapter context.
|
|
29655
|
+
*
|
|
29656
|
+
* Handle both user-controlled adapters (address resolved via
|
|
29657
|
+
* `adapter.getAddress()`) and developer-controlled adapters
|
|
29658
|
+
* (address provided explicitly in the context).
|
|
29659
|
+
*
|
|
29660
|
+
* @param from - Adapter context containing adapter, chain, and optional address
|
|
29661
|
+
* @returns Resolved wallet address, API chain string, and chain definition
|
|
29662
|
+
* @throws {@link KitError} If the chain is unsupported by the Earn Service provider.
|
|
29663
|
+
* @throws Propagates errors from `adapter.getAddress(chain)` when
|
|
29664
|
+
* `from.address` is not supplied.
|
|
29124
29665
|
*
|
|
29125
29666
|
* @example
|
|
29126
29667
|
* ```typescript
|
|
29127
|
-
*
|
|
29128
|
-
*
|
|
29668
|
+
* const { address, chain, chainDefinition } =
|
|
29669
|
+
* await resolveAdapterContext(params.from)
|
|
29670
|
+
* ```
|
|
29129
29671
|
*
|
|
29130
|
-
*
|
|
29131
|
-
|
|
29132
|
-
|
|
29133
|
-
|
|
29134
|
-
|
|
29135
|
-
|
|
29672
|
+
* @internal
|
|
29673
|
+
*/
|
|
29674
|
+
async function resolveAdapterContext(from) {
|
|
29675
|
+
const chainDef = resolveChainIdentifier(from.chain);
|
|
29676
|
+
const apiChain = toApiChain(chainDef.chain);
|
|
29677
|
+
if (apiChain === undefined) {
|
|
29678
|
+
throw createInvalidChainError(chainDef.chain, 'Chain is not supported by the Earn Service provider');
|
|
29679
|
+
}
|
|
29680
|
+
const hasAddress = 'address' in from &&
|
|
29681
|
+
typeof from.address === 'string' &&
|
|
29682
|
+
from.address.length > 0;
|
|
29683
|
+
const address = hasAddress
|
|
29684
|
+
? from.address
|
|
29685
|
+
: await from.adapter.getAddress(chainDef);
|
|
29686
|
+
return { address, chain: apiChain, chainDefinition: chainDef };
|
|
29687
|
+
}
|
|
29688
|
+
|
|
29689
|
+
/**
|
|
29690
|
+
* Assert that a value is a 0x-prefixed 20-byte hex address.
|
|
29136
29691
|
*
|
|
29137
|
-
*
|
|
29138
|
-
*
|
|
29692
|
+
* @param field - Name of the field being validated.
|
|
29693
|
+
* @param value - Runtime value to validate.
|
|
29694
|
+
* @param message - Optional message describing the expected address.
|
|
29695
|
+
* @returns The validated value narrowed to a 0x-prefixed string.
|
|
29696
|
+
* @throws {@link KitError} If the value is not an EVM address.
|
|
29139
29697
|
*
|
|
29140
29698
|
* @example
|
|
29141
29699
|
* ```typescript
|
|
29142
|
-
*
|
|
29143
|
-
*
|
|
29700
|
+
* const token = assertHexAddress(
|
|
29701
|
+
* 'chain.usdcAddress',
|
|
29702
|
+
* '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
29703
|
+
* )
|
|
29704
|
+
* ```
|
|
29144
29705
|
*
|
|
29145
|
-
*
|
|
29146
|
-
|
|
29147
|
-
|
|
29148
|
-
|
|
29706
|
+
* @internal
|
|
29707
|
+
*/
|
|
29708
|
+
function assertHexAddress(field, value, message = `${field} must be a 0x-prefixed 20-byte hex address`) {
|
|
29709
|
+
const result = evmAddressSchema.safeParse(value);
|
|
29710
|
+
if (!result.success) {
|
|
29711
|
+
throw createValidationFailedError$1(field, value, message);
|
|
29712
|
+
}
|
|
29713
|
+
return result.data;
|
|
29714
|
+
}
|
|
29715
|
+
|
|
29716
|
+
/**
|
|
29717
|
+
* Return the adapter contract address configured for the chain, or throw a
|
|
29718
|
+
* fatal KitError if unavailable. Earn operations require an adapter contract
|
|
29719
|
+
* to forward service-signed payloads.
|
|
29149
29720
|
*
|
|
29150
|
-
*
|
|
29721
|
+
* @param chain - Resolved chain definition.
|
|
29722
|
+
* @returns Adapter contract address as a 0x-prefixed hex string.
|
|
29723
|
+
* @throws {@link KitError} If `chain.kitContracts.adapter` is undefined.
|
|
29724
|
+
* @throws {@link KitError} If `chain.kitContracts.adapter` is malformed.
|
|
29725
|
+
*
|
|
29726
|
+
* @example
|
|
29727
|
+
* ```typescript
|
|
29728
|
+
* const adapterContract = requireAdapterContract(chain)
|
|
29151
29729
|
* ```
|
|
29730
|
+
*
|
|
29731
|
+
* @internal
|
|
29152
29732
|
*/
|
|
29153
|
-
|
|
29154
|
-
|
|
29155
|
-
|
|
29156
|
-
|
|
29157
|
-
if (action === '*') {
|
|
29158
|
-
// Wildcard handlers are registered as-is
|
|
29159
|
-
kit.on('*', handler);
|
|
29160
|
-
}
|
|
29161
|
-
else if (prefix && action.startsWith(prefix)) {
|
|
29162
|
-
// Remove prefix to get the actual kit action name
|
|
29163
|
-
const kitAction = action.split('.').at(1);
|
|
29164
|
-
if (kitAction) {
|
|
29165
|
-
kit.on(kitAction, handler);
|
|
29166
|
-
}
|
|
29167
|
-
}
|
|
29168
|
-
else if (!prefix) {
|
|
29169
|
-
// No prefix configured, register action as-is
|
|
29170
|
-
kit.on(action, handler);
|
|
29171
|
-
}
|
|
29172
|
-
// Actions that don't match the prefix are silently ignored
|
|
29173
|
-
}
|
|
29733
|
+
function requireAdapterContract(chain) {
|
|
29734
|
+
const adapterContractAddress = chain.kitContracts?.adapter;
|
|
29735
|
+
if (adapterContractAddress === undefined) {
|
|
29736
|
+
throw createValidationFailedError$1('chain.kitContracts.adapter', adapterContractAddress, `Adapter contract not configured for chain ${chain.name}. Earn operations require an adapter contract.`);
|
|
29174
29737
|
}
|
|
29175
|
-
};
|
|
29738
|
+
return assertHexAddress('chain.kitContracts.adapter', adapterContractAddress, `Adapter contract for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
29739
|
+
}
|
|
29176
29740
|
|
|
29177
29741
|
/**
|
|
29178
|
-
*
|
|
29179
|
-
*
|
|
29180
|
-
* This array is used for runtime validation to check if a token string
|
|
29181
|
-
* is a known alias rather than a custom address.
|
|
29742
|
+
* Safety multiplier applied to locally estimated gas for earn transactions.
|
|
29182
29743
|
*
|
|
29183
|
-
*
|
|
29184
|
-
*
|
|
29185
|
-
*
|
|
29744
|
+
* Earn transactions can include nested vault and token contract calls.
|
|
29745
|
+
* Local gas estimation has undercounted those execution paths in production.
|
|
29746
|
+
* A 1.3x buffer matching swap-kit reduces out-of-gas risk while preserving
|
|
29747
|
+
* the adapter fallback path when local estimation fails.
|
|
29186
29748
|
*
|
|
29187
|
-
*
|
|
29188
|
-
* via SwapKit's SupportedToken type.
|
|
29749
|
+
* @internal
|
|
29189
29750
|
*/
|
|
29190
|
-
const
|
|
29751
|
+
const GAS_SAFETY_MULTIPLIER = 1.3;
|
|
29191
29752
|
/**
|
|
29192
|
-
*
|
|
29753
|
+
* Estimate gas for a prepared chain request and apply {@link GAS_SAFETY_MULTIPLIER}.
|
|
29193
29754
|
*
|
|
29194
|
-
*
|
|
29195
|
-
*
|
|
29196
|
-
*
|
|
29755
|
+
* Returns the buffered gas limit, or `undefined` if estimation fails or
|
|
29756
|
+
* produces an invalid value. Callers should fall through to the adapter's
|
|
29757
|
+
* default gas handling when `undefined` is returned.
|
|
29197
29758
|
*
|
|
29198
|
-
* @
|
|
29199
|
-
|
|
29759
|
+
* @internal
|
|
29760
|
+
*/
|
|
29761
|
+
async function estimateBufferedGasLimit(prepared) {
|
|
29762
|
+
try {
|
|
29763
|
+
const estimate = await prepared.estimate();
|
|
29764
|
+
const estimatedGas = Number(estimate.gas);
|
|
29765
|
+
if (Number.isFinite(estimatedGas) && estimatedGas > 0) {
|
|
29766
|
+
const buffered = Math.ceil(estimatedGas * GAS_SAFETY_MULTIPLIER);
|
|
29767
|
+
if (Number.isFinite(buffered) && buffered > 0) {
|
|
29768
|
+
return buffered;
|
|
29769
|
+
}
|
|
29770
|
+
}
|
|
29771
|
+
}
|
|
29772
|
+
catch {
|
|
29773
|
+
// If estimation fails, fall through and let the adapter handle gas internally
|
|
29774
|
+
}
|
|
29775
|
+
return undefined;
|
|
29776
|
+
}
|
|
29777
|
+
|
|
29778
|
+
/**
|
|
29779
|
+
* Maximum `uint256` value. The approval amount used when this helper needs
|
|
29780
|
+
* to send a token approval.
|
|
29200
29781
|
*
|
|
29201
|
-
* @
|
|
29202
|
-
|
|
29203
|
-
|
|
29782
|
+
* @internal
|
|
29783
|
+
*/
|
|
29784
|
+
const MAX_UINT256$1 = 2n ** 256n - 1n;
|
|
29785
|
+
/**
|
|
29786
|
+
* Allowance threshold above which a prior max-approve is assumed to still
|
|
29787
|
+
* cover any earn operation. Detects prior max-approvals even when the token
|
|
29788
|
+
* decrements allowance on `transferFrom`, since that decay is negligible
|
|
29789
|
+
* relative to 2^255.
|
|
29204
29790
|
*
|
|
29205
|
-
*
|
|
29206
|
-
* console.log(isTokenAlias('NATIVE')) // true
|
|
29207
|
-
* console.log(isTokenAlias('0x6B175474E89094C44Da98b954EedeAC495271d0F')) // false
|
|
29208
|
-
* ```
|
|
29791
|
+
* @internal
|
|
29209
29792
|
*/
|
|
29210
|
-
|
|
29211
|
-
|
|
29793
|
+
const ALLOWANCE_THRESHOLD = MAX_UINT256$1 / 2n;
|
|
29794
|
+
function parseAllowanceResponse(allowanceRaw) {
|
|
29795
|
+
if (allowanceRaw === undefined || allowanceRaw === null) {
|
|
29796
|
+
return 0n;
|
|
29797
|
+
}
|
|
29798
|
+
let allowance;
|
|
29799
|
+
if (typeof allowanceRaw === 'bigint') {
|
|
29800
|
+
allowance = allowanceRaw;
|
|
29801
|
+
}
|
|
29802
|
+
else if (typeof allowanceRaw === 'string') {
|
|
29803
|
+
try {
|
|
29804
|
+
allowance = BigInt(allowanceRaw);
|
|
29805
|
+
}
|
|
29806
|
+
catch {
|
|
29807
|
+
allowance = undefined;
|
|
29808
|
+
}
|
|
29809
|
+
}
|
|
29810
|
+
if (allowance === undefined || allowance < 0n) {
|
|
29811
|
+
throw createValidationFailedError$1('token.allowance', allowanceRaw, 'token.allowance response must be a non-negative bigint-compatible string or bigint');
|
|
29812
|
+
}
|
|
29813
|
+
return allowance;
|
|
29212
29814
|
}
|
|
29213
29815
|
/**
|
|
29214
|
-
*
|
|
29816
|
+
* Read the current ERC-20 allowance and, if below threshold, send a
|
|
29817
|
+
* max-approval transaction and wait for confirmation.
|
|
29215
29818
|
*
|
|
29216
|
-
*
|
|
29217
|
-
*
|
|
29218
|
-
*
|
|
29819
|
+
* Uses the generalised `token.allowance` + `token.approve` adapter actions so
|
|
29820
|
+
* the kit stays chain-agnostic. Assumes the target token supports
|
|
29821
|
+
* non-zero-to-non-zero approve (USDC, ERC-4626 share tokens). Tokens with
|
|
29822
|
+
* USDT-style semantics (require allowance == 0 before non-zero approve) need
|
|
29823
|
+
* a different pattern.
|
|
29219
29824
|
*
|
|
29220
|
-
*
|
|
29825
|
+
* Approval status is checked explicitly because `waitForTransaction` returns
|
|
29826
|
+
* a receipt with `status: 'reverted'` rather than throwing on revert.
|
|
29221
29827
|
*
|
|
29222
|
-
* @
|
|
29223
|
-
*
|
|
29224
|
-
* @
|
|
29828
|
+
* @returns The approval tx hash when an approval was sent, or `undefined` if
|
|
29829
|
+
* the existing allowance was already above threshold.
|
|
29830
|
+
* @throws {@link KitError} If the allowance response is malformed.
|
|
29831
|
+
* @throws {@link KitError} If the approval transaction reverts on-chain.
|
|
29225
29832
|
*
|
|
29226
29833
|
* @example
|
|
29227
29834
|
* ```typescript
|
|
29228
|
-
*
|
|
29229
|
-
* import { Ethereum } from '@core/chains'
|
|
29230
|
-
*
|
|
29231
|
-
* const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'
|
|
29232
|
-
* if (isTokenAddress(daiAddress, Ethereum)) {
|
|
29233
|
-
* // TypeScript knows daiAddress is TokenAddress here
|
|
29234
|
-
* console.log('Valid token address:', daiAddress)
|
|
29235
|
-
* }
|
|
29835
|
+
* const delegate = requireAdapterContract(chain)
|
|
29236
29836
|
*
|
|
29237
|
-
*
|
|
29238
|
-
*
|
|
29837
|
+
* const approvalTxHash = await approveMaxIfNeeded({
|
|
29838
|
+
* adapter,
|
|
29839
|
+
* chain,
|
|
29840
|
+
* tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
29841
|
+
* delegate,
|
|
29842
|
+
* address,
|
|
29843
|
+
* revertMessage: 'USDC approval reverted on-chain',
|
|
29844
|
+
* })
|
|
29239
29845
|
* ```
|
|
29846
|
+
*
|
|
29847
|
+
* @internal
|
|
29240
29848
|
*/
|
|
29241
|
-
function
|
|
29242
|
-
|
|
29849
|
+
async function approveMaxIfNeeded(params) {
|
|
29850
|
+
const { adapter, chain, tokenAddress, delegate, address, revertMessage } = params;
|
|
29851
|
+
const allowancePrepared = await adapter.prepareAction('token.allowance', { tokenAddress, delegate }, { chain, address });
|
|
29852
|
+
const allowanceRaw = await allowancePrepared.execute();
|
|
29853
|
+
const currentAllowance = parseAllowanceResponse(allowanceRaw);
|
|
29854
|
+
if (currentAllowance >= ALLOWANCE_THRESHOLD) {
|
|
29855
|
+
return undefined;
|
|
29856
|
+
}
|
|
29857
|
+
const approvalPrepared = await adapter.prepareAction('token.approve', { tokenAddress, delegate, amount: MAX_UINT256$1 }, { chain, address });
|
|
29858
|
+
const gasLimitOverride = await estimateBufferedGasLimit(approvalPrepared);
|
|
29859
|
+
const approvalTxHash = approvalPrepared.type === 'evm' && gasLimitOverride !== undefined
|
|
29860
|
+
? await approvalPrepared.execute({ gasLimit: gasLimitOverride })
|
|
29861
|
+
: await approvalPrepared.execute();
|
|
29862
|
+
const approvalReceipt = await adapter.waitForTransaction(approvalTxHash, { confirmations: 1 }, chain);
|
|
29863
|
+
if (approvalReceipt.status === 'reverted') {
|
|
29864
|
+
throw createTransactionRevertedError(chain.name, revertMessage, undefined, approvalTxHash);
|
|
29865
|
+
}
|
|
29866
|
+
return approvalTxHash;
|
|
29243
29867
|
}
|
|
29868
|
+
|
|
29244
29869
|
/**
|
|
29245
|
-
*
|
|
29870
|
+
* Build the `tokenInputs` array forwarded to the adapter contract's
|
|
29871
|
+
* `execute(executeParams, tokenInputs, signature)` call.
|
|
29246
29872
|
*
|
|
29247
|
-
*
|
|
29248
|
-
*
|
|
29249
|
-
*
|
|
29250
|
-
*
|
|
29873
|
+
* The adapter contract pulls ERC-20 tokens from `msg.sender` using the
|
|
29874
|
+
* entries in `tokenInputs`, combined either with a pre-existing allowance
|
|
29875
|
+
* ({@link PermitType.NONE}) or a permit signature ({@link PermitType.EIP2612}).
|
|
29876
|
+
* Without an entry, the contract never moves the user's tokens and the
|
|
29877
|
+
* nested instruction reverts with "ERC20: transfer amount exceeds balance"
|
|
29878
|
+
* when the adapter tries to forward tokens it does not hold.
|
|
29251
29879
|
*
|
|
29252
|
-
*
|
|
29253
|
-
*
|
|
29880
|
+
* Earn deposit and withdraw responses are schema-validated to contain at
|
|
29881
|
+
* least one instruction. This helper emits one `PermitType.NONE` entry per
|
|
29882
|
+
* instruction with a positive `amountToApprove`. Return an empty array when
|
|
29883
|
+
* the service did not request any token pulls.
|
|
29254
29884
|
*
|
|
29255
|
-
* @param
|
|
29256
|
-
* @param
|
|
29257
|
-
* @returns
|
|
29885
|
+
* @param executionParams - Service-signed `ExecutionParams` forwarded to the adapter.
|
|
29886
|
+
* @param approvedToken - Token approved for adapter spending on this chain.
|
|
29887
|
+
* @returns `TokenInput[]` entries when pulls are required, `[]` otherwise.
|
|
29888
|
+
* @throws {@link KitError} If `tokenIn` differs from the approved token.
|
|
29258
29889
|
*
|
|
29259
29890
|
* @example
|
|
29260
29891
|
* ```typescript
|
|
29261
|
-
*
|
|
29262
|
-
*
|
|
29263
|
-
*
|
|
29264
|
-
*
|
|
29265
|
-
* const usdc = validateToken('USDC', Ethereum)
|
|
29266
|
-
* console.log(usdc) // { isAlias: true, isAddress: false, isValid: true }
|
|
29267
|
-
*
|
|
29268
|
-
* // Custom token address
|
|
29269
|
-
* const dai = validateToken('0x6B175474E89094C44Da98b954EedeAC495271d0F', Ethereum)
|
|
29270
|
-
* console.log(dai) // { isAlias: false, isAddress: true, isValid: true }
|
|
29892
|
+
* const approvedToken = assertHexAddress(
|
|
29893
|
+
* 'chain.usdcAddress',
|
|
29894
|
+
* chain.usdcAddress,
|
|
29895
|
+
* )
|
|
29271
29896
|
*
|
|
29272
|
-
*
|
|
29273
|
-
*
|
|
29274
|
-
*
|
|
29897
|
+
* const tokenInputs = buildEarnTokenInputs(
|
|
29898
|
+
* executionPayload.executionParams,
|
|
29899
|
+
* approvedToken,
|
|
29900
|
+
* )
|
|
29275
29901
|
* ```
|
|
29902
|
+
*
|
|
29903
|
+
* @internal
|
|
29276
29904
|
*/
|
|
29277
|
-
function
|
|
29278
|
-
|
|
29279
|
-
|
|
29280
|
-
|
|
29281
|
-
|
|
29282
|
-
|
|
29283
|
-
|
|
29284
|
-
|
|
29285
|
-
|
|
29286
|
-
|
|
29287
|
-
|
|
29288
|
-
|
|
29289
|
-
|
|
29290
|
-
|
|
29291
|
-
|
|
29292
|
-
|
|
29293
|
-
|
|
29905
|
+
function buildEarnTokenInputs(executionParams, approvedToken) {
|
|
29906
|
+
const tokenInputs = [];
|
|
29907
|
+
executionParams.instructions.forEach((instruction, index) => {
|
|
29908
|
+
const amount = BigInt(instruction.amountToApprove);
|
|
29909
|
+
if (amount <= 0n) {
|
|
29910
|
+
return;
|
|
29911
|
+
}
|
|
29912
|
+
const { tokenIn } = instruction;
|
|
29913
|
+
if (tokenIn.toLowerCase() !== approvedToken.toLowerCase()) {
|
|
29914
|
+
throw createValidationFailedError$1(`executionParams.instructions[${index.toString()}].tokenIn`, tokenIn, 'tokenIn must match the token approved for adapter spending');
|
|
29915
|
+
}
|
|
29916
|
+
tokenInputs.push({
|
|
29917
|
+
permitType: PermitType.NONE,
|
|
29918
|
+
token: tokenIn,
|
|
29919
|
+
amount,
|
|
29920
|
+
permitCalldata: '0x',
|
|
29921
|
+
});
|
|
29922
|
+
});
|
|
29923
|
+
return tokenInputs;
|
|
29294
29924
|
}
|
|
29295
29925
|
|
|
29296
29926
|
/**
|
|
29297
|
-
*
|
|
29927
|
+
* Prepare an earn adapter action, execute it, wait for confirmation, and
|
|
29928
|
+
* throw a structured revert error if the receipt status is `'reverted'`.
|
|
29298
29929
|
*
|
|
29299
|
-
*
|
|
29300
|
-
*
|
|
29301
|
-
*
|
|
29930
|
+
* Wraps the prepareAction, execute, waitForTransaction, and status check
|
|
29931
|
+
* sequence so the provider can dispatch any `earn.*` action key with
|
|
29932
|
+
* consistent revert handling.
|
|
29302
29933
|
*
|
|
29303
|
-
*
|
|
29304
|
-
*
|
|
29305
|
-
*
|
|
29934
|
+
* Gas estimation: after preparing the action the helper calls
|
|
29935
|
+
* {@link estimateBufferedGasLimit}, which applies a safety buffer to the
|
|
29936
|
+
* returned gas value. If estimation fails, execution falls through to the
|
|
29937
|
+
* adapter's default gas handling.
|
|
29306
29938
|
*
|
|
29307
|
-
* @
|
|
29308
|
-
* @param params -
|
|
29309
|
-
* @returns
|
|
29310
|
-
* @throws If
|
|
29311
|
-
* @throws If the bridge route is not supported
|
|
29312
|
-
* @throws If estimation fails due to network or configuration issues
|
|
29939
|
+
* @typeParam TActionKey - Earn action key being executed.
|
|
29940
|
+
* @param params - Adapter action, execution context, and revert message.
|
|
29941
|
+
* @returns The confirmed on-chain transaction hash and explorer URL.
|
|
29942
|
+
* @throws {@link KitError} If the transaction reverts on-chain.
|
|
29313
29943
|
*
|
|
29314
29944
|
* @example
|
|
29315
29945
|
* ```typescript
|
|
29316
|
-
*
|
|
29317
|
-
*
|
|
29318
|
-
*
|
|
29319
|
-
*
|
|
29320
|
-
*
|
|
29321
|
-
*
|
|
29946
|
+
* const { txHash, explorerUrl } = await executeEarnAction({
|
|
29947
|
+
* adapter,
|
|
29948
|
+
* chain,
|
|
29949
|
+
* address,
|
|
29950
|
+
* actionKey: 'earn.deposit',
|
|
29951
|
+
* actionParams: { executeParams, tokenInputs, signature },
|
|
29952
|
+
* revertMessage: 'Earn deposit reverted on-chain',
|
|
29322
29953
|
* })
|
|
29954
|
+
* ```
|
|
29323
29955
|
*
|
|
29324
|
-
*
|
|
29956
|
+
* @internal
|
|
29957
|
+
*/
|
|
29958
|
+
async function executeEarnAction(params) {
|
|
29959
|
+
const { adapter, chain, address, actionKey, actionParams, revertMessage } = params;
|
|
29960
|
+
const prepared = await adapter.prepareAction(actionKey, actionParams, {
|
|
29961
|
+
chain,
|
|
29962
|
+
address,
|
|
29963
|
+
});
|
|
29964
|
+
const gasLimitOverride = await estimateBufferedGasLimit(prepared);
|
|
29965
|
+
const txHash = prepared.type === 'evm' && gasLimitOverride !== undefined
|
|
29966
|
+
? await prepared.execute({ gasLimit: gasLimitOverride })
|
|
29967
|
+
: await prepared.execute();
|
|
29968
|
+
const explorerUrl = buildExplorerUrl(chain, txHash);
|
|
29969
|
+
const receipt = await adapter.waitForTransaction(txHash, { confirmations: 1 }, chain);
|
|
29970
|
+
if (receipt.status === 'reverted') {
|
|
29971
|
+
throw createTransactionRevertedError(chain.name, revertMessage, undefined, txHash, explorerUrl);
|
|
29972
|
+
}
|
|
29973
|
+
return { txHash, explorerUrl };
|
|
29974
|
+
}
|
|
29975
|
+
|
|
29976
|
+
/**
|
|
29977
|
+
* Validate that a service-signed execution payload has not expired before
|
|
29978
|
+
* the SDK asks the wallet to broadcast a transaction.
|
|
29979
|
+
*
|
|
29980
|
+
* @internal
|
|
29981
|
+
*/
|
|
29982
|
+
function validateExecutionDeadline(executionParams) {
|
|
29983
|
+
const deadline = BigInt(executionParams.deadline);
|
|
29984
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
29985
|
+
if (deadline <= now) {
|
|
29986
|
+
throw createValidationFailedError$1('executionParams.deadline', executionParams.deadline, 'Earn execution deadline has expired');
|
|
29987
|
+
}
|
|
29988
|
+
}
|
|
29989
|
+
|
|
29990
|
+
// ---------------------------------------------------------------------------
|
|
29991
|
+
// Shared primitives
|
|
29992
|
+
// ---------------------------------------------------------------------------
|
|
29993
|
+
function isBigIntLike(value) {
|
|
29994
|
+
try {
|
|
29995
|
+
BigInt(value);
|
|
29996
|
+
return true;
|
|
29997
|
+
}
|
|
29998
|
+
catch {
|
|
29999
|
+
return false;
|
|
30000
|
+
}
|
|
30001
|
+
}
|
|
30002
|
+
function isNonNegativeBigIntLike(value) {
|
|
30003
|
+
try {
|
|
30004
|
+
return BigInt(value) >= 0n;
|
|
30005
|
+
}
|
|
30006
|
+
catch {
|
|
30007
|
+
return false;
|
|
30008
|
+
}
|
|
30009
|
+
}
|
|
30010
|
+
const hexSignatureSchema = evmSignatureSchema;
|
|
30011
|
+
const hexAddressSchema = evmAddressSchema;
|
|
30012
|
+
/**
|
|
30013
|
+
* Zod schema for a non-negative uint256-like value.
|
|
30014
|
+
*
|
|
30015
|
+
* The service emits uint256 fields as decimal strings in JSON, while tests and
|
|
30016
|
+
* lower-level SDK callers may already hold bigint values. Keep the parsed value
|
|
30017
|
+
* unchanged so signed execution params can be forwarded verbatim.
|
|
30018
|
+
*
|
|
30019
|
+
* @internal
|
|
30020
|
+
*/
|
|
30021
|
+
const uint256LikeSchema = z
|
|
30022
|
+
.union([z.string(), z.bigint()])
|
|
30023
|
+
.refine(isNonNegativeBigIntLike, {
|
|
30024
|
+
message: 'value must be a non-negative bigint-compatible string or bigint',
|
|
30025
|
+
});
|
|
30026
|
+
/**
|
|
30027
|
+
* Zod schema for typed amount objects emitted by the API.
|
|
30028
|
+
*
|
|
30029
|
+
* @internal
|
|
30030
|
+
*/
|
|
30031
|
+
const amountJsonSchema = z.object({
|
|
30032
|
+
raw: z.string().refine(isBigIntLike, {
|
|
30033
|
+
message: 'raw must be a bigint-compatible string',
|
|
30034
|
+
}),
|
|
30035
|
+
decimals: z.number().int().nonnegative(),
|
|
30036
|
+
});
|
|
30037
|
+
/** @internal */
|
|
30038
|
+
const hexBytesSchema = z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
|
|
30039
|
+
message: 'value must be a 0x-prefixed even-length hex string',
|
|
30040
|
+
});
|
|
30041
|
+
// ---------------------------------------------------------------------------
|
|
30042
|
+
// Vault response schemas
|
|
30043
|
+
// ---------------------------------------------------------------------------
|
|
30044
|
+
/**
|
|
30045
|
+
* Zod schema for a vault reward token in the API response.
|
|
30046
|
+
*
|
|
30047
|
+
* @internal
|
|
30048
|
+
*/
|
|
30049
|
+
const vaultRewardSchema = z.object({
|
|
30050
|
+
token: z.string(),
|
|
30051
|
+
tokenAddress: z.string(),
|
|
30052
|
+
apy: z.number(),
|
|
30053
|
+
});
|
|
30054
|
+
/**
|
|
30055
|
+
* Zod schema for a vault collateral market in the API response.
|
|
30056
|
+
*
|
|
30057
|
+
* @internal
|
|
30058
|
+
*/
|
|
30059
|
+
const collateralSchema = z.object({
|
|
30060
|
+
asset: z.string(),
|
|
30061
|
+
assetAddress: z.string(),
|
|
30062
|
+
lltv: z.number(),
|
|
30063
|
+
supplyUsd: z.number(),
|
|
30064
|
+
});
|
|
30065
|
+
/**
|
|
30066
|
+
* Zod schema for a Morpho vault warning in the API response.
|
|
30067
|
+
*
|
|
30068
|
+
* @internal
|
|
30069
|
+
*/
|
|
30070
|
+
const vaultWarningSchema = z.object({
|
|
30071
|
+
type: z.string(),
|
|
30072
|
+
level: z.enum(['YELLOW', 'RED']),
|
|
30073
|
+
});
|
|
30074
|
+
/**
|
|
30075
|
+
* Zod schema for a single vault info object in the API response.
|
|
30076
|
+
*
|
|
30077
|
+
* @internal
|
|
30078
|
+
*/
|
|
30079
|
+
const vaultInfoResponseSchema = z.object({
|
|
30080
|
+
vaultAddress: z.string(),
|
|
30081
|
+
chain: z.string(),
|
|
30082
|
+
name: z.string(),
|
|
30083
|
+
protocol: z.string(),
|
|
30084
|
+
asset: z.string(),
|
|
30085
|
+
assetAddress: z.string(),
|
|
30086
|
+
currentApy: z.number(),
|
|
30087
|
+
nativeApy: z.number(),
|
|
30088
|
+
vaultFee: z.number(),
|
|
30089
|
+
rewards: z.array(vaultRewardSchema),
|
|
30090
|
+
collateral: z.array(collateralSchema),
|
|
30091
|
+
totalDeposits: amountJsonSchema,
|
|
30092
|
+
liquidity: amountJsonSchema,
|
|
30093
|
+
status: z.enum(['active', 'low_liquidity']),
|
|
30094
|
+
warnings: z.array(vaultWarningSchema).optional(),
|
|
30095
|
+
earnKitWarnings: z.array(z.string()).optional(),
|
|
30096
|
+
});
|
|
30097
|
+
// ---------------------------------------------------------------------------
|
|
30098
|
+
// Position response schema
|
|
30099
|
+
// ---------------------------------------------------------------------------
|
|
30100
|
+
/**
|
|
30101
|
+
* Zod schema for an accrued reward in the position API response.
|
|
30102
|
+
*
|
|
30103
|
+
* @internal
|
|
30104
|
+
*/
|
|
30105
|
+
const accruedRewardSchema = z.object({
|
|
30106
|
+
token: z.string(),
|
|
30107
|
+
symbol: z.string(),
|
|
30108
|
+
amount: amountJsonSchema,
|
|
30109
|
+
});
|
|
30110
|
+
const positionPnlSchema = z.discriminatedUnion('status', [
|
|
30111
|
+
z.object({
|
|
30112
|
+
status: z.literal('available'),
|
|
30113
|
+
principalDeposited: amountJsonSchema,
|
|
30114
|
+
totalYieldEarned: amountJsonSchema,
|
|
30115
|
+
}),
|
|
30116
|
+
z.object({
|
|
30117
|
+
status: z.literal('pending'),
|
|
30118
|
+
}),
|
|
30119
|
+
z.object({
|
|
30120
|
+
status: z.literal('unavailable'),
|
|
30121
|
+
reason: z.string(),
|
|
30122
|
+
}),
|
|
30123
|
+
]);
|
|
30124
|
+
/**
|
|
30125
|
+
* Zod schema for the inner position payload.
|
|
30126
|
+
*
|
|
30127
|
+
* @internal
|
|
30128
|
+
*/
|
|
30129
|
+
const positionPayloadSchema = z.object({
|
|
30130
|
+
wallet: z.string(),
|
|
30131
|
+
chain: z.string(),
|
|
30132
|
+
vaultAddress: z.string(),
|
|
30133
|
+
vaultName: z.string(),
|
|
30134
|
+
asset: z.string(),
|
|
30135
|
+
currentBalance: amountJsonSchema,
|
|
30136
|
+
currentApy: z.number(),
|
|
30137
|
+
shares: amountJsonSchema,
|
|
30138
|
+
pnl: positionPnlSchema,
|
|
30139
|
+
accruedRewards: z.array(accruedRewardSchema).optional(),
|
|
30140
|
+
rewardsUnavailableReason: z.string().optional(),
|
|
30141
|
+
});
|
|
30142
|
+
/**
|
|
30143
|
+
* Zod schema for the `GET /v1/earnKit/position/{address}` API response.
|
|
30144
|
+
*
|
|
30145
|
+
* The Earn Service API wraps the position payload in a `data` envelope.
|
|
30146
|
+
*
|
|
30147
|
+
* @internal
|
|
30148
|
+
*/
|
|
30149
|
+
const positionResponseSchema = z.object({
|
|
30150
|
+
data: positionPayloadSchema,
|
|
30151
|
+
});
|
|
30152
|
+
// ---------------------------------------------------------------------------
|
|
30153
|
+
// Deposit response schema
|
|
30154
|
+
// ---------------------------------------------------------------------------
|
|
30155
|
+
/**
|
|
30156
|
+
* Zod schema for the deposit instruction fields used by the provider.
|
|
30157
|
+
*
|
|
30158
|
+
* The canonical instruction shape includes more fields, but deposit
|
|
30159
|
+
* orchestration only depends on `tokenIn` and `amountToApprove` to build the
|
|
30160
|
+
* token input and approval flow. Preserve additional signed fields with
|
|
30161
|
+
* `passthrough()` so the adapter receives the full service payload.
|
|
30162
|
+
*
|
|
30163
|
+
* @internal
|
|
30164
|
+
*/
|
|
30165
|
+
const earnInstructionSchema = z
|
|
30166
|
+
.object({
|
|
30167
|
+
tokenIn: hexAddressSchema,
|
|
30168
|
+
amountToApprove: uint256LikeSchema,
|
|
30169
|
+
})
|
|
30170
|
+
.passthrough();
|
|
30171
|
+
/**
|
|
30172
|
+
* Zod schema for earn execution params returned by the earn service.
|
|
30173
|
+
*
|
|
30174
|
+
* Require at least one instruction so the provider can derive token pulls
|
|
30175
|
+
* before it submits the signed params on-chain. Claim rewards has its own
|
|
30176
|
+
* schema because it does not run approval preflight.
|
|
30177
|
+
*
|
|
30178
|
+
* @internal
|
|
30179
|
+
*/
|
|
30180
|
+
const earnExecutionParamsSchema = z
|
|
30181
|
+
.object({
|
|
30182
|
+
instructions: z.tuple([earnInstructionSchema]).rest(earnInstructionSchema),
|
|
30183
|
+
deadline: uint256LikeSchema,
|
|
30184
|
+
})
|
|
30185
|
+
.passthrough();
|
|
30186
|
+
/** @internal */
|
|
30187
|
+
const depositExecutionParamsSchema = earnExecutionParamsSchema;
|
|
30188
|
+
/** @internal */
|
|
30189
|
+
const withdrawExecutionParamsSchema = earnExecutionParamsSchema;
|
|
30190
|
+
/**
|
|
30191
|
+
* Zod schema for the claim rewards instruction fields that must be safe to
|
|
30192
|
+
* ABI-encode.
|
|
30193
|
+
*
|
|
30194
|
+
* Claim rewards has no approval preflight, so this validates the forwarded
|
|
30195
|
+
* signed payload at the provider boundary. Additional instruction fields are
|
|
30196
|
+
* accepted and preserved.
|
|
30197
|
+
*
|
|
30198
|
+
* @internal
|
|
30199
|
+
*/
|
|
30200
|
+
const claimRewardsInstructionSchema = z
|
|
30201
|
+
.object({
|
|
30202
|
+
target: hexAddressSchema,
|
|
30203
|
+
data: hexBytesSchema,
|
|
30204
|
+
value: uint256LikeSchema,
|
|
30205
|
+
tokenIn: hexAddressSchema,
|
|
30206
|
+
amountToApprove: uint256LikeSchema,
|
|
30207
|
+
tokenOut: hexAddressSchema,
|
|
30208
|
+
minTokenOut: uint256LikeSchema,
|
|
30209
|
+
})
|
|
30210
|
+
.passthrough();
|
|
30211
|
+
/** @internal */
|
|
30212
|
+
const claimRewardsTokenSchema = z
|
|
30213
|
+
.object({
|
|
30214
|
+
token: hexAddressSchema,
|
|
30215
|
+
beneficiary: hexAddressSchema,
|
|
30216
|
+
})
|
|
30217
|
+
.passthrough();
|
|
30218
|
+
/**
|
|
30219
|
+
* Zod schema for signed claim rewards execution params.
|
|
30220
|
+
*
|
|
30221
|
+
* This enforces the required envelope and preserves additive fields so the
|
|
30222
|
+
* adapter receives the signed params verbatim.
|
|
30223
|
+
*
|
|
30224
|
+
* @internal
|
|
30225
|
+
*/
|
|
30226
|
+
const claimRewardsExecutionParamsSchema = z
|
|
30227
|
+
.object({
|
|
30228
|
+
instructions: z
|
|
30229
|
+
.tuple([claimRewardsInstructionSchema])
|
|
30230
|
+
.rest(claimRewardsInstructionSchema),
|
|
30231
|
+
tokens: z.tuple([claimRewardsTokenSchema]).rest(claimRewardsTokenSchema),
|
|
30232
|
+
execId: uint256LikeSchema,
|
|
30233
|
+
deadline: uint256LikeSchema,
|
|
30234
|
+
metadata: hexBytesSchema,
|
|
30235
|
+
})
|
|
30236
|
+
.passthrough();
|
|
30237
|
+
/**
|
|
30238
|
+
* Zod schema for the deposit payload inside the API `data` envelope.
|
|
30239
|
+
*
|
|
30240
|
+
* @internal
|
|
30241
|
+
*/
|
|
30242
|
+
const depositPayloadSchema = z.object({
|
|
30243
|
+
executionParams: depositExecutionParamsSchema,
|
|
30244
|
+
signature: hexSignatureSchema,
|
|
30245
|
+
});
|
|
30246
|
+
/**
|
|
30247
|
+
* Zod schema for the `POST /v1/earnKit/deposit` API response.
|
|
30248
|
+
*
|
|
30249
|
+
* The API wraps the deposit payload in a `data` envelope.
|
|
30250
|
+
*
|
|
30251
|
+
* @internal
|
|
30252
|
+
*/
|
|
30253
|
+
const depositResponseSchema = z.object({
|
|
30254
|
+
data: depositPayloadSchema,
|
|
30255
|
+
});
|
|
30256
|
+
// ---------------------------------------------------------------------------
|
|
30257
|
+
// Withdraw response schema
|
|
30258
|
+
// ---------------------------------------------------------------------------
|
|
30259
|
+
/**
|
|
30260
|
+
* Zod schema for the withdraw payload inside the API `data` envelope.
|
|
30261
|
+
*
|
|
30262
|
+
* @internal
|
|
30263
|
+
*/
|
|
30264
|
+
const withdrawPayloadSchema = z.object({
|
|
30265
|
+
executionParams: withdrawExecutionParamsSchema,
|
|
30266
|
+
signature: hexSignatureSchema,
|
|
30267
|
+
});
|
|
30268
|
+
/**
|
|
30269
|
+
* Zod schema for the `POST /v1/earnKit/withdraw` API response.
|
|
30270
|
+
*
|
|
30271
|
+
* The Earn Service API wraps the withdraw payload in a `data` envelope.
|
|
30272
|
+
*
|
|
30273
|
+
* @internal
|
|
30274
|
+
*/
|
|
30275
|
+
const withdrawResponseSchema = z.object({
|
|
30276
|
+
data: withdrawPayloadSchema,
|
|
30277
|
+
});
|
|
30278
|
+
// ---------------------------------------------------------------------------
|
|
30279
|
+
// Claim rewards response schema
|
|
30280
|
+
// ---------------------------------------------------------------------------
|
|
30281
|
+
/**
|
|
30282
|
+
* Zod schema for a claimed reward amount in the API response.
|
|
30283
|
+
*
|
|
30284
|
+
* @internal
|
|
30285
|
+
*/
|
|
30286
|
+
const claimedAmountSchema = z.object({
|
|
30287
|
+
token: hexAddressSchema,
|
|
30288
|
+
symbol: z.string(),
|
|
30289
|
+
amount: amountJsonSchema,
|
|
30290
|
+
});
|
|
30291
|
+
/**
|
|
30292
|
+
* Zod schema for the claim rewards payload inside the API `data` envelope.
|
|
30293
|
+
*
|
|
30294
|
+
* Enforce that `executionParams` and `signature` are either both
|
|
30295
|
+
* present (claimable rewards) or both absent (nothing to claim).
|
|
30296
|
+
*
|
|
30297
|
+
* @internal
|
|
30298
|
+
*/
|
|
30299
|
+
const claimRewardsPayloadSchema = z
|
|
30300
|
+
.object({
|
|
30301
|
+
rewards: z.array(claimedAmountSchema),
|
|
30302
|
+
executionParams: claimRewardsExecutionParamsSchema.optional(),
|
|
30303
|
+
signature: hexSignatureSchema.optional(),
|
|
30304
|
+
})
|
|
30305
|
+
.refine((data) => {
|
|
30306
|
+
const signedFieldCount = (data.executionParams === undefined ? 0 : 1) +
|
|
30307
|
+
(data.signature === undefined ? 0 : 1);
|
|
30308
|
+
if (data.rewards.length > 0) {
|
|
30309
|
+
return signedFieldCount === 2;
|
|
30310
|
+
}
|
|
30311
|
+
return signedFieldCount === 0;
|
|
30312
|
+
}, {
|
|
30313
|
+
message: 'claim rewards response must include executionParams and signature only when rewards are claimable',
|
|
30314
|
+
});
|
|
30315
|
+
/**
|
|
30316
|
+
* Zod schema for the `POST /v1/earnKit/claimRewards` API response.
|
|
30317
|
+
*
|
|
30318
|
+
* The Earn Service API wraps the claim rewards payload in a `data` envelope.
|
|
30319
|
+
*
|
|
30320
|
+
* @internal
|
|
30321
|
+
*/
|
|
30322
|
+
const claimRewardsResponseSchema = z.object({
|
|
30323
|
+
data: claimRewardsPayloadSchema,
|
|
30324
|
+
});
|
|
30325
|
+
// ---------------------------------------------------------------------------
|
|
30326
|
+
// Deposit quote response schema
|
|
30327
|
+
// ---------------------------------------------------------------------------
|
|
30328
|
+
/**
|
|
30329
|
+
* Zod schema for the inner deposit quote payload.
|
|
30330
|
+
*
|
|
30331
|
+
* @internal
|
|
30332
|
+
*/
|
|
30333
|
+
const depositQuotePayloadSchema = z.object({
|
|
30334
|
+
vaultAddress: z.string(),
|
|
30335
|
+
vaultName: z.string(),
|
|
30336
|
+
asset: z.string(),
|
|
30337
|
+
depositAmount: amountJsonSchema,
|
|
30338
|
+
expectedShares: amountJsonSchema,
|
|
30339
|
+
sharePrice: z.string(),
|
|
30340
|
+
currentApy: z.number(),
|
|
30341
|
+
});
|
|
30342
|
+
/**
|
|
30343
|
+
* Zod schema for the `POST /v1/earnKit/deposit/quote` API response.
|
|
30344
|
+
*
|
|
30345
|
+
* The Zenith API wraps the payload in a `data` envelope.
|
|
30346
|
+
*
|
|
30347
|
+
* @internal
|
|
30348
|
+
*/
|
|
30349
|
+
const depositQuoteResponseSchema = z.object({
|
|
30350
|
+
data: depositQuotePayloadSchema,
|
|
30351
|
+
});
|
|
30352
|
+
// ---------------------------------------------------------------------------
|
|
30353
|
+
// Withdrawal quote response schema
|
|
30354
|
+
// ---------------------------------------------------------------------------
|
|
30355
|
+
/**
|
|
30356
|
+
* Zod schema for a withdrawal quote fee in the API response.
|
|
30357
|
+
*
|
|
30358
|
+
* @internal
|
|
30359
|
+
*/
|
|
30360
|
+
const withdrawalQuoteFeeSchema = z.object({
|
|
30361
|
+
token: z.string(),
|
|
30362
|
+
amount: amountJsonSchema,
|
|
30363
|
+
});
|
|
30364
|
+
/**
|
|
30365
|
+
* Zod schema for the inner withdrawal quote payload.
|
|
30366
|
+
*
|
|
30367
|
+
* @internal
|
|
30368
|
+
*/
|
|
30369
|
+
const withdrawalQuotePayloadSchema = z.object({
|
|
30370
|
+
vaultAddress: z.string(),
|
|
30371
|
+
vaultName: z.string(),
|
|
30372
|
+
asset: z.string(),
|
|
30373
|
+
withdrawAmount: amountJsonSchema,
|
|
30374
|
+
sharesToRedeem: amountJsonSchema,
|
|
30375
|
+
sharePrice: z.string(),
|
|
30376
|
+
maxWithdrawable: amountJsonSchema,
|
|
30377
|
+
fees: z.array(withdrawalQuoteFeeSchema),
|
|
30378
|
+
warnings: z.array(z.string()).optional(),
|
|
30379
|
+
});
|
|
30380
|
+
/**
|
|
30381
|
+
* Zod schema for the `POST /v1/earnKit/withdrawal/quote` API response.
|
|
30382
|
+
*
|
|
30383
|
+
* The Zenith API wraps the payload in a `data` envelope.
|
|
30384
|
+
*
|
|
30385
|
+
* @internal
|
|
30386
|
+
*/
|
|
30387
|
+
const withdrawalQuoteResponseSchema = z.object({
|
|
30388
|
+
data: withdrawalQuotePayloadSchema,
|
|
30389
|
+
});
|
|
30390
|
+
// ---------------------------------------------------------------------------
|
|
30391
|
+
// Claim rewards quote response schema
|
|
30392
|
+
// ---------------------------------------------------------------------------
|
|
30393
|
+
/**
|
|
30394
|
+
* Zod schema for a reward in the claim rewards quote response.
|
|
30395
|
+
*
|
|
30396
|
+
* @internal
|
|
30397
|
+
*/
|
|
30398
|
+
const claimRewardsQuoteRewardSchema = z.object({
|
|
30399
|
+
token: z.string(),
|
|
30400
|
+
symbol: z.string(),
|
|
30401
|
+
amount: amountJsonSchema,
|
|
30402
|
+
});
|
|
30403
|
+
/**
|
|
30404
|
+
* Zod schema for the inner claim rewards quote payload.
|
|
30405
|
+
*
|
|
30406
|
+
* @internal
|
|
30407
|
+
*/
|
|
30408
|
+
const claimRewardsQuotePayloadSchema = z.object({
|
|
30409
|
+
rewards: z.array(claimRewardsQuoteRewardSchema),
|
|
30410
|
+
});
|
|
30411
|
+
/**
|
|
30412
|
+
* Zod schema for the `POST /v1/earnKit/claimRewards/quote` API response.
|
|
30413
|
+
*
|
|
30414
|
+
* The Zenith API wraps the payload in a `data` envelope.
|
|
30415
|
+
*
|
|
30416
|
+
* @internal
|
|
30417
|
+
*/
|
|
30418
|
+
const claimRewardsQuoteResponseSchema = z.object({
|
|
30419
|
+
data: claimRewardsQuotePayloadSchema,
|
|
30420
|
+
});
|
|
30421
|
+
// ---------------------------------------------------------------------------
|
|
30422
|
+
// Batch vault response schema
|
|
30423
|
+
// ---------------------------------------------------------------------------
|
|
30424
|
+
/**
|
|
30425
|
+
* Zod schema for a per-vault error in the batch vault API response.
|
|
30426
|
+
*
|
|
30427
|
+
* @internal
|
|
30428
|
+
*/
|
|
30429
|
+
const vaultErrorSchema = z.object({
|
|
30430
|
+
chain: z.string(),
|
|
30431
|
+
vaultAddress: z.string(),
|
|
30432
|
+
code: z.number(),
|
|
30433
|
+
message: z.string(),
|
|
30434
|
+
});
|
|
30435
|
+
/**
|
|
30436
|
+
* Zod schema for the inner batch vault payload.
|
|
30437
|
+
*
|
|
30438
|
+
* @internal
|
|
30439
|
+
*/
|
|
30440
|
+
const getVaultsPayloadSchema = z.object({
|
|
30441
|
+
vaults: z.array(vaultInfoResponseSchema),
|
|
30442
|
+
errors: z.array(vaultErrorSchema),
|
|
30443
|
+
});
|
|
30444
|
+
/**
|
|
30445
|
+
* Zod schema for the `GET /v1/earnKit/vaults` batch API response.
|
|
30446
|
+
*
|
|
30447
|
+
* The Earn Service API wraps the vault payload in a `data` envelope.
|
|
30448
|
+
*
|
|
30449
|
+
* @internal
|
|
30450
|
+
*/
|
|
30451
|
+
const getVaultsResponseSchema = z.object({
|
|
30452
|
+
data: getVaultsPayloadSchema,
|
|
30453
|
+
});
|
|
30454
|
+
|
|
30455
|
+
/**
|
|
30456
|
+
* Type guard for the batch vault lookup API response.
|
|
30457
|
+
*
|
|
30458
|
+
* @param value - Unknown response value to validate
|
|
30459
|
+
* @returns True when the value matches the batch vault response shape
|
|
30460
|
+
*
|
|
30461
|
+
* @internal
|
|
30462
|
+
*/
|
|
30463
|
+
function isGetVaultsResponse(value) {
|
|
30464
|
+
return getVaultsResponseSchema.safeParse(value).success;
|
|
30465
|
+
}
|
|
30466
|
+
/**
|
|
30467
|
+
* Type guard for the position API response.
|
|
30468
|
+
*
|
|
30469
|
+
* @param value - Unknown response value to validate
|
|
30470
|
+
* @returns True when the value matches the position response shape
|
|
30471
|
+
*
|
|
30472
|
+
* @internal
|
|
30473
|
+
*/
|
|
30474
|
+
function isPositionResponse(value) {
|
|
30475
|
+
return positionResponseSchema.safeParse(value).success;
|
|
30476
|
+
}
|
|
30477
|
+
/**
|
|
30478
|
+
* Type guard for the deposit API response.
|
|
30479
|
+
*
|
|
30480
|
+
* @param value - Unknown response value to validate
|
|
30481
|
+
* @returns True when the value matches the deposit response shape
|
|
30482
|
+
*
|
|
30483
|
+
* @internal
|
|
30484
|
+
*/
|
|
30485
|
+
function isDepositResponse(value) {
|
|
30486
|
+
return depositResponseSchema.safeParse(value).success;
|
|
30487
|
+
}
|
|
30488
|
+
/**
|
|
30489
|
+
* Type guard for the withdraw API response.
|
|
30490
|
+
*
|
|
30491
|
+
* @param value - Unknown response value to validate
|
|
30492
|
+
* @returns True when the value matches the withdraw response shape
|
|
30493
|
+
*
|
|
30494
|
+
* @internal
|
|
30495
|
+
*/
|
|
30496
|
+
function isWithdrawResponse(value) {
|
|
30497
|
+
return withdrawResponseSchema.safeParse(value).success;
|
|
30498
|
+
}
|
|
30499
|
+
/**
|
|
30500
|
+
* Type guard for the claim rewards API response.
|
|
30501
|
+
*
|
|
30502
|
+
* @param value - Unknown response value to validate
|
|
30503
|
+
* @returns True when the value matches the claim rewards response shape
|
|
30504
|
+
*
|
|
30505
|
+
* @internal
|
|
30506
|
+
*/
|
|
30507
|
+
function isClaimRewardsResponse(value) {
|
|
30508
|
+
return claimRewardsResponseSchema.safeParse(value).success;
|
|
30509
|
+
}
|
|
30510
|
+
/**
|
|
30511
|
+
* Type guard for the deposit quote API response.
|
|
30512
|
+
*
|
|
30513
|
+
* @param value - Unknown response value to validate
|
|
30514
|
+
* @returns True when the value matches the deposit quote response shape
|
|
30515
|
+
*
|
|
30516
|
+
* @internal
|
|
30517
|
+
*/
|
|
30518
|
+
function isDepositQuoteResponse(value) {
|
|
30519
|
+
return depositQuoteResponseSchema.safeParse(value).success;
|
|
30520
|
+
}
|
|
30521
|
+
/**
|
|
30522
|
+
* Type guard for the withdrawal quote API response.
|
|
30523
|
+
*
|
|
30524
|
+
* @param value - Unknown response value to validate
|
|
30525
|
+
* @returns True when the value matches the withdrawal quote response shape
|
|
30526
|
+
*
|
|
30527
|
+
* @internal
|
|
30528
|
+
*/
|
|
30529
|
+
function isWithdrawalQuoteResponse(value) {
|
|
30530
|
+
return withdrawalQuoteResponseSchema.safeParse(value).success;
|
|
30531
|
+
}
|
|
30532
|
+
/**
|
|
30533
|
+
* Type guard for the claim rewards quote API response.
|
|
30534
|
+
*
|
|
30535
|
+
* @param value - Unknown response value to validate
|
|
30536
|
+
* @returns True when the value matches the claim rewards quote response shape
|
|
30537
|
+
*
|
|
30538
|
+
* @internal
|
|
30539
|
+
*/
|
|
30540
|
+
function isClaimRewardsQuoteResponse(value) {
|
|
30541
|
+
return claimRewardsQuoteResponseSchema.safeParse(value).success;
|
|
30542
|
+
}
|
|
30543
|
+
|
|
30544
|
+
/**
|
|
30545
|
+
* Build an API polling config with optional authorization header,
|
|
30546
|
+
* and resolve the base URL (configurable for testing).
|
|
30547
|
+
*
|
|
30548
|
+
* @param serviceConfig - Optional earn service configuration
|
|
30549
|
+
* @returns Resolved polling config and base URL
|
|
30550
|
+
*
|
|
30551
|
+
* @internal
|
|
30552
|
+
*/
|
|
30553
|
+
function buildConfig(serviceConfig) {
|
|
30554
|
+
const baseUrl = serviceConfig?.baseUrl ?? EARN_SERVICE_BASE_URL;
|
|
30555
|
+
if (serviceConfig?.kitKey === undefined) {
|
|
30556
|
+
return { pollingConfig: DEFAULT_CONFIG, baseUrl };
|
|
30557
|
+
}
|
|
30558
|
+
if (!isValidApiKey(serviceConfig.kitKey)) {
|
|
30559
|
+
throw new KitError({
|
|
30560
|
+
...InputError.VALIDATION_FAILED,
|
|
30561
|
+
recoverability: 'FATAL',
|
|
30562
|
+
message: 'Invalid kitKey format. Expected KIT_KEY:<keyId>:<keySecret>.',
|
|
30563
|
+
});
|
|
30564
|
+
}
|
|
30565
|
+
return {
|
|
30566
|
+
pollingConfig: {
|
|
30567
|
+
...DEFAULT_CONFIG,
|
|
30568
|
+
headers: {
|
|
30569
|
+
...DEFAULT_CONFIG.headers,
|
|
30570
|
+
Authorization: `Bearer ${serviceConfig.kitKey}`,
|
|
30571
|
+
},
|
|
30572
|
+
},
|
|
30573
|
+
baseUrl,
|
|
30574
|
+
};
|
|
30575
|
+
}
|
|
30576
|
+
|
|
30577
|
+
function toVaultInfo(data) {
|
|
30578
|
+
const { totalDeposits, liquidity, ...vault } = data;
|
|
30579
|
+
const chain = toSdkChain(vault.chain);
|
|
30580
|
+
if (chain === undefined) {
|
|
30581
|
+
throw createInvalidChainError(vault.chain, 'Chain returned by the Earn Service is not supported by the SDK');
|
|
30582
|
+
}
|
|
30583
|
+
return {
|
|
30584
|
+
...vault,
|
|
30585
|
+
chain,
|
|
30586
|
+
totalDeposits: Amount.fromJSON(totalDeposits),
|
|
30587
|
+
liquidity: Amount.fromJSON(liquidity),
|
|
30588
|
+
};
|
|
30589
|
+
}
|
|
30590
|
+
function toVaultError(error) {
|
|
30591
|
+
const chain = toSdkChain(error.chain);
|
|
30592
|
+
if (chain === undefined) {
|
|
30593
|
+
throw createInvalidChainError(error.chain, 'Chain returned by the Earn Service is not supported by the SDK');
|
|
30594
|
+
}
|
|
30595
|
+
return { ...error, chain };
|
|
30596
|
+
}
|
|
30597
|
+
function getVaultQueryChainLabel(chain) {
|
|
30598
|
+
return typeof chain === 'string' ? chain : 'unknown';
|
|
30599
|
+
}
|
|
30600
|
+
function resolveVaultQueryChain(chain) {
|
|
30601
|
+
try {
|
|
30602
|
+
return resolveChainIdentifier(chain);
|
|
30603
|
+
}
|
|
30604
|
+
catch {
|
|
30605
|
+
throw createInvalidChainError(getVaultQueryChainLabel(chain), 'Chain is not supported by the Earn Service provider');
|
|
30606
|
+
}
|
|
30607
|
+
}
|
|
30608
|
+
/**
|
|
30609
|
+
* Fetch vault information from the Earn Service API.
|
|
30610
|
+
*
|
|
30611
|
+
* Call `GET /v1/earnKit/vaults` with repeated chain and vaultAddress
|
|
30612
|
+
* query parameters for batch vault lookup.
|
|
30613
|
+
*
|
|
30614
|
+
* @param params - Vault query parameters
|
|
30615
|
+
* @returns Batch result with vaults and per-vault errors
|
|
30616
|
+
* @throws {@link KitError} When the API call fails
|
|
30617
|
+
*
|
|
30618
|
+
* @internal
|
|
30619
|
+
*/
|
|
30620
|
+
async function fetchVaults(params) {
|
|
30621
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30622
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/vaults`, baseUrl);
|
|
30623
|
+
// The API pairs chain[i] with vaultAddress[i] by insertion order.
|
|
30624
|
+
for (const vault of params.vaults) {
|
|
30625
|
+
const chainDefinition = resolveVaultQueryChain(vault.chain);
|
|
30626
|
+
const apiChain = toApiChain(chainDefinition.chain);
|
|
30627
|
+
if (apiChain === undefined) {
|
|
30628
|
+
throw createInvalidChainError(chainDefinition.chain, 'Chain is not supported by the Earn Service provider');
|
|
30629
|
+
}
|
|
30630
|
+
url.searchParams.append('chain', apiChain);
|
|
30631
|
+
url.searchParams.append('vaultAddress', vault.vaultAddress);
|
|
30632
|
+
}
|
|
30633
|
+
try {
|
|
30634
|
+
const response = await pollApiGet(url.toString(), isGetVaultsResponse, pollingConfig);
|
|
30635
|
+
return {
|
|
30636
|
+
vaults: response.data.vaults.map(toVaultInfo),
|
|
30637
|
+
errors: response.data.errors.map(toVaultError),
|
|
30638
|
+
};
|
|
30639
|
+
}
|
|
30640
|
+
catch (error) {
|
|
30641
|
+
throw parseEarnApiError(error, { operation: 'getVaults' });
|
|
30642
|
+
}
|
|
30643
|
+
}
|
|
30644
|
+
|
|
30645
|
+
function toAccruedReward(reward) {
|
|
30646
|
+
return {
|
|
30647
|
+
tokenAddress: reward.token,
|
|
30648
|
+
symbol: reward.symbol,
|
|
30649
|
+
amount: Amount.fromJSON(reward.amount),
|
|
30650
|
+
};
|
|
30651
|
+
}
|
|
30652
|
+
function assertNever(value) {
|
|
30653
|
+
throw new KitError({
|
|
30654
|
+
...EarnError.INTERNAL_ERROR,
|
|
30655
|
+
recoverability: 'FATAL',
|
|
30656
|
+
message: `Unhandled PnL status: ${JSON.stringify(value)}`,
|
|
30657
|
+
cause: {
|
|
30658
|
+
trace: value,
|
|
30659
|
+
},
|
|
30660
|
+
});
|
|
30661
|
+
}
|
|
30662
|
+
function toPositionPnL(data) {
|
|
30663
|
+
switch (data.status) {
|
|
30664
|
+
case 'available':
|
|
30665
|
+
return {
|
|
30666
|
+
status: 'available',
|
|
30667
|
+
principalDeposited: Amount.fromJSON(data.principalDeposited),
|
|
30668
|
+
totalYieldEarned: Amount.fromJSON(data.totalYieldEarned),
|
|
30669
|
+
};
|
|
30670
|
+
case 'pending':
|
|
30671
|
+
return { status: 'pending' };
|
|
30672
|
+
case 'unavailable':
|
|
30673
|
+
return { status: 'unavailable', reason: data.reason };
|
|
30674
|
+
default:
|
|
30675
|
+
return assertNever(data);
|
|
30676
|
+
}
|
|
30677
|
+
}
|
|
30678
|
+
function toPositionInfo(data) {
|
|
30679
|
+
const { currentBalance, shares, pnl, accruedRewards = [], ...position } = data;
|
|
30680
|
+
const chain = toSdkChain(position.chain);
|
|
30681
|
+
if (chain === undefined) {
|
|
30682
|
+
throw new KitError({
|
|
30683
|
+
...EarnError.INTERNAL_ERROR,
|
|
30684
|
+
recoverability: 'FATAL',
|
|
30685
|
+
message: `Unsupported Earn Service chain: ${position.chain}`,
|
|
30686
|
+
cause: {
|
|
30687
|
+
trace: {
|
|
30688
|
+
chain: position.chain,
|
|
30689
|
+
},
|
|
30690
|
+
},
|
|
30691
|
+
});
|
|
30692
|
+
}
|
|
30693
|
+
return {
|
|
30694
|
+
...position,
|
|
30695
|
+
chain,
|
|
30696
|
+
currentBalance: Amount.fromJSON(currentBalance),
|
|
30697
|
+
shares: Amount.fromJSON(shares),
|
|
30698
|
+
pnl: toPositionPnL(pnl),
|
|
30699
|
+
accruedRewards: accruedRewards.map(toAccruedReward),
|
|
30700
|
+
};
|
|
30701
|
+
}
|
|
30702
|
+
/**
|
|
30703
|
+
* Fetch a user's vault position from the Earn Service API.
|
|
30704
|
+
*
|
|
30705
|
+
* Call `GET /v1/earnKit/position/{address}` with the provided chain
|
|
30706
|
+
* and vault address as query parameters.
|
|
30707
|
+
*
|
|
30708
|
+
* @param params - Position query parameters
|
|
30709
|
+
* @returns The user's position information
|
|
30710
|
+
* @throws {@link KitError} When the API call fails
|
|
30711
|
+
*
|
|
30712
|
+
* @internal
|
|
30713
|
+
*/
|
|
30714
|
+
async function fetchPosition(params) {
|
|
30715
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30716
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/position/${encodeURIComponent(params.address)}`, baseUrl);
|
|
30717
|
+
url.searchParams.set('chain', params.chain);
|
|
30718
|
+
url.searchParams.set('vaultAddress', params.vaultAddress);
|
|
30719
|
+
try {
|
|
30720
|
+
const response = await pollApiGet(url.toString(), isPositionResponse, pollingConfig);
|
|
30721
|
+
return toPositionInfo(response.data);
|
|
30722
|
+
}
|
|
30723
|
+
catch (error) {
|
|
30724
|
+
throw parseEarnApiError(error, { operation: 'getPosition' });
|
|
30725
|
+
}
|
|
30726
|
+
}
|
|
30727
|
+
|
|
30728
|
+
/**
|
|
30729
|
+
* Build signed deposit instructions via the Earn Service API.
|
|
30730
|
+
*
|
|
30731
|
+
* Call `POST /v1/earnKit/deposit` with the vault address, amount,
|
|
30732
|
+
* wallet address, and chain. Return EIP-712 signed execution
|
|
30733
|
+
* parameters for on-chain submission.
|
|
30734
|
+
*
|
|
30735
|
+
* @param params - Deposit parameters
|
|
30736
|
+
* @returns Signed execution parameters for the deposit
|
|
30737
|
+
* @throws {@link KitError} When the API call fails
|
|
30738
|
+
*
|
|
30739
|
+
* @internal
|
|
30740
|
+
*/
|
|
30741
|
+
async function fetchDeposit(params) {
|
|
30742
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30743
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/deposit`, baseUrl);
|
|
30744
|
+
const requestBody = {
|
|
30745
|
+
vaultAddress: params.vaultAddress,
|
|
30746
|
+
amount: params.amount,
|
|
30747
|
+
address: params.address,
|
|
30748
|
+
chain: params.chain,
|
|
30749
|
+
};
|
|
30750
|
+
try {
|
|
30751
|
+
const response = await pollApiPost(url.toString(), requestBody, isDepositResponse, pollingConfig);
|
|
30752
|
+
return response.data;
|
|
30753
|
+
}
|
|
30754
|
+
catch (error) {
|
|
30755
|
+
throw parseEarnApiError(error, { operation: 'deposit' });
|
|
30756
|
+
}
|
|
30757
|
+
}
|
|
30758
|
+
|
|
30759
|
+
/**
|
|
30760
|
+
* Build signed withdrawal instructions via the Earn Service API.
|
|
30761
|
+
*
|
|
30762
|
+
* Call `POST /v1/earnKit/withdraw` with the vault address, amount,
|
|
30763
|
+
* wallet address, and chain. Return EIP-712 signed execution
|
|
30764
|
+
* parameters for on-chain submission.
|
|
30765
|
+
*
|
|
30766
|
+
* @param params - Withdrawal parameters
|
|
30767
|
+
* @returns Signed execution parameters for the withdrawal
|
|
30768
|
+
* @throws {@link KitError} When the API call fails
|
|
30769
|
+
*
|
|
30770
|
+
* @internal
|
|
30771
|
+
*/
|
|
30772
|
+
async function fetchWithdraw(params) {
|
|
30773
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30774
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/withdraw`, baseUrl);
|
|
30775
|
+
const requestBody = {
|
|
30776
|
+
vaultAddress: params.vaultAddress,
|
|
30777
|
+
amount: params.amount,
|
|
30778
|
+
address: params.address,
|
|
30779
|
+
chain: params.chain,
|
|
30780
|
+
};
|
|
30781
|
+
try {
|
|
30782
|
+
const response = await pollApiPost(url.toString(), requestBody, isWithdrawResponse, pollingConfig);
|
|
30783
|
+
return response.data;
|
|
30784
|
+
}
|
|
30785
|
+
catch (error) {
|
|
30786
|
+
throw parseEarnApiError(error, { operation: 'withdraw' });
|
|
30787
|
+
}
|
|
30788
|
+
}
|
|
30789
|
+
|
|
30790
|
+
function toClaimedAmount(reward) {
|
|
30791
|
+
return {
|
|
30792
|
+
address: reward.token,
|
|
30793
|
+
symbol: reward.symbol,
|
|
30794
|
+
amount: Amount.fromJSON(reward.amount),
|
|
30795
|
+
};
|
|
30796
|
+
}
|
|
30797
|
+
/**
|
|
30798
|
+
* Build signed claim rewards instructions via the Earn Service API.
|
|
30799
|
+
*
|
|
30800
|
+
* Call `POST /v1/earnKit/claimRewards` with the wallet address, chain, and
|
|
30801
|
+
* vault address. Return EIP-712 signed execution parameters for on-chain
|
|
30802
|
+
* submission. When no rewards are claimable, return an empty rewards array
|
|
30803
|
+
* without execution parameters.
|
|
30804
|
+
*
|
|
30805
|
+
* @param params - Claim rewards parameters
|
|
30806
|
+
* @returns Signed execution parameters and reward details
|
|
30807
|
+
* @throws {@link KitError} When the API call fails
|
|
30808
|
+
*
|
|
30809
|
+
* @internal
|
|
30810
|
+
*/
|
|
30811
|
+
async function fetchClaimRewards(params) {
|
|
30812
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30813
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/claimRewards`, baseUrl);
|
|
30814
|
+
const requestBody = {
|
|
30815
|
+
address: params.address,
|
|
30816
|
+
chain: params.chain,
|
|
30817
|
+
vaultAddress: params.vaultAddress,
|
|
30818
|
+
};
|
|
30819
|
+
try {
|
|
30820
|
+
const response = await pollApiPost(url.toString(), requestBody, isClaimRewardsResponse, pollingConfig);
|
|
30821
|
+
const { rewards, executionParams, signature } = response.data;
|
|
30822
|
+
return {
|
|
30823
|
+
rewards: rewards.map(toClaimedAmount),
|
|
30824
|
+
...(executionParams === undefined ? {} : { executionParams }),
|
|
30825
|
+
...(signature === undefined ? {} : { signature }),
|
|
30826
|
+
};
|
|
30827
|
+
}
|
|
30828
|
+
catch (error) {
|
|
30829
|
+
throw parseEarnApiError(error, { operation: 'claimRewards' });
|
|
30830
|
+
}
|
|
30831
|
+
}
|
|
30832
|
+
|
|
30833
|
+
function toDepositQuoteInfo(data) {
|
|
30834
|
+
return {
|
|
30835
|
+
vaultAddress: data.vaultAddress,
|
|
30836
|
+
vaultName: data.vaultName,
|
|
30837
|
+
deposit: {
|
|
30838
|
+
symbol: data.asset,
|
|
30839
|
+
amount: Amount.fromJSON(data.depositAmount),
|
|
30840
|
+
},
|
|
30841
|
+
expectedShares: {
|
|
30842
|
+
symbol: VAULT_SHARE_SYMBOL,
|
|
30843
|
+
address: data.vaultAddress,
|
|
30844
|
+
amount: Amount.fromJSON(data.expectedShares),
|
|
30845
|
+
},
|
|
30846
|
+
sharePrice: data.sharePrice,
|
|
30847
|
+
currentApy: data.currentApy,
|
|
30848
|
+
fees: [],
|
|
30849
|
+
};
|
|
30850
|
+
}
|
|
30851
|
+
/**
|
|
30852
|
+
* Fetch a deposit quote from the Earn Service API.
|
|
30853
|
+
*
|
|
30854
|
+
* Call `POST /v1/earnKit/deposit/quote` with the vault address, amount,
|
|
30855
|
+
* wallet address, and chain. Return informational quote data including
|
|
30856
|
+
* expected shares, share price, and APY.
|
|
30857
|
+
*
|
|
30858
|
+
* @param params - Deposit quote parameters
|
|
30859
|
+
* @returns Deposit quote information
|
|
30860
|
+
* @throws {@link KitError} When the API call fails
|
|
30861
|
+
*
|
|
30862
|
+
* @internal
|
|
30863
|
+
*/
|
|
30864
|
+
async function fetchDepositQuote(params) {
|
|
30865
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30866
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/deposit/quote`, baseUrl);
|
|
30867
|
+
const requestBody = {
|
|
30868
|
+
vaultAddress: params.vaultAddress,
|
|
30869
|
+
amount: params.amount,
|
|
30870
|
+
address: params.address,
|
|
30871
|
+
chain: params.chain,
|
|
30872
|
+
};
|
|
30873
|
+
try {
|
|
30874
|
+
const response = await pollApiPost(url.toString(), requestBody, isDepositQuoteResponse, pollingConfig);
|
|
30875
|
+
return toDepositQuoteInfo(response.data);
|
|
30876
|
+
}
|
|
30877
|
+
catch (error) {
|
|
30878
|
+
throw parseEarnApiError(error, { operation: 'getDepositQuote' });
|
|
30879
|
+
}
|
|
30880
|
+
}
|
|
30881
|
+
|
|
30882
|
+
function toWithdrawalQuoteInfo(data) {
|
|
30883
|
+
return {
|
|
30884
|
+
vaultAddress: data.vaultAddress,
|
|
30885
|
+
vaultName: data.vaultName,
|
|
30886
|
+
withdrawal: {
|
|
30887
|
+
symbol: data.asset,
|
|
30888
|
+
amount: Amount.fromJSON(data.withdrawAmount),
|
|
30889
|
+
},
|
|
30890
|
+
sharesToRedeem: {
|
|
30891
|
+
symbol: VAULT_SHARE_SYMBOL,
|
|
30892
|
+
address: data.vaultAddress,
|
|
30893
|
+
amount: Amount.fromJSON(data.sharesToRedeem),
|
|
30894
|
+
},
|
|
30895
|
+
sharePrice: data.sharePrice,
|
|
30896
|
+
maxWithdrawable: {
|
|
30897
|
+
symbol: data.asset,
|
|
30898
|
+
amount: Amount.fromJSON(data.maxWithdrawable),
|
|
30899
|
+
},
|
|
30900
|
+
fees: data.fees.map((fee) => ({
|
|
30901
|
+
symbol: fee.token,
|
|
30902
|
+
amount: Amount.fromJSON(fee.amount),
|
|
30903
|
+
})),
|
|
30904
|
+
// Wire format uses `warnings`, but the SDK surface uses
|
|
30905
|
+
// `earnKitWarnings` to match the precedent set by `VaultInfo` —
|
|
30906
|
+
// `warnings` is reserved for the structured `VaultWarning` shape.
|
|
30907
|
+
...(data.warnings !== undefined && { earnKitWarnings: data.warnings }),
|
|
30908
|
+
};
|
|
30909
|
+
}
|
|
30910
|
+
/**
|
|
30911
|
+
* Fetch a withdrawal quote from the Earn Service API.
|
|
30912
|
+
*
|
|
30913
|
+
* Call `POST /v1/earnKit/withdrawal/quote` with the vault address, amount,
|
|
30914
|
+
* wallet address, and chain. Return informational quote data including
|
|
30915
|
+
* shares to redeem, max withdrawable, and fees.
|
|
30916
|
+
*
|
|
30917
|
+
* @param params - Withdrawal quote parameters
|
|
30918
|
+
* @returns Withdrawal quote information
|
|
30919
|
+
* @throws {@link KitError} When the API call fails
|
|
30920
|
+
*
|
|
30921
|
+
* @internal
|
|
30922
|
+
*/
|
|
30923
|
+
async function fetchWithdrawalQuote(params) {
|
|
30924
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30925
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/withdrawal/quote`, baseUrl);
|
|
30926
|
+
const requestBody = {
|
|
30927
|
+
vaultAddress: params.vaultAddress,
|
|
30928
|
+
amount: params.amount,
|
|
30929
|
+
address: params.address,
|
|
30930
|
+
chain: params.chain,
|
|
30931
|
+
};
|
|
30932
|
+
try {
|
|
30933
|
+
const response = await pollApiPost(url.toString(), requestBody, isWithdrawalQuoteResponse, pollingConfig);
|
|
30934
|
+
return toWithdrawalQuoteInfo(response.data);
|
|
30935
|
+
}
|
|
30936
|
+
catch (error) {
|
|
30937
|
+
throw parseEarnApiError(error, { operation: 'getWithdrawalQuote' });
|
|
30938
|
+
}
|
|
30939
|
+
}
|
|
30940
|
+
|
|
30941
|
+
/**
|
|
30942
|
+
* Fetch a claim rewards quote from the Earn Service API.
|
|
30943
|
+
*
|
|
30944
|
+
* Call `POST /v1/earnKit/claimRewards/quote` with the wallet address,
|
|
30945
|
+
* chain, and vault address. Return informational quote data including
|
|
30946
|
+
* claimable reward details.
|
|
30947
|
+
*
|
|
30948
|
+
* @param params - Claim rewards quote parameters
|
|
30949
|
+
* @returns Claim rewards quote information
|
|
30950
|
+
* @throws {@link KitError} When the API call fails
|
|
30951
|
+
*
|
|
30952
|
+
* @internal
|
|
30953
|
+
*/
|
|
30954
|
+
async function fetchClaimRewardsQuote(params) {
|
|
30955
|
+
const { pollingConfig, baseUrl } = buildConfig(params.config);
|
|
30956
|
+
const url = new URL(`${EARN_KIT_API_PREFIX}/claimRewards/quote`, baseUrl);
|
|
30957
|
+
const requestBody = {
|
|
30958
|
+
vaultAddress: params.vaultAddress,
|
|
30959
|
+
address: params.address,
|
|
30960
|
+
chain: params.chain,
|
|
30961
|
+
};
|
|
30962
|
+
try {
|
|
30963
|
+
const response = await pollApiPost(url.toString(), requestBody, isClaimRewardsQuoteResponse, pollingConfig);
|
|
30964
|
+
return {
|
|
30965
|
+
rewards: response.data.rewards.map((r) => ({
|
|
30966
|
+
symbol: r.symbol,
|
|
30967
|
+
amount: Amount.fromJSON(r.amount),
|
|
30968
|
+
address: r.token,
|
|
30969
|
+
})),
|
|
30970
|
+
};
|
|
30971
|
+
}
|
|
30972
|
+
catch (error) {
|
|
30973
|
+
throw parseEarnApiError(error, { operation: 'getClaimRewardsQuote' });
|
|
30974
|
+
}
|
|
30975
|
+
}
|
|
30976
|
+
|
|
30977
|
+
/**
|
|
30978
|
+
* Earn Service provider for the Circle earn service API.
|
|
30979
|
+
*
|
|
30980
|
+
* Implement the {@link EarningProvider} interface for yield-bearing vault
|
|
30981
|
+
* operations including vault discovery, position queries, deposits,
|
|
30982
|
+
* withdrawals, and reward claiming.
|
|
30983
|
+
*
|
|
30984
|
+
* @example
|
|
30985
|
+
* ```typescript
|
|
30986
|
+
* import { EarnServiceProvider } from '@circle-fin/provider-earn-service'
|
|
30987
|
+
*
|
|
30988
|
+
* // Permissionless mode
|
|
30989
|
+
* const provider = new EarnServiceProvider()
|
|
30990
|
+
*
|
|
30991
|
+
* // Permissioned mode with Kit Key
|
|
30992
|
+
* const provider = new EarnServiceProvider({
|
|
30993
|
+
* kitKey: 'KIT_KEY:keyId:keySecret',
|
|
30994
|
+
* })
|
|
30995
|
+
*
|
|
30996
|
+
* // Custom base URL for testing
|
|
30997
|
+
* const provider = new EarnServiceProvider({
|
|
30998
|
+
* baseUrl: 'https://api-staging.circle.com',
|
|
30999
|
+
* })
|
|
31000
|
+
*
|
|
31001
|
+
* const result = await provider.getVaults({
|
|
31002
|
+
* vaults: [
|
|
31003
|
+
* { chain: 'Arc_Testnet', vaultAddress: '0x8eB67...' },
|
|
31004
|
+
* ],
|
|
31005
|
+
* })
|
|
31006
|
+
* ```
|
|
31007
|
+
*/
|
|
31008
|
+
class EarnServiceProvider {
|
|
31009
|
+
/** {@inheritdoc} */
|
|
31010
|
+
name = 'EarnService';
|
|
31011
|
+
/** {@inheritdoc} */
|
|
31012
|
+
supportedChains = Object.keys(CHAIN_TO_API).map((chain) => resolveChainIdentifier(chain));
|
|
31013
|
+
defaultConfig;
|
|
31014
|
+
/**
|
|
31015
|
+
* Create a new EarnServiceProvider.
|
|
31016
|
+
*
|
|
31017
|
+
* @param config - Optional default configuration applied to all operations.
|
|
31018
|
+
* Per-operation config takes precedence when provided.
|
|
31019
|
+
*/
|
|
31020
|
+
constructor(config) {
|
|
31021
|
+
this.defaultConfig = config;
|
|
31022
|
+
}
|
|
31023
|
+
resolveConfig(config) {
|
|
31024
|
+
const resolvedConfig = this.defaultConfig === undefined
|
|
31025
|
+
? config
|
|
31026
|
+
: { ...this.defaultConfig, ...config };
|
|
31027
|
+
return resolvedConfig;
|
|
31028
|
+
}
|
|
31029
|
+
/** {@inheritdoc} */
|
|
31030
|
+
async getVaults(params) {
|
|
31031
|
+
const config = this.resolveConfig(params.config);
|
|
31032
|
+
return fetchVaults({
|
|
31033
|
+
vaults: params.vaults,
|
|
31034
|
+
config,
|
|
31035
|
+
});
|
|
31036
|
+
}
|
|
31037
|
+
/** {@inheritdoc} */
|
|
31038
|
+
async getPosition(params) {
|
|
31039
|
+
const config = this.resolveConfig(params.config);
|
|
31040
|
+
const { address, chain } = await resolveAdapterContext(params.from);
|
|
31041
|
+
return fetchPosition({
|
|
31042
|
+
address,
|
|
31043
|
+
chain,
|
|
31044
|
+
vaultAddress: params.vaultAddress,
|
|
31045
|
+
config,
|
|
31046
|
+
});
|
|
31047
|
+
}
|
|
31048
|
+
/** {@inheritdoc} */
|
|
31049
|
+
async deposit(params) {
|
|
31050
|
+
const config = this.resolveConfig(params.config);
|
|
31051
|
+
const { address, chain: apiChain, chainDefinition: chain, } = await resolveAdapterContext(params.from);
|
|
31052
|
+
const adapterContractAddress = requireAdapterContract(chain);
|
|
31053
|
+
const rawUsdcAddress = chain.usdcAddress;
|
|
31054
|
+
if (rawUsdcAddress === null) {
|
|
31055
|
+
throw createUnsupportedTokenError('USDC', chain.name);
|
|
31056
|
+
}
|
|
31057
|
+
const usdcAddress = assertHexAddress('chain.usdcAddress', rawUsdcAddress, `USDC address for chain ${chain.name} must be a 0x-prefixed 20-byte hex address.`);
|
|
31058
|
+
const { executionParams, signature } = await fetchDeposit({
|
|
31059
|
+
vaultAddress: params.vaultAddress,
|
|
31060
|
+
amount: params.amount,
|
|
31061
|
+
address,
|
|
31062
|
+
chain: apiChain,
|
|
31063
|
+
config,
|
|
31064
|
+
});
|
|
31065
|
+
validateExecutionDeadline(executionParams);
|
|
31066
|
+
const { adapter } = params.from;
|
|
31067
|
+
const tokenInputs = buildEarnTokenInputs(executionParams, usdcAddress);
|
|
31068
|
+
const approvalToken = tokenInputs[0]?.token;
|
|
31069
|
+
if (approvalToken !== undefined) {
|
|
31070
|
+
await approveMaxIfNeeded({
|
|
31071
|
+
adapter,
|
|
31072
|
+
chain,
|
|
31073
|
+
tokenAddress: approvalToken,
|
|
31074
|
+
delegate: adapterContractAddress,
|
|
31075
|
+
address,
|
|
31076
|
+
revertMessage: 'USDC approval reverted on-chain',
|
|
31077
|
+
});
|
|
31078
|
+
}
|
|
31079
|
+
const { txHash, explorerUrl } = await executeEarnAction({
|
|
31080
|
+
adapter,
|
|
31081
|
+
chain,
|
|
31082
|
+
address,
|
|
31083
|
+
actionKey: 'earn.deposit',
|
|
31084
|
+
actionParams: {
|
|
31085
|
+
executeParams: executionParams,
|
|
31086
|
+
tokenInputs,
|
|
31087
|
+
signature,
|
|
31088
|
+
},
|
|
31089
|
+
revertMessage: 'Earn deposit reverted on-chain',
|
|
31090
|
+
});
|
|
31091
|
+
return {
|
|
31092
|
+
txHash,
|
|
31093
|
+
explorerUrl,
|
|
31094
|
+
vaultAddress: params.vaultAddress,
|
|
31095
|
+
amount: params.amount,
|
|
31096
|
+
};
|
|
31097
|
+
}
|
|
31098
|
+
/** {@inheritdoc} */
|
|
31099
|
+
async withdraw(params) {
|
|
31100
|
+
const config = this.resolveConfig(params.config);
|
|
31101
|
+
const { address, chain: apiChain, chainDefinition: chain, } = await resolveAdapterContext(params.from);
|
|
31102
|
+
const adapterContractAddress = requireAdapterContract(chain);
|
|
31103
|
+
const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
|
|
31104
|
+
const { executionParams, signature } = await fetchWithdraw({
|
|
31105
|
+
vaultAddress,
|
|
31106
|
+
amount: params.amount,
|
|
31107
|
+
address,
|
|
31108
|
+
chain: apiChain,
|
|
31109
|
+
config,
|
|
31110
|
+
});
|
|
31111
|
+
validateExecutionDeadline(executionParams);
|
|
31112
|
+
const { adapter } = params.from;
|
|
31113
|
+
const tokenInputs = buildEarnTokenInputs(executionParams, vaultAddress);
|
|
31114
|
+
const approvalToken = tokenInputs[0]?.token;
|
|
31115
|
+
if (approvalToken !== undefined) {
|
|
31116
|
+
await approveMaxIfNeeded({
|
|
31117
|
+
adapter,
|
|
31118
|
+
chain,
|
|
31119
|
+
tokenAddress: approvalToken,
|
|
31120
|
+
delegate: adapterContractAddress,
|
|
31121
|
+
address,
|
|
31122
|
+
revertMessage: 'Vault share token approval reverted on-chain',
|
|
31123
|
+
});
|
|
31124
|
+
}
|
|
31125
|
+
const { txHash, explorerUrl } = await executeEarnAction({
|
|
31126
|
+
adapter,
|
|
31127
|
+
chain,
|
|
31128
|
+
address,
|
|
31129
|
+
actionKey: 'earn.withdraw',
|
|
31130
|
+
actionParams: {
|
|
31131
|
+
executeParams: executionParams,
|
|
31132
|
+
tokenInputs,
|
|
31133
|
+
signature,
|
|
31134
|
+
},
|
|
31135
|
+
revertMessage: 'Earn withdraw reverted on-chain',
|
|
31136
|
+
});
|
|
31137
|
+
return {
|
|
31138
|
+
txHash,
|
|
31139
|
+
explorerUrl,
|
|
31140
|
+
vaultAddress,
|
|
31141
|
+
amount: params.amount,
|
|
31142
|
+
};
|
|
31143
|
+
}
|
|
31144
|
+
/** {@inheritdoc} */
|
|
31145
|
+
async claimRewards(params) {
|
|
31146
|
+
const config = this.resolveConfig(params.config);
|
|
31147
|
+
const { address, chain: apiChain, chainDefinition: chain, } = await resolveAdapterContext(params.from);
|
|
31148
|
+
// Claim rewards has no approval step, but still requires adapter support.
|
|
31149
|
+
requireAdapterContract(chain);
|
|
31150
|
+
const { rewards, executionParams, signature } = await fetchClaimRewards({
|
|
31151
|
+
address,
|
|
31152
|
+
chain: apiChain,
|
|
31153
|
+
vaultAddress: params.vaultAddress,
|
|
31154
|
+
config,
|
|
31155
|
+
});
|
|
31156
|
+
const nothingToClaim = rewards.length === 0;
|
|
31157
|
+
if (nothingToClaim) {
|
|
31158
|
+
return { status: 'no_rewards', rewards: [] };
|
|
31159
|
+
}
|
|
31160
|
+
const missingExecutionParams = executionParams === undefined;
|
|
31161
|
+
const missingSignature = signature === undefined;
|
|
31162
|
+
if (missingExecutionParams || missingSignature) {
|
|
31163
|
+
throw new KitError({
|
|
31164
|
+
...EarnError.INTERNAL_ERROR,
|
|
31165
|
+
recoverability: 'RETRYABLE',
|
|
31166
|
+
message: 'Claim rewards response must include executionParams and signature when rewards are claimable',
|
|
31167
|
+
cause: {
|
|
31168
|
+
trace: {
|
|
31169
|
+
rewardsCount: rewards.length,
|
|
31170
|
+
missingExecutionParams,
|
|
31171
|
+
missingSignature,
|
|
31172
|
+
},
|
|
31173
|
+
},
|
|
31174
|
+
});
|
|
31175
|
+
}
|
|
31176
|
+
validateExecutionDeadline(executionParams);
|
|
31177
|
+
const { txHash, explorerUrl } = await executeEarnAction({
|
|
31178
|
+
adapter: params.from.adapter,
|
|
31179
|
+
chain,
|
|
31180
|
+
address,
|
|
31181
|
+
actionKey: 'earn.claimRewards',
|
|
31182
|
+
actionParams: {
|
|
31183
|
+
executeParams: executionParams,
|
|
31184
|
+
tokenInputs: [],
|
|
31185
|
+
signature,
|
|
31186
|
+
},
|
|
31187
|
+
revertMessage: 'Earn claim rewards reverted on-chain',
|
|
31188
|
+
});
|
|
31189
|
+
return { status: 'claimed', rewards, txHash, explorerUrl };
|
|
31190
|
+
}
|
|
31191
|
+
/** {@inheritdoc} */
|
|
31192
|
+
async getDepositQuote(params) {
|
|
31193
|
+
const config = this.resolveConfig(params.config);
|
|
31194
|
+
const { address, chain } = await resolveAdapterContext(params.from);
|
|
31195
|
+
return fetchDepositQuote({
|
|
31196
|
+
vaultAddress: params.vaultAddress,
|
|
31197
|
+
amount: params.amount,
|
|
31198
|
+
address,
|
|
31199
|
+
chain,
|
|
31200
|
+
config,
|
|
31201
|
+
});
|
|
31202
|
+
}
|
|
31203
|
+
/** {@inheritdoc} */
|
|
31204
|
+
async getWithdrawalQuote(params) {
|
|
31205
|
+
const config = this.resolveConfig(params.config);
|
|
31206
|
+
const { address, chain } = await resolveAdapterContext(params.from);
|
|
31207
|
+
return fetchWithdrawalQuote({
|
|
31208
|
+
vaultAddress: params.vaultAddress,
|
|
31209
|
+
amount: params.amount,
|
|
31210
|
+
address,
|
|
31211
|
+
chain,
|
|
31212
|
+
config,
|
|
31213
|
+
});
|
|
31214
|
+
}
|
|
31215
|
+
/** {@inheritdoc} */
|
|
31216
|
+
async getClaimRewardsQuote(params) {
|
|
31217
|
+
const config = this.resolveConfig(params.config);
|
|
31218
|
+
const { address, chain } = await resolveAdapterContext(params.from);
|
|
31219
|
+
return fetchClaimRewardsQuote({
|
|
31220
|
+
vaultAddress: params.vaultAddress,
|
|
31221
|
+
address,
|
|
31222
|
+
chain,
|
|
31223
|
+
config,
|
|
31224
|
+
});
|
|
31225
|
+
}
|
|
31226
|
+
}
|
|
31227
|
+
|
|
31228
|
+
/**
|
|
31229
|
+
* The default providers used when no custom providers are specified.
|
|
31230
|
+
*
|
|
31231
|
+
* @returns An array containing the default EarnServiceProvider
|
|
31232
|
+
* @internal
|
|
31233
|
+
*/
|
|
31234
|
+
const getDefaultProviders$1 = () => [new EarnServiceProvider()];
|
|
31235
|
+
/**
|
|
31236
|
+
* Create an EarnKit context with validated configuration.
|
|
31237
|
+
*
|
|
31238
|
+
* Initialize an EarnKitContext with default providers and optional
|
|
31239
|
+
* custom configuration. Custom and default providers are merged,
|
|
31240
|
+
* preserving their exact types for type safety.
|
|
31241
|
+
*
|
|
31242
|
+
* @typeParam TExtraProviders - Array type of additional earn providers
|
|
31243
|
+
* @param config - Optional configuration for the EarnKit context
|
|
31244
|
+
* @returns A fully initialized EarnKitContext ready for earn operations
|
|
31245
|
+
*
|
|
31246
|
+
* @example
|
|
31247
|
+
* ```typescript
|
|
31248
|
+
* import { createEarnKitContext } from '@circle-fin/earn-kit'
|
|
31249
|
+
*
|
|
31250
|
+
* // Create context with defaults
|
|
31251
|
+
* const context = createEarnKitContext()
|
|
31252
|
+
* ```
|
|
31253
|
+
*
|
|
31254
|
+
* @example
|
|
31255
|
+
* ```typescript
|
|
31256
|
+
* import { createEarnKitContext } from '@circle-fin/earn-kit'
|
|
31257
|
+
*
|
|
31258
|
+
* // Create context with custom providers
|
|
31259
|
+
* const context = createEarnKitContext({
|
|
31260
|
+
* providers: [myCustomEarnProvider],
|
|
31261
|
+
* })
|
|
31262
|
+
* ```
|
|
31263
|
+
*/
|
|
31264
|
+
function createEarnKitContext(config = {}) {
|
|
31265
|
+
const defaultProviders = getDefaultProviders$1();
|
|
31266
|
+
const providers = [...(config.providers ?? []), ...defaultProviders];
|
|
31267
|
+
const context = {
|
|
31268
|
+
providers,
|
|
31269
|
+
};
|
|
31270
|
+
return context;
|
|
31271
|
+
}
|
|
31272
|
+
|
|
31273
|
+
/**
|
|
31274
|
+
* Symbol used to track that assertEarnParams has validated an object.
|
|
31275
|
+
* @internal
|
|
31276
|
+
*/
|
|
31277
|
+
const ASSERT_EARN_PARAMS_SYMBOL = Symbol('assertEarnParams');
|
|
31278
|
+
/**
|
|
31279
|
+
* Assert that the provided value conforms to the given earn params schema.
|
|
31280
|
+
*
|
|
31281
|
+
* Validate earn parameters using the provided Zod schema and track
|
|
31282
|
+
* validation state to avoid duplicate checks. Throw a structured
|
|
31283
|
+
* error with detailed validation messages if any parameter is invalid.
|
|
31284
|
+
*
|
|
31285
|
+
* @typeParam T - The expected type after validation
|
|
31286
|
+
* @param params - The earn parameters to validate
|
|
31287
|
+
* @param schema - The Zod schema to validate against
|
|
31288
|
+
* @throws {@link KitError} If the parameters fail validation
|
|
31289
|
+
*
|
|
31290
|
+
* @example
|
|
31291
|
+
* ```typescript
|
|
31292
|
+
* import { assertEarnParams, depositParamsSchema } from '@circle-fin/earn-kit'
|
|
31293
|
+
*
|
|
31294
|
+
* assertEarnParams(params, depositParamsSchema)
|
|
31295
|
+
* ```
|
|
31296
|
+
*/
|
|
31297
|
+
function assertEarnParams(params, schema) {
|
|
31298
|
+
validateWithStateTracking(params, schema, 'earn parameters', ASSERT_EARN_PARAMS_SYMBOL);
|
|
31299
|
+
}
|
|
31300
|
+
|
|
31301
|
+
function findProvider(context, chain, operation = 'earn') {
|
|
31302
|
+
let fallback;
|
|
31303
|
+
for (const provider of context.providers) {
|
|
31304
|
+
fallback ??= provider;
|
|
31305
|
+
if (provider.supportedChains.length === 0)
|
|
31306
|
+
continue;
|
|
31307
|
+
if (chain === undefined ||
|
|
31308
|
+
provider.supportedChains.some((c) => c.chain === chain.chain)) {
|
|
31309
|
+
return provider;
|
|
31310
|
+
}
|
|
31311
|
+
}
|
|
31312
|
+
if (fallback === undefined) {
|
|
31313
|
+
throw createValidationFailedError$1('context.providers', [], 'No earn providers configured');
|
|
31314
|
+
}
|
|
31315
|
+
if (chain !== undefined) {
|
|
31316
|
+
throw createUnsupportedEarnRouteError(operation, chain.name);
|
|
31317
|
+
}
|
|
31318
|
+
return fallback;
|
|
31319
|
+
}
|
|
31320
|
+
|
|
31321
|
+
/**
|
|
31322
|
+
* Format a provider amount object as a human-readable decimal string.
|
|
31323
|
+
*
|
|
31324
|
+
* The kit result surface intentionally exposes only the decimal string value,
|
|
31325
|
+
* so nested provider amount metadata is not retained at this boundary.
|
|
31326
|
+
*
|
|
31327
|
+
* @param amount - Provider amount object to format
|
|
31328
|
+
* @returns Human-readable decimal amount string
|
|
31329
|
+
*/
|
|
31330
|
+
function formatAmount$1(amount) {
|
|
31331
|
+
return formatUnits(amount.raw.toString(), amount.decimals);
|
|
31332
|
+
}
|
|
31333
|
+
function formatAssetAmount(assetAmount) {
|
|
31334
|
+
const { amount, ...rest } = assetAmount;
|
|
31335
|
+
return {
|
|
31336
|
+
...rest,
|
|
31337
|
+
amount: formatAmount$1(amount),
|
|
31338
|
+
};
|
|
31339
|
+
}
|
|
31340
|
+
function formatClaimedAmount(claimedAmount) {
|
|
31341
|
+
const { amount, ...rest } = claimedAmount;
|
|
31342
|
+
return {
|
|
31343
|
+
...rest,
|
|
31344
|
+
amount: formatAmount$1(amount),
|
|
31345
|
+
};
|
|
31346
|
+
}
|
|
31347
|
+
function formatAccruedReward(reward) {
|
|
31348
|
+
const { amount, ...rest } = reward;
|
|
31349
|
+
return {
|
|
31350
|
+
...rest,
|
|
31351
|
+
amount: formatAmount$1(amount),
|
|
31352
|
+
};
|
|
31353
|
+
}
|
|
31354
|
+
function formatPositionPnL(pnl) {
|
|
31355
|
+
if (pnl.status !== 'available') {
|
|
31356
|
+
return pnl;
|
|
31357
|
+
}
|
|
31358
|
+
return {
|
|
31359
|
+
...pnl,
|
|
31360
|
+
principalDeposited: formatAmount$1(pnl.principalDeposited),
|
|
31361
|
+
totalYieldEarned: formatAmount$1(pnl.totalYieldEarned),
|
|
31362
|
+
};
|
|
31363
|
+
}
|
|
31364
|
+
/**
|
|
31365
|
+
* Convert provider vault info into an EarnKit vault result.
|
|
31366
|
+
*
|
|
31367
|
+
* @param vault - Provider vault info with raw amount objects
|
|
31368
|
+
* @returns Vault info with total deposits and liquidity formatted as strings
|
|
31369
|
+
*/
|
|
31370
|
+
function formatVaultInfo(vault) {
|
|
31371
|
+
const { totalDeposits, liquidity, ...rest } = vault;
|
|
31372
|
+
return {
|
|
31373
|
+
...rest,
|
|
31374
|
+
totalDeposits: formatAmount$1(totalDeposits),
|
|
31375
|
+
liquidity: formatAmount$1(liquidity),
|
|
31376
|
+
};
|
|
31377
|
+
}
|
|
31378
|
+
/**
|
|
31379
|
+
* Convert a provider vault lookup result into an EarnKit result.
|
|
31380
|
+
*
|
|
31381
|
+
* @param result - Provider vault lookup result
|
|
31382
|
+
* @returns Vault lookup result with each vault amount formatted as strings
|
|
31383
|
+
*/
|
|
31384
|
+
function formatGetVaultsResult(result) {
|
|
31385
|
+
return {
|
|
31386
|
+
...result,
|
|
31387
|
+
vaults: result.vaults.map(formatVaultInfo),
|
|
31388
|
+
};
|
|
31389
|
+
}
|
|
31390
|
+
/**
|
|
31391
|
+
* Convert provider position info into an EarnKit position result.
|
|
31392
|
+
*
|
|
31393
|
+
* @param position - Provider position info with raw amount objects
|
|
31394
|
+
* @returns Position info with balances, shares, PnL, and rewards formatted as strings
|
|
31395
|
+
*/
|
|
31396
|
+
function formatPositionInfo(position) {
|
|
31397
|
+
const { currentBalance, shares, pnl, accruedRewards, ...rest } = position;
|
|
31398
|
+
return {
|
|
31399
|
+
...rest,
|
|
31400
|
+
currentBalance: formatAmount$1(currentBalance),
|
|
31401
|
+
shares: formatAmount$1(shares),
|
|
31402
|
+
pnl: formatPositionPnL(pnl),
|
|
31403
|
+
accruedRewards: accruedRewards.map(formatAccruedReward),
|
|
31404
|
+
};
|
|
31405
|
+
}
|
|
31406
|
+
/**
|
|
31407
|
+
* Convert a provider deposit quote into an EarnKit deposit quote result.
|
|
31408
|
+
*
|
|
31409
|
+
* @param quote - Provider deposit quote with raw amount objects
|
|
31410
|
+
* @returns Deposit quote with deposit, shares, and fees formatted as strings
|
|
31411
|
+
*/
|
|
31412
|
+
function formatDepositQuoteInfo(quote) {
|
|
31413
|
+
return {
|
|
31414
|
+
...quote,
|
|
31415
|
+
deposit: formatAssetAmount(quote.deposit),
|
|
31416
|
+
expectedShares: formatAssetAmount(quote.expectedShares),
|
|
31417
|
+
fees: quote.fees.map(formatAssetAmount),
|
|
31418
|
+
};
|
|
31419
|
+
}
|
|
31420
|
+
/**
|
|
31421
|
+
* Convert a provider withdrawal quote into an EarnKit withdrawal quote result.
|
|
31422
|
+
*
|
|
31423
|
+
* @param quote - Provider withdrawal quote with raw amount objects
|
|
31424
|
+
* @returns Withdrawal quote with withdrawal, shares, max, and fees formatted as strings
|
|
31425
|
+
*/
|
|
31426
|
+
function formatWithdrawalQuoteInfo(quote) {
|
|
31427
|
+
return {
|
|
31428
|
+
...quote,
|
|
31429
|
+
withdrawal: formatAssetAmount(quote.withdrawal),
|
|
31430
|
+
sharesToRedeem: formatAssetAmount(quote.sharesToRedeem),
|
|
31431
|
+
maxWithdrawable: formatAssetAmount(quote.maxWithdrawable),
|
|
31432
|
+
fees: quote.fees.map(formatAssetAmount),
|
|
31433
|
+
};
|
|
31434
|
+
}
|
|
31435
|
+
/**
|
|
31436
|
+
* Convert a provider claim-rewards quote into an EarnKit quote result.
|
|
31437
|
+
*
|
|
31438
|
+
* @param quote - Provider claim-rewards quote with raw reward amount objects
|
|
31439
|
+
* @returns Claim-rewards quote with rewards formatted as strings
|
|
31440
|
+
*/
|
|
31441
|
+
function formatClaimRewardsQuoteInfo(quote) {
|
|
31442
|
+
return {
|
|
31443
|
+
...quote,
|
|
31444
|
+
rewards: quote.rewards.map(formatAssetAmount),
|
|
31445
|
+
};
|
|
31446
|
+
}
|
|
31447
|
+
/**
|
|
31448
|
+
* Convert a provider claim-rewards result into an EarnKit result.
|
|
31449
|
+
*
|
|
31450
|
+
* @param result - Provider claim-rewards result
|
|
31451
|
+
* @returns Claim-rewards result with claimed reward amounts formatted as strings
|
|
31452
|
+
*/
|
|
31453
|
+
function formatClaimRewardsResult(result) {
|
|
31454
|
+
if (result.status === 'no_rewards') {
|
|
31455
|
+
return result;
|
|
31456
|
+
}
|
|
31457
|
+
return {
|
|
31458
|
+
...result,
|
|
31459
|
+
rewards: result.rewards.map(formatClaimedAmount),
|
|
31460
|
+
};
|
|
31461
|
+
}
|
|
31462
|
+
|
|
31463
|
+
/**
|
|
31464
|
+
* Schema for the adapter context within earn operations.
|
|
31465
|
+
*
|
|
31466
|
+
* Validate that a properly-shaped adapter instance and a valid chain
|
|
31467
|
+
* identifier are provided. The address field is optional (resolved from
|
|
31468
|
+
* the adapter at runtime).
|
|
31469
|
+
*
|
|
31470
|
+
* @internal
|
|
31471
|
+
*/
|
|
31472
|
+
const adapterContextSchema$4 = z.object({
|
|
31473
|
+
adapter: adapterSchema$1,
|
|
31474
|
+
// AdapterContext accepts a wider chain field, but runtime validation must
|
|
31475
|
+
// stay constrained to EarnChain identifiers.
|
|
31476
|
+
chain: earnChainIdentifierSchema,
|
|
31477
|
+
address: z.string().optional(),
|
|
31478
|
+
});
|
|
31479
|
+
/**
|
|
31480
|
+
* Schema for the EarnConfig options.
|
|
31481
|
+
*
|
|
31482
|
+
* Validate the optional Kit Key field using the standard `apiKeySchema`
|
|
31483
|
+
* format (`KIT_KEY:<keyId>:<keySecret>`). When omitted, the SDK
|
|
31484
|
+
* operates in permissionless mode.
|
|
31485
|
+
*
|
|
31486
|
+
* @internal
|
|
31487
|
+
*/
|
|
31488
|
+
const earnConfigSchema = z.object({
|
|
31489
|
+
kitKey: apiKeySchema.optional(),
|
|
31490
|
+
});
|
|
31491
|
+
/**
|
|
31492
|
+
* Schema for validating human-readable decimal amount strings.
|
|
31493
|
+
*
|
|
31494
|
+
* Accept positive decimal strings like '100', '100.50', '0.001'.
|
|
31495
|
+
* Reject zero, negative, and non-numeric strings. Support up to 18
|
|
31496
|
+
* decimal places for maximum token compatibility.
|
|
31497
|
+
*
|
|
31498
|
+
* @internal
|
|
31499
|
+
*/
|
|
31500
|
+
const amountSchema$1 = z
|
|
31501
|
+
.string({ required_error: 'amount is required' })
|
|
31502
|
+
.min(1, 'amount is required')
|
|
31503
|
+
.pipe(createDecimalStringValidator({
|
|
31504
|
+
allowZero: false,
|
|
31505
|
+
regexMessage: AMOUNT_FORMAT_ERROR_MESSAGE,
|
|
31506
|
+
attributeName: 'amount',
|
|
31507
|
+
maxDecimals: 18,
|
|
31508
|
+
})(z.string()));
|
|
31509
|
+
/**
|
|
31510
|
+
* Schema for vault address validation.
|
|
31511
|
+
*
|
|
31512
|
+
* Validate that the vault address is a valid EVM address (0x + 40 hex
|
|
31513
|
+
* chars). EarnKit currently supports EVM vault addresses on Arc Testnet.
|
|
31514
|
+
*
|
|
31515
|
+
* @internal
|
|
31516
|
+
*/
|
|
31517
|
+
const vaultAddressSchema = evmAddressSchema;
|
|
31518
|
+
/**
|
|
31519
|
+
* Validation schema for VaultQuery.
|
|
31520
|
+
*
|
|
31521
|
+
* @example
|
|
31522
|
+
* ```typescript
|
|
31523
|
+
* import { vaultQuerySchema } from '@circle-fin/earn-kit'
|
|
31524
|
+
*
|
|
31525
|
+
* const result = vaultQuerySchema.safeParse({
|
|
31526
|
+
* chain: 'Arc_Testnet',
|
|
31527
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31528
|
+
* })
|
|
31529
|
+
* ```
|
|
31530
|
+
*/
|
|
31531
|
+
const vaultQuerySchema = z.object({
|
|
31532
|
+
chain: earnChainIdentifierSchema,
|
|
31533
|
+
vaultAddress: vaultAddressSchema,
|
|
31534
|
+
});
|
|
31535
|
+
/**
|
|
31536
|
+
* Validation schema for GetVaultsParams.
|
|
31537
|
+
*
|
|
31538
|
+
* @example
|
|
31539
|
+
* ```typescript
|
|
31540
|
+
* import { getVaultsParamsSchema } from '@circle-fin/earn-kit'
|
|
31541
|
+
*
|
|
31542
|
+
* const result = getVaultsParamsSchema.safeParse({
|
|
31543
|
+
* vaults: [
|
|
31544
|
+
* {
|
|
31545
|
+
* chain: 'Arc_Testnet',
|
|
31546
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31547
|
+
* },
|
|
31548
|
+
* ],
|
|
31549
|
+
* })
|
|
31550
|
+
* ```
|
|
31551
|
+
*/
|
|
31552
|
+
const getVaultsParamsSchema = z.object({
|
|
31553
|
+
vaults: z
|
|
31554
|
+
.array(vaultQuerySchema)
|
|
31555
|
+
.min(1, 'at least one vault query is required')
|
|
31556
|
+
.max(20, 'maximum 20 vault queries per request'),
|
|
31557
|
+
config: earnConfigSchema.optional(),
|
|
31558
|
+
});
|
|
31559
|
+
/**
|
|
31560
|
+
* Validation schema for GetPositionParams.
|
|
31561
|
+
*
|
|
31562
|
+
* @example
|
|
31563
|
+
* ```typescript
|
|
31564
|
+
* import { getPositionParamsSchema } from '@circle-fin/earn-kit'
|
|
31565
|
+
*
|
|
31566
|
+
* const result = getPositionParamsSchema.safeParse({
|
|
31567
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31568
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31569
|
+
* })
|
|
31570
|
+
* ```
|
|
31571
|
+
*/
|
|
31572
|
+
const getPositionParamsSchema = z.object({
|
|
31573
|
+
from: adapterContextSchema$4,
|
|
31574
|
+
vaultAddress: vaultAddressSchema,
|
|
31575
|
+
config: earnConfigSchema.optional(),
|
|
31576
|
+
});
|
|
31577
|
+
/**
|
|
31578
|
+
* Validation schema for DepositParams.
|
|
31579
|
+
*
|
|
31580
|
+
* @example
|
|
31581
|
+
* ```typescript
|
|
31582
|
+
* import { depositParamsSchema } from '@circle-fin/earn-kit'
|
|
31583
|
+
*
|
|
31584
|
+
* const result = depositParamsSchema.safeParse({
|
|
31585
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31586
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31587
|
+
* amount: '100.50',
|
|
31588
|
+
* })
|
|
31589
|
+
* ```
|
|
31590
|
+
*/
|
|
31591
|
+
const depositParamsSchema$1 = z.object({
|
|
31592
|
+
from: adapterContextSchema$4,
|
|
31593
|
+
vaultAddress: vaultAddressSchema,
|
|
31594
|
+
amount: amountSchema$1,
|
|
31595
|
+
config: earnConfigSchema.optional(),
|
|
31596
|
+
});
|
|
31597
|
+
/**
|
|
31598
|
+
* Validation schema for WithdrawParams.
|
|
31599
|
+
*
|
|
31600
|
+
* @example
|
|
31601
|
+
* ```typescript
|
|
31602
|
+
* import { withdrawParamsSchema } from '@circle-fin/earn-kit'
|
|
31603
|
+
*
|
|
31604
|
+
* const result = withdrawParamsSchema.safeParse({
|
|
31605
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31606
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31607
|
+
* amount: '50.00',
|
|
31608
|
+
* })
|
|
31609
|
+
* ```
|
|
31610
|
+
*/
|
|
31611
|
+
const withdrawParamsSchema = z.object({
|
|
31612
|
+
from: adapterContextSchema$4,
|
|
31613
|
+
vaultAddress: vaultAddressSchema,
|
|
31614
|
+
amount: amountSchema$1,
|
|
31615
|
+
config: earnConfigSchema.optional(),
|
|
31616
|
+
});
|
|
31617
|
+
/**
|
|
31618
|
+
* Validation schema for ClaimRewardsParams.
|
|
31619
|
+
*
|
|
31620
|
+
* @example
|
|
31621
|
+
* ```typescript
|
|
31622
|
+
* import { claimRewardsParamsSchema } from '@circle-fin/earn-kit'
|
|
31623
|
+
*
|
|
31624
|
+
* const result = claimRewardsParamsSchema.safeParse({
|
|
31625
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31626
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31627
|
+
* })
|
|
31628
|
+
* ```
|
|
31629
|
+
*/
|
|
31630
|
+
const claimRewardsParamsSchema = z.object({
|
|
31631
|
+
from: adapterContextSchema$4,
|
|
31632
|
+
vaultAddress: vaultAddressSchema,
|
|
31633
|
+
config: earnConfigSchema.optional(),
|
|
31634
|
+
});
|
|
31635
|
+
/**
|
|
31636
|
+
* Validation schema for GetDepositQuoteParams.
|
|
31637
|
+
*
|
|
31638
|
+
* @example
|
|
31639
|
+
* ```typescript
|
|
31640
|
+
* import { getDepositQuoteParamsSchema } from '@circle-fin/earn-kit'
|
|
31641
|
+
*
|
|
31642
|
+
* const result = getDepositQuoteParamsSchema.safeParse({
|
|
31643
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31644
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31645
|
+
* amount: '100.50',
|
|
31646
|
+
* })
|
|
31647
|
+
* ```
|
|
31648
|
+
*/
|
|
31649
|
+
const getDepositQuoteParamsSchema = z.object({
|
|
31650
|
+
from: adapterContextSchema$4,
|
|
31651
|
+
vaultAddress: vaultAddressSchema,
|
|
31652
|
+
amount: amountSchema$1,
|
|
31653
|
+
config: earnConfigSchema.optional(),
|
|
31654
|
+
});
|
|
31655
|
+
/**
|
|
31656
|
+
* Validation schema for GetWithdrawalQuoteParams.
|
|
31657
|
+
*
|
|
31658
|
+
* @example
|
|
31659
|
+
* ```typescript
|
|
31660
|
+
* import { getWithdrawalQuoteParamsSchema } from '@circle-fin/earn-kit'
|
|
31661
|
+
*
|
|
31662
|
+
* const result = getWithdrawalQuoteParamsSchema.safeParse({
|
|
31663
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31664
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31665
|
+
* amount: '50.00',
|
|
31666
|
+
* })
|
|
31667
|
+
* ```
|
|
31668
|
+
*/
|
|
31669
|
+
const getWithdrawalQuoteParamsSchema = z.object({
|
|
31670
|
+
from: adapterContextSchema$4,
|
|
31671
|
+
vaultAddress: vaultAddressSchema,
|
|
31672
|
+
amount: amountSchema$1,
|
|
31673
|
+
config: earnConfigSchema.optional(),
|
|
31674
|
+
});
|
|
31675
|
+
/**
|
|
31676
|
+
* Validation schema for GetClaimRewardsQuoteParams.
|
|
31677
|
+
*
|
|
31678
|
+
* @example
|
|
31679
|
+
* ```typescript
|
|
31680
|
+
* import { getClaimRewardsQuoteParamsSchema } from '@circle-fin/earn-kit'
|
|
31681
|
+
*
|
|
31682
|
+
* const result = getClaimRewardsQuoteParamsSchema.safeParse({
|
|
31683
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
31684
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31685
|
+
* })
|
|
31686
|
+
* ```
|
|
31687
|
+
*/
|
|
31688
|
+
const getClaimRewardsQuoteParamsSchema = z.object({
|
|
31689
|
+
from: adapterContextSchema$4,
|
|
31690
|
+
vaultAddress: vaultAddressSchema,
|
|
31691
|
+
config: earnConfigSchema.optional(),
|
|
31692
|
+
});
|
|
31693
|
+
|
|
31694
|
+
/**
|
|
31695
|
+
* Fetch vault information from the earn service.
|
|
31696
|
+
*
|
|
31697
|
+
* Query the configured earn providers to get information about one
|
|
31698
|
+
* or more DeFi lending vaults. Accept batch queries as chain +
|
|
31699
|
+
* vaultAddress pairs.
|
|
31700
|
+
*
|
|
31701
|
+
* @param context - The EarnKit context containing providers
|
|
31702
|
+
* @param params - Vault query parameters
|
|
31703
|
+
* @returns A promise resolving to the vault lookup results
|
|
31704
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
31705
|
+
*
|
|
31706
|
+
* @example
|
|
31707
|
+
* ```typescript
|
|
31708
|
+
* import { createEarnKitContext, getVaults } from '@circle-fin/earn-kit'
|
|
31709
|
+
*
|
|
31710
|
+
* const context = createEarnKitContext()
|
|
31711
|
+
* const result = await getVaults(context, {
|
|
31712
|
+
* vaults: [{ chain: 'Arc_Testnet', vaultAddress: '0x...' }],
|
|
31713
|
+
* })
|
|
31714
|
+
* console.log(`Found ${result.vaults.length} vaults`)
|
|
31715
|
+
* ```
|
|
31716
|
+
*/
|
|
31717
|
+
async function getVaults$1(context, params) {
|
|
31718
|
+
assertEarnParams(params, getVaultsParamsSchema);
|
|
31719
|
+
const provider = findProvider(context);
|
|
31720
|
+
const result = await provider.getVaults({
|
|
31721
|
+
vaults: params.vaults,
|
|
31722
|
+
...(params.config !== undefined && { config: params.config }),
|
|
31723
|
+
});
|
|
31724
|
+
return formatGetVaultsResult(result);
|
|
31725
|
+
}
|
|
31726
|
+
|
|
31727
|
+
/**
|
|
31728
|
+
* Resolve the wallet address to use for on-chain operations.
|
|
31729
|
+
*
|
|
31730
|
+
* For developer-controlled adapters, callers pass `address` explicitly and we
|
|
31731
|
+
* return it unchanged. For user-controlled adapters, we fall back to
|
|
31732
|
+
* `adapter.getAddress(chain)`.
|
|
31733
|
+
*
|
|
31734
|
+
* @param from - Adapter context containing the adapter and optional explicit address.
|
|
31735
|
+
* @param chain - Resolved chain definition.
|
|
31736
|
+
* @returns The wallet address.
|
|
31737
|
+
* @throws Propagates errors from `adapter.getAddress(chain)` when
|
|
31738
|
+
* `from.address` is not supplied.
|
|
31739
|
+
*
|
|
31740
|
+
* @example
|
|
31741
|
+
* ```typescript
|
|
31742
|
+
* const address = await resolveAdapterAddress(params.from, chain)
|
|
31743
|
+
* ```
|
|
31744
|
+
*
|
|
31745
|
+
* @internal
|
|
31746
|
+
*/
|
|
31747
|
+
async function resolveAdapterAddress(from, chain) {
|
|
31748
|
+
if (from.address !== undefined && from.address !== '') {
|
|
31749
|
+
return from.address;
|
|
31750
|
+
}
|
|
31751
|
+
return from.adapter.getAddress(chain);
|
|
31752
|
+
}
|
|
31753
|
+
|
|
31754
|
+
/**
|
|
31755
|
+
* Fetch P&L position data for a wallet.
|
|
31756
|
+
*
|
|
31757
|
+
* Query the configured earn providers to get balance, principal,
|
|
31758
|
+
* earnings, and shares data for the wallet specified in the adapter context.
|
|
31759
|
+
*
|
|
31760
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
31761
|
+
* @param context - The EarnKit context containing providers
|
|
31762
|
+
* @param params - Position query parameters with adapter context
|
|
31763
|
+
* @returns A promise resolving to the wallet's position info
|
|
31764
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
31765
|
+
*
|
|
31766
|
+
* @example
|
|
31767
|
+
* ```typescript
|
|
31768
|
+
* import {
|
|
31769
|
+
* createEarnKitContext,
|
|
31770
|
+
* getPosition,
|
|
31771
|
+
* EarnChain,
|
|
31772
|
+
* } from '@circle-fin/earn-kit'
|
|
31773
|
+
* import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
31774
|
+
*
|
|
31775
|
+
* const context = createEarnKitContext()
|
|
31776
|
+
* const adapter = createViemAdapterFromPrivateKey({ privateKey: '0x...' })
|
|
31777
|
+
*
|
|
31778
|
+
* const position = await getPosition(context, {
|
|
31779
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
31780
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31781
|
+
* })
|
|
31782
|
+
* console.log(`Balance: ${position.currentBalance}`)
|
|
31783
|
+
* ```
|
|
31784
|
+
*/
|
|
31785
|
+
async function getPosition$1(context, params) {
|
|
31786
|
+
assertEarnParams(params, getPositionParamsSchema);
|
|
31787
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
31788
|
+
params.from.adapter.validateChainSupport(chain);
|
|
31789
|
+
const provider = findProvider(context, chain, 'getPosition');
|
|
31790
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
31791
|
+
const result = await provider.getPosition({
|
|
31792
|
+
from: { ...params.from, chain, address },
|
|
31793
|
+
vaultAddress: params.vaultAddress,
|
|
31794
|
+
...(params.config !== undefined && { config: params.config }),
|
|
31795
|
+
});
|
|
31796
|
+
return formatPositionInfo(result);
|
|
31797
|
+
}
|
|
31798
|
+
|
|
31799
|
+
/**
|
|
31800
|
+
* Execute a deposit into a DeFi lending vault.
|
|
31801
|
+
*
|
|
31802
|
+
* Validate user params, resolve the chain and wallet address, then delegate
|
|
31803
|
+
* the full on-chain flow (fetch signed instructions, issue a max USDC ERC-20
|
|
31804
|
+
* approval when needed, submit the deposit, wait for confirmation) to the
|
|
31805
|
+
* selected earn provider.
|
|
31806
|
+
*
|
|
31807
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
31808
|
+
* @param context - The EarnKit context containing providers
|
|
31809
|
+
* @param params - Deposit parameters including vault address, amount, and adapter
|
|
31810
|
+
* @returns A promise resolving to the deposit result with transaction details
|
|
31811
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
31812
|
+
* @throws {@link KitError} If the chain has no adapter contract configured
|
|
31813
|
+
* @throws {@link KitError} If the chain does not support USDC
|
|
31814
|
+
* @throws {@link KitError} If the USDC approval transaction reverts on-chain
|
|
31815
|
+
* @throws {@link KitError} If the deposit transaction reverts on-chain
|
|
31816
|
+
*
|
|
31817
|
+
* @example
|
|
31818
|
+
* ```typescript
|
|
31819
|
+
* import {
|
|
31820
|
+
* createEarnKitContext,
|
|
31821
|
+
* deposit,
|
|
31822
|
+
* EarnChain,
|
|
31823
|
+
* } from '@circle-fin/earn-kit'
|
|
31824
|
+
* import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
31825
|
+
*
|
|
31826
|
+
* const context = createEarnKitContext()
|
|
31827
|
+
* const adapter = createViemAdapterFromPrivateKey({ privateKey: '0x...' })
|
|
31828
|
+
*
|
|
31829
|
+
* const result = await deposit(context, {
|
|
31830
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
31831
|
+
* vaultAddress: '0x...',
|
|
31832
|
+
* amount: '100.50',
|
|
31833
|
+
* })
|
|
31834
|
+
* console.log(`Deposited ${result.amount} into ${result.vaultAddress}, tx: ${result.txHash}`)
|
|
31835
|
+
* ```
|
|
31836
|
+
*/
|
|
31837
|
+
async function deposit$3(context, params) {
|
|
31838
|
+
assertEarnParams(params, depositParamsSchema$1);
|
|
31839
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
31840
|
+
params.from.adapter.validateChainSupport(chain);
|
|
31841
|
+
const provider = findProvider(context, chain, 'deposit');
|
|
31842
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
31843
|
+
return provider.deposit({
|
|
31844
|
+
from: { ...params.from, chain, address },
|
|
31845
|
+
vaultAddress: params.vaultAddress,
|
|
31846
|
+
amount: params.amount,
|
|
31847
|
+
...(params.config !== undefined && { config: params.config }),
|
|
31848
|
+
});
|
|
31849
|
+
}
|
|
31850
|
+
|
|
31851
|
+
/**
|
|
31852
|
+
* Execute a withdrawal from a DeFi lending vault.
|
|
31853
|
+
*
|
|
31854
|
+
* Validate user params, resolve the chain and wallet address, then delegate
|
|
31855
|
+
* the full on-chain flow (fetch signed instructions, issue a max vault-share
|
|
31856
|
+
* ERC-20 approval when needed, submit the withdrawal, wait for confirmation)
|
|
31857
|
+
* to the selected earn provider.
|
|
31858
|
+
*
|
|
31859
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
31860
|
+
* @param context - The EarnKit context containing providers
|
|
31861
|
+
* @param params - Withdrawal parameters including vault address, amount, and adapter
|
|
31862
|
+
* @returns A promise resolving to the withdrawal result with transaction details
|
|
31863
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
31864
|
+
* @throws {@link KitError} If the chain has no adapter contract configured
|
|
31865
|
+
* @throws {@link KitError} If the vault share token approval transaction reverts on-chain
|
|
31866
|
+
* @throws {@link KitError} If the withdrawal transaction reverts on-chain
|
|
31867
|
+
*
|
|
31868
|
+
* @example
|
|
31869
|
+
* ```typescript
|
|
31870
|
+
* import {
|
|
31871
|
+
* createEarnKitContext,
|
|
31872
|
+
* withdraw,
|
|
31873
|
+
* EarnChain,
|
|
31874
|
+
* } from '@circle-fin/earn-kit'
|
|
31875
|
+
* import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
31876
|
+
*
|
|
31877
|
+
* const context = createEarnKitContext()
|
|
31878
|
+
* const adapter = createViemAdapterFromPrivateKey({ privateKey: '0x...' })
|
|
31879
|
+
*
|
|
31880
|
+
* const result = await withdraw(context, {
|
|
31881
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
31882
|
+
* vaultAddress: '0x...',
|
|
31883
|
+
* amount: '50.00',
|
|
31884
|
+
* })
|
|
31885
|
+
* console.log(`Withdrew ${result.amount} from ${result.vaultAddress}, tx: ${result.txHash}`)
|
|
31886
|
+
* ```
|
|
31887
|
+
*/
|
|
31888
|
+
async function withdraw$1(context, params) {
|
|
31889
|
+
assertEarnParams(params, withdrawParamsSchema);
|
|
31890
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
31891
|
+
params.from.adapter.validateChainSupport(chain);
|
|
31892
|
+
const provider = findProvider(context, chain, 'withdraw');
|
|
31893
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
31894
|
+
return provider.withdraw({
|
|
31895
|
+
from: { ...params.from, chain, address },
|
|
31896
|
+
vaultAddress: params.vaultAddress,
|
|
31897
|
+
amount: params.amount,
|
|
31898
|
+
...(params.config !== undefined && { config: params.config }),
|
|
31899
|
+
});
|
|
31900
|
+
}
|
|
31901
|
+
|
|
31902
|
+
/**
|
|
31903
|
+
* Claim rewards from earn vaults.
|
|
31904
|
+
*
|
|
31905
|
+
* Validate user params, resolve the chain and wallet address, then delegate
|
|
31906
|
+
* the full on-chain flow (fetch signed instructions, submit the claim, wait
|
|
31907
|
+
* for confirmation) to the selected earn provider. When no rewards are
|
|
31908
|
+
* claimable, the provider returns rewards without submitting a transaction.
|
|
31909
|
+
*
|
|
31910
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
31911
|
+
* @param context - The EarnKit context containing providers
|
|
31912
|
+
* @param params - Claim parameters including adapter context and vault address
|
|
31913
|
+
* @returns A promise resolving to the claim result with reward details
|
|
31914
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
31915
|
+
* @throws {@link KitError} If the chain has no adapter contract configured
|
|
31916
|
+
* @throws {@link KitError} If the earn service API request or response fails
|
|
31917
|
+
* @throws {@link KitError} If the claim transaction reverts on-chain
|
|
31918
|
+
*
|
|
31919
|
+
* @example
|
|
31920
|
+
* ```typescript
|
|
31921
|
+
* import {
|
|
31922
|
+
* createEarnKitContext,
|
|
31923
|
+
* claimRewards,
|
|
31924
|
+
* EarnChain,
|
|
31925
|
+
* } from '@circle-fin/earn-kit'
|
|
31926
|
+
* import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
31927
|
+
*
|
|
31928
|
+
* const context = createEarnKitContext()
|
|
31929
|
+
* const adapter = createViemAdapterFromPrivateKey({ privateKey: '0x...' })
|
|
31930
|
+
*
|
|
31931
|
+
* const result = await claimRewards(context, {
|
|
31932
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
31933
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
31934
|
+
* })
|
|
31935
|
+
* if (result.status === 'no_rewards') {
|
|
31936
|
+
* console.log('No rewards to claim')
|
|
31937
|
+
* } else {
|
|
31938
|
+
* console.log(`Claimed ${result.rewards.length} reward(s), tx: ${result.txHash}`)
|
|
31939
|
+
* }
|
|
31940
|
+
* ```
|
|
31941
|
+
*/
|
|
31942
|
+
async function claimRewards$1(context, params) {
|
|
31943
|
+
assertEarnParams(params, claimRewardsParamsSchema);
|
|
31944
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
31945
|
+
params.from.adapter.validateChainSupport(chain);
|
|
31946
|
+
const provider = findProvider(context, chain, 'claimRewards');
|
|
31947
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
31948
|
+
const result = await provider.claimRewards({
|
|
31949
|
+
from: { ...params.from, chain, address },
|
|
31950
|
+
vaultAddress: params.vaultAddress,
|
|
31951
|
+
...(params.config === undefined ? {} : { config: params.config }),
|
|
31952
|
+
});
|
|
31953
|
+
return formatClaimRewardsResult(result);
|
|
31954
|
+
}
|
|
31955
|
+
|
|
31956
|
+
/**
|
|
31957
|
+
* Get an informational quote for a deposit into a vault.
|
|
31958
|
+
*
|
|
31959
|
+
* Query expected shares, share price, and APY for the specified
|
|
31960
|
+
* deposit amount without executing any transaction.
|
|
31961
|
+
*
|
|
31962
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
31963
|
+
* @param context - The EarnKit context containing providers
|
|
31964
|
+
* @param params - Deposit quote parameters including vault address and amount
|
|
31965
|
+
* @returns A promise resolving to the deposit quote information
|
|
31966
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
31967
|
+
*
|
|
31968
|
+
* @example
|
|
31969
|
+
* ```typescript
|
|
31970
|
+
* import {
|
|
31971
|
+
* createEarnKitContext,
|
|
31972
|
+
* getDepositQuote,
|
|
31973
|
+
* EarnChain,
|
|
31974
|
+
* } from '@circle-fin/earn-kit'
|
|
31975
|
+
*
|
|
31976
|
+
* const context = createEarnKitContext()
|
|
31977
|
+
* const quote = await getDepositQuote(context, {
|
|
31978
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
31979
|
+
* vaultAddress: '0x...',
|
|
31980
|
+
* amount: '100.50',
|
|
31981
|
+
* })
|
|
31982
|
+
* console.log(`Expected shares: ${quote.expectedShares.amount}`)
|
|
31983
|
+
* ```
|
|
31984
|
+
*/
|
|
31985
|
+
async function getDepositQuote$1(context, params) {
|
|
31986
|
+
assertEarnParams(params, getDepositQuoteParamsSchema);
|
|
31987
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
31988
|
+
params.from.adapter.validateChainSupport(chain);
|
|
31989
|
+
const provider = findProvider(context, chain, 'getDepositQuote');
|
|
31990
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
31991
|
+
const result = await provider.getDepositQuote({
|
|
31992
|
+
from: { ...params.from, chain, address },
|
|
31993
|
+
vaultAddress: params.vaultAddress,
|
|
31994
|
+
amount: params.amount,
|
|
31995
|
+
...(params.config !== undefined && { config: params.config }),
|
|
31996
|
+
});
|
|
31997
|
+
return formatDepositQuoteInfo(result);
|
|
31998
|
+
}
|
|
31999
|
+
|
|
32000
|
+
/**
|
|
32001
|
+
* Get an informational quote for a withdrawal from a vault.
|
|
32002
|
+
*
|
|
32003
|
+
* Query shares to redeem, max withdrawable, and fees for the specified
|
|
32004
|
+
* withdrawal amount without executing any transaction.
|
|
32005
|
+
*
|
|
32006
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
32007
|
+
* @param context - The EarnKit context containing providers
|
|
32008
|
+
* @param params - Withdrawal quote parameters including vault address and amount
|
|
32009
|
+
* @returns A promise resolving to the withdrawal quote information
|
|
32010
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32011
|
+
*
|
|
32012
|
+
* @example
|
|
32013
|
+
* ```typescript
|
|
32014
|
+
* import {
|
|
32015
|
+
* createEarnKitContext,
|
|
32016
|
+
* getWithdrawalQuote,
|
|
32017
|
+
* EarnChain,
|
|
32018
|
+
* } from '@circle-fin/earn-kit'
|
|
32019
|
+
*
|
|
32020
|
+
* const context = createEarnKitContext()
|
|
32021
|
+
* const quote = await getWithdrawalQuote(context, {
|
|
32022
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
32023
|
+
* vaultAddress: '0x...',
|
|
32024
|
+
* amount: '50.00',
|
|
32025
|
+
* })
|
|
32026
|
+
* console.log(`Shares to redeem: ${quote.sharesToRedeem.amount}`)
|
|
32027
|
+
* ```
|
|
32028
|
+
*/
|
|
32029
|
+
async function getWithdrawalQuote$1(context, params) {
|
|
32030
|
+
assertEarnParams(params, getWithdrawalQuoteParamsSchema);
|
|
32031
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
32032
|
+
params.from.adapter.validateChainSupport(chain);
|
|
32033
|
+
const provider = findProvider(context, chain, 'getWithdrawalQuote');
|
|
32034
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
32035
|
+
const result = await provider.getWithdrawalQuote({
|
|
32036
|
+
from: { ...params.from, chain, address },
|
|
32037
|
+
vaultAddress: params.vaultAddress,
|
|
32038
|
+
amount: params.amount,
|
|
32039
|
+
...(params.config !== undefined && { config: params.config }),
|
|
32040
|
+
});
|
|
32041
|
+
return formatWithdrawalQuoteInfo(result);
|
|
32042
|
+
}
|
|
32043
|
+
|
|
32044
|
+
/**
|
|
32045
|
+
* Return all chains supported by configured earn providers.
|
|
32046
|
+
*
|
|
32047
|
+
* @param context - The EarnKit context containing providers
|
|
32048
|
+
* @returns Deduplicated supported chain definitions
|
|
32049
|
+
*
|
|
32050
|
+
* @example
|
|
32051
|
+
* ```typescript
|
|
32052
|
+
* import { createEarnKitContext, getSupportedChains } from '@circle-fin/earn-kit'
|
|
32053
|
+
*
|
|
32054
|
+
* const context = createEarnKitContext()
|
|
32055
|
+
* const chains = getSupportedChains(context)
|
|
32056
|
+
* ```
|
|
32057
|
+
*/
|
|
32058
|
+
function getSupportedChains$3(context) {
|
|
32059
|
+
const earnChains = new Set(Object.values(EarnChain));
|
|
32060
|
+
const chainsById = new Map();
|
|
32061
|
+
for (const provider of context.providers) {
|
|
32062
|
+
for (const chain of provider.supportedChains) {
|
|
32063
|
+
if (earnChains.has(chain.chain)) {
|
|
32064
|
+
chainsById.set(chain.chain, chain);
|
|
32065
|
+
}
|
|
32066
|
+
}
|
|
32067
|
+
}
|
|
32068
|
+
return [...chainsById.values()];
|
|
32069
|
+
}
|
|
32070
|
+
|
|
32071
|
+
/**
|
|
32072
|
+
* Get an informational quote for claiming rewards.
|
|
32073
|
+
*
|
|
32074
|
+
* Query claimable reward details without executing any transaction.
|
|
32075
|
+
*
|
|
32076
|
+
* @typeParam TFromAdapterCapabilities - The adapter capabilities type
|
|
32077
|
+
* @param context - The EarnKit context containing providers
|
|
32078
|
+
* @param params - Claim rewards quote parameters including vault address
|
|
32079
|
+
* @returns A promise resolving to the claim rewards quote information
|
|
32080
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32081
|
+
*
|
|
32082
|
+
* @example
|
|
32083
|
+
* ```typescript
|
|
32084
|
+
* import {
|
|
32085
|
+
* createEarnKitContext,
|
|
32086
|
+
* getClaimRewardsQuote,
|
|
32087
|
+
* EarnChain,
|
|
32088
|
+
* } from '@circle-fin/earn-kit'
|
|
32089
|
+
*
|
|
32090
|
+
* const context = createEarnKitContext()
|
|
32091
|
+
* const quote = await getClaimRewardsQuote(context, {
|
|
32092
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
32093
|
+
* vaultAddress: '0x...',
|
|
32094
|
+
* })
|
|
32095
|
+
* console.log(`Claimable rewards: ${quote.rewards.length}`)
|
|
32096
|
+
* ```
|
|
32097
|
+
*/
|
|
32098
|
+
async function getClaimRewardsQuote$1(context, params) {
|
|
32099
|
+
assertEarnParams(params, getClaimRewardsQuoteParamsSchema);
|
|
32100
|
+
const chain = resolveChainIdentifier(params.from.chain);
|
|
32101
|
+
params.from.adapter.validateChainSupport(chain);
|
|
32102
|
+
const provider = findProvider(context, chain, 'getClaimRewardsQuote');
|
|
32103
|
+
const address = await resolveAdapterAddress(params.from, chain);
|
|
32104
|
+
const result = await provider.getClaimRewardsQuote({
|
|
32105
|
+
from: { ...params.from, chain, address },
|
|
32106
|
+
vaultAddress: params.vaultAddress,
|
|
32107
|
+
...(params.config !== undefined && { config: params.config }),
|
|
32108
|
+
});
|
|
32109
|
+
return formatClaimRewardsQuoteInfo(result);
|
|
32110
|
+
}
|
|
32111
|
+
|
|
32112
|
+
/**
|
|
32113
|
+
* A high-level class-based interface for DeFi lending vault operations.
|
|
32114
|
+
*
|
|
32115
|
+
* EarnKit provides a familiar class-based API for developers who prefer
|
|
32116
|
+
* traditional object-oriented patterns. The class maintains an internal
|
|
32117
|
+
* context and provides methods for depositing, withdrawing, claiming
|
|
32118
|
+
* rewards, and querying vault and position data.
|
|
32119
|
+
*
|
|
32120
|
+
* Key features:
|
|
32121
|
+
* - Strongly typed parameters with comprehensive Zod validation
|
|
32122
|
+
* - Supported earn vault deposits and withdrawals
|
|
32123
|
+
* - Deposit quote queries (expected shares, share price, APY without executing a transaction)
|
|
32124
|
+
* - Withdrawal quote queries (shares to redeem, max withdrawable, fees without executing a transaction)
|
|
32125
|
+
* - Claim rewards quote queries (claimable reward details without executing a transaction)
|
|
32126
|
+
* - Reward claiming
|
|
32127
|
+
* - P&L position tracking
|
|
32128
|
+
* - Dual-mode authentication (permissioned and permissionless)
|
|
32129
|
+
* - Full TypeScript support with IntelliSense
|
|
32130
|
+
*
|
|
32131
|
+
* @example
|
|
32132
|
+
* ```typescript
|
|
32133
|
+
* import { EarnChain, EarnKit } from '@circle-fin/earn-kit'
|
|
32134
|
+
* import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'
|
|
32135
|
+
*
|
|
32136
|
+
* const kit = new EarnKit()
|
|
32137
|
+
* const adapter = createViemAdapterFromPrivateKey({
|
|
32138
|
+
* privateKey: process.env.PRIVATE_KEY,
|
|
32139
|
+
* })
|
|
32140
|
+
*
|
|
32141
|
+
* // Fetch vault info
|
|
32142
|
+
* const { vaults } = await kit.getVaults({
|
|
32143
|
+
* vaults: [{ chain: EarnChain.Arc_Testnet, vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458' }],
|
|
32144
|
+
* })
|
|
32145
|
+
* const [vault] = vaults
|
|
32146
|
+
* if (vault === undefined) {
|
|
32147
|
+
* throw new Error('No earn vaults available')
|
|
32148
|
+
* }
|
|
32149
|
+
*
|
|
32150
|
+
* // Deposit into a vault
|
|
32151
|
+
* const result = await kit.deposit({
|
|
32152
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
32153
|
+
* vaultAddress: vault.vaultAddress,
|
|
32154
|
+
* amount: '100.50',
|
|
32155
|
+
* })
|
|
32156
|
+
* console.log(`Deposited ${result.amount} into ${result.vaultAddress}, tx: ${result.txHash}`)
|
|
32157
|
+
* ```
|
|
32158
|
+
*
|
|
32159
|
+
* @remarks
|
|
32160
|
+
* For functional usage, import and use the operations directly:
|
|
32161
|
+
* ```typescript
|
|
32162
|
+
* import { createEarnKitContext, deposit } from '@circle-fin/earn-kit'
|
|
32163
|
+
* const context = createEarnKitContext()
|
|
32164
|
+
* await deposit(context, params)
|
|
32165
|
+
* ```
|
|
32166
|
+
*/
|
|
32167
|
+
class EarnKit {
|
|
32168
|
+
context;
|
|
32169
|
+
/**
|
|
32170
|
+
* Create a new EarnKit instance.
|
|
32171
|
+
*
|
|
32172
|
+
* Accept an optional configuration object to customize the kit's
|
|
32173
|
+
* behavior. If no configuration is provided, the kit is initialized
|
|
32174
|
+
* with default settings using the built-in EarnServiceProvider.
|
|
32175
|
+
*
|
|
32176
|
+
* @param config - Optional configuration for the EarnKit instance
|
|
32177
|
+
*
|
|
32178
|
+
* @example
|
|
32179
|
+
* ```typescript
|
|
32180
|
+
* import { EarnKit } from '@circle-fin/earn-kit'
|
|
32181
|
+
*
|
|
32182
|
+
* // Create with default configuration
|
|
32183
|
+
* const kit = new EarnKit()
|
|
32184
|
+
* ```
|
|
32185
|
+
*/
|
|
32186
|
+
constructor(config = {}) {
|
|
32187
|
+
this.context = createEarnKitContext(config);
|
|
32188
|
+
}
|
|
32189
|
+
/**
|
|
32190
|
+
* Return the chains supported by configured earn providers.
|
|
32191
|
+
*
|
|
32192
|
+
* @returns Deduplicated supported chain definitions
|
|
32193
|
+
*
|
|
32194
|
+
* @example
|
|
32195
|
+
* ```typescript
|
|
32196
|
+
* const kit = new EarnKit()
|
|
32197
|
+
* const chains = kit.getSupportedChains()
|
|
32198
|
+
* ```
|
|
32199
|
+
*/
|
|
32200
|
+
getSupportedChains() {
|
|
32201
|
+
return getSupportedChains$3(this.context);
|
|
32202
|
+
}
|
|
32203
|
+
/**
|
|
32204
|
+
* Fetch available vault information.
|
|
32205
|
+
*
|
|
32206
|
+
* Query the configured earn providers to get a list of available
|
|
32207
|
+
* DeFi lending vaults with APY, TVL, and reward information.
|
|
32208
|
+
*
|
|
32209
|
+
* @param params - Query parameters with vault address and optional chain filter
|
|
32210
|
+
* @returns A promise resolving to the list of available vaults
|
|
32211
|
+
* @throws {@link KitError} If no provider is configured
|
|
32212
|
+
*
|
|
32213
|
+
* @example
|
|
32214
|
+
* ```typescript
|
|
32215
|
+
* const kit = new EarnKit()
|
|
32216
|
+
* const result = await kit.getVaults({
|
|
32217
|
+
* vaults: [{ chain: 'Arc_Testnet', vaultAddress: '0x8eB67...' }],
|
|
32218
|
+
* })
|
|
32219
|
+
* result.vaults.forEach(v => console.log(`${v.name}: ${(v.currentApy * 100).toFixed(2)}% APY`))
|
|
32220
|
+
* ```
|
|
32221
|
+
*/
|
|
32222
|
+
async getVaults(params) {
|
|
32223
|
+
return getVaults$1(this.context, params);
|
|
32224
|
+
}
|
|
32225
|
+
/**
|
|
32226
|
+
* Fetch P&L position data for the connected wallet.
|
|
32227
|
+
*
|
|
32228
|
+
* Query balance, principal, earnings, and shares data for the wallet
|
|
32229
|
+
* specified in the adapter context.
|
|
32230
|
+
*
|
|
32231
|
+
* @param params - Position query parameters with adapter context
|
|
32232
|
+
* @returns A promise resolving to the wallet's position info
|
|
32233
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32234
|
+
*
|
|
32235
|
+
* @example
|
|
32236
|
+
* ```typescript
|
|
32237
|
+
* const position = await kit.getPosition({
|
|
32238
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32239
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
32240
|
+
* })
|
|
32241
|
+
* if (position.pnl.status === 'available') {
|
|
32242
|
+
* console.log(`Yield: ${position.pnl.totalYieldEarned}`)
|
|
32243
|
+
* }
|
|
32244
|
+
* ```
|
|
32245
|
+
*/
|
|
32246
|
+
async getPosition(params) {
|
|
32247
|
+
return getPosition$1(this.context, params);
|
|
32248
|
+
}
|
|
32249
|
+
/**
|
|
32250
|
+
* Execute a deposit into a DeFi lending vault.
|
|
32251
|
+
*
|
|
32252
|
+
* Build signed deposit instructions via the earn service, issue a max USDC
|
|
32253
|
+
* ERC-20 approval for the adapter contract when the current allowance is
|
|
32254
|
+
* below the SDK threshold, then submit the deposit on-chain. Return the
|
|
32255
|
+
* confirmed transaction hash together with the deposited amount and target
|
|
32256
|
+
* vault.
|
|
32257
|
+
*
|
|
32258
|
+
* @param params - Deposit parameters including vault address and amount
|
|
32259
|
+
* @returns A promise resolving to the deposit result with transaction details
|
|
32260
|
+
* @throws {@link KitError} If validation fails, no provider is configured, the
|
|
32261
|
+
* chain has no adapter contract configured, the approval transaction
|
|
32262
|
+
* reverts on-chain, or the deposit transaction reverts on-chain
|
|
32263
|
+
*
|
|
32264
|
+
* @example
|
|
32265
|
+
* ```typescript
|
|
32266
|
+
* const result = await kit.deposit({
|
|
32267
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32268
|
+
* vaultAddress: '0x...',
|
|
32269
|
+
* amount: '100.50',
|
|
32270
|
+
* })
|
|
32271
|
+
* console.log(`Deposited ${result.amount} into ${result.vaultAddress}, tx: ${result.txHash}`)
|
|
32272
|
+
* ```
|
|
32273
|
+
*/
|
|
32274
|
+
async deposit(params) {
|
|
32275
|
+
return deposit$3(this.context, params);
|
|
32276
|
+
}
|
|
32277
|
+
/**
|
|
32278
|
+
* Execute a withdrawal from a DeFi lending vault.
|
|
32279
|
+
*
|
|
32280
|
+
* Build signed withdrawal instructions via the earn service, approve the
|
|
32281
|
+
* adapter contract to spend vault share tokens if needed, then submit the
|
|
32282
|
+
* withdrawal on-chain. Return the withdrawal result including the on-chain
|
|
32283
|
+
* transaction hash, withdrawn amount, and target vault.
|
|
32284
|
+
*
|
|
32285
|
+
* @param params - Withdrawal parameters including vault address and amount
|
|
32286
|
+
* @returns A promise resolving to the withdrawal result with transaction details
|
|
32287
|
+
* @throws {@link KitError} If validation fails, no provider is configured,
|
|
32288
|
+
* the chain has no adapter contract configured, the share-token approval
|
|
32289
|
+
* reverts on-chain, or the withdrawal transaction reverts on-chain.
|
|
32290
|
+
*
|
|
32291
|
+
* @example
|
|
32292
|
+
* ```typescript
|
|
32293
|
+
* const result = await kit.withdraw({
|
|
32294
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32295
|
+
* vaultAddress: '0x...',
|
|
32296
|
+
* amount: '50.00',
|
|
32297
|
+
* })
|
|
32298
|
+
* console.log(`Withdrew ${result.amount} from ${result.vaultAddress}, tx: ${result.txHash}`)
|
|
32299
|
+
* ```
|
|
32300
|
+
*/
|
|
32301
|
+
async withdraw(params) {
|
|
32302
|
+
return withdraw$1(this.context, params);
|
|
32303
|
+
}
|
|
32304
|
+
/**
|
|
32305
|
+
* Claim rewards from earn vaults.
|
|
32306
|
+
*
|
|
32307
|
+
* Build signed claim rewards instructions via the earn service, submit
|
|
32308
|
+
* them on-chain through the adapter, and return the confirmed transaction
|
|
32309
|
+
* hash. When no rewards are claimable, no transaction is submitted.
|
|
32310
|
+
*
|
|
32311
|
+
* @param params - Claim parameters including adapter context
|
|
32312
|
+
* @returns A promise resolving to the claim result with reward details
|
|
32313
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32314
|
+
*
|
|
32315
|
+
* @example
|
|
32316
|
+
* ```typescript
|
|
32317
|
+
* const result = await kit.claimRewards({
|
|
32318
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32319
|
+
* vaultAddress: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
32320
|
+
* })
|
|
32321
|
+
*
|
|
32322
|
+
* if (result.status === 'claimed') {
|
|
32323
|
+
* console.log(`Claimed ${result.rewards.length} reward token(s), tx: ${result.txHash}`)
|
|
32324
|
+
* }
|
|
32325
|
+
* ```
|
|
32326
|
+
*/
|
|
32327
|
+
async claimRewards(params) {
|
|
32328
|
+
return claimRewards$1(this.context, params);
|
|
32329
|
+
}
|
|
32330
|
+
/**
|
|
32331
|
+
* Get an informational quote for a deposit into a vault.
|
|
32332
|
+
*
|
|
32333
|
+
* Query expected shares, share price, and APY for the specified
|
|
32334
|
+
* deposit amount without executing any transaction.
|
|
32335
|
+
*
|
|
32336
|
+
* @param params - Deposit quote parameters including vault address and amount
|
|
32337
|
+
* @returns A promise resolving to the deposit quote information
|
|
32338
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32339
|
+
*
|
|
32340
|
+
* @example
|
|
32341
|
+
* ```typescript
|
|
32342
|
+
* const quote = await kit.getDepositQuote({
|
|
32343
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32344
|
+
* vaultAddress: '0x...',
|
|
32345
|
+
* amount: '100.50',
|
|
32346
|
+
* })
|
|
32347
|
+
* console.log(`Expected shares: ${quote.expectedShares.amount}`)
|
|
32348
|
+
* ```
|
|
32349
|
+
*/
|
|
32350
|
+
async getDepositQuote(params) {
|
|
32351
|
+
return getDepositQuote$1(this.context, params);
|
|
32352
|
+
}
|
|
32353
|
+
/**
|
|
32354
|
+
* Get an informational quote for a withdrawal from a vault.
|
|
32355
|
+
*
|
|
32356
|
+
* Query shares to redeem, max withdrawable, and fees for the specified
|
|
32357
|
+
* withdrawal amount without executing any transaction.
|
|
32358
|
+
*
|
|
32359
|
+
* @param params - Withdrawal quote parameters including vault address and amount
|
|
32360
|
+
* @returns A promise resolving to the withdrawal quote information
|
|
32361
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32362
|
+
*
|
|
32363
|
+
* @example
|
|
32364
|
+
* ```typescript
|
|
32365
|
+
* const quote = await kit.getWithdrawalQuote({
|
|
32366
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32367
|
+
* vaultAddress: '0x...',
|
|
32368
|
+
* amount: '50.00',
|
|
32369
|
+
* })
|
|
32370
|
+
* console.log(`Shares to redeem: ${quote.sharesToRedeem.amount}`)
|
|
32371
|
+
* ```
|
|
32372
|
+
*/
|
|
32373
|
+
async getWithdrawalQuote(params) {
|
|
32374
|
+
return getWithdrawalQuote$1(this.context, params);
|
|
32375
|
+
}
|
|
32376
|
+
/**
|
|
32377
|
+
* Get an informational quote for claiming rewards.
|
|
32378
|
+
*
|
|
32379
|
+
* Query claimable reward details without executing any transaction.
|
|
32380
|
+
*
|
|
32381
|
+
* @param params - Claim rewards quote parameters including vault address
|
|
32382
|
+
* @returns A promise resolving to the claim rewards quote information
|
|
32383
|
+
* @throws {@link KitError} If validation fails or no provider is configured
|
|
32384
|
+
*
|
|
32385
|
+
* @example
|
|
32386
|
+
* ```typescript
|
|
32387
|
+
* const quote = await kit.getClaimRewardsQuote({
|
|
32388
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
32389
|
+
* vaultAddress: '0x...',
|
|
32390
|
+
* })
|
|
32391
|
+
* console.log(`Claimable rewards: ${quote.rewards.length}`)
|
|
32392
|
+
* ```
|
|
32393
|
+
*/
|
|
32394
|
+
async getClaimRewardsQuote(params) {
|
|
32395
|
+
return getClaimRewardsQuote$1(this.context, params);
|
|
32396
|
+
}
|
|
32397
|
+
}
|
|
32398
|
+
|
|
32399
|
+
/**
|
|
32400
|
+
*
|
|
32401
|
+
* Earn Kit
|
|
32402
|
+
*
|
|
32403
|
+
* A strongly-typed SDK for DeFi lending vault deposits, withdrawals, and rewards
|
|
32404
|
+
* @packageDocumentation
|
|
32405
|
+
*/
|
|
32406
|
+
// Auto-register this kit for user agent tracking
|
|
32407
|
+
registerKit(`${pkg$1.name}/${pkg$1.version}`);
|
|
32408
|
+
|
|
32409
|
+
/**
|
|
32410
|
+
* Create an EarnKit instance for AppKit earn operations.
|
|
32411
|
+
*
|
|
32412
|
+
* @remarks The context parameter is reserved for future EarnKit wiring.
|
|
32413
|
+
* EarnKit does not currently support AppKit developer fee hooks, so the
|
|
32414
|
+
* factory does not read fee callbacks from the context. Earn custom fees remain
|
|
32415
|
+
* reserved until EarnKit fee support ships.
|
|
32416
|
+
*
|
|
32417
|
+
* When EarnKit supports developer fee hooks, this factory can wire AppKit context through.
|
|
32418
|
+
*
|
|
32419
|
+
* @param context - AppKit context reserved for future EarnKit wiring
|
|
32420
|
+
* @returns A new EarnKit instance
|
|
32421
|
+
*
|
|
32422
|
+
* @example
|
|
32423
|
+
* ```typescript
|
|
32424
|
+
* const earnKit = createEarnKit(context)
|
|
32425
|
+
* ```
|
|
32426
|
+
*/
|
|
32427
|
+
const createEarnKit = () => new EarnKit();
|
|
32428
|
+
|
|
32429
|
+
/**
|
|
32430
|
+
* Register event handlers from a context actions map to a kit instance.
|
|
32431
|
+
*
|
|
32432
|
+
* This utility function registers event handlers stored in a context actions map
|
|
32433
|
+
* with a kit instance that supports event handling via an `on` method. It handles
|
|
32434
|
+
* wildcard handlers ('*') and prefixed action handlers, stripping the prefix
|
|
32435
|
+
* before registration.
|
|
32436
|
+
*
|
|
32437
|
+
* The function is designed to be reusable across different operation types
|
|
32438
|
+
* (bridge, swap, stake, etc.) by accepting a configurable prefix parameter.
|
|
32439
|
+
*
|
|
32440
|
+
* @param kit - The kit instance to register handlers with (must have an `on` method)
|
|
32441
|
+
* @param handlers - Map of action names to arrays of handler functions
|
|
32442
|
+
* @param prefix - Optional prefix to strip from action names (e.g., 'bridge.')
|
|
32443
|
+
*
|
|
32444
|
+
* @example
|
|
32445
|
+
* ```typescript
|
|
32446
|
+
* import { registerActionHandlers } from '@circle-fin/app-kit/utils'
|
|
32447
|
+
* import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
32448
|
+
*
|
|
32449
|
+
* const kit = new BridgeKit()
|
|
32450
|
+
* const handlers = {
|
|
32451
|
+
* '*': [(payload) => console.log('All actions:', payload)],
|
|
32452
|
+
* 'bridge.approve': [(payload) => console.log('Approved:', payload)],
|
|
32453
|
+
* 'bridge.burn': [(payload) => console.log('Burned:', payload)],
|
|
32454
|
+
* }
|
|
32455
|
+
*
|
|
32456
|
+
* registerActionHandlers(kit, handlers, 'bridge.')
|
|
32457
|
+
* ```
|
|
32458
|
+
*
|
|
32459
|
+
* @example
|
|
32460
|
+
* ```typescript
|
|
32461
|
+
* import { registerActionHandlers } from '@circle-fin/app-kit/utils'
|
|
32462
|
+
* import { SwapKit } from '@circle-fin/swap-kit'
|
|
32463
|
+
*
|
|
32464
|
+
* const kit = new SwapKit()
|
|
32465
|
+
* const handlers = {
|
|
32466
|
+
* 'swap.initiate': [(payload) => console.log('Swap initiated:', payload)],
|
|
32467
|
+
* }
|
|
32468
|
+
*
|
|
32469
|
+
* registerActionHandlers(kit, handlers, 'swap.')
|
|
32470
|
+
* ```
|
|
32471
|
+
*/
|
|
32472
|
+
const registerActionHandlers = (kit, handlers, prefix = '') => {
|
|
32473
|
+
for (const [action, handlerArray] of Object.entries(handlers)) {
|
|
32474
|
+
// Register all handlers for this action
|
|
32475
|
+
for (const handler of handlerArray) {
|
|
32476
|
+
if (action === '*') {
|
|
32477
|
+
// Wildcard handlers are registered as-is
|
|
32478
|
+
kit.on('*', handler);
|
|
32479
|
+
}
|
|
32480
|
+
else if (prefix && action.startsWith(prefix)) {
|
|
32481
|
+
// Remove prefix to get the actual kit action name
|
|
32482
|
+
const kitAction = action.split('.').at(1);
|
|
32483
|
+
if (kitAction) {
|
|
32484
|
+
kit.on(kitAction, handler);
|
|
32485
|
+
}
|
|
32486
|
+
}
|
|
32487
|
+
else if (!prefix) {
|
|
32488
|
+
// No prefix configured, register action as-is
|
|
32489
|
+
kit.on(action, handler);
|
|
32490
|
+
}
|
|
32491
|
+
// Actions that don't match the prefix are silently ignored
|
|
32492
|
+
}
|
|
32493
|
+
}
|
|
32494
|
+
};
|
|
32495
|
+
|
|
32496
|
+
/**
|
|
32497
|
+
* List of all supported token aliases for App Kit send operations.
|
|
32498
|
+
*
|
|
32499
|
+
* This array is used for runtime validation to check if a token string
|
|
32500
|
+
* is a known alias rather than a custom address.
|
|
32501
|
+
*
|
|
32502
|
+
* @remarks
|
|
32503
|
+
* The type annotation `readonly TokenAlias[]` ensures this array stays in sync
|
|
32504
|
+
* with the TokenAlias type definition - TypeScript will enforce any changes.
|
|
32505
|
+
*
|
|
32506
|
+
* For swap operations, additional tokens (EURC, DAI, USDE, PYUSD) are supported
|
|
32507
|
+
* via SwapKit's SupportedToken type.
|
|
32508
|
+
*/
|
|
32509
|
+
const TOKEN_ALIASES = ['USDC', 'USDT', 'NATIVE'];
|
|
32510
|
+
/**
|
|
32511
|
+
* Check if a token string is a known alias.
|
|
32512
|
+
*
|
|
32513
|
+
* This function performs case-sensitive matching against the list of known
|
|
32514
|
+
* token aliases. Known aliases take precedence over custom addresses in the
|
|
32515
|
+
* send operation logic.
|
|
32516
|
+
*
|
|
32517
|
+
* @param token - The token identifier to check
|
|
32518
|
+
* @returns True if the token is a known alias, false otherwise
|
|
32519
|
+
*
|
|
32520
|
+
* @example
|
|
32521
|
+
* ```typescript
|
|
32522
|
+
* import { isTokenAlias } from '@circle-fin/app-kit'
|
|
32523
|
+
*
|
|
32524
|
+
* console.log(isTokenAlias('USDC')) // true
|
|
32525
|
+
* console.log(isTokenAlias('NATIVE')) // true
|
|
32526
|
+
* console.log(isTokenAlias('0x6B175474E89094C44Da98b954EedeAC495271d0F')) // false
|
|
32527
|
+
* ```
|
|
32528
|
+
*/
|
|
32529
|
+
function isTokenAlias(token) {
|
|
32530
|
+
return TOKEN_ALIASES.includes(token);
|
|
32531
|
+
}
|
|
32532
|
+
/**
|
|
32533
|
+
* Type guard to check if a string is a valid token address for a chain.
|
|
32534
|
+
*
|
|
32535
|
+
* This function verifies that a token string is:
|
|
32536
|
+
* 1. Not a known alias ('USDC', 'USDT', 'NATIVE')
|
|
32537
|
+
* 2. A valid address format for the specified chain
|
|
32538
|
+
*
|
|
32539
|
+
* Use this to narrow the type to `TokenAddress` in TypeScript.
|
|
32540
|
+
*
|
|
32541
|
+
* @param token - The token string to validate
|
|
32542
|
+
* @param chain - The chain definition for address validation
|
|
32543
|
+
* @returns True if the token is a valid address (not an alias)
|
|
32544
|
+
*
|
|
32545
|
+
* @example
|
|
32546
|
+
* ```typescript
|
|
32547
|
+
* import { isTokenAddress } from '@circle-fin/app-kit'
|
|
32548
|
+
* import { Ethereum } from '@core/chains'
|
|
32549
|
+
*
|
|
32550
|
+
* const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'
|
|
32551
|
+
* if (isTokenAddress(daiAddress, Ethereum)) {
|
|
32552
|
+
* // TypeScript knows daiAddress is TokenAddress here
|
|
32553
|
+
* console.log('Valid token address:', daiAddress)
|
|
32554
|
+
* }
|
|
32555
|
+
*
|
|
32556
|
+
* console.log(isTokenAddress('USDC', Ethereum)) // false - it's an alias
|
|
32557
|
+
* console.log(isTokenAddress('invalid', Ethereum)) // false - invalid format
|
|
32558
|
+
* ```
|
|
32559
|
+
*/
|
|
32560
|
+
function isTokenAddress(token, chain) {
|
|
32561
|
+
return !isTokenAlias(token) && isValidAddressForChain(token, chain);
|
|
32562
|
+
}
|
|
32563
|
+
/**
|
|
32564
|
+
* Validate and classify a token identifier.
|
|
32565
|
+
*
|
|
32566
|
+
* This function determines whether a token string is:
|
|
32567
|
+
* 1. A known alias ('USDC', 'USDT', or 'NATIVE')
|
|
32568
|
+
* 2. A valid token address for the given chain
|
|
32569
|
+
* 3. An invalid/unrecognized token identifier
|
|
32570
|
+
*
|
|
32571
|
+
* Known aliases always take precedence. If the token is not an alias,
|
|
32572
|
+
* it's validated as an address for the given chain.
|
|
32573
|
+
*
|
|
32574
|
+
* @param token - The token identifier to validate
|
|
32575
|
+
* @param chain - The chain definition for address validation
|
|
32576
|
+
* @returns An object containing validation results
|
|
32577
|
+
*
|
|
32578
|
+
* @example
|
|
32579
|
+
* ```typescript
|
|
32580
|
+
* import { validateToken } from '@circle-fin/app-kit'
|
|
32581
|
+
* import { Ethereum } from '@core/chains'
|
|
32582
|
+
*
|
|
32583
|
+
* // Known alias
|
|
32584
|
+
* const usdc = validateToken('USDC', Ethereum)
|
|
32585
|
+
* console.log(usdc) // { isAlias: true, isAddress: false, isValid: true }
|
|
32586
|
+
*
|
|
32587
|
+
* // Custom token address
|
|
32588
|
+
* const dai = validateToken('0x6B175474E89094C44Da98b954EedeAC495271d0F', Ethereum)
|
|
32589
|
+
* console.log(dai) // { isAlias: false, isAddress: true, isValid: true }
|
|
32590
|
+
*
|
|
32591
|
+
* // Invalid token
|
|
32592
|
+
* const invalid = validateToken('invalid', Ethereum)
|
|
32593
|
+
* console.log(invalid) // { isAlias: false, isAddress: false, isValid: false }
|
|
32594
|
+
* ```
|
|
32595
|
+
*/
|
|
32596
|
+
function validateToken(token, chain) {
|
|
32597
|
+
// Check if it's a known alias first (aliases take precedence)
|
|
32598
|
+
const alias = isTokenAlias(token);
|
|
32599
|
+
if (alias) {
|
|
32600
|
+
return {
|
|
32601
|
+
isAlias: true,
|
|
32602
|
+
isAddress: false,
|
|
32603
|
+
isValid: true,
|
|
32604
|
+
};
|
|
32605
|
+
}
|
|
32606
|
+
// Not an alias - check if it's a valid address for the chain
|
|
32607
|
+
const address = isTokenAddress(token, chain);
|
|
32608
|
+
return {
|
|
32609
|
+
isAlias: false,
|
|
32610
|
+
isAddress: address,
|
|
32611
|
+
isValid: address,
|
|
32612
|
+
};
|
|
32613
|
+
}
|
|
32614
|
+
|
|
32615
|
+
/**
|
|
32616
|
+
* Estimate the costs and details of a cross-chain bridge transfer using the AppKit context.
|
|
32617
|
+
*
|
|
32618
|
+
* This function provides cost estimation for bridge operations within the AppKit
|
|
32619
|
+
* ecosystem. It delegates to the underlying BridgeKit infrastructure while maintaining
|
|
32620
|
+
* consistency with the AppKit patterns and context-based architecture.
|
|
32621
|
+
*
|
|
32622
|
+
* The function validates parameters, applies context-specific configurations,
|
|
32623
|
+
* and returns comprehensive estimation details including fees, gas costs, and
|
|
32624
|
+
* transaction parameters without executing the actual transfer.
|
|
32625
|
+
*
|
|
32626
|
+
* @param context - AppKit context containing fee calculation and configuration hooks
|
|
32627
|
+
* @param params - Bridge parameters containing source, destination, amount, and token
|
|
32628
|
+
* @returns Promise resolving to the estimate result with comprehensive fee and cost information
|
|
32629
|
+
* @throws If bridge parameters are invalid
|
|
32630
|
+
* @throws If the bridge route is not supported
|
|
32631
|
+
* @throws If estimation fails due to network or configuration issues
|
|
32632
|
+
*
|
|
32633
|
+
* @example
|
|
32634
|
+
* ```typescript
|
|
32635
|
+
* import { estimateBridge } from '@circle-fin/app-kit/bridge'
|
|
32636
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
32637
|
+
*
|
|
32638
|
+
* const context = createContext({
|
|
32639
|
+
* getFee: async (type, params) => '1000000', // 1 USDC fee
|
|
32640
|
+
* getFeeRecipient: async (type, info) => '0xfee-recipient-address'
|
|
32641
|
+
* })
|
|
32642
|
+
*
|
|
32643
|
+
* const estimate = await estimateBridge(context, {
|
|
29325
32644
|
* from: sourceAdapter,
|
|
29326
32645
|
* to: { adapter: destAdapter, chain: 'Polygon' },
|
|
29327
32646
|
* amount: '100.50',
|
|
@@ -29915,14 +33234,15 @@ const estimateSwap = async (context, params) => {
|
|
|
29915
33234
|
* Get chains supported by AppKit operations.
|
|
29916
33235
|
*
|
|
29917
33236
|
* This helper returns the set of chains that can be used for a given
|
|
29918
|
-
* operation type (bridge, swap, or
|
|
29919
|
-
* it returns all chains known to support any
|
|
33237
|
+
* operation type (bridge, swap, earn, unified balance, or all). When no
|
|
33238
|
+
* operation type is specified, it returns all chains known to support any
|
|
33239
|
+
* AppKit operation.
|
|
29920
33240
|
*
|
|
29921
33241
|
* The result list contains unique chains and is safe to pass directly into
|
|
29922
33242
|
* higher-level flows such as chain pickers or routing logic.
|
|
29923
33243
|
*
|
|
29924
33244
|
* @param context - AppKit context containing fee configuration.
|
|
29925
|
-
* @param operationType - Optional operation type to filter chains ('bridge' | 'swap' | 'unifiedBalance').
|
|
33245
|
+
* @param operationType - Optional operation type to filter chains ('bridge' | 'swap' | 'earn' | 'unifiedBalance').
|
|
29926
33246
|
* @param unifiedBalance - Persistent AppKitUnifiedBalance namespace instance.
|
|
29927
33247
|
* @returns Array of unique chain definitions supporting the specified operation(s)
|
|
29928
33248
|
*
|
|
@@ -29948,6 +33268,12 @@ const estimateSwap = async (context, params) => {
|
|
|
29948
33268
|
* console.log('Chains supporting swap:', swapChains.map(c => c.name))
|
|
29949
33269
|
* ```
|
|
29950
33270
|
*
|
|
33271
|
+
* @example Get earn-specific chains
|
|
33272
|
+
* ```typescript
|
|
33273
|
+
* const earnChains = kit.getSupportedChains('earn')
|
|
33274
|
+
* console.log('Chains supporting earn:', earnChains.map(c => c.name))
|
|
33275
|
+
* ```
|
|
33276
|
+
*
|
|
29951
33277
|
* @example Check if specific chain supports an operation
|
|
29952
33278
|
* ```typescript
|
|
29953
33279
|
* import { Ethereum } from '@core/chains'
|
|
@@ -29962,11 +33288,12 @@ const getSupportedChains$2 = (context, operationType, unifiedBalance) => {
|
|
|
29962
33288
|
if (operationType !== undefined &&
|
|
29963
33289
|
operationType !== 'bridge' &&
|
|
29964
33290
|
operationType !== 'swap' &&
|
|
33291
|
+
operationType !== 'earn' &&
|
|
29965
33292
|
operationType !== 'unifiedBalance') {
|
|
29966
33293
|
throw new KitError({
|
|
29967
33294
|
...InputError.VALIDATION_FAILED,
|
|
29968
33295
|
recoverability: 'FATAL',
|
|
29969
|
-
message: `Invalid operationType: "${String(operationType)}". Expected 'bridge', 'swap', or 'unifiedBalance'.`,
|
|
33296
|
+
message: `Invalid operationType: "${String(operationType)}". Expected 'bridge', 'swap', 'earn', or 'unifiedBalance'.`,
|
|
29970
33297
|
});
|
|
29971
33298
|
}
|
|
29972
33299
|
// Return bridge-specific chains
|
|
@@ -29979,23 +33306,228 @@ const getSupportedChains$2 = (context, operationType, unifiedBalance) => {
|
|
|
29979
33306
|
const swapKit = createSwapKit(context);
|
|
29980
33307
|
return swapKit.getSupportedChains();
|
|
29981
33308
|
}
|
|
33309
|
+
// Return earn-specific chains
|
|
33310
|
+
if (operationType === 'earn') {
|
|
33311
|
+
const earnKit = createEarnKit();
|
|
33312
|
+
return earnKit.getSupportedChains();
|
|
33313
|
+
}
|
|
29982
33314
|
// Return unified balance chains
|
|
29983
33315
|
if (operationType === 'unifiedBalance') {
|
|
29984
33316
|
return unifiedBalance.getSupportedChains();
|
|
29985
33317
|
}
|
|
29986
33318
|
// No operation type specified - return union of all chains
|
|
33319
|
+
// Reuse kit instances here if provider constructors become expensive or stateful.
|
|
29987
33320
|
const bridgeKit = createBridgeKit(context);
|
|
29988
33321
|
const swapKit = createSwapKit(context);
|
|
33322
|
+
const earnKit = createEarnKit();
|
|
29989
33323
|
const bridgeChains = bridgeKit.getSupportedChains();
|
|
29990
33324
|
const swapChains = swapKit.getSupportedChains();
|
|
33325
|
+
const earnChains = earnKit.getSupportedChains();
|
|
29991
33326
|
const ubChains = unifiedBalance.getSupportedChains();
|
|
29992
|
-
|
|
29993
|
-
|
|
29994
|
-
|
|
33327
|
+
// Combine chains from all operations
|
|
33328
|
+
const allChains = [...bridgeChains, ...swapChains, ...earnChains, ...ubChains];
|
|
33329
|
+
// Deduplicate by chain identifier. Later duplicates override earlier ones.
|
|
33330
|
+
return [...new Map(allChains.map((chain) => [chain.chain, chain])).values()];
|
|
29995
33331
|
};
|
|
29996
33332
|
|
|
33333
|
+
/**
|
|
33334
|
+
* Execute an earn deposit operation.
|
|
33335
|
+
*
|
|
33336
|
+
* @param context - AppKit context
|
|
33337
|
+
* @param params - Deposit parameters including source adapter, vault address, and amount
|
|
33338
|
+
* @returns Promise resolving to the earn deposit result
|
|
33339
|
+
* @throws If EarnKit validation fails, no provider is configured, or the deposit transaction fails
|
|
33340
|
+
*
|
|
33341
|
+
* @example
|
|
33342
|
+
* ```typescript
|
|
33343
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33344
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33345
|
+
* import { deposit } from '@circle-fin/app-kit/earn'
|
|
33346
|
+
*
|
|
33347
|
+
* const context = createContext()
|
|
33348
|
+
* const result = await deposit(context, {
|
|
33349
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33350
|
+
* vaultAddress: '0x...',
|
|
33351
|
+
* amount: '100.50',
|
|
33352
|
+
* })
|
|
33353
|
+
* ```
|
|
33354
|
+
*/
|
|
33355
|
+
async function deposit$2(context, params) {
|
|
33356
|
+
return createEarnKit().deposit(params);
|
|
33357
|
+
}
|
|
33358
|
+
/**
|
|
33359
|
+
* Execute an earn withdrawal operation.
|
|
33360
|
+
*
|
|
33361
|
+
* @param context - AppKit context
|
|
33362
|
+
* @param params - Withdrawal parameters including source adapter, vault address, and amount
|
|
33363
|
+
* @returns Promise resolving to the earn withdrawal result
|
|
33364
|
+
* @throws If EarnKit validation fails, no provider is configured, or the withdrawal transaction fails
|
|
33365
|
+
*
|
|
33366
|
+
* @example
|
|
33367
|
+
* ```typescript
|
|
33368
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33369
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33370
|
+
* import { withdraw } from '@circle-fin/app-kit/earn'
|
|
33371
|
+
*
|
|
33372
|
+
* const context = createContext()
|
|
33373
|
+
* const result = await withdraw(context, {
|
|
33374
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33375
|
+
* vaultAddress: '0x...',
|
|
33376
|
+
* amount: '50.00',
|
|
33377
|
+
* })
|
|
33378
|
+
* ```
|
|
33379
|
+
*/
|
|
33380
|
+
async function withdraw(context, params) {
|
|
33381
|
+
return createEarnKit().withdraw(params);
|
|
33382
|
+
}
|
|
33383
|
+
/**
|
|
33384
|
+
* Claim earn rewards.
|
|
33385
|
+
*
|
|
33386
|
+
* @param context - AppKit context
|
|
33387
|
+
* @param params - Claim rewards parameters including source adapter and vault address
|
|
33388
|
+
* @returns Promise resolving to the earn claim rewards result
|
|
33389
|
+
* @throws If EarnKit validation fails, no provider is configured, or the claim transaction fails
|
|
33390
|
+
*
|
|
33391
|
+
* @example
|
|
33392
|
+
* ```typescript
|
|
33393
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33394
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33395
|
+
* import { claimRewards } from '@circle-fin/app-kit/earn'
|
|
33396
|
+
*
|
|
33397
|
+
* const context = createContext()
|
|
33398
|
+
* const result = await claimRewards(context, {
|
|
33399
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33400
|
+
* vaultAddress: '0x...',
|
|
33401
|
+
* })
|
|
33402
|
+
* ```
|
|
33403
|
+
*/
|
|
33404
|
+
async function claimRewards(context, params) {
|
|
33405
|
+
return createEarnKit().claimRewards(params);
|
|
33406
|
+
}
|
|
33407
|
+
/**
|
|
33408
|
+
* Fetch vault information.
|
|
33409
|
+
*
|
|
33410
|
+
* @param context - AppKit context
|
|
33411
|
+
* @param params - Vault query parameters. `vaults` selects the chain and vault address pairs to query
|
|
33412
|
+
* @returns Promise resolving to vault data and per-vault errors
|
|
33413
|
+
* @throws If EarnKit validation fails or no provider is configured
|
|
33414
|
+
*
|
|
33415
|
+
* @example
|
|
33416
|
+
* ```typescript
|
|
33417
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33418
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33419
|
+
* import { getVaults } from '@circle-fin/app-kit/earn'
|
|
33420
|
+
*
|
|
33421
|
+
* const context = createContext()
|
|
33422
|
+
* const result = await getVaults(context, {
|
|
33423
|
+
* vaults: [{ chain: EarnChain.Arc_Testnet, vaultAddress: '0x...' }],
|
|
33424
|
+
* })
|
|
33425
|
+
* ```
|
|
33426
|
+
*/
|
|
33427
|
+
async function getVaults(context, params) {
|
|
33428
|
+
return createEarnKit().getVaults(params);
|
|
33429
|
+
}
|
|
33430
|
+
/**
|
|
33431
|
+
* Fetch a wallet position in a vault.
|
|
33432
|
+
*
|
|
33433
|
+
* @param context - AppKit context
|
|
33434
|
+
* @param params - Position parameters including source adapter and vault address
|
|
33435
|
+
* @returns Promise resolving to wallet position information
|
|
33436
|
+
* @throws If EarnKit validation fails or no provider is configured
|
|
33437
|
+
*
|
|
33438
|
+
* @example
|
|
33439
|
+
* ```typescript
|
|
33440
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33441
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33442
|
+
* import { getPosition } from '@circle-fin/app-kit/earn'
|
|
33443
|
+
*
|
|
33444
|
+
* const context = createContext()
|
|
33445
|
+
* const position = await getPosition(context, {
|
|
33446
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33447
|
+
* vaultAddress: '0x...',
|
|
33448
|
+
* })
|
|
33449
|
+
* ```
|
|
33450
|
+
*/
|
|
33451
|
+
async function getPosition(context, params) {
|
|
33452
|
+
return createEarnKit().getPosition(params);
|
|
33453
|
+
}
|
|
33454
|
+
/**
|
|
33455
|
+
* Fetch a deposit quote.
|
|
33456
|
+
*
|
|
33457
|
+
* @param context - AppKit context
|
|
33458
|
+
* @param params - Deposit quote parameters including source adapter, vault address, and amount
|
|
33459
|
+
* @returns Promise resolving to deposit quote information
|
|
33460
|
+
* @throws If EarnKit validation fails or no provider is configured
|
|
33461
|
+
*
|
|
33462
|
+
* @example
|
|
33463
|
+
* ```typescript
|
|
33464
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33465
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33466
|
+
* import { getDepositQuote } from '@circle-fin/app-kit/earn'
|
|
33467
|
+
*
|
|
33468
|
+
* const context = createContext()
|
|
33469
|
+
* const quote = await getDepositQuote(context, {
|
|
33470
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33471
|
+
* vaultAddress: '0x...',
|
|
33472
|
+
* amount: '100.50',
|
|
33473
|
+
* })
|
|
33474
|
+
* ```
|
|
33475
|
+
*/
|
|
33476
|
+
async function getDepositQuote(context, params) {
|
|
33477
|
+
return createEarnKit().getDepositQuote(params);
|
|
33478
|
+
}
|
|
33479
|
+
/**
|
|
33480
|
+
* Fetch a withdrawal quote.
|
|
33481
|
+
*
|
|
33482
|
+
* @param context - AppKit context
|
|
33483
|
+
* @param params - Withdrawal quote parameters including source adapter, vault address, and amount
|
|
33484
|
+
* @returns Promise resolving to withdrawal quote information
|
|
33485
|
+
* @throws If EarnKit validation fails or no provider is configured
|
|
33486
|
+
*
|
|
33487
|
+
* @example
|
|
33488
|
+
* ```typescript
|
|
33489
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33490
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33491
|
+
* import { getWithdrawalQuote } from '@circle-fin/app-kit/earn'
|
|
33492
|
+
*
|
|
33493
|
+
* const context = createContext()
|
|
33494
|
+
* const quote = await getWithdrawalQuote(context, {
|
|
33495
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33496
|
+
* vaultAddress: '0x...',
|
|
33497
|
+
* amount: '50.00',
|
|
33498
|
+
* })
|
|
33499
|
+
* ```
|
|
33500
|
+
*/
|
|
33501
|
+
async function getWithdrawalQuote(context, params) {
|
|
33502
|
+
return createEarnKit().getWithdrawalQuote(params);
|
|
33503
|
+
}
|
|
33504
|
+
/**
|
|
33505
|
+
* Fetch a claim rewards quote.
|
|
33506
|
+
*
|
|
33507
|
+
* @param context - AppKit context
|
|
33508
|
+
* @param params - Claim rewards quote parameters including source adapter and vault address
|
|
33509
|
+
* @returns Promise resolving to claim rewards quote information
|
|
33510
|
+
* @throws If EarnKit validation fails or no provider is configured
|
|
33511
|
+
*
|
|
33512
|
+
* @example
|
|
33513
|
+
* ```typescript
|
|
33514
|
+
* import { EarnChain } from '@circle-fin/app-kit'
|
|
33515
|
+
* import { createContext } from '@circle-fin/app-kit/context'
|
|
33516
|
+
* import { getClaimRewardsQuote } from '@circle-fin/app-kit/earn'
|
|
33517
|
+
*
|
|
33518
|
+
* const context = createContext()
|
|
33519
|
+
* const quote = await getClaimRewardsQuote(context, {
|
|
33520
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
33521
|
+
* vaultAddress: '0x...',
|
|
33522
|
+
* })
|
|
33523
|
+
* ```
|
|
33524
|
+
*/
|
|
33525
|
+
async function getClaimRewardsQuote(context, params) {
|
|
33526
|
+
return createEarnKit().getClaimRewardsQuote(params);
|
|
33527
|
+
}
|
|
33528
|
+
|
|
29997
33529
|
var name = "@circle-fin/unified-balance-kit";
|
|
29998
|
-
var version = "1.1.
|
|
33530
|
+
var version = "1.1.2";
|
|
29999
33531
|
var pkg = {
|
|
30000
33532
|
name: name,
|
|
30001
33533
|
version: version};
|
|
@@ -33300,13 +36832,20 @@ async function resolveAllocationsAndIntents(params, destChain, recipientAddress,
|
|
|
33300
36832
|
const sourcesArray = rawSources.filter((s) => s != null);
|
|
33301
36833
|
const networkType = destChain.isTestnet ? 'testnet' : 'mainnet';
|
|
33302
36834
|
const balanceResults = await Promise.all(sourcesArray.map(async (source) => {
|
|
33303
|
-
|
|
33304
|
-
|
|
33305
|
-
|
|
33306
|
-
|
|
33307
|
-
|
|
33308
|
-
|
|
33309
|
-
|
|
36835
|
+
// When sourceAccount is set (delegate flow), scope the balance
|
|
36836
|
+
// query to the Gateway depositor — not the signer. Using the
|
|
36837
|
+
// address-only path bypasses adapter address resolution, which
|
|
36838
|
+
// would otherwise return the signer's balance (developer-
|
|
36839
|
+
// controlled) or reject an explicit address (user-controlled).
|
|
36840
|
+
let querySource;
|
|
36841
|
+
if (source.sourceAccount) {
|
|
36842
|
+
querySource = { address: source.sourceAccount };
|
|
36843
|
+
}
|
|
36844
|
+
else {
|
|
36845
|
+
querySource = { adapter: source.adapter };
|
|
36846
|
+
if ('address' in source && source.address) {
|
|
36847
|
+
querySource['address'] = source.address;
|
|
36848
|
+
}
|
|
33310
36849
|
}
|
|
33311
36850
|
return getBalances$1({
|
|
33312
36851
|
token: params.token,
|
|
@@ -36907,7 +40446,7 @@ class AppKitUnifiedBalance {
|
|
|
36907
40446
|
}
|
|
36908
40447
|
|
|
36909
40448
|
/**
|
|
36910
|
-
* A high-level SDK for stablecoin operations, including bridging and
|
|
40449
|
+
* A high-level SDK for stablecoin operations, including bridging, swapping, and earn.
|
|
36911
40450
|
*
|
|
36912
40451
|
* The AppKit provides a unified interface for various stablecoin operations
|
|
36913
40452
|
* with strongly typed parameters and comprehensive fee estimation capabilities.
|
|
@@ -36918,6 +40457,7 @@ class AppKitUnifiedBalance {
|
|
|
36918
40457
|
* - Strongly typed fee estimation with operation-specific parameters
|
|
36919
40458
|
* - Cross-chain USDC bridging through CCTPv2
|
|
36920
40459
|
* - Same-chain token swapping between USDC, USDT, and native tokens
|
|
40460
|
+
* - Earn vault deposits, withdrawals, rewards, and quote queries
|
|
36921
40461
|
* - Token sending operations
|
|
36922
40462
|
* - Comprehensive error handling and validation
|
|
36923
40463
|
* - Full TypeScript support with IntelliSense
|
|
@@ -36950,6 +40490,13 @@ class AppKitUnifiedBalance {
|
|
|
36950
40490
|
* amountIn: '50.00'
|
|
36951
40491
|
* })
|
|
36952
40492
|
*
|
|
40493
|
+
* // Query an earn vault
|
|
40494
|
+
* const quote = await kit.earn.getDepositQuote({
|
|
40495
|
+
* from: { adapter, chain: 'Arc_Testnet' },
|
|
40496
|
+
* vaultAddress: '0x...',
|
|
40497
|
+
* amount: '100.50',
|
|
40498
|
+
* })
|
|
40499
|
+
*
|
|
36953
40500
|
* // Query unified balances across chains
|
|
36954
40501
|
* const balances = await kit.unifiedBalance.getBalances({
|
|
36955
40502
|
* token: 'USDC',
|
|
@@ -36964,6 +40511,25 @@ class AppKitUnifiedBalance {
|
|
|
36964
40511
|
*/
|
|
36965
40512
|
class AppKit {
|
|
36966
40513
|
context;
|
|
40514
|
+
/**
|
|
40515
|
+
* Earn operations exposed under `kit.earn`.
|
|
40516
|
+
*
|
|
40517
|
+
* @see {@link AppKitEarnOperations}
|
|
40518
|
+
*
|
|
40519
|
+
* @example
|
|
40520
|
+
* ```typescript
|
|
40521
|
+
* import { AppKit, EarnChain } from '@circle-fin/app-kit'
|
|
40522
|
+
*
|
|
40523
|
+
* const kit = new AppKit()
|
|
40524
|
+
*
|
|
40525
|
+
* const result = await kit.earn.deposit({
|
|
40526
|
+
* from: { adapter, chain: EarnChain.Arc_Testnet },
|
|
40527
|
+
* vaultAddress: '0x...',
|
|
40528
|
+
* amount: '100.50',
|
|
40529
|
+
* })
|
|
40530
|
+
* ```
|
|
40531
|
+
*/
|
|
40532
|
+
earn;
|
|
36967
40533
|
/**
|
|
36968
40534
|
* Namespace object for unified balance operations (deposit, spend,
|
|
36969
40535
|
* balance queries, delegation, fund removal).
|
|
@@ -37017,6 +40583,16 @@ class AppKit {
|
|
|
37017
40583
|
disableErrorReporting: config.disableErrorReporting,
|
|
37018
40584
|
}),
|
|
37019
40585
|
});
|
|
40586
|
+
this.earn = {
|
|
40587
|
+
deposit: async (params) => deposit$2(this.context, params),
|
|
40588
|
+
withdraw: async (params) => withdraw(this.context, params),
|
|
40589
|
+
claimRewards: async (params) => claimRewards(this.context, params),
|
|
40590
|
+
getVaults: async (params) => getVaults(this.context, params),
|
|
40591
|
+
getPosition: async (params) => getPosition(this.context, params),
|
|
40592
|
+
getDepositQuote: async (params) => getDepositQuote(this.context, params),
|
|
40593
|
+
getWithdrawalQuote: async (params) => getWithdrawalQuote(this.context, params),
|
|
40594
|
+
getClaimRewardsQuote: async (params) => getClaimRewardsQuote(this.context, params),
|
|
40595
|
+
};
|
|
37020
40596
|
}
|
|
37021
40597
|
/**
|
|
37022
40598
|
* Execute a cross-chain USDC bridge transfer.
|
|
@@ -37274,9 +40850,9 @@ class AppKit {
|
|
|
37274
40850
|
*
|
|
37275
40851
|
* Returns blockchain networks that support specific stablecoin operations.
|
|
37276
40852
|
* When no operation type is specified, returns all chains supporting any
|
|
37277
|
-
* operation (bridge, swap, or unified balance).
|
|
40853
|
+
* operation (bridge, swap, earn, or unified balance).
|
|
37278
40854
|
*
|
|
37279
|
-
* @param operationType - Optional operation type to filter chains ('bridge' | 'swap' | 'unifiedBalance')
|
|
40855
|
+
* @param operationType - Optional operation type to filter chains ('bridge' | 'swap' | 'earn' | 'unifiedBalance')
|
|
37280
40856
|
* @returns Array of unique chain definitions supporting the specified operation(s)
|
|
37281
40857
|
*
|
|
37282
40858
|
* @example Get all supported chains
|
|
@@ -37337,5 +40913,5 @@ class AppKit {
|
|
|
37337
40913
|
}
|
|
37338
40914
|
}
|
|
37339
40915
|
|
|
37340
|
-
export { AppKit, BalanceError, Blockchain, BridgeChain, InputError, KitError, NetworkError, OnchainError, RateLimitError, RpcError, ServiceError, SwapChain, TOKEN_ALIASES, TransferSpeed, UnifiedBalanceChain, getErrorCode, getErrorMessage, getTokenDecimals, isBalanceError, isFatalError, isInputError, isKitError, isNetworkError, isOnchainError, isRateLimitError, isRetryableError$1 as isRetryableError, isRpcError, isServiceError, isTokenAddress, isTokenAlias, isUserCancellationError, setExternalPrefix, validateToken };
|
|
40916
|
+
export { AppKit, BalanceError, Blockchain, BridgeChain, EarnChain, EarnError, EarnKit, InputError, KitError, NetworkError, OnchainError, RateLimitError, RpcError, ServiceError, SwapChain, TOKEN_ALIASES, TransferSpeed, UnifiedBalanceChain, claimRewardsParamsSchema, createEarnKitContext, depositParamsSchema$1 as depositParamsSchema, claimRewards$1 as earnClaimRewards, deposit$3 as earnDeposit, getClaimRewardsQuote$1 as earnGetClaimRewardsQuote, getDepositQuote$1 as earnGetDepositQuote, getPosition$1 as earnGetPosition, getSupportedChains$3 as earnGetSupportedChains, getVaults$1 as earnGetVaults, getWithdrawalQuote$1 as earnGetWithdrawalQuote, withdraw$1 as earnWithdraw, getClaimRewardsQuoteParamsSchema, getDepositQuoteParamsSchema, getErrorCode, getErrorMessage, getPositionParamsSchema, getTokenDecimals, getVaultsParamsSchema, getWithdrawalQuoteParamsSchema, isBalanceError, isFatalError, isInputError, isKitError, isNetworkError, isOnchainError, isRateLimitError, isRetryableError$1 as isRetryableError, isRpcError, isServiceError, isTokenAddress, isTokenAlias, isUserCancellationError, setExternalPrefix, validateToken, withdrawParamsSchema };
|
|
37341
40917
|
//# sourceMappingURL=index.mjs.map
|