@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.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
|
}
|
|
@@ -339,7 +345,9 @@ var ZyfaiAgent = class {
|
|
|
339
345
|
if (!this.connectedAddress) {
|
|
340
346
|
throw new OwneyError(
|
|
341
347
|
"NOT_CONNECTED",
|
|
342
|
-
"Wallet not connected. Call sdk.connect() first."
|
|
348
|
+
"Wallet not connected. Call sdk.connect() first.",
|
|
349
|
+
void 0,
|
|
350
|
+
this.id
|
|
343
351
|
);
|
|
344
352
|
}
|
|
345
353
|
return this.connectedAddress;
|
|
@@ -445,13 +453,14 @@ var ZyfaiAgent = class {
|
|
|
445
453
|
}
|
|
446
454
|
}
|
|
447
455
|
// --- IAgent: Fund operations ---
|
|
448
|
-
async withdraw(state, chainId, amount) {
|
|
456
|
+
async withdraw(state, chainId, token, amount) {
|
|
449
457
|
await this.connectFull(state, chainId);
|
|
450
458
|
const validChainId = isValidChainId(chainId);
|
|
451
459
|
const raw = await this.sdk.withdrawFunds(
|
|
452
460
|
this.getAddress(),
|
|
453
461
|
validChainId,
|
|
454
|
-
amount
|
|
462
|
+
amount,
|
|
463
|
+
token
|
|
455
464
|
);
|
|
456
465
|
return mapWithdraw(raw);
|
|
457
466
|
}
|
|
@@ -505,659 +514,6 @@ var ZyfaiAgent = class {
|
|
|
505
514
|
}
|
|
506
515
|
};
|
|
507
516
|
|
|
508
|
-
// src/agents/sail/sail.mapper.ts
|
|
509
|
-
function mapDeposit2(raw) {
|
|
510
|
-
return {
|
|
511
|
-
txHash: raw.txHash,
|
|
512
|
-
smartWallet: raw.smartWallet,
|
|
513
|
-
amount: raw.amount
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
function mapWithdraw2(raw) {
|
|
517
|
-
return {
|
|
518
|
-
txHash: raw.txHash,
|
|
519
|
-
type: raw.type,
|
|
520
|
-
amount: raw.amount
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
function mapPortfolioBalances(raw, smartWallet) {
|
|
524
|
-
let total = 0;
|
|
525
|
-
const tokens = [];
|
|
526
|
-
for (const item of raw.tokens) {
|
|
527
|
-
total += item.balanceFormatted;
|
|
528
|
-
tokens.push({
|
|
529
|
-
chain: SUPPORTED_CHAINS[item.chain],
|
|
530
|
-
chainId: item.chain,
|
|
531
|
-
asset: item.symbol === "USD\u20AE0" ? "USDT" : item.symbol,
|
|
532
|
-
amount: item.balance
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
return {
|
|
536
|
-
smartWallet,
|
|
537
|
-
totalBalance: String(total),
|
|
538
|
-
tokens
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
function mapEarnings2(raw, smartWallet) {
|
|
542
|
-
return {
|
|
543
|
-
smartWallet,
|
|
544
|
-
lifetimeEarnings: raw.result
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
function mapApyHistory2(raw, walletAddress) {
|
|
548
|
-
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
549
|
-
return {
|
|
550
|
-
walletAddress,
|
|
551
|
-
weightedApyAfterFee: raw.result.apy,
|
|
552
|
-
weightedApyAfterFeeDetails: apyPerAsset ? { apyPerAsset } : void 0
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
function mapSailTransaction(tx) {
|
|
556
|
-
switch (tx.type) {
|
|
557
|
-
case "deposit":
|
|
558
|
-
case "redeem":
|
|
559
|
-
return {
|
|
560
|
-
txHashes: tx.tx_hashes,
|
|
561
|
-
chainId: tx.chain_id,
|
|
562
|
-
tokenSymbol: tx.asset,
|
|
563
|
-
amount: String(tx.amount)
|
|
564
|
-
};
|
|
565
|
-
case "swap":
|
|
566
|
-
return {
|
|
567
|
-
txHashes: tx.tx_hashes,
|
|
568
|
-
chainId: tx.chain_id,
|
|
569
|
-
tokenSymbol: tx.from_asset,
|
|
570
|
-
amount: String(tx.amount)
|
|
571
|
-
};
|
|
572
|
-
case "bridge":
|
|
573
|
-
return {
|
|
574
|
-
txHashes: tx.tx_hashes,
|
|
575
|
-
chainId: Number(tx.from_chain_id),
|
|
576
|
-
tokenSymbol: tx.asset,
|
|
577
|
-
amount: String(tx.amount)
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
function buildRebalanceLogs(transactions, failedTransactions) {
|
|
582
|
-
const redeems = transactions.filter(
|
|
583
|
-
(tx) => tx.type === "redeem"
|
|
584
|
-
);
|
|
585
|
-
const deposits = transactions.filter(
|
|
586
|
-
(tx) => tx.type === "deposit"
|
|
587
|
-
);
|
|
588
|
-
const failedRedeems = failedTransactions.filter(
|
|
589
|
-
(tx) => tx.type === "redeem"
|
|
590
|
-
);
|
|
591
|
-
const failedDeposits = failedTransactions.filter(
|
|
592
|
-
(tx) => tx.type === "deposit"
|
|
593
|
-
);
|
|
594
|
-
const logs = [];
|
|
595
|
-
const matchedDeposits = /* @__PURE__ */ new Set();
|
|
596
|
-
const matchedRedeems = /* @__PURE__ */ new Set();
|
|
597
|
-
for (let r = 0; r < redeems.length; r++) {
|
|
598
|
-
const idx = deposits.findIndex(
|
|
599
|
-
(d, i) => !matchedDeposits.has(i) && d.amount === redeems[r].amount
|
|
600
|
-
);
|
|
601
|
-
if (idx !== -1) {
|
|
602
|
-
matchedDeposits.add(idx);
|
|
603
|
-
matchedRedeems.add(r);
|
|
604
|
-
logs.push({
|
|
605
|
-
fromProtocol: redeems[r].protocol_name,
|
|
606
|
-
toProtocol: deposits[idx].protocol_name,
|
|
607
|
-
tokenSymbol: redeems[r].asset,
|
|
608
|
-
amount: String(redeems[r].amount),
|
|
609
|
-
status: "success"
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
for (let i = 0; i < deposits.length; i++) {
|
|
614
|
-
if (matchedDeposits.has(i))
|
|
615
|
-
continue;
|
|
616
|
-
const failedRedeem = failedRedeems.find(
|
|
617
|
-
(fr) => fr.amount === deposits[i].amount
|
|
618
|
-
);
|
|
619
|
-
logs.push({
|
|
620
|
-
fromProtocol: failedRedeem ? failedRedeem.protocol_name : "Wallet",
|
|
621
|
-
toProtocol: deposits[i].protocol_name,
|
|
622
|
-
tokenSymbol: deposits[i].asset,
|
|
623
|
-
amount: String(deposits[i].amount),
|
|
624
|
-
status: failedRedeem ? "failed" : "success"
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
for (let i = 0; i < redeems.length; i++) {
|
|
628
|
-
if (matchedRedeems.has(i))
|
|
629
|
-
continue;
|
|
630
|
-
const failedDeposit = failedDeposits.find(
|
|
631
|
-
(fd) => fd.amount === redeems[i].amount
|
|
632
|
-
);
|
|
633
|
-
logs.push({
|
|
634
|
-
fromProtocol: redeems[i].protocol_name,
|
|
635
|
-
toProtocol: failedDeposit ? failedDeposit.protocol_name : "Wallet",
|
|
636
|
-
tokenSymbol: redeems[i].asset,
|
|
637
|
-
amount: String(redeems[i].amount),
|
|
638
|
-
status: failedDeposit ? "failed" : "success"
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
return logs;
|
|
642
|
-
}
|
|
643
|
-
function mapHistory2(raw) {
|
|
644
|
-
const actionMap = {
|
|
645
|
-
execution: "Rebalance",
|
|
646
|
-
deposit: "Top up",
|
|
647
|
-
withdraw: "Withdraw",
|
|
648
|
-
rewards: "Earned"
|
|
649
|
-
};
|
|
650
|
-
const filtered = raw.result.items.filter(
|
|
651
|
-
(i) => !(i.type === "execution" && (i.status === "failed" || !i.transactions.length))
|
|
652
|
-
);
|
|
653
|
-
return {
|
|
654
|
-
total: filtered.length,
|
|
655
|
-
data: filtered.map((entry) => ({
|
|
656
|
-
agent: "sail",
|
|
657
|
-
action: actionMap[entry.type] ?? entry.type,
|
|
658
|
-
date: entry.time,
|
|
659
|
-
newApy: String(entry.new_apy),
|
|
660
|
-
oldApy: String(entry.previous_apy),
|
|
661
|
-
transactions: (entry.transactions ?? []).map(mapSailTransaction),
|
|
662
|
-
rebalanceLog: buildRebalanceLogs(
|
|
663
|
-
entry.transactions ?? [],
|
|
664
|
-
entry.failed_transactions ?? []
|
|
665
|
-
)
|
|
666
|
-
}))
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
function mapAgentStatus2(raw) {
|
|
670
|
-
return {
|
|
671
|
-
success: raw.success,
|
|
672
|
-
strategy: raw.strategy,
|
|
673
|
-
protocols: raw.protocols
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
function mapUserProfile2(raw) {
|
|
677
|
-
return {
|
|
678
|
-
address: raw.result.smart_account,
|
|
679
|
-
smartWallet: raw.result.smart_account,
|
|
680
|
-
chains: [],
|
|
681
|
-
hasActiveSessionKey: false,
|
|
682
|
-
protocols: []
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
function mapApyByStrategy2(raw) {
|
|
686
|
-
const apyPerAsset = remapApyPerAssetToSymbols(raw.result.apy_per_asset);
|
|
687
|
-
return {
|
|
688
|
-
averageApy: raw.result.apy,
|
|
689
|
-
detailedApys: apyPerAsset ? { apyPerAsset } : void 0
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
function remapApyPerAssetToSymbols(raw) {
|
|
693
|
-
if (!raw)
|
|
694
|
-
return void 0;
|
|
695
|
-
const remapped = {};
|
|
696
|
-
for (const asset of SupportedAssets) {
|
|
697
|
-
const chainKey = asset.chainId;
|
|
698
|
-
const apyByAddress = raw[String(chainKey)];
|
|
699
|
-
if (!apyByAddress)
|
|
700
|
-
continue;
|
|
701
|
-
const apy = apyByAddress[asset.address] ?? apyByAddress[asset.address.toLowerCase()];
|
|
702
|
-
if (apy === void 0)
|
|
703
|
-
continue;
|
|
704
|
-
const bucket = remapped[chainKey] ?? {};
|
|
705
|
-
bucket[asset.symbol] = apy;
|
|
706
|
-
remapped[chainKey] = bucket;
|
|
707
|
-
}
|
|
708
|
-
return Object.keys(remapped).length > 0 ? remapped : void 0;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// src/agents/sail/sail.agent.ts
|
|
712
|
-
var SAIL_API_BASE = "https://app.sail.money";
|
|
713
|
-
var PROJECT_ID = "sail";
|
|
714
|
-
var PAGE_ID = "home";
|
|
715
|
-
var SAIL_PORTFOLIO_TIMEOUT_MS = 8e3;
|
|
716
|
-
var SailAgent = class {
|
|
717
|
-
id = "sail";
|
|
718
|
-
minDepositAmount = "4800000000";
|
|
719
|
-
supportedChainIds = [8453, 42161];
|
|
720
|
-
supportedAssets = [
|
|
721
|
-
{
|
|
722
|
-
chainId: 8453,
|
|
723
|
-
chain: "BASE",
|
|
724
|
-
assets: ["USDC"]
|
|
725
|
-
},
|
|
726
|
-
{
|
|
727
|
-
chainId: 42161,
|
|
728
|
-
chain: "ARBITRUM",
|
|
729
|
-
assets: ["USDC", "USDT"]
|
|
730
|
-
}
|
|
731
|
-
];
|
|
732
|
-
apiKey;
|
|
733
|
-
authToken = null;
|
|
734
|
-
accountToken = null;
|
|
735
|
-
connectedAddress = null;
|
|
736
|
-
smartWalletAddress = null;
|
|
737
|
-
connectedChainId = null;
|
|
738
|
-
connectPromise = null;
|
|
739
|
-
constructor(apiKey) {
|
|
740
|
-
this.apiKey = apiKey;
|
|
741
|
-
}
|
|
742
|
-
// --- Private helpers ---
|
|
743
|
-
async request(method, path, body, timeoutMs) {
|
|
744
|
-
const headers = {
|
|
745
|
-
"Content-Type": "application/json",
|
|
746
|
-
"X-Business-API-Key": this.apiKey
|
|
747
|
-
};
|
|
748
|
-
if (this.authToken) {
|
|
749
|
-
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
750
|
-
}
|
|
751
|
-
if (this.accountToken) {
|
|
752
|
-
headers["X-Account-Token"] = this.accountToken;
|
|
753
|
-
}
|
|
754
|
-
const controller = new AbortController();
|
|
755
|
-
const timeoutId = typeof timeoutMs === "number" && timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
756
|
-
let res;
|
|
757
|
-
try {
|
|
758
|
-
res = await fetch(`${SAIL_API_BASE}${path}`, {
|
|
759
|
-
method,
|
|
760
|
-
headers,
|
|
761
|
-
body: body ? JSON.stringify(body) : void 0,
|
|
762
|
-
signal: controller.signal
|
|
763
|
-
});
|
|
764
|
-
} catch (error) {
|
|
765
|
-
if (error instanceof Error && error.name === "AbortError" && timeoutMs) {
|
|
766
|
-
throw new OwneyError(
|
|
767
|
-
"API_SAIL_TIMEOUT",
|
|
768
|
-
`Sail API timeout ${timeoutMs}ms: ${method} ${path}`,
|
|
769
|
-
{ timeoutMs, method, path }
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
throw error;
|
|
773
|
-
} finally {
|
|
774
|
-
if (timeoutId)
|
|
775
|
-
clearTimeout(timeoutId);
|
|
776
|
-
}
|
|
777
|
-
if (!res.ok) {
|
|
778
|
-
const text = await res.text().catch(() => "");
|
|
779
|
-
throw new OwneyError(
|
|
780
|
-
"API_SAIL_ERROR",
|
|
781
|
-
`Sail API error ${res.status}: ${text}`,
|
|
782
|
-
{ statusCode: res.status, method, path, responseBody: text }
|
|
783
|
-
);
|
|
784
|
-
}
|
|
785
|
-
return res.json();
|
|
786
|
-
}
|
|
787
|
-
/** Initiate SIWE and sign with the user's wallet. */
|
|
788
|
-
async signSiwe(state, chainId) {
|
|
789
|
-
const initiate = await this.request(
|
|
790
|
-
"POST",
|
|
791
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/initiate`,
|
|
792
|
-
{
|
|
793
|
-
method: "siwe",
|
|
794
|
-
address: state.walletAddress,
|
|
795
|
-
chainId,
|
|
796
|
-
identityMode: "byWallet"
|
|
797
|
-
}
|
|
798
|
-
);
|
|
799
|
-
const signature = await state.provider.request({
|
|
800
|
-
method: "personal_sign",
|
|
801
|
-
params: [initiate.message, state.walletAddress]
|
|
802
|
-
});
|
|
803
|
-
return {
|
|
804
|
-
sessionId: initiate.sessionId,
|
|
805
|
-
message: initiate.message,
|
|
806
|
-
signature
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
/** Full connect: auth handles smart account creation via SIWE flow. */
|
|
810
|
-
async connectFull(state, chainId) {
|
|
811
|
-
if (this.connectedAddress && this.connectedChainId === chainId) {
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
if (this.connectPromise) {
|
|
815
|
-
await this.connectPromise;
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
if (!state.walletAddress) {
|
|
819
|
-
throw new OwneyError(
|
|
820
|
-
"WALLET_ADDRESS_REQUIRED",
|
|
821
|
-
"Wallet address is required. Call sdk.connect(provider) first."
|
|
822
|
-
);
|
|
823
|
-
}
|
|
824
|
-
this.connectPromise = (async () => {
|
|
825
|
-
const { sessionId, signature, message } = await this.signSiwe(
|
|
826
|
-
state,
|
|
827
|
-
chainId
|
|
828
|
-
);
|
|
829
|
-
const complete = await this.request(
|
|
830
|
-
"POST",
|
|
831
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/auth/complete`,
|
|
832
|
-
{
|
|
833
|
-
method: "custom",
|
|
834
|
-
sessionId,
|
|
835
|
-
signature,
|
|
836
|
-
walletAddress: state.walletAddress,
|
|
837
|
-
identityMode: "byWallet"
|
|
838
|
-
}
|
|
839
|
-
);
|
|
840
|
-
this.authToken = complete.token;
|
|
841
|
-
this.accountToken = complete.accountToken;
|
|
842
|
-
this.connectedAddress = state.walletAddress;
|
|
843
|
-
this.smartWalletAddress = complete.walletAddress;
|
|
844
|
-
this.connectedChainId = chainId;
|
|
845
|
-
})();
|
|
846
|
-
try {
|
|
847
|
-
await this.connectPromise;
|
|
848
|
-
} finally {
|
|
849
|
-
this.connectPromise = null;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
async resolveSmartWallet(state, chainId) {
|
|
853
|
-
await this.connectFull(state, chainId);
|
|
854
|
-
const validChainId = isValidChainId(chainId);
|
|
855
|
-
if (!this.smartWalletAddress) {
|
|
856
|
-
throw new OwneyError(
|
|
857
|
-
"NOT_CONNECTED",
|
|
858
|
-
"Wallet not connected. Call sdk.connect() first."
|
|
859
|
-
);
|
|
860
|
-
}
|
|
861
|
-
return {
|
|
862
|
-
smartWallet: this.smartWalletAddress,
|
|
863
|
-
chainId: validChainId
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
// --- IAgent: Connection lifecycle ---
|
|
867
|
-
async disconnect() {
|
|
868
|
-
this.authToken = null;
|
|
869
|
-
this.accountToken = null;
|
|
870
|
-
this.connectedAddress = null;
|
|
871
|
-
this.smartWalletAddress = null;
|
|
872
|
-
this.connectedChainId = null;
|
|
873
|
-
}
|
|
874
|
-
// --- IAgent: Account lifecycle ---
|
|
875
|
-
async activateAgent(state, chainId) {
|
|
876
|
-
try {
|
|
877
|
-
await this.connectFull(state, chainId);
|
|
878
|
-
} catch (error) {
|
|
879
|
-
await this.disconnect();
|
|
880
|
-
throw error;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
async deposit(state, chainId, amount, depositCallback) {
|
|
884
|
-
if (!depositCallback) {
|
|
885
|
-
throw new OwneyError(
|
|
886
|
-
"DEPOSIT_CALLBACK_REQUIRED",
|
|
887
|
-
"depositCallback is required for Sail. Provide a callback that transfers tokens and returns the transaction hash."
|
|
888
|
-
);
|
|
889
|
-
}
|
|
890
|
-
try {
|
|
891
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
892
|
-
const depositInfo = await this.getDepositInfo();
|
|
893
|
-
const resolvedTokenAddress = depositInfo.permittedTokens[0]?.address;
|
|
894
|
-
if (!resolvedTokenAddress) {
|
|
895
|
-
throw new OwneyError(
|
|
896
|
-
"DEPOSIT_NO_PERMITTED_TOKENS",
|
|
897
|
-
"No permitted tokens available for deposit."
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
|
-
await this.executePreHooks(resolvedTokenAddress, amount, chainId);
|
|
901
|
-
const txHash = await depositCallback(smartWallet, chainId, amount);
|
|
902
|
-
if (!txHash) {
|
|
903
|
-
throw new OwneyError(
|
|
904
|
-
"DEPOSIT_CALLBACK_INVALID",
|
|
905
|
-
"depositCallback must return a transaction hash (txHash) for Sail deposits."
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
|
-
const postResult = await this.executePostHooks(
|
|
909
|
-
resolvedTokenAddress,
|
|
910
|
-
amount,
|
|
911
|
-
chainId,
|
|
912
|
-
txHash
|
|
913
|
-
);
|
|
914
|
-
return mapDeposit2({
|
|
915
|
-
txHash: postResult.transactionHash,
|
|
916
|
-
smartWallet,
|
|
917
|
-
amount
|
|
918
|
-
});
|
|
919
|
-
} catch (error) {
|
|
920
|
-
await this.disconnect();
|
|
921
|
-
throw error;
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
// --- Private deposit flow helpers ---
|
|
925
|
-
async getDepositInfo() {
|
|
926
|
-
return this.request(
|
|
927
|
-
"GET",
|
|
928
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/deposit-info`
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
async executePreHooks(tokenAddress, amount, chainId) {
|
|
932
|
-
return this.request(
|
|
933
|
-
"POST",
|
|
934
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/deposit/pre-hooks`,
|
|
935
|
-
{ tokenAddress, amount, chainId }
|
|
936
|
-
);
|
|
937
|
-
}
|
|
938
|
-
async executePostHooks(tokenAddress, amount, chainId, txHash) {
|
|
939
|
-
return this.request(
|
|
940
|
-
"POST",
|
|
941
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/deposit/post-hooks`,
|
|
942
|
-
{ tokenAddress, amount, chainId, txHash }
|
|
943
|
-
);
|
|
944
|
-
}
|
|
945
|
-
// --- Private withdraw flow helpers ---
|
|
946
|
-
async getWithdrawInfo() {
|
|
947
|
-
return this.request(
|
|
948
|
-
"GET",
|
|
949
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/withdraw-info`
|
|
950
|
-
);
|
|
951
|
-
}
|
|
952
|
-
// --- IAgent: Fund operations ---
|
|
953
|
-
async withdraw(state, chainId, amount) {
|
|
954
|
-
await this.connectFull(state, chainId);
|
|
955
|
-
const withdrawInfo = await this.getWithdrawInfo();
|
|
956
|
-
const tokenAddress = withdrawInfo.permittedTokens[0]?.address;
|
|
957
|
-
if (!tokenAddress) {
|
|
958
|
-
throw new OwneyError(
|
|
959
|
-
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
960
|
-
"No permitted tokens available for withdrawal."
|
|
961
|
-
);
|
|
962
|
-
}
|
|
963
|
-
const resolvedAmount = amount ?? withdrawInfo.currentBalance;
|
|
964
|
-
const { signature: adminWalletSignature, message: adminWalletPayload } = await this.signSiwe(state, chainId);
|
|
965
|
-
const payload = {
|
|
966
|
-
amount: String(resolvedAmount),
|
|
967
|
-
tokenAddress,
|
|
968
|
-
chainId,
|
|
969
|
-
adminWalletSignature,
|
|
970
|
-
adminWalletPayload
|
|
971
|
-
};
|
|
972
|
-
const raw = await this.request(
|
|
973
|
-
"POST",
|
|
974
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/withdraw`,
|
|
975
|
-
payload
|
|
976
|
-
);
|
|
977
|
-
return mapWithdraw2({
|
|
978
|
-
txHash: raw.transactionHash,
|
|
979
|
-
type: amount ? "partial" : "full",
|
|
980
|
-
amount: resolvedAmount
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
// --- IAgent: Portfolio reads ---
|
|
984
|
-
async getBalances(state, chainId) {
|
|
985
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
986
|
-
try {
|
|
987
|
-
const raw = await this.getPortfolio();
|
|
988
|
-
return mapPortfolioBalances(raw, smartWallet);
|
|
989
|
-
} catch (error) {
|
|
990
|
-
if (error instanceof OwneyError && error.code === "API_SAIL_TIMEOUT") {
|
|
991
|
-
console.warn(
|
|
992
|
-
"Sail portfolio request timed out; using empty snapshot.",
|
|
993
|
-
{
|
|
994
|
-
chainId
|
|
995
|
-
}
|
|
996
|
-
);
|
|
997
|
-
return mapPortfolioBalances(
|
|
998
|
-
{ message: "timeout", tokens: [] },
|
|
999
|
-
smartWallet
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
throw error;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
async getEarnings(state, chainId) {
|
|
1006
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1007
|
-
const raw = await this.getMetricsEarnings(state, chainId);
|
|
1008
|
-
return mapEarnings2(raw, smartWallet);
|
|
1009
|
-
}
|
|
1010
|
-
async getAccountApy(state, chainId, days) {
|
|
1011
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1012
|
-
const raw = await this.getMetricsYield(days, state, chainId);
|
|
1013
|
-
return mapApyHistory2(raw, smartWallet);
|
|
1014
|
-
}
|
|
1015
|
-
async getHistory(state, chainId, options) {
|
|
1016
|
-
const raw = await this.getMetricsHistory(state, chainId, options);
|
|
1017
|
-
return mapHistory2(raw);
|
|
1018
|
-
}
|
|
1019
|
-
// -- Sail-specific: Metrics ---
|
|
1020
|
-
async getMetricsPortfolio(state, chainId) {
|
|
1021
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1022
|
-
return this.request(
|
|
1023
|
-
"GET",
|
|
1024
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/portfolio?${smartWallet ? `smart_account=${smartWallet}` : ""}`,
|
|
1025
|
-
void 0,
|
|
1026
|
-
SAIL_PORTFOLIO_TIMEOUT_MS
|
|
1027
|
-
);
|
|
1028
|
-
}
|
|
1029
|
-
async getPortfolio() {
|
|
1030
|
-
return this.request(
|
|
1031
|
-
"GET",
|
|
1032
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/portfolio/tokens`,
|
|
1033
|
-
void 0,
|
|
1034
|
-
SAIL_PORTFOLIO_TIMEOUT_MS
|
|
1035
|
-
);
|
|
1036
|
-
}
|
|
1037
|
-
async getMetricsEarnings(state, chainId) {
|
|
1038
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1039
|
-
return this.request(
|
|
1040
|
-
"GET",
|
|
1041
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/earnings?smart_account=${smartWallet}`
|
|
1042
|
-
);
|
|
1043
|
-
}
|
|
1044
|
-
async getMetricsBalance(state, chainId, currency = "usd") {
|
|
1045
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1046
|
-
return this.request(
|
|
1047
|
-
"GET",
|
|
1048
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/balance?smart_account=${smartWallet}¤cy=${currency}&limit=1000`
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
async getMetricsHistory(state, chainId, filters) {
|
|
1052
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1053
|
-
const params = new URLSearchParams();
|
|
1054
|
-
params.set("smart_account", smartWallet);
|
|
1055
|
-
if (filters?.fromDate) {
|
|
1056
|
-
params.set("start_date", filters.fromDate);
|
|
1057
|
-
}
|
|
1058
|
-
if (filters?.toDate) {
|
|
1059
|
-
params.set("end_date", filters.toDate);
|
|
1060
|
-
}
|
|
1061
|
-
return this.request(
|
|
1062
|
-
"GET",
|
|
1063
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/history?${params.toString()}`
|
|
1064
|
-
);
|
|
1065
|
-
}
|
|
1066
|
-
async getMetricsUser(state, chainId, currency) {
|
|
1067
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1068
|
-
return this.request(
|
|
1069
|
-
"GET",
|
|
1070
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/user-metrics?smart_account=${smartWallet}¤cy=${currency}`
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
async getMetricsYield(days, state, chainId) {
|
|
1074
|
-
let walletAddress = "";
|
|
1075
|
-
if (state && chainId) {
|
|
1076
|
-
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
1077
|
-
walletAddress = smartWallet;
|
|
1078
|
-
}
|
|
1079
|
-
const dayCount = DayFilterMapping[days];
|
|
1080
|
-
if (dayCount === void 0) {
|
|
1081
|
-
throw new OwneyError(
|
|
1082
|
-
"VALIDATION_INVALID_DAYS",
|
|
1083
|
-
`Invalid days value: "${days}". Expected one of: "7D", "14D", "30D".`,
|
|
1084
|
-
{ days }
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
const now = /* @__PURE__ */ new Date();
|
|
1088
|
-
const end = now.toISOString().split("T")[0];
|
|
1089
|
-
const start = new Date(now.getTime() - dayCount * 864e5).toISOString().split("T")[0];
|
|
1090
|
-
return this.request(
|
|
1091
|
-
"GET",
|
|
1092
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/metrics/yield?start=${start}&end=${end}${walletAddress ? `&smart_account=${walletAddress}` : ""}`
|
|
1093
|
-
);
|
|
1094
|
-
}
|
|
1095
|
-
// --- Sail-specific: Automation jobs ---
|
|
1096
|
-
async getAutomationStatus(state, chainId) {
|
|
1097
|
-
await this.connectFull(state, chainId);
|
|
1098
|
-
return this.request(
|
|
1099
|
-
"GET",
|
|
1100
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/status`
|
|
1101
|
-
);
|
|
1102
|
-
}
|
|
1103
|
-
async startAutomation(state, chainId, graphId, prompt, options) {
|
|
1104
|
-
await this.connectFull(state, chainId);
|
|
1105
|
-
return this.request(
|
|
1106
|
-
"POST",
|
|
1107
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/start`,
|
|
1108
|
-
{
|
|
1109
|
-
graphId,
|
|
1110
|
-
prompt,
|
|
1111
|
-
paramValues: options?.paramValues ?? {},
|
|
1112
|
-
iterations: options?.iterations ?? 1,
|
|
1113
|
-
branchId: options?.branchId
|
|
1114
|
-
}
|
|
1115
|
-
);
|
|
1116
|
-
}
|
|
1117
|
-
async pauseAutomation(state, chainId) {
|
|
1118
|
-
await this.connectFull(state, chainId);
|
|
1119
|
-
return this.request(
|
|
1120
|
-
"POST",
|
|
1121
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/pause`
|
|
1122
|
-
);
|
|
1123
|
-
}
|
|
1124
|
-
async resumeAutomation(state, chainId) {
|
|
1125
|
-
await this.connectFull(state, chainId);
|
|
1126
|
-
return this.request(
|
|
1127
|
-
"POST",
|
|
1128
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/resume`
|
|
1129
|
-
);
|
|
1130
|
-
}
|
|
1131
|
-
async stopAutomation(state, chainId) {
|
|
1132
|
-
await this.connectFull(state, chainId);
|
|
1133
|
-
return this.request(
|
|
1134
|
-
"POST",
|
|
1135
|
-
`/api/v1/projects/${PROJECT_ID}/pages/${PAGE_ID}/automation/stop`
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
// --- IAgent: Agent control ---
|
|
1139
|
-
async pause(state, chainId) {
|
|
1140
|
-
await this.connectFull(state, chainId);
|
|
1141
|
-
await this.pauseAutomation(state, chainId);
|
|
1142
|
-
return mapAgentStatus2({ success: true });
|
|
1143
|
-
}
|
|
1144
|
-
async resume(state, chainId) {
|
|
1145
|
-
await this.connectFull(state, chainId);
|
|
1146
|
-
await this.resumeAutomation(state, chainId);
|
|
1147
|
-
return mapAgentStatus2({ success: true });
|
|
1148
|
-
}
|
|
1149
|
-
async getUserProfile(state, chainId) {
|
|
1150
|
-
await this.connectFull(state, chainId);
|
|
1151
|
-
const raw = await this.getMetricsUser(state, chainId, "usd");
|
|
1152
|
-
return mapUserProfile2(raw);
|
|
1153
|
-
}
|
|
1154
|
-
// --- IAgent: Discovery (no wallet required) ---
|
|
1155
|
-
async getAgentApy(days, _options) {
|
|
1156
|
-
const raw = await this.getMetricsYield(days);
|
|
1157
|
-
return mapApyByStrategy2(raw);
|
|
1158
|
-
}
|
|
1159
|
-
};
|
|
1160
|
-
|
|
1161
517
|
// src/lib/routing-api.ts
|
|
1162
518
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1163
519
|
async function fetchAgentKeys(apiKey) {
|
|
@@ -1188,7 +544,65 @@ async function fetchAgentKeys(apiKey) {
|
|
|
1188
544
|
return json.data;
|
|
1189
545
|
}
|
|
1190
546
|
|
|
547
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
548
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
549
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
550
|
+
agent,
|
|
551
|
+
balance,
|
|
552
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
553
|
+
}));
|
|
554
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
555
|
+
let remainder = requested - assigned;
|
|
556
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
557
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
558
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
559
|
+
});
|
|
560
|
+
for (const p of byHeadroom) {
|
|
561
|
+
if (remainder === 0n)
|
|
562
|
+
break;
|
|
563
|
+
const headroom = p.balance - p.planned;
|
|
564
|
+
if (headroom <= 0n)
|
|
565
|
+
continue;
|
|
566
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
567
|
+
p.planned += take;
|
|
568
|
+
remainder -= take;
|
|
569
|
+
}
|
|
570
|
+
return plans;
|
|
571
|
+
}
|
|
572
|
+
function redistributeShare(plans, fromIndex, amount) {
|
|
573
|
+
const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
|
|
574
|
+
const totalHeadroom = candidates.reduce(
|
|
575
|
+
(s, c) => s + (c.balance - c.planned),
|
|
576
|
+
0n
|
|
577
|
+
);
|
|
578
|
+
if (totalHeadroom === 0n)
|
|
579
|
+
return;
|
|
580
|
+
let distributed = 0n;
|
|
581
|
+
for (const c of candidates) {
|
|
582
|
+
const headroom = c.balance - c.planned;
|
|
583
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
584
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
585
|
+
c.planned += give;
|
|
586
|
+
distributed += give;
|
|
587
|
+
}
|
|
588
|
+
let leftover = amount - distributed;
|
|
589
|
+
for (const c of candidates) {
|
|
590
|
+
if (leftover === 0n)
|
|
591
|
+
break;
|
|
592
|
+
const headroom = c.balance - c.planned;
|
|
593
|
+
if (headroom <= 0n)
|
|
594
|
+
continue;
|
|
595
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
596
|
+
c.planned += take;
|
|
597
|
+
leftover -= take;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function sumWithdrawnAmount(results) {
|
|
601
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
602
|
+
}
|
|
603
|
+
|
|
1191
604
|
// src/client.ts
|
|
605
|
+
var import_viem = require("viem");
|
|
1192
606
|
var OwneySDK = class {
|
|
1193
607
|
agents = /* @__PURE__ */ new Map();
|
|
1194
608
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -1305,9 +719,6 @@ var OwneySDK = class {
|
|
|
1305
719
|
if (agentId === "zyfai") {
|
|
1306
720
|
return new ZyfaiAgent(key, this.zyfaiRpcUrls);
|
|
1307
721
|
}
|
|
1308
|
-
if (agentId === "sail") {
|
|
1309
|
-
return new SailAgent(key);
|
|
1310
|
-
}
|
|
1311
722
|
return null;
|
|
1312
723
|
}
|
|
1313
724
|
// --- Account lifecycle ---
|
|
@@ -1464,7 +875,8 @@ var OwneySDK = class {
|
|
|
1464
875
|
throw new OwneyError(
|
|
1465
876
|
"ASSET_UNSUPPORTED",
|
|
1466
877
|
`Agent "${agent.id}" does not support asset "${asset}" on chain ${chainId}.`,
|
|
1467
|
-
{ agentId: agent.id, asset, chainId }
|
|
878
|
+
{ agentId: agent.id, asset, chainId },
|
|
879
|
+
agent.id
|
|
1468
880
|
);
|
|
1469
881
|
}
|
|
1470
882
|
}
|
|
@@ -1498,34 +910,99 @@ var OwneySDK = class {
|
|
|
1498
910
|
const { asset, amount, agentId } = options;
|
|
1499
911
|
const state = this.requireState();
|
|
1500
912
|
const chainId = this.requireChainId();
|
|
913
|
+
const token = asset;
|
|
914
|
+
const assetInfo = SupportedAssets.find(
|
|
915
|
+
(a) => a.chainId === chainId && a.symbol === token
|
|
916
|
+
);
|
|
917
|
+
if (!assetInfo) {
|
|
918
|
+
throw new OwneyError(
|
|
919
|
+
"WITHDRAW_NO_PERMITTED_TOKENS",
|
|
920
|
+
"Asset selected is not supported"
|
|
921
|
+
);
|
|
922
|
+
}
|
|
1501
923
|
if (agentId) {
|
|
1502
924
|
const agent = this.getAgent(agentId);
|
|
1503
925
|
this.validateAssetSupport(agent, chainId, asset);
|
|
1504
|
-
return agent.withdraw(state, chainId, amount);
|
|
926
|
+
return agent.withdraw(state, chainId, token, amount);
|
|
1505
927
|
}
|
|
1506
928
|
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
1507
929
|
if (!amount) {
|
|
1508
930
|
const results2 = {};
|
|
1509
931
|
for (const agent of eligibleAgents) {
|
|
1510
|
-
results2[agent.id] = await agent.withdraw(state, chainId);
|
|
932
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token);
|
|
1511
933
|
}
|
|
1512
934
|
return { agentResult: results2 };
|
|
1513
935
|
}
|
|
936
|
+
const requested = BigInt(amount);
|
|
937
|
+
const aggregated = await this.getBalances();
|
|
938
|
+
const balances = eligibleAgents.map((agent) => {
|
|
939
|
+
const agentBalance = aggregated.agentBalances[agent.id];
|
|
940
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
941
|
+
(t) => t.chainId === chainId && t.asset === asset
|
|
942
|
+
);
|
|
943
|
+
if (!tokenBalance)
|
|
944
|
+
return { agent, balance: 0n };
|
|
945
|
+
const balance = (0, import_viem.parseUnits)(tokenBalance.amount, assetInfo.decimals);
|
|
946
|
+
return { agent, balance };
|
|
947
|
+
});
|
|
948
|
+
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
949
|
+
if (totalAvailable < requested) {
|
|
950
|
+
throw new OwneyError(
|
|
951
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
952
|
+
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
953
|
+
{
|
|
954
|
+
asset,
|
|
955
|
+
requested: amount,
|
|
956
|
+
available: totalAvailable.toString()
|
|
957
|
+
}
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
const plans = planProportionalShares(balances, requested, totalAvailable);
|
|
1514
961
|
const results = {};
|
|
1515
|
-
|
|
1516
|
-
for (
|
|
962
|
+
const agentErrors = {};
|
|
963
|
+
for (let i = 0; i < plans.length; i++) {
|
|
964
|
+
const p = plans[i];
|
|
965
|
+
if (p.planned === 0n)
|
|
966
|
+
continue;
|
|
1517
967
|
try {
|
|
1518
|
-
results[agent.id] = await agent.withdraw(
|
|
1519
|
-
|
|
968
|
+
results[p.agent.id] = await p.agent.withdraw(
|
|
969
|
+
state,
|
|
970
|
+
chainId,
|
|
971
|
+
token,
|
|
972
|
+
p.planned.toString()
|
|
973
|
+
);
|
|
1520
974
|
} catch (err) {
|
|
1521
|
-
|
|
975
|
+
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
976
|
+
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
977
|
+
redistributeShare(plans, i, p.planned);
|
|
1522
978
|
}
|
|
1523
979
|
}
|
|
1524
|
-
if (
|
|
1525
|
-
throw
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
980
|
+
if (Object.keys(results).length === 0) {
|
|
981
|
+
throw new OwneyError(
|
|
982
|
+
"WITHDRAW_ALL_FAILED",
|
|
983
|
+
`Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
|
|
984
|
+
{
|
|
985
|
+
asset,
|
|
986
|
+
amount,
|
|
987
|
+
agentErrors
|
|
988
|
+
}
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
const withdrawn = sumWithdrawnAmount(results);
|
|
992
|
+
if (withdrawn < requested) {
|
|
993
|
+
throw new OwneyError(
|
|
994
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
995
|
+
`Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
|
|
996
|
+
{
|
|
997
|
+
asset,
|
|
998
|
+
requested: amount,
|
|
999
|
+
withdrawn: withdrawn.toString(),
|
|
1000
|
+
partialResults: results,
|
|
1001
|
+
agentErrors
|
|
1002
|
+
}
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
return { agentResult: results };
|
|
1529
1006
|
}
|
|
1530
1007
|
// --- Portfolio reads ---
|
|
1531
1008
|
/**
|
|
@@ -1550,7 +1027,10 @@ var OwneySDK = class {
|
|
|
1550
1027
|
})
|
|
1551
1028
|
);
|
|
1552
1029
|
let successCount = 0;
|
|
1553
|
-
|
|
1030
|
+
const agentErrors = {};
|
|
1031
|
+
for (let i = 0; i < balanceResults.length; i++) {
|
|
1032
|
+
const settledResult = balanceResults[i];
|
|
1033
|
+
const [agentId2] = entries[i];
|
|
1554
1034
|
if (settledResult.status === "fulfilled") {
|
|
1555
1035
|
const [id, b] = settledResult.value;
|
|
1556
1036
|
results[id] = b;
|
|
@@ -1558,12 +1038,14 @@ var OwneySDK = class {
|
|
|
1558
1038
|
successCount += 1;
|
|
1559
1039
|
continue;
|
|
1560
1040
|
}
|
|
1561
|
-
|
|
1041
|
+
const reason = settledResult.reason;
|
|
1042
|
+
agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
|
|
1562
1043
|
}
|
|
1563
1044
|
if (successCount === 0) {
|
|
1564
1045
|
throw new OwneyError(
|
|
1565
1046
|
"BALANCE_ALL_FAILED",
|
|
1566
|
-
"Failed to fetch balances for all active agents."
|
|
1047
|
+
"Failed to fetch balances for all active agents.",
|
|
1048
|
+
{ agentErrors }
|
|
1567
1049
|
);
|
|
1568
1050
|
}
|
|
1569
1051
|
return {
|
|
@@ -1658,10 +1140,6 @@ var OwneySDK = class {
|
|
|
1658
1140
|
totalApy += agentApy * weight;
|
|
1659
1141
|
}
|
|
1660
1142
|
}
|
|
1661
|
-
console.log("account apy", {
|
|
1662
|
-
totalApy: String(totalApy),
|
|
1663
|
-
agentApy: results
|
|
1664
|
-
});
|
|
1665
1143
|
return {
|
|
1666
1144
|
totalApy: String(totalApy),
|
|
1667
1145
|
agentApy: results
|
|
@@ -1753,7 +1231,7 @@ var OwneySDK = class {
|
|
|
1753
1231
|
* @param options.days - Lookback period: "7D", "14D", or "30D"
|
|
1754
1232
|
* @param options.tokenSymbol - Optional asset symbol ("USDC" / "WETH") to
|
|
1755
1233
|
* scope the APY to a specific asset+chain. Agents whose backends do not
|
|
1756
|
-
* support per-asset APY
|
|
1234
|
+
* support per-asset APY ignore this.
|
|
1757
1235
|
* @param options.chainId - Optional chain id for per-asset APY lookups.
|
|
1758
1236
|
* Typically paired with `tokenSymbol`.
|
|
1759
1237
|
* @returns {AgentApy} for a single agent, or {OwneyAgentApy} with per-agent APY
|
|
@@ -1802,20 +1280,26 @@ var OwneySDK = class {
|
|
|
1802
1280
|
})
|
|
1803
1281
|
);
|
|
1804
1282
|
let successCount = 0;
|
|
1805
|
-
|
|
1283
|
+
const agentErrors = {};
|
|
1284
|
+
for (let i = 0; i < balanceResults.length; i++) {
|
|
1285
|
+
const settledResult = balanceResults[i];
|
|
1286
|
+
const [agentId] = entries[i];
|
|
1806
1287
|
if (settledResult.status === "fulfilled") {
|
|
1807
1288
|
const [id, bal] = settledResult.value;
|
|
1808
1289
|
balances[id] = bal;
|
|
1809
1290
|
totalBalance += bal;
|
|
1810
1291
|
successCount += 1;
|
|
1811
1292
|
} else {
|
|
1812
|
-
|
|
1293
|
+
const reason = settledResult.reason;
|
|
1294
|
+
agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
|
|
1295
|
+
console.error(`getAgentAllocation agent "${agentId}" failed:`, reason);
|
|
1813
1296
|
}
|
|
1814
1297
|
}
|
|
1815
1298
|
if (successCount === 0) {
|
|
1816
1299
|
throw new OwneyError(
|
|
1817
1300
|
"ALLOCATION_ALL_FAILED",
|
|
1818
|
-
"Failed to fetch allocation: all agents failed."
|
|
1301
|
+
"Failed to fetch allocation: all agents failed.",
|
|
1302
|
+
{ agentErrors }
|
|
1819
1303
|
);
|
|
1820
1304
|
}
|
|
1821
1305
|
const allocation = {};
|