@owney/sdk 0.4.4 → 0.5.0-beta.0

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/dist/index.cjs CHANGED
@@ -240,7 +240,6 @@ function mapApyHistory(raw) {
240
240
  }
241
241
  function mapEntries(rawEntries, chainId) {
242
242
  const actionMap = {
243
- "Deposit to protocol": "Deposit",
244
243
  "Top up wallet": "Top up",
245
244
  "Partial withdraw from protocol": "Withdraw",
246
245
  "Withdraw from protocol": "Withdraw",
@@ -662,7 +661,7 @@ var ZyfaiAgent = class {
662
661
  }
663
662
  async getEarnings(state, chainId) {
664
663
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
665
- const raw = await this.sdk.calculateOnchainEarnings(smartWallet);
664
+ const raw = await this.sdk.getOnchainEarnings(smartWallet);
666
665
  return mapEarnings(raw, smartWallet);
667
666
  }
668
667
  async getAccountApy(state, chainId, days) {
@@ -688,17 +687,24 @@ var ZyfaiAgent = class {
688
687
  fromDate: options?.fromDate,
689
688
  toDate
690
689
  });
691
- matched.push(...raw.data.filter((e) => e.chainId === validChainId));
690
+ raw.data.forEach((entry, idx) => {
691
+ if (entry.chainId === validChainId && entry.action !== "Deposit to protocol") {
692
+ matched.push({ entry, rawIdx: offset + idx });
693
+ }
694
+ });
692
695
  offset += raw.data.length;
693
696
  if (raw.data.length < backendPageSize) {
694
697
  backendExhausted = true;
695
698
  break;
696
699
  }
697
700
  }
698
- const overshoot = Math.max(0, matched.length - limit);
699
- const pageData = matched.slice(0, limit);
700
- const hasMore = !backendExhausted || overshoot > 0;
701
- const nextCursor = hasMore ? encodeHistoryCursor({ toDate, rawOffset: offset - overshoot }) : void 0;
701
+ const pageData = matched.slice(0, limit).map((m) => m.entry);
702
+ const firstOvershoot = matched[limit];
703
+ const hasMore = !backendExhausted || firstOvershoot !== void 0;
704
+ const nextCursor = hasMore ? encodeHistoryCursor({
705
+ toDate,
706
+ rawOffset: firstOvershoot ? firstOvershoot.rawIdx : offset
707
+ }) : void 0;
702
708
  return {
703
709
  data: mapEntries(pageData, validChainId),
704
710
  hasMore,
@@ -927,6 +933,130 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
927
933
  return out;
928
934
  }
929
935
 
936
+ // src/client.ts
937
+ var import_viem3 = require("viem");
938
+ var import_chains = require("viem/chains");
939
+
940
+ // src/lib/transfer-auth.ts
941
+ var import_viem2 = require("viem");
942
+ var ERC20_META_ABI = [
943
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
944
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
945
+ ];
946
+ function buildTransferWithAuthorizationTypedData(input) {
947
+ return {
948
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
949
+ types: {
950
+ TransferWithAuthorization: [
951
+ { name: "from", type: "address" },
952
+ { name: "to", type: "address" },
953
+ { name: "value", type: "uint256" },
954
+ { name: "validAfter", type: "uint256" },
955
+ { name: "validBefore", type: "uint256" },
956
+ { name: "nonce", type: "bytes32" }
957
+ ]
958
+ },
959
+ primaryType: "TransferWithAuthorization",
960
+ message: input.message
961
+ };
962
+ }
963
+ async function readTokenMeta(publicClient, token) {
964
+ const [tokenName, tokenVersion] = await Promise.all([
965
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
966
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
967
+ ]);
968
+ return { tokenName, tokenVersion };
969
+ }
970
+ function randomAuthNonce() {
971
+ const bytes = new Uint8Array(32);
972
+ globalThis.crypto.getRandomValues(bytes);
973
+ return (0, import_viem2.bytesToHex)(bytes);
974
+ }
975
+
976
+ // src/lib/sponsor-client.ts
977
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
978
+ async function postSponsorTransferAuth(input) {
979
+ const base2 = input.baseUrl ?? ROUTING_API_BASE_URL3;
980
+ let res;
981
+ try {
982
+ res = await fetch(`${base2}/api/v1/sponsor/erc20-transfer-auth`, {
983
+ method: "POST",
984
+ headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
985
+ body: JSON.stringify(input.body)
986
+ });
987
+ } catch (networkError) {
988
+ throw new OwneyError(
989
+ "SPONSOR_REQUEST_FAILED",
990
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
991
+ { cause: String(networkError) }
992
+ );
993
+ }
994
+ const text = await res.text();
995
+ let parsed = null;
996
+ try {
997
+ parsed = JSON.parse(text);
998
+ } catch {
999
+ }
1000
+ if (!res.ok || !parsed?.success || !parsed.data) {
1001
+ throw new OwneyError(
1002
+ "SPONSOR_REQUEST_FAILED",
1003
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
1004
+ { statusCode: res.status, responseBody: text.slice(0, 500) }
1005
+ );
1006
+ }
1007
+ return parsed.data;
1008
+ }
1009
+
1010
+ // src/lib/sponsored-deposit.ts
1011
+ var AUTH_WINDOW_SECONDS = 15 * 60;
1012
+ function makeSponsoredDepositCallback(deps) {
1013
+ const post = deps.httpPost ?? postSponsorTransferAuth;
1014
+ return async (smartWallet, chainId, amount) => {
1015
+ const cid = chainId;
1016
+ const token = deps.tokenAddressByChain[cid];
1017
+ if (!token) {
1018
+ throw new OwneyError(
1019
+ "CHAIN_UNSUPPORTED",
1020
+ `No sponsored token configured for chain ${chainId}`
1021
+ );
1022
+ }
1023
+ const pub = deps.getPublicClient(cid);
1024
+ const wallet = deps.getWalletClient(cid);
1025
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
1026
+ const validAfter = 0n;
1027
+ const validBefore = BigInt(Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS);
1028
+ const nonce = randomAuthNonce();
1029
+ const typedData = buildTransferWithAuthorizationTypedData({
1030
+ token,
1031
+ chainId: cid,
1032
+ tokenName,
1033
+ tokenVersion,
1034
+ message: { from: deps.ownerAddress, to: smartWallet, value: BigInt(amount), validAfter, validBefore, nonce }
1035
+ });
1036
+ const authSignature = await wallet.signTypedData(
1037
+ { account: deps.ownerAddress, ...typedData }
1038
+ );
1039
+ const result = await post({
1040
+ baseUrl: deps.baseUrl,
1041
+ apiKey: deps.apiKey,
1042
+ body: {
1043
+ chainId: cid,
1044
+ token,
1045
+ from: deps.ownerAddress,
1046
+ to: smartWallet,
1047
+ value: amount,
1048
+ validAfter: validAfter.toString(),
1049
+ validBefore: validBefore.toString(),
1050
+ nonce,
1051
+ authSignature,
1052
+ tokenName,
1053
+ tokenVersion
1054
+ }
1055
+ });
1056
+ return result.txHash;
1057
+ };
1058
+ }
1059
+
930
1060
  // src/client.ts
931
1061
  function encodeMultiAgentCursor(map) {
932
1062
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -948,6 +1078,11 @@ function decodeMultiAgentCursor(token) {
948
1078
  }
949
1079
  return parsed;
950
1080
  }
1081
+ var SPONSORED_USDC_BY_CHAIN = {
1082
+ 8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1083
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1084
+ };
1085
+ var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum };
951
1086
  var OwneySDK = class {
952
1087
  agents = /* @__PURE__ */ new Map();
953
1088
  activeAgents = /* @__PURE__ */ new Set();
@@ -960,6 +1095,7 @@ var OwneySDK = class {
960
1095
  state = null;
961
1096
  apiKey;
962
1097
  zyfaiRpcUrls;
1098
+ cachedSponsoredCallback = null;
963
1099
  initializingAgentsPromise = null;
964
1100
  constructor(config) {
965
1101
  this.apiKey = config.apiKey;
@@ -983,6 +1119,7 @@ var OwneySDK = class {
983
1119
  );
984
1120
  }
985
1121
  this.state = { provider, walletAddress, chainId: null };
1122
+ this.cachedSponsoredCallback = null;
986
1123
  }
987
1124
  /**
988
1125
  * Disconnect from all agents and clear connection state.
@@ -995,6 +1132,7 @@ var OwneySDK = class {
995
1132
  this.activeAgents.clear();
996
1133
  this.disabledAgents.clear();
997
1134
  this.state = null;
1135
+ this.cachedSponsoredCallback = null;
998
1136
  }
999
1137
  isAgentDisabled(agentId) {
1000
1138
  return this.disabledAgents.has(agentId);
@@ -1028,6 +1166,46 @@ var OwneySDK = class {
1028
1166
  }
1029
1167
  return chainId;
1030
1168
  }
1169
+ requireConnectedProvider() {
1170
+ if (!this.state?.provider) {
1171
+ throw new OwneyError(
1172
+ "NOT_CONNECTED",
1173
+ "connect(provider) must be called before deposit",
1174
+ {}
1175
+ );
1176
+ }
1177
+ return this.state.provider;
1178
+ }
1179
+ /**
1180
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
1181
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
1182
+ * provider with viem `custom(provider)` to read token meta and sign the
1183
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
1184
+ */
1185
+ getDefaultSponsoredCallback() {
1186
+ if (this.cachedSponsoredCallback)
1187
+ return this.cachedSponsoredCallback;
1188
+ const provider = this.requireConnectedProvider();
1189
+ const owner = this.state.walletAddress;
1190
+ this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1191
+ apiKey: this.apiKey,
1192
+ ownerAddress: owner,
1193
+ tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1194
+ // Casts work around viem's chain-narrowed Client vs the generic
1195
+ // PublicClient/WalletClient param types — structurally identical at
1196
+ // runtime, but the two share a name TS treats as unrelated.
1197
+ getPublicClient: (cid) => (0, import_viem3.createPublicClient)({
1198
+ chain: VIEM_CHAIN[cid],
1199
+ transport: (0, import_viem3.custom)(provider)
1200
+ }),
1201
+ getWalletClient: (cid) => (0, import_viem3.createWalletClient)({
1202
+ account: owner,
1203
+ chain: VIEM_CHAIN[cid],
1204
+ transport: (0, import_viem3.custom)(provider)
1205
+ })
1206
+ });
1207
+ return this.cachedSponsoredCallback;
1208
+ }
1031
1209
  getAgent(agentId) {
1032
1210
  const agent = this.agents.get(agentId);
1033
1211
  if (!agent) {
@@ -1181,6 +1359,7 @@ var OwneySDK = class {
1181
1359
  }
1182
1360
  const state = this.requireState();
1183
1361
  const chainId = this.requireChainId();
1362
+ const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
1184
1363
  if (agentId) {
1185
1364
  if (this.isAgentDisabled(agentId)) {
1186
1365
  throw new OwneyError(
@@ -1195,7 +1374,7 @@ var OwneySDK = class {
1195
1374
  return withFailureReporting(
1196
1375
  this.apiKey,
1197
1376
  agent.id,
1198
- () => agent.deposit(state, chainId, amount, depositCallback)
1377
+ () => agent.deposit(state, chainId, amount, effectiveCallback)
1199
1378
  );
1200
1379
  }
1201
1380
  const totalAmount = BigInt(amount);
@@ -1229,7 +1408,7 @@ var OwneySDK = class {
1229
1408
  agentResults[agent.id] = await withFailureReporting(
1230
1409
  this.apiKey,
1231
1410
  agent.id,
1232
- () => agent.deposit(state, chainId, agentAmount, depositCallback)
1411
+ () => agent.deposit(state, chainId, agentAmount, effectiveCallback)
1233
1412
  );
1234
1413
  }
1235
1414
  return { agentResults };
@@ -1677,9 +1856,20 @@ var OwneySDK = class {
1677
1856
  hasMore = true;
1678
1857
  }
1679
1858
  }
1680
- merged.sort(
1681
- (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
1682
- );
1859
+ merged.sort((a, b) => {
1860
+ const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime();
1861
+ if (dateDiff !== 0)
1862
+ return dateDiff;
1863
+ if (a.agent !== b.agent)
1864
+ return a.agent < b.agent ? -1 : 1;
1865
+ const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
1866
+ const bTx = b.transactions?.[0]?.txHashes?.[0] ?? "";
1867
+ if (aTx !== bTx)
1868
+ return aTx < bTx ? -1 : 1;
1869
+ if (a.action !== b.action)
1870
+ return a.action < b.action ? -1 : 1;
1871
+ return 0;
1872
+ });
1683
1873
  return {
1684
1874
  data: merged,
1685
1875
  hasMore,
package/dist/index.d.cts CHANGED
@@ -99,7 +99,7 @@ type AgentsApyOptions = {
99
99
  type DepositOptions = {
100
100
  amount: string;
101
101
  asset: Asset;
102
- depositCallback: DepositCallback;
102
+ depositCallback?: DepositCallback;
103
103
  agentId?: AgentId;
104
104
  };
105
105
 
@@ -170,6 +170,12 @@ interface RebalanceLog {
170
170
  amount: string;
171
171
  status: "success" | "failed";
172
172
  }
173
+ interface AgentHistoryPosition {
174
+ protocol: string;
175
+ pool?: string;
176
+ tokenSymbol?: string;
177
+ amount?: string;
178
+ }
173
179
  interface AgentHistoryEntry {
174
180
  agent: AgentId;
175
181
  action: HistoryAction;
@@ -178,6 +184,7 @@ interface AgentHistoryEntry {
178
184
  newApy: string | null;
179
185
  transactions: HistoryTransaction[];
180
186
  rebalanceLog: RebalanceLog[];
187
+ positions?: AgentHistoryPosition[];
181
188
  }
182
189
  interface OwneyAgentHistory {
183
190
  data: AgentHistoryEntry[];
@@ -228,6 +235,7 @@ declare class OwneySDK {
228
235
  private state;
229
236
  private apiKey;
230
237
  private zyfaiRpcUrls?;
238
+ private cachedSponsoredCallback;
231
239
  private initializingAgentsPromise;
232
240
  constructor(config: OwneySDKConfig);
233
241
  /**
@@ -254,6 +262,14 @@ declare class OwneySDK {
254
262
  getActiveChainId(): number | null;
255
263
  private requireState;
256
264
  private requireChainId;
265
+ private requireConnectedProvider;
266
+ /**
267
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
268
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
269
+ * provider with viem `custom(provider)` to read token meta and sign the
270
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
271
+ */
272
+ private getDefaultSponsoredCallback;
257
273
  private getAgent;
258
274
  private getActiveAgents;
259
275
  private ensureAgentsInitialized;
@@ -381,7 +397,7 @@ declare class OwneySDK {
381
397
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
382
398
  }
383
399
 
384
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
400
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
385
401
  declare class OwneyError extends Error {
386
402
  readonly code: OwneyErrorCode;
387
403
  readonly details?: Record<string, unknown>;
@@ -411,4 +427,4 @@ declare class InvalidHistoryCursorError extends Error {
411
427
  constructor(message: string);
412
428
  }
413
429
 
414
- export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAsset, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, ApyByChainAndAsset, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, InvalidHistoryCursorError, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneySDK, OwneySDKConfig, OwneySupportedChainId, OwneySupportedChains, OwneySupportedTokens, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
430
+ export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentHistoryPosition, AgentId, AgentNotFoundError, AgentSupportedAsset, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, ApyByChainAndAsset, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, InvalidHistoryCursorError, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneySDK, OwneySDKConfig, OwneySupportedChainId, OwneySupportedChains, OwneySupportedTokens, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
package/dist/index.d.ts CHANGED
@@ -99,7 +99,7 @@ type AgentsApyOptions = {
99
99
  type DepositOptions = {
100
100
  amount: string;
101
101
  asset: Asset;
102
- depositCallback: DepositCallback;
102
+ depositCallback?: DepositCallback;
103
103
  agentId?: AgentId;
104
104
  };
105
105
 
@@ -170,6 +170,12 @@ interface RebalanceLog {
170
170
  amount: string;
171
171
  status: "success" | "failed";
172
172
  }
173
+ interface AgentHistoryPosition {
174
+ protocol: string;
175
+ pool?: string;
176
+ tokenSymbol?: string;
177
+ amount?: string;
178
+ }
173
179
  interface AgentHistoryEntry {
174
180
  agent: AgentId;
175
181
  action: HistoryAction;
@@ -178,6 +184,7 @@ interface AgentHistoryEntry {
178
184
  newApy: string | null;
179
185
  transactions: HistoryTransaction[];
180
186
  rebalanceLog: RebalanceLog[];
187
+ positions?: AgentHistoryPosition[];
181
188
  }
182
189
  interface OwneyAgentHistory {
183
190
  data: AgentHistoryEntry[];
@@ -228,6 +235,7 @@ declare class OwneySDK {
228
235
  private state;
229
236
  private apiKey;
230
237
  private zyfaiRpcUrls?;
238
+ private cachedSponsoredCallback;
231
239
  private initializingAgentsPromise;
232
240
  constructor(config: OwneySDKConfig);
233
241
  /**
@@ -254,6 +262,14 @@ declare class OwneySDK {
254
262
  getActiveChainId(): number | null;
255
263
  private requireState;
256
264
  private requireChainId;
265
+ private requireConnectedProvider;
266
+ /**
267
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
268
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
269
+ * provider with viem `custom(provider)` to read token meta and sign the
270
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
271
+ */
272
+ private getDefaultSponsoredCallback;
257
273
  private getAgent;
258
274
  private getActiveAgents;
259
275
  private ensureAgentsInitialized;
@@ -381,7 +397,7 @@ declare class OwneySDK {
381
397
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
382
398
  }
383
399
 
384
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
400
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "SPONSOR_REQUEST_FAILED" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
385
401
  declare class OwneyError extends Error {
386
402
  readonly code: OwneyErrorCode;
387
403
  readonly details?: Record<string, unknown>;
@@ -411,4 +427,4 @@ declare class InvalidHistoryCursorError extends Error {
411
427
  constructor(message: string);
412
428
  }
413
429
 
414
- export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentId, AgentNotFoundError, AgentSupportedAsset, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, ApyByChainAndAsset, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, InvalidHistoryCursorError, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneySDK, OwneySDKConfig, OwneySupportedChainId, OwneySupportedChains, OwneySupportedTokens, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
430
+ export { AccountAgentApy, AccountApyOptions, AgentApy, AgentApyDetails, AgentBalance, AgentChainIncompatibleError, AgentEarnings, AgentHistoryEntry, AgentHistoryPosition, AgentId, AgentNotFoundError, AgentSupportedAsset, AgentSupportedAssets, AgentUserProfile, AgentWithdrawResult, AgentsApyOptions, ApyByChainAndAsset, Asset, ConnectionState, DailyApyDays, DepositCallback, DepositOptions, HistoryAction, HistoryFilters, HistoryOptions, HistoryTransaction, IAgent, InvalidHistoryCursorError, NotConnectedError, OwneyAccountApy, OwneyAgentAllocation, OwneyAgentApy, OwneyAgentHistory, OwneyAgentStatus, OwneyBalances, OwneyDepositResult, OwneyEarnings, OwneyError, OwneyErrorCode, OwneyMultiDepositResult, OwneySDK, OwneySDKConfig, OwneySupportedChainId, OwneySupportedChains, OwneySupportedTokens, OwneyToken, OwneyUserProfile, OwneyWithdrawResult, RebalanceLog, WithdrawOptions };
package/dist/index.js CHANGED
@@ -209,7 +209,6 @@ function mapApyHistory(raw) {
209
209
  }
210
210
  function mapEntries(rawEntries, chainId) {
211
211
  const actionMap = {
212
- "Deposit to protocol": "Deposit",
213
212
  "Top up wallet": "Top up",
214
213
  "Partial withdraw from protocol": "Withdraw",
215
214
  "Withdraw from protocol": "Withdraw",
@@ -631,7 +630,7 @@ var ZyfaiAgent = class {
631
630
  }
632
631
  async getEarnings(state, chainId) {
633
632
  const { smartWallet } = await this.resolveSmartWallet(state, chainId);
634
- const raw = await this.sdk.calculateOnchainEarnings(smartWallet);
633
+ const raw = await this.sdk.getOnchainEarnings(smartWallet);
635
634
  return mapEarnings(raw, smartWallet);
636
635
  }
637
636
  async getAccountApy(state, chainId, days) {
@@ -657,17 +656,24 @@ var ZyfaiAgent = class {
657
656
  fromDate: options?.fromDate,
658
657
  toDate
659
658
  });
660
- matched.push(...raw.data.filter((e) => e.chainId === validChainId));
659
+ raw.data.forEach((entry, idx) => {
660
+ if (entry.chainId === validChainId && entry.action !== "Deposit to protocol") {
661
+ matched.push({ entry, rawIdx: offset + idx });
662
+ }
663
+ });
661
664
  offset += raw.data.length;
662
665
  if (raw.data.length < backendPageSize) {
663
666
  backendExhausted = true;
664
667
  break;
665
668
  }
666
669
  }
667
- const overshoot = Math.max(0, matched.length - limit);
668
- const pageData = matched.slice(0, limit);
669
- const hasMore = !backendExhausted || overshoot > 0;
670
- const nextCursor = hasMore ? encodeHistoryCursor({ toDate, rawOffset: offset - overshoot }) : void 0;
670
+ const pageData = matched.slice(0, limit).map((m) => m.entry);
671
+ const firstOvershoot = matched[limit];
672
+ const hasMore = !backendExhausted || firstOvershoot !== void 0;
673
+ const nextCursor = hasMore ? encodeHistoryCursor({
674
+ toDate,
675
+ rawOffset: firstOvershoot ? firstOvershoot.rawIdx : offset
676
+ }) : void 0;
671
677
  return {
672
678
  data: mapEntries(pageData, validChainId),
673
679
  hasMore,
@@ -896,6 +902,134 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
896
902
  return out;
897
903
  }
898
904
 
905
+ // src/client.ts
906
+ import {
907
+ createPublicClient,
908
+ createWalletClient,
909
+ custom
910
+ } from "viem";
911
+ import { base, arbitrum } from "viem/chains";
912
+
913
+ // src/lib/transfer-auth.ts
914
+ import { bytesToHex } from "viem";
915
+ var ERC20_META_ABI = [
916
+ { type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] },
917
+ { type: "function", name: "version", stateMutability: "view", inputs: [], outputs: [{ name: "", type: "string" }] }
918
+ ];
919
+ function buildTransferWithAuthorizationTypedData(input) {
920
+ return {
921
+ domain: { name: input.tokenName, version: input.tokenVersion, chainId: input.chainId, verifyingContract: input.token },
922
+ types: {
923
+ TransferWithAuthorization: [
924
+ { name: "from", type: "address" },
925
+ { name: "to", type: "address" },
926
+ { name: "value", type: "uint256" },
927
+ { name: "validAfter", type: "uint256" },
928
+ { name: "validBefore", type: "uint256" },
929
+ { name: "nonce", type: "bytes32" }
930
+ ]
931
+ },
932
+ primaryType: "TransferWithAuthorization",
933
+ message: input.message
934
+ };
935
+ }
936
+ async function readTokenMeta(publicClient, token) {
937
+ const [tokenName, tokenVersion] = await Promise.all([
938
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "name" }),
939
+ publicClient.readContract({ address: token, abi: ERC20_META_ABI, functionName: "version" }).catch(() => "2")
940
+ ]);
941
+ return { tokenName, tokenVersion };
942
+ }
943
+ function randomAuthNonce() {
944
+ const bytes = new Uint8Array(32);
945
+ globalThis.crypto.getRandomValues(bytes);
946
+ return bytesToHex(bytes);
947
+ }
948
+
949
+ // src/lib/sponsor-client.ts
950
+ var ROUTING_API_BASE_URL3 = "https://owney-routing-api-243946518160.europe-west4.run.app";
951
+ async function postSponsorTransferAuth(input) {
952
+ const base2 = input.baseUrl ?? ROUTING_API_BASE_URL3;
953
+ let res;
954
+ try {
955
+ res = await fetch(`${base2}/api/v1/sponsor/erc20-transfer-auth`, {
956
+ method: "POST",
957
+ headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey },
958
+ body: JSON.stringify(input.body)
959
+ });
960
+ } catch (networkError) {
961
+ throw new OwneyError(
962
+ "SPONSOR_REQUEST_FAILED",
963
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
964
+ { cause: String(networkError) }
965
+ );
966
+ }
967
+ const text = await res.text();
968
+ let parsed = null;
969
+ try {
970
+ parsed = JSON.parse(text);
971
+ } catch {
972
+ }
973
+ if (!res.ok || !parsed?.success || !parsed.data) {
974
+ throw new OwneyError(
975
+ "SPONSOR_REQUEST_FAILED",
976
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
977
+ { statusCode: res.status, responseBody: text.slice(0, 500) }
978
+ );
979
+ }
980
+ return parsed.data;
981
+ }
982
+
983
+ // src/lib/sponsored-deposit.ts
984
+ var AUTH_WINDOW_SECONDS = 15 * 60;
985
+ function makeSponsoredDepositCallback(deps) {
986
+ const post = deps.httpPost ?? postSponsorTransferAuth;
987
+ return async (smartWallet, chainId, amount) => {
988
+ const cid = chainId;
989
+ const token = deps.tokenAddressByChain[cid];
990
+ if (!token) {
991
+ throw new OwneyError(
992
+ "CHAIN_UNSUPPORTED",
993
+ `No sponsored token configured for chain ${chainId}`
994
+ );
995
+ }
996
+ const pub = deps.getPublicClient(cid);
997
+ const wallet = deps.getWalletClient(cid);
998
+ const { tokenName, tokenVersion } = await readTokenMeta(pub, token);
999
+ const validAfter = 0n;
1000
+ const validBefore = BigInt(Math.floor(Date.now() / 1e3) + AUTH_WINDOW_SECONDS);
1001
+ const nonce = randomAuthNonce();
1002
+ const typedData = buildTransferWithAuthorizationTypedData({
1003
+ token,
1004
+ chainId: cid,
1005
+ tokenName,
1006
+ tokenVersion,
1007
+ message: { from: deps.ownerAddress, to: smartWallet, value: BigInt(amount), validAfter, validBefore, nonce }
1008
+ });
1009
+ const authSignature = await wallet.signTypedData(
1010
+ { account: deps.ownerAddress, ...typedData }
1011
+ );
1012
+ const result = await post({
1013
+ baseUrl: deps.baseUrl,
1014
+ apiKey: deps.apiKey,
1015
+ body: {
1016
+ chainId: cid,
1017
+ token,
1018
+ from: deps.ownerAddress,
1019
+ to: smartWallet,
1020
+ value: amount,
1021
+ validAfter: validAfter.toString(),
1022
+ validBefore: validBefore.toString(),
1023
+ nonce,
1024
+ authSignature,
1025
+ tokenName,
1026
+ tokenVersion
1027
+ }
1028
+ });
1029
+ return result.txHash;
1030
+ };
1031
+ }
1032
+
899
1033
  // src/client.ts
900
1034
  function encodeMultiAgentCursor(map) {
901
1035
  return Buffer.from(JSON.stringify(map), "utf8").toString("base64");
@@ -917,6 +1051,11 @@ function decodeMultiAgentCursor(token) {
917
1051
  }
918
1052
  return parsed;
919
1053
  }
1054
+ var SPONSORED_USDC_BY_CHAIN = {
1055
+ 8453: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1056
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
1057
+ };
1058
+ var VIEM_CHAIN = { 8453: base, 42161: arbitrum };
920
1059
  var OwneySDK = class {
921
1060
  agents = /* @__PURE__ */ new Map();
922
1061
  activeAgents = /* @__PURE__ */ new Set();
@@ -929,6 +1068,7 @@ var OwneySDK = class {
929
1068
  state = null;
930
1069
  apiKey;
931
1070
  zyfaiRpcUrls;
1071
+ cachedSponsoredCallback = null;
932
1072
  initializingAgentsPromise = null;
933
1073
  constructor(config) {
934
1074
  this.apiKey = config.apiKey;
@@ -952,6 +1092,7 @@ var OwneySDK = class {
952
1092
  );
953
1093
  }
954
1094
  this.state = { provider, walletAddress, chainId: null };
1095
+ this.cachedSponsoredCallback = null;
955
1096
  }
956
1097
  /**
957
1098
  * Disconnect from all agents and clear connection state.
@@ -964,6 +1105,7 @@ var OwneySDK = class {
964
1105
  this.activeAgents.clear();
965
1106
  this.disabledAgents.clear();
966
1107
  this.state = null;
1108
+ this.cachedSponsoredCallback = null;
967
1109
  }
968
1110
  isAgentDisabled(agentId) {
969
1111
  return this.disabledAgents.has(agentId);
@@ -997,6 +1139,46 @@ var OwneySDK = class {
997
1139
  }
998
1140
  return chainId;
999
1141
  }
1142
+ requireConnectedProvider() {
1143
+ if (!this.state?.provider) {
1144
+ throw new OwneyError(
1145
+ "NOT_CONNECTED",
1146
+ "connect(provider) must be called before deposit",
1147
+ {}
1148
+ );
1149
+ }
1150
+ return this.state.provider;
1151
+ }
1152
+ /**
1153
+ * Lazily builds (and caches) the default EIP-3009 sponsored deposit callback
1154
+ * used when the caller omits `depositCallback`. Wraps the connected EIP-1193
1155
+ * provider with viem `custom(provider)` to read token meta and sign the
1156
+ * `TransferWithAuthorization`, then POSTs to the sponsor API.
1157
+ */
1158
+ getDefaultSponsoredCallback() {
1159
+ if (this.cachedSponsoredCallback)
1160
+ return this.cachedSponsoredCallback;
1161
+ const provider = this.requireConnectedProvider();
1162
+ const owner = this.state.walletAddress;
1163
+ this.cachedSponsoredCallback = makeSponsoredDepositCallback({
1164
+ apiKey: this.apiKey,
1165
+ ownerAddress: owner,
1166
+ tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
1167
+ // Casts work around viem's chain-narrowed Client vs the generic
1168
+ // PublicClient/WalletClient param types — structurally identical at
1169
+ // runtime, but the two share a name TS treats as unrelated.
1170
+ getPublicClient: (cid) => createPublicClient({
1171
+ chain: VIEM_CHAIN[cid],
1172
+ transport: custom(provider)
1173
+ }),
1174
+ getWalletClient: (cid) => createWalletClient({
1175
+ account: owner,
1176
+ chain: VIEM_CHAIN[cid],
1177
+ transport: custom(provider)
1178
+ })
1179
+ });
1180
+ return this.cachedSponsoredCallback;
1181
+ }
1000
1182
  getAgent(agentId) {
1001
1183
  const agent = this.agents.get(agentId);
1002
1184
  if (!agent) {
@@ -1150,6 +1332,7 @@ var OwneySDK = class {
1150
1332
  }
1151
1333
  const state = this.requireState();
1152
1334
  const chainId = this.requireChainId();
1335
+ const effectiveCallback = depositCallback ?? this.getDefaultSponsoredCallback();
1153
1336
  if (agentId) {
1154
1337
  if (this.isAgentDisabled(agentId)) {
1155
1338
  throw new OwneyError(
@@ -1164,7 +1347,7 @@ var OwneySDK = class {
1164
1347
  return withFailureReporting(
1165
1348
  this.apiKey,
1166
1349
  agent.id,
1167
- () => agent.deposit(state, chainId, amount, depositCallback)
1350
+ () => agent.deposit(state, chainId, amount, effectiveCallback)
1168
1351
  );
1169
1352
  }
1170
1353
  const totalAmount = BigInt(amount);
@@ -1198,7 +1381,7 @@ var OwneySDK = class {
1198
1381
  agentResults[agent.id] = await withFailureReporting(
1199
1382
  this.apiKey,
1200
1383
  agent.id,
1201
- () => agent.deposit(state, chainId, agentAmount, depositCallback)
1384
+ () => agent.deposit(state, chainId, agentAmount, effectiveCallback)
1202
1385
  );
1203
1386
  }
1204
1387
  return { agentResults };
@@ -1646,9 +1829,20 @@ var OwneySDK = class {
1646
1829
  hasMore = true;
1647
1830
  }
1648
1831
  }
1649
- merged.sort(
1650
- (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
1651
- );
1832
+ merged.sort((a, b) => {
1833
+ const dateDiff = new Date(b.date).getTime() - new Date(a.date).getTime();
1834
+ if (dateDiff !== 0)
1835
+ return dateDiff;
1836
+ if (a.agent !== b.agent)
1837
+ return a.agent < b.agent ? -1 : 1;
1838
+ const aTx = a.transactions?.[0]?.txHashes?.[0] ?? "";
1839
+ const bTx = b.transactions?.[0]?.txHashes?.[0] ?? "";
1840
+ if (aTx !== bTx)
1841
+ return aTx < bTx ? -1 : 1;
1842
+ if (a.action !== b.action)
1843
+ return a.action < b.action ? -1 : 1;
1844
+ return 0;
1845
+ });
1652
1846
  return {
1653
1847
  data: merged,
1654
1848
  hasMore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.4.4",
3
+ "version": "0.5.0-beta.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -35,4 +35,4 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  }
38
- }
38
+ }