@sherwoodagent/cli 0.73.2 → 0.73.4

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.
Files changed (56) hide show
  1. package/dist/{addresses-YDPUPGWQ.js → addresses-DGB3D457.js} +3 -3
  2. package/dist/{agent-AML5SIMT.js → agent-UNSW5IPO.js} +6 -6
  3. package/dist/{chat-6RO4RS3L.js → chat-KT2WVDT2.js} +8 -8
  4. package/dist/{chunk-AGZQWXAR.js → chunk-2U4TWPWQ.js} +4 -4
  5. package/dist/{chunk-RQZ53CV4.js → chunk-BKJ7HYX7.js} +2 -2
  6. package/dist/{chunk-XJJEZFOA.js → chunk-CHWBQJLT.js} +5 -5
  7. package/dist/{chunk-FNJHLDRL.js → chunk-CZRJ5GWE.js} +15 -11
  8. package/dist/chunk-CZRJ5GWE.js.map +1 -0
  9. package/dist/{chunk-5ECJMM3K.js → chunk-DIZEB2VV.js} +4 -4
  10. package/dist/{chunk-HYUUR6SC.js → chunk-DXJJMRBM.js} +13 -3
  11. package/dist/chunk-DXJJMRBM.js.map +1 -0
  12. package/dist/{chunk-XMYD5SES.js → chunk-GU5VZGXB.js} +4 -4
  13. package/dist/{chunk-RAITRCM7.js → chunk-IFL5SREW.js} +2 -2
  14. package/dist/{chunk-WO4YDEDH.js → chunk-NPWYX6NU.js} +2 -2
  15. package/dist/{chunk-LZH4PJI4.js → chunk-ROKE6ZAW.js} +24 -4
  16. package/dist/chunk-ROKE6ZAW.js.map +1 -0
  17. package/dist/{chunk-TSNHEU2C.js → chunk-ZURDCYGU.js} +4 -4
  18. package/dist/{client-HMUVPZEG.js → client-NAL3HVLK.js} +7 -3
  19. package/dist/{eas-GDQYAUXR.js → eas-QAH7XVLK.js} +5 -5
  20. package/dist/{governor-PX6AMIAU.js → governor-S56M6BV6.js} +5 -5
  21. package/dist/index.js +44 -41
  22. package/dist/index.js.map +1 -1
  23. package/dist/{network-L6JKNYOJ.js → network-MKMX2NG6.js} +6 -2
  24. package/dist/{price-SGQZC4DT.js → price-HHDBMKIE.js} +4 -4
  25. package/dist/{research-S5PLKE5V.js → research-4TBGYEVI.js} +4 -4
  26. package/dist/{research-DDEAW7NP.js → research-IJBOMYL3.js} +7 -7
  27. package/dist/{session-2QRTQ6ID.js → session-L67AVWBY.js} +9 -9
  28. package/dist/{trade-GNOISJNX.js → trade-LGFQEK3I.js} +14 -14
  29. package/dist/{vault-32RDL5HM.js → vault-XWFPGHRA.js} +5 -5
  30. package/dist/{xmtp-AL5W36H6.js → xmtp-6US7GH3Z.js} +6 -6
  31. package/package.json +2 -2
  32. package/dist/chunk-FNJHLDRL.js.map +0 -1
  33. package/dist/chunk-HYUUR6SC.js.map +0 -1
  34. package/dist/chunk-LZH4PJI4.js.map +0 -1
  35. /package/dist/{addresses-YDPUPGWQ.js.map → addresses-DGB3D457.js.map} +0 -0
  36. /package/dist/{agent-AML5SIMT.js.map → agent-UNSW5IPO.js.map} +0 -0
  37. /package/dist/{chat-6RO4RS3L.js.map → chat-KT2WVDT2.js.map} +0 -0
  38. /package/dist/{chunk-AGZQWXAR.js.map → chunk-2U4TWPWQ.js.map} +0 -0
  39. /package/dist/{chunk-RQZ53CV4.js.map → chunk-BKJ7HYX7.js.map} +0 -0
  40. /package/dist/{chunk-XJJEZFOA.js.map → chunk-CHWBQJLT.js.map} +0 -0
  41. /package/dist/{chunk-5ECJMM3K.js.map → chunk-DIZEB2VV.js.map} +0 -0
  42. /package/dist/{chunk-XMYD5SES.js.map → chunk-GU5VZGXB.js.map} +0 -0
  43. /package/dist/{chunk-RAITRCM7.js.map → chunk-IFL5SREW.js.map} +0 -0
  44. /package/dist/{chunk-WO4YDEDH.js.map → chunk-NPWYX6NU.js.map} +0 -0
  45. /package/dist/{chunk-TSNHEU2C.js.map → chunk-ZURDCYGU.js.map} +0 -0
  46. /package/dist/{client-HMUVPZEG.js.map → client-NAL3HVLK.js.map} +0 -0
  47. /package/dist/{eas-GDQYAUXR.js.map → eas-QAH7XVLK.js.map} +0 -0
  48. /package/dist/{governor-PX6AMIAU.js.map → governor-S56M6BV6.js.map} +0 -0
  49. /package/dist/{network-L6JKNYOJ.js.map → network-MKMX2NG6.js.map} +0 -0
  50. /package/dist/{price-SGQZC4DT.js.map → price-HHDBMKIE.js.map} +0 -0
  51. /package/dist/{research-S5PLKE5V.js.map → research-4TBGYEVI.js.map} +0 -0
  52. /package/dist/{research-DDEAW7NP.js.map → research-IJBOMYL3.js.map} +0 -0
  53. /package/dist/{session-2QRTQ6ID.js.map → session-L67AVWBY.js.map} +0 -0
  54. /package/dist/{trade-GNOISJNX.js.map → trade-LGFQEK3I.js.map} +0 -0
  55. /package/dist/{vault-32RDL5HM.js.map → vault-XWFPGHRA.js.map} +0 -0
  56. /package/dist/{xmtp-AL5W36H6.js.map → xmtp-6US7GH3Z.js.map} +0 -0
@@ -18,8 +18,8 @@ import {
18
18
  isRobinhoodEquityFeed,
19
19
  robinhoodEquityMaxAge,
20
20
  symbolForTokenAddress
21
- } from "./chunk-RAITRCM7.js";
22
- import "./chunk-HYUUR6SC.js";
21
+ } from "./chunk-IFL5SREW.js";
22
+ import "./chunk-DXJJMRBM.js";
23
23
  import "./chunk-GSC7PVEO.js";
24
24
  export {
25
25
  AERODROME,
@@ -42,4 +42,4 @@ export {
42
42
  robinhoodEquityMaxAge,
43
43
  symbolForTokenAddress
44
44
  };
45
- //# sourceMappingURL=addresses-YDPUPGWQ.js.map
45
+ //# sourceMappingURL=addresses-DGB3D457.js.map
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  getResearchProvider,
14
14
  isX402WalletFunded
15
- } from "./chunk-RQZ53CV4.js";
15
+ } from "./chunk-BKJ7HYX7.js";
16
16
  import {
17
17
  HyperliquidProvider,
18
18
  calculateATR,
@@ -29,9 +29,9 @@ import {
29
29
  } from "./chunk-DBOINZYU.js";
30
30
  import {
31
31
  chatCompletion
32
- } from "./chunk-WO4YDEDH.js";
33
- import "./chunk-LZH4PJI4.js";
34
- import "./chunk-HYUUR6SC.js";
32
+ } from "./chunk-NPWYX6NU.js";
33
+ import "./chunk-ROKE6ZAW.js";
34
+ import "./chunk-DXJJMRBM.js";
35
35
  import {
36
36
  getVeniceApiKey
37
37
  } from "./chunk-GSC7PVEO.js";
