@owney/sdk 0.7.15 → 0.7.16-beta.1
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 +2 -0
- package/dist/index.cjs +1566 -502
- package/dist/index.d.cts +115 -5
- package/dist/index.d.ts +115 -5
- package/dist/index.js +1570 -495
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
NotConnectedError: () => NotConnectedError,
|
|
27
27
|
OwneyError: () => OwneyError,
|
|
28
28
|
OwneySDK: () => OwneySDK,
|
|
29
|
+
YieldseekerAgent: () => YieldseekerAgent,
|
|
29
30
|
createOwneySIWX: () => createOwneySIWX,
|
|
30
31
|
setOwneyDebug: () => setOwneyDebug
|
|
31
32
|
});
|
|
@@ -339,9 +340,9 @@ function mapWeightedApyByChain(raw) {
|
|
|
339
340
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
340
341
|
const perAsset = {};
|
|
341
342
|
for (const [symbol, apy] of Object.entries(tokenApy)) {
|
|
342
|
-
const
|
|
343
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
344
|
-
perAsset[
|
|
343
|
+
const token2 = symbol;
|
|
344
|
+
if (!SUPPORTED_TOKENS.includes(token2)) continue;
|
|
345
|
+
perAsset[token2] = apy;
|
|
345
346
|
}
|
|
346
347
|
if (Object.keys(perAsset).length > 0) out[chainId] = perAsset;
|
|
347
348
|
}
|
|
@@ -510,23 +511,23 @@ function computeAllocationApy(positions) {
|
|
|
510
511
|
weightedSum += apy * value;
|
|
511
512
|
totalValue += value;
|
|
512
513
|
const chainId = resolveChainId(p.chain);
|
|
513
|
-
const
|
|
514
|
+
const token2 = p.asset;
|
|
514
515
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
515
|
-
if (!SUPPORTED_TOKENS.includes(
|
|
516
|
+
if (!SUPPORTED_TOKENS.includes(token2)) continue;
|
|
516
517
|
const perAsset = buckets[chainId] ?? {};
|
|
517
|
-
const cell = perAsset[
|
|
518
|
+
const cell = perAsset[token2] ?? { weightedSum: 0, value: 0 };
|
|
518
519
|
cell.weightedSum += apy * value;
|
|
519
520
|
cell.value += value;
|
|
520
|
-
perAsset[
|
|
521
|
+
perAsset[token2] = cell;
|
|
521
522
|
buckets[chainId] = perAsset;
|
|
522
523
|
}
|
|
523
524
|
const apyByChainAndAsset = {};
|
|
524
525
|
for (const [chainKey, perAsset] of Object.entries(buckets)) {
|
|
525
526
|
const chainId = Number(chainKey);
|
|
526
527
|
const out = {};
|
|
527
|
-
for (const [
|
|
528
|
+
for (const [token2, cell] of Object.entries(perAsset ?? {})) {
|
|
528
529
|
if (cell && cell.value > 0) {
|
|
529
|
-
out[
|
|
530
|
+
out[token2] = cell.weightedSum / cell.value;
|
|
530
531
|
}
|
|
531
532
|
}
|
|
532
533
|
if (Object.keys(out).length > 0) apyByChainAndAsset[chainId] = out;
|
|
@@ -540,9 +541,9 @@ var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4d
|
|
|
540
541
|
function topicToAddress(topic) {
|
|
541
542
|
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
542
543
|
}
|
|
543
|
-
function extractWithdrawnAmount(logs, recipient,
|
|
544
|
+
function extractWithdrawnAmount(logs, recipient, token2, decimals) {
|
|
544
545
|
const wantRecipient = recipient.toLowerCase();
|
|
545
|
-
const wantToken =
|
|
546
|
+
const wantToken = token2.toLowerCase();
|
|
546
547
|
let total = 0n;
|
|
547
548
|
let matched = false;
|
|
548
549
|
for (const log of logs) {
|
|
@@ -568,11 +569,11 @@ var InvalidHistoryCursorError = class extends Error {
|
|
|
568
569
|
function encodeHistoryCursor(payload) {
|
|
569
570
|
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
570
571
|
}
|
|
571
|
-
function decodeHistoryCursor(
|
|
572
|
-
if (!
|
|
572
|
+
function decodeHistoryCursor(token2) {
|
|
573
|
+
if (!token2) throw new InvalidHistoryCursorError("Cursor is empty.");
|
|
573
574
|
let parsed;
|
|
574
575
|
try {
|
|
575
|
-
const json = Buffer.from(
|
|
576
|
+
const json = Buffer.from(token2, "base64").toString("utf8");
|
|
576
577
|
parsed = JSON.parse(json);
|
|
577
578
|
} catch {
|
|
578
579
|
throw new InvalidHistoryCursorError("Cursor is not valid base64 JSON.");
|
|
@@ -595,8 +596,8 @@ var storage = () => {
|
|
|
595
596
|
};
|
|
596
597
|
var buildKey = (address) => `${KEY_PREFIX}:${address.toLowerCase()}`;
|
|
597
598
|
var legacyKeyPrefix = (address) => `${KEY_PREFIX}:${address.toLowerCase()}:`;
|
|
598
|
-
var isJwtExpired = (
|
|
599
|
-
const parts =
|
|
599
|
+
var isJwtExpired = (token2) => {
|
|
600
|
+
const parts = token2.split(".");
|
|
600
601
|
if (parts.length !== 3) return false;
|
|
601
602
|
try {
|
|
602
603
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -1698,20 +1699,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1698
1699
|
}
|
|
1699
1700
|
}
|
|
1700
1701
|
// --- IAgent: Fund operations ---
|
|
1701
|
-
async withdraw(state, chainId,
|
|
1702
|
+
async withdraw(state, chainId, token2, amount) {
|
|
1702
1703
|
const validChainId = isValidChainId(chainId);
|
|
1703
1704
|
await this.ensureConnected(state, validChainId);
|
|
1704
1705
|
const raw = await this.sdk.withdrawFunds(
|
|
1705
1706
|
this.getAddress(),
|
|
1706
1707
|
validChainId,
|
|
1707
1708
|
amount,
|
|
1708
|
-
|
|
1709
|
+
token2
|
|
1709
1710
|
);
|
|
1710
1711
|
if (!raw.success) {
|
|
1711
1712
|
throw new OwneyError(
|
|
1712
1713
|
"WITHDRAW_FAILED",
|
|
1713
1714
|
raw.message || "Zyfai withdraw failed.",
|
|
1714
|
-
{ chainId: validChainId, token, amount, response: raw },
|
|
1715
|
+
{ chainId: validChainId, token: token2, amount, response: raw },
|
|
1715
1716
|
this.id
|
|
1716
1717
|
);
|
|
1717
1718
|
}
|
|
@@ -1862,225 +1863,47 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1862
1863
|
}
|
|
1863
1864
|
};
|
|
1864
1865
|
|
|
1865
|
-
// src/
|
|
1866
|
-
var
|
|
1867
|
-
|
|
1868
|
-
|
|
1866
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
1867
|
+
var import_viem6 = require("viem");
|
|
1868
|
+
var import_chains3 = require("viem/chains");
|
|
1869
|
+
|
|
1870
|
+
// src/lib/chain-guard.ts
|
|
1871
|
+
var CHAIN_NAMES = {
|
|
1872
|
+
1: "Ethereum",
|
|
1873
|
+
8453: "Base",
|
|
1874
|
+
42161: "Arbitrum"
|
|
1875
|
+
};
|
|
1876
|
+
function chainName(chainId) {
|
|
1877
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
1878
|
+
}
|
|
1879
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
1880
|
+
const actual = await pub.getChainId();
|
|
1881
|
+
if (actual === expected) return;
|
|
1869
1882
|
try {
|
|
1870
|
-
|
|
1871
|
-
method: "GET",
|
|
1872
|
-
headers: {
|
|
1873
|
-
"Content-Type": "application/json",
|
|
1874
|
-
"x-owney-api-key": `${apiKey}`
|
|
1875
|
-
}
|
|
1876
|
-
});
|
|
1877
|
-
if (!res.ok) {
|
|
1878
|
-
if (res.status !== 404) {
|
|
1879
|
-
console.warn(
|
|
1880
|
-
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
1881
|
-
);
|
|
1882
|
-
}
|
|
1883
|
-
return null;
|
|
1884
|
-
}
|
|
1885
|
-
const json = await res.json();
|
|
1886
|
-
const policy = json.success ? json.data ?? null : null;
|
|
1887
|
-
debugLog(
|
|
1888
|
-
"owney-sdk",
|
|
1889
|
-
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
1890
|
-
policy ?? void 0
|
|
1891
|
-
);
|
|
1892
|
-
return policy;
|
|
1883
|
+
await wallet.switchChain({ id: expected });
|
|
1893
1884
|
} catch (error) {
|
|
1894
|
-
console.warn(
|
|
1895
|
-
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
1896
|
-
error instanceof Error ? error.message : String(error)
|
|
1897
|
-
);
|
|
1898
|
-
return null;
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL) {
|
|
1902
|
-
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
1903
|
-
const res = await fetch(url, {
|
|
1904
|
-
method: "GET",
|
|
1905
|
-
headers: {
|
|
1906
|
-
"Content-Type": "application/json",
|
|
1907
|
-
"x-owney-api-key": `${apiKey}`
|
|
1908
|
-
}
|
|
1909
|
-
});
|
|
1910
|
-
if (!res.ok) {
|
|
1911
|
-
const text = await res.text().catch(() => "");
|
|
1912
1885
|
throw new OwneyError(
|
|
1913
|
-
"
|
|
1914
|
-
`
|
|
1915
|
-
{
|
|
1886
|
+
"CHAIN_MISMATCH",
|
|
1887
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1888
|
+
{
|
|
1889
|
+
expectedChainId: expected,
|
|
1890
|
+
actualChainId: actual,
|
|
1891
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
1892
|
+
}
|
|
1916
1893
|
);
|
|
1917
1894
|
}
|
|
1918
|
-
const
|
|
1919
|
-
if (
|
|
1895
|
+
const after = await pub.getChainId();
|
|
1896
|
+
if (after !== expected) {
|
|
1920
1897
|
throw new OwneyError(
|
|
1921
|
-
"
|
|
1922
|
-
`
|
|
1923
|
-
{
|
|
1924
|
-
);
|
|
1925
|
-
}
|
|
1926
|
-
return json.data;
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
// src/lib/health-report.ts
|
|
1930
|
-
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
1931
|
-
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL2) {
|
|
1932
|
-
try {
|
|
1933
|
-
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
1934
|
-
method: "POST",
|
|
1935
|
-
headers: {
|
|
1936
|
-
"Content-Type": "application/json",
|
|
1937
|
-
"x-owney-api-key": apiKey
|
|
1938
|
-
},
|
|
1939
|
-
body: JSON.stringify({
|
|
1940
|
-
agent_type: agentType,
|
|
1941
|
-
error_code: errorCode,
|
|
1942
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1943
|
-
})
|
|
1944
|
-
});
|
|
1945
|
-
} catch (err) {
|
|
1946
|
-
console.warn(
|
|
1947
|
-
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
1948
|
-
err instanceof Error ? err.message : err
|
|
1949
|
-
);
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
1953
|
-
try {
|
|
1954
|
-
return await fn();
|
|
1955
|
-
} catch (err) {
|
|
1956
|
-
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
1957
|
-
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
1958
|
-
throw err;
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
// src/lib/helpers/withdraw-helper.ts
|
|
1963
|
-
var import_viem2 = require("viem");
|
|
1964
|
-
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
1965
|
-
const target = asset.toUpperCase();
|
|
1966
|
-
return agents.map((agent) => {
|
|
1967
|
-
const agentBalance = aggregated[agent.id];
|
|
1968
|
-
const tokenBalance = agentBalance?.tokens.find(
|
|
1969
|
-
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
1898
|
+
"CHAIN_MISMATCH",
|
|
1899
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
1900
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
1970
1901
|
);
|
|
1971
|
-
if (!tokenBalance) return { agent, balance: 0n };
|
|
1972
|
-
return { agent, balance: (0, import_viem2.parseUnits)(tokenBalance.amount, decimals) };
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
function planProportionalShares(balances, requested, totalAvailable) {
|
|
1976
|
-
const plans = balances.map(({ agent, balance }) => ({
|
|
1977
|
-
agent,
|
|
1978
|
-
balance,
|
|
1979
|
-
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
1980
|
-
}));
|
|
1981
|
-
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
1982
|
-
let remainder = requested - assigned;
|
|
1983
|
-
const byHeadroom = [...plans].sort((a, b) => {
|
|
1984
|
-
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
1985
|
-
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
1986
|
-
});
|
|
1987
|
-
for (const p of byHeadroom) {
|
|
1988
|
-
if (remainder === 0n) break;
|
|
1989
|
-
const headroom = p.balance - p.planned;
|
|
1990
|
-
if (headroom <= 0n) continue;
|
|
1991
|
-
const take = headroom < remainder ? headroom : remainder;
|
|
1992
|
-
p.planned += take;
|
|
1993
|
-
remainder -= take;
|
|
1994
|
-
}
|
|
1995
|
-
return plans;
|
|
1996
|
-
}
|
|
1997
|
-
function planDisabledDrain(disabled, requested) {
|
|
1998
|
-
const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
1999
|
-
const plans = [];
|
|
2000
|
-
let remaining = requested;
|
|
2001
|
-
for (const { agent, balance } of sorted) {
|
|
2002
|
-
if (remaining === 0n) {
|
|
2003
|
-
plans.push({ agent, balance, planned: 0n });
|
|
2004
|
-
continue;
|
|
2005
|
-
}
|
|
2006
|
-
const take = balance < remaining ? balance : remaining;
|
|
2007
|
-
plans.push({ agent, balance, planned: take });
|
|
2008
|
-
remaining -= take;
|
|
2009
|
-
}
|
|
2010
|
-
return { plans, remaining };
|
|
2011
|
-
}
|
|
2012
|
-
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
2013
|
-
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
2014
|
-
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
2015
|
-
const totalHeadroom = candidates.reduce(
|
|
2016
|
-
(s, c) => s + (c.balance - c.planned),
|
|
2017
|
-
0n
|
|
2018
|
-
);
|
|
2019
|
-
if (totalHeadroom === 0n) return;
|
|
2020
|
-
let distributed = 0n;
|
|
2021
|
-
for (const c of candidates) {
|
|
2022
|
-
const headroom = c.balance - c.planned;
|
|
2023
|
-
const proportional = headroom * amount / totalHeadroom;
|
|
2024
|
-
const give = proportional > headroom ? headroom : proportional;
|
|
2025
|
-
c.planned += give;
|
|
2026
|
-
distributed += give;
|
|
2027
|
-
}
|
|
2028
|
-
let leftover = amount - distributed;
|
|
2029
|
-
for (const c of candidates) {
|
|
2030
|
-
if (leftover === 0n) break;
|
|
2031
|
-
const headroom = c.balance - c.planned;
|
|
2032
|
-
if (headroom <= 0n) continue;
|
|
2033
|
-
const take = headroom < leftover ? headroom : leftover;
|
|
2034
|
-
c.planned += take;
|
|
2035
|
-
leftover -= take;
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
function sumWithdrawnAmount(results) {
|
|
2039
|
-
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
// src/lib/helpers/account-apy-helper.ts
|
|
2043
|
-
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
2044
|
-
const sums = {};
|
|
2045
|
-
const weights = {};
|
|
2046
|
-
for (const id of Object.keys(agentApys)) {
|
|
2047
|
-
const cells = agentApys[id].apyByChainAndAsset;
|
|
2048
|
-
const balance = agentBalances[id] ?? 0;
|
|
2049
|
-
if (!cells || balance <= 0) continue;
|
|
2050
|
-
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
2051
|
-
if (!perAsset) continue;
|
|
2052
|
-
const chainId = Number(chainKey);
|
|
2053
|
-
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
2054
|
-
const apy = Number(apyValue ?? 0);
|
|
2055
|
-
if (apy === 0) continue;
|
|
2056
|
-
sums[chainId] ??= {};
|
|
2057
|
-
weights[chainId] ??= {};
|
|
2058
|
-
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
2059
|
-
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
const out = {};
|
|
2064
|
-
for (const chainKey of Object.keys(sums)) {
|
|
2065
|
-
const chainId = Number(chainKey);
|
|
2066
|
-
const perAssetOut = {};
|
|
2067
|
-
for (const asset of Object.keys(sums[chainId])) {
|
|
2068
|
-
const w = weights[chainId][asset];
|
|
2069
|
-
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
2070
|
-
}
|
|
2071
|
-
if (Object.keys(perAssetOut).length > 0) {
|
|
2072
|
-
out[chainId] = perAssetOut;
|
|
2073
|
-
}
|
|
2074
1902
|
}
|
|
2075
|
-
return out;
|
|
2076
1903
|
}
|
|
2077
1904
|
|
|
2078
|
-
// src/client.ts
|
|
2079
|
-
var import_viem6 = require("viem");
|
|
2080
|
-
var import_chains2 = require("viem/chains");
|
|
2081
|
-
|
|
2082
1905
|
// src/lib/transfer-auth.ts
|
|
2083
|
-
var
|
|
1906
|
+
var import_viem2 = require("viem");
|
|
2084
1907
|
var ERC20_META_ABI = [
|
|
2085
1908
|
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
|
|
2086
1909
|
{ type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
|
|
@@ -2102,30 +1925,31 @@ function buildTransferWithAuthorizationTypedData(input) {
|
|
|
2102
1925
|
message: input.message
|
|
2103
1926
|
};
|
|
2104
1927
|
}
|
|
2105
|
-
async function readTokenMeta(publicClient,
|
|
1928
|
+
async function readTokenMeta(publicClient, token2) {
|
|
2106
1929
|
const [tokenName, tokenVersion] = await Promise.all([
|
|
2107
|
-
publicClient.readContract({ address:
|
|
2108
|
-
publicClient.readContract({ address:
|
|
1930
|
+
publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "name" }),
|
|
1931
|
+
publicClient.readContract({ address: token2, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
|
|
2109
1932
|
]);
|
|
2110
1933
|
return { tokenName, tokenVersion };
|
|
2111
1934
|
}
|
|
2112
1935
|
function randomAuthNonce() {
|
|
2113
1936
|
const bytes = new Uint8Array(32);
|
|
2114
1937
|
globalThis.crypto.getRandomValues(bytes);
|
|
2115
|
-
return (0,
|
|
1938
|
+
return (0, import_viem2.bytesToHex)(bytes);
|
|
2116
1939
|
}
|
|
2117
1940
|
|
|
2118
1941
|
// src/lib/sponsor-client.ts
|
|
2119
|
-
var
|
|
1942
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2120
1943
|
async function postSponsorTransferAuth(input) {
|
|
2121
|
-
const
|
|
1944
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2122
1945
|
let res;
|
|
2123
1946
|
try {
|
|
2124
|
-
res = await fetch(`${
|
|
1947
|
+
res = await fetch(`${base5}/api/v1/sponsor/erc20-transfer-auth`, {
|
|
2125
1948
|
method: "POST",
|
|
2126
1949
|
headers: {
|
|
2127
1950
|
"content-type": "application/json",
|
|
2128
|
-
"x-owney-api-key": input.apiKey
|
|
1951
|
+
"x-owney-api-key": input.apiKey,
|
|
1952
|
+
...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
|
|
2129
1953
|
},
|
|
2130
1954
|
body: JSON.stringify(input.body)
|
|
2131
1955
|
});
|
|
@@ -2158,14 +1982,15 @@ async function postSponsorTransferAuth(input) {
|
|
|
2158
1982
|
return parsed.data;
|
|
2159
1983
|
}
|
|
2160
1984
|
async function postSponsorPermit2Transfer(input) {
|
|
2161
|
-
const
|
|
1985
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2162
1986
|
let res;
|
|
2163
1987
|
try {
|
|
2164
|
-
res = await fetch(`${
|
|
1988
|
+
res = await fetch(`${base5}/api/v1/sponsor/permit2-transfer`, {
|
|
2165
1989
|
method: "POST",
|
|
2166
1990
|
headers: {
|
|
2167
1991
|
"content-type": "application/json",
|
|
2168
|
-
"x-owney-api-key": input.apiKey
|
|
1992
|
+
"x-owney-api-key": input.apiKey,
|
|
1993
|
+
...input.yieldseekerSignature ? { "x-signature": input.yieldseekerSignature } : {}
|
|
2169
1994
|
},
|
|
2170
1995
|
body: JSON.stringify(input.body)
|
|
2171
1996
|
});
|
|
@@ -2196,11 +2021,11 @@ async function postSponsorPermit2Transfer(input) {
|
|
|
2196
2021
|
return parsed.data;
|
|
2197
2022
|
}
|
|
2198
2023
|
async function getSponsorRelayerAddress(input) {
|
|
2199
|
-
const
|
|
2024
|
+
const base5 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2200
2025
|
let res;
|
|
2201
2026
|
try {
|
|
2202
2027
|
res = await fetch(
|
|
2203
|
-
`${
|
|
2028
|
+
`${base5}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2204
2029
|
{
|
|
2205
2030
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2206
2031
|
}
|
|
@@ -2233,7 +2058,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
2233
2058
|
}
|
|
2234
2059
|
|
|
2235
2060
|
// src/lib/permit2.ts
|
|
2236
|
-
var
|
|
2061
|
+
var import_viem3 = require("viem");
|
|
2237
2062
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2238
2063
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2239
2064
|
var ERC20_ALLOWANCE_ABI = [
|
|
@@ -2291,223 +2116,1391 @@ function buildPermitTransferFromTypedData(input) {
|
|
|
2291
2116
|
function randomPermit2Nonce() {
|
|
2292
2117
|
const bytes = new Uint8Array(32);
|
|
2293
2118
|
globalThis.crypto.getRandomValues(bytes);
|
|
2294
|
-
return BigInt((0,
|
|
2119
|
+
return BigInt((0, import_viem3.bytesToHex)(bytes));
|
|
2295
2120
|
}
|
|
2296
|
-
async function readPermit2Allowance(publicClient,
|
|
2121
|
+
async function readPermit2Allowance(publicClient, token2, owner) {
|
|
2297
2122
|
return publicClient.readContract({
|
|
2298
|
-
address:
|
|
2123
|
+
address: token2,
|
|
2299
2124
|
abi: ERC20_ALLOWANCE_ABI,
|
|
2300
2125
|
functionName: "allowance",
|
|
2301
2126
|
args: [owner, PERMIT2_ADDRESS]
|
|
2302
2127
|
});
|
|
2303
2128
|
}
|
|
2304
|
-
async function readErc20Balance(publicClient,
|
|
2305
|
-
return publicClient.readContract({
|
|
2306
|
-
address:
|
|
2307
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
2308
|
-
functionName: "balanceOf",
|
|
2309
|
-
args: [owner]
|
|
2129
|
+
async function readErc20Balance(publicClient, token2, owner) {
|
|
2130
|
+
return publicClient.readContract({
|
|
2131
|
+
address: token2,
|
|
2132
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
2133
|
+
functionName: "balanceOf",
|
|
2134
|
+
args: [owner]
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/lib/sponsored-deposit.ts
|
|
2139
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
2140
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
2141
|
+
function provideDepositVerificationContext(callback, context) {
|
|
2142
|
+
callback[verificationSetter]?.(context);
|
|
2143
|
+
}
|
|
2144
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
2145
|
+
let nextVerification;
|
|
2146
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
2147
|
+
const verification = nextVerification;
|
|
2148
|
+
nextVerification = void 0;
|
|
2149
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
2150
|
+
};
|
|
2151
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
2152
|
+
value: (context) => {
|
|
2153
|
+
nextVerification = context;
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
return callback;
|
|
2157
|
+
}
|
|
2158
|
+
function makeSponsoredDepositCallback(deps) {
|
|
2159
|
+
const post = deps.httpPost ?? postSponsorTransferAuth;
|
|
2160
|
+
return makeVerificationAwareDepositCallback(
|
|
2161
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
2162
|
+
const cid = chainId;
|
|
2163
|
+
const token2 = deps.tokenAddressByChain[cid];
|
|
2164
|
+
if (!token2) {
|
|
2165
|
+
throw new OwneyError(
|
|
2166
|
+
"CHAIN_UNSUPPORTED",
|
|
2167
|
+
`No sponsored token configured for chain ${chainId}`
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
const pub = deps.getPublicClient(cid);
|
|
2171
|
+
const wallet = deps.getWalletClient(cid);
|
|
2172
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
2173
|
+
try {
|
|
2174
|
+
const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
|
|
2175
|
+
if (balance < BigInt(amount)) {
|
|
2176
|
+
throw new OwneyError(
|
|
2177
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
2178
|
+
"Insufficient balance for this deposit.",
|
|
2179
|
+
{ token: token2, chainId: cid, balance: balance.toString(), amount }
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
} catch (err) {
|
|
2183
|
+
if (err instanceof OwneyError) throw err;
|
|
2184
|
+
console.warn(
|
|
2185
|
+
"[owney-sdk] Deposit balance pre-check failed (non-fatal):",
|
|
2186
|
+
err instanceof Error ? err.message : String(err)
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
const { tokenName, tokenVersion } = await readTokenMeta(pub, token2);
|
|
2190
|
+
const validAfter = 0n;
|
|
2191
|
+
const validBefore = BigInt(
|
|
2192
|
+
Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS
|
|
2193
|
+
);
|
|
2194
|
+
const nonce = randomAuthNonce();
|
|
2195
|
+
const typedData = buildTransferWithAuthorizationTypedData({
|
|
2196
|
+
token: token2,
|
|
2197
|
+
chainId: cid,
|
|
2198
|
+
tokenName,
|
|
2199
|
+
tokenVersion,
|
|
2200
|
+
message: {
|
|
2201
|
+
from: deps.ownerAddress,
|
|
2202
|
+
to: smartWallet,
|
|
2203
|
+
value: BigInt(amount),
|
|
2204
|
+
validAfter,
|
|
2205
|
+
validBefore,
|
|
2206
|
+
nonce
|
|
2207
|
+
}
|
|
2208
|
+
});
|
|
2209
|
+
const authSignature = await wallet.signTypedData({
|
|
2210
|
+
account: deps.ownerAddress,
|
|
2211
|
+
...typedData
|
|
2212
|
+
});
|
|
2213
|
+
deps.onApproved?.();
|
|
2214
|
+
const result = await post({
|
|
2215
|
+
baseUrl: deps.baseUrl,
|
|
2216
|
+
apiKey: deps.apiKey,
|
|
2217
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
2218
|
+
body: {
|
|
2219
|
+
chainId: cid,
|
|
2220
|
+
token: token2,
|
|
2221
|
+
from: deps.ownerAddress,
|
|
2222
|
+
to: smartWallet,
|
|
2223
|
+
value: amount,
|
|
2224
|
+
validAfter: validAfter.toString(),
|
|
2225
|
+
validBefore: validBefore.toString(),
|
|
2226
|
+
nonce,
|
|
2227
|
+
authSignature,
|
|
2228
|
+
tokenName,
|
|
2229
|
+
tokenVersion
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
return result.txHash;
|
|
2233
|
+
}
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2238
|
+
var import_siwe = require("siwe");
|
|
2239
|
+
var import_viem4 = require("viem");
|
|
2240
|
+
var import_chains2 = require("viem/chains");
|
|
2241
|
+
|
|
2242
|
+
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
2243
|
+
var KEY_PREFIX2 = "owney.yieldseeker.session";
|
|
2244
|
+
var storage2 = () => {
|
|
2245
|
+
if (typeof window === "undefined") return null;
|
|
2246
|
+
try {
|
|
2247
|
+
return window.localStorage;
|
|
2248
|
+
} catch {
|
|
2249
|
+
return null;
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
var buildKey2 = (address, chainId) => `${KEY_PREFIX2}:${address.toLowerCase()}:${chainId}`;
|
|
2253
|
+
var memorySessions2 = /* @__PURE__ */ new Map();
|
|
2254
|
+
var isValidSession = (session) => {
|
|
2255
|
+
if (!session?.token) return false;
|
|
2256
|
+
try {
|
|
2257
|
+
const parsed = JSON.parse(atob(session.token));
|
|
2258
|
+
return typeof parsed.message === "string" && typeof parsed.signature === "string" && parsed.signature.startsWith("0x");
|
|
2259
|
+
} catch {
|
|
2260
|
+
return false;
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2263
|
+
var readYieldseekerSession = (address, chainId) => {
|
|
2264
|
+
if (typeof window === "undefined") return null;
|
|
2265
|
+
const key2 = buildKey2(address, chainId);
|
|
2266
|
+
const store = storage2();
|
|
2267
|
+
let raw = null;
|
|
2268
|
+
try {
|
|
2269
|
+
raw = store?.getItem(key2) ?? null;
|
|
2270
|
+
} catch {
|
|
2271
|
+
raw = null;
|
|
2272
|
+
}
|
|
2273
|
+
if (raw) {
|
|
2274
|
+
try {
|
|
2275
|
+
const parsed = JSON.parse(raw);
|
|
2276
|
+
if (isValidSession(parsed)) return parsed.token;
|
|
2277
|
+
} catch {
|
|
2278
|
+
}
|
|
2279
|
+
memorySessions2.delete(key2);
|
|
2280
|
+
try {
|
|
2281
|
+
store?.removeItem(key2);
|
|
2282
|
+
} catch {
|
|
2283
|
+
}
|
|
2284
|
+
return null;
|
|
2285
|
+
}
|
|
2286
|
+
const cached = memorySessions2.get(key2);
|
|
2287
|
+
if (isValidSession(cached)) return cached.token;
|
|
2288
|
+
if (cached) memorySessions2.delete(key2);
|
|
2289
|
+
return null;
|
|
2290
|
+
};
|
|
2291
|
+
var writeYieldseekerSession = (address, chainId, token2) => {
|
|
2292
|
+
if (typeof window === "undefined") return;
|
|
2293
|
+
const session = { token: token2 };
|
|
2294
|
+
if (!isValidSession(session)) return;
|
|
2295
|
+
const key2 = buildKey2(address, chainId);
|
|
2296
|
+
memorySessions2.set(key2, session);
|
|
2297
|
+
const store = storage2();
|
|
2298
|
+
try {
|
|
2299
|
+
store?.setItem(key2, JSON.stringify(session));
|
|
2300
|
+
} catch {
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
var clearYieldseekerSession = (address, chainId) => {
|
|
2304
|
+
const key2 = buildKey2(address, chainId);
|
|
2305
|
+
memorySessions2.delete(key2);
|
|
2306
|
+
const store = storage2();
|
|
2307
|
+
try {
|
|
2308
|
+
store?.removeItem(key2);
|
|
2309
|
+
} catch {
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
2312
|
+
|
|
2313
|
+
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2314
|
+
function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
2315
|
+
return new import_siwe.SiweMessage({
|
|
2316
|
+
domain: "yieldseeker.xyz",
|
|
2317
|
+
address: (0, import_viem4.getAddress)(address),
|
|
2318
|
+
uri: "https://yieldseeker.xyz",
|
|
2319
|
+
version: "1",
|
|
2320
|
+
chainId,
|
|
2321
|
+
nonce: (dependencies.nonce ?? import_siwe.generateNonce)(),
|
|
2322
|
+
issuedAt: (dependencies.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
2323
|
+
}).prepareMessage();
|
|
2324
|
+
}
|
|
2325
|
+
function encodeYieldseekerAuthToken(token2) {
|
|
2326
|
+
const bytes = new TextEncoder().encode(JSON.stringify(token2));
|
|
2327
|
+
let binary = "";
|
|
2328
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
2329
|
+
return btoa(binary);
|
|
2330
|
+
}
|
|
2331
|
+
var YieldseekerAuth = class {
|
|
2332
|
+
constructor(dependencies = {}) {
|
|
2333
|
+
this.dependencies = dependencies;
|
|
2334
|
+
}
|
|
2335
|
+
dependencies;
|
|
2336
|
+
tokens = /* @__PURE__ */ new Map();
|
|
2337
|
+
pending = /* @__PURE__ */ new Map();
|
|
2338
|
+
scopes = /* @__PURE__ */ new Map();
|
|
2339
|
+
key(state, chainId) {
|
|
2340
|
+
return `${state.walletAddress.toLowerCase()}:${chainId}`;
|
|
2341
|
+
}
|
|
2342
|
+
async getToken(state, chainId) {
|
|
2343
|
+
const key2 = this.key(state, chainId);
|
|
2344
|
+
const scope = { address: state.walletAddress, chainId };
|
|
2345
|
+
this.scopes.set(key2, scope);
|
|
2346
|
+
const cached = this.tokens.get(key2);
|
|
2347
|
+
if (cached) return cached;
|
|
2348
|
+
const persisted = readYieldseekerSession(scope.address, scope.chainId);
|
|
2349
|
+
if (persisted) {
|
|
2350
|
+
this.tokens.set(key2, persisted);
|
|
2351
|
+
return persisted;
|
|
2352
|
+
}
|
|
2353
|
+
const inFlight = this.pending.get(key2);
|
|
2354
|
+
if (inFlight) return inFlight;
|
|
2355
|
+
const request = this.sign(state, chainId).then((token2) => {
|
|
2356
|
+
this.tokens.set(key2, token2);
|
|
2357
|
+
writeYieldseekerSession(scope.address, scope.chainId, token2);
|
|
2358
|
+
return token2;
|
|
2359
|
+
});
|
|
2360
|
+
this.pending.set(key2, request);
|
|
2361
|
+
try {
|
|
2362
|
+
return await request;
|
|
2363
|
+
} finally {
|
|
2364
|
+
this.pending.delete(key2);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
clear(state, chainId) {
|
|
2368
|
+
if (!state || chainId === void 0) {
|
|
2369
|
+
for (const scope of this.scopes.values()) {
|
|
2370
|
+
clearYieldseekerSession(scope.address, scope.chainId);
|
|
2371
|
+
}
|
|
2372
|
+
this.tokens.clear();
|
|
2373
|
+
this.pending.clear();
|
|
2374
|
+
this.scopes.clear();
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
const key2 = this.key(state, chainId);
|
|
2378
|
+
this.tokens.delete(key2);
|
|
2379
|
+
this.pending.delete(key2);
|
|
2380
|
+
this.scopes.delete(key2);
|
|
2381
|
+
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2382
|
+
}
|
|
2383
|
+
async sign(state, chainId) {
|
|
2384
|
+
const account = (0, import_viem4.getAddress)(state.walletAddress);
|
|
2385
|
+
const publicClient = (0, import_viem4.createPublicClient)({
|
|
2386
|
+
chain: import_chains2.base,
|
|
2387
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2388
|
+
});
|
|
2389
|
+
const walletClient = (0, import_viem4.createWalletClient)({
|
|
2390
|
+
account,
|
|
2391
|
+
chain: import_chains2.base,
|
|
2392
|
+
transport: (0, import_viem4.custom)(state.provider)
|
|
2393
|
+
});
|
|
2394
|
+
await ensureWalletOnChain(
|
|
2395
|
+
publicClient,
|
|
2396
|
+
walletClient,
|
|
2397
|
+
8453
|
|
2398
|
+
);
|
|
2399
|
+
const message = createYieldseekerSiweMessage(
|
|
2400
|
+
account,
|
|
2401
|
+
chainId,
|
|
2402
|
+
this.dependencies
|
|
2403
|
+
);
|
|
2404
|
+
const signature = await walletClient.signMessage({ account, message });
|
|
2405
|
+
return encodeYieldseekerAuthToken({ message, signature });
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
|
|
2409
|
+
// src/agents/yieldseeker/yieldseeker.client.ts
|
|
2410
|
+
var DEFAULT_ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2411
|
+
function getYieldseekerProxyBaseUrl(routingApiBaseUrl = DEFAULT_ROUTING_API_BASE_URL) {
|
|
2412
|
+
return `${routingApiBaseUrl.replace(/\/$/, "")}/api/v1/agent/yieldseeker`;
|
|
2413
|
+
}
|
|
2414
|
+
var YIELDSEEKER_API_BASE_URL = getYieldseekerProxyBaseUrl();
|
|
2415
|
+
var YieldseekerApiError = class extends Error {
|
|
2416
|
+
constructor(status, providerCode, responseFields) {
|
|
2417
|
+
super(`Yieldseeker request failed (${status}): ${providerCode}`);
|
|
2418
|
+
this.status = status;
|
|
2419
|
+
this.providerCode = providerCode;
|
|
2420
|
+
this.responseFields = responseFields;
|
|
2421
|
+
this.name = "YieldseekerApiError";
|
|
2422
|
+
}
|
|
2423
|
+
status;
|
|
2424
|
+
providerCode;
|
|
2425
|
+
responseFields;
|
|
2426
|
+
get isAuthenticationError() {
|
|
2427
|
+
return this.status === 401 || this.status === 403;
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
function providerError(body, fallback) {
|
|
2431
|
+
if (!body || typeof body !== "object") return { code: fallback };
|
|
2432
|
+
const record = body;
|
|
2433
|
+
return {
|
|
2434
|
+
code: typeof record.message === "string" ? record.message : fallback,
|
|
2435
|
+
fields: record.fields && typeof record.fields === "object" ? record.fields : void 0
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
var YieldseekerApiClient = class {
|
|
2439
|
+
constructor(owneyApiKey, baseUrl = YIELDSEEKER_API_BASE_URL, fetchFn = fetch) {
|
|
2440
|
+
this.owneyApiKey = owneyApiKey;
|
|
2441
|
+
this.baseUrl = baseUrl;
|
|
2442
|
+
this.fetchFn = fetchFn;
|
|
2443
|
+
}
|
|
2444
|
+
owneyApiKey;
|
|
2445
|
+
baseUrl;
|
|
2446
|
+
fetchFn;
|
|
2447
|
+
async request(path, options = {}) {
|
|
2448
|
+
const controller = new AbortController();
|
|
2449
|
+
const timer = setTimeout(
|
|
2450
|
+
() => controller.abort(),
|
|
2451
|
+
options.timeoutMs ?? 15e3
|
|
2452
|
+
);
|
|
2453
|
+
try {
|
|
2454
|
+
const response = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
2455
|
+
method: options.method ?? "GET",
|
|
2456
|
+
headers: {
|
|
2457
|
+
"Content-Type": "application/json",
|
|
2458
|
+
"x-owney-api-key": this.owneyApiKey,
|
|
2459
|
+
...options.signature ? { "X-Signature": options.signature } : {}
|
|
2460
|
+
},
|
|
2461
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2462
|
+
signal: controller.signal
|
|
2463
|
+
});
|
|
2464
|
+
const payload = await response.json().catch(() => null);
|
|
2465
|
+
if (!response.ok) {
|
|
2466
|
+
const error = providerError(payload, `HTTP_${response.status}`);
|
|
2467
|
+
throw new YieldseekerApiError(
|
|
2468
|
+
response.status,
|
|
2469
|
+
error.code,
|
|
2470
|
+
error.fields
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
if (payload && typeof payload === "object" && payload.success === true && "data" in payload) {
|
|
2474
|
+
return payload.data;
|
|
2475
|
+
}
|
|
2476
|
+
return payload;
|
|
2477
|
+
} catch (error) {
|
|
2478
|
+
if (error instanceof YieldseekerApiError) throw error;
|
|
2479
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2480
|
+
throw new YieldseekerApiError(408, "REQUEST_TIMEOUT");
|
|
2481
|
+
}
|
|
2482
|
+
throw new YieldseekerApiError(0, "NETWORK_ERROR", {
|
|
2483
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
2484
|
+
});
|
|
2485
|
+
} finally {
|
|
2486
|
+
clearTimeout(timer);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
};
|
|
2490
|
+
|
|
2491
|
+
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2492
|
+
var import_viem5 = require("viem");
|
|
2493
|
+
var REBALANCE_AMOUNT_DECIMALS = {
|
|
2494
|
+
USDC: 6,
|
|
2495
|
+
WETH: 18
|
|
2496
|
+
};
|
|
2497
|
+
function rebalanceAmount(value, tokenSymbol) {
|
|
2498
|
+
const decimals = REBALANCE_AMOUNT_DECIMALS[tokenSymbol.toUpperCase()];
|
|
2499
|
+
if (decimals === void 0) {
|
|
2500
|
+
return invalid(
|
|
2501
|
+
"history",
|
|
2502
|
+
`unsupported rebalance token ${tokenSymbol || "<empty>"}`
|
|
2503
|
+
);
|
|
2504
|
+
}
|
|
2505
|
+
try {
|
|
2506
|
+
return (0, import_viem5.formatUnits)(BigInt(value), decimals);
|
|
2507
|
+
} catch {
|
|
2508
|
+
return invalid("history", `invalid rebalance amount ${value}`);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
function invalid(endpoint, detail) {
|
|
2512
|
+
throw new OwneyError(
|
|
2513
|
+
"AGENT_INVALID_RESPONSE",
|
|
2514
|
+
`Yieldseeker returned an invalid ${endpoint} response: ${detail}.`,
|
|
2515
|
+
{ endpoint, detail },
|
|
2516
|
+
"yieldseeker"
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
function requireObject(value, endpoint) {
|
|
2520
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2521
|
+
return invalid(endpoint, "expected an object");
|
|
2522
|
+
}
|
|
2523
|
+
return value;
|
|
2524
|
+
}
|
|
2525
|
+
function requireEnvelope(value, key2, endpoint) {
|
|
2526
|
+
const root = requireObject(value, endpoint);
|
|
2527
|
+
return requireObject(root[key2], endpoint);
|
|
2528
|
+
}
|
|
2529
|
+
function token(value) {
|
|
2530
|
+
return {
|
|
2531
|
+
chain: value.chain,
|
|
2532
|
+
chainId: value.chainId,
|
|
2533
|
+
asset: value.asset,
|
|
2534
|
+
amount: String(value.amount)
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
function position(value) {
|
|
2538
|
+
return {
|
|
2539
|
+
chain: String(value.chain),
|
|
2540
|
+
protocol: String(value.protocol),
|
|
2541
|
+
...value.protocolId ? { protocolId: value.protocolId } : {},
|
|
2542
|
+
...value.pool ? { pool: value.pool } : {},
|
|
2543
|
+
asset: String(value.asset),
|
|
2544
|
+
amount: String(value.amount),
|
|
2545
|
+
...value.amountRaw !== void 0 ? { amountRaw: value.amountRaw } : {},
|
|
2546
|
+
...typeof value.apy === "number" ? { apy: value.apy } : {},
|
|
2547
|
+
...typeof value.tvl === "number" ? { tvl: value.tvl } : {},
|
|
2548
|
+
...typeof value.liquidity === "number" ? { liquidity: value.liquidity } : {}
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
function mapApyMatrix(matrix) {
|
|
2552
|
+
const mapped = {};
|
|
2553
|
+
for (const [chain, assets] of Object.entries(matrix ?? {})) {
|
|
2554
|
+
const chainId = Number(chain);
|
|
2555
|
+
if (chainId !== 8453 || !assets || typeof assets !== "object") continue;
|
|
2556
|
+
mapped[8453] = {};
|
|
2557
|
+
for (const [asset, apy] of Object.entries(assets)) {
|
|
2558
|
+
if ((asset === "USDC" || asset === "WETH") && typeof apy === "number") {
|
|
2559
|
+
mapped[8453][asset] = apy;
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return mapped;
|
|
2564
|
+
}
|
|
2565
|
+
function mapYieldseekerProfile(value) {
|
|
2566
|
+
const profile = requireEnvelope(
|
|
2567
|
+
value,
|
|
2568
|
+
"profile",
|
|
2569
|
+
"profile"
|
|
2570
|
+
);
|
|
2571
|
+
if (typeof profile.address !== "string" || typeof profile.smartWallet !== "string" || !Array.isArray(profile.chains) || typeof profile.hasActiveSessionKey !== "boolean" || !Array.isArray(profile.protocols)) {
|
|
2572
|
+
return invalid("profile", "missing required fields");
|
|
2573
|
+
}
|
|
2574
|
+
return {
|
|
2575
|
+
address: profile.address,
|
|
2576
|
+
smartWallet: profile.smartWallet,
|
|
2577
|
+
chains: profile.chains.map(Number),
|
|
2578
|
+
hasActiveSessionKey: profile.hasActiveSessionKey,
|
|
2579
|
+
protocols: profile.protocols.map(String)
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
function mapYieldseekerBalances(value) {
|
|
2583
|
+
const balances = requireEnvelope(
|
|
2584
|
+
value,
|
|
2585
|
+
"balances",
|
|
2586
|
+
"balances"
|
|
2587
|
+
);
|
|
2588
|
+
if (typeof balances.smartWallet !== "string" || typeof balances.totalBalance !== "string" || typeof balances.totalBalanceAsset !== "string" || !Array.isArray(balances.tokens) || !Array.isArray(balances.positions)) {
|
|
2589
|
+
return invalid("balances", "missing required fields");
|
|
2590
|
+
}
|
|
2591
|
+
return {
|
|
2592
|
+
smartWallet: balances.smartWallet,
|
|
2593
|
+
totalBalance: balances.totalBalance,
|
|
2594
|
+
totalBalanceAsset: balances.totalBalanceAsset.toLowerCase(),
|
|
2595
|
+
tokens: balances.tokens.map(token),
|
|
2596
|
+
positions: balances.positions.map(position)
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
function mapYieldseekerEarnings(value) {
|
|
2600
|
+
const earnings = requireEnvelope(
|
|
2601
|
+
value,
|
|
2602
|
+
"earnings",
|
|
2603
|
+
"earnings"
|
|
2604
|
+
);
|
|
2605
|
+
if (typeof earnings.smartWallet !== "string" || typeof earnings.lifetimeEarnings !== "number" || !Array.isArray(earnings.tokens)) {
|
|
2606
|
+
return invalid("earnings", "missing required fields");
|
|
2607
|
+
}
|
|
2608
|
+
return {
|
|
2609
|
+
smartWallet: earnings.smartWallet,
|
|
2610
|
+
lifetimeEarnings: earnings.lifetimeEarnings,
|
|
2611
|
+
tokens: earnings.tokens.map(token)
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
function mapYieldseekerApy(value) {
|
|
2615
|
+
const apy = requireEnvelope(
|
|
2616
|
+
value,
|
|
2617
|
+
"apy",
|
|
2618
|
+
"apy"
|
|
2619
|
+
);
|
|
2620
|
+
if (typeof apy.walletAddress !== "string" || typeof apy.weightedApyAfterFee !== "number" || !apy.apyByChainAndAsset || typeof apy.apyByChainAndAsset !== "object" || !Array.isArray(apy.history)) {
|
|
2621
|
+
return invalid("apy", "missing required fields");
|
|
2622
|
+
}
|
|
2623
|
+
return {
|
|
2624
|
+
walletAddress: apy.walletAddress,
|
|
2625
|
+
weightedApyAfterFee: apy.weightedApyAfterFee,
|
|
2626
|
+
apyByChainAndAsset: mapApyMatrix(apy.apyByChainAndAsset),
|
|
2627
|
+
history: apy.history.map((point) => ({
|
|
2628
|
+
date: String(point.date),
|
|
2629
|
+
apy: Number(point.apy)
|
|
2630
|
+
}))
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
function historyAction(value) {
|
|
2634
|
+
const normalized = value.trim().toLowerCase();
|
|
2635
|
+
if (normalized === "rebalance") return "Rebalance";
|
|
2636
|
+
if (normalized === "deposit") return "Deposit";
|
|
2637
|
+
if (normalized === "top up" || normalized === "topup") return "Top up";
|
|
2638
|
+
if (normalized === "withdraw" || normalized === "withdrawal")
|
|
2639
|
+
return "Withdraw";
|
|
2640
|
+
if (normalized === "earned" || normalized === "earnings") return "Earned";
|
|
2641
|
+
return invalid("history", `unsupported action ${value}`);
|
|
2642
|
+
}
|
|
2643
|
+
function historyItem(value) {
|
|
2644
|
+
return {
|
|
2645
|
+
agent: "yieldseeker",
|
|
2646
|
+
action: historyAction(value.action),
|
|
2647
|
+
date: String(value.date),
|
|
2648
|
+
oldApy: value.oldApy === null ? null : String(value.oldApy),
|
|
2649
|
+
newApy: value.newApy === null ? null : String(value.newApy),
|
|
2650
|
+
transactions: (value.transactions ?? []).map((transaction) => ({
|
|
2651
|
+
txHashes: transaction.txHashes.map(String),
|
|
2652
|
+
...typeof transaction.chainId === "number" ? { chainId: transaction.chainId } : {},
|
|
2653
|
+
...typeof transaction.tokenSymbol === "string" ? { tokenSymbol: transaction.tokenSymbol } : {},
|
|
2654
|
+
...typeof transaction.amount === "string" ? { amount: transaction.amount } : {}
|
|
2655
|
+
})),
|
|
2656
|
+
rebalanceLog: (value.rebalanceLog ?? []).map((entry) => ({
|
|
2657
|
+
fromProtocol: String(entry.fromProtocol),
|
|
2658
|
+
toProtocol: String(entry.toProtocol),
|
|
2659
|
+
...entry.fromPool ? { fromPool: entry.fromPool } : {},
|
|
2660
|
+
...entry.toPool ? { toPool: entry.toPool } : {},
|
|
2661
|
+
tokenSymbol: String(entry.tokenSymbol),
|
|
2662
|
+
// Unlike Yieldseeker's other read-response amounts, live history
|
|
2663
|
+
// rebalance amounts are returned in the token's smallest unit. Normalize
|
|
2664
|
+
// them at the adapter boundary so every Owney consumer receives the
|
|
2665
|
+
// shared human-readable decimal shape.
|
|
2666
|
+
amount: rebalanceAmount(String(entry.amount), String(entry.tokenSymbol)),
|
|
2667
|
+
status: entry.status
|
|
2668
|
+
}))
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
function mapYieldseekerHistory(value) {
|
|
2672
|
+
const history = requireEnvelope(
|
|
2673
|
+
value,
|
|
2674
|
+
"history",
|
|
2675
|
+
"history"
|
|
2676
|
+
);
|
|
2677
|
+
if (!Array.isArray(history.data) || typeof history.hasMore !== "boolean") {
|
|
2678
|
+
return invalid("history", "missing required fields");
|
|
2679
|
+
}
|
|
2680
|
+
return {
|
|
2681
|
+
data: history.data.map(historyItem),
|
|
2682
|
+
hasMore: history.hasMore,
|
|
2683
|
+
...history.hasMore && typeof history.nextCursor === "string" ? { nextCursor: history.nextCursor } : {}
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2686
|
+
function mapYieldseekerAgentApy(value) {
|
|
2687
|
+
const agentApy = requireEnvelope(
|
|
2688
|
+
value,
|
|
2689
|
+
"agentApy",
|
|
2690
|
+
"agent APY"
|
|
2691
|
+
);
|
|
2692
|
+
if (typeof agentApy.averageApy !== "number") {
|
|
2693
|
+
return invalid("agent APY", "missing averageApy");
|
|
2694
|
+
}
|
|
2695
|
+
return {
|
|
2696
|
+
averageApy: agentApy.averageApy,
|
|
2697
|
+
...agentApy.detailedApys ? {
|
|
2698
|
+
detailedApys: {
|
|
2699
|
+
apyPerAsset: mapApyMatrix(agentApy.detailedApys.apyPerAsset)
|
|
2700
|
+
}
|
|
2701
|
+
} : {}
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2706
|
+
function query(params) {
|
|
2707
|
+
const search = new URLSearchParams();
|
|
2708
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
2709
|
+
if (value !== void 0) search.set(key2, String(value));
|
|
2710
|
+
}
|
|
2711
|
+
const encoded = search.toString();
|
|
2712
|
+
return encoded ? `?${encoded}` : "";
|
|
2713
|
+
}
|
|
2714
|
+
var YieldseekerAgent = class {
|
|
2715
|
+
id = "yieldseeker";
|
|
2716
|
+
balanceComposition = "tokens-plus-positions";
|
|
2717
|
+
supportedChainIds = [8453];
|
|
2718
|
+
supportedAssets = [
|
|
2719
|
+
{
|
|
2720
|
+
chainId: 8453,
|
|
2721
|
+
chain: "BASE",
|
|
2722
|
+
assets: [
|
|
2723
|
+
{ symbol: "USDC", minDepositAmount: "1" },
|
|
2724
|
+
{ symbol: "WETH", minDepositAmount: "1" }
|
|
2725
|
+
]
|
|
2726
|
+
}
|
|
2727
|
+
];
|
|
2728
|
+
api;
|
|
2729
|
+
auth;
|
|
2730
|
+
transactionExecutor;
|
|
2731
|
+
unwindReceiptWaiter;
|
|
2732
|
+
activated = /* @__PURE__ */ new Set();
|
|
2733
|
+
constructor(owneyApiKey, options = {}) {
|
|
2734
|
+
this.api = new YieldseekerApiClient(
|
|
2735
|
+
owneyApiKey,
|
|
2736
|
+
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
2737
|
+
options.fetchFn
|
|
2738
|
+
);
|
|
2739
|
+
this.auth = new YieldseekerAuth(options.auth);
|
|
2740
|
+
this.transactionExecutor = options.transactionExecutor;
|
|
2741
|
+
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
2742
|
+
}
|
|
2743
|
+
async disconnect() {
|
|
2744
|
+
this.auth.clear();
|
|
2745
|
+
this.activated.clear();
|
|
2746
|
+
}
|
|
2747
|
+
async activateAgent(state, chainId, asset) {
|
|
2748
|
+
this.assertChain(chainId);
|
|
2749
|
+
await this.auth.getToken(state, chainId);
|
|
2750
|
+
if (asset === "USDC" || asset === "WETH") {
|
|
2751
|
+
await this.ensureActivated(state, chainId, asset);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2755
|
+
this.assertChain(chainId);
|
|
2756
|
+
this.assertAsset(asset);
|
|
2757
|
+
if (BigInt(amount) <= 0n) {
|
|
2758
|
+
throw new OwneyError(
|
|
2759
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
2760
|
+
"Yieldseeker deposits must be greater than zero.",
|
|
2761
|
+
{ amount, minDepositAmount: "1" },
|
|
2762
|
+
this.id
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
await this.ensureActivated(state, chainId, asset);
|
|
2766
|
+
const response = await this.walletRequest(
|
|
2767
|
+
state,
|
|
2768
|
+
chainId,
|
|
2769
|
+
"/deposits",
|
|
2770
|
+
{ method: "POST", body: { chainId, asset, amount } }
|
|
2771
|
+
);
|
|
2772
|
+
const deposit = response?.deposit;
|
|
2773
|
+
if (!deposit?.transaction || typeof deposit.amount !== "string" || deposit.amount !== amount || typeof deposit.smartWallet !== "string" || !(0, import_viem6.isAddress)(deposit.smartWallet)) {
|
|
2774
|
+
throw this.invalidResponse("deposit");
|
|
2775
|
+
}
|
|
2776
|
+
if (depositCallback) {
|
|
2777
|
+
provideDepositVerificationContext(depositCallback, {
|
|
2778
|
+
agentId: "yieldseeker",
|
|
2779
|
+
// Re-read after /deposits so an authentication retry cannot forward
|
|
2780
|
+
// the stale token that Yieldseeker just rejected.
|
|
2781
|
+
signature: await this.auth.getToken(state, chainId)
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
const txHash = depositCallback ? await depositCallback(deposit.smartWallet, chainId, amount) : await this.submitTransaction(state, chainId, deposit.transaction);
|
|
2785
|
+
const confirmation = await this.walletRequest(
|
|
2786
|
+
state,
|
|
2787
|
+
chainId,
|
|
2788
|
+
"/deposits/confirm",
|
|
2789
|
+
{ method: "POST", body: { chainId, asset } }
|
|
2790
|
+
);
|
|
2791
|
+
if (typeof confirmation?.confirmation?.autoseekQueued !== "boolean") {
|
|
2792
|
+
throw this.invalidResponse("deposit confirmation", {
|
|
2793
|
+
transactionHash: txHash,
|
|
2794
|
+
transactionConfirmed: true
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
return {
|
|
2798
|
+
txHash,
|
|
2799
|
+
smartWallet: deposit.smartWallet,
|
|
2800
|
+
amount: deposit.amount
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
async withdraw(state, chainId, asset, amount) {
|
|
2804
|
+
this.assertChain(chainId);
|
|
2805
|
+
this.assertAsset(asset);
|
|
2806
|
+
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
2807
|
+
throw new OwneyError(
|
|
2808
|
+
"WITHDRAW_FAILED",
|
|
2809
|
+
"Yieldseeker withdrawals must be greater than zero.",
|
|
2810
|
+
{ amount },
|
|
2811
|
+
this.id
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
const balances = await this.getBalances(state, chainId);
|
|
2815
|
+
const decimals = asset === "USDC" ? 6 : 18;
|
|
2816
|
+
const targetAsset = asset.toUpperCase();
|
|
2817
|
+
const idle = balances.tokens.filter(
|
|
2818
|
+
(token2) => token2.chainId === chainId && token2.asset.toUpperCase() === targetAsset
|
|
2819
|
+
).reduce((total, token2) => total + (0, import_viem6.parseUnits)(token2.amount, decimals), 0n);
|
|
2820
|
+
const positions = (balances.positions ?? []).filter(
|
|
2821
|
+
(position2) => this.positionMatchesChain(position2.chain, chainId) && position2.asset.toUpperCase() === targetAsset
|
|
2822
|
+
).map((position2) => ({
|
|
2823
|
+
position: position2,
|
|
2824
|
+
amount: position2.amountRaw !== void 0 ? BigInt(position2.amountRaw) : (0, import_viem6.parseUnits)(position2.amount, decimals)
|
|
2825
|
+
}));
|
|
2826
|
+
const deployed = positions.reduce(
|
|
2827
|
+
(total, position2) => total + position2.amount,
|
|
2828
|
+
0n
|
|
2829
|
+
);
|
|
2830
|
+
const totalAvailable = idle + deployed;
|
|
2831
|
+
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
2832
|
+
if (requested > totalAvailable) {
|
|
2833
|
+
throw new OwneyError(
|
|
2834
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2835
|
+
`Requested withdrawal "${requested.toString()}" exceeds available Yieldseeker balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
2836
|
+
{
|
|
2837
|
+
asset,
|
|
2838
|
+
requested: requested.toString(),
|
|
2839
|
+
available: totalAvailable.toString()
|
|
2840
|
+
},
|
|
2841
|
+
this.id
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
const requiredFromPositions = requested > idle ? requested - idle : 0n;
|
|
2845
|
+
const unwindPlans = [];
|
|
2846
|
+
let remainingToUnwind = requiredFromPositions;
|
|
2847
|
+
for (const { position: position2, amount: positionAmount } of positions) {
|
|
2848
|
+
if (remainingToUnwind === 0n) break;
|
|
2849
|
+
if (positionAmount <= 0n) continue;
|
|
2850
|
+
if (!position2.protocolId || !(0, import_viem6.isAddress)(position2.protocolId)) {
|
|
2851
|
+
throw this.invalidResponse("position", {
|
|
2852
|
+
reason: "A deployed position is missing its vault address.",
|
|
2853
|
+
protocol: position2.protocol,
|
|
2854
|
+
pool: position2.pool
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2857
|
+
const unwindAmount = positionAmount < remainingToUnwind ? positionAmount : remainingToUnwind;
|
|
2858
|
+
unwindPlans.push({
|
|
2859
|
+
vaultAddress: (0, import_viem6.getAddress)(position2.protocolId),
|
|
2860
|
+
amount: unwindAmount
|
|
2861
|
+
});
|
|
2862
|
+
remainingToUnwind -= unwindAmount;
|
|
2863
|
+
}
|
|
2864
|
+
if (remainingToUnwind > 0n) {
|
|
2865
|
+
throw this.invalidResponse("balances", {
|
|
2866
|
+
reason: "Deployed position balances could not cover the unwind.",
|
|
2867
|
+
required: requiredFromPositions.toString(),
|
|
2868
|
+
planned: (requiredFromPositions - remainingToUnwind).toString()
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
for (const plan of unwindPlans) {
|
|
2872
|
+
const response2 = await this.walletRequest(
|
|
2873
|
+
state,
|
|
2874
|
+
chainId,
|
|
2875
|
+
"/positions/unwind",
|
|
2876
|
+
{
|
|
2877
|
+
method: "POST",
|
|
2878
|
+
body: {
|
|
2879
|
+
chainId,
|
|
2880
|
+
asset,
|
|
2881
|
+
vaultAddress: plan.vaultAddress,
|
|
2882
|
+
amount: plan.amount.toString()
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
);
|
|
2886
|
+
const transactionHash = response2?.unwind?.transactionHash;
|
|
2887
|
+
if (!this.isTransactionHash(transactionHash)) {
|
|
2888
|
+
throw this.invalidResponse("position unwind");
|
|
2889
|
+
}
|
|
2890
|
+
await this.waitForUnwindReceipt(state, chainId, transactionHash);
|
|
2891
|
+
}
|
|
2892
|
+
const response = await this.walletRequest(
|
|
2893
|
+
state,
|
|
2894
|
+
chainId,
|
|
2895
|
+
"/withdrawals",
|
|
2896
|
+
{
|
|
2897
|
+
method: "POST",
|
|
2898
|
+
body: { chainId, asset, ...amount !== void 0 ? { amount } : {} }
|
|
2899
|
+
}
|
|
2900
|
+
);
|
|
2901
|
+
const withdrawal = response?.withdrawal;
|
|
2902
|
+
if (!withdrawal?.transaction || typeof withdrawal.amount !== "string") {
|
|
2903
|
+
throw this.invalidResponse("withdrawal");
|
|
2904
|
+
}
|
|
2905
|
+
const txHash = await this.submitTransaction(
|
|
2906
|
+
state,
|
|
2907
|
+
chainId,
|
|
2908
|
+
withdrawal.transaction
|
|
2909
|
+
);
|
|
2910
|
+
return {
|
|
2911
|
+
txHash,
|
|
2912
|
+
type: amount === void 0 ? "full" : "partial",
|
|
2913
|
+
amount: withdrawal.amount
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
async getBalances(state, chainId) {
|
|
2917
|
+
return mapYieldseekerBalances(
|
|
2918
|
+
await this.walletRequest(
|
|
2919
|
+
state,
|
|
2920
|
+
chainId,
|
|
2921
|
+
`/balances${query({ chainId })}`
|
|
2922
|
+
)
|
|
2923
|
+
);
|
|
2924
|
+
}
|
|
2925
|
+
async getEarnings(state, chainId) {
|
|
2926
|
+
return mapYieldseekerEarnings(
|
|
2927
|
+
await this.walletRequest(
|
|
2928
|
+
state,
|
|
2929
|
+
chainId,
|
|
2930
|
+
`/earnings${query({ chainId })}`
|
|
2931
|
+
)
|
|
2932
|
+
);
|
|
2933
|
+
}
|
|
2934
|
+
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
2935
|
+
return mapYieldseekerApy(
|
|
2936
|
+
await this.walletRequest(
|
|
2937
|
+
state,
|
|
2938
|
+
chainId,
|
|
2939
|
+
`/apy${query({ chainId, days, tokenSymbol })}`
|
|
2940
|
+
)
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
async getHistory(state, chainId, options) {
|
|
2944
|
+
return mapYieldseekerHistory(
|
|
2945
|
+
await this.walletRequest(
|
|
2946
|
+
state,
|
|
2947
|
+
chainId,
|
|
2948
|
+
`/history${query({
|
|
2949
|
+
chainId,
|
|
2950
|
+
limit: options?.limit ?? 10,
|
|
2951
|
+
cursor: options?.cursor,
|
|
2952
|
+
fromDate: options?.fromDate,
|
|
2953
|
+
toDate: options?.toDate
|
|
2954
|
+
})}`
|
|
2955
|
+
)
|
|
2956
|
+
);
|
|
2957
|
+
}
|
|
2958
|
+
async getUserProfile(state, chainId) {
|
|
2959
|
+
return mapYieldseekerProfile(
|
|
2960
|
+
await this.walletRequest(state, chainId, `/profile${query({ chainId })}`)
|
|
2961
|
+
);
|
|
2962
|
+
}
|
|
2963
|
+
async getAgentApy(days, options) {
|
|
2964
|
+
this.assertOptionalChain(options?.chainId);
|
|
2965
|
+
return mapYieldseekerAgentApy(
|
|
2966
|
+
await this.api.request(
|
|
2967
|
+
`/agent/apy${query({
|
|
2968
|
+
days,
|
|
2969
|
+
tokenSymbol: options?.tokenSymbol,
|
|
2970
|
+
chainId: options?.chainId
|
|
2971
|
+
})}`
|
|
2972
|
+
)
|
|
2973
|
+
);
|
|
2974
|
+
}
|
|
2975
|
+
async ensureActivated(state, chainId, asset) {
|
|
2976
|
+
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${asset}`;
|
|
2977
|
+
if (this.activated.has(key2)) return;
|
|
2978
|
+
const response = await this.walletRequest(
|
|
2979
|
+
state,
|
|
2980
|
+
chainId,
|
|
2981
|
+
"/activation",
|
|
2982
|
+
{ method: "POST", body: { chainId, asset } }
|
|
2983
|
+
);
|
|
2984
|
+
if (typeof response?.activation?.smartWallet !== "string" || typeof response.activation.deployed !== "boolean" || typeof response.activation.hasActiveSessionKey !== "boolean") {
|
|
2985
|
+
throw this.invalidResponse("activation");
|
|
2986
|
+
}
|
|
2987
|
+
this.activated.add(key2);
|
|
2988
|
+
}
|
|
2989
|
+
async walletRequest(state, chainId, path, options = {}) {
|
|
2990
|
+
this.assertChain(chainId);
|
|
2991
|
+
let signature = await this.auth.getToken(state, chainId);
|
|
2992
|
+
try {
|
|
2993
|
+
return await this.api.request(path, { ...options, signature });
|
|
2994
|
+
} catch (error) {
|
|
2995
|
+
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
2996
|
+
if (error.isAuthenticationError) {
|
|
2997
|
+
this.auth.clear(state, chainId);
|
|
2998
|
+
signature = await this.auth.getToken(state, chainId);
|
|
2999
|
+
try {
|
|
3000
|
+
return await this.api.request(path, { ...options, signature });
|
|
3001
|
+
} catch (retryError) {
|
|
3002
|
+
throw this.mapApiError(retryError);
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
throw this.mapApiError(error);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
mapApiError(error) {
|
|
3009
|
+
if (!(error instanceof YieldseekerApiError)) {
|
|
3010
|
+
return new OwneyError(
|
|
3011
|
+
"AGENT_API_ERROR",
|
|
3012
|
+
"Yieldseeker request failed.",
|
|
3013
|
+
{ cause: error instanceof Error ? error.message : String(error) },
|
|
3014
|
+
this.id
|
|
3015
|
+
);
|
|
3016
|
+
}
|
|
3017
|
+
const code = error.isAuthenticationError ? "AGENT_AUTH_FAILED" : error.providerCode === "REQUEST_TIMEOUT" ? "AGENT_TIMEOUT" : "AGENT_API_ERROR";
|
|
3018
|
+
return new OwneyError(
|
|
3019
|
+
code,
|
|
3020
|
+
`Yieldseeker request failed: ${error.providerCode}.`,
|
|
3021
|
+
{
|
|
3022
|
+
statusCode: error.status,
|
|
3023
|
+
providerCode: error.providerCode,
|
|
3024
|
+
...error.responseFields ? { fields: error.responseFields } : {}
|
|
3025
|
+
},
|
|
3026
|
+
this.id
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
3029
|
+
async submitTransaction(state, chainId, transaction) {
|
|
3030
|
+
if (this.transactionExecutor) {
|
|
3031
|
+
return this.transactionExecutor(state, chainId, transaction);
|
|
3032
|
+
}
|
|
3033
|
+
if (typeof transaction.from !== "string" || typeof transaction.to !== "string" || typeof transaction.data !== "string" || typeof transaction.value !== "string" || transaction.chainId !== chainId) {
|
|
3034
|
+
throw this.invalidResponse("transaction");
|
|
3035
|
+
}
|
|
3036
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3037
|
+
if ((0, import_viem6.getAddress)(transaction.from) !== account) {
|
|
3038
|
+
throw this.invalidResponse("transaction", {
|
|
3039
|
+
reason: "transaction.from does not match the connected wallet"
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
const walletClient = (0, import_viem6.createWalletClient)({
|
|
3043
|
+
account,
|
|
3044
|
+
chain: import_chains3.base,
|
|
3045
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3046
|
+
});
|
|
3047
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3048
|
+
chain: import_chains3.base,
|
|
3049
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3050
|
+
});
|
|
3051
|
+
await ensureWalletOnChain(
|
|
3052
|
+
publicClient,
|
|
3053
|
+
walletClient,
|
|
3054
|
+
8453
|
|
3055
|
+
);
|
|
3056
|
+
const hash = await walletClient.sendTransaction({
|
|
3057
|
+
account,
|
|
3058
|
+
chain: import_chains3.base,
|
|
3059
|
+
to: (0, import_viem6.getAddress)(transaction.to),
|
|
3060
|
+
data: transaction.data,
|
|
3061
|
+
value: BigInt(transaction.value)
|
|
3062
|
+
});
|
|
3063
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3064
|
+
hash,
|
|
3065
|
+
confirmations: 1
|
|
3066
|
+
});
|
|
3067
|
+
if (receipt.status !== "success") {
|
|
3068
|
+
throw new OwneyError(
|
|
3069
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3070
|
+
`Yieldseeker transaction reverted (${hash}).`,
|
|
3071
|
+
{ transactionHash: hash },
|
|
3072
|
+
this.id
|
|
3073
|
+
);
|
|
3074
|
+
}
|
|
3075
|
+
return hash;
|
|
3076
|
+
}
|
|
3077
|
+
async waitForUnwindReceipt(state, chainId, transactionHash) {
|
|
3078
|
+
if (this.unwindReceiptWaiter) {
|
|
3079
|
+
await this.unwindReceiptWaiter(state, chainId, transactionHash);
|
|
3080
|
+
return;
|
|
3081
|
+
}
|
|
3082
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
3083
|
+
chain: import_chains3.base,
|
|
3084
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
3085
|
+
});
|
|
3086
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3087
|
+
hash: transactionHash,
|
|
3088
|
+
confirmations: 1
|
|
3089
|
+
});
|
|
3090
|
+
if (receipt.status !== "success") {
|
|
3091
|
+
throw new OwneyError(
|
|
3092
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3093
|
+
`Yieldseeker position unwind reverted (${transactionHash}).`,
|
|
3094
|
+
{ transactionHash },
|
|
3095
|
+
this.id
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
positionMatchesChain(chain, chainId) {
|
|
3100
|
+
const normalized = chain.trim().toUpperCase();
|
|
3101
|
+
return normalized === String(chainId) || normalized === "BASE";
|
|
3102
|
+
}
|
|
3103
|
+
isTransactionHash(value) {
|
|
3104
|
+
return typeof value === "string" && /^0x[a-fA-F0-9]{64}$/.test(value);
|
|
3105
|
+
}
|
|
3106
|
+
assertChain(chainId) {
|
|
3107
|
+
if (chainId !== 8453) {
|
|
3108
|
+
throw new OwneyError(
|
|
3109
|
+
"CHAIN_UNSUPPORTED",
|
|
3110
|
+
`Yieldseeker does not support chain ${chainId}.`,
|
|
3111
|
+
{ chainId, supportedChainIds: [8453] },
|
|
3112
|
+
this.id
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
assertOptionalChain(chainId) {
|
|
3117
|
+
if (chainId !== void 0) this.assertChain(chainId);
|
|
3118
|
+
}
|
|
3119
|
+
assertAsset(asset) {
|
|
3120
|
+
if (asset !== "USDC" && asset !== "WETH") {
|
|
3121
|
+
throw new OwneyError(
|
|
3122
|
+
"ASSET_UNSUPPORTED",
|
|
3123
|
+
`Yieldseeker does not support asset ${asset}.`,
|
|
3124
|
+
{ asset, supportedAssets: ["USDC", "WETH"] },
|
|
3125
|
+
this.id
|
|
3126
|
+
);
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
invalidResponse(operation, details = {}) {
|
|
3130
|
+
return new OwneyError(
|
|
3131
|
+
"AGENT_INVALID_RESPONSE",
|
|
3132
|
+
`Yieldseeker returned an invalid ${operation} response.`,
|
|
3133
|
+
details,
|
|
3134
|
+
this.id
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
};
|
|
3138
|
+
|
|
3139
|
+
// src/lib/routing-api.ts
|
|
3140
|
+
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3141
|
+
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3142
|
+
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
3143
|
+
try {
|
|
3144
|
+
const res = await fetch(url, {
|
|
3145
|
+
method: "GET",
|
|
3146
|
+
headers: {
|
|
3147
|
+
"Content-Type": "application/json",
|
|
3148
|
+
"x-owney-api-key": `${apiKey}`
|
|
3149
|
+
}
|
|
3150
|
+
});
|
|
3151
|
+
if (!res.ok) {
|
|
3152
|
+
if (res.status !== 404) {
|
|
3153
|
+
console.warn(
|
|
3154
|
+
`[owney-sdk] Could not read org agent config (${res.status}); leaving user profiles unchanged.`
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
3157
|
+
return null;
|
|
3158
|
+
}
|
|
3159
|
+
const json = await res.json();
|
|
3160
|
+
const policy = json.success ? json.data ?? null : null;
|
|
3161
|
+
debugLog(
|
|
3162
|
+
"owney-sdk",
|
|
3163
|
+
policy ? "org agent config: loaded" : "org agent config: none set by this partner \u2014 user profiles will be left as they are",
|
|
3164
|
+
policy ?? void 0
|
|
3165
|
+
);
|
|
3166
|
+
return policy;
|
|
3167
|
+
} catch (error) {
|
|
3168
|
+
console.warn(
|
|
3169
|
+
"[owney-sdk] Could not read org agent config (non-fatal):",
|
|
3170
|
+
error instanceof Error ? error.message : String(error)
|
|
3171
|
+
);
|
|
3172
|
+
return null;
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
3176
|
+
const url = `${baseUrl}/api/v1/agent/keys`;
|
|
3177
|
+
const res = await fetch(url, {
|
|
3178
|
+
method: "GET",
|
|
3179
|
+
headers: {
|
|
3180
|
+
"Content-Type": "application/json",
|
|
3181
|
+
"x-owney-api-key": `${apiKey}`
|
|
3182
|
+
}
|
|
3183
|
+
});
|
|
3184
|
+
if (!res.ok) {
|
|
3185
|
+
const text = await res.text().catch(() => "");
|
|
3186
|
+
throw new OwneyError(
|
|
3187
|
+
"API_ROUTING_ERROR",
|
|
3188
|
+
`Routing API error ${res.status}: ${text}`,
|
|
3189
|
+
{ statusCode: res.status, responseBody: text }
|
|
3190
|
+
);
|
|
3191
|
+
}
|
|
3192
|
+
const json = await res.json();
|
|
3193
|
+
if (!json.success) {
|
|
3194
|
+
throw new OwneyError(
|
|
3195
|
+
"API_ROUTING_FAILED",
|
|
3196
|
+
`Routing API request failed: ${json.message}`,
|
|
3197
|
+
{ message: json.message }
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
return json.data;
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
// src/lib/health-report.ts
|
|
3204
|
+
var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3205
|
+
async function reportAgentFailure(apiKey, agentType, errorCode, baseUrl = ROUTING_API_BASE_URL3) {
|
|
3206
|
+
try {
|
|
3207
|
+
await fetch(`${baseUrl}/api/v1/agent/health-report`, {
|
|
3208
|
+
method: "POST",
|
|
3209
|
+
headers: {
|
|
3210
|
+
"Content-Type": "application/json",
|
|
3211
|
+
"x-owney-api-key": apiKey
|
|
3212
|
+
},
|
|
3213
|
+
body: JSON.stringify({
|
|
3214
|
+
agent_type: agentType,
|
|
3215
|
+
error_code: errorCode,
|
|
3216
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3217
|
+
})
|
|
3218
|
+
});
|
|
3219
|
+
} catch (err) {
|
|
3220
|
+
console.warn(
|
|
3221
|
+
`[owney-sdk] health-report failed for agent "${agentType}":`,
|
|
3222
|
+
err instanceof Error ? err.message : err
|
|
3223
|
+
);
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
3227
|
+
try {
|
|
3228
|
+
return await fn();
|
|
3229
|
+
} catch (err) {
|
|
3230
|
+
const errorCode = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
3231
|
+
void reportAgentFailure(apiKey, agentType, errorCode, baseUrl);
|
|
3232
|
+
throw err;
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
// src/lib/helpers/withdraw-helper.ts
|
|
3237
|
+
var import_viem7 = require("viem");
|
|
3238
|
+
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
3239
|
+
const target = asset.toUpperCase();
|
|
3240
|
+
return agents.map((agent) => {
|
|
3241
|
+
const agentBalance = aggregated[agent.id];
|
|
3242
|
+
const tokenBalance = agentBalance?.tokens.find(
|
|
3243
|
+
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
3244
|
+
);
|
|
3245
|
+
let balance = tokenBalance ? (0, import_viem7.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
3246
|
+
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
3247
|
+
const chainNameById = {
|
|
3248
|
+
1: "ETHEREUM",
|
|
3249
|
+
8453: "BASE",
|
|
3250
|
+
42161: "ARBITRUM"
|
|
3251
|
+
};
|
|
3252
|
+
const targetChain = chainNameById[chainId];
|
|
3253
|
+
for (const position2 of agentBalance?.positions ?? []) {
|
|
3254
|
+
const positionChain = position2.chain.trim().toUpperCase();
|
|
3255
|
+
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
3256
|
+
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
3257
|
+
if (position2.amountRaw !== void 0) {
|
|
3258
|
+
try {
|
|
3259
|
+
balance += BigInt(position2.amountRaw);
|
|
3260
|
+
continue;
|
|
3261
|
+
} catch {
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
balance += (0, import_viem7.parseUnits)(position2.amount, decimals);
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
return { agent, balance };
|
|
3268
|
+
});
|
|
3269
|
+
}
|
|
3270
|
+
function planProportionalShares(balances, requested, totalAvailable) {
|
|
3271
|
+
const plans = balances.map(({ agent, balance }) => ({
|
|
3272
|
+
agent,
|
|
3273
|
+
balance,
|
|
3274
|
+
planned: totalAvailable > 0n ? balance * requested / totalAvailable : 0n
|
|
3275
|
+
}));
|
|
3276
|
+
const assigned = plans.reduce((s, p) => s + p.planned, 0n);
|
|
3277
|
+
let remainder = requested - assigned;
|
|
3278
|
+
const byHeadroom = [...plans].sort((a, b) => {
|
|
3279
|
+
const diff = b.balance - b.planned - (a.balance - a.planned);
|
|
3280
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : 0;
|
|
2310
3281
|
});
|
|
3282
|
+
for (const p of byHeadroom) {
|
|
3283
|
+
if (remainder === 0n) break;
|
|
3284
|
+
const headroom = p.balance - p.planned;
|
|
3285
|
+
if (headroom <= 0n) continue;
|
|
3286
|
+
const take = headroom < remainder ? headroom : remainder;
|
|
3287
|
+
p.planned += take;
|
|
3288
|
+
remainder -= take;
|
|
3289
|
+
}
|
|
3290
|
+
return plans;
|
|
2311
3291
|
}
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
|
|
3292
|
+
function planDisabledDrain(disabled, requested) {
|
|
3293
|
+
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
3294
|
+
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
3295
|
+
);
|
|
3296
|
+
const plans = [];
|
|
3297
|
+
let remaining = requested;
|
|
3298
|
+
for (const { agent, balance } of sorted) {
|
|
3299
|
+
if (remaining === 0n) {
|
|
3300
|
+
plans.push({ agent, balance, planned: 0n });
|
|
3301
|
+
continue;
|
|
3302
|
+
}
|
|
3303
|
+
const take = balance < remaining ? balance : remaining;
|
|
3304
|
+
plans.push({ agent, balance, planned: take });
|
|
3305
|
+
remaining -= take;
|
|
3306
|
+
}
|
|
3307
|
+
return { plans, remaining };
|
|
2321
3308
|
}
|
|
2322
|
-
|
|
2323
|
-
const
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
3309
|
+
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
3310
|
+
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
3311
|
+
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
3312
|
+
const totalHeadroom = candidates.reduce(
|
|
3313
|
+
(s, c) => s + (c.balance - c.planned),
|
|
3314
|
+
0n
|
|
3315
|
+
);
|
|
3316
|
+
if (totalHeadroom === 0n) return;
|
|
3317
|
+
let distributed = 0n;
|
|
3318
|
+
for (const c of candidates) {
|
|
3319
|
+
const headroom = c.balance - c.planned;
|
|
3320
|
+
const proportional = headroom * amount / totalHeadroom;
|
|
3321
|
+
const give = proportional > headroom ? headroom : proportional;
|
|
3322
|
+
c.planned += give;
|
|
3323
|
+
distributed += give;
|
|
2337
3324
|
}
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
3325
|
+
let leftover = amount - distributed;
|
|
3326
|
+
for (const c of candidates) {
|
|
3327
|
+
if (leftover === 0n) break;
|
|
3328
|
+
const headroom = c.balance - c.planned;
|
|
3329
|
+
if (headroom <= 0n) continue;
|
|
3330
|
+
const take = headroom < leftover ? headroom : leftover;
|
|
3331
|
+
c.planned += take;
|
|
3332
|
+
leftover -= take;
|
|
2345
3333
|
}
|
|
2346
3334
|
}
|
|
3335
|
+
function sumWithdrawnAmount(results) {
|
|
3336
|
+
return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
|
|
3337
|
+
}
|
|
2347
3338
|
|
|
2348
|
-
// src/lib/
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
`No sponsored token configured for chain ${chainId}`
|
|
2359
|
-
);
|
|
3339
|
+
// src/lib/helpers/account-apy-helper.ts
|
|
3340
|
+
function balanceForApyScope(balance, chainId, tokenSymbol) {
|
|
3341
|
+
if (!tokenSymbol) {
|
|
3342
|
+
const total = Number(balance.totalBalance);
|
|
3343
|
+
return Number.isFinite(total) && total > 0 ? total : 0;
|
|
3344
|
+
}
|
|
3345
|
+
const normalizedToken = tokenSymbol.toUpperCase();
|
|
3346
|
+
return balance.tokens.reduce((total, token2) => {
|
|
3347
|
+
if (Number(token2.chainId) !== chainId || String(token2.asset).toUpperCase() !== normalizedToken) {
|
|
3348
|
+
return total;
|
|
2360
3349
|
}
|
|
2361
|
-
const
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
account: deps.ownerAddress,
|
|
2402
|
-
...typedData
|
|
2403
|
-
});
|
|
2404
|
-
deps.onApproved?.();
|
|
2405
|
-
const result = await post({
|
|
2406
|
-
baseUrl: deps.baseUrl,
|
|
2407
|
-
apiKey: deps.apiKey,
|
|
2408
|
-
body: {
|
|
2409
|
-
chainId: cid,
|
|
2410
|
-
token,
|
|
2411
|
-
from: deps.ownerAddress,
|
|
2412
|
-
to: smartWallet,
|
|
2413
|
-
value: amount,
|
|
2414
|
-
validAfter: validAfter.toString(),
|
|
2415
|
-
validBefore: validBefore.toString(),
|
|
2416
|
-
nonce,
|
|
2417
|
-
authSignature,
|
|
2418
|
-
tokenName,
|
|
2419
|
-
tokenVersion
|
|
3350
|
+
const amount = Number(token2.amount);
|
|
3351
|
+
return Number.isFinite(amount) && amount > 0 ? total + amount : total;
|
|
3352
|
+
}, 0);
|
|
3353
|
+
}
|
|
3354
|
+
function aggregateApyHistory(agentApys, agentBalances) {
|
|
3355
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
3356
|
+
for (const [id, accountApy] of Object.entries(agentApys)) {
|
|
3357
|
+
const balance = agentBalances[id] ?? 0;
|
|
3358
|
+
if (!Number.isFinite(balance) || balance <= 0) continue;
|
|
3359
|
+
for (const point of accountApy.history ?? []) {
|
|
3360
|
+
const apy = Number(point.apy);
|
|
3361
|
+
if (!point.date || !Number.isFinite(apy)) continue;
|
|
3362
|
+
const current = byDate.get(point.date) ?? { weightedSum: 0, weight: 0 };
|
|
3363
|
+
current.weightedSum += apy * balance;
|
|
3364
|
+
current.weight += balance;
|
|
3365
|
+
byDate.set(point.date, current);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
return [...byDate.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([date, value]) => ({
|
|
3369
|
+
date,
|
|
3370
|
+
apy: value.weightedSum / value.weight
|
|
3371
|
+
}));
|
|
3372
|
+
}
|
|
3373
|
+
function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
3374
|
+
const sums = {};
|
|
3375
|
+
const weights = {};
|
|
3376
|
+
for (const id of Object.keys(agentApys)) {
|
|
3377
|
+
const cells = agentApys[id].apyByChainAndAsset;
|
|
3378
|
+
const balance = agentBalances[id] ?? 0;
|
|
3379
|
+
if (!cells || balance <= 0) continue;
|
|
3380
|
+
for (const [chainKey, perAsset] of Object.entries(cells)) {
|
|
3381
|
+
if (!perAsset) continue;
|
|
3382
|
+
const chainId = Number(chainKey);
|
|
3383
|
+
for (const [asset, apyValue] of Object.entries(perAsset)) {
|
|
3384
|
+
const apy = Number(apyValue ?? 0);
|
|
3385
|
+
if (apy === 0) continue;
|
|
3386
|
+
sums[chainId] ??= {};
|
|
3387
|
+
weights[chainId] ??= {};
|
|
3388
|
+
sums[chainId][asset] = (sums[chainId][asset] ?? 0) + apy * balance;
|
|
3389
|
+
weights[chainId][asset] = (weights[chainId][asset] ?? 0) + balance;
|
|
2420
3390
|
}
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
};
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
const out = {};
|
|
3394
|
+
for (const chainKey of Object.keys(sums)) {
|
|
3395
|
+
const chainId = Number(chainKey);
|
|
3396
|
+
const perAssetOut = {};
|
|
3397
|
+
for (const asset of Object.keys(sums[chainId])) {
|
|
3398
|
+
const w = weights[chainId][asset];
|
|
3399
|
+
if (w > 0) perAssetOut[asset] = sums[chainId][asset] / w;
|
|
3400
|
+
}
|
|
3401
|
+
if (Object.keys(perAssetOut).length > 0) {
|
|
3402
|
+
out[chainId] = perAssetOut;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
return out;
|
|
2424
3406
|
}
|
|
2425
3407
|
|
|
3408
|
+
// src/client.ts
|
|
3409
|
+
var import_viem9 = require("viem");
|
|
3410
|
+
var import_chains4 = require("viem/chains");
|
|
3411
|
+
|
|
2426
3412
|
// src/lib/sponsored-weth-deposit.ts
|
|
2427
3413
|
var PERMIT_WINDOW_SECONDS = 15 * 60;
|
|
2428
3414
|
function makeSponsoredWethCallback(deps) {
|
|
2429
3415
|
const get = deps.httpGet ?? getSponsorRelayerAddress;
|
|
2430
3416
|
const post = deps.httpPost ?? postSponsorPermit2Transfer;
|
|
2431
|
-
return
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
"CHAIN_UNSUPPORTED",
|
|
2437
|
-
`No sponsored WETH configured for chain ${chainId}`
|
|
2438
|
-
);
|
|
2439
|
-
}
|
|
2440
|
-
const amountWei = BigInt(amount);
|
|
2441
|
-
const pub = deps.getPublicClient(cid);
|
|
2442
|
-
const wallet = deps.getWalletClient(cid);
|
|
2443
|
-
await ensureWalletOnChain(pub, wallet, cid);
|
|
2444
|
-
try {
|
|
2445
|
-
const balance = await readErc20Balance(pub, token, deps.ownerAddress);
|
|
2446
|
-
if (balance < amountWei) {
|
|
3417
|
+
return makeVerificationAwareDepositCallback(
|
|
3418
|
+
async (smartWallet, chainId, amount, verification) => {
|
|
3419
|
+
const cid = chainId;
|
|
3420
|
+
const token2 = deps.tokenAddressByChain[cid];
|
|
3421
|
+
if (!token2) {
|
|
2447
3422
|
throw new OwneyError(
|
|
2448
|
-
"
|
|
2449
|
-
|
|
2450
|
-
{ token, chainId: cid, balance: balance.toString(), amount }
|
|
3423
|
+
"CHAIN_UNSUPPORTED",
|
|
3424
|
+
`No sponsored WETH configured for chain ${chainId}`
|
|
2451
3425
|
);
|
|
2452
3426
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
3427
|
+
const amountWei = BigInt(amount);
|
|
3428
|
+
const pub = deps.getPublicClient(cid);
|
|
3429
|
+
const wallet = deps.getWalletClient(cid);
|
|
3430
|
+
await ensureWalletOnChain(pub, wallet, cid);
|
|
3431
|
+
try {
|
|
3432
|
+
const balance = await readErc20Balance(pub, token2, deps.ownerAddress);
|
|
3433
|
+
if (balance < amountWei) {
|
|
3434
|
+
throw new OwneyError(
|
|
3435
|
+
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
3436
|
+
"Insufficient WETH balance for this deposit.",
|
|
3437
|
+
{ token: token2, chainId: cid, balance: balance.toString(), amount }
|
|
3438
|
+
);
|
|
3439
|
+
}
|
|
3440
|
+
} catch (err) {
|
|
3441
|
+
if (err instanceof OwneyError) throw err;
|
|
3442
|
+
console.warn(
|
|
3443
|
+
"[owney-sdk] WETH balance pre-check failed (non-fatal):",
|
|
3444
|
+
err instanceof Error ? err.message : String(err)
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
const allowance = await readPermit2Allowance(
|
|
3448
|
+
pub,
|
|
3449
|
+
token2,
|
|
3450
|
+
deps.ownerAddress
|
|
2466
3451
|
);
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
const nonce = randomPermit2Nonce();
|
|
2474
|
-
const deadline = BigInt(
|
|
2475
|
-
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
2476
|
-
);
|
|
2477
|
-
const typedData = buildPermitTransferFromTypedData({
|
|
2478
|
-
chainId: cid,
|
|
2479
|
-
message: {
|
|
2480
|
-
permitted: { token, amount: amountWei },
|
|
2481
|
-
spender: relayer,
|
|
2482
|
-
nonce,
|
|
2483
|
-
deadline
|
|
3452
|
+
if (allowance < amountWei) {
|
|
3453
|
+
throw new OwneyError(
|
|
3454
|
+
"PERMIT2_APPROVAL_REQUIRED",
|
|
3455
|
+
"WETH gasless deposits need a one-time Permit2 approval; call approvePermit2() first",
|
|
3456
|
+
{ token: token2, chainId: cid, allowance: allowance.toString(), amount }
|
|
3457
|
+
);
|
|
2484
3458
|
}
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
3459
|
+
const relayer = await get({
|
|
3460
|
+
baseUrl: deps.baseUrl,
|
|
3461
|
+
apiKey: deps.apiKey,
|
|
3462
|
+
chainId: cid
|
|
3463
|
+
});
|
|
3464
|
+
const nonce = randomPermit2Nonce();
|
|
3465
|
+
const deadline = BigInt(
|
|
3466
|
+
Math.floor(Date.now() / 1e3) + PERMIT_WINDOW_SECONDS
|
|
3467
|
+
);
|
|
3468
|
+
const typedData = buildPermitTransferFromTypedData({
|
|
2495
3469
|
chainId: cid,
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
3470
|
+
message: {
|
|
3471
|
+
permitted: { token: token2, amount: amountWei },
|
|
3472
|
+
spender: relayer,
|
|
3473
|
+
nonce,
|
|
3474
|
+
deadline
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
const signature = await wallet.signTypedData({
|
|
3478
|
+
account: deps.ownerAddress,
|
|
3479
|
+
...typedData
|
|
3480
|
+
});
|
|
3481
|
+
deps.onApproved?.();
|
|
3482
|
+
const result = await post({
|
|
3483
|
+
baseUrl: deps.baseUrl,
|
|
3484
|
+
apiKey: deps.apiKey,
|
|
3485
|
+
...verification?.agentId === "yieldseeker" ? { yieldseekerSignature: verification.signature } : {},
|
|
3486
|
+
body: {
|
|
3487
|
+
chainId: cid,
|
|
3488
|
+
token: token2,
|
|
3489
|
+
from: deps.ownerAddress,
|
|
3490
|
+
to: smartWallet,
|
|
3491
|
+
amount,
|
|
3492
|
+
nonce: nonce.toString(),
|
|
3493
|
+
deadline: deadline.toString(),
|
|
3494
|
+
signature
|
|
3495
|
+
}
|
|
3496
|
+
});
|
|
3497
|
+
return result.txHash;
|
|
3498
|
+
}
|
|
3499
|
+
);
|
|
2507
3500
|
}
|
|
2508
3501
|
|
|
2509
3502
|
// src/lib/sponsored-calls-deposit.ts
|
|
2510
|
-
var
|
|
3503
|
+
var import_viem8 = require("viem");
|
|
2511
3504
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
2512
3505
|
var DEFAULT_MAX_POLLS = 30;
|
|
2513
3506
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -2515,7 +3508,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
2515
3508
|
method: "wallet_getCapabilities",
|
|
2516
3509
|
params: [owner]
|
|
2517
3510
|
});
|
|
2518
|
-
const forChain = caps?.[(0,
|
|
3511
|
+
const forChain = caps?.[(0, import_viem8.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
2519
3512
|
return Boolean(forChain?.paymasterService?.supported);
|
|
2520
3513
|
}
|
|
2521
3514
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -2535,8 +3528,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2535
3528
|
};
|
|
2536
3529
|
return async (smartWallet, chainId, amount) => {
|
|
2537
3530
|
const cid = chainId;
|
|
2538
|
-
const
|
|
2539
|
-
if (!
|
|
3531
|
+
const token2 = deps.tokenAddressByChain[cid];
|
|
3532
|
+
if (!token2) {
|
|
2540
3533
|
throw new OwneyError(
|
|
2541
3534
|
"CHAIN_UNSUPPORTED",
|
|
2542
3535
|
`No sponsored token configured for chain ${chainId}`
|
|
@@ -2549,8 +3542,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2549
3542
|
{ chainId }
|
|
2550
3543
|
);
|
|
2551
3544
|
}
|
|
2552
|
-
const data = (0,
|
|
2553
|
-
abi:
|
|
3545
|
+
const data = (0, import_viem8.encodeFunctionData)({
|
|
3546
|
+
abi: import_viem8.erc20Abi,
|
|
2554
3547
|
functionName: "transfer",
|
|
2555
3548
|
args: [smartWallet, BigInt(amount)]
|
|
2556
3549
|
});
|
|
@@ -2560,9 +3553,9 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2560
3553
|
{
|
|
2561
3554
|
version: "2.0.0",
|
|
2562
3555
|
from: deps.ownerAddress,
|
|
2563
|
-
chainId: (0,
|
|
3556
|
+
chainId: (0, import_viem8.toHex)(chainId),
|
|
2564
3557
|
atomicRequired: false,
|
|
2565
|
-
calls: [{ to:
|
|
3558
|
+
calls: [{ to: token2, value: "0x0", data }],
|
|
2566
3559
|
capabilities: {
|
|
2567
3560
|
paymasterService: { url: absolutePaymasterUrl() }
|
|
2568
3561
|
}
|
|
@@ -2601,10 +3594,10 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
2601
3594
|
function encodeMultiAgentCursor(map) {
|
|
2602
3595
|
return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
|
|
2603
3596
|
}
|
|
2604
|
-
function decodeMultiAgentCursor(
|
|
3597
|
+
function decodeMultiAgentCursor(token2) {
|
|
2605
3598
|
let parsed;
|
|
2606
3599
|
try {
|
|
2607
|
-
const json = Buffer.from(
|
|
3600
|
+
const json = Buffer.from(token2, "base64").toString("utf8");
|
|
2608
3601
|
parsed = JSON.parse(json);
|
|
2609
3602
|
} catch {
|
|
2610
3603
|
throw new InvalidHistoryCursorError(
|
|
@@ -2624,9 +3617,9 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
2624
3617
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
2625
3618
|
};
|
|
2626
3619
|
var VIEM_CHAIN2 = {
|
|
2627
|
-
8453:
|
|
2628
|
-
42161:
|
|
2629
|
-
1:
|
|
3620
|
+
8453: import_chains4.base,
|
|
3621
|
+
42161: import_chains4.arbitrum,
|
|
3622
|
+
1: import_chains4.mainnet
|
|
2630
3623
|
};
|
|
2631
3624
|
var SPONSORED_WETH_BY_CHAIN = {
|
|
2632
3625
|
8453: "0x4200000000000000000000000000000000000006",
|
|
@@ -2654,6 +3647,7 @@ var OwneySDK = class {
|
|
|
2654
3647
|
orgAgentConfig;
|
|
2655
3648
|
orgAgentConfigPromise = null;
|
|
2656
3649
|
zyfaiRpcUrls;
|
|
3650
|
+
yieldseekerApiBaseUrl;
|
|
2657
3651
|
routingApiBaseUrl;
|
|
2658
3652
|
referralSource;
|
|
2659
3653
|
cachedSponsoredCallback = null;
|
|
@@ -2665,6 +3659,7 @@ var OwneySDK = class {
|
|
|
2665
3659
|
this.apiKey = config.apiKey;
|
|
2666
3660
|
if (config.debug) setOwneyDebug(true);
|
|
2667
3661
|
this.zyfaiRpcUrls = config.zyfaiRpcUrls;
|
|
3662
|
+
this.yieldseekerApiBaseUrl = config.yieldseekerApiBaseUrl;
|
|
2668
3663
|
this.routingApiBaseUrl = config.routingApiBaseUrl;
|
|
2669
3664
|
this.paymasterServiceUrl = config.paymasterServiceUrl;
|
|
2670
3665
|
this.referralSource = config.referralSource;
|
|
@@ -2767,14 +3762,14 @@ var OwneySDK = class {
|
|
|
2767
3762
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2768
3763
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2769
3764
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2770
|
-
getPublicClient: (cid) => (0,
|
|
3765
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
2771
3766
|
chain: VIEM_CHAIN2[cid],
|
|
2772
|
-
transport: (0,
|
|
3767
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2773
3768
|
}),
|
|
2774
|
-
getWalletClient: (cid) => (0,
|
|
3769
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
2775
3770
|
account: owner,
|
|
2776
3771
|
chain: VIEM_CHAIN2[cid],
|
|
2777
|
-
transport: (0,
|
|
3772
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2778
3773
|
})
|
|
2779
3774
|
});
|
|
2780
3775
|
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
@@ -2820,14 +3815,14 @@ var OwneySDK = class {
|
|
|
2820
3815
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
2821
3816
|
// PublicClient/WalletClient param types — structurally identical at
|
|
2822
3817
|
// runtime, but the two share a name TS treats as unrelated.
|
|
2823
|
-
getPublicClient: (cid) => (0,
|
|
3818
|
+
getPublicClient: (cid) => (0, import_viem9.createPublicClient)({
|
|
2824
3819
|
chain: VIEM_CHAIN2[cid],
|
|
2825
|
-
transport: (0,
|
|
3820
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2826
3821
|
}),
|
|
2827
|
-
getWalletClient: (cid) => (0,
|
|
3822
|
+
getWalletClient: (cid) => (0, import_viem9.createWalletClient)({
|
|
2828
3823
|
account: owner,
|
|
2829
3824
|
chain: VIEM_CHAIN2[cid],
|
|
2830
|
-
transport: (0,
|
|
3825
|
+
transport: (0, import_viem9.custom)(provider)
|
|
2831
3826
|
})
|
|
2832
3827
|
});
|
|
2833
3828
|
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
@@ -2858,12 +3853,10 @@ var OwneySDK = class {
|
|
|
2858
3853
|
this.orgAgentConfigPromise = fetchOrgAgentConfig(
|
|
2859
3854
|
this.apiKey,
|
|
2860
3855
|
this.routingApiBaseUrl
|
|
2861
|
-
).then(
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
}
|
|
2866
|
-
);
|
|
3856
|
+
).then((config) => {
|
|
3857
|
+
this.orgAgentConfig = config;
|
|
3858
|
+
return config;
|
|
3859
|
+
});
|
|
2867
3860
|
}
|
|
2868
3861
|
return this.orgAgentConfigPromise;
|
|
2869
3862
|
}
|
|
@@ -2903,7 +3896,14 @@ var OwneySDK = class {
|
|
|
2903
3896
|
this.routingApiBaseUrl
|
|
2904
3897
|
);
|
|
2905
3898
|
this.disabledAgents.clear();
|
|
2906
|
-
for (const {
|
|
3899
|
+
for (const {
|
|
3900
|
+
key: key2,
|
|
3901
|
+
agent_type,
|
|
3902
|
+
is_enabled,
|
|
3903
|
+
is_configured
|
|
3904
|
+
} of agentKeys) {
|
|
3905
|
+
const configured = is_configured ?? Boolean(key2);
|
|
3906
|
+
if (!configured) continue;
|
|
2907
3907
|
const agent = this.createAgent(agent_type, key2);
|
|
2908
3908
|
if (!agent) continue;
|
|
2909
3909
|
this.agents.set(agent_type, agent);
|
|
@@ -2927,10 +3927,47 @@ var OwneySDK = class {
|
|
|
2927
3927
|
}
|
|
2928
3928
|
createAgent(agentId, key2) {
|
|
2929
3929
|
if (agentId === "zyfai") {
|
|
3930
|
+
if (!key2) return null;
|
|
2930
3931
|
return new ZyfaiAgent(key2, this.zyfaiRpcUrls, this.referralSource);
|
|
2931
3932
|
}
|
|
3933
|
+
if (agentId === "yieldseeker") {
|
|
3934
|
+
return new YieldseekerAgent(this.apiKey, {
|
|
3935
|
+
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl)
|
|
3936
|
+
});
|
|
3937
|
+
}
|
|
2932
3938
|
return null;
|
|
2933
3939
|
}
|
|
3940
|
+
/**
|
|
3941
|
+
* Discover the agents actually returned by routing for this organization.
|
|
3942
|
+
* Consumers should use this instead of hardcoding a global agent roster.
|
|
3943
|
+
*/
|
|
3944
|
+
async getAvailableAgents(options = {}) {
|
|
3945
|
+
await this.ensureAgentsInitialized();
|
|
3946
|
+
const { chainId, asset, includeDisabled = false } = options;
|
|
3947
|
+
const available = [];
|
|
3948
|
+
for (const [id, agent] of this.agents) {
|
|
3949
|
+
const isEnabled = !this.isAgentDisabled(id);
|
|
3950
|
+
if (!includeDisabled && !isEnabled) continue;
|
|
3951
|
+
if (chainId !== void 0 && !agent.supportedChainIds.includes(chainId)) {
|
|
3952
|
+
continue;
|
|
3953
|
+
}
|
|
3954
|
+
if (asset !== void 0) {
|
|
3955
|
+
const supportsAsset = agent.supportedAssets.some(
|
|
3956
|
+
(entry) => (chainId === void 0 || entry.chainId === chainId) && entry.assets.some((candidate) => candidate.symbol === asset)
|
|
3957
|
+
);
|
|
3958
|
+
if (!supportsAsset) {
|
|
3959
|
+
continue;
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
available.push({
|
|
3963
|
+
id,
|
|
3964
|
+
isEnabled,
|
|
3965
|
+
supportedChainIds: agent.supportedChainIds,
|
|
3966
|
+
supportedAssets: agent.supportedAssets
|
|
3967
|
+
});
|
|
3968
|
+
}
|
|
3969
|
+
return available;
|
|
3970
|
+
}
|
|
2934
3971
|
// --- Account lifecycle ---
|
|
2935
3972
|
/**
|
|
2936
3973
|
* Activate the user's smart wallet for the specified agents, or all chain-compatible agents if omitted.
|
|
@@ -2942,7 +3979,7 @@ var OwneySDK = class {
|
|
|
2942
3979
|
* If provided, ALL specified agents must support the chainId or the call
|
|
2943
3980
|
* throws before activating any agent.
|
|
2944
3981
|
*/
|
|
2945
|
-
async activateAgent(chainId, agentId) {
|
|
3982
|
+
async activateAgent(chainId, agentId, asset) {
|
|
2946
3983
|
const state = this.requireState();
|
|
2947
3984
|
await this.ensureAgentsInitialized();
|
|
2948
3985
|
if (agentId !== void 0) {
|
|
@@ -2978,9 +4015,7 @@ var OwneySDK = class {
|
|
|
2978
4015
|
this.activeAgents.add(id);
|
|
2979
4016
|
}
|
|
2980
4017
|
state.chainId = chainId;
|
|
2981
|
-
this.activateAgentsInTurn(agents, state, chainId)
|
|
2982
|
-
console.error("activateAgent background init failed:", error);
|
|
2983
|
-
});
|
|
4018
|
+
await this.activateAgentsInTurn(agents, state, chainId, asset);
|
|
2984
4019
|
return;
|
|
2985
4020
|
}
|
|
2986
4021
|
const compatible = [...this.agents.values()].filter(
|
|
@@ -3001,11 +4036,7 @@ var OwneySDK = class {
|
|
|
3001
4036
|
const enabledCompatible = compatible.filter(
|
|
3002
4037
|
(agent) => !this.isAgentDisabled(agent.id)
|
|
3003
4038
|
);
|
|
3004
|
-
this.activateAgentsInTurn(enabledCompatible, state, chainId)
|
|
3005
|
-
(error) => {
|
|
3006
|
-
console.error("activateAgent background init failed:", error);
|
|
3007
|
-
}
|
|
3008
|
-
);
|
|
4039
|
+
await this.activateAgentsInTurn(enabledCompatible, state, chainId, asset);
|
|
3009
4040
|
}
|
|
3010
4041
|
/**
|
|
3011
4042
|
* Activate agents ONE AT A TIME, each followed by its org policy.
|
|
@@ -3025,11 +4056,11 @@ var OwneySDK = class {
|
|
|
3025
4056
|
* rethrown (matching the previous `Promise.all` rejection) once all agents
|
|
3026
4057
|
* have had a chance to activate.
|
|
3027
4058
|
*/
|
|
3028
|
-
async activateAgentsInTurn(agents, state, chainId) {
|
|
4059
|
+
async activateAgentsInTurn(agents, state, chainId, asset) {
|
|
3029
4060
|
let firstError = null;
|
|
3030
4061
|
for (const agent of agents) {
|
|
3031
4062
|
try {
|
|
3032
|
-
await agent.activateAgent(state, chainId);
|
|
4063
|
+
await agent.activateAgent(state, chainId, asset);
|
|
3033
4064
|
await this.applyOrgPolicyTo(agent, state, chainId);
|
|
3034
4065
|
} catch (error) {
|
|
3035
4066
|
if (firstError === null) {
|
|
@@ -3238,10 +4269,10 @@ var OwneySDK = class {
|
|
|
3238
4269
|
try {
|
|
3239
4270
|
const balance = await agent.getBalances(state, chainId);
|
|
3240
4271
|
const target = asset.toLowerCase();
|
|
3241
|
-
const
|
|
4272
|
+
const token2 = balance.tokens.find(
|
|
3242
4273
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
3243
4274
|
);
|
|
3244
|
-
return !!
|
|
4275
|
+
return !!token2 && Number(token2.amount) > 0;
|
|
3245
4276
|
} catch {
|
|
3246
4277
|
return false;
|
|
3247
4278
|
}
|
|
@@ -3292,9 +4323,9 @@ var OwneySDK = class {
|
|
|
3292
4323
|
const { asset, amount, agentId } = options;
|
|
3293
4324
|
const state = this.requireState();
|
|
3294
4325
|
const chainId = this.requireChainId();
|
|
3295
|
-
const
|
|
4326
|
+
const token2 = asset;
|
|
3296
4327
|
const assetInfo = SupportedAssets.find(
|
|
3297
|
-
(a) => a.chainId === chainId && a.symbol ===
|
|
4328
|
+
(a) => a.chainId === chainId && a.symbol === token2
|
|
3298
4329
|
);
|
|
3299
4330
|
if (!assetInfo) {
|
|
3300
4331
|
throw new OwneyError(
|
|
@@ -3308,7 +4339,7 @@ var OwneySDK = class {
|
|
|
3308
4339
|
return withFailureReporting(
|
|
3309
4340
|
this.apiKey,
|
|
3310
4341
|
agent.id,
|
|
3311
|
-
() => agent.withdraw(state, chainId,
|
|
4342
|
+
() => agent.withdraw(state, chainId, token2, amount),
|
|
3312
4343
|
this.routingApiBaseUrl
|
|
3313
4344
|
);
|
|
3314
4345
|
}
|
|
@@ -3318,7 +4349,7 @@ var OwneySDK = class {
|
|
|
3318
4349
|
const agentErrors2 = {};
|
|
3319
4350
|
for (const agent of eligibleAgents) {
|
|
3320
4351
|
try {
|
|
3321
|
-
results2[agent.id] = await agent.withdraw(state, chainId,
|
|
4352
|
+
results2[agent.id] = await agent.withdraw(state, chainId, token2);
|
|
3322
4353
|
} catch (err) {
|
|
3323
4354
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
3324
4355
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
@@ -3345,6 +4376,10 @@ var OwneySDK = class {
|
|
|
3345
4376
|
}
|
|
3346
4377
|
const requested = BigInt(amount);
|
|
3347
4378
|
const aggregated = await this.getBalances();
|
|
4379
|
+
const unavailableAgents = eligibleAgents.filter(
|
|
4380
|
+
(agent) => !(agent.id in aggregated.agentBalances)
|
|
4381
|
+
);
|
|
4382
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
3348
4383
|
const balances = projectAgentBalancesForAsset(
|
|
3349
4384
|
eligibleAgents,
|
|
3350
4385
|
aggregated.agentBalances,
|
|
@@ -3353,7 +4388,18 @@ var OwneySDK = class {
|
|
|
3353
4388
|
assetInfo.decimals
|
|
3354
4389
|
);
|
|
3355
4390
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
3356
|
-
if (totalAvailable
|
|
4391
|
+
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
4392
|
+
throw new OwneyError(
|
|
4393
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
4394
|
+
`Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
|
|
4395
|
+
{
|
|
4396
|
+
asset,
|
|
4397
|
+
unavailableAgents: unavailableAgentIds,
|
|
4398
|
+
agentErrors: aggregated.agentErrors
|
|
4399
|
+
}
|
|
4400
|
+
);
|
|
4401
|
+
}
|
|
4402
|
+
if (totalAvailable < requested && unavailableAgents.length === 0) {
|
|
3357
4403
|
throw new OwneyError(
|
|
3358
4404
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3359
4405
|
`Requested withdrawal "${amount}" exceeds available balance "${totalAvailable.toString()}" for asset "${asset}".`,
|
|
@@ -3364,6 +4410,7 @@ var OwneySDK = class {
|
|
|
3364
4410
|
}
|
|
3365
4411
|
);
|
|
3366
4412
|
}
|
|
4413
|
+
const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
|
|
3367
4414
|
const disabledBalances = balances.filter(
|
|
3368
4415
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
3369
4416
|
);
|
|
@@ -3372,7 +4419,7 @@ var OwneySDK = class {
|
|
|
3372
4419
|
);
|
|
3373
4420
|
const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
|
|
3374
4421
|
disabledBalances,
|
|
3375
|
-
|
|
4422
|
+
plannedTarget
|
|
3376
4423
|
);
|
|
3377
4424
|
const enabledTotal = enabledBalances.reduce((s, b) => s + b.balance, 0n);
|
|
3378
4425
|
const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
|
|
@@ -3382,7 +4429,9 @@ var OwneySDK = class {
|
|
|
3382
4429
|
}));
|
|
3383
4430
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
3384
4431
|
const results = {};
|
|
3385
|
-
const agentErrors = {
|
|
4432
|
+
const agentErrors = {
|
|
4433
|
+
...aggregated.agentErrors ?? {}
|
|
4434
|
+
};
|
|
3386
4435
|
for (let i = 0; i < plans.length; i++) {
|
|
3387
4436
|
const p = plans[i];
|
|
3388
4437
|
if (p.planned === 0n) continue;
|
|
@@ -3390,7 +4439,7 @@ var OwneySDK = class {
|
|
|
3390
4439
|
results[p.agent.id] = await p.agent.withdraw(
|
|
3391
4440
|
state,
|
|
3392
4441
|
chainId,
|
|
3393
|
-
|
|
4442
|
+
token2,
|
|
3394
4443
|
p.planned.toString()
|
|
3395
4444
|
);
|
|
3396
4445
|
} catch (err) {
|
|
@@ -3429,7 +4478,8 @@ var OwneySDK = class {
|
|
|
3429
4478
|
requested: amount,
|
|
3430
4479
|
withdrawn: withdrawn.toString(),
|
|
3431
4480
|
partialResults: results,
|
|
3432
|
-
agentErrors
|
|
4481
|
+
agentErrors,
|
|
4482
|
+
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
3433
4483
|
}
|
|
3434
4484
|
);
|
|
3435
4485
|
}
|
|
@@ -3470,7 +4520,12 @@ var OwneySDK = class {
|
|
|
3470
4520
|
continue;
|
|
3471
4521
|
}
|
|
3472
4522
|
const reason = settledResult.reason;
|
|
3473
|
-
|
|
4523
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
4524
|
+
agentErrors[agentId2] = message;
|
|
4525
|
+
console.error(
|
|
4526
|
+
`[owney-sdk] Balance fetch failed for agent "${agentId2}":`,
|
|
4527
|
+
reason
|
|
4528
|
+
);
|
|
3474
4529
|
}
|
|
3475
4530
|
if (successCount === 0) {
|
|
3476
4531
|
throw new OwneyError(
|
|
@@ -3482,7 +4537,8 @@ var OwneySDK = class {
|
|
|
3482
4537
|
return {
|
|
3483
4538
|
totalBalance: String(totalBalance),
|
|
3484
4539
|
totalBalanceAsset: "usdc",
|
|
3485
|
-
agentBalances: results
|
|
4540
|
+
agentBalances: results,
|
|
4541
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors } : {}
|
|
3486
4542
|
};
|
|
3487
4543
|
}
|
|
3488
4544
|
/**
|
|
@@ -3576,7 +4632,10 @@ var OwneySDK = class {
|
|
|
3576
4632
|
Promise.all(
|
|
3577
4633
|
entries.map(async ([id, agent]) => {
|
|
3578
4634
|
const b = await agent.getBalances(state, chainId);
|
|
3579
|
-
return [
|
|
4635
|
+
return [
|
|
4636
|
+
id,
|
|
4637
|
+
balanceForApyScope(b, chainId, tokenSymbol)
|
|
4638
|
+
];
|
|
3580
4639
|
})
|
|
3581
4640
|
)
|
|
3582
4641
|
]);
|
|
@@ -3604,10 +4663,12 @@ var OwneySDK = class {
|
|
|
3604
4663
|
}
|
|
3605
4664
|
}
|
|
3606
4665
|
const apyByChainAndAsset = aggregateApyByChainAndAsset(results, balances);
|
|
4666
|
+
const history = aggregateApyHistory(results, balances);
|
|
3607
4667
|
return {
|
|
3608
4668
|
totalApy: String(totalApy),
|
|
3609
4669
|
agentApy: results,
|
|
3610
|
-
apyByChainAndAsset
|
|
4670
|
+
apyByChainAndAsset,
|
|
4671
|
+
history
|
|
3611
4672
|
};
|
|
3612
4673
|
}
|
|
3613
4674
|
/**
|
|
@@ -3729,30 +4790,30 @@ var OwneySDK = class {
|
|
|
3729
4790
|
const state = this.requireState();
|
|
3730
4791
|
const chainId = this.requireChainId();
|
|
3731
4792
|
this.validateAssetSupport(this.getAgent("zyfai"), chainId, "WETH");
|
|
3732
|
-
const
|
|
3733
|
-
if (!
|
|
4793
|
+
const token2 = SPONSORED_WETH_BY_CHAIN[chainId];
|
|
4794
|
+
if (!token2) {
|
|
3734
4795
|
throw new OwneyError(
|
|
3735
4796
|
"CHAIN_UNSUPPORTED",
|
|
3736
4797
|
`No sponsored WETH on chain ${chainId}`
|
|
3737
4798
|
);
|
|
3738
4799
|
}
|
|
3739
4800
|
const provider = this.requireConnectedProvider();
|
|
3740
|
-
const wallet = (0,
|
|
4801
|
+
const wallet = (0, import_viem9.createWalletClient)({
|
|
3741
4802
|
account: state.walletAddress,
|
|
3742
4803
|
chain: VIEM_CHAIN2[chainId],
|
|
3743
|
-
transport: (0,
|
|
4804
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3744
4805
|
});
|
|
3745
4806
|
const hash = await wallet.writeContract({
|
|
3746
|
-
address:
|
|
4807
|
+
address: token2,
|
|
3747
4808
|
abi: ERC20_ALLOWANCE_ABI,
|
|
3748
4809
|
functionName: "approve",
|
|
3749
4810
|
args: [PERMIT2_ADDRESS, MAX_UINT256],
|
|
3750
4811
|
account: state.walletAddress,
|
|
3751
4812
|
chain: VIEM_CHAIN2[chainId]
|
|
3752
4813
|
});
|
|
3753
|
-
const publicClient = (0,
|
|
4814
|
+
const publicClient = (0, import_viem9.createPublicClient)({
|
|
3754
4815
|
chain: VIEM_CHAIN2[chainId],
|
|
3755
|
-
transport: (0,
|
|
4816
|
+
transport: (0, import_viem9.custom)(provider)
|
|
3756
4817
|
});
|
|
3757
4818
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
3758
4819
|
hash,
|
|
@@ -3788,7 +4849,9 @@ var OwneySDK = class {
|
|
|
3788
4849
|
return this.getAgent(agentId).getAgentApy(days, agentOptions);
|
|
3789
4850
|
}
|
|
3790
4851
|
const results = {};
|
|
3791
|
-
const agentEntries = [...this.agents.entries()]
|
|
4852
|
+
const agentEntries = [...this.agents.entries()].filter(
|
|
4853
|
+
([id]) => !this.isAgentDisabled(id)
|
|
4854
|
+
);
|
|
3792
4855
|
const apyResults = await Promise.all(
|
|
3793
4856
|
agentEntries.map(async ([id, agent]) => {
|
|
3794
4857
|
const apy = await agent.getAgentApy(days, agentOptions);
|
|
@@ -3861,13 +4924,13 @@ var OwneySDK = class {
|
|
|
3861
4924
|
};
|
|
3862
4925
|
|
|
3863
4926
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
3864
|
-
var
|
|
3865
|
-
var
|
|
4927
|
+
var import_viem10 = require("viem");
|
|
4928
|
+
var import_siwe2 = require("siwe");
|
|
3866
4929
|
var import_sdk2 = require("@zyfai/sdk");
|
|
3867
4930
|
|
|
3868
4931
|
// src/agents/zyfai/zyfai.siwx-cache.ts
|
|
3869
|
-
var
|
|
3870
|
-
var
|
|
4932
|
+
var KEY_PREFIX3 = "owney.siwx.session";
|
|
4933
|
+
var storage3 = () => {
|
|
3871
4934
|
if (typeof window === "undefined") return null;
|
|
3872
4935
|
try {
|
|
3873
4936
|
return window.localStorage;
|
|
@@ -3875,8 +4938,8 @@ var storage2 = () => {
|
|
|
3875
4938
|
return null;
|
|
3876
4939
|
}
|
|
3877
4940
|
};
|
|
3878
|
-
var
|
|
3879
|
-
var legacyKeyPrefix2 = (address) => `${
|
|
4941
|
+
var buildKey3 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}`;
|
|
4942
|
+
var legacyKeyPrefix2 = (address) => `${KEY_PREFIX3}:${address.toLowerCase()}:`;
|
|
3880
4943
|
var memorySiwxSessions = /* @__PURE__ */ new Map();
|
|
3881
4944
|
var readLegacySiwxSession = (store, address) => {
|
|
3882
4945
|
if (!store) return null;
|
|
@@ -3907,8 +4970,8 @@ var readLegacySiwxSession = (store, address) => {
|
|
|
3907
4970
|
};
|
|
3908
4971
|
var readSiwxSession = (address, chainId) => {
|
|
3909
4972
|
if (typeof window === "undefined") return null;
|
|
3910
|
-
const key2 =
|
|
3911
|
-
const store =
|
|
4973
|
+
const key2 = buildKey3(address);
|
|
4974
|
+
const store = storage3();
|
|
3912
4975
|
let raw = null;
|
|
3913
4976
|
try {
|
|
3914
4977
|
raw = store?.getItem(key2) ?? null;
|
|
@@ -3936,18 +4999,18 @@ var readSiwxSession = (address, chainId) => {
|
|
|
3936
4999
|
};
|
|
3937
5000
|
var writeSiwxSession = (address, _chainId, session) => {
|
|
3938
5001
|
if (typeof window === "undefined") return;
|
|
3939
|
-
const key2 =
|
|
5002
|
+
const key2 = buildKey3(address);
|
|
3940
5003
|
memorySiwxSessions.set(key2, session);
|
|
3941
|
-
const store =
|
|
5004
|
+
const store = storage3();
|
|
3942
5005
|
try {
|
|
3943
5006
|
store?.setItem(key2, JSON.stringify(session));
|
|
3944
5007
|
} catch {
|
|
3945
5008
|
}
|
|
3946
5009
|
};
|
|
3947
5010
|
var clearSiwxSession = (address, _chainId) => {
|
|
3948
|
-
const key2 =
|
|
5011
|
+
const key2 = buildKey3(address);
|
|
3949
5012
|
memorySiwxSessions.delete(key2);
|
|
3950
|
-
const store =
|
|
5013
|
+
const store = storage3();
|
|
3951
5014
|
try {
|
|
3952
5015
|
store?.removeItem(key2);
|
|
3953
5016
|
} catch {
|
|
@@ -3987,8 +5050,8 @@ function buildSIWXConfig(deps) {
|
|
|
3987
5050
|
statement: STATEMENT,
|
|
3988
5051
|
issuedAt,
|
|
3989
5052
|
toString() {
|
|
3990
|
-
return new
|
|
3991
|
-
address: (0,
|
|
5053
|
+
return new import_siwe2.SiweMessage({
|
|
5054
|
+
address: (0, import_viem10.getAddress)(accountAddress),
|
|
3992
5055
|
chainId: numericChainId(chainId),
|
|
3993
5056
|
domain,
|
|
3994
5057
|
uri,
|
|
@@ -4030,7 +5093,7 @@ function buildSIWXConfig(deps) {
|
|
|
4030
5093
|
const persistSession = async (session) => {
|
|
4031
5094
|
const address = session.data.accountAddress;
|
|
4032
5095
|
const id = numericChainId(session.data.chainId);
|
|
4033
|
-
const message = new
|
|
5096
|
+
const message = new import_siwe2.SiweMessage(session.message);
|
|
4034
5097
|
const login = await post("/auth/login", {
|
|
4035
5098
|
message,
|
|
4036
5099
|
signature: session.signature,
|
|
@@ -4079,6 +5142,7 @@ function createOwneySIWX(config) {
|
|
|
4079
5142
|
NotConnectedError,
|
|
4080
5143
|
OwneyError,
|
|
4081
5144
|
OwneySDK,
|
|
5145
|
+
YieldseekerAgent,
|
|
4082
5146
|
createOwneySIWX,
|
|
4083
5147
|
setOwneyDebug
|
|
4084
5148
|
});
|