@owney/sdk 0.7.22-beta.4 → 0.7.22-beta.6

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.
@@ -251,8 +251,20 @@ async function depositSponsored(input) {
251
251
  }
252
252
  return { txHash, vault };
253
253
  }
254
+ var PROCEEDS_POLL_ATTEMPTS = 6;
255
+ var PROCEEDS_POLL_INTERVAL_MS = 1500;
256
+ var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
257
+ async function awaitProceeds(chain, smartAccount, sleep) {
258
+ for (let attempt = 0; attempt < PROCEEDS_POLL_ATTEMPTS; attempt += 1) {
259
+ const balance = await chain.usdcBalanceOf(smartAccount);
260
+ if (balance > 0n) return balance;
261
+ if (attempt < PROCEEDS_POLL_ATTEMPTS - 1) await sleep(PROCEEDS_POLL_INTERVAL_MS);
262
+ }
263
+ return 0n;
264
+ }
254
265
  async function withdrawSponsored(input) {
255
266
  const { api, chain, wallet, amount } = input;
267
+ const sleep = input.sleep ?? defaultSleep;
256
268
  const registered = await api.getVault(wallet.smartAccountAddress);
257
269
  if (!registered.userVaultAddress) {
258
270
  throw new Error("SurfLiquid has no vault registered for this wallet");
@@ -272,7 +284,7 @@ async function withdrawSponsored(input) {
272
284
  return { txHash: sweepHash, amount: stranded.toString() };
273
285
  }
274
286
  try {
275
- const proceeds = await chain.usdcBalanceOf(wallet.smartAccountAddress);
287
+ const proceeds = await awaitProceeds(chain, wallet.smartAccountAddress, sleep);
276
288
  if (proceeds === 0n) return { txHash: withdrawHash, amount: "0" };
277
289
  const sweepHash = await wallet.sendCalls(
278
290
  buildSweepCalls({ owner: wallet.ownerAddress, amount: proceeds })
package/dist/index.cjs CHANGED
@@ -501,7 +501,7 @@ function createSurfBroker(options) {
501
501
  )
502
502
  };
503
503
  }
504
- var SURF_SESSION_HEADER, SURF_BROKER_TIMEOUT_MS, VAULT_MEMO_TTL_MS, AGENT_ID, DISCOVERY_WALLET, SurfSessionExpiredError, asRecord, unavailable;
504
+ var SURF_SESSION_HEADER, SURF_BROKER_TIMEOUT_MS, VAULT_MEMO_TTL_MS, AGENT_ID, DISCOVERY_WALLET, SurfSessionExpiredError, asRecord, unavailable, emptyVaultInfo;
505
505
  var init_surfliquid_broker = __esm({
506
506
  "src/agents/surfliquid/surfliquid.broker.ts"() {
507
507
  "use strict";
@@ -520,6 +520,7 @@ var init_surfliquid_broker = __esm({
520
520
  };
521
521
  asRecord = (value) => value && typeof value === "object" ? value : {};
522
522
  unavailable = (message, details) => new OwneyError("SURF_UNAVAILABLE", message, details, AGENT_ID);
523
+ emptyVaultInfo = () => normalizeVault(void 0);
523
524
  }
524
525
  });
525
526
 
@@ -562,8 +563,18 @@ function mapMessage(message) {
562
563
  }
563
564
  return [base4];
564
565
  }
566
+ function dedupeMessages(messages) {
567
+ const seen = /* @__PURE__ */ new Set();
568
+ return messages.filter((message) => {
569
+ if (!message.txHash) return true;
570
+ const key2 = `${message.transactionType}:${message.txHash}`;
571
+ if (seen.has(key2)) return false;
572
+ seen.add(key2);
573
+ return true;
574
+ });
575
+ }
565
576
  function mapHistory(result, _chainId) {
566
- const data = result.messages.flatMap(mapMessage);
577
+ const data = dedupeMessages(result.messages).flatMap(mapMessage);
567
578
  const hasMore = result.page < result.pages;
568
579
  return {
569
580
  data,
@@ -873,8 +884,17 @@ async function depositSponsored(input) {
873
884
  }
874
885
  return { txHash, vault };
875
886
  }
887
+ async function awaitProceeds(chain, smartAccount, sleep) {
888
+ for (let attempt = 0; attempt < PROCEEDS_POLL_ATTEMPTS; attempt += 1) {
889
+ const balance = await chain.usdcBalanceOf(smartAccount);
890
+ if (balance > 0n) return balance;
891
+ if (attempt < PROCEEDS_POLL_ATTEMPTS - 1) await sleep(PROCEEDS_POLL_INTERVAL_MS);
892
+ }
893
+ return 0n;
894
+ }
876
895
  async function withdrawSponsored(input) {
877
896
  const { api, chain, wallet, amount } = input;
897
+ const sleep = input.sleep ?? defaultSleep;
878
898
  const registered = await api.getVault(wallet.smartAccountAddress);
879
899
  if (!registered.userVaultAddress) {
880
900
  throw new Error("SurfLiquid has no vault registered for this wallet");
@@ -894,7 +914,7 @@ async function withdrawSponsored(input) {
894
914
  return { txHash: sweepHash, amount: stranded.toString() };
895
915
  }
896
916
  try {
897
- const proceeds = await chain.usdcBalanceOf(wallet.smartAccountAddress);
917
+ const proceeds = await awaitProceeds(chain, wallet.smartAccountAddress, sleep);
898
918
  if (proceeds === 0n) return { txHash: withdrawHash, amount: "0" };
899
919
  const sweepHash = await wallet.sendCalls(
900
920
  buildSweepCalls({ owner: wallet.ownerAddress, amount: proceeds })
@@ -906,7 +926,7 @@ async function withdrawSponsored(input) {
906
926
  );
907
927
  }
908
928
  }
909
- var import_viem8, SubmittedError, VaultNotSponsorableError;
929
+ var import_viem8, SubmittedError, VaultNotSponsorableError, PROCEEDS_POLL_ATTEMPTS, PROCEEDS_POLL_INTERVAL_MS, defaultSleep;
910
930
  var init_surfliquid_sponsorship = __esm({
911
931
  "src/agents/surfliquid/surfliquid.sponsorship.ts"() {
912
932
  "use strict";
@@ -928,6 +948,9 @@ var init_surfliquid_sponsorship = __esm({
928
948
  this.name = "VaultNotSponsorableError";
929
949
  }
930
950
  };
951
+ PROCEEDS_POLL_ATTEMPTS = 6;
952
+ PROCEEDS_POLL_INTERVAL_MS = 1500;
953
+ defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
931
954
  }
932
955
  });
933
956
 
@@ -1114,19 +1137,10 @@ async function createSponsoredWallet(input) {
1114
1137
  message: typedData.message
1115
1138
  }),
1116
1139
  // The smart account signs (ERC-1271), not the EOA: SurfLiquid's SIWE session must
1117
- // be the vault owner, which is the smart account. Safe wraps this as a SafeMessage
1118
- // EIP-712 signature the deployed account verifies via isValidSignature.
1140
+ // be the vault owner, which is the smart account. While the account is still
1141
+ // counterfactual, Safe/permissionless wraps this as an EIP-6492 signature SurfLiquid
1142
+ // accepts; the first deposit's userOp deploys the account via its initCode.
1119
1143
  signMessage: (message) => account.signMessage({ message }),
1120
- // Deploy-on-connect: the account is counterfactual until its first userOp, but SIWE
1121
- // (isValidSignature) needs on-chain code. A no-op self-call carries the factory
1122
- // initCode and deploys it. Idempotency is the caller's (it checks isDeployed first).
1123
- deployAccount: async () => {
1124
- const userOpHash = await smartAccountClient.sendUserOperation({
1125
- calls: [{ to: account.address, value: 0n, data: "0x" }]
1126
- });
1127
- const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash: userOpHash });
1128
- return receipt.receipt.transactionHash;
1129
- },
1130
1144
  sendCalls: async (calls) => {
1131
1145
  const userOpHash = await smartAccountClient.sendUserOperation({
1132
1146
  calls: calls.map((c) => ({ to: c.to, data: c.data, value: 0n }))
@@ -1245,10 +1259,6 @@ var init_surfliquid_agent = __esm({
1245
1259
  this.smartAccountAddress = smartAccountAddress;
1246
1260
  const broker = this.brokerFor(smartAccountAddress);
1247
1261
  if (!shouldForce && broker.hasSession()) return broker;
1248
- const { createSponsoredChain: createSponsoredChain2 } = await Promise.resolve().then(() => (init_surfliquid_smart_account(), surfliquid_smart_account_exports));
1249
- if (!await createSponsoredChain2(this.config.rpcUrl).isDeployed(smartAccountAddress)) {
1250
- await wallet.deployAccount();
1251
- }
1252
1262
  const message = await broker.nonce();
1253
1263
  const signature = await wallet.signMessage(message);
1254
1264
  await broker.login(message, signature);
@@ -1296,9 +1306,21 @@ var init_surfliquid_agent = __esm({
1296
1306
  this.brokerWallet = null;
1297
1307
  this.smartAccountAddress = null;
1298
1308
  }
1309
+ // One signature on connect: SIWE the counterfactual smart account (EIP-6492). The
1310
+ // account isn't deployed and no funds move — the first deposit's userOp deploys it.
1299
1311
  async activateAgent(state, _chainId) {
1300
1312
  await this.connect(state);
1301
1313
  }
1314
+ /** A counterfactual account owns no vault and can't be a SIWE signer yet — reads answer empty. */
1315
+ async isSmartAccountDeployed(state) {
1316
+ if (this.broker?.hasSession()) return true;
1317
+ const wallet = await this.sponsoredWallet(state);
1318
+ this.smartAccountAddress = wallet.smartAccountAddress;
1319
+ const { createSponsoredChain: createSponsoredChain2 } = await Promise.resolve().then(() => (init_surfliquid_smart_account(), surfliquid_smart_account_exports));
1320
+ return createSponsoredChain2(this.config.rpcUrl).isDeployed(
1321
+ wallet.smartAccountAddress
1322
+ );
1323
+ }
1302
1324
  // --- IAgent: funds (gasless only) ---
1303
1325
  async deposit(state, _chainId, amount, _asset, _depositCallback) {
1304
1326
  const broker = await this.connect(state);
@@ -1355,6 +1377,7 @@ var init_surfliquid_agent = __esm({
1355
1377
  }
1356
1378
  // --- IAgent: portfolio reads ---
1357
1379
  async vault(state) {
1380
+ if (!await this.isSmartAccountDeployed(state)) return emptyVaultInfo();
1358
1381
  const broker = await this.connect(state);
1359
1382
  return this.withSession(state, () => broker.getVault(this.surfWallet(state)));
1360
1383
  }
@@ -1372,6 +1395,7 @@ var init_surfliquid_agent = __esm({
1372
1395
  return mapAccountApy(await this.vault(state), days);
1373
1396
  }
1374
1397
  async getHistory(state, chainId, options) {
1398
+ if (!await this.isSmartAccountDeployed(state)) return { data: [], hasMore: false };
1375
1399
  const broker = await this.connect(state);
1376
1400
  const page = options?.cursor ? Number(options.cursor) : 1;
1377
1401
  const limit = options?.limit ?? HISTORY_DEFAULT_LIMIT;
package/dist/index.js CHANGED
@@ -3036,7 +3036,7 @@ var OwneySDK = class {
3036
3036
  }
3037
3037
  if (agentId === "surfliquid") {
3038
3038
  if (!key2) return null;
3039
- const { SurfLiquidAgent } = await import("./surfliquid.agent-ZTIK54MW.js");
3039
+ const { SurfLiquidAgent } = await import("./surfliquid.agent-2XTX6I4E.js");
3040
3040
  return new SurfLiquidAgent({
3041
3041
  apiKey: this.apiKey,
3042
3042
  routingApiBaseUrl: this.routingApiBaseUrl
@@ -13,7 +13,7 @@ import {
13
13
  readStuckBalance,
14
14
  sweepStuckBalance,
15
15
  withdrawSponsored
16
- } from "./chunk-T5YJVIQG.js";
16
+ } from "./chunk-CWGCQLB2.js";
17
17
  import "./chunk-AURO3C3R.js";
18
18
 
19
19
  // src/agents/surfliquid/surfliquid.session-store.ts
@@ -96,6 +96,7 @@ function parseJson(text) {
96
96
  return void 0;
97
97
  }
98
98
  }
99
+ var emptyVaultInfo = () => normalizeVault(void 0);
99
100
  function normalizeVault(json) {
100
101
  const root = asRecord(json);
101
102
  const data = asRecord(root.data);
@@ -301,8 +302,18 @@ function mapMessage(message) {
301
302
  }
302
303
  return [base];
303
304
  }
305
+ function dedupeMessages(messages) {
306
+ const seen = /* @__PURE__ */ new Set();
307
+ return messages.filter((message) => {
308
+ if (!message.txHash) return true;
309
+ const key = `${message.transactionType}:${message.txHash}`;
310
+ if (seen.has(key)) return false;
311
+ seen.add(key);
312
+ return true;
313
+ });
314
+ }
304
315
  function mapHistory(result, _chainId) {
305
- const data = result.messages.flatMap(mapMessage);
316
+ const data = dedupeMessages(result.messages).flatMap(mapMessage);
306
317
  const hasMore = result.page < result.pages;
307
318
  return {
308
319
  data,
@@ -532,10 +543,6 @@ var SurfLiquidAgent = class {
532
543
  this.smartAccountAddress = smartAccountAddress;
533
544
  const broker = this.brokerFor(smartAccountAddress);
534
545
  if (!shouldForce && broker.hasSession()) return broker;
535
- const { createSponsoredChain } = await import("./surfliquid.smart-account-YV3PLZ72.js");
536
- if (!await createSponsoredChain(this.config.rpcUrl).isDeployed(smartAccountAddress)) {
537
- await wallet.deployAccount();
538
- }
539
546
  const message = await broker.nonce();
540
547
  const signature = await wallet.signMessage(message);
541
548
  await broker.login(message, signature);
@@ -567,7 +574,7 @@ var SurfLiquidAgent = class {
567
574
  }
568
575
  }
569
576
  async sponsoredWallet(state) {
570
- const { createSponsoredWallet } = await import("./surfliquid.smart-account-YV3PLZ72.js");
577
+ const { createSponsoredWallet } = await import("./surfliquid.smart-account-JHW6VYLJ.js");
571
578
  return createSponsoredWallet({
572
579
  provider: state.provider,
573
580
  ownerAddress: state.walletAddress,
@@ -583,14 +590,26 @@ var SurfLiquidAgent = class {
583
590
  this.brokerWallet = null;
584
591
  this.smartAccountAddress = null;
585
592
  }
593
+ // One signature on connect: SIWE the counterfactual smart account (EIP-6492). The
594
+ // account isn't deployed and no funds move — the first deposit's userOp deploys it.
586
595
  async activateAgent(state, _chainId) {
587
596
  await this.connect(state);
588
597
  }
598
+ /** A counterfactual account owns no vault and can't be a SIWE signer yet — reads answer empty. */
599
+ async isSmartAccountDeployed(state) {
600
+ if (this.broker?.hasSession()) return true;
601
+ const wallet = await this.sponsoredWallet(state);
602
+ this.smartAccountAddress = wallet.smartAccountAddress;
603
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-JHW6VYLJ.js");
604
+ return createSponsoredChain(this.config.rpcUrl).isDeployed(
605
+ wallet.smartAccountAddress
606
+ );
607
+ }
589
608
  // --- IAgent: funds (gasless only) ---
590
609
  async deposit(state, _chainId, amount, _asset, _depositCallback) {
591
610
  const broker = await this.connect(state);
592
611
  const wallet = await this.sponsoredWallet(state);
593
- const { createSponsoredChain } = await import("./surfliquid.smart-account-YV3PLZ72.js");
612
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-JHW6VYLJ.js");
594
613
  try {
595
614
  const { txHash, vault } = await this.withSession(
596
615
  state,
@@ -605,7 +624,7 @@ var SurfLiquidAgent = class {
605
624
  async withdraw(state, _chainId, _token, amount) {
606
625
  const broker = await this.connect(state);
607
626
  const wallet = await this.sponsoredWallet(state);
608
- const { createSponsoredChain } = await import("./surfliquid.smart-account-YV3PLZ72.js");
627
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-JHW6VYLJ.js");
609
628
  try {
610
629
  const result = await this.withSession(
611
630
  state,
@@ -625,14 +644,14 @@ var SurfLiquidAgent = class {
625
644
  /** Funds a prior withdrawal's sweep left in the smart account (never reached the EOA). No session needed — pure read. */
626
645
  async getStuckBalance(state) {
627
646
  const wallet = await this.sponsoredWallet(state);
628
- const { createSponsoredChain } = await import("./surfliquid.smart-account-YV3PLZ72.js");
647
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-JHW6VYLJ.js");
629
648
  const amount = await readStuckBalance({ chain: createSponsoredChain(this.config.rpcUrl), wallet });
630
649
  return { amount: amount.toString(), smartAccountAddress: wallet.smartAccountAddress };
631
650
  }
632
651
  /** Sweep the stranded balance to the owner's EOA — one sponsored userOp, one signature. */
633
652
  async recoverStuckBalance(state) {
634
653
  const wallet = await this.sponsoredWallet(state);
635
- const { createSponsoredChain } = await import("./surfliquid.smart-account-YV3PLZ72.js");
654
+ const { createSponsoredChain } = await import("./surfliquid.smart-account-JHW6VYLJ.js");
636
655
  try {
637
656
  const { txHash, amount } = await sweepStuckBalance({ chain: createSponsoredChain(this.config.rpcUrl), wallet });
638
657
  return { txHash, type: "full", amount: amount.toString() };
@@ -642,6 +661,7 @@ var SurfLiquidAgent = class {
642
661
  }
643
662
  // --- IAgent: portfolio reads ---
644
663
  async vault(state) {
664
+ if (!await this.isSmartAccountDeployed(state)) return emptyVaultInfo();
645
665
  const broker = await this.connect(state);
646
666
  return this.withSession(state, () => broker.getVault(this.surfWallet(state)));
647
667
  }
@@ -659,6 +679,7 @@ var SurfLiquidAgent = class {
659
679
  return mapAccountApy(await this.vault(state), days);
660
680
  }
661
681
  async getHistory(state, chainId, options) {
682
+ if (!await this.isSmartAccountDeployed(state)) return { data: [], hasMore: false };
662
683
  const broker = await this.connect(state);
663
684
  const page = options?.cursor ? Number(options.cursor) : 1;
664
685
  const limit = options?.limit ?? HISTORY_DEFAULT_LIMIT;
@@ -6,7 +6,7 @@ import {
6
6
  SURFLIQUID_VAULT_ABI,
7
7
  SubmittedError,
8
8
  USDC_ABI
9
- } from "./chunk-T5YJVIQG.js";
9
+ } from "./chunk-CWGCQLB2.js";
10
10
  import {
11
11
  readTokenMeta
12
12
  } from "./chunk-AURO3C3R.js";
@@ -114,19 +114,10 @@ async function createSponsoredWallet(input) {
114
114
  message: typedData.message
115
115
  }),
116
116
  // The smart account signs (ERC-1271), not the EOA: SurfLiquid's SIWE session must
117
- // be the vault owner, which is the smart account. Safe wraps this as a SafeMessage
118
- // EIP-712 signature the deployed account verifies via isValidSignature.
117
+ // be the vault owner, which is the smart account. While the account is still
118
+ // counterfactual, Safe/permissionless wraps this as an EIP-6492 signature SurfLiquid
119
+ // accepts; the first deposit's userOp deploys the account via its initCode.
119
120
  signMessage: (message) => account.signMessage({ message }),
120
- // Deploy-on-connect: the account is counterfactual until its first userOp, but SIWE
121
- // (isValidSignature) needs on-chain code. A no-op self-call carries the factory
122
- // initCode and deploys it. Idempotency is the caller's (it checks isDeployed first).
123
- deployAccount: async () => {
124
- const userOpHash = await smartAccountClient.sendUserOperation({
125
- calls: [{ to: account.address, value: 0n, data: "0x" }]
126
- });
127
- const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash: userOpHash });
128
- return receipt.receipt.transactionHash;
129
- },
130
121
  sendCalls: async (calls) => {
131
122
  const userOpHash = await smartAccountClient.sendUserOperation({
132
123
  calls: calls.map((c) => ({ to: c.to, data: c.data, value: 0n }))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.7.22-beta.4",
3
+ "version": "0.7.22-beta.6",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",