@@ -5573,7 +5573,7 @@ var PortfolioTracker = class {
5573
5573
  }
5574
5574
  try {
5575
5575
  const { createPublicClient, http } = await import("viem");
5576
- const { getChain } = await import("./network-L6JKNYOJ.js");
5576
+ const { getChain } = await import("./network-MKMX2NG6.js");
5577
5577
  const { BASE_STRATEGY_ABI, SYNDICATE_VAULT_ABI } = await import("./abis-KP7M4IPM.js");
5578
5578
  void chain;
5579
5579
  const client = createPublicClient({
@@ -9550,4 +9550,4 @@ function formatFearGreed(value) {
9550
9550
  export {
9551
9551
  registerAgentCommands
9552
9552
  };
9553
- //# sourceMappingURL=agent-AML5SIMT.js.map
9553
+ //# sourceMappingURL=agent-UNSW5IPO.js.map
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  getFund,
3
3
  updateMetadata
4
- } from "./chunk-AGZQWXAR.js";
4
+ } from "./chunk-2U4TWPWQ.js";
5
5
  import {
6
6
  getTextRecord,
7
7
  resolveFund,
8
8
  setTextRecord
9
- } from "./chunk-XJJEZFOA.js";
10
- import "./chunk-FNJHLDRL.js";
9
+ } from "./chunk-CHWBQJLT.js";
10
+ import "./chunk-CZRJ5GWE.js";
11
11
  import {
12
12
  ENS
13
- } from "./chunk-RAITRCM7.js";
13
+ } from "./chunk-IFL5SREW.js";
14
14
  import {
15
15
  formatContractError,
16
16
  getAccount
17
- } from "./chunk-LZH4PJI4.js";
18
- import "./chunk-HYUUR6SC.js";
17
+ } from "./chunk-ROKE6ZAW.js";
18
+ import "./chunk-DXJJMRBM.js";
19
19
  import {
20
20
  cacheGroupId,
21
21
  getCachedGroupId
@@ -30,7 +30,7 @@ import {
30
30
  import chalk from "chalk";
31
31
  import ora from "ora";
32
32
  async function loadXmtp() {
33
- return import("./xmtp-AL5W36H6.js");
33
+ return import("./xmtp-6US7GH3Z.js");
34
34
  }
35
35
  function formatTimestamp(date) {
36
36
  return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
@@ -564,4 +564,4 @@ function registerChatCommands(program) {
564
564
  export {
565
565
  registerChatCommands
566
566
  };
567
- //# sourceMappingURL=chat-6RO4RS3L.js.map
567
+ //# sourceMappingURL=chat-KT2WVDT2.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  SHERWOOD
3
- } from "./chunk-RAITRCM7.js";
3
+ } from "./chunk-IFL5SREW.js";
4
4
  import {
5
5
  getAccount,
6
6
  getPublicClient,
7
7
  waitForReceipt,
8
8
  writeContractWithRetry
9
- } from "./chunk-LZH4PJI4.js";
9
+ } from "./chunk-ROKE6ZAW.js";
10
10
  import {
11
11
  getChain
12
- } from "./chunk-HYUUR6SC.js";
12
+ } from "./chunk-DXJJMRBM.js";
13
13
  import {
14
14
  SYNDICATE_FACTORY_ABI
15
15
  } from "./chunk-BQMOE4FJ.js";
@@ -144,4 +144,4 @@ export {
144
144
  getActiveFunds,
145
145
  updateMetadata
146
146
  };
147
- //# sourceMappingURL=chunk-AGZQWXAR.js.map
147
+ //# sourceMappingURL=chunk-2U4TWPWQ.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getAccount
3
- } from "./chunk-LZH4PJI4.js";
3
+ } from "./chunk-ROKE6ZAW.js";
4
4
 
5
5
  // src/providers/research/messari.ts
6
6
  import { base, baseSepolia } from "viem/chains";
@@ -635,4 +635,4 @@ export {
635
635
  NansenProvider,
636
636
  getResearchProvider
637
637
  };
638
- //# sourceMappingURL=chunk-RQZ53CV4.js.map
638
+ //# sourceMappingURL=chunk-BKJ7HYX7.js.map
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  executeBatch,
3
3
  setVaultAddress
4
- } from "./chunk-FNJHLDRL.js";
4
+ } from "./chunk-CZRJ5GWE.js";
5
5
  import {
6
6
  ENS,
7
7
  SHERWOOD
8
- } from "./chunk-RAITRCM7.js";
8
+ } from "./chunk-IFL5SREW.js";
9
9
  import {
10
10
  getPublicClient
11
- } from "./chunk-LZH4PJI4.js";
11
+ } from "./chunk-ROKE6ZAW.js";
12
12
  import {
13
13
  VALID_NETWORKS,
14
14
  withNetwork
15
- } from "./chunk-HYUUR6SC.js";
15
+ } from "./chunk-DXJJMRBM.js";
16
16
  import {
17
17
  L2_REGISTRY_ABI,
18
18
  SYNDICATE_FACTORY_ABI
@@ -124,4 +124,4 @@ export {
124
124
  setTextRecord,
125
125
  getTextRecord
126
126
  };
127
- //# sourceMappingURL=chunk-XJJEZFOA.js.map
127
+ //# sourceMappingURL=chunk-CHWBQJLT.js.map
@@ -1,16 +1,18 @@
1
1
  import {
2
2
  TOKENS
3
- } from "./chunk-RAITRCM7.js";
3
+ } from "./chunk-IFL5SREW.js";
4
4
  import {
5
+ GAS_UNITS,
6
+ estimateGasCushion,
5
7
  getAccount,
6
8
  getPublicClient,
7
9
  getWalletClient,
8
10
  waitForReceipt,
9
11
  writeContractWithRetry
10
- } from "./chunk-LZH4PJI4.js";
12
+ } from "./chunk-ROKE6ZAW.js";
11
13
  import {
12
14
  getChain
13
- } from "./chunk-HYUUR6SC.js";
15
+ } from "./chunk-DXJJMRBM.js";
14
16
  import {
15
17
  getChainContracts
16
18
  } from "./chunk-GSC7PVEO.js";
@@ -20,7 +22,7 @@ import {
20
22
  } from "./chunk-BQMOE4FJ.js";
21
23
 
22
24
  // src/lib/vault.ts
23
- import { formatUnits, parseEther } from "viem";
25
+ import { formatUnits } from "viem";
24
26
  var _vaultOverride = null;
25
27
  function setVaultAddress(addr) {
26
28
  _vaultOverride = addr;
@@ -111,10 +113,10 @@ async function preflightDeposit(amount) {
111
113
  );
112
114
  }
113
115
  const ethBalance = await client.getBalance({ address: account.address });
