@sage-protocol/sdk 0.1.2 → 0.1.7

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.
@@ -14,7 +14,7 @@ var require_package = __commonJS({
14
14
  "package.json"(exports2, module2) {
15
15
  module2.exports = {
16
16
  name: "@sage-protocol/sdk",
17
- version: "0.1.2",
17
+ version: "0.1.4",
18
18
  description: "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
19
19
  main: "dist/index.cjs",
20
20
  module: "dist/index.mjs",
@@ -50,10 +50,10 @@ var require_package = __commonJS({
50
50
  ],
51
51
  sideEffects: false,
52
52
  browser: {
53
+ child_process: false,
53
54
  fs: false,
54
- path: false,
55
55
  os: false,
56
- child_process: false
56
+ path: false
57
57
  },
58
58
  repository: {
59
59
  type: "git",
@@ -82,6 +82,18 @@ var require_package = __commonJS({
82
82
  sinon: "^17.0.1",
83
83
  tsup: "^8.1.0",
84
84
  typescript: "^5.4.0"
85
+ },
86
+ peerDependencies: {
87
+ react: "^18.0.0 || ^19.0.0",
88
+ swr: "^2.0.0"
89
+ },
90
+ peerDependenciesMeta: {
91
+ react: {
92
+ optional: true
93
+ },
94
+ swr: {
95
+ optional: true
96
+ }
85
97
  }
86
98
  };
87
99
  }
@@ -529,9 +541,9 @@ var require_subgraph = __commonJS({
529
541
  * Canonical proposal timeline. Tries common fields first, then event-style fallbacks.
530
542
  * Returns { id, createdAt, queuedAt, executedAt, canceledAt, eta, state } (numbers/strings may be null when unavailable).
531
543
  */
532
- async getProposalTimeline({ url, id }) {
544
+ async getProposalTimeline({ url, id: id2 }) {
533
545
  if (!url) throw new Error("subgraph url required");
534
- const pid = typeof id === "bigint" ? id.toString() : String(id);
546
+ const pid = typeof id2 === "bigint" ? id2.toString() : String(id2);
535
547
  try {
536
548
  const data = await query(url, `
537
549
  query($id: ID!) {
@@ -719,10 +731,10 @@ var require_subgraph = __commonJS({
719
731
  updatedAt: Number(p.updatedAt || 0)
720
732
  } : null;
721
733
  },
722
- async getProposalById({ url, id }) {
734
+ async getProposalById({ url, id: id2 }) {
723
735
  if (!url) throw new Error("subgraph url required");
724
736
  const doc = `query($id: ID!){ proposal(id:$id){ id proposer description createdAt updatedAt state eta targets values calldatas } }`;
725
- const data = await query(url, doc, { id: String(id) });
737
+ const data = await query(url, doc, { id: String(id2) });
726
738
  const p = data?.proposal;
727
739
  if (!p) return null;
728
740
  return {
@@ -770,7 +782,7 @@ var require_ipfs = __commonJS({
770
782
  "src/ipfs/index.js"(exports2, module2) {
771
783
  var axiosDefault = require("axios");
772
784
  var FormData = require("form-data");
773
- var { ethers } = require("ethers");
785
+ var { ethers: ethers2 } = require("ethers");
774
786
  var DEFAULT_GATEWAY = "https://ipfs.dev.sageprotocol.io/ipfs";
775
787
  function toLowerSafe(value, fallback = "") {
776
788
  if (value == null) return fallback;
@@ -825,14 +837,14 @@ var require_ipfs = __commonJS({
825
837
  }
826
838
  return Array.from(urls);
827
839
  }
828
- function ensureLeadingSlash(path, fallback = "") {
829
- const value = path || fallback;
840
+ function ensureLeadingSlash(path2, fallback = "") {
841
+ const value = path2 || fallback;
830
842
  if (!value) return fallback;
831
843
  return value.startsWith("/") ? value : `/${value}`;
832
844
  }
833
845
  function generateDeterministicCid(payload) {
834
846
  const content = Buffer.isBuffer(payload) ? payload : typeof payload === "string" ? Buffer.from(payload) : Buffer.from(JSON.stringify(payload));
835
- const hash = ethers.keccak256(content);
847
+ const hash = ethers2.keccak256(content);
836
848
  const hashStr = hash.slice(2);
837
849
  const cidSuffix = (hashStr + hashStr).slice(0, 44);
838
850
  return `Qm${cidSuffix}`;
@@ -904,7 +916,7 @@ var require_ipfs = __commonJS({
904
916
  }
905
917
  function workerUrl(kind, cid) {
906
918
  const base = workerBaseUrl();
907
- const ensure = (path) => path.startsWith("/") ? path : `/${path}`;
919
+ const ensure = (path2) => path2.startsWith("/") ? path2 : `/${path2}`;
908
920
  if (base) {
909
921
  if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/auth/challenge")}`;
910
922
  if (kind === "upload") return `${base}${ensure(config.worker.uploadPath || "/ipfs/upload")}`;
@@ -1226,18 +1238,18 @@ var require_ipfs = __commonJS({
1226
1238
  return postWorkerJson("discoveryEventsPath", "/discover/events", payload);
1227
1239
  }
1228
1240
  async function recordMcpUsageEvent({ promptId, metadata, address } = {}) {
1229
- const id = typeof promptId === "string" ? promptId.trim() : "";
1230
- if (!id) throw new Error("promptId required");
1231
- const payload = { promptId: id };
1241
+ const id2 = typeof promptId === "string" ? promptId.trim() : "";
1242
+ if (!id2) throw new Error("promptId required");
1243
+ const payload = { promptId: id2 };
1232
1244
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
1233
1245
  const actor = address || config.worker.address;
1234
1246
  if (actor) payload.address = actor;
1235
1247
  return postWorkerJson("discoveryMcpPath", "/discover/mcp", payload);
1236
1248
  }
1237
1249
  async function recordLaunchEvent({ promptId, metadata, address } = {}) {
1238
- const id = typeof promptId === "string" ? promptId.trim() : "";
1239
- if (!id) throw new Error("promptId required");
1240
- const payload = { promptId: id };
1250
+ const id2 = typeof promptId === "string" ? promptId.trim() : "";
1251
+ if (!id2) throw new Error("promptId required");
1252
+ const payload = { promptId: id2 };
1241
1253
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
1242
1254
  const actor = address || config.worker.address;
1243
1255
  if (actor) payload.address = actor;
@@ -1267,11 +1279,11 @@ var require_ipfs = __commonJS({
1267
1279
  }
1268
1280
  async function reviewGovernanceReport(payload = {}) {
1269
1281
  if (!payload || typeof payload !== "object") throw new Error("payload required");
1270
- const id = typeof payload.id === "string" ? payload.id.trim() : "";
1271
- if (!id) throw new Error("report id required");
1282
+ const id2 = typeof payload.id === "string" ? payload.id.trim() : "";
1283
+ if (!id2) throw new Error("report id required");
1272
1284
  const status = typeof payload.status === "string" ? payload.status.trim() : "";
1273
1285
  if (!status) throw new Error("status required");
1274
- const body = { id, status };
1286
+ const body = { id: id2, status };
1275
1287
  if (typeof payload.note === "string") body.note = payload.note;
1276
1288
  if (typeof payload.action === "string") body.action = payload.action;
1277
1289
  return postWorkerJson("governanceReviewPath", "/governance/report/review", body);
@@ -1597,18 +1609,18 @@ var require_validation = __commonJS({
1597
1609
  } catch (_) {
1598
1610
  }
1599
1611
  }
1600
- function normaliseSchemaPaths(schemaPaths, { path }) {
1612
+ function normaliseSchemaPaths(schemaPaths, { path: path2 }) {
1601
1613
  if (Array.isArray(schemaPaths) && schemaPaths.length) {
1602
1614
  return schemaPaths;
1603
1615
  }
1604
1616
  return [
1605
- path.join(process.cwd(), "docs", "schemas", "manifest.schema.json"),
1606
- path.join(__dirname, "..", "..", "docs", "schemas", "manifest.schema.json")
1617
+ path2.join(process.cwd(), "docs", "schemas", "manifest.schema.json"),
1618
+ path2.join(__dirname, "..", "..", "docs", "schemas", "manifest.schema.json")
1607
1619
  ];
1608
1620
  }
1609
- function safeJsonParse(fs, file) {
1621
+ function safeJsonParse(fs2, file) {
1610
1622
  try {
1611
- const raw = fs.readFileSync(file, "utf8");
1623
+ const raw = fs2.readFileSync(file, "utf8");
1612
1624
  return JSON.parse(raw);
1613
1625
  } catch (_) {
1614
1626
  return null;
@@ -1634,17 +1646,17 @@ var require_validation = __commonJS({
1634
1646
  }
1635
1647
  function createManifestValidator(options = {}) {
1636
1648
  const {
1637
- fs = fsDefault,
1638
- path = pathDefault,
1649
+ fs: fs2 = fsDefault,
1650
+ path: path2 = pathDefault,
1639
1651
  ajvFactory = defaultAjvFactory,
1640
1652
  addFormats = defaultAddFormats,
1641
1653
  schemaPaths
1642
1654
  } = options;
1643
- const searchPaths = normaliseSchemaPaths(schemaPaths, { path });
1655
+ const searchPaths = normaliseSchemaPaths(schemaPaths, { path: path2 });
1644
1656
  function loadSchema() {
1645
1657
  for (const schemaPath of searchPaths) {
1646
- if (!fs.existsSync(schemaPath)) continue;
1647
- const schema = safeJsonParse(fs, schemaPath);
1658
+ if (!fs2.existsSync(schemaPath)) continue;
1659
+ const schema = safeJsonParse(fs2, schemaPath);
1648
1660
  if (schema) return { schema, schemaPath };
1649
1661
  }
1650
1662
  return { schema: null, schemaPath: null };
@@ -1717,7 +1729,7 @@ var require_validation = __commonJS({
1717
1729
  if (!manifestPath) {
1718
1730
  throw new Error("manifestPath required");
1719
1731
  }
1720
- const data = safeJsonParse(fs, manifestPath);
1732
+ const data = safeJsonParse(fs2, manifestPath);
1721
1733
  if (!data) {
1722
1734
  return {
1723
1735
  ok: false,
@@ -1750,8 +1762,8 @@ var require_validation = __commonJS({
1750
1762
  seenKeys.add(key);
1751
1763
  const files = Array.isArray(prompt.files) ? prompt.files : [];
1752
1764
  for (const file of files) {
1753
- const resolved = path.isAbsolute(file) ? file : manifestPath ? path.join(path.dirname(manifestPath), file) : file;
1754
- if (!fs.existsSync(resolved)) {
1765
+ const resolved = path2.isAbsolute(file) ? file : manifestPath ? path2.join(path2.dirname(manifestPath), file) : file;
1766
+ if (!fs2.existsSync(resolved)) {
1755
1767
  issues.push({
1756
1768
  type: "missing_file",
1757
1769
  message: `File not found: ${file}`,
@@ -1774,7 +1786,7 @@ var require_validation = __commonJS({
1774
1786
  }
1775
1787
  function bestPracticeCheckFile(manifestPath, options2 = {}) {
1776
1788
  if (!manifestPath) throw new Error("manifestPath required");
1777
- const data = safeJsonParse(fs, manifestPath);
1789
+ const data = safeJsonParse(fs2, manifestPath);
1778
1790
  if (!data) {
1779
1791
  return [{ type: "parse_error", message: `Failed to parse JSON at ${manifestPath}`, manifestPath }];
1780
1792
  }
@@ -1797,7 +1809,7 @@ var require_validation = __commonJS({
1797
1809
  // src/library/index.js
1798
1810
  var require_library = __commonJS({
1799
1811
  "src/library/index.js"(exports2, module2) {
1800
- var { Contract, getAddress, keccak256, toUtf8Bytes } = require("ethers");
1812
+ var { Contract, getAddress, keccak256, toUtf8Bytes, AbiCoder } = require("ethers");
1801
1813
  var { Interface } = require("ethers");
1802
1814
  var ABI = require_abi();
1803
1815
  var { SageSDKError, CODES } = require_errors();
@@ -1836,17 +1848,31 @@ var require_library = __commonJS({
1836
1848
  promptCount: Number(promptCount)
1837
1849
  };
1838
1850
  }
1851
+ function _computeLibraryKey(subdao, libraryId) {
1852
+ const coder = AbiCoder.defaultAbiCoder ? AbiCoder.defaultAbiCoder() : new AbiCoder();
1853
+ const encoded = coder.encode(["address", "string"], [getAddress(subdao), String(libraryId)]);
1854
+ return keccak256(encoded);
1855
+ }
1839
1856
  async function getLatestLibrary({ provider, registry, subdao, libraryId = "main" }) {
1840
1857
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1841
1858
  const addr = normalise(registry, "registry");
1842
1859
  const sub = normalise(subdao, "subdao");
1843
1860
  const contract = new Contract(addr, ABI.LibraryRegistry, provider);
1844
- const key = keccak256(toUtf8Bytes(`${sub.toLowerCase()}::${libraryId}`));
1861
+ const key = _computeLibraryKey(sub, libraryId);
1845
1862
  const latestCID = await contract.subdaoLibraryLatest(key).catch(() => "");
1846
1863
  if (!latestCID || latestCID.length === 0) return null;
1847
1864
  const info = await getManifestInfo({ provider, registry: addr, manifestCID: latestCID });
1848
1865
  return info;
1849
1866
  }
1867
+ async function getBeforeAfterForUpdate({ provider, registry, subdao, libraryId = "main", newCid }) {
1868
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1869
+ const addr = normalise(registry, "registry");
1870
+ const sub = normalise(subdao, "subdao");
1871
+ const contract = new Contract(addr, ABI.LibraryRegistry, provider);
1872
+ const key = _computeLibraryKey(sub, libraryId);
1873
+ const prev = await contract.subdaoLibraryLatest(key).catch(() => "");
1874
+ return { previousCID: prev || null, newCID: String(newCid), libraryId: String(libraryId) };
1875
+ }
1850
1876
  async function hasScopedOwnership({ provider, registry, subdao, manifestCID }) {
1851
1877
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1852
1878
  const addr = normalise(registry, "registry");
@@ -1872,6 +1898,8 @@ var require_library = __commonJS({
1872
1898
  getLatestLibrary,
1873
1899
  hasScopedOwnership,
1874
1900
  buildUpdateLibraryForSubDAOTx,
1901
+ _computeLibraryKey,
1902
+ getBeforeAfterForUpdate,
1875
1903
  /** Build an authorizeTimelock(timelock, subdao) call for a LibraryRegistry. */
1876
1904
  buildAuthorizeTimelockTx: function buildAuthorizeTimelockTx({ registry, timelock, subdao }) {
1877
1905
  const to = normalise(registry, "registry");
@@ -1958,11 +1986,11 @@ var require_governance = __commonJS({
1958
1986
  stakeAmount
1959
1987
  };
1960
1988
  }
1961
- async function getProposal({ provider, governor, id }) {
1989
+ async function getProposal({ provider, governor, id: id2 }) {
1962
1990
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
1963
1991
  const addr = normaliseGovernor(governor);
1964
1992
  const g = new Contract(addr, ABI.Governor, provider);
1965
- const pid = typeof id === "bigint" ? id : String(id).startsWith("0x") ? BigInt(id) : BigInt(String(id));
1993
+ const pid = typeof id2 === "bigint" ? id2 : String(id2).startsWith("0x") ? BigInt(id2) : BigInt(String(id2));
1966
1994
  const [state, snapshot, deadline, votes] = await Promise.all([
1967
1995
  g.state(pid),
1968
1996
  g.proposalSnapshot(pid),
@@ -2016,12 +2044,12 @@ var require_governance = __commonJS({
2016
2044
  }
2017
2045
  return proposals;
2018
2046
  }
2019
- async function getProposalMetadata({ provider, governor, id, fromBlock = 0, toBlock = "latest" }) {
2047
+ async function getProposalMetadata({ provider, governor, id: id2, fromBlock = 0, toBlock = "latest" }) {
2020
2048
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
2021
2049
  const addr = normaliseGovernor(governor);
2022
2050
  const iface = new Interface([ABI.Events.ProposalCreated]);
2023
2051
  const topic = iface.getEvent("ProposalCreated").topicHash;
2024
- const pid = typeof id === "bigint" ? id : String(id).startsWith("0x") ? BigInt(id) : BigInt(String(id));
2052
+ const pid = typeof id2 === "bigint" ? id2 : String(id2).startsWith("0x") ? BigInt(id2) : BigInt(String(id2));
2025
2053
  const logs = await provider.getLogs({ address: addr, fromBlock, toBlock, topics: [topic] });
2026
2054
  for (const log of logs) {
2027
2055
  try {
@@ -2082,10 +2110,10 @@ var require_governance = __commonJS({
2082
2110
  function buildCastVoteTx({ governor, proposalId, support, reason }) {
2083
2111
  const addr = normaliseGovernor(governor);
2084
2112
  const iface = new Interface(ABI.Governor);
2085
- const id = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2113
+ const id2 = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2086
2114
  const s = Number(support);
2087
2115
  const hasReason = reason && String(reason).length > 0;
2088
- const data = hasReason ? iface.encodeFunctionData("castVoteWithReason(uint256,uint8,string)", [id, s, String(reason)]) : iface.encodeFunctionData("castVote(uint256,uint8)", [id, s]);
2116
+ const data = hasReason ? iface.encodeFunctionData("castVoteWithReason(uint256,uint8,string)", [id2, s, String(reason)]) : iface.encodeFunctionData("castVote(uint256,uint8)", [id2, s]);
2089
2117
  return { to: addr, data, value: BigIntZero };
2090
2118
  }
2091
2119
  function cueTx(fn, { governor, targets = [], values = [], calldatas = [], descriptionOrHash = "" }) {
@@ -2106,17 +2134,73 @@ var require_governance = __commonJS({
2106
2134
  function buildQueueByIdTx({ governor, proposalId }) {
2107
2135
  const addr = normaliseGovernor(governor);
2108
2136
  const iface = new Interface(ABI.Governor);
2109
- const id = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2110
- const data = iface.encodeFunctionData("queue(uint256)", [id]);
2137
+ const id2 = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2138
+ const data = iface.encodeFunctionData("queue(uint256)", [id2]);
2111
2139
  return { to: addr, data, value: BigIntZero };
2112
2140
  }
2113
2141
  function buildExecuteByIdTx({ governor, proposalId }) {
2114
2142
  const addr = normaliseGovernor(governor);
2115
2143
  const iface = new Interface(ABI.Governor);
2116
- const id = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2117
- const data = iface.encodeFunctionData("execute(uint256)", [id]);
2144
+ const id2 = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2145
+ const data = iface.encodeFunctionData("execute(uint256)", [id2]);
2118
2146
  return { to: addr, data, value: BigIntZero };
2119
2147
  }
2148
+ async function decodeProposalEffects({ provider, governor, proposalId, fromBlock = 0, toBlock = "latest" }) {
2149
+ if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
2150
+ const govAddr = normaliseGovernor(governor);
2151
+ const iface = new Interface([ABI.Events.ProposalCreated]);
2152
+ const topic = iface.getEvent("ProposalCreated").topicHash;
2153
+ const id2 = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
2154
+ let parsed = null;
2155
+ let logTxHash = null;
2156
+ const logs = await provider.getLogs({ address: govAddr, fromBlock, toBlock, topics: [topic] });
2157
+ for (const log of logs) {
2158
+ try {
2159
+ const p = iface.parseLog(log);
2160
+ if (BigInt(p.args.id.toString()) === id2) {
2161
+ parsed = p;
2162
+ logTxHash = log.transactionHash;
2163
+ break;
2164
+ }
2165
+ } catch (_) {
2166
+ }
2167
+ }
2168
+ if (!parsed) throw new Error("ProposalCreated event not found");
2169
+ const targets = parsed.args.targets.map(getAddress);
2170
+ const calldatas = parsed.args.calldatas.map((d) => typeof d === "string" ? d : "0x" + Buffer.from(d).toString("hex"));
2171
+ const LibraryIface = new Interface(["function updateLibraryForSubDAO(address,string,string,uint256)", "function subdaoLibraryLatest(bytes32) view returns (string)"]);
2172
+ const PromptIface = new Interface(["function updatePromptByGovernance(string,string)"]);
2173
+ const coder = AbiCoder.defaultAbiCoder ? AbiCoder.defaultAbiCoder() : new AbiCoder();
2174
+ const effects = [];
2175
+ for (let i = 0; i < calldatas.length; i++) {
2176
+ const data = calldatas[i];
2177
+ const sel = data.slice(0, 10);
2178
+ try {
2179
+ const decoded = LibraryIface.decodeFunctionData("updateLibraryForSubDAO", data);
2180
+ const [subdao, libraryId, newCid, promptCount] = decoded;
2181
+ let previousCID = null;
2182
+ try {
2183
+ const reg = new Contract(targets[i], ["function subdaoLibraryLatest(bytes32) view returns (string)"], provider);
2184
+ const key = keccak256(coder.encode(["address", "string"], [getAddress(subdao), String(libraryId)]));
2185
+ const prev = await reg.subdaoLibraryLatest(key).catch(() => "");
2186
+ previousCID = prev && prev.length ? String(prev) : null;
2187
+ } catch (_) {
2188
+ }
2189
+ effects.push({ type: "libraryUpdate", index: i, target: targets[i], subdao: getAddress(subdao), libraryId: String(libraryId), previousCid: previousCID, newCid: String(newCid), promptCount: Number(promptCount) });
2190
+ continue;
2191
+ } catch (_) {
2192
+ }
2193
+ try {
2194
+ const decodedP = PromptIface.decodeFunctionData("updatePromptByGovernance", data);
2195
+ const [key, newCid] = decodedP;
2196
+ effects.push({ type: "promptUpdate", index: i, target: targets[i], key: String(key), newCid: String(newCid) });
2197
+ continue;
2198
+ } catch (_) {
2199
+ }
2200
+ effects.push({ type: "unknown", index: i, target: targets[i], selector: sel });
2201
+ }
2202
+ return { governor: govAddr, proposalId: id2, tx: logTxHash, actions: { count: calldatas.length }, effects };
2203
+ }
2120
2204
  async function getQuorumAt({ provider, governor, blockTag }) {
2121
2205
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
2122
2206
  const addr = normaliseGovernor(governor);
@@ -2246,6 +2330,7 @@ var require_governance = __commonJS({
2246
2330
  buildExecuteTx,
2247
2331
  buildQueueByIdTx,
2248
2332
  buildExecuteByIdTx,
2333
+ decodeProposalEffects,
2249
2334
  makeProposalDescription,
2250
2335
  stripProposalSalt,
2251
2336
  getQuorumAt,
@@ -2952,7 +3037,7 @@ var require_openzeppelin = __commonJS({
2952
3037
  for (const log of logs) {
2953
3038
  try {
2954
3039
  const parsed = iface.parseLog(log);
2955
- const id = BigInt(parsed.args.id.toString());
3040
+ const id2 = BigInt(parsed.args.id.toString());
2956
3041
  const proposer = getAddress(parsed.args.proposer);
2957
3042
  const targets = parsed.args.targets.map(getAddress);
2958
3043
  const values = parsed.args.values.map((v) => BigInt(v.toString()));
@@ -2962,7 +3047,7 @@ var require_openzeppelin = __commonJS({
2962
3047
  const block = await provider.getBlock(log.blockNumber).catch(() => ({ timestamp: 0 }));
2963
3048
  const quorum = await governance.getQuorumAt({ provider, governor: addr, blockTag: BigInt(endBlock || log.blockNumber) }).catch(() => null);
2964
3049
  const signatures = await getSignatureList({ provider, targets, calldatas, abiResolver, selectorResolver, chainId: resolvedChainId });
2965
- items.push({ id, proposer, createdAt: Number(block.timestamp || 0), startBlock, endBlock, quorum, txHash: log.transactionHash, targets, values, calldatas, signatures });
3050
+ items.push({ id: id2, proposer, createdAt: Number(block.timestamp || 0), startBlock, endBlock, quorum, txHash: log.transactionHash, targets, values, calldatas, signatures });
2966
3051
  } catch (_) {
2967
3052
  }
2968
3053
  }
@@ -2970,10 +3055,10 @@ var require_openzeppelin = __commonJS({
2970
3055
  }
2971
3056
  return { items, nextCursor: null };
2972
3057
  }
2973
- async function getTimelineOnchain({ provider, governor, id, fromBlock = 0, toBlock = "latest" }) {
3058
+ async function getTimelineOnchain({ provider, governor, id: id2, fromBlock = 0, toBlock = "latest" }) {
2974
3059
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
2975
3060
  const addr = normaliseGovernor(governor);
2976
- const pid = typeof id === "bigint" ? id : String(id).startsWith("0x") ? BigInt(id) : BigInt(String(id));
3061
+ const pid = typeof id2 === "bigint" ? id2 : String(id2).startsWith("0x") ? BigInt(id2) : BigInt(String(id2));
2977
3062
  const iface = new Interface([Events.ProposalQueued, Events.ProposalExecuted, Events.ProposalCanceled]);
2978
3063
  const topics = [
2979
3064
  iface.getEvent("ProposalQueued").topicHash,
@@ -3206,9 +3291,9 @@ var require_operations = __commonJS({
3206
3291
  throw new SageSDKError(CODES.INVALID_ARGS, "invalid governor address", { cause: error });
3207
3292
  }
3208
3293
  }
3209
- function normaliseProposalId(id) {
3210
- if (typeof id === "bigint") return id;
3211
- const value = String(id);
3294
+ function normaliseProposalId(id2) {
3295
+ if (typeof id2 === "bigint") return id2;
3296
+ const value = String(id2);
3212
3297
  try {
3213
3298
  return value.startsWith("0x") ? BigInt(value) : BigInt(value);
3214
3299
  } catch (error) {
@@ -3230,7 +3315,7 @@ var require_operations = __commonJS({
3230
3315
  if (!load || !save) return null;
3231
3316
  return { load, save };
3232
3317
  }
3233
- function normaliseTuple(governor, id, metadata) {
3318
+ function normaliseTuple(governor, id2, metadata) {
3234
3319
  const targets = Array.isArray(metadata.targets) ? metadata.targets.map((t) => getAddress(t)) : [];
3235
3320
  let values = Array.isArray(metadata.values) ? metadata.values.map((v) => BigInt(v)) : new Array(targets.length).fill(BigIntZero);
3236
3321
  const calldatas = Array.isArray(metadata.calldatas) ? metadata.calldatas.map(String) : [];
@@ -3240,7 +3325,7 @@ var require_operations = __commonJS({
3240
3325
  const description = metadata.description || metadata.body || "";
3241
3326
  const descriptionHash = metadata.descriptionHash || (typeof metadata.hashDescription === "function" ? metadata.hashDescription(description) : keccak256(toUtf8Bytes(String(description || ""))));
3242
3327
  return {
3243
- id,
3328
+ id: id2,
3244
3329
  governor,
3245
3330
  targets,
3246
3331
  values,
@@ -3265,10 +3350,10 @@ var require_operations = __commonJS({
3265
3350
  }) {
3266
3351
  if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
3267
3352
  const govAddr = normaliseGovernor(governor);
3268
- const id = normaliseProposalId(proposalId);
3353
+ const id2 = normaliseProposalId(proposalId);
3269
3354
  const cacheAdapter = wrapCache(cache);
3270
3355
  if (!refresh && cacheAdapter) {
3271
- const cached = await cacheAdapter.load(govAddr, id);
3356
+ const cached = await cacheAdapter.load(govAddr, id2);
3272
3357
  if (cached) return cached;
3273
3358
  }
3274
3359
  if (helperAddress) {
@@ -3277,10 +3362,10 @@ var require_operations = __commonJS({
3277
3362
  "function getProposalTuple(uint256) view returns (address[] targets,uint256[] values,bytes[] calldatas,bytes32 descriptionHash,bool exists)"
3278
3363
  ];
3279
3364
  const helper = new Contract(helperAddress, helperAbi, provider);
3280
- const res = await helper.getProposalTuple(id);
3365
+ const res = await helper.getProposalTuple(id2);
3281
3366
  if (res && res.exists) {
3282
3367
  const tuple2 = {
3283
- id,
3368
+ id: id2,
3284
3369
  governor: normaliseGovernor(governor),
3285
3370
  targets: res.targets || [],
3286
3371
  values: (res.values || []).map((v) => BigInt(v)),
@@ -3289,7 +3374,7 @@ var require_operations = __commonJS({
3289
3374
  descriptionHash: res.descriptionHash || null,
3290
3375
  createdBlock: null
3291
3376
  };
3292
- if (cacheAdapter) await cacheAdapter.save(governor, id, tuple2);
3377
+ if (cacheAdapter) await cacheAdapter.save(governor, id2, tuple2);
3293
3378
  return tuple2;
3294
3379
  }
3295
3380
  } catch (_) {
@@ -3297,7 +3382,7 @@ var require_operations = __commonJS({
3297
3382
  }
3298
3383
  let metadata = null;
3299
3384
  try {
3300
- metadata = await governance.getProposalMetadata({ provider, governor: govAddr, id });
3385
+ metadata = await governance.getProposalMetadata({ provider, governor: govAddr, id: id2 });
3301
3386
  } catch (_) {
3302
3387
  metadata = null;
3303
3388
  }
@@ -3306,7 +3391,7 @@ var require_operations = __commonJS({
3306
3391
  try {
3307
3392
  const govAbi = new Interface(ABI.Governor);
3308
3393
  const govC = new Contract(govAddr, govAbi, provider);
3309
- const snapshot = hints.snapshot ?? await govC.proposalSnapshot(id).catch(() => null);
3394
+ const snapshot = hints.snapshot ?? await govC.proposalSnapshot(id2).catch(() => null);
3310
3395
  const votingDelay = hints.votingDelay ?? await govC.votingDelay().catch(() => null);
3311
3396
  if (snapshot !== null && votingDelay !== null) {
3312
3397
  const approxCreation = Number(snapshot) - Number(votingDelay);
@@ -3329,9 +3414,9 @@ var require_operations = __commonJS({
3329
3414
  try {
3330
3415
  const parsed = govC.interface.parseLog(log);
3331
3416
  const pid = normaliseProposalId(parsed.args.proposalId);
3332
- if (pid === id) {
3417
+ if (pid === id2) {
3333
3418
  metadata = {
3334
- id,
3419
+ id: id2,
3335
3420
  governor: govAddr,
3336
3421
  targets: Array.from(parsed.args.targets || [], String),
3337
3422
  values: Array.from(parsed.args.values || [], (v) => BigInt(v.toString())),
@@ -3356,7 +3441,7 @@ var require_operations = __commonJS({
3356
3441
  const page = await governance.listProposals({ provider, governor: govAddr, fromBlock, toBlock: "latest" });
3357
3442
  metadata = page.find((entry) => {
3358
3443
  try {
3359
- return normaliseProposalId(entry.id || entry.proposalId) === id;
3444
+ return normaliseProposalId(entry.id || entry.proposalId) === id2;
3360
3445
  } catch (_) {
3361
3446
  return false;
3362
3447
  }
@@ -3366,9 +3451,9 @@ var require_operations = __commonJS({
3366
3451
  if (!metadata) {
3367
3452
  throw new SageSDKError(CODES.NOT_FOUND, "proposal tuple not found");
3368
3453
  }
3369
- const tuple = normaliseTuple(govAddr, id, metadata);
3454
+ const tuple = normaliseTuple(govAddr, id2, metadata);
3370
3455
  if (cacheAdapter) {
3371
- await cacheAdapter.save(govAddr, id, tuple);
3456
+ await cacheAdapter.save(govAddr, id2, tuple);
3372
3457
  }
3373
3458
  return tuple;
3374
3459
  }
@@ -3664,6 +3749,91 @@ var require_operations = __commonJS({
3664
3749
  }
3665
3750
  });
3666
3751
 
3752
+ // src/governance/grants.js
3753
+ var require_grants = __commonJS({
3754
+ "src/governance/grants.js"(exports2, module2) {
3755
+ var { Interface, getAddress, isAddress, parseUnits } = require("ethers");
3756
+ var { SageSDKError, CODES } = require_errors();
3757
+ function normalizeAddress(label, value) {
3758
+ if (!value) throw new SageSDKError(CODES.INVALID_ARGS, `${label} required`);
3759
+ if (!isAddress(value)) throw new SageSDKError(CODES.INVALID_ARGS, `${label} invalid`);
3760
+ return getAddress(value);
3761
+ }
3762
+ async function buildGrantProposal({ provider = null, vault, token, recipient, amount, decimals = null, raw = false, description = null }) {
3763
+ const vaultAddr = normalizeAddress("vault", vault);
3764
+ const tokenAddr = normalizeAddress("token", token);
3765
+ const rcptAddr = normalizeAddress("recipient", recipient);
3766
+ if (amount == null) throw new SageSDKError(CODES.INVALID_ARGS, "amount required");
3767
+ let resolvedDecimals = decimals != null ? Number(decimals) : null;
3768
+ let symbol = null;
3769
+ if (provider && resolvedDecimals == null) {
3770
+ try {
3771
+ const erc20 = new (require("ethers")).Contract(tokenAddr, ["function decimals() view returns (uint8)", "function symbol() view returns (string)"], provider);
3772
+ resolvedDecimals = Number(await erc20.decimals());
3773
+ symbol = await erc20.symbol().catch(() => null);
3774
+ } catch (_) {
3775
+ }
3776
+ }
3777
+ if (resolvedDecimals == null) resolvedDecimals = 18;
3778
+ let amtWei;
3779
+ if (raw) {
3780
+ amtWei = BigInt(String(amount));
3781
+ } else {
3782
+ amtWei = parseUnits(String(amount), resolvedDecimals);
3783
+ }
3784
+ const iface = new Interface(["function withdraw(address token,uint256 amount,address recipient)"]);
3785
+ const data = iface.encodeFunctionData("withdraw", [tokenAddr, amtWei, rcptAddr]);
3786
+ const targets = [vaultAddr];
3787
+ const values = [0n];
3788
+ const calldatas = [data];
3789
+ const desc = description && description.length ? description : `Grant ${String(amount)}${symbol ? " " + symbol : ""} to ${rcptAddr}`;
3790
+ return { targets, values, calldatas, description: desc };
3791
+ }
3792
+ module2.exports = {
3793
+ buildGrantProposal,
3794
+ /**
3795
+ * Build a batched proposal for multiple grants. Each item can specify its own vault
3796
+ * or inherit from defaultVault. Decimals are resolved per-token when provider is present.
3797
+ * items: Array<{ vault?, token, recipient, amount, decimals?, raw? }>
3798
+ */
3799
+ buildBatchGrantProposal: async function buildBatchGrantProposal({ provider = null, items = [], defaultVault = null, description = null }) {
3800
+ if (!Array.isArray(items) || items.length === 0) {
3801
+ throw new SageSDKError(CODES.INVALID_ARGS, "items required");
3802
+ }
3803
+ const targets = [];
3804
+ const values = [];
3805
+ const calldatas = [];
3806
+ const iface = new Interface(["function withdraw(address token,uint256 amount,address recipient)"]);
3807
+ for (const [i, it] of items.entries()) {
3808
+ const vault = it.vault || defaultVault;
3809
+ if (!vault) throw new SageSDKError(CODES.INVALID_ARGS, `item[${i}]: vault missing and no defaultVault provided`);
3810
+ const vaultAddr = normalizeAddress("vault", vault);
3811
+ const tokenAddr = normalizeAddress("token", it.token);
3812
+ const rcptAddr = normalizeAddress("recipient", it.recipient);
3813
+ let resolvedDecimals = it.decimals != null ? Number(it.decimals) : null;
3814
+ if (provider && resolvedDecimals == null && !it.raw) {
3815
+ try {
3816
+ const erc20 = new (require("ethers")).Contract(tokenAddr, ["function decimals() view returns (uint8)"], provider);
3817
+ resolvedDecimals = Number(await erc20.decimals());
3818
+ } catch (_) {
3819
+ }
3820
+ }
3821
+ if (resolvedDecimals == null) resolvedDecimals = 18;
3822
+ let amtWei;
3823
+ if (it.raw) amtWei = BigInt(String(it.amount));
3824
+ else amtWei = parseUnits(String(it.amount), resolvedDecimals);
3825
+ const data = iface.encodeFunctionData("withdraw", [tokenAddr, amtWei, rcptAddr]);
3826
+ targets.push(vaultAddr);
3827
+ values.push(0n);
3828
+ calldatas.push(data);
3829
+ }
3830
+ const desc = description && description.length ? description : `Batch Grants: ${items.length} transfer(s)`;
3831
+ return { targets, values, calldatas, description: desc };
3832
+ }
3833
+ };
3834
+ }
3835
+ });
3836
+
3667
3837
  // src/timelock/index.js
3668
3838
  var require_timelock = __commonJS({
3669
3839
  "src/timelock/index.js"(exports2, module2) {
@@ -3726,8 +3896,8 @@ var require_timelock = __commonJS({
3726
3896
  for (const log of logs) {
3727
3897
  try {
3728
3898
  const parsed = iface.parseLog(log);
3729
- const id = parsed.args.id;
3730
- const op = operations.get(id) || { id, scheduled: [], executed: [], cancelled: false };
3899
+ const id2 = parsed.args.id;
3900
+ const op = operations.get(id2) || { id: id2, scheduled: [], executed: [], cancelled: false };
3731
3901
  if (parsed.name === "CallScheduled") {
3732
3902
  op.scheduled.push({
3733
3903
  index: Number(parsed.args.index),
@@ -3749,7 +3919,7 @@ var require_timelock = __commonJS({
3749
3919
  } else if (parsed.name === "Cancelled") {
3750
3920
  op.cancelled = true;
3751
3921
  }
3752
- operations.set(id, op);
3922
+ operations.set(id2, op);
3753
3923
  } catch (err) {
3754
3924
  continue;
3755
3925
  }
@@ -3772,10 +3942,10 @@ var require_timelock = __commonJS({
3772
3942
  ]);
3773
3943
  return { to, data: payload, value: 0n };
3774
3944
  },
3775
- buildCancelTx: ({ timelock, id }) => {
3776
- if (!id) throw new SageSDKError(CODES.INVALID_ARGS, "operation id required");
3945
+ buildCancelTx: ({ timelock, id: id2 }) => {
3946
+ if (!id2) throw new SageSDKError(CODES.INVALID_ARGS, "operation id required");
3777
3947
  const addr = normalise(timelock, "timelock");
3778
- const payload = TimelockInterface.encodeFunctionData("cancel", [id]);
3948
+ const payload = TimelockInterface.encodeFunctionData("cancel", [id2]);
3779
3949
  return { to: addr, data: payload, value: 0n };
3780
3950
  },
3781
3951
  buildExecuteTx: ({ timelock, target, value = 0n, data = "0x", predecessor = "0x0000000000000000000000000000000000000000000000000000000000000000", salt = "0x0000000000000000000000000000000000000000000000000000000000000000" }) => {
@@ -3884,10 +4054,10 @@ var require_factory = __commonJS({
3884
4054
  const moduleContract = new Contract(templateAddr, ABI.TemplateModule, provider);
3885
4055
  const ids = await moduleContract.getActiveTemplates().catch(() => []);
3886
4056
  const templates = [];
3887
- for (const id of ids) {
3888
- const template = await moduleContract.getTemplateStruct(id);
4057
+ for (const id2 of ids) {
4058
+ const template = await moduleContract.getTemplateStruct(id2);
3889
4059
  templates.push({
3890
- id: Number(id),
4060
+ id: Number(id2),
3891
4061
  name: template[0],
3892
4062
  description: template[1],
3893
4063
  accessModel: Number(template[2]),
@@ -4342,8 +4512,8 @@ var require_prompt = __commonJS({
4342
4512
  for (let i = 1; i <= upto; i++) ids.push(i);
4343
4513
  }
4344
4514
  const results = [];
4345
- for (const id of ids) {
4346
- const key = await contract.getPromptKey(id).catch(() => null);
4515
+ for (const id2 of ids) {
4516
+ const key = await contract.getPromptKey(id2).catch(() => null);
4347
4517
  if (!key) continue;
4348
4518
  results.push(await getPromptData({ provider, registry: addr, key }));
4349
4519
  }
@@ -4355,8 +4525,8 @@ var require_prompt = __commonJS({
4355
4525
  const contract = new Contract(addr, ABI.PromptRegistry, provider);
4356
4526
  const ids = await contract.getByTagPage(tagHash, BigInt(offset), BigInt(Math.min(Number(limit), 100)));
4357
4527
  const out = [];
4358
- for (const id of ids) {
4359
- const key = await contract.getPromptKey(id).catch(() => null);
4528
+ for (const id2 of ids) {
4529
+ const key = await contract.getPromptKey(id2).catch(() => null);
4360
4530
  if (!key) continue;
4361
4531
  out.push(await getPromptData({ provider, registry: addr, key }));
4362
4532
  }
@@ -4368,8 +4538,8 @@ var require_prompt = __commonJS({
4368
4538
  const contract = new Contract(addr, ABI.PromptRegistry, provider);
4369
4539
  const ids = await contract.getByCreatorPage(getAddress(creator), BigInt(offset), BigInt(Math.min(Number(limit), 100)));
4370
4540
  const out = [];
4371
- for (const id of ids) {
4372
- const key = await contract.getPromptKey(id).catch(() => null);
4541
+ for (const id2 of ids) {
4542
+ const key = await contract.getPromptKey(id2).catch(() => null);
4373
4543
  if (!key) continue;
4374
4544
  out.push(await getPromptData({ provider, registry: addr, key }));
4375
4545
  }
@@ -4381,8 +4551,8 @@ var require_prompt = __commonJS({
4381
4551
  const contract = new Contract(addr, ABI.PromptRegistry, provider);
4382
4552
  const ids = await contract.getByCategoryPage(Number(category), BigInt(offset), BigInt(Math.min(Number(limit), 100)));
4383
4553
  const out = [];
4384
- for (const id of ids) {
4385
- const key = await contract.getPromptKey(id).catch(() => null);
4554
+ for (const id2 of ids) {
4555
+ const key = await contract.getPromptKey(id2).catch(() => null);
4386
4556
  if (!key) continue;
4387
4557
  out.push(await getPromptData({ provider, registry: addr, key }));
4388
4558
  }
@@ -4472,9 +4642,9 @@ var require_ipns = __commonJS({
4472
4642
  function removeTrailingSlash(str) {
4473
4643
  return str ? str.replace(/\/$/, "") : str;
4474
4644
  }
4475
- function ensureLeadingSlash(path) {
4476
- if (!path) return "/";
4477
- return path.startsWith("/") ? path : `/${path}`;
4645
+ function ensureLeadingSlash(path2) {
4646
+ if (!path2) return "/";
4647
+ return path2.startsWith("/") ? path2 : `/${path2}`;
4478
4648
  }
4479
4649
  function dedupe(list = []) {
4480
4650
  const seen = /* @__PURE__ */ new Set();
@@ -4569,10 +4739,10 @@ var require_ipns = __commonJS({
4569
4739
  }
4570
4740
  return null;
4571
4741
  }
4572
- async function fetchWithGateways(path, axiosInstance, gateways, { timeout, attempts = 3, baseDelay = 500, jitterMs = 250 } = {}) {
4742
+ async function fetchWithGateways(path2, axiosInstance, gateways, { timeout, attempts = 3, baseDelay = 500, jitterMs = 250 } = {}) {
4573
4743
  const errors = [];
4574
4744
  const unique = dedupe(gateways);
4575
- const suffix = path.replace(/^\/+/, "");
4745
+ const suffix = path2.replace(/^\/+/, "");
4576
4746
  for (let attempt = 0; attempt < attempts; attempt += 1) {
4577
4747
  const delayMs = attempt === 0 ? 0 : jitter(baseDelay * attempt, jitterMs);
4578
4748
  if (delayMs) await delay(delayMs);
@@ -4594,7 +4764,7 @@ var require_ipns = __commonJS({
4594
4764
  }
4595
4765
  }
4596
4766
  }
4597
- const failure = new Error(`Failed to fetch ${path} from gateways`);
4767
+ const failure = new Error(`Failed to fetch ${path2} from gateways`);
4598
4768
  failure.attempts = errors;
4599
4769
  throw failure;
4600
4770
  }
@@ -5021,7 +5191,7 @@ var require_token = __commonJS({
5021
5191
  // src/personal/helpers.js
5022
5192
  var require_helpers = __commonJS({
5023
5193
  "src/personal/helpers.js"(exports2, module2) {
5024
- var { getAddress, ZeroAddress, id } = require("ethers");
5194
+ var { getAddress, ZeroAddress, id: id2 } = require("ethers");
5025
5195
  function normaliseAddress(addr, label) {
5026
5196
  if (!addr) throw new Error(`[personal] missing ${label}`);
5027
5197
  const value = addr.toString();
@@ -5032,7 +5202,7 @@ var require_helpers = __commonJS({
5032
5202
  if (typeof key === "string" && key.startsWith("0x") && key.length === 66) {
5033
5203
  return key;
5034
5204
  }
5035
- return id(key);
5205
+ return id2(key);
5036
5206
  }
5037
5207
  module2.exports = {
5038
5208
  normaliseAddress,
@@ -5061,8 +5231,8 @@ var require_receipt = __commonJS({
5061
5231
  const receiptAddress = normaliseAddress(receipt, "receipt");
5062
5232
  const holderAddress = normaliseAddress(holder, "holder");
5063
5233
  const contract = new Contract(receiptAddress, ABI.PersonalLicenseReceipt, provider);
5064
- const id = typeof receiptId === "bigint" ? receiptId : BigInt(receiptId);
5065
- const balance = await contract.balanceOf(holderAddress, id);
5234
+ const id2 = typeof receiptId === "bigint" ? receiptId : BigInt(receiptId);
5235
+ const balance = await contract.balanceOf(holderAddress, id2);
5066
5236
  return typeof balance === "bigint" ? balance : BigInt(balance.toString());
5067
5237
  }
5068
5238
  module2.exports = {
@@ -5441,12 +5611,12 @@ var require_treasury = __commonJS({
5441
5611
  idList = Array.from({ length: total - start }, (_, i) => start + i);
5442
5612
  }
5443
5613
  const withdrawals = [];
5444
- for (const id of idList) {
5445
- const entry = await contract.pendingWithdrawals(id).catch(() => null);
5614
+ for (const id2 of idList) {
5615
+ const entry = await contract.pendingWithdrawals(id2).catch(() => null);
5446
5616
  if (!entry || !entry.exists) continue;
5447
5617
  const [token, recipient, amount, value, requester, balanceBefore, recipientBalanceBefore, depositSnapshot, isLP, isEmergency] = entry;
5448
5618
  withdrawals.push({
5449
- id: Number(id),
5619
+ id: Number(id2),
5450
5620
  token: token && token !== ZERO_ADDRESS ? getAddress(token) : null,
5451
5621
  recipient: recipient && recipient !== ZERO_ADDRESS ? getAddress(recipient) : null,
5452
5622
  amount: toBigInt(amount),
@@ -5621,17 +5791,17 @@ var require_treasury = __commonJS({
5621
5791
  id: parsed?.id != null ? Number(parsed.id) : null
5622
5792
  };
5623
5793
  }
5624
- async function confirmWithdrawal({ signer, treasury, id, waitMs }) {
5794
+ async function confirmWithdrawal({ signer, treasury, id: id2, waitMs }) {
5625
5795
  const contract = createWriteContract({ signer, treasury });
5626
- const tx = await contract.confirmWithdrawal(Number(id));
5796
+ const tx = await contract.confirmWithdrawal(Number(id2));
5627
5797
  const receipt = await waitForReceipt({ signer, tx, waitMs });
5628
- return { transaction: tx, receipt, id: Number(id) };
5798
+ return { transaction: tx, receipt, id: Number(id2) };
5629
5799
  }
5630
- async function cancelWithdrawal({ signer, treasury, id, waitMs }) {
5800
+ async function cancelWithdrawal({ signer, treasury, id: id2, waitMs }) {
5631
5801
  const contract = createWriteContract({ signer, treasury });
5632
- const tx = await contract.cancelWithdrawal(Number(id));
5802
+ const tx = await contract.cancelWithdrawal(Number(id2));
5633
5803
  const receipt = await waitForReceipt({ signer, tx, waitMs });
5634
- return { transaction: tx, receipt, id: Number(id) };
5804
+ return { transaction: tx, receipt, id: Number(id2) };
5635
5805
  }
5636
5806
  async function setPriceOverride({ signer, treasury, token, price, ttlSeconds, waitMs }) {
5637
5807
  const contract = createWriteContract({ signer, treasury });
@@ -5685,7 +5855,23 @@ var require_treasury = __commonJS({
5685
5855
  confirmWithdrawal,
5686
5856
  cancelWithdrawal,
5687
5857
  setPriceOverride,
5688
- clearPriceOverride
5858
+ clearPriceOverride,
5859
+ // TX builders for app usage
5860
+ buildApproveTx: ({ token, spender, amount, decimals = 18 }) => {
5861
+ if (!token || !spender) throw new SageSDKError(CODES.INVALID_ARGS, "token and spender required");
5862
+ const iface = new Interface(["function approve(address,uint256)"]);
5863
+ return { to: getAddress(token), data: iface.encodeFunctionData("approve", [getAddress(spender), BigInt(amount.toString ? amount.toString() : amount)]), value: 0n };
5864
+ },
5865
+ buildTransferTx: ({ token, to, amount, decimals = 18 }) => {
5866
+ if (!token || !to) throw new SageSDKError(CODES.INVALID_ARGS, "token and to required");
5867
+ const iface = new Interface(["function transfer(address,uint256)"]);
5868
+ return { to: getAddress(token), data: iface.encodeFunctionData("transfer", [getAddress(to), BigInt(amount.toString ? amount.toString() : amount)]), value: 0n };
5869
+ },
5870
+ buildWrapEthTx: ({ weth, amountWei }) => {
5871
+ if (!weth) throw new SageSDKError(CODES.INVALID_ARGS, "weth required");
5872
+ const iface = new Interface(["function deposit() payable"]);
5873
+ return { to: getAddress(weth), data: iface.encodeFunctionData("deposit", []), value: BigInt(amountWei.toString ? amountWei.toString() : amountWei) };
5874
+ }
5689
5875
  };
5690
5876
  }
5691
5877
  });
@@ -5897,8 +6083,8 @@ var require_bounty = __commonJS({
5897
6083
  ["function approveBountyCompletion(uint256,string)"],
5898
6084
  signer
5899
6085
  );
5900
- const id = BigInt(bountyId);
5901
- const tx = await bountyContract.approveBountyCompletion(id, String(deliverable));
6086
+ const id2 = BigInt(bountyId);
6087
+ const tx = await bountyContract.approveBountyCompletion(id2, String(deliverable));
5902
6088
  return tx.wait();
5903
6089
  }
5904
6090
  async function proposeApproveWinner({
@@ -5916,14 +6102,14 @@ var require_bounty = __commonJS({
5916
6102
  const context = await subdao.getSubDAOInfo({ provider, subdao: subdaoAddress });
5917
6103
  const governor = normaliseAddress("governor", context.governor);
5918
6104
  const iface = new Interface(["function approveBountyCompletion(uint256,string)"]);
5919
- const id = BigInt(bountyId);
5920
- const data = iface.encodeFunctionData("approveBountyCompletion", [id, String(deliverable)]);
6105
+ const id2 = BigInt(bountyId);
6106
+ const data = iface.encodeFunctionData("approveBountyCompletion", [id2, String(deliverable)]);
5921
6107
  const proposalCall = governance.buildProposeTx({
5922
6108
  governor,
5923
6109
  targets: [bountySystemAddress],
5924
6110
  values: [0n],
5925
6111
  calldatas: [data],
5926
- description: `Approve bounty completion for id=${id}`
6112
+ description: `Approve bounty completion for id=${id2}`
5927
6113
  });
5928
6114
  return { governor, proposalCall };
5929
6115
  }
@@ -5936,248 +6122,6 @@ var require_bounty = __commonJS({
5936
6122
  }
5937
6123
  });
5938
6124
 
5939
- // src/bond/index.js
5940
- var require_bond = __commonJS({
5941
- "src/bond/index.js"(exports2, module2) {
5942
- var {
5943
- Contract,
5944
- Interface,
5945
- MaxUint256,
5946
- WeiPerEther,
5947
- formatUnits,
5948
- getAddress,
5949
- parseUnits
5950
- } = require("ethers");
5951
- var ABI = require_abi();
5952
- var { SageSDKError, CODES } = require_errors();
5953
- function normalise(address, label) {
5954
- if (!address) throw new SageSDKError(CODES.INVALID_ARGS, `${label} required`);
5955
- try {
5956
- return getAddress(address);
5957
- } catch (err) {
5958
- throw new SageSDKError(CODES.INVALID_ARGS, `invalid ${label}`, { cause: err });
5959
- }
5960
- }
5961
- var ERC20 = [
5962
- "function decimals() view returns (uint8)",
5963
- "function allowance(address owner, address spender) view returns (uint256)",
5964
- "function approve(address spender, uint256 value) returns (bool)",
5965
- "function balanceOf(address) view returns (uint256)"
5966
- ];
5967
- var BondInterface = new Interface(ABI.BondDepository);
5968
- async function getInfo({ provider, bond }) {
5969
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
5970
- const addr = normalise(bond, "bond");
5971
- const c = new Contract(addr, ABI.BondDepository, provider);
5972
- const [terms, price, priceUsd, totalDebt, payoutToken, principalToken, treasury] = await Promise.all([
5973
- c.terms(),
5974
- c.bondPrice(),
5975
- c.bondPriceInUSD().catch(() => 0n),
5976
- // totalDebt is keyed by payout token in this implementation
5977
- c.payoutToken().then((pt) => c.totalDebt(pt)).catch(() => 0n),
5978
- c.payoutToken(),
5979
- c.principalToken(),
5980
- c.treasury()
5981
- ]);
5982
- return {
5983
- bond: addr,
5984
- payoutToken,
5985
- principalToken,
5986
- treasury,
5987
- terms: {
5988
- controlVariable: BigInt(terms.controlVariable?.toString?.() || terms[0]?.toString?.() || "0"),
5989
- minimumPrice: BigInt(terms.minimumPrice?.toString?.() || terms[1]?.toString?.() || "0"),
5990
- maxPayoutBps: BigInt(terms.maxPayout?.toString?.() || terms[2]?.toString?.() || "0"),
5991
- maxDebt: BigInt(terms.maxDebt?.toString?.() || terms[3]?.toString?.() || "0"),
5992
- vestingTerm: BigInt(terms.vestingTerm?.toString?.() || terms[4]?.toString?.() || "0"),
5993
- feeBps: BigInt(terms.fee?.toString?.() || terms[5]?.toString?.() || "0")
5994
- },
5995
- price: BigInt(price.toString()),
5996
- priceUSD: BigInt(priceUsd.toString()),
5997
- totalDebt: BigInt(totalDebt.toString())
5998
- };
5999
- }
6000
- async function getPrice({ provider, bond }) {
6001
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
6002
- const c = new Contract(normalise(bond, "bond"), ABI.BondDepository, provider);
6003
- return c.bondPrice();
6004
- }
6005
- async function getUserStatus({ provider, bond, user }) {
6006
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
6007
- const addr = normalise(bond, "bond");
6008
- const who = normalise(user, "user");
6009
- const c = new Contract(addr, ABI.BondDepository, provider);
6010
- const [info, pending, vested] = await Promise.all([
6011
- c.bondInfo(who),
6012
- c.pendingPayout(who),
6013
- c.percentVestedFor(who).catch(() => 0n)
6014
- ]);
6015
- return {
6016
- address: who,
6017
- bond: addr,
6018
- payoutRemaining: BigInt(info?.payout?.toString?.() || info[0]?.toString?.() || "0"),
6019
- vestingBlocks: BigInt(info?.vesting?.toString?.() || info[1]?.toString?.() || "0"),
6020
- lastInteractionBlock: BigInt(info?.lastBlock?.toString?.() || info[2]?.toString?.() || "0"),
6021
- pricePaid: BigInt(info?.pricePaid?.toString?.() || info[3]?.toString?.() || "0"),
6022
- pendingPayout: BigInt(pending.toString()),
6023
- percentVestedBps: BigInt(vested.toString())
6024
- };
6025
- }
6026
- function buildDepositTx({ bond, amount, maxPrice }) {
6027
- const to = normalise(bond, "bond");
6028
- const iface = new Interface(ABI.BondDepository);
6029
- const data = iface.encodeFunctionData("deposit", [
6030
- BigInt(amount ?? 0n),
6031
- // principal amount in token units
6032
- BigInt(maxPrice ?? 0n)
6033
- // scaled by 1e18
6034
- ]);
6035
- return { to, data, value: 0n };
6036
- }
6037
- function buildRedeemTx({ bond, recipient, stake = false }) {
6038
- const to = normalise(bond, "bond");
6039
- const iface = new Interface(ABI.BondDepository);
6040
- const data = iface.encodeFunctionData("redeem", [normalise(recipient, "recipient"), !!stake]);
6041
- return { to, data, value: 0n };
6042
- }
6043
- async function getTokenDecimals({ provider, token }) {
6044
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
6045
- const c = new Contract(normalise(token, "token"), ERC20, provider);
6046
- const d = await c.decimals().catch(() => 18);
6047
- return Number(d);
6048
- }
6049
- async function getPrincipalAndPayout({ provider, bond }) {
6050
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
6051
- const c = new Contract(normalise(bond, "bond"), ABI.BondDepository, provider);
6052
- const [payoutToken, principalToken] = await Promise.all([
6053
- c.payoutToken(),
6054
- c.principalToken()
6055
- ]);
6056
- return { payoutToken, principalToken };
6057
- }
6058
- async function estimatePayout({ provider, bond, amount, principalDecimals, payoutDecimals }) {
6059
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
6060
- if (amount == null) throw new SageSDKError(CODES.INVALID_ARGS, "amount required");
6061
- const { principalToken, payoutToken } = await getPrincipalAndPayout({ provider, bond });
6062
- const principalDec = principalDecimals ?? await getTokenDecimals({ provider, token: principalToken });
6063
- const payoutDec = payoutDecimals ?? await getTokenDecimals({ provider, token: payoutToken });
6064
- const amountUnits = parseUnits(String(amount), principalDec);
6065
- const price = await getPrice({ provider, bond });
6066
- if (price === 0n) {
6067
- throw new SageSDKError(CODES.RPC_ERROR, "bond price returned zero");
6068
- }
6069
- const payoutUnits = amountUnits * WeiPerEther / price;
6070
- return {
6071
- inputAmount: String(amount),
6072
- expectedPayout: formatUnits(payoutUnits, payoutDec),
6073
- bondPrice: formatUnits(price, 18),
6074
- principalToken,
6075
- payoutToken,
6076
- principalDecimals: principalDec,
6077
- payoutDecimals: payoutDec
6078
- };
6079
- }
6080
- async function ensurePrincipalAllowance({ signer, bond, amount, principalToken }) {
6081
- if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
6082
- const owner = await signer.getAddress();
6083
- const provider = signer.provider;
6084
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer requires provider");
6085
- const bondAddress = normalise(bond, "bond");
6086
- const tokenAddress = principalToken ? normalise(principalToken, "principalToken") : (await getPrincipalAndPayout({ provider, bond: bondAddress })).principalToken;
6087
- const principal = new Contract(tokenAddress, ERC20, signer);
6088
- const allowance = await principal.allowance(owner, bondAddress);
6089
- if (allowance >= amount) {
6090
- return { approved: false, transactionHash: null };
6091
- }
6092
- const approvalTx = await principal.approve(bondAddress, MaxUint256);
6093
- const receipt = await approvalTx.wait();
6094
- return {
6095
- approved: true,
6096
- transactionHash: receipt.hash ?? approvalTx.hash ?? null
6097
- };
6098
- }
6099
- async function purchase({ signer, bond, amount, maxPrice }) {
6100
- if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
6101
- if (amount == null) throw new SageSDKError(CODES.INVALID_ARGS, "amount required");
6102
- if (maxPrice == null) throw new SageSDKError(CODES.INVALID_ARGS, "maxPrice required");
6103
- const provider = signer.provider;
6104
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer requires provider");
6105
- const bondAddress = normalise(bond, "bond");
6106
- const bondContract = new Contract(bondAddress, ABI.BondDepository, signer);
6107
- const { principalToken, payoutToken } = await getPrincipalAndPayout({ provider, bond: bondAddress });
6108
- const principalDec = await getTokenDecimals({ provider, token: principalToken });
6109
- const payoutDec = await getTokenDecimals({ provider, token: payoutToken });
6110
- const amountUnits = parseUnits(String(amount), principalDec);
6111
- const maxPriceUnits = parseUnits(String(maxPrice), 18);
6112
- const currentPrice = await getPrice({ provider, bond: bondAddress });
6113
- if (currentPrice > maxPriceUnits) {
6114
- throw new SageSDKError(CODES.INVALID_ARGS, "current bond price exceeds max price", {
6115
- currentPrice: formatUnits(currentPrice, 18),
6116
- maxPrice: String(maxPrice)
6117
- });
6118
- }
6119
- await ensurePrincipalAllowance({ signer, bond: bondAddress, amount: amountUnits, principalToken });
6120
- const tx = await bondContract.deposit(amountUnits, maxPriceUnits);
6121
- const receipt = await tx.wait();
6122
- let depositEvent = null;
6123
- for (const log of receipt.logs || []) {
6124
- try {
6125
- const parsed = BondInterface.parseLog(log);
6126
- if (parsed?.name === "BondCreated") {
6127
- depositEvent = parsed.args;
6128
- break;
6129
- }
6130
- } catch (_) {
6131
- }
6132
- }
6133
- return {
6134
- transactionHash: receipt.hash ?? tx.hash ?? null,
6135
- deposit: formatUnits(depositEvent?.deposit ?? amountUnits, principalDec),
6136
- payout: depositEvent?.payout != null ? formatUnits(depositEvent.payout, payoutDec) : null,
6137
- expires: depositEvent?.expires ?? null,
6138
- price: formatUnits(currentPrice, 18)
6139
- };
6140
- }
6141
- async function redeem({ signer, bond, recipient, stake = false }) {
6142
- if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
6143
- const provider = signer.provider;
6144
- if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer requires provider");
6145
- const bondAddress = normalise(bond, "bond");
6146
- const user = normalise(recipient || await signer.getAddress(), "recipient");
6147
- const bondContract = new Contract(bondAddress, ABI.BondDepository, signer);
6148
- const { payoutToken } = await getPrincipalAndPayout({ provider, bond: bondAddress });
6149
- const payoutDec = await getTokenDecimals({ provider, token: payoutToken });
6150
- const pending = await bondContract.pendingPayout(user);
6151
- if (pending === 0n) {
6152
- return {
6153
- transactionHash: null,
6154
- redeemed: "0",
6155
- message: "No bonds available for redemption"
6156
- };
6157
- }
6158
- const tx = await bondContract.redeem(user, !!stake);
6159
- const receipt = await tx.wait();
6160
- return {
6161
- transactionHash: receipt.hash ?? tx.hash ?? null,
6162
- redeemed: formatUnits(pending, payoutDec)
6163
- };
6164
- }
6165
- module2.exports = {
6166
- getInfo,
6167
- getPrice,
6168
- getUserStatus,
6169
- buildDepositTx,
6170
- buildRedeemTx,
6171
- getTokenDecimals,
6172
- getPrincipalAndPayout,
6173
- estimatePayout,
6174
- ensurePrincipalAllowance,
6175
- purchase,
6176
- redeem
6177
- };
6178
- }
6179
- });
6180
-
6181
6125
  // src/utils/provider.js
6182
6126
  var require_provider = __commonJS({
6183
6127
  "src/utils/provider.js"(exports2, module2) {
@@ -6201,7 +6145,7 @@ var require_provider = __commonJS({
6201
6145
  // src/wallet/index.js
6202
6146
  var require_wallet = __commonJS({
6203
6147
  "src/wallet/index.js"(exports2, module2) {
6204
- var { ethers } = require("ethers");
6148
+ var { ethers: ethers2 } = require("ethers");
6205
6149
  var { SageSDKError, CODES } = require_errors();
6206
6150
  var { getProvider } = require_provider();
6207
6151
  function isBrowser() {
@@ -6228,7 +6172,7 @@ var require_wallet = __commonJS({
6228
6172
  const rpcUrl = src.rpcUrl || getDefaultRpc();
6229
6173
  const provider = getProvider({ rpcUrl });
6230
6174
  const pk = src.privateKey.startsWith("0x") ? src.privateKey : "0x" + src.privateKey;
6231
- const signer = new ethers.Wallet(pk, provider);
6175
+ const signer = new ethers2.Wallet(pk, provider);
6232
6176
  return { signer, provider };
6233
6177
  }
6234
6178
  case "pkEnv": {
@@ -6237,7 +6181,7 @@ var require_wallet = __commonJS({
6237
6181
  if (!pk) throw new SageSDKError(CODES.INVALID_ARGS, `${envName} not set`);
6238
6182
  const rpcUrl = src.rpcUrl || getDefaultRpc();
6239
6183
  const provider = getProvider({ rpcUrl });
6240
- const signer = new ethers.Wallet(pk.startsWith("0x") ? pk : "0x" + pk, provider);
6184
+ const signer = new ethers2.Wallet(pk.startsWith("0x") ? pk : "0x" + pk, provider);
6241
6185
  return { signer, provider };
6242
6186
  }
6243
6187
  case "rpc": {
@@ -6248,7 +6192,7 @@ var require_wallet = __commonJS({
6248
6192
  case "injected": {
6249
6193
  if (!isBrowser()) throw new SageSDKError(CODES.MISSING_DEPENDENCY, "Injected provider only available in browser");
6250
6194
  try {
6251
- const provider = new ethers.BrowserProvider(window.ethereum);
6195
+ const provider = new ethers2.BrowserProvider(window.ethereum);
6252
6196
  const signer = await provider.getSigner();
6253
6197
  return { signer, provider };
6254
6198
  } catch (e) {
@@ -6276,7 +6220,7 @@ var require_wallet = __commonJS({
6276
6220
  return process.env.RPC_URL || process.env.BASE_SEPOLIA_RPC_URL || process.env.BASE_SEPOLIA_RPC || "https://base-sepolia.publicnode.com";
6277
6221
  }
6278
6222
  function create() {
6279
- const wallet = ethers.Wallet.createRandom();
6223
+ const wallet = ethers2.Wallet.createRandom();
6280
6224
  return {
6281
6225
  address: wallet.address,
6282
6226
  // Intentionally do not expose privateKey by default to avoid accidental leaks
@@ -6289,7 +6233,7 @@ var require_wallet = __commonJS({
6289
6233
  throw new SageSDKError(CODES.INVALID_ARGS, "secret required to import wallet");
6290
6234
  }
6291
6235
  const pk = secret.startsWith("0x") ? secret : "0x" + secret;
6292
- const wallet = new ethers.Wallet(pk);
6236
+ const wallet = new ethers2.Wallet(pk);
6293
6237
  return { address: wallet.address, _wallet: wallet };
6294
6238
  }
6295
6239
  function exportWallet(w) {
@@ -6324,7 +6268,7 @@ var require_wallet = __commonJS({
6324
6268
  } catch (_) {
6325
6269
  if (!opts || !opts.mnemonic || !opts.rpcUrl) throw new SageSDKError(CODES.INVALID_ARGS, "mnemonic and rpcUrl required");
6326
6270
  const provider = getProvider({ rpcUrl: opts.rpcUrl });
6327
- const wallet = ethers.HDNodeWallet.fromPhrase(opts.mnemonic, opts.path);
6271
+ const wallet = ethers2.HDNodeWallet.fromPhrase(opts.mnemonic, opts.path);
6328
6272
  const signer = wallet.connect(provider);
6329
6273
  return { signer, provider };
6330
6274
  }
@@ -6336,7 +6280,7 @@ var require_wallet = __commonJS({
6336
6280
  } catch (_) {
6337
6281
  if (!opts || !opts.json || !opts.password || !opts.rpcUrl) throw new SageSDKError(CODES.INVALID_ARGS, "json, password, rpcUrl required");
6338
6282
  const provider = getProvider({ rpcUrl: opts.rpcUrl });
6339
- const signer = await ethers.Wallet.fromEncryptedJson(opts.json, opts.password);
6283
+ const signer = await ethers2.Wallet.fromEncryptedJson(opts.json, opts.password);
6340
6284
  return { signer: signer.connect(provider), provider };
6341
6285
  }
6342
6286
  },
@@ -6421,7 +6365,7 @@ var require_session = __commonJS({
6421
6365
 
6422
6366
  // src/wallet/cast-manager.js
6423
6367
  var require_cast_manager = __commonJS({
6424
- "src/wallet/cast-manager.js"(exports2, module2) {
6368
+ "src/wallet/cast-manager.js"(exports, module) {
6425
6369
  var { spawn } = require("child_process");
6426
6370
  var { ethers } = require("ethers");
6427
6371
  var fs = require("fs");
@@ -6438,6 +6382,32 @@ var require_cast_manager = __commonJS({
6438
6382
  blue: (text) => `\x1B[34m${text}\x1B[0m`,
6439
6383
  cyan: (text) => `\x1B[36m${text}\x1B[0m`
6440
6384
  };
6385
+ function optionalRequire(id) {
6386
+ try {
6387
+ const req = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : eval("require");
6388
+ return req(id);
6389
+ } catch (_) {
6390
+ return null;
6391
+ }
6392
+ }
6393
+ function loadConfigBridge() {
6394
+ const shared = optionalRequire("@sage-protocol/shared");
6395
+ if (shared) {
6396
+ if (shared.ConfigManager) return shared.ConfigManager;
6397
+ if (typeof shared.getProjectDir === "function" || typeof shared.readProfiles === "function") {
6398
+ return shared;
6399
+ }
6400
+ }
6401
+ const local = optionalRequire("./config");
6402
+ if (local) {
6403
+ if (local.ConfigManager) return local.ConfigManager;
6404
+ return local;
6405
+ }
6406
+ return {
6407
+ getProjectDir: () => process.cwd(),
6408
+ readProfiles: () => ({ profiles: {}, activeProfile: "default" })
6409
+ };
6410
+ }
6441
6411
  var CastSigner = class extends ethers.VoidSigner {
6442
6412
  constructor(address, provider, castWalletManager) {
6443
6413
  super(address, provider);
@@ -6572,13 +6542,8 @@ var require_cast_manager = __commonJS({
6572
6542
  if (!address) return;
6573
6543
  try {
6574
6544
  const normalized = ethers.getAddress(address);
6575
- let cfgModule;
6576
- try {
6577
- ({ ConfigManager: cfgModule } = require("@sage-protocol/shared"));
6578
- } catch (_) {
6579
- cfgModule = require("./config");
6580
- }
6581
- const projectDir = cfgModule.getProjectDir ? cfgModule.getProjectDir() : process.cwd();
6545
+ const cfgModule = loadConfigBridge();
6546
+ const projectDir = typeof cfgModule.getProjectDir === "function" ? cfgModule.getProjectDir() : process.cwd();
6582
6547
  const cfgPath = path.join(projectDir, ".sage", "config.json");
6583
6548
  const current = fs.existsSync(cfgPath) ? JSON.parse(fs.readFileSync(cfgPath, "utf8") || "{}") : {};
6584
6549
  const active = current.activeProfile || "default";
@@ -6664,14 +6629,9 @@ var require_cast_manager = __commonJS({
6664
6629
  console.log(colors.blue("\u{1F517} Connecting wallet using Cast keystore..."));
6665
6630
  this.ensureKeystoreDir();
6666
6631
  try {
6667
- let cliConfig;
6668
- try {
6669
- ({ ConfigManager: cliConfig } = require("@sage-protocol/shared"));
6670
- } catch (_) {
6671
- cliConfig = require("./config");
6672
- }
6673
- const profiles = cliConfig.readProfiles();
6674
- const active = profiles.activeProfile || "default";
6632
+ const cliConfig = loadConfigBridge();
6633
+ const profiles = typeof cliConfig.readProfiles === "function" ? cliConfig.readProfiles() : { profiles: {}, activeProfile: "default" };
6634
+ const active = profiles?.activeProfile || "default";
6675
6635
  const wanted = profiles?.profiles?.[active]?.wallet?.defaultAccount;
6676
6636
  if (wanted && this.keystoreDir && fs.existsSync(this.keystoreDir)) {
6677
6637
  const files = fs.readdirSync(this.keystoreDir);
@@ -6707,7 +6667,7 @@ var require_cast_manager = __commonJS({
6707
6667
  this.ensureAccountSynced(walletData.address, walletData.keystorePath, { silent: true });
6708
6668
  }
6709
6669
  try {
6710
- const { checkRpcHealth } = require("./utils/rpc-health");
6670
+ const { checkRpcHealth } = optionalRequire("./utils/rpc-health") || {};
6711
6671
  const candidates = [
6712
6672
  this.rpcUrl,
6713
6673
  process.env.BASE_SEPOLIA_RPC,
@@ -7663,16 +7623,16 @@ ${error}` : ""}`;
7663
7623
  return fullPath;
7664
7624
  }
7665
7625
  };
7666
- module2.exports = CastWalletManager;
7626
+ module.exports = CastWalletManager;
7667
7627
  }
7668
7628
  });
7669
7629
 
7670
7630
  // src/wallet/cdp-manager.js
7671
7631
  var require_cdp_manager = __commonJS({
7672
7632
  "src/wallet/cdp-manager.js"(exports2, module2) {
7673
- var fs = require("fs");
7674
- var path = require("path");
7675
- var { ethers } = require("ethers");
7633
+ var fs2 = require("fs");
7634
+ var path2 = require("path");
7635
+ var { ethers: ethers2 } = require("ethers");
7676
7636
  var MinimalCdpSigner = class {
7677
7637
  constructor(manager, provider) {
7678
7638
  this._mgr = manager;
@@ -7701,7 +7661,7 @@ var require_cdp_manager = __commonJS({
7701
7661
  this.provider = null;
7702
7662
  this.signer = null;
7703
7663
  this.connected = false;
7704
- this._profilePath = path.join(process.cwd(), ".cdp-wallet.json");
7664
+ this._profilePath = path2.join(process.cwd(), ".cdp-wallet.json");
7705
7665
  this._client = null;
7706
7666
  this._userId = null;
7707
7667
  this._walletId = null;
@@ -7724,9 +7684,9 @@ var require_cdp_manager = __commonJS({
7724
7684
  }
7725
7685
  }
7726
7686
  _loadProfile() {
7727
- if (fs.existsSync(this._profilePath)) {
7687
+ if (fs2.existsSync(this._profilePath)) {
7728
7688
  try {
7729
- const x = JSON.parse(fs.readFileSync(this._profilePath, "utf8"));
7689
+ const x = JSON.parse(fs2.readFileSync(this._profilePath, "utf8"));
7730
7690
  this._userId = x.userId || null;
7731
7691
  this._walletId = x.walletId || null;
7732
7692
  this.account = x.address || null;
@@ -7736,14 +7696,14 @@ var require_cdp_manager = __commonJS({
7736
7696
  }
7737
7697
  _saveProfile() {
7738
7698
  const payload = { userId: this._userId, walletId: this._walletId, address: this.account };
7739
- fs.writeFileSync(this._profilePath, JSON.stringify(payload, null, 2));
7699
+ fs2.writeFileSync(this._profilePath, JSON.stringify(payload, null, 2));
7740
7700
  }
7741
7701
  async connect() {
7742
7702
  if (!process.env.CDP_API_KEY_ID || !process.env.CDP_API_KEY_SECRET) {
7743
7703
  throw new Error("Missing CDP_API_KEY_ID/CDP_API_KEY_SECRET in env");
7744
7704
  }
7745
7705
  const rpc = process.env.RPC_URL || "https://base-sepolia.publicnode.com";
7746
- this.provider = new ethers.JsonRpcProvider(rpc);
7706
+ this.provider = new ethers2.JsonRpcProvider(rpc);
7747
7707
  this._loadProfile();
7748
7708
  let client;
7749
7709
  try {
@@ -7755,7 +7715,7 @@ var require_cdp_manager = __commonJS({
7755
7715
  const email = process.env.CDP_USER_EMAIL || await this._promptEmail();
7756
7716
  const fakeUserId = `user_${Buffer.from(email).toString("hex").slice(0, 8)}`;
7757
7717
  const fakeWalletId = `w_${Date.now()}`;
7758
- const fakeAddress = this.account || ethers.Wallet.createRandom().address;
7718
+ const fakeAddress = this.account || ethers2.Wallet.createRandom().address;
7759
7719
  this._userId = fakeUserId;
7760
7720
  this._walletId = fakeWalletId;
7761
7721
  this.account = fakeAddress;
@@ -7769,7 +7729,7 @@ var require_cdp_manager = __commonJS({
7769
7729
  console.log("\u{1F4CD} Address:", this.account);
7770
7730
  try {
7771
7731
  const bal = await this.getBalance();
7772
- console.log("\u{1F4B0} Balance:", ethers.formatEther(bal), "ETH");
7732
+ console.log("\u{1F4B0} Balance:", ethers2.formatEther(bal), "ETH");
7773
7733
  } catch (_) {
7774
7734
  }
7775
7735
  }
@@ -7794,7 +7754,7 @@ var require_cdp_manager = __commonJS({
7794
7754
  if (!process.env.SAGE_QUIET_JSON) {
7795
7755
  console.log("\u{1F4DD} Transaction Details:");
7796
7756
  console.log("To:", to);
7797
- console.log("Value:", ethers.formatEther(value));
7757
+ console.log("Value:", ethers2.formatEther(value));
7798
7758
  console.log("Data:", data);
7799
7759
  console.log("\u{1F4E8} Check your email/app to approve the transaction...");
7800
7760
  }
@@ -7846,7 +7806,7 @@ var require_cdp_manager = __commonJS({
7846
7806
  // src/wallet/typed.js
7847
7807
  var require_typed = __commonJS({
7848
7808
  "src/wallet/typed.js"(exports2, module2) {
7849
- var { ethers } = require("ethers");
7809
+ var { ethers: ethers2 } = require("ethers");
7850
7810
  function buildGovernorDomain({ name, chainId, verifyingContract }) {
7851
7811
  if (!name || !chainId || !verifyingContract) throw new Error("name, chainId, verifyingContract required");
7852
7812
  return { name, version: "1", chainId: Number(chainId), verifyingContract };
@@ -7963,12 +7923,40 @@ var require_doppler = __commonJS({
7963
7923
  }
7964
7924
  return { auctionAddress, note: "Lens API not available in this doppler-sdk version" };
7965
7925
  }
7926
+ async function listAuctions(sdk, { limit = 10, cursor } = {}) {
7927
+ const lens = sdk.lens;
7928
+ if (lens) {
7929
+ if (typeof lens.listAuctions === "function") {
7930
+ try {
7931
+ return await lens.listAuctions({ limit, cursor });
7932
+ } catch (e) {
7933
+ }
7934
+ }
7935
+ if (typeof lens.getAuctions === "function") {
7936
+ try {
7937
+ return await lens.getAuctions({ limit, cursor });
7938
+ } catch (e) {
7939
+ }
7940
+ }
7941
+ }
7942
+ const fac = sdk.factory;
7943
+ if (fac && typeof fac.listAuctions === "function") {
7944
+ try {
7945
+ return await fac.listAuctions({ limit, cursor });
7946
+ } catch (e) {
7947
+ }
7948
+ }
7949
+ return { ok: false, error: "LIST_UNSUPPORTED", message: "Listing auctions not supported by this doppler-sdk version" };
7950
+ }
7966
7951
  async function buyTokens(sdk, { auctionAddress, numeraireAmount }) {
7967
7952
  const trade = sdk.trade || sdk.swap || sdk.auction;
7968
7953
  if (trade && typeof trade.buy === "function") {
7969
- return await trade.buy({ auctionAddress, numeraireAmount });
7954
+ try {
7955
+ return await trade.buy({ auctionAddress, numeraireAmount });
7956
+ } catch (e) {
7957
+ }
7970
7958
  }
7971
- throw new Error("Buy is not supported by the current doppler-sdk version");
7959
+ return { ok: false, error: "BUY_UNSUPPORTED", message: "Buy not supported by this doppler-sdk version. Use executeV3BuyExactIn/urExecute helpers or upgrade doppler-sdk." };
7972
7960
  }
7973
7961
  async function migrate(sdk, asset) {
7974
7962
  if (sdk.airlock && typeof sdk.airlock.migrate === "function") {
@@ -7980,6 +7968,7 @@ var require_doppler = __commonJS({
7980
7968
  initDoppler,
7981
7969
  deployDynamicAuction,
7982
7970
  getAuctionStatus,
7971
+ listAuctions,
7983
7972
  buyTokens,
7984
7973
  migrate
7985
7974
  };
@@ -8134,6 +8123,952 @@ var require_doppler = __commonJS({
8134
8123
  }
8135
8124
  });
8136
8125
 
8126
+ // src/services/utils/cache.js
8127
+ var require_cache = __commonJS({
8128
+ "src/services/utils/cache.js"(exports2, module2) {
8129
+ var SimpleCache = class {
8130
+ constructor(options = {}) {
8131
+ this.enabled = options.enabled !== false;
8132
+ this.defaultTTL = options.ttl || 3e4;
8133
+ this.maxSize = options.maxSize || 100;
8134
+ this._store = /* @__PURE__ */ new Map();
8135
+ }
8136
+ /**
8137
+ * Get value from cache
8138
+ * @param {string} key - Cache key
8139
+ * @returns {any|null} - Cached value or null if expired/missing
8140
+ */
8141
+ get(key) {
8142
+ if (!this.enabled) return null;
8143
+ const entry = this._store.get(key);
8144
+ if (!entry) return null;
8145
+ if (Date.now() > entry.expires) {
8146
+ this._store.delete(key);
8147
+ return null;
8148
+ }
8149
+ return entry.data;
8150
+ }
8151
+ /**
8152
+ * Set value in cache with TTL
8153
+ * @param {string} key - Cache key
8154
+ * @param {any} data - Data to cache
8155
+ * @param {number} ttl - Time to live in milliseconds (optional)
8156
+ */
8157
+ set(key, data, ttl) {
8158
+ if (!this.enabled) return;
8159
+ if (this._store.size >= this.maxSize && !this._store.has(key)) {
8160
+ const firstKey = this._store.keys().next().value;
8161
+ this._store.delete(firstKey);
8162
+ }
8163
+ this._store.set(key, {
8164
+ data,
8165
+ expires: Date.now() + (ttl || this.defaultTTL)
8166
+ });
8167
+ }
8168
+ /**
8169
+ * Clear all cached entries
8170
+ */
8171
+ clear() {
8172
+ this._store.clear();
8173
+ }
8174
+ /**
8175
+ * Delete specific key
8176
+ * @param {string} key - Cache key to delete
8177
+ */
8178
+ delete(key) {
8179
+ this._store.delete(key);
8180
+ }
8181
+ /**
8182
+ * Get cache statistics
8183
+ * @returns {object} - Cache stats (size, enabled)
8184
+ */
8185
+ stats() {
8186
+ return {
8187
+ size: this._store.size,
8188
+ maxSize: this.maxSize,
8189
+ enabled: this.enabled
8190
+ };
8191
+ }
8192
+ };
8193
+ module2.exports = { SimpleCache };
8194
+ }
8195
+ });
8196
+
8197
+ // src/services/utils/retry.js
8198
+ var require_retry = __commonJS({
8199
+ "src/services/utils/retry.js"(exports2, module2) {
8200
+ async function retryWithBackoff(fn, options = {}) {
8201
+ const {
8202
+ attempts = 3,
8203
+ baseDelay = 1e3,
8204
+ maxDelay = 1e4,
8205
+ onRetry = null
8206
+ } = options;
8207
+ let lastError;
8208
+ for (let i = 0; i < attempts; i++) {
8209
+ try {
8210
+ return await fn();
8211
+ } catch (error) {
8212
+ lastError = error;
8213
+ if (i === attempts - 1) {
8214
+ break;
8215
+ }
8216
+ const delay = Math.min(baseDelay * Math.pow(2, i), maxDelay);
8217
+ if (onRetry) {
8218
+ onRetry({
8219
+ attempt: i + 1,
8220
+ totalAttempts: attempts,
8221
+ delay,
8222
+ error
8223
+ });
8224
+ }
8225
+ await sleep(delay);
8226
+ }
8227
+ }
8228
+ throw lastError;
8229
+ }
8230
+ function sleep(ms) {
8231
+ return new Promise((resolve) => setTimeout(resolve, ms));
8232
+ }
8233
+ module2.exports = { retryWithBackoff, sleep };
8234
+ }
8235
+ });
8236
+
8237
+ // src/errors/index.js
8238
+ var require_errors2 = __commonJS({
8239
+ "src/errors/index.js"(exports2, module2) {
8240
+ var SageSDKError = class extends Error {
8241
+ constructor(message, code, retryable = false, originalError = null) {
8242
+ super(message);
8243
+ this.name = this.constructor.name;
8244
+ this.code = code;
8245
+ this.retryable = retryable;
8246
+ this.originalError = originalError;
8247
+ }
8248
+ };
8249
+ var SubgraphError = class extends SageSDKError {
8250
+ /**
8251
+ * @param {string} message - Error message
8252
+ * @param {'TIMEOUT'|'NETWORK'|'INVALID_RESPONSE'|'NOT_FOUND'|'QUERY_FAILED'} code - Error code
8253
+ * @param {boolean} retryable - Whether error is retryable
8254
+ * @param {Error} originalError - Original error (optional)
8255
+ */
8256
+ constructor(message, code, retryable = false, originalError = null) {
8257
+ super(message, code, retryable, originalError);
8258
+ }
8259
+ };
8260
+ var IPFSError = class extends SageSDKError {
8261
+ /**
8262
+ * @param {string} message - Error message
8263
+ * @param {'TIMEOUT'|'PIN_FAILED'|'INVALID_CID'|'NOT_FOUND'|'GATEWAY_FAILED'|'UPLOAD_FAILED'} code - Error code
8264
+ * @param {boolean} retryable - Whether error is retryable
8265
+ * @param {Error} originalError - Original error (optional)
8266
+ */
8267
+ constructor(message, code, retryable = false, originalError = null) {
8268
+ super(message, code, retryable, originalError);
8269
+ }
8270
+ };
8271
+ function formatErrorMessage(error) {
8272
+ if (!error) return "An unknown error occurred";
8273
+ if (error instanceof SubgraphError || error instanceof IPFSError) {
8274
+ switch (error.code) {
8275
+ case "TIMEOUT":
8276
+ return "Request timed out. Please try again.";
8277
+ case "NETWORK":
8278
+ return "Network error. Check your connection and try again.";
8279
+ case "INVALID_RESPONSE":
8280
+ return "Received invalid data from server.";
8281
+ case "NOT_FOUND":
8282
+ return "Content not found.";
8283
+ case "PIN_FAILED":
8284
+ return "Failed to pin content to IPFS. Please try again.";
8285
+ case "INVALID_CID":
8286
+ return "Invalid content identifier (CID).";
8287
+ case "GATEWAY_FAILED":
8288
+ return "All IPFS gateways failed. Content may be temporarily unavailable.";
8289
+ case "UPLOAD_FAILED":
8290
+ return "Failed to upload content. Please try again.";
8291
+ case "QUERY_FAILED":
8292
+ return "Failed to query data. Please try again.";
8293
+ default:
8294
+ return error.message || "An error occurred";
8295
+ }
8296
+ }
8297
+ return error.message || "An error occurred";
8298
+ }
8299
+ function isRetryable(error) {
8300
+ if (error instanceof SageSDKError) {
8301
+ return error.retryable;
8302
+ }
8303
+ if (error.name === "AbortError" || error.name === "TimeoutError") {
8304
+ return true;
8305
+ }
8306
+ const message = String(error.message || "").toLowerCase();
8307
+ const retryablePatterns = [
8308
+ "timeout",
8309
+ "network",
8310
+ "econnrefused",
8311
+ "econnreset",
8312
+ "etimedout",
8313
+ "fetch failed"
8314
+ ];
8315
+ return retryablePatterns.some((pattern) => message.includes(pattern));
8316
+ }
8317
+ module2.exports = {
8318
+ SageSDKError,
8319
+ SubgraphError,
8320
+ IPFSError,
8321
+ formatErrorMessage,
8322
+ isRetryable
8323
+ };
8324
+ }
8325
+ });
8326
+
8327
+ // src/services/subgraph/client.js
8328
+ var require_client = __commonJS({
8329
+ "src/services/subgraph/client.js"(exports2, module2) {
8330
+ var subgraph = require_subgraph();
8331
+ var { SimpleCache } = require_cache();
8332
+ var { retryWithBackoff } = require_retry();
8333
+ var { SubgraphError } = require_errors2();
8334
+ var SubgraphService = class {
8335
+ /**
8336
+ * @param {object} config - Service configuration
8337
+ * @param {string} config.url - Subgraph GraphQL endpoint
8338
+ * @param {number} [config.timeout=10000] - Request timeout in ms
8339
+ * @param {number} [config.retries=3] - Number of retry attempts
8340
+ * @param {object} [config.cache] - Cache configuration
8341
+ * @param {boolean} [config.cache.enabled=true] - Enable caching
8342
+ * @param {number} [config.cache.ttl=30000] - Cache TTL in ms
8343
+ * @param {number} [config.cache.maxSize=100] - Max cache entries
8344
+ */
8345
+ constructor(config) {
8346
+ if (!config || !config.url) {
8347
+ throw new Error("SubgraphService requires a url in config");
8348
+ }
8349
+ this.url = config.url;
8350
+ this.timeout = config.timeout || 1e4;
8351
+ this.retries = config.retries || 3;
8352
+ this._cache = new SimpleCache({
8353
+ enabled: config.cache?.enabled !== false,
8354
+ ttl: config.cache?.ttl || 3e4,
8355
+ maxSize: config.cache?.maxSize || 100
8356
+ });
8357
+ }
8358
+ /**
8359
+ * Get list of SubDAOs
8360
+ * @param {object} [options] - Query options
8361
+ * @param {number} [options.limit=50] - Max results
8362
+ * @param {number} [options.skip=0] - Results to skip
8363
+ * @param {boolean} [options.cache=true] - Use cache
8364
+ * @returns {Promise<Array>} - List of SubDAOs
8365
+ */
8366
+ async getSubDAOs(options = {}) {
8367
+ const { limit = 50, skip = 0, cache = true } = options;
8368
+ const cacheKey = `subdaos:${limit}:${skip}`;
8369
+ if (cache) {
8370
+ const cached = this._cache.get(cacheKey);
8371
+ if (cached) return cached;
8372
+ }
8373
+ try {
8374
+ const result = await retryWithBackoff(
8375
+ () => this._querySubDAOs({ limit, skip }),
8376
+ { attempts: this.retries }
8377
+ );
8378
+ if (cache) {
8379
+ this._cache.set(cacheKey, result);
8380
+ }
8381
+ return result;
8382
+ } catch (error) {
8383
+ throw new SubgraphError(
8384
+ `Failed to fetch SubDAOs: ${error.message}`,
8385
+ "QUERY_FAILED",
8386
+ true,
8387
+ error
8388
+ );
8389
+ }
8390
+ }
8391
+ /**
8392
+ * Get list of proposals
8393
+ * @param {object} [options] - Query options
8394
+ * @param {string} [options.governor] - Filter by governor address
8395
+ * @param {string} [options.subdao] - Filter by SubDAO address
8396
+ * @param {string[]} [options.states] - Filter by proposal states
8397
+ * @param {number} [options.fromTimestamp] - Filter by creation time (>=)
8398
+ * @param {number} [options.toTimestamp] - Filter by creation time (<=)
8399
+ * @param {number} [options.limit=20] - Max results
8400
+ * @param {number} [options.skip=0] - Results to skip
8401
+ * @param {boolean} [options.cache=true] - Use cache
8402
+ * @returns {Promise<Array>} - List of proposals
8403
+ */
8404
+ async getProposals(options = {}) {
8405
+ const {
8406
+ governor,
8407
+ subdao,
8408
+ states,
8409
+ fromTimestamp,
8410
+ toTimestamp,
8411
+ limit = 20,
8412
+ skip = 0,
8413
+ cache = true
8414
+ } = options;
8415
+ const cacheKey = `proposals:${JSON.stringify({
8416
+ governor,
8417
+ subdao,
8418
+ states,
8419
+ fromTimestamp,
8420
+ toTimestamp,
8421
+ limit,
8422
+ skip
8423
+ })}`;
8424
+ if (cache) {
8425
+ const cached = this._cache.get(cacheKey);
8426
+ if (cached) return cached;
8427
+ }
8428
+ try {
8429
+ const result = await retryWithBackoff(
8430
+ () => this._queryProposals({
8431
+ governor,
8432
+ subdao,
8433
+ states,
8434
+ fromTimestamp,
8435
+ toTimestamp,
8436
+ limit,
8437
+ skip
8438
+ }),
8439
+ { attempts: this.retries }
8440
+ );
8441
+ if (cache) {
8442
+ this._cache.set(cacheKey, result);
8443
+ }
8444
+ return result;
8445
+ } catch (error) {
8446
+ throw new SubgraphError(
8447
+ `Failed to fetch proposals: ${error.message}`,
8448
+ "QUERY_FAILED",
8449
+ true,
8450
+ error
8451
+ );
8452
+ }
8453
+ }
8454
+ /**
8455
+ * Get proposal by ID
8456
+ * @param {string} id - Proposal ID
8457
+ * @param {object} [options] - Query options
8458
+ * @param {boolean} [options.cache=true] - Use cache
8459
+ * @returns {Promise<object|null>} - Proposal or null
8460
+ */
8461
+ async getProposalById(id2, options = {}) {
8462
+ const { cache = true } = options;
8463
+ const cacheKey = `proposal:${id2}`;
8464
+ if (cache) {
8465
+ const cached = this._cache.get(cacheKey);
8466
+ if (cached !== null) return cached;
8467
+ }
8468
+ try {
8469
+ const result = await retryWithBackoff(
8470
+ () => subgraph.getProposalById({ url: this.url, id: id2 }),
8471
+ { attempts: this.retries }
8472
+ );
8473
+ if (cache) {
8474
+ this._cache.set(cacheKey, result);
8475
+ }
8476
+ return result;
8477
+ } catch (error) {
8478
+ throw new SubgraphError(
8479
+ `Failed to fetch proposal ${id2}: ${error.message}`,
8480
+ "QUERY_FAILED",
8481
+ true,
8482
+ error
8483
+ );
8484
+ }
8485
+ }
8486
+ /**
8487
+ * Get libraries
8488
+ * @param {object} [options] - Query options
8489
+ * @param {string} [options.subdao] - Filter by SubDAO address
8490
+ * @param {number} [options.limit=50] - Max results
8491
+ * @param {number} [options.skip=0] - Results to skip
8492
+ * @param {boolean} [options.cache=true] - Use cache
8493
+ * @returns {Promise<Array>} - List of libraries
8494
+ */
8495
+ async getLibraries(options = {}) {
8496
+ const { subdao, limit = 50, skip = 0, cache = true } = options;
8497
+ const cacheKey = `libraries:${subdao || "all"}:${limit}:${skip}`;
8498
+ if (cache) {
8499
+ const cached = this._cache.get(cacheKey);
8500
+ if (cached) return cached;
8501
+ }
8502
+ try {
8503
+ const result = await retryWithBackoff(
8504
+ () => subgraph.listLibraries({ url: this.url, subdao, first: limit, skip }),
8505
+ { attempts: this.retries }
8506
+ );
8507
+ if (cache) {
8508
+ this._cache.set(cacheKey, result);
8509
+ }
8510
+ return result;
8511
+ } catch (error) {
8512
+ throw new SubgraphError(
8513
+ `Failed to fetch libraries: ${error.message}`,
8514
+ "QUERY_FAILED",
8515
+ true,
8516
+ error
8517
+ );
8518
+ }
8519
+ }
8520
+ /**
8521
+ * Get prompts by tag
8522
+ * @param {object} options - Query options
8523
+ * @param {string} options.tagsHash - Tag hash to filter by
8524
+ * @param {string} [options.registry] - Filter by registry address
8525
+ * @param {number} [options.limit=50] - Max results
8526
+ * @param {number} [options.skip=0] - Results to skip
8527
+ * @param {boolean} [options.cache=true] - Use cache
8528
+ * @returns {Promise<Array>} - List of prompts
8529
+ */
8530
+ async getPromptsByTag(options) {
8531
+ if (!options || !options.tagsHash) {
8532
+ throw new Error("tagsHash is required");
8533
+ }
8534
+ const { tagsHash, registry, limit = 50, skip = 0, cache = true } = options;
8535
+ const cacheKey = `prompts-tag:${tagsHash}:${registry || "all"}:${limit}:${skip}`;
8536
+ if (cache) {
8537
+ const cached = this._cache.get(cacheKey);
8538
+ if (cached) return cached;
8539
+ }
8540
+ try {
8541
+ const result = await retryWithBackoff(
8542
+ () => subgraph.listPromptsByTag({
8543
+ url: this.url,
8544
+ tagsHash,
8545
+ registry,
8546
+ first: limit,
8547
+ skip
8548
+ }),
8549
+ { attempts: this.retries }
8550
+ );
8551
+ if (cache) {
8552
+ this._cache.set(cacheKey, result);
8553
+ }
8554
+ return result;
8555
+ } catch (error) {
8556
+ throw new SubgraphError(
8557
+ `Failed to fetch prompts by tag: ${error.message}`,
8558
+ "QUERY_FAILED",
8559
+ true,
8560
+ error
8561
+ );
8562
+ }
8563
+ }
8564
+ /**
8565
+ * Get registry prompts
8566
+ * @param {object} options - Query options
8567
+ * @param {string} options.registry - Registry address
8568
+ * @param {number} [options.limit=50] - Max results
8569
+ * @param {number} [options.skip=0] - Results to skip
8570
+ * @param {boolean} [options.cache=true] - Use cache
8571
+ * @returns {Promise<Array>} - List of prompts
8572
+ */
8573
+ async getRegistryPrompts(options) {
8574
+ if (!options || !options.registry) {
8575
+ throw new Error("registry is required");
8576
+ }
8577
+ const { registry, limit = 50, skip = 0, cache = true } = options;
8578
+ const cacheKey = `registry-prompts:${registry}:${limit}:${skip}`;
8579
+ if (cache) {
8580
+ const cached = this._cache.get(cacheKey);
8581
+ if (cached) return cached;
8582
+ }
8583
+ try {
8584
+ const result = await retryWithBackoff(
8585
+ () => subgraph.listRegistryPrompts({
8586
+ url: this.url,
8587
+ registry,
8588
+ first: limit,
8589
+ skip
8590
+ }),
8591
+ { attempts: this.retries }
8592
+ );
8593
+ if (cache) {
8594
+ this._cache.set(cacheKey, result);
8595
+ }
8596
+ return result;
8597
+ } catch (error) {
8598
+ throw new SubgraphError(
8599
+ `Failed to fetch registry prompts: ${error.message}`,
8600
+ "QUERY_FAILED",
8601
+ true,
8602
+ error
8603
+ );
8604
+ }
8605
+ }
8606
+ /**
8607
+ * Clear cache
8608
+ */
8609
+ clearCache() {
8610
+ this._cache.clear();
8611
+ }
8612
+ /**
8613
+ * Get cache statistics
8614
+ * @returns {object} - Cache stats
8615
+ */
8616
+ getCacheStats() {
8617
+ return this._cache.stats();
8618
+ }
8619
+ // Private methods
8620
+ async _querySubDAOs({ limit, skip }) {
8621
+ const data = await subgraph.query(
8622
+ this.url,
8623
+ `
8624
+ query($first: Int!, $skip: Int!) {
8625
+ subDAOs(first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
8626
+ id
8627
+ address
8628
+ name
8629
+ description
8630
+ governor
8631
+ registry
8632
+ token
8633
+ createdAt
8634
+ }
8635
+ }
8636
+ `,
8637
+ { first: limit, skip }
8638
+ );
8639
+ return data?.subDAOs || [];
8640
+ }
8641
+ async _queryProposals({ governor, subdao, states, fromTimestamp, toTimestamp, limit, skip }) {
8642
+ return await subgraph.listProposalsFiltered({
8643
+ url: this.url,
8644
+ governor,
8645
+ states,
8646
+ fromTimestamp,
8647
+ toTimestamp,
8648
+ first: limit,
8649
+ skip
8650
+ });
8651
+ }
8652
+ };
8653
+ module2.exports = { SubgraphService };
8654
+ }
8655
+ });
8656
+
8657
+ // src/services/ipfs/client.js
8658
+ var require_client2 = __commonJS({
8659
+ "src/services/ipfs/client.js"(exports2, module2) {
8660
+ var ipfs = require_ipfs();
8661
+ var { SimpleCache } = require_cache();
8662
+ var { retryWithBackoff } = require_retry();
8663
+ var { IPFSError } = require_errors2();
8664
+ var IPFSService = class {
8665
+ /**
8666
+ * @param {object} config - Service configuration
8667
+ * @param {string} config.workerBaseUrl - IPFS worker base URL
8668
+ * @param {string} config.gateway - Primary IPFS gateway URL
8669
+ * @param {object} [config.signer] - ethers v6 signer for worker auth
8670
+ * @param {Function} [config.getAuth] - Function to get auth credentials
8671
+ * @param {string} [config.workerToken] - Bearer token for worker auth
8672
+ * @param {number} [config.timeout=15000] - Request timeout in ms
8673
+ * @param {number} [config.retries=2] - Number of retry attempts
8674
+ * @param {object} [config.cache] - Cache configuration
8675
+ * @param {boolean} [config.cache.enabled=true] - Enable caching
8676
+ * @param {number} [config.cache.ttl=300000] - Cache TTL in ms (default 5min for immutable CIDs)
8677
+ * @param {number} [config.cache.maxSize=50] - Max cache entries
8678
+ */
8679
+ constructor(config) {
8680
+ if (!config) {
8681
+ throw new Error("IPFSService requires a config object");
8682
+ }
8683
+ this.workerBaseUrl = config.workerBaseUrl;
8684
+ this.gateway = config.gateway;
8685
+ this.timeout = config.timeout || 15e3;
8686
+ this.retries = config.retries || 2;
8687
+ this._client = ipfs.createClient({
8688
+ workerBaseUrl: config.workerBaseUrl,
8689
+ gateway: config.gateway,
8690
+ workerSigner: config.signer,
8691
+ workerGetAuth: config.getAuth,
8692
+ workerToken: config.workerToken,
8693
+ timeoutMs: this.timeout,
8694
+ retries: this.retries
8695
+ });
8696
+ this._cache = new SimpleCache({
8697
+ enabled: config.cache?.enabled !== false,
8698
+ ttl: config.cache?.ttl || 3e5,
8699
+ // 5 minutes
8700
+ maxSize: config.cache?.maxSize || 50
8701
+ });
8702
+ }
8703
+ /**
8704
+ * Upload content to IPFS worker
8705
+ * @param {any} content - Content to upload (will be JSON stringified)
8706
+ * @param {object} [options] - Upload options
8707
+ * @param {string} [options.name] - Content name
8708
+ * @param {boolean} [options.warm=false] - Warm gateways after upload
8709
+ * @returns {Promise<string>} - CID of uploaded content
8710
+ */
8711
+ async upload(content, options = {}) {
8712
+ const { name = "upload", warm = false } = options;
8713
+ try {
8714
+ const result = await retryWithBackoff(
8715
+ () => this._client.uploadJson(content, name, {
8716
+ provider: "worker",
8717
+ warm
8718
+ }),
8719
+ { attempts: this.retries }
8720
+ );
8721
+ return result.cid;
8722
+ } catch (error) {
8723
+ throw new IPFSError(
8724
+ `Failed to upload content: ${error.message}`,
8725
+ "UPLOAD_FAILED",
8726
+ true,
8727
+ error
8728
+ );
8729
+ }
8730
+ }
8731
+ /**
8732
+ * Fetch JSON content by CID with parallel gateway fetching
8733
+ * @param {string} cid - IPFS CID
8734
+ * @param {object} [options] - Fetch options
8735
+ * @param {boolean} [options.cache=true] - Use cache
8736
+ * @param {number} [options.timeout=5000] - Timeout per gateway in ms
8737
+ * @param {string[]} [options.extraGateways] - Additional gateways to try
8738
+ * @returns {Promise<any|null>} - Parsed JSON content or null
8739
+ */
8740
+ async fetchByCID(cid, options = {}) {
8741
+ if (!cid) {
8742
+ throw new IPFSError("CID is required", "INVALID_CID", false);
8743
+ }
8744
+ const { cache = true, timeout = 5e3, extraGateways = [] } = options;
8745
+ if (cache) {
8746
+ const cached = this._cache.get(cid);
8747
+ if (cached) return cached;
8748
+ }
8749
+ const urls = this._client.buildGatewayUrls(cid, extraGateways);
8750
+ try {
8751
+ const result = await this._parallelFetch(urls, timeout);
8752
+ if (result === null) {
8753
+ throw new IPFSError(
8754
+ `All gateways failed for CID ${cid}`,
8755
+ "GATEWAY_FAILED",
8756
+ true
8757
+ );
8758
+ }
8759
+ if (cache) {
8760
+ this._cache.set(cid, result);
8761
+ }
8762
+ return result;
8763
+ } catch (error) {
8764
+ if (error instanceof IPFSError) throw error;
8765
+ throw new IPFSError(
8766
+ `Failed to fetch CID ${cid}: ${error.message}`,
8767
+ "GATEWAY_FAILED",
8768
+ true,
8769
+ error
8770
+ );
8771
+ }
8772
+ }
8773
+ /**
8774
+ * Pin CIDs to IPFS worker
8775
+ * @param {string|string[]} cids - CID or array of CIDs to pin
8776
+ * @param {object} [options] - Pin options
8777
+ * @param {boolean} [options.warm=false] - Warm gateways after pinning
8778
+ * @returns {Promise<void>}
8779
+ */
8780
+ async pin(cids, options = {}) {
8781
+ const { warm = false } = options;
8782
+ const cidList = Array.isArray(cids) ? cids : [cids];
8783
+ if (cidList.length === 0) {
8784
+ throw new IPFSError("At least one CID is required", "INVALID_CID", false);
8785
+ }
8786
+ try {
8787
+ await retryWithBackoff(
8788
+ () => this._client.pin({ cids: cidList, warm }),
8789
+ { attempts: this.retries }
8790
+ );
8791
+ } catch (error) {
8792
+ throw new IPFSError(
8793
+ `Failed to pin CIDs: ${error.message}`,
8794
+ "PIN_FAILED",
8795
+ true,
8796
+ error
8797
+ );
8798
+ }
8799
+ }
8800
+ /**
8801
+ * Warm gateways for a CID (prefetch)
8802
+ * @param {string} cid - CID to warm
8803
+ * @param {object} [options] - Warm options
8804
+ * @param {string[]} [options.gateways] - Specific gateways to warm
8805
+ * @returns {Promise<object>} - Warm result
8806
+ */
8807
+ async warm(cid, options = {}) {
8808
+ const { gateways } = options;
8809
+ try {
8810
+ return await this._client.warmGateways(cid, { gateways });
8811
+ } catch (error) {
8812
+ return { warmed: [] };
8813
+ }
8814
+ }
8815
+ /**
8816
+ * Clear cache
8817
+ */
8818
+ clearCache() {
8819
+ this._cache.clear();
8820
+ }
8821
+ /**
8822
+ * Get cache statistics
8823
+ * @returns {object} - Cache stats
8824
+ */
8825
+ getCacheStats() {
8826
+ return this._cache.stats();
8827
+ }
8828
+ // Private methods
8829
+ /**
8830
+ * Fetch from multiple gateways in parallel
8831
+ * @param {string[]} urls - Gateway URLs to try
8832
+ * @param {number} timeout - Timeout per gateway in ms
8833
+ * @returns {Promise<any|null>} - First successful result or null
8834
+ * @private
8835
+ */
8836
+ async _parallelFetch(urls, timeout) {
8837
+ if (!urls || urls.length === 0) {
8838
+ return null;
8839
+ }
8840
+ const fetchOne = async (url) => {
8841
+ const controller = new AbortController();
8842
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
8843
+ try {
8844
+ const response = await fetch(url, {
8845
+ signal: controller.signal,
8846
+ headers: { Accept: "application/json" }
8847
+ });
8848
+ clearTimeout(timeoutId);
8849
+ if (!response.ok) {
8850
+ return null;
8851
+ }
8852
+ const text = await response.text();
8853
+ try {
8854
+ return JSON.parse(text);
8855
+ } catch {
8856
+ return text;
8857
+ }
8858
+ } catch (error) {
8859
+ clearTimeout(timeoutId);
8860
+ return null;
8861
+ }
8862
+ };
8863
+ const results = await Promise.allSettled(urls.map((url) => fetchOne(url)));
8864
+ for (const result of results) {
8865
+ if (result.status === "fulfilled" && result.value !== null) {
8866
+ return result.value;
8867
+ }
8868
+ }
8869
+ return null;
8870
+ }
8871
+ };
8872
+ module2.exports = { IPFSService };
8873
+ }
8874
+ });
8875
+
8876
+ // src/hooks/useSubDAOs.js
8877
+ var require_useSubDAOs = __commonJS({
8878
+ "src/hooks/useSubDAOs.js"(exports2, module2) {
8879
+ var useSWR = require("swr");
8880
+ function useSubDAOs(subgraphService, options = {}) {
8881
+ const {
8882
+ limit = 50,
8883
+ skip = 0,
8884
+ cache = true,
8885
+ refreshInterval,
8886
+ revalidateOnFocus = true,
8887
+ revalidateOnReconnect = true
8888
+ } = options;
8889
+ const cacheKey = subgraphService ? ["subdaos", subgraphService.url, limit, skip] : null;
8890
+ const fetcher = async () => {
8891
+ if (!subgraphService) {
8892
+ throw new Error("SubgraphService is required");
8893
+ }
8894
+ return await subgraphService.getSubDAOs({ limit, skip, cache });
8895
+ };
8896
+ return useSWR(cacheKey, fetcher, {
8897
+ refreshInterval,
8898
+ revalidateOnFocus,
8899
+ revalidateOnReconnect,
8900
+ dedupingInterval: cache ? 3e4 : 0
8901
+ // Match service cache TTL
8902
+ });
8903
+ }
8904
+ module2.exports = { useSubDAOs };
8905
+ }
8906
+ });
8907
+
8908
+ // src/hooks/useProposals.js
8909
+ var require_useProposals = __commonJS({
8910
+ "src/hooks/useProposals.js"(exports2, module2) {
8911
+ var useSWR = require("swr");
8912
+ function useProposals(subgraphService, options = {}) {
8913
+ const {
8914
+ governor,
8915
+ subdao,
8916
+ states,
8917
+ fromTimestamp,
8918
+ toTimestamp,
8919
+ limit = 20,
8920
+ skip = 0,
8921
+ cache = true,
8922
+ refreshInterval,
8923
+ revalidateOnFocus = true,
8924
+ revalidateOnReconnect = true
8925
+ } = options;
8926
+ const cacheKey = subgraphService ? [
8927
+ "proposals",
8928
+ subgraphService.url,
8929
+ governor,
8930
+ subdao,
8931
+ states?.join(","),
8932
+ fromTimestamp,
8933
+ toTimestamp,
8934
+ limit,
8935
+ skip
8936
+ ] : null;
8937
+ const fetcher = async () => {
8938
+ if (!subgraphService) {
8939
+ throw new Error("SubgraphService is required");
8940
+ }
8941
+ return await subgraphService.getProposals({
8942
+ governor,
8943
+ subdao,
8944
+ states,
8945
+ fromTimestamp,
8946
+ toTimestamp,
8947
+ limit,
8948
+ skip,
8949
+ cache
8950
+ });
8951
+ };
8952
+ return useSWR(cacheKey, fetcher, {
8953
+ refreshInterval,
8954
+ revalidateOnFocus,
8955
+ revalidateOnReconnect,
8956
+ dedupingInterval: cache ? 3e4 : 0
8957
+ // Match service cache TTL
8958
+ });
8959
+ }
8960
+ module2.exports = { useProposals };
8961
+ }
8962
+ });
8963
+
8964
+ // src/hooks/useFetchCID.js
8965
+ var require_useFetchCID = __commonJS({
8966
+ "src/hooks/useFetchCID.js"(exports2, module2) {
8967
+ var useSWR = require("swr");
8968
+ function useFetchCID(ipfsService, cid, options = {}) {
8969
+ const {
8970
+ cache = true,
8971
+ timeout = 5e3,
8972
+ extraGateways = [],
8973
+ refreshInterval,
8974
+ revalidateOnFocus = false,
8975
+ // CIDs are immutable
8976
+ revalidateOnReconnect = false
8977
+ // CIDs are immutable
8978
+ } = options;
8979
+ const cacheKey = ipfsService && cid ? ["ipfs-cid", cid] : null;
8980
+ const fetcher = async () => {
8981
+ if (!ipfsService) {
8982
+ throw new Error("IPFSService is required");
8983
+ }
8984
+ if (!cid) {
8985
+ throw new Error("CID is required");
8986
+ }
8987
+ return await ipfsService.fetchByCID(cid, {
8988
+ cache,
8989
+ timeout,
8990
+ extraGateways
8991
+ });
8992
+ };
8993
+ return useSWR(cacheKey, fetcher, {
8994
+ refreshInterval,
8995
+ revalidateOnFocus,
8996
+ revalidateOnReconnect,
8997
+ dedupingInterval: cache ? 3e5 : 0,
8998
+ // Match service cache TTL (5min)
8999
+ // CIDs are immutable, so we can cache errors too
9000
+ shouldRetryOnError: true,
9001
+ errorRetryInterval: 5e3,
9002
+ errorRetryCount: 3
9003
+ });
9004
+ }
9005
+ module2.exports = { useFetchCID };
9006
+ }
9007
+ });
9008
+
9009
+ // src/hooks/useUpload.js
9010
+ var require_useUpload = __commonJS({
9011
+ "src/hooks/useUpload.js"(exports2, module2) {
9012
+ var { useState, useCallback } = require("react");
9013
+ function useUpload(ipfsService) {
9014
+ const [isUploading, setIsUploading] = useState(false);
9015
+ const [error, setError] = useState(null);
9016
+ const [data, setData] = useState(null);
9017
+ const upload = useCallback(
9018
+ async (content, options = {}) => {
9019
+ if (!ipfsService) {
9020
+ const err = new Error("IPFSService is required");
9021
+ setError(err);
9022
+ throw err;
9023
+ }
9024
+ setIsUploading(true);
9025
+ setError(null);
9026
+ try {
9027
+ const cid = await ipfsService.upload(content, options);
9028
+ setData(cid);
9029
+ return cid;
9030
+ } catch (err) {
9031
+ setError(err);
9032
+ throw err;
9033
+ } finally {
9034
+ setIsUploading(false);
9035
+ }
9036
+ },
9037
+ [ipfsService]
9038
+ );
9039
+ const reset = useCallback(() => {
9040
+ setIsUploading(false);
9041
+ setError(null);
9042
+ setData(null);
9043
+ }, []);
9044
+ return {
9045
+ upload,
9046
+ isUploading,
9047
+ error,
9048
+ data,
9049
+ reset
9050
+ };
9051
+ }
9052
+ module2.exports = { useUpload };
9053
+ }
9054
+ });
9055
+
9056
+ // src/hooks/index.js
9057
+ var require_hooks = __commonJS({
9058
+ "src/hooks/index.js"(exports2, module2) {
9059
+ var { useSubDAOs } = require_useSubDAOs();
9060
+ var { useProposals } = require_useProposals();
9061
+ var { useFetchCID } = require_useFetchCID();
9062
+ var { useUpload } = require_useUpload();
9063
+ module2.exports = {
9064
+ useSubDAOs,
9065
+ useProposals,
9066
+ useFetchCID,
9067
+ useUpload
9068
+ };
9069
+ }
9070
+ });
9071
+
8137
9072
  // src/index.js
8138
9073
  var require_src = __commonJS({
8139
9074
  "src/index.js"(exports2, module2) {
@@ -8143,6 +9078,7 @@ var require_src = __commonJS({
8143
9078
  governance.intents = require_intents();
8144
9079
  var governanceTemplates = require_templates();
8145
9080
  governance.operations = require_operations();
9081
+ governance.grants = require_grants();
8146
9082
  var subdao = require_subdao();
8147
9083
  var timelock = require_timelock();
8148
9084
  var factory = require_factory();
@@ -8159,7 +9095,6 @@ var require_src = __commonJS({
8159
9095
  var treasury = require_treasury();
8160
9096
  var boost = require_boost();
8161
9097
  var bounty = require_bounty();
8162
- var bond = require_bond();
8163
9098
  var wallet = require_wallet();
8164
9099
  wallet.session = require_session();
8165
9100
  var walletCastManager = require_cast_manager();
@@ -8174,6 +9109,16 @@ var require_src = __commonJS({
8174
9109
  openzeppelin: require_openzeppelin()
8175
9110
  }
8176
9111
  };
9112
+ var { SubgraphService } = require_client();
9113
+ var { IPFSService } = require_client2();
9114
+ var serviceErrors = require_errors2();
9115
+ var { SimpleCache } = require_cache();
9116
+ var { retryWithBackoff } = require_retry();
9117
+ var hooks = null;
9118
+ try {
9119
+ hooks = require_hooks();
9120
+ } catch (err) {
9121
+ }
8177
9122
  module2.exports = {
8178
9123
  version: pkg.version,
8179
9124
  getProvider: utils.getProvider,
@@ -8192,7 +9137,7 @@ var require_src = __commonJS({
8192
9137
  personal,
8193
9138
  treasury,
8194
9139
  boost,
8195
- bond,
9140
+ // bond module removed; bonds deprecated in CLI/SDK
8196
9141
  subgraph,
8197
9142
  utils: { ...utils, privateTx, safe },
8198
9143
  bounty,
@@ -8204,6 +9149,18 @@ var require_src = __commonJS({
8204
9149
  errors,
8205
9150
  doppler,
8206
9151
  adapters,
9152
+ // New service layer exports
9153
+ services: {
9154
+ SubgraphService,
9155
+ IPFSService
9156
+ },
9157
+ serviceErrors,
9158
+ serviceUtils: {
9159
+ SimpleCache,
9160
+ retryWithBackoff
9161
+ },
9162
+ // React hooks (optional - requires react + swr peer dependencies)
9163
+ hooks,
8207
9164
  // Legacy exports (deprecated): maintain compatibility while consumers migrate
8208
9165
  resolveGovernanceContext: async function legacyResolveGovernanceContext(args) {
8209
9166
  console.warn("[@sage-protocol/sdk] resolveGovernanceContext is deprecated. Use governance helpers instead.");