@owney/sdk 0.2.9 → 0.2.11
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 +19 -6
- package/dist/index.cjs +173 -689
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +173 -689
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -12,17 +12,20 @@ var SupportedAssets = [
|
|
|
12
12
|
{
|
|
13
13
|
address: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`,
|
|
14
14
|
symbol: "USDC",
|
|
15
|
-
chainId: 8453
|
|
15
|
+
chainId: 8453,
|
|
16
|
+
decimals: 6
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
19
|
address: `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`,
|
|
19
20
|
symbol: "USDC",
|
|
20
|
-
chainId: 42161
|
|
21
|
+
chainId: 42161,
|
|
22
|
+
decimals: 6
|
|
21
23
|
},
|
|
22
24
|
{
|
|
23
25
|
address: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`,
|
|
24
26
|
symbol: "USDT",
|
|
25
|
-
chainId: 42161
|
|
27
|
+
chainId: 42161,
|
|
28
|
+
decimals: 6
|
|
26
29
|
}
|
|
27
30
|
];
|
|
28
31
|
|
|
@@ -30,11 +33,13 @@ var SupportedAssets = [
|
|
|
30
33
|
var OwneyError = class extends Error {
|
|
31
34
|
code;
|
|
32
35
|
details;
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
agentId;
|
|
37
|
+
constructor(code, message, details, agentId) {
|
|
38
|
+
super(agentId ? `[agent:${agentId}] ${message}` : message);
|
|
35
39
|
this.name = "OwneyError";
|
|
36
40
|
this.code = code;
|
|
37
41
|
this.details = details;
|
|
42
|
+
this.agentId = agentId;
|
|
38
43
|
}
|
|
39
44
|
};
|
|
40
45
|
var AgentNotFoundError = class extends OwneyError {
|
|
@@ -42,7 +47,8 @@ var AgentNotFoundError = class extends OwneyError {
|
|
|
42
47
|
super(
|
|
43
48
|
"AGENT_NOT_FOUND",
|
|
44
49
|
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
45
|
-
{ agentId, available }
|
|
50
|
+
{ agentId, available },
|
|
51
|
+
agentId
|
|
46
52
|
);
|
|
47
53
|
this.name = "AgentNotFoundError";
|
|
48
54
|
}
|
|
@@ -309,7 +315,9 @@ var ZyfaiAgent = class {
|
|
|
309
315
|
if (!this.connectedAddress) {
|
|
310
316
|
throw new OwneyError(
|
|
311
317
|
"NOT_CONNECTED",
|
|
312
|
-
"Wallet not connected. Call sdk.connect() first."
|
|
318
|
+
"Wallet not connected. Call sdk.connect() first.",
|
|
319
|
+
void 0,
|
|
320
|
+
this.id
|
|
313
321
|
);
|
|
314
322
|
}
|
|
315
323
|
return this.connectedAddress;
|
|
@@ -415,13 +423,14 @@ var ZyfaiAgent = class {
|
|
|
415
423
|
}
|
|
416
424
|
}
|
|
417
425
|
// --- IAgent: Fund operations ---
|
|
418
|
-
async withdraw(state, chainId, amount) {
|
|
426
|
+
async withdraw(state, chainId, token, amount) {
|
|
419
427
|
await this.connectFull(state, chainId);
|
|
420
428
|
const validChainId = isValidChainId(chainId);
|
|
421
429
|
const raw = await this.sdk.withdrawFunds(
|
|
422
430
|
this.getAddress(),
|
|
423
431
|
validChainId,
|
|
424
|
-
amount
|
|
432
|
+
amount,
|
|
433
|
+
token
|
|
425
434
|
);
|
|
426
435
|
return mapWithdraw(raw);
|
|
427
436
|
}
|
|
@@ -475,659 +484,6 @@ var ZyfaiAgent = class {
|
|
|
475
484
|
}
|
|
476
485
|
};
|
|
477
486
|
|
|
478
|
-
// src/agents/sail/sail.mapper.ts
|
|
479
|
-
function mapDeposit2(raw) {
|
|
480
|
-
return {
|
|
481
|
-
txHash: raw.txHash,
|
|
482
|
-
smartWallet: raw.smartWallet,
|
|
483
|
-
amount: raw.amount
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
function mapWithdraw2(raw) {
|
|
487
|
-
return {
|
|
488
|
-
txHash: raw.txHash,
|
|
489
|
-
type: raw.type,
|
|
490
|
-
amount: raw.amount
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
function mapPortfolioBalances(raw, smartWallet) {
|
|
494
|
-
let total = 0;
|
|
495
|
-
const tokens = [];
|
|
496
|
-
for (const item of raw.tokens) {
|
|
497
|
-
total += item.balanceFormatted;
|
|
498
|
-
tokens.push({
|
|
499
|
-
chain: SUPPORTED_CHAINS[item.chain],
|
|
500
|
-
chainId: item.chain,
|
|
501
|
-
asset: item.symbol === "USD\u20AE0" ? "USDT" : item.symbol,
|
|
502
|
-
amount: item.balance
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
return {
|
|
506
|
-
smartWallet,
|
|
507
|
-
totalBalance: String(total),
|
|
508
|
-
tokens
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
function mapEarnings2(raw, smartWallet) {
|
|
512
|
-
return {
|
|
513
|
-
smartWallet,
|
|
514
|
-
lifetimeEarnings: raw.result
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
function mapApyHistory2(raw, walletAddress) {
|
|
518
|
-
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
519
|
-
return {
|
|
520
|
-
walletAddress,
|
|
521
|
-
weightedApyAfterFee: raw.result.apy,
|
|
522
|
-
weightedApyAfterFeeDetails: apyPerAsset ? { apyPerAsset } : void 0
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
function mapSailTransaction(tx) {
|
|
526
|
-
switch (tx.type) {
|
|
527
|
-
case "deposit":
|
|
528
|
-
case "redeem":
|
|
529
|
-
return {
|
|
530
|
-
txHashes: tx.tx_hashes,
|
|
531
|
-
chainId: tx.chain_id,
|
|
532
|
-
tokenSymbol: tx.asset,
|
|
533
|
-
amount: String(tx.amount)
|
|
534
|
-
};
|
|
535
|
-
case "swap":
|
|
536
|
-
return {
|
|
537
|
-
txHashes: tx.tx_hashes,
|
|
538
|
-
chainId: tx.chain_id,
|
|
539
|
-
tokenSymbol: tx.from_asset,
|
|
540
|
-
amount: String(tx.amount)
|
|
541
|
-
};
|
|
542
|
-
case "bridge":
|
|
543
|
-
return {
|
|
544
|
-
txHashes: tx.tx_hashes,
|
|
545
|
-
chainId: Number(tx.from_chain_id),
|
|
546
|
-
tokenSymbol: tx.asset,
|
|
547
|
-
amount: String(tx.amount)
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
function buildRebalanceLogs(transactions, failedTransactions) {
|
|
552
|
-
const redeems = transactions.filter(
|
|
553
|
-
(tx) => tx.type === "redeem"
|
|
554
|
-
);
|
|
555
|
-
const deposits = transactions.filter(
|
|
556
|
-
(tx) => tx.type === "deposit"
|
|
557
|
-
);
|
|
558
|
-
const failedRedeems = failedTransactions.filter(
|
|
559
|
-
(tx) => tx.type === "redeem"
|
|
560
|
-
);
|
|
561
|
-
const failedDeposits = failedTransactions.filter(
|
|
562
|
-
(tx) => tx.type === "deposit"
|
|
563
|
-
);
|
|
564
|
-
const logs = [];
|
|
565
|
-
const matchedDeposits = /* @__PURE__ */ new Set();
|
|
566
|
-
const matchedRedeems = /* @__PURE__ */ new Set();
|
|
567
|
-
for (let r = 0; r < redeems.length; r++) {
|
|
568
|
-
const idx = deposits.findIndex(
|
|
569
|
-
(d, i) => !matchedDeposits.has(i) && d.amount === redeems[r].amount
|
|
570
|
-
);
|
|
571
|
-
if (idx !== -1) {
|
|
572
|
-
matchedDeposits.add(idx);
|
|
573
|
-
matchedRedeems.add(r);
|
|
574
|
-
logs.push({
|
|
575
|
-
fromProtocol: redeems[r].protocol_name,
|
|
576
|
-
toProtocol: deposits[idx].protocol_name,
|
|
577
|
-
tokenSymbol: redeems[r].asset,
|
|
578
|
-
amount: String(redeems[r].amount),
|
|
579
|
-
status: "success"
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
for (let i = 0; i < deposits.length; i++) {
|
|
584
|
-
if (matchedDeposits.has(i))
|
|
585
|
-
continue;
|
|
586
|
-
const failedRedeem = failedRedeems.find(
|
|
587
|
-
(fr) => fr.amount === deposits[i].amount
|
|
588
|
-
);
|
|
589
|
-
logs.push({
|
|
590
|
-
fromProtocol: failedRedeem ? failedRedeem.protocol_name : "Wallet",
|
|
591
|
-
toProtocol: deposits[i].protocol_name,
|
|
592
|
-
tokenSymbol: deposits[i].asset,
|
|
593
|
-
amount: String(deposits[i].amount),
|
|
594
|
-
status: failedRedeem ? "failed" : "success"
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
for (let i = 0; i < redeems.length; i++) {
|
|
598
|
-
if (matchedRedeems.has(i))
|
|
599
|
-
continue;
|
|
600
|
-
const failedDeposit = failedDeposits.find(
|
|
601
|
-
(fd) => fd.amount === redeems[i].amount
|
|
602
|
-
);
|
|
603
|
-
logs.push({
|
|
604
|
-
fromProtocol: redeems[i].protocol_name,
|
|
605
|
-
toProtocol: failedDeposit ? failedDeposit.protocol_name : "Wallet",
|
|
606
|
-
tokenSymbol: redeems[i].asset,
|
|
607
|
-
amount: String(redeems[i].amount),
|
|
608
|
-
status: failedDeposit ? "failed" : "success"
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
return logs;
|
|
612
|
-
}
|
|
613
|
-
function mapHistory2(raw) {
|
|
614
|
-
const actionMap = {
|
|
615
|
-
execution: "Rebalance",
|
|
616
|
-
deposit: "Top up",
|
|
617
|
-
withdraw: "Withdraw",
|
|
618
|
-
rewards: "Earned"
|
|
619
|
-
};
|
|
620
|
-
const filtered = raw.result.items.filter(
|
|
621
|
-
(i) => !(i.type === "execution" && (i.status === "failed" || !i.transactions.length))
|
|
622
|
-
);
|
|
623
|
-
return {
|
|
624
|
-
total: filtered.length,
|
|
625
|
-
data: filtered.map((entry) => ({
|
|
626
|
-
agent: "sail",
|
|
627
|
-
action: actionMap[entry.type] ?? entry.type,
|
|
628
|
-
date: entry.time,
|
|
629
|
-
newApy: String(entry.new_apy),
|
|
630
|
-
oldApy: String(entry.previous_apy),
|
|
631
|
-
transactions: (entry.transactions ?? []).map(mapSailTransaction),
|
|
632
|
-
rebalanceLog: buildRebalanceLogs(
|
|
633
|
-
entry.transactions ?? [],
|
|
634
|
-
entry.failed_transactions ?? []
|
|
635
|
-
)
|
|
636
|
-
}))
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
function mapAgentStatus2(raw) {
|
|
640
|
-
return {
|
|
641
|
-
success: raw.success,
|
|
642
|
-
strategy: raw.strategy,
|
|
643
|
-
protocols: raw.protocols
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function mapUserProfile2(raw) {
|
|
647
|
-
return {
|
|
648
|
-
address: raw.result.smart_account,
|
|
649
|
-
smartWallet: raw.result.smart_account,
|
|
650
|
-
chains: [],
|
|
651
|
-
hasActiveSessionKey: false,
|
|
652
|
-
protocols: []
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
function mapApyByStrategy2(raw) {
|
|
656
|
-
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
657
|
-
return {
|
|
658
|
-
averageApy: raw.result.apy,
|
|
659
|
-
detailedApys: apyPerAsset ? { apyPerAsset } : void 0
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
function remapApyPerAssetToSymbols(raw) {
|
|
663
|
-
if (!raw)
|
|
664
|
-
return void 0;
|
|
665
|
-
const remapped = {};
|
|
666
|
-
for (const asset of SupportedAssets) {
|
|
667
|
-
const chainKey = asset.chainId;
|
|
668
|
-
const apyByAddress = raw[String(chainKey)];
|
|
669
|
-
if (!apyByAddress)
|
|
670
|
-
continue;
|
|
671
|
-
const apy = apyByAddress[asset.address] ?? apyByAddress[asset.address.toLowerCase()];
|
|
672
|
-
if (apy === void 0)
|
|
673
|
-
continue;
|
|
674
|
-
const bucket = remapped[chainKey] ?? {};
|
|
675
|
-
bucket[asset.symbol] = apy;
|
|
676
|
-
remapped[chainKey] = bucket;
|
|
677
|
-
}
|
|
678
|
-
return Object.keys(remapped).length > 0 ? remapped : void 0;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
// src/agents/sail/sail.agent.ts
|
|
682
|
-
var SAIL_API_BASE = "https://app.sail.money";
|
|
683
|
-
var PROJECT_ID = "sail";
|
|
684
|
-
var PAGE_ID = "home";
|
|
685
|
-
var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
|
|
686
|
-
var SailAgent = class {
|
|
687
|
-
id = "sail";
|
|
688
|
-
minDepositAmount = "4800000000";
|
|
689
|
-
supportedChainIds = [8453, 42161];
|
|
690
|
-
supportedAssets = [
|
|
691
|
-
{
|
|
692
|
-
chainId: 8453,
|
|
693
|
-
chain: "BASE",
|
|
694
|
-
assets: ["USDC"]
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
chainId: 42161,
|
|
698
|
-
chain: "ARBITRUM",
|
|
699
|
-
assets: ["USDC", "USDT"]
|
|
700
|
-
}
|
|
701
|
-
];
|
|
702
|
-
apiKey;
|
|
703
|
-
authToken = null;
|
|
704
|
-
accountToken = null;
|
|
705
|
-
connectedAddress = null;
|
|
706
|
-
smartWalletAddress = null;
|
|
707
|
-
connectedChainId = null;
|
|
708
|
-
connectPromise = null;
|
|
709
|
-
constructor(apiKey) {
|
|
710
|
-
this.apiKey = apiKey;
|
|
711
|
-
}
|
|
712
|
-
// --- Private helpers ---
|
|
713
|
-
async request(method, path, body, timeoutMs) {
|
|
714
|
-
const headers = {
|
|
715
|
-
"Content-Type": "application/json",
|
|
716
|
-
"X-Business-API-Key": this.apiKey
|
|
717
|
-
};
|
|
718
|
-
if (this.authToken) {
|
|
719
|
-
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
720
|
-
}
|
|
721
|
-
if (this.accountToken) {
|
|
722
|
-
headers["X-Account-Token"] = this.accountToken;
|
|
723
|
-
}
|
|
724
|
-
const controller = new AbortController();
|
|
725
|
-
const timeoutId = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
726
|
-
let res;
|
|
727
|
-
try {
|
|
728
|
-
res = await fetch(`${SAIL_API_BASE}${path}`, {
|
|
729
|
-
method,
|
|
730
|
-
headers,
|
|
731
|
-
body: body ? JSON.stringify(body) : void 0,
|
|
732
|
-
signal: controller.signal
|
|
733
|
-
});
|
|
734
|
-
} catch (error) {
|
|
735
|
-
if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
|
|
736
|
-
throw new OwneyError(
|
|
737
|
-
"API_SAIL_TIMEOUT",
|
|
738
|
-
`Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
|
|
739
|
-
{ timeoutMs, method, path }
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
throw error;
|
|
743
|
-
} finally {
|
|
744
|
-
if (timeoutId)
|
|
745
|
-
clearTimeout(timeoutId);
|
|
746
|
-
}
|
|
747
|
-
if (!res.ok) {
|
|
748
|
-
const text = await res.text().catch(() => "");
|
|
749
|
-
throw new OwneyError(
|
|
750
|
-
"API_SAIL_ERROR",
|
|
751
|
-
`Sail API error ${res.status}: ${text}`,
|
|
752
|
-
{ statusCode: res.status, method, path, responseBody: text }
|
|
753
|
-
);
|
|
754
|
-
}
|
|
755
|
-
return res.json();
|
|
756
|
-
}
|
|
757
|
-
/** Initiate SIWE and sign with the user's wallet. */
|
|
758
|
-
async signSiwe(state, chainId) {
|
|
759
|
-
const initiate = await this.request(
|
|
760
|
-
"POST",
|
|
761
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
|
|
762
|
-
{
|
|
763
|
-
method: "siwe",
|
|
764
|
-
address: state.walletAddress,
|
|
765
|
-
chainId,
|
|
766
|
-
identityMode: "byWallet"
|
|
767
|
-
}
|
|
768
|
-
);
|
|
769
|
-
const signature = await state.provider.request({
|
|
770
|
-
method: "personal_sign",
|
|
771
|
-
params: [initiate.message, state.walletAddress]
|
|
772
|
-
});
|
|
773
|
-
return {
|
|
774
|
-
sessionId: initiate.sessionId,
|
|
775
|
-
message: initiate.message,
|
|
776
|
-
signature
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
/** Full connect: auth handles smart account creation via SIWE flow. */
|
|
780
|
-
async connectFull(state, chainId) {
|
|
781
|
-
if (this.connectedAddress && this.connectedChainId === chainId) {
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
if (this.connectPromise) {
|
|
785
|
-
await this.connectPromise;
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
if (!state.walletAddress) {
|
|
789
|
-
throw new OwneyError(
|
|
790
|
-
"WALLET_ADDRESS_REQUIRED",
|
|
791
|
-
"Wallet address is required. Call sdk.connect(provider) first."
|
|
792
|
-
);
|
|
793
|
-
}
|
|
794
|
-
this.connectPromise = (async () => {
|
|
795
|
-
const { sessionId, signature, message } = await this.signSiwe(
|
|
796
|
-
state,
|
|
797
|
-
chainId
|
|
798
|
-
);
|
|
799
|
-
const complete = await this.request(
|
|
800
|
-
"POST",
|
|
801
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
|
|
802
|
-
{
|
|
803
|
-
method: "custom",
|
|
804
|
-
sessionId,
|
|
805
|
-
signature,
|
|
806
|
-
walletAddress: state.walletAddress,
|
|
807
|
-
identityMode: "byWallet"
|
|
808
|
-
}
|
|
809
|
-
);
|
|
810
|
-
this.authToken = complete.token;
|
|
811
|
-
this.accountToken = complete.accountToken;
|
|
812
|
-
this.connectedAddress = state.walletAddress;
|
|
813
|
-
this.smartWalletAddress = complete.walletAddress;
|
|
814
|
-
this.connectedChainId = chainId;
|
|
815
|
-
})();
|
|
816
|
-
try {
|
|
817
|
-
await this.connectPromise;
|
|
818
|
-
} finally {
|
|
819
|
-
this.connectPromise = null;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
async resolveSmartWallet(state, chainId) {
|
|
823
|
-
await this.connectFull(state, chainId);
|
|
824
|
-
const validChainId = isValidChainId(chainId);
|
|
825
|
-
if (!this.smartWalletAddress) {
|
|
826
|
-
throw new OwneyError(
|
|
827
|
-
"NOT_CONNECTED",
|
|
828
|
-
"Wallet not connected. Call sdk.connect() first."
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
return {
|
|
832
|
-
smartWallet: this.smartWalletAddress,
|
|
833
|
-
chainId: validChainId
|
|
834
|
-
};
|
|
835
|
-
}
|
|
836
|
-
// --- IAgent: Connection lifecycle ---
|
|
837
|
-
async disconnect() {
|
|
838
|
-
this.authToken = null;
|
|
839
|
-
this.accountToken = null;
|
|
840
|
-
this.connectedAddress = null;
|
|
841
|
-
this.smartWalletAddress = null;
|
|
842
|
-
this.connectedChainId = null;
|
|
843
|
-
}
|
|
844
|
-
// --- IAgent: Account lifecycle ---
|
|
845
|
-
async activateAgent(state, chainId) {
|
|
846
|
-
try {
|
|
847
|
-
await this.connectFull(state, chainId);
|
|
848
|
-
} catch (error) {
|
|
849
|
-
await this.disconnect();
|
|
850
|
-
throw error;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
async deposit(state, chainId, amount, depositCallback) {
|
|
854
|
-
if (!depositCallback) {
|
|
855
|
-
throw new OwneyError(
|
|
856
|
-
"DEPOSIT_CALLBACK_REQUIRED",
|
|
857
|
-
"depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
try {
|
|
861
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
862
|
-
const depositInfo = await this.getDepositInfo();
|
|
863
|
-
const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
|
|
864
|
-
if (!resolvedTokenAddress) {
|
|
865
|
-
throw new OwneyError(
|
|
866
|
-
"DEPOSIT_NO_PERMITTED_TOKENS",
|
|
867
|
-
"No permitted tokens available for deposit."
|
|
868
|
-
);
|
|
869
|
-
}
|
|
870
|
-
await this.executePreHooks(resolvedTokenAddress, amount, chainId);
|
|
871
|
-
const txHash = await depositCallback(smartWallet, chainId, amount);
|
|
872
|
-
if (!txHash) {
|
|
873
|
-
throw new OwneyError(
|
|
874
|
-
"DEPOSIT_CALLBACK_INVALID",
|
|
875
|
-
"depositCallback must return a transaction hash (txHash) for Sail deposits."
|
|
876
|
-
);
|
|
877
|
-
}
|
|
878
|
-
const postResult = await this.executePostHooks(
|
|
879
|
-
resolvedTokenAddress,
|
|
880
|
-
amount,
|
|
881
|
-
chainId,
|
|
882
|
-
txHash
|
|
883
|
-
);
|
|
884
|
-
return mapDeposit2({
|
|
885
|
-
txHash: postResult.transactionHash,
|
|
886
|
-
smartWallet,
|
|
887
|
-
amount
|
|
888
|
-
});
|
|
889
|
-
} catch (error) {
|
|
890
|
-
await this.disconnect();
|
|
891
|
-
throw error;
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
// --- Private deposit flow helpers ---
|
|
895
|
-
async getDepositInfo() {
|
|
896
|
-
return this.request(
|
|
897
|
-
"GET",
|
|
898
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/deposit-info`
|
|
899
|
-
);
|
|
900
|
-
}
|
|
901
|
-
async executePreHooks(tokenAddress, amount, chainId) {
|
|
902
|
-
return this.request(
|
|
903
|
-
"POST",
|
|
904
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/deposit/pre-hooks`,
|
|
905
|
-
{ tokenAddress, amount, chainId }
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
|
-
async executePostHooks(tokenAddress, amount, chainId, txHash) {
|
|
909
|
-
return this.request(
|
|
910
|
-
"POST",
|
|
911
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/deposit/post-hooks`,
|
|
912
|
-
{ tokenAddress, amount, chainId, txHash }
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
// --- Private withdraw flow helpers ---
|
|
916
|
-
async getWithdrawInfo() {
|
|
917
|
-
return this.request(
|
|
918
|
-
"GET",
|
|
919
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/withdraw-info`
|
|
920
|
-
);
|
|
921
|
-
}
|
|
922
|
-
// --- IAgent: Fund operations ---
|
|
923
|
-
async withdraw(state, chainId, amount) {
|
|
924
|
-
await this.connectFull(state, chainId);
|
|
925
|
-
const withdrawInfo = await this.getWithdrawInfo();
|
|
926
|
-
const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
|
|
927
|
-
if (!tokenAddress) {
|
|
928
|
-
throw new OwneyError(
|
|
929
|
-
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
930
|
-
"No permitted tokens available for withdrawal."
|
|
931
|
-
);
|
|
932
|
-
}
|
|
933
|
-
const resolvedAmount = amount ?? withdrawInfo.currentBalance;
|
|
934
|
-
const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
|
|
935
|
-
const payload = {
|
|
936
|
-
amount: String(resolvedAmount),
|
|
937
|
-
tokenAddress,
|
|
938
|
-
chainId,
|
|
939
|
-
adminWalletSignature,
|
|
940
|
-
adminWalletPayload
|
|
941
|
-
};
|
|
942
|
-
const raw = await this.request(
|
|
943
|
-
"POST",
|
|
944
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/withdraw`,
|
|
945
|
-
payload
|
|
946
|
-
);
|
|
947
|
-
return mapWithdraw2({
|
|
948
|
-
txHash: raw.transactionHash,
|
|
949
|
-
type: amount ? "partial" : "full",
|
|
950
|
-
amount: resolvedAmount
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
// --- IAgent: Portfolio reads ---
|
|
954
|
-
async getBalances(state, chainId) {
|
|
955
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
956
|
-
try {
|
|
957
|
-
const raw = await this.getPortfolio();
|
|
958
|
-
return mapPortfolioBalances(raw, smartWallet);
|
|
959
|
-
} catch (error) {
|
|
960
|
-
if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
|
|
961
|
-
console.warn(
|
|
962
|
-
"Sail portfolio request timed out; using empty snapshot.",
|
|
963
|
-
{
|
|
964
|
-
chainId
|
|
965
|
-
}
|
|
966
|
-
);
|
|
967
|
-
return mapPortfolioBalances(
|
|
968
|
-
{ message: "timeout", tokens: [] },
|
|
969
|
-
smartWallet
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
throw error;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
async getEarnings(state, chainId) {
|
|
976
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
977
|
-
const raw = await this.getMetricsEarnings(state, chainId);
|
|
978
|
-
return mapEarnings2(raw, smartWallet);
|
|
979
|
-
}
|
|
980
|
-
async getAccountApy(state, chainId, days) {
|
|
981
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
982
|
-
const raw = await this.getMetricsYield(days, state, chainId);
|
|
983
|
-
return mapApyHistory2(raw, smartWallet);
|
|
984
|
-
}
|
|
985
|
-
async getHistory(state, chainId, options) {
|
|
986
|
-
const raw = await this.getMetricsHistory(state, chainId, options);
|
|
987
|
-
return mapHistory2(raw);
|
|
988
|
-
}
|
|
989
|
-
// -- Sail-specific: Metrics ---
|
|
990
|
-
async getMetricsPortfolio(state, chainId) {
|
|
991
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
992
|
-
return this.request(
|
|
993
|
-
"GET",
|
|
994
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`,
|
|
995
|
-
void 0,
|
|
996
|
-
SAIL_PORTFOLIO_TIMEOUT_MS
|
|
997
|
-
);
|
|
998
|
-
}
|
|
999
|
-
async getPortfolio() {
|
|
1000
|
-
return this.request(
|
|
1001
|
-
"GET",
|
|
1002
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/portfolio/tokens`,
|
|
1003
|
-
void 0,
|
|
1004
|
-
SAIL_PORTFOLIO_TIMEOUT_MS
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
async getMetricsEarnings(state, chainId) {
|
|
1008
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1009
|
-
return this.request(
|
|
1010
|
-
"GET",
|
|
1011
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/earnings?smart_account=${smartWallet}`
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
async getMetricsBalance(state, chainId, currency = "usd") {
|
|
1015
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1016
|
-
return this.request(
|
|
1017
|
-
"GET",
|
|
1018
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/balance?smart_account=${smartWallet}¤cy=${currency}&limit=1000`
|
|
1019
|
-
);
|
|
1020
|
-
}
|
|
1021
|
-
async getMetricsHistory(state, chainId, filters) {
|
|
1022
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1023
|
-
const params = new URLSearchParams();
|
|
1024
|
-
params.set("smart_account", smartWallet);
|
|
1025
|
-
if (filters?.fromDate) {
|
|
1026
|
-
params.set("start_date", filters.fromDate);
|
|
1027
|
-
}
|
|
1028
|
-
if (filters?.toDate) {
|
|
1029
|
-
params.set("end_date", filters.toDate);
|
|
1030
|
-
}
|
|
1031
|
-
return this.request(
|
|
1032
|
-
"GET",
|
|
1033
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/history?${params.toString()}`
|
|
1034
|
-
);
|
|
1035
|
-
}
|
|
1036
|
-
async getMetricsUser(state, chainId, currency) {
|
|
1037
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1038
|
-
return this.request(
|
|
1039
|
-
"GET",
|
|
1040
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/user-metrics?smart_account=${smartWallet}¤cy=${currency}`
|
|
1041
|
-
);
|
|
1042
|
-
}
|
|
1043
|
-
async getMetricsYield(days, state, chainId) {
|
|
1044
|
-
let walletAddress = "";
|
|
1045
|
-
if (state && chainId) {
|
|
1046
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1047
|
-
walletAddress = smartWallet;
|
|
1048
|
-
}
|
|
1049
|
-
const dayCount = DayFilterMapping[days];
|
|
1050
|
-
if (dayCount === void 0) {
|
|
1051
|
-
throw new OwneyError(
|
|
1052
|
-
"VALIDATION_INVALID_DAYS",
|
|
1053
|
-
`Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
|
|
1054
|
-
{ days }
|
|
1055
|
-
);
|
|
1056
|
-
}
|
|
1057
|
-
const now = /* @__PURE__ */ new Date();
|
|
1058
|
-
const end = now.toISOString().split("T")[0];
|
|
1059
|
-
const start = new Date(now.getTime() - dayCount * 864e5).toISOString().split("T")[0];
|
|
1060
|
-
return this.request(
|
|
1061
|
-
"GET",
|
|
1062
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/yield?start=${start}&end=${end}${walletAddress ? `&smart_account=${walletAddress}` : ""}`
|
|
1063
|
-
);
|
|
1064
|
-
}
|
|
1065
|
-
// --- Sail-specific: Automation jobs ---
|
|
1066
|
-
async getAutomationStatus(state, chainId) {
|
|
1067
|
-
await this.connectFull(state, chainId);
|
|
1068
|
-
return this.request(
|
|
1069
|
-
"GET",
|
|
1070
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/status`
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
async startAutomation(state, chainId, graphId, prompt, options) {
|
|
1074
|
-
await this.connectFull(state, chainId);
|
|
1075
|
-
return this.request(
|
|
1076
|
-
"POST",
|
|
1077
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/start`,
|
|
1078
|
-
{
|
|
1079
|
-
graphId,
|
|
1080
|
-
prompt,
|
|
1081
|
-
paramValues: options?.paramValues ?? {},
|
|
1082
|
-
iterations: options?.iterations ?? 1,
|
|
1083
|
-
branchId: options?.branchId
|
|
1084
|
-
}
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
async pauseAutomation(state, chainId) {
|
|
1088
|
-
await this.connectFull(state, chainId);
|
|
1089
|
-
return this.request(
|
|
1090
|
-
"POST",
|
|
1091
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/pause`
|
|
1092
|
-
);
|
|
1093
|
-
}
|
|
1094
|
-
async resumeAutomation(state, chainId) {
|
|
1095
|
-
await this.connectFull(state, chainId);
|
|
1096
|
-
return this.request(
|
|
1097
|
-
"POST",
|
|
1098
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/resume`
|
|
1099
|
-
);
|
|
1100
|
-
}
|
|
1101
|
-
async stopAutomation(state, chainId) {
|
|
1102
|
-
await this.connectFull(state, chainId);
|
|
1103
|
-
return this.request(
|
|
1104
|
-
"POST",
|
|
1105
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/stop`
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
// --- IAgent: Agent control ---
|
|
1109
|
-
async pause(state, chainId) {
|
|
1110
|
-
await this.connectFull(state, chainId);
|
|
1111
|
-
await this.pauseAutomation(state, chainId);
|
|
1112
|
-
return mapAgentStatus2({ success: true });
|
|
1113
|
-
}
|
|
1114
|
-
async resume(state, chainId) {
|
|
1115
|
-
await this.connectFull(state, chainId);
|
|
1116
|
-
await this.resumeAutomation(state, chainId);
|
|
1117
|
-
return mapAgentStatus2({ success: true });
|
|
1118
|
-
}
|
|
1119
|
-
async getUserProfile(state, chainId) {
|
|
1120
|
-
await this.connectFull(state, chainId);
|
|
1121
|
-
const raw = await this.getMetricsUser(state, chainId, "usd");
|
|
1122
|
-
return mapUserProfile2(raw);
|
|
1123
|
-
}
|
|
1124
|
-
// --- IAgent: Discovery (no wallet required) ---
|
|
1125
|
-
async getAgentApy(days, _options) {
|
|
1126
|
-
const raw = await this.getMetricsYield(days);
|
|
1127
|
-
return mapApyByStrategy2(raw);
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
|
|
1131
487
|
// src/lib/routing-api.ts
|
|
1132
488
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1133
489
|
async function fetchAgentKeys(apiKey) {
|
|
@@ -1158,7 +514,65 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1158
514
|
return json.data;
|
|
1159
515
|
}
|
|
1160
516
|
|
|
517
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
518
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
519
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
520
|
+
agent,
|
|
521
|
+
balance,
|
|
522
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
523
|
+
}));
|
|
524
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
525
|
+
let remainder = requested - assigned;
|
|
526
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
527
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
528
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
529
|
+
});
|
|
530
|
+
for (const p of byHeadroom) {
|
|
531
|
+
if (remainder === 0n)
|
|
532
|
+
break;
|
|
533
|
+
const headroom = p.balance - p.planned;
|
|
534
|
+
if (headroom <= 0n)
|
|
535
|
+
continue;
|
|
536
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
537
|
+
p.planned += take;
|
|
538
|
+
remainder -= take;
|
|
539
|
+
}
|
|
540
|
+
return plans;
|
|
541
|
+
}
|
|
542
|
+
function redistributeShare(plans, fromIndex, amount) {
|
|
543
|
+
const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
|
|
544
|
+
const totalHeadroom = candidates.reduce(
|
|
545
|
+
(s, c) => s + (c.balance - c.planned),
|
|
546
|
+
0n
|
|
547
|
+
);
|
|
548
|
+
if (totalHeadroom === 0n)
|
|
549
|
+
return;
|
|
550
|
+
let distributed = 0n;
|
|
551
|
+
for (const c of candidates) {
|
|
552
|
+
const headroom = c.balance - c.planned;
|
|
553
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
554
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
555
|
+
c.planned += give;
|
|
556
|
+
distributed += give;
|
|
557
|
+
}
|
|
558
|
+
let leftover = amount - distributed;
|
|
559
|
+
for (const c of candidates) {
|
|
560
|
+
if (leftover === 0n)
|
|
561
|
+
break;
|
|
562
|
+
const headroom = c.balance - c.planned;
|
|
563
|
+
if (headroom <= 0n)
|
|
564
|
+
continue;
|
|
565
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
566
|
+
c.planned += take;
|
|
567
|
+
leftover -= take;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function sumWithdrawnAmount(results) {
|
|
571
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
572
|
+
}
|
|
573
|
+
|
|
1161
574
|
// src/client.ts
|
|
575
|
+
import { parseUnits } from "viem";
|
|
1162
576
|
var OwneySDK = class {
|
|
1163
577
|
agents = /* @__PURE__ */ new Map();
|
|
1164
578
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1275,9 +689,6 @@ var OwneySDK = class {
|
|
|
1275
689
|
if (agentId === "zyfai") {
|
|
1276
690
|
return new ZyfaiAgent(key, this.zyfaiRpcUrls);
|
|
1277
691
|
}
|
|
1278
|
-
if (agentId === "sail") {
|
|
1279
|
-
return new SailAgent(key);
|
|
1280
|
-
}
|
|
1281
692
|
return null;
|
|
1282
693
|
}
|
|
1283
694
|
// --- Account lifecycle ---
|
|
@@ -1434,7 +845,8 @@ var OwneySDK = class {
|
|
|
1434
845
|
throw new OwneyError(
|
|
1435
846
|
"ASSET_UNSUPPORTED",
|
|
1436
847
|
`Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
|
|
1437
|
-
{ agentId: agent.id, asset, chainId }
|
|
848
|
+
{ agentId: agent.id, asset, chainId },
|
|
849
|
+
agent.id
|
|
1438
850
|
);
|
|
1439
851
|
}
|
|
1440
852
|
}
|
|
@@ -1468,34 +880,99 @@ var OwneySDK = class {
|
|
|
1468
880
|
const { asset, amount, agentId } = options;
|
|
1469
881
|
const state = this.requireState();
|
|
1470
882
|
const chainId = this.requireChainId();
|
|
883
|
+
const token = asset;
|
|
884
|
+
const assetInfo = SupportedAssets.find(
|
|
885
|
+
(a) => a.chainId === chainId && a.symbol === token
|
|
886
|
+
);
|
|
887
|
+
if (!assetInfo) {
|
|
888
|
+
throw new OwneyError(
|
|
889
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
890
|
+
"Asset selected is not supported"
|
|
891
|
+
);
|
|
892
|
+
}
|
|
1471
893
|
if (agentId) {
|
|
1472
894
|
const agent = this.getAgent(agentId);
|
|
1473
895
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1474
|
-
return agent.withdraw(state, chainId, amount);
|
|
896
|
+
return agent.withdraw(state, chainId, token, amount);
|
|
1475
897
|
}
|
|
1476
898
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1477
899
|
if (!amount) {
|
|
1478
900
|
const results2 = {};
|
|
1479
901
|
for (const agent of eligibleAgents) {
|
|
1480
|
-
results2[agent.id] = await agent.withdraw(state, chainId);
|
|
902
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
1481
903
|
}
|
|
1482
904
|
return { agentResult: results2 };
|
|
1483
905
|
}
|
|
906
|
+
const requested = BigInt(amount);
|
|
907
|
+
const aggregated = await this.getBalances();
|
|
908
|
+
const balances = eligibleAgents.map((agent) => {
|
|
909
|
+
const agentBalance = aggregated.agentBalances[agent.id];
|
|
910
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
911
|
+
(t) => t.chainId === chainId && t.asset === asset
|
|
912
|
+
);
|
|
913
|
+
if (!tokenBalance)
|
|
914
|
+
return { agent, balance: 0n };
|
|
915
|
+
const balance = parseUnits(tokenBalance.amount, assetInfo.decimals);
|
|
916
|
+
return { agent, balance };
|
|
917
|
+
});
|
|
918
|
+
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
919
|
+
if (totalAvailable < requested) {
|
|
920
|
+
throw new OwneyError(
|
|
921
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
922
|
+
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
923
|
+
{
|
|
924
|
+
asset,
|
|
925
|
+
requested: amount,
|
|
926
|
+
available: totalAvailable.toString()
|
|
927
|
+
}
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
const plans = planProportionalShares(balances, requested, totalAvailable);
|
|
1484
931
|
const results = {};
|
|
1485
|
-
|
|
1486
|
-
for (
|
|
932
|
+
const agentErrors = {};
|
|
933
|
+
for (let i = 0; i < plans.length; i++) {
|
|
934
|
+
const p = plans[i];
|
|
935
|
+
if (p.planned === 0n)
|
|
936
|
+
continue;
|
|
1487
937
|
try {
|
|
1488
|
-
results[agent.id] = await agent.withdraw(
|
|
1489
|
-
|
|
938
|
+
results[p.agent.id] = await p.agent.withdraw(
|
|
939
|
+
state,
|
|
940
|
+
chainId,
|
|
941
|
+
token,
|
|
942
|
+
p.planned.toString()
|
|
943
|
+
);
|
|
1490
944
|
} catch (err) {
|
|
1491
|
-
|
|
945
|
+
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
946
|
+
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
947
|
+
redistributeShare(plans, i, p.planned);
|
|
1492
948
|
}
|
|
1493
949
|
}
|
|
1494
|
-
if (
|
|
1495
|
-
throw
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
950
|
+
if (Object.keys(results).length === 0) {
|
|
951
|
+
throw new OwneyError(
|
|
952
|
+
"WITHDRAW_ALL_FAILED",
|
|
953
|
+
`Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
|
|
954
|
+
{
|
|
955
|
+
asset,
|
|
956
|
+
amount,
|
|
957
|
+
agentErrors
|
|
958
|
+
}
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
const withdrawn = sumWithdrawnAmount(results);
|
|
962
|
+
if (withdrawn < requested) {
|
|
963
|
+
throw new OwneyError(
|
|
964
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
965
|
+
`Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
|
|
966
|
+
{
|
|
967
|
+
asset,
|
|
968
|
+
requested: amount,
|
|
969
|
+
withdrawn: withdrawn.toString(),
|
|
970
|
+
partialResults: results,
|
|
971
|
+
agentErrors
|
|
972
|
+
}
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
return { agentResult: results };
|
|
1499
976
|
}
|
|
1500
977
|
// --- Portfolio reads ---
|
|
1501
978
|
/**
|
|
@@ -1520,7 +997,10 @@ var OwneySDK = class {
|
|
|
1520
997
|
})
|
|
1521
998
|
);
|
|
1522
999
|
let successCount = 0;
|
|
1523
|
-
|
|
1000
|
+
const agentErrors = {};
|
|
1001
|
+
for (let i = 0; i < balanceResults.length; i++) {
|
|
1002
|
+
const settledResult = balanceResults[i];
|
|
1003
|
+
const [agentId2] = entries[i];
|
|
1524
1004
|
if (settledResult.status === "fulfilled") {
|
|
1525
1005
|
const [id, b] = settledResult.value;
|
|
1526
1006
|
results[id] = b;
|
|
@@ -1528,12 +1008,14 @@ var OwneySDK = class {
|
|
|
1528
1008
|
successCount += 1;
|
|
1529
1009
|
continue;
|
|
1530
1010
|
}
|
|
1531
|
-
|
|
1011
|
+
const reason = settledResult.reason;
|
|
1012
|
+
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
1532
1013
|
}
|
|
1533
1014
|
if (successCount === 0) {
|
|
1534
1015
|
throw new OwneyError(
|
|
1535
1016
|
"BALANCE_ALL_FAILED",
|
|
1536
|
-
"Failed to fetch balances for all active agents."
|
|
1017
|
+
"Failed to fetch balances for all active agents.",
|
|
1018
|
+
{ agentErrors }
|
|
1537
1019
|
);
|
|
1538
1020
|
}
|
|
1539
1021
|
return {
|
|
@@ -1628,10 +1110,6 @@ var OwneySDK = class {
|
|
|
1628
1110
|
totalApy += agentApy * weight;
|
|
1629
1111
|
}
|
|
1630
1112
|
}
|
|
1631
|
-
console.log("account apy", {
|
|
1632
|
-
totalApy: String(totalApy),
|
|
1633
|
-
agentApy: results
|
|
1634
|
-
});
|
|
1635
1113
|
return {
|
|
1636
1114
|
totalApy: String(totalApy),
|
|
1637
1115
|
agentApy: results
|
|
@@ -1723,7 +1201,7 @@ var OwneySDK = class {
|
|
|
1723
1201
|
* @param options.days - Lookback period: "7D", "14D", or "30D"
|
|
1724
1202
|
* @param options.tokenSymbol - Optional asset symbol ("USDC" / "WETH") to
|
|
1725
1203
|
* scope the APY to a specific asset+chain. Agents whose backends do not
|
|
1726
|
-
* support per-asset APY
|
|
1204
|
+
* support per-asset APY ignore this.
|
|
1727
1205
|
* @param options.chainId - Optional chain id for per-asset APY lookups.
|
|
1728
1206
|
* Typically paired with `tokenSymbol`.
|
|
1729
1207
|
* @returns {AgentApy} for a single agent, or {OwneyAgentApy} with per-agent APY
|
|
@@ -1772,20 +1250,26 @@ var OwneySDK = class {
|
|
|
1772
1250
|
})
|
|
1773
1251
|
);
|
|
1774
1252
|
let successCount = 0;
|
|
1775
|
-
|
|
1253
|
+
const agentErrors = {};
|
|
1254
|
+
for (let i = 0; i < balanceResults.length; i++) {
|
|
1255
|
+
const settledResult = balanceResults[i];
|
|
1256
|
+
const [agentId] = entries[i];
|
|
1776
1257
|
if (settledResult.status === "fulfilled") {
|
|
1777
1258
|
const [id, bal] = settledResult.value;
|
|
1778
1259
|
balances[id] = bal;
|
|
1779
1260
|
totalBalance += bal;
|
|
1780
1261
|
successCount += 1;
|
|
1781
1262
|
} else {
|
|
1782
|
-
|
|
1263
|
+
const reason = settledResult.reason;
|
|
1264
|
+
agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
|
|
1265
|
+
console.error(`getAgentAllocation agent "${agentId}" failed:`, reason);
|
|
1783
1266
|
}
|
|
1784
1267
|
}
|
|
1785
1268
|
if (successCount === 0) {
|
|
1786
1269
|
throw new OwneyError(
|
|
1787
1270
|
"ALLOCATION_ALL_FAILED",
|
|
1788
|
-
"Failed to fetch allocation: all agents failed."
|
|
1271
|
+
"Failed to fetch allocation: all agents failed.",
|
|
1272
|
+
{ agentErrors }
|
|
1789
1273
|
);
|
|
1790
1274
|
}
|
|
1791
1275
|
const allocation = {};
|