114
- const minGas = parseEther("0.0005");
116
+ const minGas = await estimateGasCushion(GAS_UNITS.approve + GAS_UNITS.deposit);
115
117
  if (ethBalance < minGas) {
116
118
  throw new Error(
117
- `Insufficient ETH for gas. Have ${formatUnits(ethBalance, 18)} ETH, need at least 0.0005 ETH. Top up your wallet with ETH on Base.`
119
+ `Insufficient ETH for gas. Have ${formatUnits(ethBalance, 18)} ETH, need at least ${formatUnits(minGas, 18)} ETH. Top up your wallet with ETH on ${getChain().name}.`
118
120
  );
119
121
  }
120
122
  const paused = await client.readContract({
@@ -180,10 +182,12 @@ async function depositWithEthWrap(amount) {
180
182
  if (wethBalance < amount) {
181
183
  const shortfall = amount - wethBalance;
182
184
  const ethBalance = await client.getBalance({ address: account.address });
183
- const minGas = parseEther("0.0005");
185
+ const minGas = await estimateGasCushion(
186
+ GAS_UNITS.wrap + GAS_UNITS.approve + GAS_UNITS.deposit
187
+ );
184
188
  if (ethBalance < shortfall + minGas) {
185
189
  throw new Error(
186
- `Insufficient ETH. Have ${formatUnits(ethBalance, 18)} ETH, need ${formatUnits(shortfall, 18)} to wrap + gas.`
190
+ `Insufficient ETH on ${getChain().name}. Have ${formatUnits(ethBalance, 18)} ETH, need ${formatUnits(shortfall, 18)} to wrap + ~${formatUnits(minGas, 18)} for gas.`
187
191
  );
188
192
  }
189
193
  const wallet = getWalletClient();
@@ -307,10 +311,10 @@ async function preflightRedeem(shares) {
307
311
  }
308
312
  }
309
313
  const ethBalance = await client.getBalance({ address: account.address });
310
- const minGas = parseEther("0.0005");
314
+ const minGas = await estimateGasCushion(GAS_UNITS.redeem);
311
315
  if (ethBalance < minGas) {
312
316
  throw new Error(
313
- `Insufficient ETH for gas. Have ${formatUnits(ethBalance, 18)} ETH, need at least 0.0005 ETH.`
317
+ `Insufficient ETH for gas on ${getChain().name}. Have ${formatUnits(ethBalance, 18)} ETH, need at least ${formatUnits(minGas, 18)} ETH.`
314
318
  );
315
319
  }
316
320
  }
@@ -523,4 +527,4 @@ export {
523
527
  registerAgent,
524
528
  getVaultInfo
525
529
  };
526
- //# sourceMappingURL=chunk-FNJHLDRL.js.map
530
+ //# sourceMappingURL=chunk-CZRJ5GWE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/vault.ts"],"sourcesContent":["/**\n * SyndicateVault contract wrapper.\n *\n * The vault is the onchain identity — it holds all positions via delegatecall\n * to a shared BatchExecutorLib. No separate executor contract needed.\n */\n\nimport type { Address, Hex } from \"viem\";\nimport { formatUnits } from \"viem\";\nimport { getChain } from \"./network.js\";\nimport {\n getPublicClient,\n getAccount,\n getWalletClient,\n writeContractWithRetry,\n waitForReceipt,\n estimateGasCushion,\n GAS_UNITS,\n} from \"./client.js\";\nimport { SYNDICATE_VAULT_ABI, ERC20_ABI } from \"./abis.js\";\nimport type { BatchCall } from \"./batch.js\";\nimport { getChainContracts } from \"./config.js\";\nimport { TOKENS } from \"./addresses.js\";\n\n// Per-command override (set by --vault flag in index.ts)\nlet _vaultOverride: Address | null = null;\n\nexport function setVaultAddress(addr: Address): void {\n _vaultOverride = addr;\n}\n\nexport function getVaultAddress(): Address {\n // 1. Per-command override (--vault flag)\n if (_vaultOverride) return _vaultOverride;\n\n // 2. Config (~/.sherwood/config.json) — default vault\n const chainId = getChain().id;\n const fromConfig = getChainContracts(chainId).vault;\n if (fromConfig) return fromConfig as Address;\n\n throw new Error(\n \"Vault address not found. Pass --vault <addr> or run 'sherwood config set --vault <addr>'.\",\n );\n}\n\n// ── Asset Helpers ──\n\n/**\n * Read the vault's underlying ERC-20 asset address.\n */\nexport async function getAssetAddress(): Promise<Address> {\n const client = getPublicClient();\n return client.readContract({\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"asset\",\n }) as Promise<Address>;\n}\n\n/**\n * Read decimals from the vault's underlying asset.\n * Works with any ERC-20 (USDC=6, WETH=18, WBTC=8, etc.).\n */\nexport async function getAssetDecimals(): Promise<number> {\n const client = getPublicClient();\n const asset = await getAssetAddress();\n return client.readContract({\n address: asset,\n abi: ERC20_ABI,\n functionName: \"decimals\",\n }) as Promise<number>;\n}\n\n/**\n * Read the vault owner (OwnableUpgradeable).\n */\nexport async function getVaultOwner(vault?: Address): Promise<Address> {\n const client = getPublicClient();\n return client.readContract({\n address: vault ?? getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"owner\",\n }) as Promise<Address>;\n}\n\n/**\n * Read the vault's agent performance fee (basis points). Owner-set, default\n * 500 (5%). Snapshotted onto a proposal at propose time and clamped to the\n * governor's `maxPerformanceFeeBps` at settlement.\n */\nexport async function getAgentFeeBps(vault?: Address): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: vault ?? getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"agentFeeBps\",\n }) as Promise<bigint>;\n}\n\n/**\n * Set the vault's agent performance fee (basis points). Vault owner only;\n * reverts above 1500 (15%).\n */\nexport async function setAgentFeeBps(bps: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"setAgentFeeBps\",\n args: [bps],\n });\n await waitForReceipt(hash);\n return hash;\n}\n\n// ── WETH ABI (for auto-wrapping ETH) ──\n\nconst WETH_ABI = [\n {\n name: \"deposit\",\n type: \"function\",\n stateMutability: \"payable\",\n inputs: [],\n outputs: [],\n },\n] as const;\n\n// ── Pre-flight Checks ──\n\n/**\n * Validate that a deposit can succeed before submitting any transactions.\n * Throws descriptive errors if any pre-condition fails.\n */\nexport async function preflightDeposit(amount: bigint): Promise<void> {\n const client = getPublicClient();\n const vaultAddress = getVaultAddress();\n const account = getAccount();\n const asset = await getAssetAddress();\n const decimals = await getAssetDecimals();\n\n // 1. Check asset balance\n const balance = (await client.readContract({\n address: asset,\n abi: ERC20_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n\n if (balance < amount) {\n const symbol = (await client.readContract({\n address: asset,\n abi: ERC20_ABI,\n functionName: \"symbol\",\n })) as string;\n throw new Error(\n `Insufficient ${symbol}. Have ${formatUnits(balance, decimals)}, need ${formatUnits(amount, decimals)}.`,\n );\n }\n\n // 2. Check ETH for gas — cushion derived from live chain gas (approve +\n // deposit), floored per-chain. Never a fixed Base-calibrated constant: on a\n // ~14-wei-base-fee chain 0.0005 ETH was ~7 orders too high and rejected\n // valid deposits client-side.\n const ethBalance = await client.getBalance({ address: account.address });\n const minGas = await estimateGasCushion(GAS_UNITS.approve + GAS_UNITS.deposit);\n if (ethBalance < minGas) {\n throw new Error(\n `Insufficient ETH for gas. Have ${formatUnits(ethBalance, 18)} ETH, need at least ` +\n `${formatUnits(minGas, 18)} ETH. Top up your wallet with ETH on ${getChain().name}.`,\n );\n }\n\n // 3. Check vault not paused\n const paused = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"paused\",\n })) as boolean;\n if (paused) {\n throw new Error(\"Vault is paused — deposits are temporarily disabled.\");\n }\n\n // 4. Active proposal: deposits lock UNLESS Lane A (the V2 oracle instant\n // lane, PR #357) is live for the running strategy — then the vault accepts\n // instant entries up to maxDeposit. The old unconditional refusal made Lane\n // A unusable through the CLI (vnet e2e finding, issue #416).\n const locked = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"redemptionsLocked\",\n })) as boolean;\n if (locked) {\n const cap = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"maxDeposit\",\n args: [account.address],\n })) as bigint;\n if (cap < amount) {\n throw new Error(\n cap === 0n\n ? \"Deposits are locked while a strategy is executing (Lane A unavailable for this strategy). \" +\n \"Queue an async deposit instead:\\n sherwood queue request-deposit --vault <vault> --amount <usdc>\"\n : `Deposit exceeds the Lane A instant cap while a strategy is executing (maxDeposit ${cap.toString()} ` +\n `raw asset units, requested ${amount.toString()}). Reduce the amount or queue it:\\n` +\n \" sherwood queue request-deposit --vault <vault> --amount <usdc>\",\n );\n }\n }\n\n // 5. Check depositor approval (if whitelist vault)\n const openDeposits = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"openDeposits\",\n })) as boolean;\n if (!openDeposits) {\n const approved = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"isApprovedDepositor\",\n args: [account.address],\n })) as boolean;\n if (!approved) {\n throw new Error(\n \"Your address is not an approved depositor. Ask the vault creator to approve you:\\n sherwood fund approve-depositor --depositor \" +\n account.address,\n );\n }\n }\n}\n\n// ── LP Functions ──\n\n/**\n * Deposit into the vault with optional ETH auto-wrapping for WETH vaults.\n * When `useEth` is true and the vault asset is WETH, wraps ETH first.\n */\nexport async function depositWithEthWrap(amount: bigint): Promise<Hex> {\n const client = getPublicClient();\n const account = getAccount();\n const asset = await getAssetAddress();\n const wethAddress = TOKENS().WETH;\n\n if (asset.toLowerCase() !== wethAddress.toLowerCase()) {\n throw new Error(\"--use-eth is only supported for WETH vaults.\");\n }\n\n // Check current WETH balance — only wrap the shortfall\n const wethBalance = (await client.readContract({\n address: wethAddress,\n abi: ERC20_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n\n if (wethBalance < amount) {\n const shortfall = amount - wethBalance;\n\n // Verify sufficient ETH to cover shortfall + gas. The gas cushion is\n // derived from live chain gas (wrap + approve + deposit), floored per-chain\n // — not a fixed Base-calibrated constant that would reject valid low-fee\n // deposits (e.g. a 0.00058-ETH wallet wrapping 0.0005 on Robinhood testnet).\n const ethBalance = await client.getBalance({ address: account.address });\n const minGas = await estimateGasCushion(\n GAS_UNITS.wrap + GAS_UNITS.approve + GAS_UNITS.deposit,\n );\n if (ethBalance < shortfall + minGas) {\n throw new Error(\n `Insufficient ETH on ${getChain().name}. Have ${formatUnits(ethBalance, 18)} ETH, need ` +\n `${formatUnits(shortfall, 18)} to wrap + ~${formatUnits(minGas, 18)} for gas.`,\n );\n }\n\n // Wrap ETH → WETH\n const wallet = getWalletClient();\n const wrapHash = await wallet.writeContract({\n address: wethAddress,\n abi: WETH_ABI,\n functionName: \"deposit\",\n args: [],\n value: shortfall,\n account,\n chain: getChain(),\n });\n const wrapReceipt = await client.waitForTransactionReceipt({ hash: wrapHash });\n // A reverted wrap leaves WETH short; the downstream deposit would then\n // revert on transferFrom. Fail loudly here instead.\n if (wrapReceipt.status !== \"success\") {\n throw new Error(`ETH->WETH wrap reverted on-chain (tx ${wrapHash}).`);\n }\n }\n\n // Proceed with normal deposit\n return deposit(amount);\n}\n\n/**\n * Deposit into the vault. Handles approval + deposit for the vault's asset.\n */\nexport async function deposit(amount: bigint): Promise<Hex> {\n const client = getPublicClient();\n const vaultAddress = getVaultAddress();\n const account = getAccount();\n const asset = await getAssetAddress();\n\n // Check existing allowance — skip approve if sufficient\n const currentAllowance = (await client.readContract({\n address: asset,\n abi: ERC20_ABI,\n functionName: \"allowance\",\n args: [account.address, vaultAddress],\n })) as bigint;\n\n if (currentAllowance < amount) {\n const approveHash = await writeContractWithRetry({\n account,\n chain: getChain(),\n address: asset,\n abi: ERC20_ABI,\n functionName: \"approve\",\n args: [vaultAddress, amount],\n });\n // Wait for approve to confirm before deposit — prevents nonce collision\n const approveReceipt = await waitForReceipt(approveHash);\n // A reverted approve leaves allowance below `amount`; the deposit's\n // transferFrom would then revert. Surface it here.\n if (approveReceipt.status !== \"success\") {\n throw new Error(`Asset approve reverted on-chain (tx ${approveHash}).`);\n }\n }\n\n // Snapshot share balance before deposit so we can assert shares were minted.\n const sharesBefore = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n\n // Deposit — retry wrapper handles nonce/gas automatically\n const depositHash = await writeContractWithRetry({\n account,\n chain: getChain(),\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"deposit\",\n args: [amount, account.address],\n });\n const receipt = await waitForReceipt(depositHash);\n\n // A mined-but-reverted deposit is NOT a success. viem's\n // waitForTransactionReceipt resolves even for `status: \"reverted\"` txs, so\n // without this guard a reverted deposit would be reported as a successful\n // one and the caller would believe it holds shares it does not.\n if (receipt.status !== \"success\") {\n throw new Error(\n `Deposit transaction reverted on-chain (tx ${depositHash}). No shares were minted.`,\n );\n }\n\n // Confirm shares were actually minted to the receiver. Guards against a\n // silent zero-share mint (e.g. rounding on an inflated vault) that would\n // otherwise pass unnoticed because the tx did not revert.\n const sharesAfter = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n if (sharesAfter <= sharesBefore) {\n throw new Error(\n `Deposit confirmed on-chain (tx ${depositHash}) but minted 0 shares ` +\n `(balance ${sharesBefore} -> ${sharesAfter}). Aborting to avoid reporting a phantom deposit.`,\n );\n }\n\n return depositHash;\n}\n\n// ── Redeem ──\n\n/**\n * Pre-flight check for redeem. Validates that the caller has enough shares\n * and that the vault allows redemption right now.\n */\nexport async function preflightRedeem(shares: bigint): Promise<void> {\n const client = getPublicClient();\n const vaultAddress = getVaultAddress();\n const account = getAccount();\n\n if (shares === 0n) {\n throw new Error(\"No shares to redeem.\");\n }\n\n // 1. Check share balance\n const balance = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n\n if (balance < shares) {\n throw new Error(\n `Insufficient shares. Have ${balance.toString()}, need ${shares.toString()}.`,\n );\n }\n\n // 2. Check vault not paused\n const paused = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"paused\",\n })) as boolean;\n if (paused) {\n throw new Error(\"Vault is paused — redemptions are temporarily disabled.\");\n }\n\n // 3. Active proposal: redemptions lock UNLESS Lane A (the V2 oracle instant\n // lane, PR #357) is live — then instant exits are allowed up to maxRedeem.\n // The old unconditional refusal made Lane A unusable through the CLI (vnet\n // e2e finding, issue #416).\n const locked = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"redemptionsLocked\",\n })) as boolean;\n if (locked) {\n const cap = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"maxRedeem\",\n args: [account.address],\n })) as bigint;\n if (cap < shares) {\n throw new Error(\n cap === 0n\n ? \"Redemptions are locked while a strategy is executing (Lane A unavailable for this strategy). \" +\n \"Queue an async redeem instead:\\n sherwood queue request-redeem --vault <vault> --shares <raw-shares>\"\n : `Redeem exceeds the Lane A instant cap while a strategy is executing (maxRedeem ${cap.toString()} ` +\n `raw shares, requested ${shares.toString()}). Reduce the share amount or queue it:\\n` +\n \" sherwood queue request-redeem --vault <vault> --shares <raw-shares>\",\n );\n }\n }\n\n // 4. Check ETH for gas — cushion derived from live chain gas (redeem),\n // floored per-chain; never a fixed Base-calibrated constant.\n const ethBalance = await client.getBalance({ address: account.address });\n const minGas = await estimateGasCushion(GAS_UNITS.redeem);\n if (ethBalance < minGas) {\n throw new Error(\n `Insufficient ETH for gas on ${getChain().name}. Have ${formatUnits(ethBalance, 18)} ETH, ` +\n `need at least ${formatUnits(minGas, 18)} ETH.`,\n );\n }\n}\n\n/**\n * Redeem shares for the underlying asset (standard ERC-4626 redeem).\n * Returns the tx hash and the assets received.\n */\nexport async function redeem(\n shares: bigint,\n receiver?: Address,\n): Promise<{ hash: Hex; assetsOut: bigint }> {\n const client = getPublicClient();\n const vaultAddress = getVaultAddress();\n const account = getAccount();\n const to = receiver ?? account.address;\n\n // Preview so we can report assets received\n const assetsOut = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"previewRedeem\",\n args: [shares],\n })) as bigint;\n\n const hash = await writeContractWithRetry({\n account,\n chain: getChain(),\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"redeem\",\n args: [shares, to, account.address],\n });\n await waitForReceipt(hash);\n return { hash, assetsOut };\n}\n\n/**\n * Read the current share balance for an address (defaults to the caller).\n */\nexport async function getShareBalance(address?: Address): Promise<bigint> {\n const client = getPublicClient();\n const owner = address ?? getAccount().address;\n return client.readContract({\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"balanceOf\",\n args: [owner],\n }) as Promise<bigint>;\n}\n\n// ── Batch Execution ──\n\n/**\n * Execute a batch of protocol calls through the vault (owner only).\n * The vault delegatecalls to the executor lib.\n * All calls execute as the vault — positions live on the vault.\n */\nexport async function executeBatch(calls: BatchCall[]): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"executeBatch\",\n args: [\n calls.map((c) => ({\n target: c.target,\n data: c.data,\n value: c.value,\n })),\n ],\n });\n await waitForReceipt(hash);\n return hash;\n}\n\n// ── Depositor Management ──\n\n/**\n * Approve a depositor address (owner only).\n */\nexport async function approveDepositor(depositor: Address): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"approveDepositor\",\n args: [depositor],\n });\n await waitForReceipt(hash);\n return hash;\n}\n\n/**\n * Remove a depositor from the whitelist (owner only).\n */\nexport async function removeDepositor(depositor: Address): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"removeDepositor\",\n args: [depositor],\n });\n await waitForReceipt(hash);\n return hash;\n}\n\n/**\n * Approve multiple depositors in a batch (owner only).\n */\nexport async function approveDepositors(depositors: Address[]): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"approveDepositors\",\n args: [depositors],\n });\n await waitForReceipt(hash);\n return hash;\n}\n\n/**\n * Check if an address is a registered agent on the vault.\n */\nexport async function isAgent(agentAddress: Address): Promise<boolean> {\n const client = getPublicClient();\n return client.readContract({\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"isAgent\",\n args: [agentAddress],\n }) as Promise<boolean>;\n}\n\n/**\n * Check if an address is an approved depositor.\n */\nexport async function isApprovedDepositor(depositor: Address): Promise<boolean> {\n const client = getPublicClient();\n return client.readContract({\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"isApprovedDepositor\",\n args: [depositor],\n }) as Promise<boolean>;\n}\n\n/**\n * Get LP share balance and asset value.\n */\nexport async function getBalance(address?: Address): Promise<{\n shares: bigint;\n assetsValue: string;\n percentOfVault: string;\n}> {\n const client = getPublicClient();\n const vaultAddress = getVaultAddress();\n const account = address || getAccount().address;\n\n const [shares, totalSupply] = await Promise.all([\n client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"balanceOf\",\n args: [account],\n }) as Promise<bigint>,\n client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"totalSupply\",\n }) as Promise<bigint>,\n ]);\n\n let assetsValue = 0n;\n if (shares > 0n) {\n assetsValue = (await client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"convertToAssets\",\n args: [shares],\n })) as bigint;\n }\n\n const percent =\n totalSupply > 0n ? ((Number(shares) / Number(totalSupply)) * 100).toFixed(2) : \"0.00\";\n\n const decimals = await getAssetDecimals();\n\n return {\n shares,\n assetsValue: formatUnits(assetsValue, decimals),\n percentOfVault: `${percent}%`,\n };\n}\n\n// ── Agent Management ──\n\n/**\n * Register a new agent (owner only). Requires ERC-8004 agent identity.\n */\nexport async function registerAgent(\n agentId: bigint,\n agentAddress: Address,\n): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: getVaultAddress(),\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"registerAgent\",\n args: [agentId, agentAddress],\n });\n await waitForReceipt(hash);\n return hash;\n}\n\n// ── Views ──\n\nexport interface VaultInfo {\n address: Address;\n totalAssets: string;\n agentCount: bigint;\n redemptionsLocked: boolean;\n managementFeeBps: bigint;\n}\n\n/**\n * Get vault overview info.\n */\nexport async function getVaultInfo(): Promise<VaultInfo> {\n const client = getPublicClient();\n const vaultAddress = getVaultAddress();\n\n const [totalAssets, agentCount, redemptionsLocked, managementFeeBps, decimals] =\n await Promise.all([\n client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"totalAssets\",\n }) as Promise<bigint>,\n client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"getAgentCount\",\n }) as Promise<bigint>,\n client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"redemptionsLocked\",\n }) as Promise<boolean>,\n client.readContract({\n address: vaultAddress,\n abi: SYNDICATE_VAULT_ABI,\n functionName: \"managementFeeBps\",\n }) as Promise<bigint>,\n getAssetDecimals(),\n ]);\n\n return {\n address: vaultAddress,\n totalAssets: formatUnits(totalAssets, decimals),\n agentCount,\n redemptionsLocked,\n managementFeeBps,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  UNISWAP
3
- } from "./chunk-RAITRCM7.js";
3
+ } from "./chunk-IFL5SREW.js";
4
4
  import {
5
5
  getAccount,
6
6
  getPublicClient,
7
7
  sendTxWithRetry,
8
8
  waitForReceipt
9
- } from "./chunk-LZH4PJI4.js";
9
+ } from "./chunk-ROKE6ZAW.js";
10
10
  import {
11
11
  getChain
12
- } from "./chunk-HYUUR6SC.js";
12
+ } from "./chunk-DXJJMRBM.js";
13
13
  import {
14
14
  getUniswapApiKey
15
15
  } from "./chunk-GSC7PVEO.js";
