@owney/sdk 0.7.25-beta.6 → 0.7.25-beta.8
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/README.md +17 -23
- package/dist/index.cjs +356 -153
- package/dist/index.d.cts +166 -147
- package/dist/index.d.ts +166 -147
- package/dist/index.js +356 -153
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1102,21 +1102,32 @@ import {
|
|
|
1102
1102
|
} from "viem";
|
|
1103
1103
|
var PAID_RPC_RETRY_COUNT = 3;
|
|
1104
1104
|
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1105
|
+
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
1106
|
+
const configured = (baseUrl ?? "")?.trim();
|
|
1107
|
+
if (!configured) {
|
|
1108
|
+
throw new Error(
|
|
1109
|
+
"OWNEY_ROUTING_API_BASE_URL is required for RPC proxy requests unless routingApiBaseUrl or all rpcUrls are configured."
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
return configured;
|
|
1113
|
+
}
|
|
1114
|
+
function rpcProxyUrl(chainId, apiKey, baseUrl) {
|
|
1115
|
+
const url = new URL(
|
|
1116
|
+
`${configuredRpcProxyBaseUrl(baseUrl).replace(/\/$/, "")}/api/v1/rpc/${chainId}`
|
|
1117
|
+
);
|
|
1118
|
+
if (apiKey) url.searchParams.set("apiKey", apiKey);
|
|
1119
|
+
return url.toString();
|
|
1120
|
+
}
|
|
1121
|
+
function resolveRpcUrl(rpcUrls, chainId, apiKey = "", baseUrl) {
|
|
1111
1122
|
const url = rpcUrls?.[chainId]?.trim();
|
|
1112
1123
|
if (url) return url;
|
|
1113
|
-
return
|
|
1124
|
+
return rpcProxyUrl(chainId, apiKey, baseUrl);
|
|
1114
1125
|
}
|
|
1115
|
-
function resolveRpcUrls(rpcUrls) {
|
|
1126
|
+
function resolveRpcUrls(rpcUrls, apiKey = "", baseUrl) {
|
|
1116
1127
|
return {
|
|
1117
|
-
1: resolveRpcUrl(rpcUrls, 1),
|
|
1118
|
-
8453: resolveRpcUrl(rpcUrls, 8453),
|
|
1119
|
-
42161: resolveRpcUrl(rpcUrls, 42161)
|
|
1128
|
+
1: resolveRpcUrl(rpcUrls, 1, apiKey, baseUrl),
|
|
1129
|
+
8453: resolveRpcUrl(rpcUrls, 8453, apiKey, baseUrl),
|
|
1130
|
+
42161: resolveRpcUrl(rpcUrls, 42161, apiKey, baseUrl)
|
|
1120
1131
|
};
|
|
1121
1132
|
}
|
|
1122
1133
|
function createPaidRpcClient(chain, rpcUrls) {
|
|
@@ -1142,7 +1153,13 @@ function headerValue(error, name) {
|
|
|
1142
1153
|
seen.add(candidate);
|
|
1143
1154
|
const record = candidate;
|
|
1144
1155
|
const headers = record.headers;
|
|
1145
|
-
|
|
1156
|
+
let found;
|
|
1157
|
+
if (typeof headers?.get === "function") {
|
|
1158
|
+
found = headers.get(name);
|
|
1159
|
+
} else {
|
|
1160
|
+
const headerRecord = headers;
|
|
1161
|
+
found = headerRecord?.[name] ?? headerRecord?.[name.toLowerCase()];
|
|
1162
|
+
}
|
|
1146
1163
|
if (typeof found === "string" && found) {
|
|
1147
1164
|
value = found;
|
|
1148
1165
|
return;
|
|
@@ -2870,10 +2887,11 @@ var YieldseekerApiClient = class {
|
|
|
2870
2887
|
const payload = await response.json().catch(() => null);
|
|
2871
2888
|
if (!response.ok) {
|
|
2872
2889
|
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2890
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
2873
2891
|
throw new YieldseekerApiError(
|
|
2874
2892
|
response.status,
|
|
2875
2893
|
error.code,
|
|
2876
|
-
error.fields
|
|
2894
|
+
retryAfter ? { ...error.fields, headers: { "Retry-After": retryAfter } } : error.fields
|
|
2877
2895
|
);
|
|
2878
2896
|
}
|
|
2879
2897
|
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
@@ -3004,13 +3022,14 @@ function position(value, asset, baseAssetDecimals) {
|
|
|
3004
3022
|
// differ from the underlying asset. Yieldseeker already converts it to
|
|
3005
3023
|
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
3006
3024
|
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
3007
|
-
// share quantity
|
|
3025
|
+
// share quantity separate from the withdrawable underlying asset amount.
|
|
3008
3026
|
amount: decimal(
|
|
3009
3027
|
value.assetsBase,
|
|
3010
3028
|
baseAssetDecimals,
|
|
3011
3029
|
"yield positions"
|
|
3012
3030
|
),
|
|
3013
3031
|
amountRaw: String(value.assetsRaw),
|
|
3032
|
+
withdrawableAmountRaw: String(value.withdrawableAssetsRaw),
|
|
3014
3033
|
apy: percent(option.riskAdjustedApy),
|
|
3015
3034
|
tvl: Number(option.totalDepositsUsd),
|
|
3016
3035
|
liquidity: Number(option.withdrawableDepositsUsd)
|
|
@@ -3339,8 +3358,12 @@ var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
|
3339
3358
|
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
3340
3359
|
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
3341
3360
|
var YIELDSEEKER_YIELD_OPTIONS_CACHE_MS = 6e4;
|
|
3342
|
-
var YIELDSEEKER_AGENT_LIST_CACHE_MS =
|
|
3343
|
-
var YIELDSEEKER_PORTFOLIO_CACHE_MS =
|
|
3361
|
+
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 12e4;
|
|
3362
|
+
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 12e4;
|
|
3363
|
+
var YIELDSEEKER_SETTLEMENT_CACHE_MS = 5e3;
|
|
3364
|
+
var YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS = 15e3;
|
|
3365
|
+
var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
3366
|
+
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
3344
3367
|
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
3345
3368
|
function generateYieldseekerUsername() {
|
|
3346
3369
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
@@ -3410,6 +3433,15 @@ var YieldseekerAgent = class {
|
|
|
3410
3433
|
readCache = /* @__PURE__ */ new Map();
|
|
3411
3434
|
pendingReads = /* @__PURE__ */ new Map();
|
|
3412
3435
|
readGeneration = 0;
|
|
3436
|
+
portfolioVersions = /* @__PURE__ */ new Map();
|
|
3437
|
+
snapshotFailures = /* @__PURE__ */ new Map();
|
|
3438
|
+
standardReadFailures = /* @__PURE__ */ new Map();
|
|
3439
|
+
reconcileUntil = /* @__PURE__ */ new Map();
|
|
3440
|
+
activityRefreshUntil = /* @__PURE__ */ new Map();
|
|
3441
|
+
// null means a confirmed movement has no reliable baseline. Do not guess
|
|
3442
|
+
// settlement; keep slow reconciliation after the fast window expires.
|
|
3443
|
+
snapshotMovements = /* @__PURE__ */ new Map();
|
|
3444
|
+
movementEpochs = /* @__PURE__ */ new Map();
|
|
3413
3445
|
yieldOptions = /* @__PURE__ */ new Map();
|
|
3414
3446
|
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3415
3447
|
constructor(owneyApiKey, options = {}) {
|
|
@@ -3440,6 +3472,13 @@ var YieldseekerAgent = class {
|
|
|
3440
3472
|
this.pendingWalletContexts.clear();
|
|
3441
3473
|
this.readCache.clear();
|
|
3442
3474
|
this.pendingReads.clear();
|
|
3475
|
+
this.portfolioVersions.clear();
|
|
3476
|
+
this.snapshotFailures.clear();
|
|
3477
|
+
this.standardReadFailures.clear();
|
|
3478
|
+
this.reconcileUntil.clear();
|
|
3479
|
+
this.activityRefreshUntil.clear();
|
|
3480
|
+
this.snapshotMovements.clear();
|
|
3481
|
+
this.movementEpochs.clear();
|
|
3443
3482
|
}
|
|
3444
3483
|
async activateAgent(state, chainId, asset) {
|
|
3445
3484
|
this.assertChain(chainId);
|
|
@@ -3459,41 +3498,37 @@ var YieldseekerAgent = class {
|
|
|
3459
3498
|
);
|
|
3460
3499
|
}
|
|
3461
3500
|
const context = await this.ensureAgent(state, chainId, asset);
|
|
3501
|
+
const generation = this.readGeneration;
|
|
3502
|
+
const movement = this.snapshotMovement(state, chainId, context, BigInt(amount));
|
|
3462
3503
|
let txHash;
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
context.wallet.walletAddress,
|
|
3473
|
-
chainId,
|
|
3474
|
-
amount
|
|
3475
|
-
);
|
|
3476
|
-
await this.waitForReceipt(state, chainId, txHash);
|
|
3477
|
-
} else {
|
|
3478
|
-
txHash = await this.submitTransaction(state, chainId, {
|
|
3479
|
-
from: getAddress2(state.walletAddress),
|
|
3480
|
-
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3481
|
-
data: encodeFunctionData({
|
|
3482
|
-
abi: erc20Abi,
|
|
3483
|
-
functionName: "transfer",
|
|
3484
|
-
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3485
|
-
}),
|
|
3486
|
-
value: "0",
|
|
3487
|
-
chainId
|
|
3488
|
-
});
|
|
3489
|
-
}
|
|
3490
|
-
} finally {
|
|
3491
|
-
await this.refreshSnapshotAfterMovement(
|
|
3492
|
-
state,
|
|
3504
|
+
if (depositCallback) {
|
|
3505
|
+
provideDepositVerificationContext(depositCallback, {
|
|
3506
|
+
agentId: "yieldseeker",
|
|
3507
|
+
signature: await this.auth.getToken(state, chainId),
|
|
3508
|
+
userId: context.user.userId,
|
|
3509
|
+
yieldseekerAgentId: context.agent.agentId
|
|
3510
|
+
});
|
|
3511
|
+
txHash = await depositCallback(
|
|
3512
|
+
context.wallet.walletAddress,
|
|
3493
3513
|
chainId,
|
|
3494
|
-
|
|
3495
|
-
"deposit"
|
|
3514
|
+
amount
|
|
3496
3515
|
);
|
|
3516
|
+
await this.waitForReceipt(state, chainId, txHash);
|
|
3517
|
+
} else {
|
|
3518
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
3519
|
+
from: getAddress2(state.walletAddress),
|
|
3520
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3521
|
+
data: encodeFunctionData({
|
|
3522
|
+
abi: erc20Abi,
|
|
3523
|
+
functionName: "transfer",
|
|
3524
|
+
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3525
|
+
}),
|
|
3526
|
+
value: "0",
|
|
3527
|
+
chainId
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
if (this.readGeneration === generation) {
|
|
3531
|
+
await this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
3497
3532
|
}
|
|
3498
3533
|
return {
|
|
3499
3534
|
txHash,
|
|
@@ -3521,6 +3556,9 @@ var YieldseekerAgent = class {
|
|
|
3521
3556
|
this.id
|
|
3522
3557
|
);
|
|
3523
3558
|
}
|
|
3559
|
+
const generation = this.readGeneration;
|
|
3560
|
+
let confirmedMovement = false;
|
|
3561
|
+
let settlement = null;
|
|
3524
3562
|
try {
|
|
3525
3563
|
const portfolio = await this.loadPortfolioContext(
|
|
3526
3564
|
state,
|
|
@@ -3538,6 +3576,7 @@ var YieldseekerAgent = class {
|
|
|
3538
3576
|
);
|
|
3539
3577
|
const totalAvailable = idle + deployed;
|
|
3540
3578
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3579
|
+
const plannedMovement = this.snapshotMovement(state, chainId, context, -requested);
|
|
3541
3580
|
if (requested > totalAvailable) {
|
|
3542
3581
|
throw new OwneyError(
|
|
3543
3582
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -3573,6 +3612,7 @@ var YieldseekerAgent = class {
|
|
|
3573
3612
|
throw this.invalidResponse("position withdrawal");
|
|
3574
3613
|
}
|
|
3575
3614
|
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
3615
|
+
confirmedMovement = true;
|
|
3576
3616
|
remaining -= assetsRaw;
|
|
3577
3617
|
}
|
|
3578
3618
|
if (remaining > 0n) {
|
|
@@ -3597,18 +3637,23 @@ var YieldseekerAgent = class {
|
|
|
3597
3637
|
value: "0",
|
|
3598
3638
|
chainId
|
|
3599
3639
|
});
|
|
3640
|
+
confirmedMovement = true;
|
|
3641
|
+
settlement = plannedMovement;
|
|
3600
3642
|
return {
|
|
3601
3643
|
txHash,
|
|
3602
3644
|
type: amount === void 0 ? "full" : "partial",
|
|
3603
3645
|
amount: requested.toString()
|
|
3604
3646
|
};
|
|
3605
3647
|
} finally {
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3648
|
+
if (confirmedMovement && this.readGeneration === generation) {
|
|
3649
|
+
await this.refreshSnapshotAfterMovement(
|
|
3650
|
+
state,
|
|
3651
|
+
chainId,
|
|
3652
|
+
context,
|
|
3653
|
+
"withdrawal",
|
|
3654
|
+
settlement
|
|
3655
|
+
);
|
|
3656
|
+
}
|
|
3612
3657
|
}
|
|
3613
3658
|
}
|
|
3614
3659
|
async getBalances(state, chainId) {
|
|
@@ -3702,6 +3747,16 @@ var YieldseekerAgent = class {
|
|
|
3702
3747
|
contextKey(state, chainId, asset) {
|
|
3703
3748
|
return `${this.userKey(state, chainId)}:${asset}`;
|
|
3704
3749
|
}
|
|
3750
|
+
assertSession(generation, chainId, asset) {
|
|
3751
|
+
if (this.readGeneration !== generation) {
|
|
3752
|
+
throw new OwneyError(
|
|
3753
|
+
"NOT_CONNECTED",
|
|
3754
|
+
"Wallet session changed during the Yieldseeker request.",
|
|
3755
|
+
{ rpcSource: "agent-api", chainId, ...asset ? { asset } : {} },
|
|
3756
|
+
this.id
|
|
3757
|
+
);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3705
3760
|
cachedRead(key2, ttlMs, read2) {
|
|
3706
3761
|
const cached = this.readCache.get(key2);
|
|
3707
3762
|
if (cached && cached.expiresAt > Date.now()) {
|
|
@@ -3711,7 +3766,7 @@ var YieldseekerAgent = class {
|
|
|
3711
3766
|
if (pending) return pending;
|
|
3712
3767
|
const generation = this.readGeneration;
|
|
3713
3768
|
const request = Promise.resolve().then(read2).then((value) => {
|
|
3714
|
-
if (this.readGeneration === generation) {
|
|
3769
|
+
if (this.readGeneration === generation && this.pendingReads.get(key2) === request) {
|
|
3715
3770
|
this.readCache.set(key2, {
|
|
3716
3771
|
expiresAt: Date.now() + ttlMs,
|
|
3717
3772
|
value
|
|
@@ -3759,6 +3814,7 @@ var YieldseekerAgent = class {
|
|
|
3759
3814
|
chainId,
|
|
3760
3815
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3761
3816
|
).then((walletResponse) => {
|
|
3817
|
+
this.assertSession(generation, chainId, asset);
|
|
3762
3818
|
if (!walletResponse?.agentWallet || !isAddress2(walletResponse.agentWallet.walletAddress)) {
|
|
3763
3819
|
throw this.invalidResponse("agent wallet");
|
|
3764
3820
|
}
|
|
@@ -3768,9 +3824,7 @@ var YieldseekerAgent = class {
|
|
|
3768
3824
|
wallet: walletResponse.agentWallet,
|
|
3769
3825
|
asset
|
|
3770
3826
|
};
|
|
3771
|
-
|
|
3772
|
-
this.agentContexts.set(key2, context);
|
|
3773
|
-
}
|
|
3827
|
+
this.agentContexts.set(key2, context);
|
|
3774
3828
|
return context;
|
|
3775
3829
|
});
|
|
3776
3830
|
this.pendingWalletContexts.set(key2, request);
|
|
@@ -3792,6 +3846,7 @@ var YieldseekerAgent = class {
|
|
|
3792
3846
|
return persisted;
|
|
3793
3847
|
}
|
|
3794
3848
|
const walletAddress = getAddress2(state.walletAddress);
|
|
3849
|
+
const generation = this.readGeneration;
|
|
3795
3850
|
let user = null;
|
|
3796
3851
|
try {
|
|
3797
3852
|
const login = await this.providerRequest(
|
|
@@ -3838,6 +3893,7 @@ var YieldseekerAgent = class {
|
|
|
3838
3893
|
if (!user || !/^[a-zA-Z0-9_-]{1,128}$/.test(user.userId)) {
|
|
3839
3894
|
throw this.invalidResponse("wallet identity");
|
|
3840
3895
|
}
|
|
3896
|
+
this.assertSession(generation, chainId);
|
|
3841
3897
|
const resolved = { userId: user.userId };
|
|
3842
3898
|
this.users.set(key2, resolved);
|
|
3843
3899
|
writeYieldseekerIdentity(state.walletAddress, chainId, resolved.userId);
|
|
@@ -3853,10 +3909,13 @@ var YieldseekerAgent = class {
|
|
|
3853
3909
|
if (cached) return cached;
|
|
3854
3910
|
const pending = this.pendingAgents.get(key2);
|
|
3855
3911
|
if (pending) return pending;
|
|
3912
|
+
const generation = this.readGeneration;
|
|
3856
3913
|
const request = this.resolveAgent(state, chainId, asset, true).then(
|
|
3857
3914
|
async (context) => {
|
|
3915
|
+
this.assertSession(generation, chainId, asset);
|
|
3858
3916
|
if (!context) throw this.invalidResponse("agent creation");
|
|
3859
3917
|
await this.deployAgent(state, chainId, context);
|
|
3918
|
+
this.assertSession(generation, chainId, asset);
|
|
3860
3919
|
this.agentContexts.set(key2, context);
|
|
3861
3920
|
return context;
|
|
3862
3921
|
}
|
|
@@ -3865,20 +3924,25 @@ var YieldseekerAgent = class {
|
|
|
3865
3924
|
try {
|
|
3866
3925
|
return await request;
|
|
3867
3926
|
} finally {
|
|
3868
|
-
this.pendingAgents.delete(key2);
|
|
3927
|
+
if (this.pendingAgents.get(key2) === request) this.pendingAgents.delete(key2);
|
|
3869
3928
|
}
|
|
3870
3929
|
}
|
|
3871
3930
|
async findAgent(state, chainId, asset) {
|
|
3872
3931
|
const key2 = this.contextKey(state, chainId, asset);
|
|
3873
3932
|
const cached = this.agentContexts.get(key2);
|
|
3874
3933
|
if (cached) return cached;
|
|
3934
|
+
const generation = this.readGeneration;
|
|
3875
3935
|
const context = await this.resolveAgent(state, chainId, asset, false);
|
|
3936
|
+
this.assertSession(generation, chainId, asset);
|
|
3876
3937
|
if (context) this.agentContexts.set(key2, context);
|
|
3877
3938
|
return context;
|
|
3878
3939
|
}
|
|
3879
3940
|
async resolveAgent(state, chainId, asset, createIfMissing) {
|
|
3941
|
+
const generation = this.readGeneration;
|
|
3880
3942
|
const user = await this.resolveUser(state, chainId);
|
|
3943
|
+
this.assertSession(generation, chainId, asset);
|
|
3881
3944
|
const agents = await this.listAgents(state, chainId, user);
|
|
3945
|
+
this.assertSession(generation, chainId, asset);
|
|
3882
3946
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3883
3947
|
let agent = agents.find(
|
|
3884
3948
|
(candidate) => this.isOwneyAgent(candidate) && candidate.chainId === chainId && candidate.type === "vault" && candidate.assetAddress.toLowerCase() === metadata.address.toLowerCase()
|
|
@@ -3900,6 +3964,7 @@ var YieldseekerAgent = class {
|
|
|
3900
3964
|
}
|
|
3901
3965
|
}
|
|
3902
3966
|
);
|
|
3967
|
+
this.assertSession(generation, chainId, asset);
|
|
3903
3968
|
agent = created?.agent;
|
|
3904
3969
|
if (agent) {
|
|
3905
3970
|
this.readCache.set(this.agentListKey(state, chainId), {
|
|
@@ -3915,8 +3980,11 @@ var YieldseekerAgent = class {
|
|
|
3915
3980
|
return this.contextForAgent(state, chainId, user, agent, asset);
|
|
3916
3981
|
}
|
|
3917
3982
|
async loadPortfolio(state, chainId, options) {
|
|
3983
|
+
const generation = this.readGeneration;
|
|
3918
3984
|
const user = await this.resolveUser(state, chainId);
|
|
3985
|
+
this.assertSession(generation, chainId);
|
|
3919
3986
|
const agents = await this.listAgents(state, chainId, user);
|
|
3987
|
+
this.assertSession(generation, chainId);
|
|
3920
3988
|
const contexts = [];
|
|
3921
3989
|
for (const agent of agents) {
|
|
3922
3990
|
const asset = this.assetForAgent(agent);
|
|
@@ -3927,81 +3995,187 @@ var YieldseekerAgent = class {
|
|
|
3927
3995
|
contexts.push(this.contextForAgent(state, chainId, user, agent, asset));
|
|
3928
3996
|
}
|
|
3929
3997
|
const resolvedContexts = await Promise.all(contexts);
|
|
3998
|
+
this.assertSession(generation, chainId);
|
|
3930
3999
|
return Promise.all(
|
|
3931
4000
|
resolvedContexts.map(
|
|
3932
4001
|
(context) => this.loadPortfolioContext(state, chainId, context, options)
|
|
3933
4002
|
)
|
|
3934
4003
|
);
|
|
3935
4004
|
}
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
4005
|
+
portfolioVersion(contextKey) {
|
|
4006
|
+
return this.portfolioVersions.get(contextKey) ?? 0;
|
|
4007
|
+
}
|
|
4008
|
+
portfolioCacheMs(contextKey) {
|
|
4009
|
+
return (this.reconcileUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_CACHE_MS : YIELDSEEKER_PORTFOLIO_CACHE_MS;
|
|
4010
|
+
}
|
|
4011
|
+
snapshotMovement(state, chainId, context, delta) {
|
|
4012
|
+
const key2 = this.contextKey(state, chainId, context.asset);
|
|
4013
|
+
const previous = this.snapshotMovements.get(key2);
|
|
4014
|
+
if (this.snapshotMovements.has(key2)) {
|
|
4015
|
+
if (!previous || previous.delta > 0n !== delta > 0n) return null;
|
|
4016
|
+
return {
|
|
4017
|
+
...previous,
|
|
4018
|
+
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4019
|
+
delta: previous.delta + delta,
|
|
4020
|
+
expectedBalance: previous.expectedBalance + delta,
|
|
4021
|
+
expectedNetDeposits: previous.expectedNetDeposits + delta
|
|
4022
|
+
};
|
|
4023
|
+
}
|
|
4024
|
+
const cached = this.readCache.get(`snapshot:${key2}`);
|
|
4025
|
+
if (!cached || cached.expiresAt <= Date.now()) return null;
|
|
4026
|
+
const snapshot = cached.value.agentSnapshot;
|
|
4027
|
+
if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) return null;
|
|
4028
|
+
if (!/^-?\d+$/.test(snapshot.netDepositsBase) || !/^\d+$/.test(snapshot.totalValueBase)) return null;
|
|
4029
|
+
return {
|
|
4030
|
+
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4031
|
+
delta,
|
|
4032
|
+
decimals: snapshot.baseAssetDecimals,
|
|
4033
|
+
expectedBalance: BigInt(snapshot.totalValueBase) + delta,
|
|
4034
|
+
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
advancePortfolioVersion(contextKey) {
|
|
4038
|
+
const version = this.portfolioVersion(contextKey) + 1;
|
|
4039
|
+
this.portfolioVersions.set(contextKey, version);
|
|
4040
|
+
this.snapshotFailures.delete(contextKey);
|
|
4041
|
+
for (const kind of ["snapshot", "positions", "historic", "actions"]) {
|
|
4042
|
+
const key2 = `${kind}:${contextKey}`;
|
|
4043
|
+
this.readCache.delete(key2);
|
|
4044
|
+
this.pendingReads.delete(key2);
|
|
4045
|
+
}
|
|
4046
|
+
return version;
|
|
4047
|
+
}
|
|
4048
|
+
requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, forceRefresh = false) {
|
|
4049
|
+
return this.walletRequest(
|
|
4050
|
+
state,
|
|
4051
|
+
chainId,
|
|
4052
|
+
`${this.agentPath(context, "snapshot")}${query(forceRefresh ? { shouldForceRefresh: true } : { shouldOnlyUseRecentValue: true, shouldAllowStaleOnError: true })}`
|
|
4053
|
+
).then((response) => {
|
|
4054
|
+
if (!response?.agentSnapshot) {
|
|
4055
|
+
throw this.invalidResponse("agent snapshot");
|
|
4056
|
+
}
|
|
4057
|
+
if (this.readGeneration === generation && this.portfolioVersion(contextKey) === version) {
|
|
4058
|
+
this.snapshotFailures.delete(contextKey);
|
|
4059
|
+
const movement = this.snapshotMovements.get(contextKey);
|
|
4060
|
+
const snapshot = response.agentSnapshot;
|
|
4061
|
+
if (movement && snapshot.baseAssetDecimals === movement.decimals && /^-?\d+$/.test(snapshot.netDepositsBase) && /^\d+$/.test(snapshot.totalValueBase)) {
|
|
4062
|
+
const balance = BigInt(snapshot.totalValueBase);
|
|
4063
|
+
const netDeposits = BigInt(snapshot.netDepositsBase);
|
|
4064
|
+
const reconciled = movement.delta > 0n ? balance >= movement.expectedBalance && netDeposits >= movement.expectedNetDeposits : balance <= movement.expectedBalance && netDeposits <= movement.expectedNetDeposits;
|
|
4065
|
+
if (reconciled) {
|
|
4066
|
+
this.snapshotMovements.delete(contextKey);
|
|
4067
|
+
this.reconcileUntil.delete(contextKey);
|
|
3968
4068
|
}
|
|
3969
|
-
return response;
|
|
3970
4069
|
}
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4070
|
+
}
|
|
4071
|
+
return response;
|
|
4072
|
+
}).catch((error) => {
|
|
4073
|
+
if (this.readGeneration === generation && this.portfolioVersion(contextKey) === version) {
|
|
4074
|
+
this.snapshotFailures.set(contextKey, {
|
|
4075
|
+
retryAt: Date.now() + Math.max(YIELDSEEKER_READ_FAILURE_COOLDOWN_MS, rateLimitDelay(error) ?? 0),
|
|
4076
|
+
error
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
throw error;
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
4082
|
+
portfolioSnapshot(state, chainId, context, contextKey) {
|
|
4083
|
+
const key2 = `snapshot:${contextKey}`;
|
|
4084
|
+
const cached = this.readCache.get(key2);
|
|
4085
|
+
if ((!cached || cached.expiresAt <= Date.now()) && !this.pendingReads.has(key2)) {
|
|
4086
|
+
const failure = this.snapshotFailures.get(contextKey);
|
|
4087
|
+
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
4088
|
+
this.advancePortfolioVersion(contextKey);
|
|
4089
|
+
}
|
|
4090
|
+
const version = this.portfolioVersion(contextKey);
|
|
4091
|
+
const generation = this.readGeneration;
|
|
4092
|
+
return {
|
|
4093
|
+
version,
|
|
4094
|
+
read: this.cachedRead(key2, this.portfolioCacheMs(contextKey), () => (
|
|
4095
|
+
// Only event-driven reconciliation forces refreshes. Routine reads
|
|
4096
|
+
// remain non-forced; the cache and shared cooldown still bound calls.
|
|
4097
|
+
this.requestPortfolioSnapshot(
|
|
3985
4098
|
state,
|
|
3986
4099
|
chainId,
|
|
3987
|
-
|
|
4100
|
+
context,
|
|
4101
|
+
contextKey,
|
|
4102
|
+
version,
|
|
4103
|
+
generation,
|
|
4104
|
+
this.snapshotMovements.has(contextKey)
|
|
3988
4105
|
)
|
|
3989
|
-
)
|
|
3990
|
-
]);
|
|
3991
|
-
if (!snapshot?.agentSnapshot) {
|
|
3992
|
-
throw this.invalidResponse("agent snapshot");
|
|
3993
|
-
}
|
|
3994
|
-
if (!Array.isArray(positions?.yieldPositions)) {
|
|
3995
|
-
throw this.invalidResponse("yield positions");
|
|
3996
|
-
}
|
|
3997
|
-
return {
|
|
3998
|
-
...context,
|
|
3999
|
-
snapshot: snapshot.agentSnapshot,
|
|
4000
|
-
positions: positions.yieldPositions,
|
|
4001
|
-
...historic?.position ? { historic: historic.position } : {},
|
|
4002
|
-
...actions?.actions ? { actions: actions.actions } : {}
|
|
4106
|
+
))
|
|
4003
4107
|
};
|
|
4004
4108
|
}
|
|
4109
|
+
async loadPortfolioContext(state, chainId, context, options = {}) {
|
|
4110
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4111
|
+
const generation = this.readGeneration;
|
|
4112
|
+
const assertSession = () => this.assertSession(generation, chainId, context.asset);
|
|
4113
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4114
|
+
const { version, read: read2 } = this.portfolioSnapshot(state, chainId, context, contextKey);
|
|
4115
|
+
try {
|
|
4116
|
+
const snapshot = await read2;
|
|
4117
|
+
assertSession();
|
|
4118
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4119
|
+
const [positions, historic, actions] = await Promise.all([
|
|
4120
|
+
this.cachedRead(
|
|
4121
|
+
`positions:${contextKey}`,
|
|
4122
|
+
this.portfolioCacheMs(contextKey),
|
|
4123
|
+
async () => {
|
|
4124
|
+
const response = await this.walletRequest(
|
|
4125
|
+
state,
|
|
4126
|
+
chainId,
|
|
4127
|
+
this.agentPath(context, "yield-positions")
|
|
4128
|
+
);
|
|
4129
|
+
if (!Array.isArray(response?.yieldPositions)) {
|
|
4130
|
+
throw this.invalidResponse("yield positions");
|
|
4131
|
+
}
|
|
4132
|
+
return response;
|
|
4133
|
+
}
|
|
4134
|
+
),
|
|
4135
|
+
options.historic ? this.cachedRead(
|
|
4136
|
+
`historic:${contextKey}`,
|
|
4137
|
+
(this.activityRefreshUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS : YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4138
|
+
() => this.walletRequest(
|
|
4139
|
+
state,
|
|
4140
|
+
chainId,
|
|
4141
|
+
this.agentPath(context, "wallet/historic-position")
|
|
4142
|
+
)
|
|
4143
|
+
) : Promise.resolve(void 0),
|
|
4144
|
+
options.actions ? this.cachedRead(
|
|
4145
|
+
`actions:${contextKey}`,
|
|
4146
|
+
(this.activityRefreshUntil.get(contextKey) ?? 0) > Date.now() ? YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS : YIELDSEEKER_ACTIVITY_CACHE_MS,
|
|
4147
|
+
() => this.walletRequest(
|
|
4148
|
+
state,
|
|
4149
|
+
chainId,
|
|
4150
|
+
this.agentPath(context, "actions")
|
|
4151
|
+
)
|
|
4152
|
+
) : Promise.resolve(void 0)
|
|
4153
|
+
]);
|
|
4154
|
+
assertSession();
|
|
4155
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4156
|
+
if (!Array.isArray(positions?.yieldPositions)) {
|
|
4157
|
+
throw this.invalidResponse("yield positions");
|
|
4158
|
+
}
|
|
4159
|
+
return {
|
|
4160
|
+
...context,
|
|
4161
|
+
snapshot: snapshot.agentSnapshot,
|
|
4162
|
+
positions: positions.yieldPositions,
|
|
4163
|
+
...historic?.position ? { historic: historic.position } : {},
|
|
4164
|
+
...actions?.actions ? { actions: actions.actions } : {}
|
|
4165
|
+
};
|
|
4166
|
+
} catch (error) {
|
|
4167
|
+
assertSession();
|
|
4168
|
+
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4169
|
+
throw error;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
throw new OwneyError(
|
|
4173
|
+
"AGENT_API_ERROR",
|
|
4174
|
+
"Yieldseeker portfolio changed during the read. Please retry.",
|
|
4175
|
+
{ rpcSource: "agent-api", chainId, asset: context.asset },
|
|
4176
|
+
this.id
|
|
4177
|
+
);
|
|
4178
|
+
}
|
|
4005
4179
|
async deployAgent(state, chainId, context) {
|
|
4006
4180
|
if (context.wallet.initializedDate != null) return;
|
|
4007
4181
|
const walletAddress = context.wallet.walletAddress.toLowerCase();
|
|
@@ -4018,42 +4192,52 @@ var YieldseekerAgent = class {
|
|
|
4018
4192
|
}
|
|
4019
4193
|
context.wallet = deployed.agentWallet;
|
|
4020
4194
|
}
|
|
4021
|
-
async refreshSnapshotAfterMovement(state, chainId, context, movement) {
|
|
4195
|
+
async refreshSnapshotAfterMovement(state, chainId, context, movement, settlement) {
|
|
4022
4196
|
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4197
|
+
this.reconcileUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4198
|
+
this.activityRefreshUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4199
|
+
const epoch = this.movementEpochs.get(contextKey) ?? 0;
|
|
4200
|
+
this.snapshotMovements.set(contextKey, settlement?.epoch === epoch ? settlement : null);
|
|
4201
|
+
this.movementEpochs.set(contextKey, epoch + 1);
|
|
4202
|
+
const version = this.advancePortfolioVersion(contextKey);
|
|
4203
|
+
const generation = this.readGeneration;
|
|
4029
4204
|
try {
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
shouldForceRefresh: true
|
|
4035
|
-
})}`
|
|
4205
|
+
await this.cachedRead(
|
|
4206
|
+
`snapshot:${contextKey}`,
|
|
4207
|
+
YIELDSEEKER_SETTLEMENT_CACHE_MS,
|
|
4208
|
+
() => this.requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, true)
|
|
4036
4209
|
);
|
|
4037
|
-
if (!response?.agentSnapshot) {
|
|
4038
|
-
throw this.invalidResponse("agent snapshot refresh");
|
|
4039
|
-
}
|
|
4040
4210
|
} catch (error) {
|
|
4041
4211
|
console.warn(
|
|
4042
4212
|
`[owney-sdk] Yieldseeker ${movement} snapshot refresh failed:`,
|
|
4043
4213
|
error
|
|
4044
4214
|
);
|
|
4045
|
-
} finally {
|
|
4046
|
-
invalidatePortfolio();
|
|
4047
4215
|
}
|
|
4048
4216
|
}
|
|
4049
4217
|
agentPath(context, suffix) {
|
|
4050
4218
|
return `/users/${context.user.userId}/agents/${context.agent.agentId}/${suffix}`;
|
|
4051
4219
|
}
|
|
4052
4220
|
async walletRequest(state, chainId, path, options = {}) {
|
|
4221
|
+
const read2 = (options.method ?? "GET") === "GET";
|
|
4222
|
+
const userKey = this.userKey(state, chainId);
|
|
4223
|
+
if (read2) {
|
|
4224
|
+
const failure = this.standardReadFailures.get(userKey);
|
|
4225
|
+
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
4226
|
+
if (failure) this.standardReadFailures.delete(userKey);
|
|
4227
|
+
}
|
|
4228
|
+
const generation = this.readGeneration;
|
|
4053
4229
|
try {
|
|
4054
4230
|
return await this.providerRequest(state, chainId, path, options);
|
|
4055
4231
|
} catch (error) {
|
|
4056
|
-
|
|
4232
|
+
const mapped = this.mapApiError(error);
|
|
4233
|
+
const delay = read2 ? rateLimitDelay(mapped) : void 0;
|
|
4234
|
+
if (delay !== void 0 && this.readGeneration === generation) {
|
|
4235
|
+
this.standardReadFailures.set(userKey, {
|
|
4236
|
+
retryAt: Date.now() + Math.max(YIELDSEEKER_READ_FAILURE_COOLDOWN_MS, delay),
|
|
4237
|
+
error: mapped
|
|
4238
|
+
});
|
|
4239
|
+
}
|
|
4240
|
+
throw mapped;
|
|
4057
4241
|
}
|
|
4058
4242
|
}
|
|
4059
4243
|
async providerRequest(state, chainId, path, options = {}) {
|
|
@@ -4350,16 +4534,16 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
4350
4534
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
4351
4535
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4352
4536
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4353
|
-
|
|
4354
|
-
try {
|
|
4355
|
-
balance += BigInt(position2.amountRaw);
|
|
4356
|
-
continue;
|
|
4357
|
-
} catch {
|
|
4358
|
-
}
|
|
4359
|
-
}
|
|
4360
|
-
balance += parseUnits(position2.amount, decimals);
|
|
4537
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : parseUnits(position2.amount, decimals);
|
|
4361
4538
|
}
|
|
4362
4539
|
}
|
|
4540
|
+
const assetTotal = agentBalance?.assetBalances?.find(
|
|
4541
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4542
|
+
);
|
|
4543
|
+
if (assetTotal) {
|
|
4544
|
+
const total = parseUnits(assetTotal.amount, decimals);
|
|
4545
|
+
if (total < balance) balance = total > 0n ? total : 0n;
|
|
4546
|
+
}
|
|
4363
4547
|
return { agent, balance };
|
|
4364
4548
|
});
|
|
4365
4549
|
}
|
|
@@ -5127,8 +5311,12 @@ var OwneySDK = class {
|
|
|
5127
5311
|
constructor(config) {
|
|
5128
5312
|
this.apiKey = config.apiKey;
|
|
5129
5313
|
if (config.debug) setOwneyDebug(true);
|
|
5130
|
-
this.rpcUrls =
|
|
5131
|
-
|
|
5314
|
+
this.rpcUrls = resolveRpcUrls(
|
|
5315
|
+
config.rpcUrls,
|
|
5316
|
+
config.apiKey,
|
|
5317
|
+
config.routingApiBaseUrl
|
|
5318
|
+
);
|
|
5319
|
+
this.zyfaiRpcUrls = config.rpcUrls ? void 0 : config.zyfaiRpcUrls;
|
|
5132
5320
|
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
5133
5321
|
this.yieldseekerSiweOrigin = config.yieldseekerSiweOrigin;
|
|
5134
5322
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
@@ -5403,7 +5591,11 @@ var OwneySDK = class {
|
|
|
5403
5591
|
if (!key2) return null;
|
|
5404
5592
|
return new ZyfaiAgent(
|
|
5405
5593
|
key2,
|
|
5406
|
-
this.
|
|
5594
|
+
this.zyfaiRpcUrls ? resolveRpcUrls(
|
|
5595
|
+
this.zyfaiRpcUrls,
|
|
5596
|
+
this.apiKey,
|
|
5597
|
+
this.routingApiBaseUrl
|
|
5598
|
+
) : this.rpcUrls,
|
|
5407
5599
|
this.referralSource
|
|
5408
5600
|
);
|
|
5409
5601
|
}
|
|
@@ -5980,7 +6172,14 @@ var OwneySDK = class {
|
|
|
5980
6172
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
5981
6173
|
*/
|
|
5982
6174
|
async withdraw(options) {
|
|
5983
|
-
const { asset, amount, agentId } = options;
|
|
6175
|
+
const { asset, amount, agentId, onAgentResult } = options;
|
|
6176
|
+
const notifyAgentResult = (id, result) => {
|
|
6177
|
+
try {
|
|
6178
|
+
onAgentResult?.(id, result);
|
|
6179
|
+
} catch (error) {
|
|
6180
|
+
console.warn("Withdrawal result observer failed:", error);
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
5984
6183
|
const state = this.requireState();
|
|
5985
6184
|
const chainId = this.requireChainId();
|
|
5986
6185
|
const token = asset;
|
|
@@ -5996,12 +6195,14 @@ var OwneySDK = class {
|
|
|
5996
6195
|
if (agentId) {
|
|
5997
6196
|
const agent = this.getAgent(agentId);
|
|
5998
6197
|
this.validateAssetSupport(agent, chainId, asset);
|
|
5999
|
-
|
|
6198
|
+
const result = await withFailureReporting(
|
|
6000
6199
|
this.apiKey,
|
|
6001
6200
|
agent.id,
|
|
6002
6201
|
() => agent.withdraw(state, chainId, token, amount),
|
|
6003
6202
|
this.routingApiBaseUrl
|
|
6004
6203
|
);
|
|
6204
|
+
notifyAgentResult(agent.id, result);
|
|
6205
|
+
return result;
|
|
6005
6206
|
}
|
|
6006
6207
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6007
6208
|
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
@@ -6011,6 +6212,7 @@ var OwneySDK = class {
|
|
|
6011
6212
|
for (const agent of withdrawalAgents) {
|
|
6012
6213
|
try {
|
|
6013
6214
|
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
6215
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
6014
6216
|
} catch (err) {
|
|
6015
6217
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6016
6218
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
@@ -6108,6 +6310,7 @@ var OwneySDK = class {
|
|
|
6108
6310
|
token,
|
|
6109
6311
|
p.planned.toString()
|
|
6110
6312
|
);
|
|
6313
|
+
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
6111
6314
|
} catch (err) {
|
|
6112
6315
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6113
6316
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|