@owney/sdk 0.2.8 → 0.2.10
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 +159 -116
- package/dist/index.cjs +283 -58
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +283 -58
- package/package.json +6 -2
package/dist/index.cjs
CHANGED
|
@@ -42,17 +42,20 @@ var SupportedAssets = [
|
|
|
42
42
|
{
|
|
43
43
|
address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
|
|
44
44
|
symbol: "USDC",
|
|
45
|
-
chainId: 8453
|
|
45
|
+
chainId: 8453,
|
|
46
|
+
decimals: 6
|
|
46
47
|
},
|
|
47
48
|
{
|
|
48
49
|
address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
|
|
49
50
|
symbol: "USDC",
|
|
50
|
-
chainId: 42161
|
|
51
|
+
chainId: 42161,
|
|
52
|
+
decimals: 6
|
|
51
53
|
},
|
|
52
54
|
{
|
|
53
55
|
address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
|
|
54
56
|
symbol: "USDT",
|
|
55
|
-
chainId: 42161
|
|
57
|
+
chainId: 42161,
|
|
58
|
+
decimals: 6
|
|
56
59
|
}
|
|
57
60
|
];
|
|
58
61
|
|
|
@@ -60,11 +63,13 @@ var SupportedAssets = [
|
|
|
60
63
|
var OwneyError = class extends Error {
|
|
61
64
|
code;
|
|
62
65
|
details;
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
agentId;
|
|
67
|
+
constructor(code, message, details, agentId) {
|
|
68
|
+
super(agentId ? `[agent:${agentId}] ${message}` : message);
|
|
65
69
|
this.name = "OwneyError";
|
|
66
70
|
this.code = code;
|
|
67
71
|
this.details = details;
|
|
72
|
+
this.agentId = agentId;
|
|
68
73
|
}
|
|
69
74
|
};
|
|
70
75
|
var AgentNotFoundError = class extends OwneyError {
|
|
@@ -72,7 +77,8 @@ var AgentNotFoundError = class extends OwneyError {
|
|
|
72
77
|
super(
|
|
73
78
|
"AGENT_NOT_FOUND",
|
|
74
79
|
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
75
|
-
{ agentId, available }
|
|
80
|
+
{ agentId, available },
|
|
81
|
+
agentId
|
|
76
82
|
);
|
|
77
83
|
this.name = "AgentNotFoundError";
|
|
78
84
|
}
|
|
@@ -303,8 +309,13 @@ function mapApyByStrategy(raw) {
|
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
// src/agents/zyfai/zyfai.agent.ts
|
|
312
|
+
var DEFAULT_ZYFAI_RPC_URLS = {
|
|
313
|
+
8453: "https://base-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V",
|
|
314
|
+
42161: "https://arb-mainnet.g.alchemy.com/v2/ZWyVU-9XfS3z8Rn-xkq7V"
|
|
315
|
+
};
|
|
306
316
|
var ZyfaiAgent = class {
|
|
307
317
|
id = "zyfai";
|
|
318
|
+
minDepositAmount = "0";
|
|
308
319
|
supportedChainIds = [8453, 42161];
|
|
309
320
|
supportedAssets = [
|
|
310
321
|
{
|
|
@@ -324,14 +335,19 @@ var ZyfaiAgent = class {
|
|
|
324
335
|
smartWalletCache = /* @__PURE__ */ new Map();
|
|
325
336
|
connectAuthPromise = null;
|
|
326
337
|
constructor(apiKey, rpcUrls) {
|
|
327
|
-
this.sdk = new import_sdk.ZyfaiSDK({
|
|
338
|
+
this.sdk = new import_sdk.ZyfaiSDK({
|
|
339
|
+
apiKey,
|
|
340
|
+
rpcUrls: rpcUrls ?? DEFAULT_ZYFAI_RPC_URLS
|
|
341
|
+
});
|
|
328
342
|
}
|
|
329
343
|
// --- Private helpers ---
|
|
330
344
|
getAddress() {
|
|
331
345
|
if (!this.connectedAddress) {
|
|
332
346
|
throw new OwneyError(
|
|
333
347
|
"NOT_CONNECTED",
|
|
334
|
-
"Wallet not connected. Call sdk.connect() first."
|
|
348
|
+
"Wallet not connected. Call sdk.connect() first.",
|
|
349
|
+
void 0,
|
|
350
|
+
this.id
|
|
335
351
|
);
|
|
336
352
|
}
|
|
337
353
|
return this.connectedAddress;
|
|
@@ -437,13 +453,14 @@ var ZyfaiAgent = class {
|
|
|
437
453
|
}
|
|
438
454
|
}
|
|
439
455
|
// --- IAgent: Fund operations ---
|
|
440
|
-
async withdraw(state, chainId, amount) {
|
|
456
|
+
async withdraw(state, chainId, token, amount) {
|
|
441
457
|
await this.connectFull(state, chainId);
|
|
442
458
|
const validChainId = isValidChainId(chainId);
|
|
443
459
|
const raw = await this.sdk.withdrawFunds(
|
|
444
460
|
this.getAddress(),
|
|
445
461
|
validChainId,
|
|
446
|
-
amount
|
|
462
|
+
amount,
|
|
463
|
+
token
|
|
447
464
|
);
|
|
448
465
|
return mapWithdraw(raw);
|
|
449
466
|
}
|
|
@@ -640,7 +657,7 @@ function mapHistory2(raw) {
|
|
|
640
657
|
rewards: "Earned"
|
|
641
658
|
};
|
|
642
659
|
const filtered = raw.result.items.filter(
|
|
643
|
-
(i) => i.
|
|
660
|
+
(i) => !(i.type === "execution" && (i.status === "failed" || !i.transactions.length))
|
|
644
661
|
);
|
|
645
662
|
return {
|
|
646
663
|
total: filtered.length,
|
|
@@ -701,12 +718,14 @@ function remapApyPerAssetToSymbols(raw) {
|
|
|
701
718
|
}
|
|
702
719
|
|
|
703
720
|
// src/agents/sail/sail.agent.ts
|
|
721
|
+
var import_viem = require("viem");
|
|
704
722
|
var SAIL_API_BASE = "https://app.sail.money";
|
|
705
723
|
var PROJECT_ID = "sail";
|
|
706
724
|
var PAGE_ID = "home";
|
|
707
725
|
var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
|
|
708
726
|
var SailAgent = class {
|
|
709
727
|
id = "sail";
|
|
728
|
+
minDepositAmount = "4800000000";
|
|
710
729
|
supportedChainIds = [8453, 42161];
|
|
711
730
|
supportedAssets = [
|
|
712
731
|
{
|
|
@@ -757,7 +776,8 @@ var SailAgent = class {
|
|
|
757
776
|
throw new OwneyError(
|
|
758
777
|
"API_SAIL_TIMEOUT",
|
|
759
778
|
`Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
|
|
760
|
-
{ timeoutMs, method, path }
|
|
779
|
+
{ timeoutMs, method, path },
|
|
780
|
+
this.id
|
|
761
781
|
);
|
|
762
782
|
}
|
|
763
783
|
throw error;
|
|
@@ -770,7 +790,8 @@ var SailAgent = class {
|
|
|
770
790
|
throw new OwneyError(
|
|
771
791
|
"API_SAIL_ERROR",
|
|
772
792
|
`Sail API error ${res.status}: ${text}`,
|
|
773
|
-
{ statusCode: res.status, method, path, responseBody: text }
|
|
793
|
+
{ statusCode: res.status, method, path, responseBody: text },
|
|
794
|
+
this.id
|
|
774
795
|
);
|
|
775
796
|
}
|
|
776
797
|
return res.json();
|
|
@@ -809,7 +830,9 @@ var SailAgent = class {
|
|
|
809
830
|
if (!state.walletAddress) {
|
|
810
831
|
throw new OwneyError(
|
|
811
832
|
"WALLET_ADDRESS_REQUIRED",
|
|
812
|
-
"Wallet address is required. Call sdk.connect(provider) first."
|
|
833
|
+
"Wallet address is required. Call sdk.connect(provider) first.",
|
|
834
|
+
void 0,
|
|
835
|
+
this.id
|
|
813
836
|
);
|
|
814
837
|
}
|
|
815
838
|
this.connectPromise = (async () => {
|
|
@@ -846,7 +869,9 @@ var SailAgent = class {
|
|
|
846
869
|
if (!this.smartWalletAddress) {
|
|
847
870
|
throw new OwneyError(
|
|
848
871
|
"NOT_CONNECTED",
|
|
849
|
-
"Wallet not connected. Call sdk.connect() first."
|
|
872
|
+
"Wallet not connected. Call sdk.connect() first.",
|
|
873
|
+
void 0,
|
|
874
|
+
this.id
|
|
850
875
|
);
|
|
851
876
|
}
|
|
852
877
|
return {
|
|
@@ -875,7 +900,9 @@ var SailAgent = class {
|
|
|
875
900
|
if (!depositCallback) {
|
|
876
901
|
throw new OwneyError(
|
|
877
902
|
"DEPOSIT_CALLBACK_REQUIRED",
|
|
878
|
-
"depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
|
|
903
|
+
"depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash.",
|
|
904
|
+
void 0,
|
|
905
|
+
this.id
|
|
879
906
|
);
|
|
880
907
|
}
|
|
881
908
|
try {
|
|
@@ -885,7 +912,9 @@ var SailAgent = class {
|
|
|
885
912
|
if (!resolvedTokenAddress) {
|
|
886
913
|
throw new OwneyError(
|
|
887
914
|
"DEPOSIT_NO_PERMITTED_TOKENS",
|
|
888
|
-
"No permitted tokens available for deposit."
|
|
915
|
+
"No permitted tokens available for deposit.",
|
|
916
|
+
void 0,
|
|
917
|
+
this.id
|
|
889
918
|
);
|
|
890
919
|
}
|
|
891
920
|
await this.executePreHooks(resolvedTokenAddress, amount, chainId);
|
|
@@ -893,7 +922,9 @@ var SailAgent = class {
|
|
|
893
922
|
if (!txHash) {
|
|
894
923
|
throw new OwneyError(
|
|
895
924
|
"DEPOSIT_CALLBACK_INVALID",
|
|
896
|
-
"depositCallback must return a transaction hash (txHash) for Sail deposits."
|
|
925
|
+
"depositCallback must return a transaction hash (txHash) for Sail deposits.",
|
|
926
|
+
void 0,
|
|
927
|
+
this.id
|
|
897
928
|
);
|
|
898
929
|
}
|
|
899
930
|
const postResult = await this.executePostHooks(
|
|
@@ -941,14 +972,29 @@ var SailAgent = class {
|
|
|
941
972
|
);
|
|
942
973
|
}
|
|
943
974
|
// --- IAgent: Fund operations ---
|
|
944
|
-
async withdraw(state, chainId, amount) {
|
|
975
|
+
async withdraw(state, chainId, token, amount) {
|
|
945
976
|
await this.connectFull(state, chainId);
|
|
946
977
|
const withdrawInfo = await this.getWithdrawInfo();
|
|
947
|
-
const
|
|
978
|
+
const selectedTokenAddress = SupportedAssets.find(
|
|
979
|
+
(a) => a.symbol === token && a.chainId === chainId
|
|
980
|
+
);
|
|
981
|
+
if (!selectedTokenAddress) {
|
|
982
|
+
throw new OwneyError(
|
|
983
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
984
|
+
"Unsupported asset.",
|
|
985
|
+
void 0,
|
|
986
|
+
this.id
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
const tokenAddress = withdrawInfo.permittedTokens.find(
|
|
990
|
+
(t) => t.address === selectedTokenAddress.address
|
|
991
|
+
)?.address;
|
|
948
992
|
if (!tokenAddress) {
|
|
949
993
|
throw new OwneyError(
|
|
950
994
|
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
951
|
-
"No permitted tokens available for withdrawal."
|
|
995
|
+
"No permitted tokens available for withdrawal.",
|
|
996
|
+
void 0,
|
|
997
|
+
this.id
|
|
952
998
|
);
|
|
953
999
|
}
|
|
954
1000
|
const resolvedAmount = amount ?? withdrawInfo.currentBalance;
|
|
@@ -968,7 +1014,10 @@ var SailAgent = class {
|
|
|
968
1014
|
return mapWithdraw2({
|
|
969
1015
|
txHash: raw.transactionHash,
|
|
970
1016
|
type: amount ? "partial" : "full",
|
|
971
|
-
amount:
|
|
1017
|
+
amount: (0, import_viem.parseUnits)(
|
|
1018
|
+
resolvedAmount,
|
|
1019
|
+
selectedTokenAddress.decimals
|
|
1020
|
+
).toString()
|
|
972
1021
|
});
|
|
973
1022
|
}
|
|
974
1023
|
// --- IAgent: Portfolio reads ---
|
|
@@ -1072,7 +1121,8 @@ var SailAgent = class {
|
|
|
1072
1121
|
throw new OwneyError(
|
|
1073
1122
|
"VALIDATION_INVALID_DAYS",
|
|
1074
1123
|
`Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
|
|
1075
|
-
{ days }
|
|
1124
|
+
{ days },
|
|
1125
|
+
this.id
|
|
1076
1126
|
);
|
|
1077
1127
|
}
|
|
1078
1128
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -1179,7 +1229,65 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1179
1229
|
return json.data;
|
|
1180
1230
|
}
|
|
1181
1231
|
|
|
1232
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
1233
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1234
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
1235
|
+
agent,
|
|
1236
|
+
balance,
|
|
1237
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
1238
|
+
}));
|
|
1239
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
1240
|
+
let remainder = requested - assigned;
|
|
1241
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
1242
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
1243
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1244
|
+
});
|
|
1245
|
+
for (const p of byHeadroom) {
|
|
1246
|
+
if (remainder === 0n)
|
|
1247
|
+
break;
|
|
1248
|
+
const headroom = p.balance - p.planned;
|
|
1249
|
+
if (headroom <= 0n)
|
|
1250
|
+
continue;
|
|
1251
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
1252
|
+
p.planned += take;
|
|
1253
|
+
remainder -= take;
|
|
1254
|
+
}
|
|
1255
|
+
return plans;
|
|
1256
|
+
}
|
|
1257
|
+
function redistributeShare(plans, fromIndex, amount) {
|
|
1258
|
+
const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
|
|
1259
|
+
const totalHeadroom = candidates.reduce(
|
|
1260
|
+
(s, c) => s + (c.balance - c.planned),
|
|
1261
|
+
0n
|
|
1262
|
+
);
|
|
1263
|
+
if (totalHeadroom === 0n)
|
|
1264
|
+
return;
|
|
1265
|
+
let distributed = 0n;
|
|
1266
|
+
for (const c of candidates) {
|
|
1267
|
+
const headroom = c.balance - c.planned;
|
|
1268
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
1269
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
1270
|
+
c.planned += give;
|
|
1271
|
+
distributed += give;
|
|
1272
|
+
}
|
|
1273
|
+
let leftover = amount - distributed;
|
|
1274
|
+
for (const c of candidates) {
|
|
1275
|
+
if (leftover === 0n)
|
|
1276
|
+
break;
|
|
1277
|
+
const headroom = c.balance - c.planned;
|
|
1278
|
+
if (headroom <= 0n)
|
|
1279
|
+
continue;
|
|
1280
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
1281
|
+
c.planned += take;
|
|
1282
|
+
leftover -= take;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
function sumWithdrawnAmount(results) {
|
|
1286
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1182
1289
|
// src/client.ts
|
|
1290
|
+
var import_viem2 = require("viem");
|
|
1183
1291
|
var OwneySDK = class {
|
|
1184
1292
|
agents = /* @__PURE__ */ new Map();
|
|
1185
1293
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1372,7 +1480,7 @@ var OwneySDK = class {
|
|
|
1372
1480
|
}
|
|
1373
1481
|
/**
|
|
1374
1482
|
* Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
|
|
1375
|
-
|
|
1483
|
+
* Validates that the asset is supported and amount meets minimums for the target agent(s).
|
|
1376
1484
|
* @param options - Deposit parameters
|
|
1377
1485
|
* @param options.amount - Amount to deposit in smallest unit (e.g. "100000000" for 100 USDC)
|
|
1378
1486
|
* @param options.asset - Asset symbol to deposit (e.g. "USDC")
|
|
@@ -1384,28 +1492,29 @@ var OwneySDK = class {
|
|
|
1384
1492
|
*/
|
|
1385
1493
|
async deposit(options) {
|
|
1386
1494
|
const { amount, asset, depositCallback, agentId } = options;
|
|
1495
|
+
if (BigInt(amount) === 0n) {
|
|
1496
|
+
return { agentResults: {} };
|
|
1497
|
+
}
|
|
1387
1498
|
const state = this.requireState();
|
|
1388
1499
|
const chainId = this.requireChainId();
|
|
1389
1500
|
if (agentId) {
|
|
1390
1501
|
const agent = this.getAgent(agentId);
|
|
1391
1502
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1503
|
+
this.validateMinDepositAmount(agent, amount);
|
|
1392
1504
|
return agent.deposit(state, chainId, amount, depositCallback);
|
|
1393
1505
|
}
|
|
1394
|
-
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1395
1506
|
const totalAmount = BigInt(amount);
|
|
1396
|
-
|
|
1507
|
+
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1508
|
+
const agentAmounts = this.splitDepositAmount(totalAmount, eligibleAgents);
|
|
1509
|
+
if (agentAmounts.length === 0) {
|
|
1397
1510
|
throw new OwneyError(
|
|
1398
|
-
"
|
|
1399
|
-
`Amount "${amount}"
|
|
1400
|
-
{ amount
|
|
1511
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
1512
|
+
`Amount "${amount}" cannot satisfy minimum deposit requirements for any eligible agent.`,
|
|
1513
|
+
{ amount }
|
|
1401
1514
|
);
|
|
1402
1515
|
}
|
|
1403
|
-
const perAgent = totalAmount / BigInt(eligibleAgents.length);
|
|
1404
|
-
const remainder = totalAmount % BigInt(eligibleAgents.length);
|
|
1405
1516
|
const agentResults = {};
|
|
1406
|
-
for (
|
|
1407
|
-
const agent = eligibleAgents[i];
|
|
1408
|
-
const agentAmount = i === eligibleAgents.length - 1 ? String(perAgent + remainder) : String(perAgent);
|
|
1517
|
+
for (const { agent, amount: agentAmount } of agentAmounts) {
|
|
1409
1518
|
agentResults[agent.id] = await agent.deposit(
|
|
1410
1519
|
state,
|
|
1411
1520
|
chainId,
|
|
@@ -1415,6 +1524,37 @@ var OwneySDK = class {
|
|
|
1415
1524
|
}
|
|
1416
1525
|
return { agentResults };
|
|
1417
1526
|
}
|
|
1527
|
+
splitDepositAmount(totalAmount, agents) {
|
|
1528
|
+
if (agents.length === 0)
|
|
1529
|
+
return [];
|
|
1530
|
+
const perAgent = totalAmount / BigInt(agents.length);
|
|
1531
|
+
const remainder = totalAmount % BigInt(agents.length);
|
|
1532
|
+
const splits = agents.map((agent, i) => ({
|
|
1533
|
+
agent,
|
|
1534
|
+
amount: i === agents.length - 1 ? perAgent + remainder : perAgent
|
|
1535
|
+
}));
|
|
1536
|
+
const valid = splits.filter(
|
|
1537
|
+
(s) => s.amount >= BigInt(s.agent.minDepositAmount)
|
|
1538
|
+
);
|
|
1539
|
+
if (valid.length === agents.length) {
|
|
1540
|
+
return splits.map((s) => ({ agent: s.agent, amount: String(s.amount) }));
|
|
1541
|
+
}
|
|
1542
|
+
return this.splitDepositAmount(totalAmount, valid.map((s) => s.agent));
|
|
1543
|
+
}
|
|
1544
|
+
validateMinDepositAmount(agent, amount) {
|
|
1545
|
+
const minDepositAmount = BigInt(agent.minDepositAmount);
|
|
1546
|
+
const parsedAmount = BigInt(amount);
|
|
1547
|
+
if (parsedAmount < minDepositAmount) {
|
|
1548
|
+
throw new OwneyError(
|
|
1549
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
1550
|
+
`Amount "${amount}" is below the required minimum deposit for agent "${agent.id}".`,
|
|
1551
|
+
{
|
|
1552
|
+
agentId: agent.id,
|
|
1553
|
+
amount
|
|
1554
|
+
}
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1418
1558
|
validateAssetSupport(agent, chainId, asset) {
|
|
1419
1559
|
const chainAssets = agent.supportedAssets.find(
|
|
1420
1560
|
(sa) => sa.chainId === chainId
|
|
@@ -1423,7 +1563,8 @@ var OwneySDK = class {
|
|
|
1423
1563
|
throw new OwneyError(
|
|
1424
1564
|
"ASSET_UNSUPPORTED",
|
|
1425
1565
|
`Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
|
|
1426
|
-
{ agentId: agent.id, asset, chainId }
|
|
1566
|
+
{ agentId: agent.id, asset, chainId },
|
|
1567
|
+
agent.id
|
|
1427
1568
|
);
|
|
1428
1569
|
}
|
|
1429
1570
|
}
|
|
@@ -1457,34 +1598,99 @@ var OwneySDK = class {
|
|
|
1457
1598
|
const { asset, amount, agentId } = options;
|
|
1458
1599
|
const state = this.requireState();
|
|
1459
1600
|
const chainId = this.requireChainId();
|
|
1601
|
+
const token = asset;
|
|
1602
|
+
const assetInfo = SupportedAssets.find(
|
|
1603
|
+
(a) => a.chainId === chainId && a.symbol === token
|
|
1604
|
+
);
|
|
1605
|
+
if (!assetInfo) {
|
|
1606
|
+
throw new OwneyError(
|
|
1607
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
1608
|
+
"Asset selected is not supported"
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1460
1611
|
if (agentId) {
|
|
1461
1612
|
const agent = this.getAgent(agentId);
|
|
1462
1613
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1463
|
-
return agent.withdraw(state, chainId, amount);
|
|
1614
|
+
return agent.withdraw(state, chainId, token, amount);
|
|
1464
1615
|
}
|
|
1465
1616
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1466
1617
|
if (!amount) {
|
|
1467
1618
|
const results2 = {};
|
|
1468
1619
|
for (const agent of eligibleAgents) {
|
|
1469
|
-
results2[agent.id] = await agent.withdraw(state, chainId);
|
|
1620
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
1470
1621
|
}
|
|
1471
1622
|
return { agentResult: results2 };
|
|
1472
1623
|
}
|
|
1624
|
+
const requested = BigInt(amount);
|
|
1625
|
+
const aggregated = await this.getBalances();
|
|
1626
|
+
const balances = eligibleAgents.map((agent) => {
|
|
1627
|
+
const agentBalance = aggregated.agentBalances[agent.id];
|
|
1628
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
1629
|
+
(t) => t.chainId === chainId && t.asset === asset
|
|
1630
|
+
);
|
|
1631
|
+
if (!tokenBalance)
|
|
1632
|
+
return { agent, balance: 0n };
|
|
1633
|
+
const balance = (0, import_viem2.parseUnits)(tokenBalance.amount, assetInfo.decimals);
|
|
1634
|
+
return { agent, balance };
|
|
1635
|
+
});
|
|
1636
|
+
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
1637
|
+
if (totalAvailable < requested) {
|
|
1638
|
+
throw new OwneyError(
|
|
1639
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
1640
|
+
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
1641
|
+
{
|
|
1642
|
+
asset,
|
|
1643
|
+
requested: amount,
|
|
1644
|
+
available: totalAvailable.toString()
|
|
1645
|
+
}
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
const plans = planProportionalShares(balances, requested, totalAvailable);
|
|
1473
1649
|
const results = {};
|
|
1474
|
-
|
|
1475
|
-
for (
|
|
1650
|
+
const agentErrors = {};
|
|
1651
|
+
for (let i = 0; i < plans.length; i++) {
|
|
1652
|
+
const p = plans[i];
|
|
1653
|
+
if (p.planned === 0n)
|
|
1654
|
+
continue;
|
|
1476
1655
|
try {
|
|
1477
|
-
results[agent.id] = await agent.withdraw(
|
|
1478
|
-
|
|
1656
|
+
results[p.agent.id] = await p.agent.withdraw(
|
|
1657
|
+
state,
|
|
1658
|
+
chainId,
|
|
1659
|
+
token,
|
|
1660
|
+
p.agent.id === "sail" ? (0, import_viem2.formatUnits)(p.planned, assetInfo.decimals) : p.planned.toString()
|
|
1661
|
+
);
|
|
1479
1662
|
} catch (err) {
|
|
1480
|
-
|
|
1663
|
+
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
1664
|
+
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
1665
|
+
redistributeShare(plans, i, p.planned);
|
|
1481
1666
|
}
|
|
1482
1667
|
}
|
|
1483
|
-
if (
|
|
1484
|
-
throw
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1668
|
+
if (Object.keys(results).length === 0) {
|
|
1669
|
+
throw new OwneyError(
|
|
1670
|
+
"WITHDRAW_ALL_FAILED",
|
|
1671
|
+
`Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
|
|
1672
|
+
{
|
|
1673
|
+
asset,
|
|
1674
|
+
amount,
|
|
1675
|
+
agentErrors
|
|
1676
|
+
}
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1679
|
+
const withdrawn = sumWithdrawnAmount(results);
|
|
1680
|
+
if (withdrawn < requested) {
|
|
1681
|
+
throw new OwneyError(
|
|
1682
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
1683
|
+
`Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
|
|
1684
|
+
{
|
|
1685
|
+
asset,
|
|
1686
|
+
requested: amount,
|
|
1687
|
+
withdrawn: withdrawn.toString(),
|
|
1688
|
+
partialResults: results,
|
|
1689
|
+
agentErrors
|
|
1690
|
+
}
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
return { agentResult: results };
|
|
1488
1694
|
}
|
|
1489
1695
|
// --- Portfolio reads ---
|
|
1490
1696
|
/**
|
|
@@ -1509,7 +1715,10 @@ var OwneySDK = class {
|
|
|
1509
1715
|
})
|
|
1510
1716
|
);
|
|
1511
1717
|
let successCount = 0;
|
|
1512
|
-
|
|
1718
|
+
const agentErrors = {};
|
|
1719
|
+
for (let i = 0; i < balanceResults.length; i++) {
|
|
1720
|
+
const settledResult = balanceResults[i];
|
|
1721
|
+
const [agentId2] = entries[i];
|
|
1513
1722
|
if (settledResult.status === "fulfilled") {
|
|
1514
1723
|
const [id, b] = settledResult.value;
|
|
1515
1724
|
results[id] = b;
|
|
@@ -1517,12 +1726,14 @@ var OwneySDK = class {
|
|
|
1517
1726
|
successCount += 1;
|
|
1518
1727
|
continue;
|
|
1519
1728
|
}
|
|
1520
|
-
|
|
1729
|
+
const reason = settledResult.reason;
|
|
1730
|
+
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
1521
1731
|
}
|
|
1522
1732
|
if (successCount === 0) {
|
|
1523
1733
|
throw new OwneyError(
|
|
1524
1734
|
"BALANCE_ALL_FAILED",
|
|
1525
|
-
"Failed to fetch balances for all active agents."
|
|
1735
|
+
"Failed to fetch balances for all active agents.",
|
|
1736
|
+
{ agentErrors }
|
|
1526
1737
|
);
|
|
1527
1738
|
}
|
|
1528
1739
|
return {
|
|
@@ -1597,16 +1808,24 @@ var OwneySDK = class {
|
|
|
1597
1808
|
results[id] = apy;
|
|
1598
1809
|
}
|
|
1599
1810
|
const balances = {};
|
|
1600
|
-
let totalBalance = 0;
|
|
1601
1811
|
for (const [id, bal] of balanceResults) {
|
|
1602
1812
|
balances[id] = bal;
|
|
1603
|
-
totalBalance += bal;
|
|
1604
1813
|
}
|
|
1605
1814
|
let totalApy = 0;
|
|
1606
|
-
|
|
1815
|
+
let relevantBalance = 0;
|
|
1816
|
+
for (const id of Object.keys(results)) {
|
|
1817
|
+
const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
|
|
1818
|
+
if (agentApy === 0)
|
|
1819
|
+
continue;
|
|
1820
|
+
relevantBalance += balances[id];
|
|
1821
|
+
}
|
|
1822
|
+
if (relevantBalance > 0) {
|
|
1607
1823
|
for (const id of Object.keys(results)) {
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1824
|
+
const agentApy = Number(results[id].weightedApyAfterFee ?? 0);
|
|
1825
|
+
if (agentApy === 0)
|
|
1826
|
+
continue;
|
|
1827
|
+
const weight = balances[id] / relevantBalance;
|
|
1828
|
+
totalApy += agentApy * weight;
|
|
1610
1829
|
}
|
|
1611
1830
|
}
|
|
1612
1831
|
return {
|
|
@@ -1749,20 +1968,26 @@ var OwneySDK = class {
|
|
|
1749
1968
|
})
|
|
1750
1969
|
);
|
|
1751
1970
|
let successCount = 0;
|
|
1752
|
-
|
|
1971
|
+
const agentErrors = {};
|
|
1972
|
+
for (let i = 0; i < balanceResults.length; i++) {
|
|
1973
|
+
const settledResult = balanceResults[i];
|
|
1974
|
+
const [agentId] = entries[i];
|
|
1753
1975
|
if (settledResult.status === "fulfilled") {
|
|
1754
1976
|
const [id, bal] = settledResult.value;
|
|
1755
1977
|
balances[id] = bal;
|
|
1756
1978
|
totalBalance += bal;
|
|
1757
1979
|
successCount += 1;
|
|
1758
1980
|
} else {
|
|
1759
|
-
|
|
1981
|
+
const reason = settledResult.reason;
|
|
1982
|
+
agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
|
|
1983
|
+
console.error(`getAgentAllocation agent "${agentId}" failed:`, reason);
|
|
1760
1984
|
}
|
|
1761
1985
|
}
|
|
1762
1986
|
if (successCount === 0) {
|
|
1763
1987
|
throw new OwneyError(
|
|
1764
1988
|
"ALLOCATION_ALL_FAILED",
|
|
1765
|
-
"Failed to fetch allocation: all agents failed."
|
|
1989
|
+
"Failed to fetch allocation: all agents failed.",
|
|
1990
|
+
{ agentErrors }
|
|
1766
1991
|
);
|
|
1767
1992
|
}
|
|
1768
1993
|
const allocation = {};
|
package/dist/index.d.cts
CHANGED
|
@@ -24,10 +24,11 @@ interface IAgent {
|
|
|
24
24
|
readonly id: string;
|
|
25
25
|
readonly supportedChainIds: readonly OwneySupportedChainId[];
|
|
26
26
|
readonly supportedAssets: readonly AgentSupportedAssets[];
|
|
27
|
+
readonly minDepositAmount: string;
|
|
27
28
|
disconnect(): Promise<void>;
|
|
28
29
|
activateAgent(state: ConnectionState, chainId: number): Promise<void>;
|
|
29
30
|
deposit(state: ConnectionState, chainId: number, amount: string, depositCallback?: DepositCallback): Promise<OwneyDepositResult>;
|
|
30
|
-
withdraw(state: ConnectionState, chainId: number, amount?: string): Promise<AgentWithdrawResult>;
|
|
31
|
+
withdraw(state: ConnectionState, chainId: number, token: OwneySupportedTokens, amount?: string): Promise<AgentWithdrawResult>;
|
|
31
32
|
getBalances(state: ConnectionState, chainId: number): Promise<AgentBalance>;
|
|
32
33
|
getEarnings(state: ConnectionState, chainId: number): Promise<AgentEarnings>;
|
|
33
34
|
getAccountApy(state: ConnectionState, chainId: number, days: DailyApyDays): Promise<AccountAgentApy>;
|
|
@@ -196,10 +197,6 @@ interface AgentApy {
|
|
|
196
197
|
interface OwneyAgentApy {
|
|
197
198
|
agentApy: Record<AgentId, AgentApy>;
|
|
198
199
|
}
|
|
199
|
-
interface OwneyRouteResponse {
|
|
200
|
-
agent: AgentId;
|
|
201
|
-
chainId: number;
|
|
202
|
-
}
|
|
203
200
|
interface OwneyAgentAllocation {
|
|
204
201
|
agentAllocation: Record<AgentId, number>;
|
|
205
202
|
}
|
|
@@ -252,7 +249,7 @@ declare class OwneySDK {
|
|
|
252
249
|
activateAgent(chainId: number, agentId?: AgentId[]): Promise<void>;
|
|
253
250
|
/**
|
|
254
251
|
* Deposit funds into a specific agent, or split equally across all agents if agentId is omitted.
|
|
255
|
-
|
|
252
|
+
* Validates that the asset is supported and amount meets minimums for the target agent(s).
|
|
256
253
|
* @param options - Deposit parameters
|
|
257
254
|
* @param options.amount - Amount to deposit in smallest unit (e.g. "100000000" for 100 USDC)
|
|
258
255
|
* @param options.asset - Asset symbol to deposit (e.g. "USDC")
|
|
@@ -263,6 +260,8 @@ declare class OwneySDK {
|
|
|
263
260
|
* @returns {OwneyDepositResult} for a single agent, or {OwneyMultiDepositResult} with per-agent results
|
|
264
261
|
*/
|
|
265
262
|
deposit(options: DepositOptions): Promise<OwneyDepositResult | OwneyMultiDepositResult>;
|
|
263
|
+
private splitDepositAmount;
|
|
264
|
+
private validateMinDepositAmount;
|
|
266
265
|
private validateAssetSupport;
|
|
267
266
|
private getEligibleAgents;
|
|
268
267
|
/**
|
|
@@ -342,11 +341,12 @@ declare class OwneySDK {
|
|
|
342
341
|
getAgentAllocation(): Promise<OwneyAgentAllocation>;
|
|
343
342
|
}
|
|
344
343
|
|
|
345
|
-
type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "
|
|
344
|
+
type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
|
|
346
345
|
declare class OwneyError extends Error {
|
|
347
346
|
readonly code: OwneyErrorCode;
|
|
348
347
|
readonly details?: Record<string, unknown>;
|
|
349
|
-
|
|
348
|
+
readonly agentId?: string;
|
|
349
|
+
constructor(code: OwneyErrorCode, message: string, details?: Record<string, unknown>, agentId?: string);
|
|
350
350
|
}
|
|
351
351
|
declare class AgentNotFoundError extends OwneyError {
|
|
352
352
|
constructor(agentId: string, available: string[]);
|
|
@@ -366,4 +366,4 @@ declare class AgentChainIncompatibleError extends OwneyError {
|
|
|
366
366
|
}>, connectedChainId: number);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult,
|
|
369
|
+
export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneySDK, OwneySDKConfig, OwneySupportedChainId, OwneySupportedChains, OwneySupportedTokens, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
|