@@ -322,4 +322,4 @@ export {
322
322
  encodeSwapPath,
323
323
  getMultiHopQuote
324
324
  };
325
- //# sourceMappingURL=chunk-5ECJMM3K.js.map
325
+ //# sourceMappingURL=chunk-DIZEB2VV.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GSC7PVEO.js";
4
4
 
5
5
  // src/lib/network.ts
6
- import { defineChain } from "viem";
6
+ import { defineChain, parseEther } from "viem";
7
7
  var robinhoodTestnet = defineChain({
8
8
  id: 46630,
9
9
  name: "Robinhood Chain Testnet",
@@ -19,6 +19,7 @@ var robinhoodTestnet = defineChain({
19
19
  },
20
20
  testnet: true
21
21
  });
22
+ var DEFAULT_MIN_GAS_RESERVE = parseEther("0.0005");
22
23
  var CHAIN_REGISTRY = {
23
24
  "robinhood-testnet": {
24
25
  chain: robinhoodTestnet,
@@ -28,7 +29,11 @@ var CHAIN_REGISTRY = {
28
29
  easGraphqlUrl: null,
29
30
  easScanHost: null,
30
31
  xmtpEnv: "production",
31
- isTestnet: true
32
+ isTestnet: true,
33
+ // ~14-wei base fee: a full wrap+approve+deposit sequence costs a few
34
+ // million wei (~1e-11 ETH). 0.00002 ETH is a generous floor that still
35
+ // leaves a 0.00058-ETH wallet room to wrap 0.0005 and deposit.
36
+ minGasReserve: parseEther("0.00002")
32
37
  }
33
38
  };
34
39
  var VALID_NETWORKS = Object.keys(CHAIN_REGISTRY);
@@ -58,6 +63,9 @@ async function withNetwork(network, fn) {
58
63
  function getChainConfig() {
59
64
  return CHAIN_REGISTRY[_network];
60
65
  }
66
+ function getMinGasReserve() {
67
+ return CHAIN_REGISTRY[_network].minGasReserve ?? DEFAULT_MIN_GAS_RESERVE;
68
+ }
61
69
  function applyChainIdOverride(chain) {
62
70
  const raw = process.env.SIM_CHAIN_ID;
63
71
  if (!raw || !process.env.SIM_ADDRESSES_FILE) return chain;
@@ -94,6 +102,7 @@ function isTestnet() {
94
102
 
95
103
  export {
96
104
  robinhoodTestnet,
105
+ DEFAULT_MIN_GAS_RESERVE,
97
106
  CHAIN_REGISTRY,
98
107
  VALID_NETWORKS,
99
108
  DEFAULT_NETWORK,
@@ -101,6 +110,7 @@ export {
101
110
  getNetwork,
102
111
  withNetwork,
103
112
  getChainConfig,
113
+ getMinGasReserve,
104
114
  applyChainIdOverride,
105
115
  getChain,
106
116
  getRpcUrls,
@@ -108,4 +118,4 @@ export {
108
118
  getExplorerUrl,
109
119
  isTestnet
110
120
  };
111
- //# sourceMappingURL=chunk-HYUUR6SC.js.map
121
+ //# sourceMappingURL=chunk-DXJJMRBM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/network.ts"],"sourcesContent":["/**\n * Network state singleton.\n *\n * Called once at CLI startup via the --chain flag's preAction hook.\n * Every other module reads from here — never hardcodes a chain.\n *\n * Current deployment target: Robinhood L2 testnet (chain 46630) only. Sherwood\n * launches on Robinhood first; other chains may be added back later, at which\n * point they re-enter the Network union + CHAIN_REGISTRY below.\n */\n\nimport { type Chain, defineChain, parseEther } from \"viem\";\nimport { getConfigRpcUrl } from \"./config.js\";\n\n// ── Robinhood L2 Testnet (Arbitrum Orbit, chain ID 46630) ──\n\nexport const robinhoodTestnet = defineChain({\n id: 46630,\n name: \"Robinhood Chain Testnet\",\n nativeCurrency: { name: \"Ethereum\", symbol: \"ETH\", decimals: 18 },\n rpcUrls: {\n default: { http: [\"https://rpc.testnet.chain.robinhood.com\"] },\n },\n blockExplorers: {\n default: {\n name: \"Blockscout\",\n url: \"https://explorer.testnet.chain.robinhood.com\",\n },\n },\n testnet: true,\n});\n\n// ── Types ──\n\nexport type Network = \"robinhood-testnet\";\n\nexport interface ChainConfig {\n chain: Chain;\n /**\n * Ordered list of public RPC URLs tried in sequence when no user RPC is set.\n * First entry is the primary; the rest are fallbacks used transparently via\n * viem's `fallback` transport on rate-limit / network errors.\n */\n rpcFallbacks: string[];\n /** Legacy env var for backwards compat (optional per chain) */\n rpcEnvVar?: string;\n explorerHost: string;\n /** EAS GraphQL endpoint — null if EAS is not available on this chain */\n easGraphqlUrl: string | null;\n /** EAS scan host — null if unavailable */\n easScanHost: string | null;\n xmtpEnv: \"production\" | \"dev\";\n isTestnet: boolean;\n /**\n * ETH held back for gas in the native-ETH-wrap deposit path — the floor and\n * fallback for the live per-tx cushion estimate (`estimateGasCushion` in\n * client.ts), NOT a fixed requirement. High-fee chains (Base) want a\n * Base-scale reserve; near-zero-fee chains (Robinhood testnet, ~14-wei base\n * fee) want a tiny one so a valid deposit isn't rejected client-side. Unset →\n * `DEFAULT_MIN_GAS_RESERVE`.\n */\n minGasReserve?: bigint;\n}\n\n/**\n * Base-scale default reserve, used when a chain omits `minGasReserve`. Matches\n * the old hardcoded 0.0005-ETH cushion so higher-fee chains keep prior behavior.\n */\nexport const DEFAULT_MIN_GAS_RESERVE = parseEther(\"0.0005\");\n\n// ── Chain Registry ──\n\nexport const CHAIN_REGISTRY: Record<Network, ChainConfig> = {\n \"robinhood-testnet\": {\n chain: robinhoodTestnet,\n rpcFallbacks: [\"https://rpc.testnet.chain.robinhood.com\"],\n rpcEnvVar: \"ROBINHOOD_TESTNET_RPC_URL\",\n explorerHost: \"explorer.testnet.chain.robinhood.com\",\n easGraphqlUrl: null,\n easScanHost: null,\n xmtpEnv: \"production\",\n isTestnet: true,\n // ~14-wei base fee: a full wrap+approve+deposit sequence costs a few\n // million wei (~1e-11 ETH). 0.00002 ETH is a generous floor that still\n // leaves a 0.00058-ETH wallet room to wrap 0.0005 and deposit.\n minGasReserve: parseEther(\"0.00002\"),\n },\n};\n\nexport const VALID_NETWORKS = Object.keys(CHAIN_REGISTRY) as Network[];\n\nexport const DEFAULT_NETWORK: Network = \"robinhood-testnet\";\n\n// ── Singleton state ──\n\nlet _network: Network = DEFAULT_NETWORK;\n\nexport function setNetwork(n: Network) {\n const config = CHAIN_REGISTRY[n];\n if (!config) {\n throw new Error(\n `Unknown network: ${n}. Valid: ${VALID_NETWORKS.join(\", \")}`,\n );\n }\n _network = n;\n}\n\nexport function getNetwork(): Network {\n return _network;\n}\n\n/**\n * Run `fn` with the network singleton temporarily switched to `network`,\n * restoring the previous network in `finally`.\n *\n * The viem clients (`getPublicClient`, `getWalletClient`) auto-invalidate on\n * chain change, so callers get fresh per-chain RPC + chain context inside the\n * block. Used for cross-chain fund resolution.\n */\nexport async function withNetwork<T>(\n network: Network,\n fn: () => Promise<T>,\n): Promise<T> {\n const previous = _network;\n _network = network;\n try {\n return await fn();\n } finally {\n _network = previous;\n }\n}\n\nexport function getChainConfig(): ChainConfig {\n return CHAIN_REGISTRY[_network];\n}\n\n/**\n * Per-chain ETH gas reserve floor/fallback for the ETH-wrap deposit path.\n * Falls back to the Base-scale default when the active chain doesn't set one.\n */\nexport function getMinGasReserve(): bigint {\n return CHAIN_REGISTRY[_network].minGasReserve ?? DEFAULT_MIN_GAS_RESERVE;\n}\n\n/**\n * Dev/CI-only chain-id override (`SIM_CHAIN_ID`) for custom-chainId vnet forks.\n * viem asserts the node's chainId matches the chain definition on every write,\n * so signing for such a fork requires the id to be swapped at the source.\n *\n * Interlock: honored ONLY while `SIM_ADDRESSES_FILE` is also set (the vnet\n * e2e harness's address override). A bare SIM_CHAIN_ID does nothing, so this\n * can never redirect signing on a real network run.\n */\nexport function applyChainIdOverride(chain: Chain): Chain {\n const raw = process.env.SIM_CHAIN_ID;\n if (!raw || !process.env.SIM_ADDRESSES_FILE) return chain;\n const id = Number(raw);\n if (!Number.isInteger(id) || id <= 0) return chain;\n return { ...chain, id };\n}\n\nexport function getChain(): Chain {\n return applyChainIdOverride(CHAIN_REGISTRY[_network].chain);\n}\n\n/**\n * Ordered list of RPC URLs to try, deduped:\n * 1. User config (~/.sherwood/config.json) — honored first\n * 2. Legacy env var (ROBINHOOD_TESTNET_RPC_URL)\n * 3. Public fallback list from CHAIN_REGISTRY\n *\n * Fed to viem's `fallback()` transport so rate-limited RPCs transparently\n * advance to the next entry. See issue #182.\n */\nexport function getRpcUrls(): string[] {\n const cfg = CHAIN_REGISTRY[_network];\n const urls: string[] = [];\n\n const fromConfig = getConfigRpcUrl(_network);\n if (fromConfig) urls.push(fromConfig);\n\n if (cfg.rpcEnvVar && process.env[cfg.rpcEnvVar]) {\n urls.push(process.env[cfg.rpcEnvVar]!);\n }\n\n // Vnet harness (SIM_CHAIN_ID + SIM_ADDRESSES_FILE interlock): the fork RPC\n // is the ONLY valid endpoint — the public fallbacks point at the real\n // network. viem's fallback() otherwise rotates there on a vnet hiccup and\n // silently estimates gas/fees against MAINNET state (live finding: an\n // approve estimated at 21.9k gas on mainnet — no code at that address —\n // reverted out-of-gas on the vnet where it needs ~46k). A vnet call must\n // fail loudly, never retarget the real chain.\n if (process.env.SIM_CHAIN_ID && process.env.SIM_ADDRESSES_FILE && urls.length > 0) {\n return Array.from(new Set(urls));\n }\n\n urls.push(...cfg.rpcFallbacks);\n\n return Array.from(new Set(urls));\n}\n\nexport function getRpcUrl(): string {\n return getRpcUrls()[0];\n}\n\nexport function getExplorerUrl(txHash: string): string {\n return `https://${CHAIN_REGISTRY[_network].explorerHost}/tx/${txHash}`;\n}\n\nexport function isTestnet(): boolean {\n return CHAIN_REGISTRY[_network].isTestnet;\n}\n"],"mappings":";;;;;AAWA,SAAqB,aAAa,kBAAkB;AAK7C,IAAM,mBAAmB,YAAY;AAAA,EAC1C,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,gBAAgB,EAAE,MAAM,YAAY,QAAQ,OAAO,UAAU,GAAG;AAAA,EAChE,SAAS;AAAA,IACP,SAAS,EAAE,MAAM,CAAC,yCAAyC,EAAE;AAAA,EAC/D;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,MACP,MAAM;AAAA,MACN,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EACA,SAAS;AACX,CAAC;AAsCM,IAAM,0BAA0B,WAAW,QAAQ;AAInD,IAAM,iBAA+C;AAAA,EAC1D,qBAAqB;AAAA,IACnB,OAAO;AAAA,IACP,cAAc,CAAC,yCAAyC;AAAA,IACxD,WAAW;AAAA,IACX,cAAc;AAAA,IACd,eAAe;AAAA,IACf,aAAa;AAAA,IACb,SAAS;AAAA,IACT,WAAW;AAAA;AAAA;AAAA;AAAA,IAIX,eAAe,WAAW,SAAS;AAAA,EACrC;AACF;AAEO,IAAM,iBAAiB,OAAO,KAAK,cAAc;AAEjD,IAAM,kBAA2B;AAIxC,IAAI,WAAoB;AAEjB,SAAS,WAAW,GAAY;AACrC,QAAM,SAAS,eAAe,CAAC;AAC/B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,oBAAoB,CAAC,YAAY,eAAe,KAAK,IAAI,CAAC;AAAA,IAC5D;AAAA,EACF;AACA,aAAW;AACb;AAEO,SAAS,aAAsB;AACpC,SAAO;AACT;AAUA,eAAsB,YACpB,SACA,IACY;AACZ,QAAM,WAAW;AACjB,aAAW;AACX,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,UAAE;AACA,eAAW;AAAA,EACb;AACF;AAEO,SAAS,iBAA8B;AAC5C,SAAO,eAAe,QAAQ;AAChC;AAMO,SAAS,mBAA2B;AACzC,SAAO,eAAe,QAAQ,EAAE,iBAAiB;AACnD;AAWO,SAAS,qBAAqB,OAAqB;AACxD,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,mBAAoB,QAAO;AACpD,QAAM,KAAK,OAAO,GAAG;AACrB,MAAI,CAAC,OAAO,UAAU,EAAE,KAAK,MAAM,EAAG,QAAO;AAC7C,SAAO,EAAE,GAAG,OAAO,GAAG;AACxB;AAEO,SAAS,WAAkB;AAChC,SAAO,qBAAqB,eAAe,QAAQ,EAAE,KAAK;AAC5D;AAWO,SAAS,aAAuB;AACrC,QAAM,MAAM,eAAe,QAAQ;AACnC,QAAM,OAAiB,CAAC;AAExB,QAAM,aAAa,gBAAgB,QAAQ;AAC3C,MAAI,WAAY,MAAK,KAAK,UAAU;AAEpC,MAAI,IAAI,aAAa,QAAQ,IAAI,IAAI,SAAS,GAAG;AAC/C,SAAK,KAAK,QAAQ,IAAI,IAAI,SAAS,CAAE;AAAA,EACvC;AASA,MAAI,QAAQ,IAAI,gBAAgB,QAAQ,IAAI,sBAAsB,KAAK,SAAS,GAAG;AACjF,WAAO,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC;AAAA,EACjC;AAEA,OAAK,KAAK,GAAG,IAAI,YAAY;AAE7B,SAAO,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC;AACjC;AAEO,SAAS,YAAoB;AAClC,SAAO,WAAW,EAAE,CAAC;AACvB;AAEO,SAAS,eAAe,QAAwB;AACrD,SAAO,WAAW,eAAe,QAAQ,EAAE,YAAY,OAAO,MAAM;AACtE;AAEO,SAAS,YAAqB;AACnC,SAAO,eAAe,QAAQ,EAAE;AAClC;","names":[]}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  EAS_CONTRACTS,
3
3
  EAS_SCHEMAS
4
- } from "./chunk-RAITRCM7.js";
4
+ } from "./chunk-IFL5SREW.js";
5
5
  import {
6
6
  getAccount,
7
7
  getPublicClient,
8
8
  waitForReceipt,
9
9
  writeContractWithRetry
10
- } from "./chunk-LZH4PJI4.js";
10
+ } from "./chunk-ROKE6ZAW.js";
11
11
  import {
12
12
  CHAIN_REGISTRY,
13
13
  getChain,
@@ -15,7 +15,7 @@ import {
15
15
  getNetwork,
16
16
  isTestnet,
17
17
  withNetwork
18
- } from "./chunk-HYUUR6SC.js";
18
+ } from "./chunk-DXJJMRBM.js";
19
19
  import {
20
20
  EAS_ABI
21
21
  } from "./chunk-BQMOE4FJ.js";
@@ -453,4 +453,4 @@ export {
453
453
  queryApprovals,
454
454
  queryJoinRequests
455
455
  };
456
- //# sourceMappingURL=chunk-XMYD5SES.js.map
456
+ //# sourceMappingURL=chunk-GU5VZGXB.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getNetwork
3
- } from "./chunk-HYUUR6SC.js";
3
+ } from "./chunk-DXJJMRBM.js";
4
4
 
5
5
  // src/lib/addresses.ts
6
6
  import { isAddress } from "viem";
@@ -305,4 +305,4 @@ export {
305
305
  SYNTHRA,
306
306
  CHAINLINK
307
307
  };
308
- //# sourceMappingURL=chunk-RAITRCM7.js.map
308
+ //# sourceMappingURL=chunk-IFL5SREW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getAccount
3
- } from "./chunk-LZH4PJI4.js";
3
+ } from "./chunk-ROKE6ZAW.js";
4
4
  import {
5
5
  getVeniceApiKey,
6
6
  setVeniceApiKey
@@ -186,4 +186,4 @@ export {
186
186
  chatCompletion,
187
187
  listModels
188
188
  };
189
- //# sourceMappingURL=chunk-WO4YDEDH.js.map
189
+ //# sourceMappingURL=chunk-NPWYX6NU.js.map
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  getChain,
3
+ getMinGasReserve,
3
4
  getRpcUrls
4
- } from "./chunk-HYUUR6SC.js";
5
+ } from "./chunk-DXJJMRBM.js";
5
6
  import {
6
7
  loadConfig
7
8
  } from "./chunk-GSC7PVEO.js";
