@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.
- package/README.md +288 -0
- package/dist/browser/index.mjs +155 -10
- package/dist/index.cjs +1351 -394
- package/dist/index.mjs +1444 -487
- package/dist/node/index.cjs +1351 -394
- package/dist/node/index.mjs +1446 -489
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -68,9 +68,297 @@ Module Overview
|
|
|
68
68
|
| `treasury` | Reserve/POL snapshot, pending withdrawals, liquidity plans | Treasury analytics, Safe operators |
|
|
69
69
|
| `boost` | Merkle/Direct boost readers + builders | Incentive distribution tooling |
|
|
70
70
|
| `subgraph` | GraphQL helpers for proposals/libraries | Historical analytics |
|
|
71
|
+
| `services` | High-level SubgraphService + IPFSService with retry/caching | Web app, CLI integration |
|
|
71
72
|
| `adapters` | Normalized governance adapters (OZ) | UI layers needing a unified model |
|
|
72
73
|
| `errors` | `SageSDKError` codes | Consistent downstream error handling |
|
|
73
74
|
|
|
75
|
+
### Service Layer
|
|
76
|
+
|
|
77
|
+
The SDK provides high-level service classes with built-in retry logic, caching, and error handling for production applications.
|
|
78
|
+
|
|
79
|
+
**SubgraphService** - GraphQL queries with automatic retry and caching
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
import { services, serviceErrors } from '@sage-protocol/sdk';
|
|
83
|
+
|
|
84
|
+
// Initialize service
|
|
85
|
+
const subgraphService = new services.SubgraphService({
|
|
86
|
+
url: 'https://api.studio.thegraph.com/query/your-subgraph',
|
|
87
|
+
timeout: 10000, // 10s timeout (default)
|
|
88
|
+
retries: 3, // 3 retry attempts with exponential backoff (default)
|
|
89
|
+
cache: {
|
|
90
|
+
enabled: true, // Enable caching (default: true)
|
|
91
|
+
ttl: 30000, // 30s cache TTL (default)
|
|
92
|
+
maxSize: 100, // Max 100 cache entries (default)
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Fetch SubDAOs with caching
|
|
97
|
+
const subdaos = await subgraphService.getSubDAOs({ limit: 50, skip: 0 });
|
|
98
|
+
|
|
99
|
+
// Fetch proposals with filters
|
|
100
|
+
const proposals = await subgraphService.getProposals({
|
|
101
|
+
governor: '0xGovernor',
|
|
102
|
+
states: ['ACTIVE', 'PENDING'],
|
|
103
|
+
fromTimestamp: 1640000000,
|
|
104
|
+
limit: 20,
|
|
105
|
+
cache: true, // Use cache (default: true)
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Get single proposal by ID
|
|
109
|
+
const proposal = await subgraphService.getProposalById('0x123...', { cache: true });
|
|
110
|
+
|
|
111
|
+
// Fetch libraries
|
|
112
|
+
const libraries = await subgraphService.getLibraries({
|
|
113
|
+
subdao: '0xSubDAO',
|
|
114
|
+
limit: 50,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Fetch prompts by tag
|
|
118
|
+
const prompts = await subgraphService.getPromptsByTag({
|
|
119
|
+
tagsHash: '0xabc123...',
|
|
120
|
+
registry: '0xRegistry',
|
|
121
|
+
limit: 50,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Cache management
|
|
125
|
+
subgraphService.clearCache();
|
|
126
|
+
const stats = subgraphService.getCacheStats();
|
|
127
|
+
// → { enabled: true, size: 12, maxSize: 100 }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**IPFSService** - Parallel gateway fetching with caching
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
import { services, serviceErrors } from '@sage-protocol/sdk';
|
|
134
|
+
import { ethers } from 'ethers';
|
|
135
|
+
|
|
136
|
+
// Initialize service
|
|
137
|
+
const ipfsService = new services.IPFSService({
|
|
138
|
+
workerBaseUrl: 'https://api.sageprotocol.io',
|
|
139
|
+
gateway: 'https://ipfs.io',
|
|
140
|
+
signer: ethers.Wallet.fromPhrase('...'), // Optional: for worker auth
|
|
141
|
+
timeout: 15000, // 15s timeout (default)
|
|
142
|
+
retries: 2, // 2 retry attempts (default)
|
|
143
|
+
cache: {
|
|
144
|
+
enabled: true, // Enable caching (default: true)
|
|
145
|
+
ttl: 300000, // 5min cache TTL for immutable CIDs (default)
|
|
146
|
+
maxSize: 50, // Max 50 cache entries (default)
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Fetch content by CID (parallel gateway race)
|
|
151
|
+
// Tries multiple gateways in parallel, returns first success
|
|
152
|
+
const content = await ipfsService.fetchByCID('QmTest123...', {
|
|
153
|
+
cache: true,
|
|
154
|
+
timeout: 5000, // 5s timeout per gateway
|
|
155
|
+
extraGateways: ['https://cloudflare-ipfs.com', 'https://gateway.pinata.cloud'],
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Upload content to IPFS worker
|
|
159
|
+
const cid = await ipfsService.upload(
|
|
160
|
+
{ title: 'My Prompt', content: '...' },
|
|
161
|
+
{ name: 'prompt.json', warm: true }
|
|
162
|
+
);
|
|
163
|
+
// → 'QmNewContent123...'
|
|
164
|
+
|
|
165
|
+
// Pin CIDs to worker
|
|
166
|
+
await ipfsService.pin(['QmTest1...', 'QmTest2...'], { warm: false });
|
|
167
|
+
|
|
168
|
+
// Warm gateways (prefetch)
|
|
169
|
+
await ipfsService.warm('QmTest123...', {
|
|
170
|
+
gateways: ['https://ipfs.io', 'https://cloudflare-ipfs.com'],
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Cache management
|
|
174
|
+
ipfsService.clearCache();
|
|
175
|
+
const stats = ipfsService.getCacheStats();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Error Handling**
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
import { services, serviceErrors } from '@sage-protocol/sdk';
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const subdaos = await subgraphService.getSubDAOs({ limit: 50 });
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (error instanceof serviceErrors.SubgraphError) {
|
|
187
|
+
console.error(`Subgraph error [${error.code}]:`, error.message);
|
|
188
|
+
console.log('Retryable:', error.retryable);
|
|
189
|
+
// Codes: TIMEOUT, NETWORK, INVALID_RESPONSE, NOT_FOUND, QUERY_FAILED
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const content = await ipfsService.fetchByCID('QmTest...');
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (error instanceof serviceErrors.IPFSError) {
|
|
197
|
+
console.error(`IPFS error [${error.code}]:`, error.message);
|
|
198
|
+
// Codes: TIMEOUT, PIN_FAILED, INVALID_CID, NOT_FOUND, GATEWAY_FAILED, UPLOAD_FAILED
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Format user-friendly error messages
|
|
203
|
+
const friendlyMsg = serviceErrors.formatErrorMessage(error);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Utility Exports**
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
import { serviceUtils } from '@sage-protocol/sdk';
|
|
210
|
+
|
|
211
|
+
// Simple in-memory cache with TTL
|
|
212
|
+
const cache = new serviceUtils.SimpleCache({
|
|
213
|
+
enabled: true,
|
|
214
|
+
ttl: 60000, // 1 minute
|
|
215
|
+
maxSize: 200,
|
|
216
|
+
});
|
|
217
|
+
cache.set('key', { data: 'value' });
|
|
218
|
+
const val = cache.get('key');
|
|
219
|
+
|
|
220
|
+
// Exponential backoff retry
|
|
221
|
+
const result = await serviceUtils.retryWithBackoff(
|
|
222
|
+
async () => {
|
|
223
|
+
// Your async operation
|
|
224
|
+
return await fetchData();
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
attempts: 3,
|
|
228
|
+
baseDelay: 1000, // Start at 1s
|
|
229
|
+
maxDelay: 10000, // Cap at 10s
|
|
230
|
+
onRetry: ({ attempt, totalAttempts, delay, error }) => {
|
|
231
|
+
console.log(`Retry ${attempt}/${totalAttempts} after ${delay}ms:`, error.message);
|
|
232
|
+
},
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Performance Notes**
|
|
238
|
+
|
|
239
|
+
- **Parallel Gateway Fetching**: IPFSService fetches from multiple IPFS gateways simultaneously (Promise.race pattern), reducing typical fetch times from 7-28s to 1-2s (10-14x improvement)
|
|
240
|
+
- **In-Memory Caching**: Both services cache results in memory with configurable TTL to reduce redundant network calls
|
|
241
|
+
- **Exponential Backoff**: Automatic retry with exponential backoff (1s → 2s → 4s delays) for transient failures
|
|
242
|
+
- **Edge Runtime Compatible**: Uses `fetch()` instead of axios, compatible with Cloudflare Pages and Vercel Edge
|
|
243
|
+
|
|
244
|
+
**React Integration**
|
|
245
|
+
|
|
246
|
+
The SDK provides React hooks built on SWR for seamless integration in React applications:
|
|
247
|
+
|
|
248
|
+
**Prerequisites**:
|
|
249
|
+
```bash
|
|
250
|
+
# Install peer dependencies
|
|
251
|
+
npm install react swr
|
|
252
|
+
# or
|
|
253
|
+
yarn add react swr
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Usage**:
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
import { services, hooks } from '@sage-protocol/sdk';
|
|
260
|
+
|
|
261
|
+
// Initialize services (once, typically in a context provider)
|
|
262
|
+
const subgraphService = new services.SubgraphService({
|
|
263
|
+
url: 'https://api.studio.thegraph.com/query/your-subgraph',
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const ipfsService = new services.IPFSService({
|
|
267
|
+
workerBaseUrl: 'https://api.sageprotocol.io',
|
|
268
|
+
gateway: 'https://ipfs.io',
|
|
269
|
+
signer: yourSigner, // Optional for uploads
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Use hooks in components
|
|
273
|
+
function SubDAOList() {
|
|
274
|
+
const { data: subdaos, error, isLoading } = hooks.useSubDAOs(subgraphService, {
|
|
275
|
+
limit: 50,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
if (isLoading) return <div>Loading...</div>;
|
|
279
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<ul>
|
|
283
|
+
{subdaos.map(subdao => (
|
|
284
|
+
<li key={subdao.id}>{subdao.name}</li>
|
|
285
|
+
))}
|
|
286
|
+
</ul>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function ProposalList({ governorAddress }) {
|
|
291
|
+
const { data: proposals } = hooks.useProposals(subgraphService, {
|
|
292
|
+
governor: governorAddress,
|
|
293
|
+
states: ['ACTIVE', 'PENDING'],
|
|
294
|
+
limit: 20,
|
|
295
|
+
refreshInterval: 30000, // Auto-refresh every 30s
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<ul>
|
|
300
|
+
{proposals?.map(proposal => (
|
|
301
|
+
<li key={proposal.id}>{proposal.description}</li>
|
|
302
|
+
))}
|
|
303
|
+
</ul>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function PromptViewer({ cid }) {
|
|
308
|
+
const { data: content, isLoading } = hooks.useFetchCID(ipfsService, cid, {
|
|
309
|
+
extraGateways: ['https://cloudflare-ipfs.com'],
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (isLoading) return <div>Loading prompt...</div>;
|
|
313
|
+
|
|
314
|
+
return <pre>{JSON.stringify(content, null, 2)}</pre>;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function UploadForm() {
|
|
318
|
+
const { upload, isUploading, error, data: cid } = hooks.useUpload(ipfsService);
|
|
319
|
+
|
|
320
|
+
const handleSubmit = async (e) => {
|
|
321
|
+
e.preventDefault();
|
|
322
|
+
const content = { title: 'My Prompt', content: '...' };
|
|
323
|
+
try {
|
|
324
|
+
const newCid = await upload(content, { name: 'prompt.json' });
|
|
325
|
+
console.log('Uploaded:', newCid);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
console.error('Upload failed:', err);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<form onSubmit={handleSubmit}>
|
|
333
|
+
<button type="submit" disabled={isUploading}>
|
|
334
|
+
{isUploading ? 'Uploading...' : 'Upload'}
|
|
335
|
+
</button>
|
|
336
|
+
{error && <div>Error: {error.message}</div>}
|
|
337
|
+
{cid && <div>Uploaded to: {cid}</div>}
|
|
338
|
+
</form>
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Available Hooks**:
|
|
344
|
+
|
|
345
|
+
| Hook | Description | Returns |
|
|
346
|
+
|------|-------------|---------|
|
|
347
|
+
| `useSubDAOs(service, options)` | Fetch SubDAOs from subgraph | `{ data, error, isLoading, mutate }` |
|
|
348
|
+
| `useProposals(service, options)` | Fetch proposals from subgraph | `{ data, error, isLoading, mutate }` |
|
|
349
|
+
| `useFetchCID(service, cid, options)` | Fetch IPFS content by CID | `{ data, error, isLoading, mutate }` |
|
|
350
|
+
| `useUpload(service)` | Upload content to IPFS | `{ upload, isUploading, error, data, reset }` |
|
|
351
|
+
|
|
352
|
+
**Hook Options**:
|
|
353
|
+
|
|
354
|
+
All data-fetching hooks support SWR options:
|
|
355
|
+
- `refreshInterval` - Auto-refresh interval in ms
|
|
356
|
+
- `revalidateOnFocus` - Revalidate when window focused
|
|
357
|
+
- `revalidateOnReconnect` - Revalidate on network reconnect
|
|
358
|
+
- `cache` - Use service-level caching
|
|
359
|
+
|
|
360
|
+
**Note**: Hooks are optional and only available when `react` and `swr` peer dependencies are installed.
|
|
361
|
+
|
|
74
362
|
### Examples
|
|
75
363
|
|
|
76
364
|
- [Legacy base mini app hook](examples/base-mini-app/README.md) – React-friendly context mirroring the former in-repo app (now maintained externally).
|
package/dist/browser/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ var require_package = __commonJS({
|
|
|
20
20
|
"package.json"(exports, module) {
|
|
21
21
|
module.exports = {
|
|
22
22
|
name: "@sage-protocol/sdk",
|
|
23
|
-
version: "0.1.
|
|
23
|
+
version: "0.1.4",
|
|
24
24
|
description: "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
|
|
25
25
|
main: "dist/index.cjs",
|
|
26
26
|
module: "dist/index.mjs",
|
|
@@ -56,10 +56,10 @@ var require_package = __commonJS({
|
|
|
56
56
|
],
|
|
57
57
|
sideEffects: false,
|
|
58
58
|
browser: {
|
|
59
|
+
child_process: false,
|
|
59
60
|
fs: false,
|
|
60
|
-
path: false,
|
|
61
61
|
os: false,
|
|
62
|
-
|
|
62
|
+
path: false
|
|
63
63
|
},
|
|
64
64
|
repository: {
|
|
65
65
|
type: "git",
|
|
@@ -88,6 +88,18 @@ var require_package = __commonJS({
|
|
|
88
88
|
sinon: "^17.0.1",
|
|
89
89
|
tsup: "^8.1.0",
|
|
90
90
|
typescript: "^5.4.0"
|
|
91
|
+
},
|
|
92
|
+
peerDependencies: {
|
|
93
|
+
react: "^18.0.0 || ^19.0.0",
|
|
94
|
+
swr: "^2.0.0"
|
|
95
|
+
},
|
|
96
|
+
peerDependenciesMeta: {
|
|
97
|
+
react: {
|
|
98
|
+
optional: true
|
|
99
|
+
},
|
|
100
|
+
swr: {
|
|
101
|
+
optional: true
|
|
102
|
+
}
|
|
91
103
|
}
|
|
92
104
|
};
|
|
93
105
|
}
|
|
@@ -1803,7 +1815,7 @@ var require_validation = __commonJS({
|
|
|
1803
1815
|
// src/library/index.js
|
|
1804
1816
|
var require_library = __commonJS({
|
|
1805
1817
|
"src/library/index.js"(exports, module) {
|
|
1806
|
-
var { Contract, getAddress, keccak256, toUtf8Bytes } = __require("ethers");
|
|
1818
|
+
var { Contract, getAddress, keccak256, toUtf8Bytes, AbiCoder } = __require("ethers");
|
|
1807
1819
|
var { Interface } = __require("ethers");
|
|
1808
1820
|
var ABI = require_abi();
|
|
1809
1821
|
var { SageSDKError, CODES } = require_errors();
|
|
@@ -1842,17 +1854,31 @@ var require_library = __commonJS({
|
|
|
1842
1854
|
promptCount: Number(promptCount)
|
|
1843
1855
|
};
|
|
1844
1856
|
}
|
|
1857
|
+
function _computeLibraryKey(subdao, libraryId) {
|
|
1858
|
+
const coder = AbiCoder.defaultAbiCoder ? AbiCoder.defaultAbiCoder() : new AbiCoder();
|
|
1859
|
+
const encoded = coder.encode(["address", "string"], [getAddress(subdao), String(libraryId)]);
|
|
1860
|
+
return keccak256(encoded);
|
|
1861
|
+
}
|
|
1845
1862
|
async function getLatestLibrary({ provider, registry, subdao, libraryId = "main" }) {
|
|
1846
1863
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1847
1864
|
const addr = normalise(registry, "registry");
|
|
1848
1865
|
const sub = normalise(subdao, "subdao");
|
|
1849
1866
|
const contract = new Contract(addr, ABI.LibraryRegistry, provider);
|
|
1850
|
-
const key =
|
|
1867
|
+
const key = _computeLibraryKey(sub, libraryId);
|
|
1851
1868
|
const latestCID = await contract.subdaoLibraryLatest(key).catch(() => "");
|
|
1852
1869
|
if (!latestCID || latestCID.length === 0) return null;
|
|
1853
1870
|
const info = await getManifestInfo({ provider, registry: addr, manifestCID: latestCID });
|
|
1854
1871
|
return info;
|
|
1855
1872
|
}
|
|
1873
|
+
async function getBeforeAfterForUpdate({ provider, registry, subdao, libraryId = "main", newCid }) {
|
|
1874
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1875
|
+
const addr = normalise(registry, "registry");
|
|
1876
|
+
const sub = normalise(subdao, "subdao");
|
|
1877
|
+
const contract = new Contract(addr, ABI.LibraryRegistry, provider);
|
|
1878
|
+
const key = _computeLibraryKey(sub, libraryId);
|
|
1879
|
+
const prev = await contract.subdaoLibraryLatest(key).catch(() => "");
|
|
1880
|
+
return { previousCID: prev || null, newCID: String(newCid), libraryId: String(libraryId) };
|
|
1881
|
+
}
|
|
1856
1882
|
async function hasScopedOwnership({ provider, registry, subdao, manifestCID }) {
|
|
1857
1883
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1858
1884
|
const addr = normalise(registry, "registry");
|
|
@@ -1878,6 +1904,8 @@ var require_library = __commonJS({
|
|
|
1878
1904
|
getLatestLibrary,
|
|
1879
1905
|
hasScopedOwnership,
|
|
1880
1906
|
buildUpdateLibraryForSubDAOTx,
|
|
1907
|
+
_computeLibraryKey,
|
|
1908
|
+
getBeforeAfterForUpdate,
|
|
1881
1909
|
/** Build an authorizeTimelock(timelock, subdao) call for a LibraryRegistry. */
|
|
1882
1910
|
buildAuthorizeTimelockTx: function buildAuthorizeTimelockTx({ registry, timelock, subdao }) {
|
|
1883
1911
|
const to = normalise(registry, "registry");
|
|
@@ -2123,6 +2151,62 @@ var require_governance = __commonJS({
|
|
|
2123
2151
|
const data = iface.encodeFunctionData("execute(uint256)", [id]);
|
|
2124
2152
|
return { to: addr, data, value: BigIntZero };
|
|
2125
2153
|
}
|
|
2154
|
+
async function decodeProposalEffects({ provider, governor, proposalId, fromBlock = 0, toBlock = "latest" }) {
|
|
2155
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
2156
|
+
const govAddr = normaliseGovernor(governor);
|
|
2157
|
+
const iface = new Interface([ABI.Events.ProposalCreated]);
|
|
2158
|
+
const topic = iface.getEvent("ProposalCreated").topicHash;
|
|
2159
|
+
const id = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
|
|
2160
|
+
let parsed = null;
|
|
2161
|
+
let logTxHash = null;
|
|
2162
|
+
const logs = await provider.getLogs({ address: govAddr, fromBlock, toBlock, topics: [topic] });
|
|
2163
|
+
for (const log of logs) {
|
|
2164
|
+
try {
|
|
2165
|
+
const p = iface.parseLog(log);
|
|
2166
|
+
if (BigInt(p.args.id.toString()) === id) {
|
|
2167
|
+
parsed = p;
|
|
2168
|
+
logTxHash = log.transactionHash;
|
|
2169
|
+
break;
|
|
2170
|
+
}
|
|
2171
|
+
} catch (_) {
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
if (!parsed) throw new Error("ProposalCreated event not found");
|
|
2175
|
+
const targets = parsed.args.targets.map(getAddress);
|
|
2176
|
+
const calldatas = parsed.args.calldatas.map((d) => typeof d === "string" ? d : "0x" + Buffer.from(d).toString("hex"));
|
|
2177
|
+
const LibraryIface = new Interface(["function updateLibraryForSubDAO(address,string,string,uint256)", "function subdaoLibraryLatest(bytes32) view returns (string)"]);
|
|
2178
|
+
const PromptIface = new Interface(["function updatePromptByGovernance(string,string)"]);
|
|
2179
|
+
const coder = AbiCoder.defaultAbiCoder ? AbiCoder.defaultAbiCoder() : new AbiCoder();
|
|
2180
|
+
const effects = [];
|
|
2181
|
+
for (let i = 0; i < calldatas.length; i++) {
|
|
2182
|
+
const data = calldatas[i];
|
|
2183
|
+
const sel = data.slice(0, 10);
|
|
2184
|
+
try {
|
|
2185
|
+
const decoded = LibraryIface.decodeFunctionData("updateLibraryForSubDAO", data);
|
|
2186
|
+
const [subdao, libraryId, newCid, promptCount] = decoded;
|
|
2187
|
+
let previousCID = null;
|
|
2188
|
+
try {
|
|
2189
|
+
const reg = new Contract(targets[i], ["function subdaoLibraryLatest(bytes32) view returns (string)"], provider);
|
|
2190
|
+
const key = keccak256(coder.encode(["address", "string"], [getAddress(subdao), String(libraryId)]));
|
|
2191
|
+
const prev = await reg.subdaoLibraryLatest(key).catch(() => "");
|
|
2192
|
+
previousCID = prev && prev.length ? String(prev) : null;
|
|
2193
|
+
} catch (_) {
|
|
2194
|
+
}
|
|
2195
|
+
effects.push({ type: "libraryUpdate", index: i, target: targets[i], subdao: getAddress(subdao), libraryId: String(libraryId), previousCid: previousCID, newCid: String(newCid), promptCount: Number(promptCount) });
|
|
2196
|
+
continue;
|
|
2197
|
+
} catch (_) {
|
|
2198
|
+
}
|
|
2199
|
+
try {
|
|
2200
|
+
const decodedP = PromptIface.decodeFunctionData("updatePromptByGovernance", data);
|
|
2201
|
+
const [key, newCid] = decodedP;
|
|
2202
|
+
effects.push({ type: "promptUpdate", index: i, target: targets[i], key: String(key), newCid: String(newCid) });
|
|
2203
|
+
continue;
|
|
2204
|
+
} catch (_) {
|
|
2205
|
+
}
|
|
2206
|
+
effects.push({ type: "unknown", index: i, target: targets[i], selector: sel });
|
|
2207
|
+
}
|
|
2208
|
+
return { governor: govAddr, proposalId: id, tx: logTxHash, actions: { count: calldatas.length }, effects };
|
|
2209
|
+
}
|
|
2126
2210
|
async function getQuorumAt({ provider, governor, blockTag }) {
|
|
2127
2211
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
2128
2212
|
const addr = normaliseGovernor(governor);
|
|
@@ -2252,6 +2336,7 @@ var require_governance = __commonJS({
|
|
|
2252
2336
|
buildExecuteTx,
|
|
2253
2337
|
buildQueueByIdTx,
|
|
2254
2338
|
buildExecuteByIdTx,
|
|
2339
|
+
decodeProposalEffects,
|
|
2255
2340
|
makeProposalDescription,
|
|
2256
2341
|
stripProposalSalt,
|
|
2257
2342
|
getQuorumAt,
|
|
@@ -4091,7 +4176,7 @@ var require_factory = __commonJS({
|
|
|
4091
4176
|
// src/browser/library.js
|
|
4092
4177
|
var require_library2 = __commonJS({
|
|
4093
4178
|
"src/browser/library.js"(exports, module) {
|
|
4094
|
-
var { Contract, getAddress, keccak256,
|
|
4179
|
+
var { Contract, getAddress, keccak256, AbiCoder, Interface } = __require("ethers");
|
|
4095
4180
|
var ABI = require_abi();
|
|
4096
4181
|
var { SageSDKError, CODES } = require_errors();
|
|
4097
4182
|
var { searchRegistry } = require_search();
|
|
@@ -4128,12 +4213,17 @@ var require_library2 = __commonJS({
|
|
|
4128
4213
|
promptCount: Number(promptCount)
|
|
4129
4214
|
};
|
|
4130
4215
|
}
|
|
4216
|
+
function _computeLibraryKey(subdao, libraryId) {
|
|
4217
|
+
const coder = AbiCoder.defaultAbiCoder ? AbiCoder.defaultAbiCoder() : new AbiCoder();
|
|
4218
|
+
const encoded = coder.encode(["address", "string"], [getAddress(subdao), String(libraryId)]);
|
|
4219
|
+
return keccak256(encoded);
|
|
4220
|
+
}
|
|
4131
4221
|
async function getLatestLibrary({ provider, registry, subdao, libraryId = "main" }) {
|
|
4132
4222
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4133
4223
|
const addr = normalise(registry, "registry");
|
|
4134
4224
|
const sub = normalise(subdao, "subdao");
|
|
4135
4225
|
const contract = new Contract(addr, ABI.LibraryRegistry, provider);
|
|
4136
|
-
const key =
|
|
4226
|
+
const key = _computeLibraryKey(sub, libraryId);
|
|
4137
4227
|
const latestCID = await contract.subdaoLibraryLatest(key).catch(() => "");
|
|
4138
4228
|
if (!latestCID || latestCID.length === 0) return null;
|
|
4139
4229
|
const info = await getManifestInfo({ provider, registry: addr, manifestCID: latestCID });
|
|
@@ -4158,6 +4248,15 @@ var require_library2 = __commonJS({
|
|
|
4158
4248
|
]);
|
|
4159
4249
|
return { to, data, value: 0n };
|
|
4160
4250
|
}
|
|
4251
|
+
async function getBeforeAfterForUpdate({ provider, registry, subdao, libraryId = "main", newCid }) {
|
|
4252
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4253
|
+
const addr = normalise(registry, "registry");
|
|
4254
|
+
const sub = normalise(subdao, "subdao");
|
|
4255
|
+
const contract = new Contract(addr, ABI.LibraryRegistry, provider);
|
|
4256
|
+
const key = _computeLibraryKey(sub, libraryId);
|
|
4257
|
+
const prev = await contract.subdaoLibraryLatest(key).catch(() => "");
|
|
4258
|
+
return { previousCID: prev || null, newCID: String(newCid), libraryId: String(libraryId) };
|
|
4259
|
+
}
|
|
4161
4260
|
function buildAuthorizeTimelockTx({ registry, timelock, subdao }) {
|
|
4162
4261
|
const to = normalise(registry, "registry");
|
|
4163
4262
|
const iface = new Interface(["function authorizeTimelock(address,address)"]);
|
|
@@ -4170,6 +4269,7 @@ var require_library2 = __commonJS({
|
|
|
4170
4269
|
getLatestLibrary,
|
|
4171
4270
|
hasScopedOwnership,
|
|
4172
4271
|
buildUpdateLibraryForSubDAOTx,
|
|
4272
|
+
getBeforeAfterForUpdate,
|
|
4173
4273
|
buildAuthorizeTimelockTx,
|
|
4174
4274
|
searchRegistry
|
|
4175
4275
|
};
|
|
@@ -5779,7 +5879,23 @@ var require_treasury = __commonJS({
|
|
|
5779
5879
|
confirmWithdrawal,
|
|
5780
5880
|
cancelWithdrawal,
|
|
5781
5881
|
setPriceOverride,
|
|
5782
|
-
clearPriceOverride
|
|
5882
|
+
clearPriceOverride,
|
|
5883
|
+
// TX builders for app usage
|
|
5884
|
+
buildApproveTx: ({ token, spender, amount, decimals = 18 }) => {
|
|
5885
|
+
if (!token || !spender) throw new SageSDKError(CODES.INVALID_ARGS, "token and spender required");
|
|
5886
|
+
const iface = new Interface(["function approve(address,uint256)"]);
|
|
5887
|
+
return { to: getAddress(token), data: iface.encodeFunctionData("approve", [getAddress(spender), BigInt(amount.toString ? amount.toString() : amount)]), value: 0n };
|
|
5888
|
+
},
|
|
5889
|
+
buildTransferTx: ({ token, to, amount, decimals = 18 }) => {
|
|
5890
|
+
if (!token || !to) throw new SageSDKError(CODES.INVALID_ARGS, "token and to required");
|
|
5891
|
+
const iface = new Interface(["function transfer(address,uint256)"]);
|
|
5892
|
+
return { to: getAddress(token), data: iface.encodeFunctionData("transfer", [getAddress(to), BigInt(amount.toString ? amount.toString() : amount)]), value: 0n };
|
|
5893
|
+
},
|
|
5894
|
+
buildWrapEthTx: ({ weth, amountWei }) => {
|
|
5895
|
+
if (!weth) throw new SageSDKError(CODES.INVALID_ARGS, "weth required");
|
|
5896
|
+
const iface = new Interface(["function deposit() payable"]);
|
|
5897
|
+
return { to: getAddress(weth), data: iface.encodeFunctionData("deposit", []), value: BigInt(amountWei.toString ? amountWei.toString() : amountWei) };
|
|
5898
|
+
}
|
|
5783
5899
|
};
|
|
5784
5900
|
}
|
|
5785
5901
|
});
|
|
@@ -6120,12 +6236,40 @@ var require_doppler = __commonJS({
|
|
|
6120
6236
|
}
|
|
6121
6237
|
return { auctionAddress, note: "Lens API not available in this doppler-sdk version" };
|
|
6122
6238
|
}
|
|
6239
|
+
async function listAuctions(sdk, { limit = 10, cursor } = {}) {
|
|
6240
|
+
const lens = sdk.lens;
|
|
6241
|
+
if (lens) {
|
|
6242
|
+
if (typeof lens.listAuctions === "function") {
|
|
6243
|
+
try {
|
|
6244
|
+
return await lens.listAuctions({ limit, cursor });
|
|
6245
|
+
} catch (e) {
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
if (typeof lens.getAuctions === "function") {
|
|
6249
|
+
try {
|
|
6250
|
+
return await lens.getAuctions({ limit, cursor });
|
|
6251
|
+
} catch (e) {
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
const fac = sdk.factory;
|
|
6256
|
+
if (fac && typeof fac.listAuctions === "function") {
|
|
6257
|
+
try {
|
|
6258
|
+
return await fac.listAuctions({ limit, cursor });
|
|
6259
|
+
} catch (e) {
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
6262
|
+
return { ok: false, error: "LIST_UNSUPPORTED", message: "Listing auctions not supported by this doppler-sdk version" };
|
|
6263
|
+
}
|
|
6123
6264
|
async function buyTokens(sdk, { auctionAddress, numeraireAmount }) {
|
|
6124
6265
|
const trade = sdk.trade || sdk.swap || sdk.auction;
|
|
6125
6266
|
if (trade && typeof trade.buy === "function") {
|
|
6126
|
-
|
|
6267
|
+
try {
|
|
6268
|
+
return await trade.buy({ auctionAddress, numeraireAmount });
|
|
6269
|
+
} catch (e) {
|
|
6270
|
+
}
|
|
6127
6271
|
}
|
|
6128
|
-
|
|
6272
|
+
return { ok: false, error: "BUY_UNSUPPORTED", message: "Buy not supported by this doppler-sdk version. Use executeV3BuyExactIn/urExecute helpers or upgrade doppler-sdk." };
|
|
6129
6273
|
}
|
|
6130
6274
|
async function migrate(sdk, asset) {
|
|
6131
6275
|
if (sdk.airlock && typeof sdk.airlock.migrate === "function") {
|
|
@@ -6137,6 +6281,7 @@ var require_doppler = __commonJS({
|
|
|
6137
6281
|
initDoppler,
|
|
6138
6282
|
deployDynamicAuction,
|
|
6139
6283
|
getAuctionStatus,
|
|
6284
|
+
listAuctions,
|
|
6140
6285
|
buyTokens,
|
|
6141
6286
|
migrate
|
|
6142
6287
|
};
|