@@ -295,14 +296,15 @@ function formatContractError(err) {
295
296
  return "Transaction nonce conflict \u2014 a previous transaction is still pending. Wait a moment and retry.";
296
297
  }
297
298
  if (rawMsg.includes("insufficient funds for gas")) {
298
- return "Insufficient ETH for gas fees. Top up your wallet with ETH on Base.";
299
+ return `Insufficient ETH for gas fees. Top up your wallet with ETH on ${getChain().name}.`;
299
300
  }
300
301
  let cleaned = rawMsg.replace(/ContractFunctionRevertedError:\s*/g, "").replace(/Docs:\s*https:\/\/\S+/g, "").replace(/Version:\s*viem@\S+/g, "").replace(/Request Arguments:[\s\S]*$/m, "").replace(/Contract Call:[\s\S]*?(?=\n\n|\n[A-Z]|$)/m, "").trim();
301
302
  const sigMatch = cleaned.match(/signature[:\s]+"?(0x[0-9a-fA-F]{8,})"?/);
302
303
  if (sigMatch) {
303
304
  const decoded = decodeContractError(sigMatch[1]);
304
305
  if (decoded) return decoded.message;
305
- cleaned = `Contract reverted with unknown error selector ${sigMatch[1]}. Check the transaction on Basescan for details.`;
306
+ const explorer = getChain().blockExplorers?.default?.name ?? "the block explorer";
307
+ cleaned = `Contract reverted with unknown error selector ${sigMatch[1]}. Check the transaction on ${explorer} for details.`;
306
308
  }
307
309
  return cleaned;
308
310
  }
@@ -383,6 +385,22 @@ async function estimateFeesWithBuffer() {
383
385
  }
384
386
  return { maxFeePerGas: fee, maxPriorityFeePerGas: tip };
385
387
  }
388
+ var GAS_UNITS = {
389
+ wrap: 55000n,
390
+ approve: 60000n,
391
+ deposit: 250000n,
392
+ redeem: 250000n
393
+ };
394
+ async function estimateGasCushion(gasUnits, safetyFactor = 2n) {
395
+ const floor = getMinGasReserve();
396
+ try {
397
+ const { maxFeePerGas } = await estimateFeesWithBuffer();
398
+ const derived = gasUnits * maxFeePerGas * safetyFactor;
399
+ return derived > floor ? derived : floor;
400
+ } catch {
401
+ return floor;
402
+ }
403
+ }
386
404
  var MAX_RETRIES = 3;
387
405
  var RETRY_DELAY_MS = 1500;
388
406
  var STUCK_NONCE_CONFIRM_DELAY_MS = 2500;
@@ -512,10 +530,12 @@ export {
512
530
  resetClients,
513
531
  getAccount,
514
532
  estimateFeesWithBuffer,
533
+ GAS_UNITS,
534
+ estimateGasCushion,
515
535
  detectStuckNonce,
516
536
  unstickWallet,
517
537
  sendTxWithRetry,
518
538
  writeContractWithRetry,
519
539
  waitForReceipt
520
540
  };
521
- //# sourceMappingURL=chunk-LZH4PJI4.js.map
541
+ //# sourceMappingURL=chunk-ROKE6ZAW.js.map