@sage-protocol/sdk 0.2.2 → 0.2.3
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 +103 -721
- package/dist/browser/index.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,820 +1,202 @@
|
|
|
1
|
-
|
|
1
|
+
# Sage Protocol SDK
|
|
2
2
|
|
|
3
|
-
Sage Protocol
|
|
4
|
-
=================
|
|
3
|
+
Backend-agnostic helpers for interacting with Sage Protocol contracts, IPFS, and governance systems.
|
|
5
4
|
|
|
6
|
-
Purpose
|
|
7
|
-
-------
|
|
8
|
-
- Backend-agnostic helpers for interacting with Sage Protocol contracts (governance, SubDAOs, libraries, prompts, tokens).
|
|
9
|
-
- Shared foundation for the CLI, backend services, and upcoming web UI.
|
|
10
|
-
|
|
11
|
-
Design Principles
|
|
12
|
-
-----------------
|
|
13
|
-
- Pure data in/out; no console prompts or environment coupling.
|
|
14
|
-
- Minimal ABI fragments maintained alongside the contracts (see `src/abi`).
|
|
15
|
-
- Composable modules – import only what you need.
|
|
16
|
-
|
|
17
|
-
Quick Start
|
|
18
|
-
-----------
|
|
19
5
|
```bash
|
|
20
|
-
npm i
|
|
6
|
+
npm i @sage-protocol/sdk
|
|
21
7
|
```
|
|
22
8
|
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
23
11
|
```js
|
|
24
12
|
import sdk from '@sage-protocol/sdk';
|
|
25
13
|
|
|
26
14
|
const provider = sdk.getProvider({ rpcUrl: 'https://base-sepolia.publicnode.com' });
|
|
27
15
|
|
|
28
|
-
// Discover SubDAOs
|
|
16
|
+
// Discover SubDAOs
|
|
29
17
|
const subdaos = await sdk.subdao.discoverSubDAOs({
|
|
30
18
|
provider,
|
|
31
19
|
factoryAddress: '0x89Fd9FfD04503A62c52E6769401AB928abbeD5cA',
|
|
32
20
|
fromBlock: 0,
|
|
33
21
|
});
|
|
34
22
|
|
|
35
|
-
// Fetch
|
|
23
|
+
// Fetch SubDAO info
|
|
36
24
|
const info = await sdk.subdao.getSubDAOInfo({ provider, subdao: subdaos[0].subdao });
|
|
37
|
-
// info.profileCID points at an IPFS JSON profile/playbook document (name, description, avatar, social links, etc.)
|
|
38
25
|
```
|
|
39
26
|
|
|
40
|
-
**CommonJS
|
|
41
|
-
|
|
27
|
+
**CommonJS**
|
|
42
28
|
```js
|
|
43
29
|
const sdk = require('@sage-protocol/sdk');
|
|
44
30
|
```
|
|
45
31
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| `treasury` | Reserve/POL snapshot, pending withdrawals, liquidity plans | Treasury analytics, Safe operators |
|
|
71
|
-
| `boost` | Merkle/Direct boost readers + builders | Incentive distribution tooling |
|
|
72
|
-
| `subgraph` | GraphQL helpers for proposals/libraries | Historical analytics |
|
|
73
|
-
| `services` | High-level SubgraphService + IPFSService with retry/caching | Web app, CLI integration |
|
|
74
|
-
| `adapters` | Normalized governance adapters (OZ) | UI layers needing a unified model |
|
|
75
|
-
| `errors` | `SageSDKError` codes | Consistent downstream error handling |
|
|
76
|
-
|
|
77
|
-
### Service Layer
|
|
78
|
-
|
|
79
|
-
The SDK provides high-level service classes with built-in retry logic, caching, and error handling for production applications.
|
|
80
|
-
|
|
81
|
-
**SubgraphService** - GraphQL queries with automatic retry and caching
|
|
32
|
+
## Module Overview
|
|
33
|
+
|
|
34
|
+
| Module | Description |
|
|
35
|
+
|--------|-------------|
|
|
36
|
+
| `getProvider()` | Minimal ethers v6 RPC helper |
|
|
37
|
+
| `governance` | Governor metadata, proposals, voting |
|
|
38
|
+
| `timelock` | Queue operations, scheduling |
|
|
39
|
+
| `factory` | SubDAO creation and forking |
|
|
40
|
+
| `library` | Manifest listings, ownership |
|
|
41
|
+
| `lineage` | Fork ancestry, per-library fees |
|
|
42
|
+
| `prompt` | Prompt metadata, usage counters |
|
|
43
|
+
| `ipfs` | Upload, discovery, worker APIs |
|
|
44
|
+
| `subdao` | Discovery, staking, user stats |
|
|
45
|
+
| `token` | SXXX balances, allowances |
|
|
46
|
+
| `treasury` | Reserve snapshots, withdrawals |
|
|
47
|
+
| `boost` | Merkle/Direct boost helpers |
|
|
48
|
+
| `subgraph` | GraphQL queries |
|
|
49
|
+
| `services` | High-level SubgraphService + IPFSService |
|
|
50
|
+
| `adapters` | Normalized governance adapters |
|
|
51
|
+
| `errors` | `SageSDKError` codes |
|
|
52
|
+
|
|
53
|
+
## Services
|
|
54
|
+
|
|
55
|
+
### SubgraphService
|
|
82
56
|
|
|
83
57
|
```js
|
|
84
|
-
import { services
|
|
58
|
+
import { services } from '@sage-protocol/sdk';
|
|
85
59
|
|
|
86
|
-
// Initialize service
|
|
87
60
|
const subgraphService = new services.SubgraphService({
|
|
88
61
|
url: 'https://api.studio.thegraph.com/query/your-subgraph',
|
|
89
|
-
timeout: 10000,
|
|
90
|
-
retries: 3,
|
|
91
|
-
cache: {
|
|
92
|
-
enabled: true, // Enable caching (default: true)
|
|
93
|
-
ttl: 30000, // 30s cache TTL (default)
|
|
94
|
-
maxSize: 100, // Max 100 cache entries (default)
|
|
95
|
-
},
|
|
62
|
+
timeout: 10000,
|
|
63
|
+
retries: 3,
|
|
64
|
+
cache: { enabled: true, ttl: 30000 },
|
|
96
65
|
});
|
|
97
66
|
|
|
98
|
-
|
|
99
|
-
const subdaos = await subgraphService.getSubDAOs({ limit: 50, skip: 0 });
|
|
100
|
-
|
|
101
|
-
// Fetch proposals with filters
|
|
67
|
+
const subdaos = await subgraphService.getSubDAOs({ limit: 50 });
|
|
102
68
|
const proposals = await subgraphService.getProposals({
|
|
103
69
|
governor: '0xGovernor',
|
|
104
70
|
states: ['ACTIVE', 'PENDING'],
|
|
105
|
-
fromTimestamp: 1640000000,
|
|
106
|
-
limit: 20,
|
|
107
|
-
cache: true, // Use cache (default: true)
|
|
108
71
|
});
|
|
109
|
-
|
|
110
|
-
// Get single proposal by ID
|
|
111
|
-
const proposal = await subgraphService.getProposalById('0x123...', { cache: true });
|
|
112
|
-
|
|
113
|
-
// Fetch libraries
|
|
114
|
-
const libraries = await subgraphService.getLibraries({
|
|
115
|
-
subdao: '0xSubDAO',
|
|
116
|
-
limit: 50,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Fetch prompts by tag
|
|
120
|
-
const prompts = await subgraphService.getPromptsByTag({
|
|
121
|
-
tagsHash: '0xabc123...',
|
|
122
|
-
registry: '0xRegistry',
|
|
123
|
-
limit: 50,
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Cache management
|
|
127
|
-
subgraphService.clearCache();
|
|
128
|
-
const stats = subgraphService.getCacheStats();
|
|
129
|
-
// → { enabled: true, size: 12, maxSize: 100 }
|
|
130
72
|
```
|
|
131
73
|
|
|
132
|
-
|
|
74
|
+
### IPFSService
|
|
133
75
|
|
|
134
76
|
```js
|
|
135
|
-
import { services
|
|
136
|
-
import { ethers } from 'ethers';
|
|
77
|
+
import { services } from '@sage-protocol/sdk';
|
|
137
78
|
|
|
138
|
-
// Initialize service
|
|
139
79
|
const ipfsService = new services.IPFSService({
|
|
140
80
|
workerBaseUrl: 'https://api.sageprotocol.io',
|
|
141
81
|
gateway: 'https://ipfs.io',
|
|
142
|
-
signer:
|
|
143
|
-
timeout: 15000, // 15s timeout (default)
|
|
144
|
-
retries: 2, // 2 retry attempts (default)
|
|
145
|
-
cache: {
|
|
146
|
-
enabled: true, // Enable caching (default: true)
|
|
147
|
-
ttl: 300000, // 5min cache TTL for immutable CIDs (default)
|
|
148
|
-
maxSize: 50, // Max 50 cache entries (default)
|
|
149
|
-
},
|
|
82
|
+
signer: yourSigner, // Optional for uploads
|
|
150
83
|
});
|
|
151
84
|
|
|
152
|
-
// Fetch
|
|
153
|
-
|
|
154
|
-
const content = await ipfsService.fetchByCID('QmTest123...', {
|
|
155
|
-
cache: true,
|
|
156
|
-
timeout: 5000, // 5s timeout per gateway
|
|
157
|
-
extraGateways: ['https://cloudflare-ipfs.com', 'https://gateway.pinata.cloud'],
|
|
158
|
-
});
|
|
85
|
+
// Fetch (parallel gateway race)
|
|
86
|
+
const content = await ipfsService.fetchByCID('QmTest123...');
|
|
159
87
|
|
|
160
|
-
// Upload
|
|
88
|
+
// Upload
|
|
161
89
|
const cid = await ipfsService.upload(
|
|
162
90
|
{ title: 'My Prompt', content: '...' },
|
|
163
91
|
{ name: 'prompt.json', warm: true }
|
|
164
92
|
);
|
|
165
|
-
// → 'QmNewContent123...'
|
|
166
|
-
|
|
167
|
-
// Pin CIDs to worker
|
|
168
|
-
await ipfsService.pin(['QmTest1...', 'QmTest2...'], { warm: false });
|
|
169
|
-
|
|
170
|
-
// Warm gateways (prefetch)
|
|
171
|
-
await ipfsService.warm('QmTest123...', {
|
|
172
|
-
gateways: ['https://ipfs.io', 'https://cloudflare-ipfs.com'],
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Cache management
|
|
176
|
-
ipfsService.clearCache();
|
|
177
|
-
const stats = ipfsService.getCacheStats();
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**Error Handling**
|
|
181
|
-
|
|
182
|
-
```js
|
|
183
|
-
import { services, serviceErrors } from '@sage-protocol/sdk';
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
const subdaos = await subgraphService.getSubDAOs({ limit: 50 });
|
|
187
|
-
} catch (error) {
|
|
188
|
-
if (error instanceof serviceErrors.SubgraphError) {
|
|
189
|
-
console.error(`Subgraph error [${error.code}]:`, error.message);
|
|
190
|
-
console.log('Retryable:', error.retryable);
|
|
191
|
-
// Codes: TIMEOUT, NETWORK, INVALID_RESPONSE, NOT_FOUND, QUERY_FAILED
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
const content = await ipfsService.fetchByCID('QmTest...');
|
|
197
|
-
} catch (error) {
|
|
198
|
-
if (error instanceof serviceErrors.IPFSError) {
|
|
199
|
-
console.error(`IPFS error [${error.code}]:`, error.message);
|
|
200
|
-
// Codes: TIMEOUT, PIN_FAILED, INVALID_CID, NOT_FOUND, GATEWAY_FAILED, UPLOAD_FAILED
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Format user-friendly error messages
|
|
205
|
-
const friendlyMsg = serviceErrors.formatErrorMessage(error);
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
**Utility Exports**
|
|
209
|
-
|
|
210
|
-
```js
|
|
211
|
-
import { serviceUtils } from '@sage-protocol/sdk';
|
|
212
|
-
|
|
213
|
-
// Simple in-memory cache with TTL
|
|
214
|
-
const cache = new serviceUtils.SimpleCache({
|
|
215
|
-
enabled: true,
|
|
216
|
-
ttl: 60000, // 1 minute
|
|
217
|
-
maxSize: 200,
|
|
218
|
-
});
|
|
219
|
-
cache.set('key', { data: 'value' });
|
|
220
|
-
const val = cache.get('key');
|
|
221
|
-
|
|
222
|
-
// Exponential backoff retry
|
|
223
|
-
const result = await serviceUtils.retryWithBackoff(
|
|
224
|
-
async () => {
|
|
225
|
-
// Your async operation
|
|
226
|
-
return await fetchData();
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
attempts: 3,
|
|
230
|
-
baseDelay: 1000, // Start at 1s
|
|
231
|
-
maxDelay: 10000, // Cap at 10s
|
|
232
|
-
onRetry: ({ attempt, totalAttempts, delay, error }) => {
|
|
233
|
-
console.log(`Retry ${attempt}/${totalAttempts} after ${delay}ms:`, error.message);
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
**Performance Notes**
|
|
240
|
-
|
|
241
|
-
- **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)
|
|
242
|
-
- **In-Memory Caching**: Both services cache results in memory with configurable TTL to reduce redundant network calls
|
|
243
|
-
- **Exponential Backoff**: Automatic retry with exponential backoff (1s → 2s → 4s delays) for transient failures
|
|
244
|
-
- **Edge Runtime Compatible**: Uses `fetch()` instead of axios, compatible with Cloudflare Pages and Vercel Edge
|
|
245
|
-
|
|
246
|
-
**React Integration**
|
|
247
|
-
|
|
248
|
-
The SDK provides React hooks built on SWR for seamless integration in React applications:
|
|
249
|
-
|
|
250
|
-
**Prerequisites**:
|
|
251
|
-
```bash
|
|
252
|
-
# Install peer dependencies
|
|
253
|
-
npm install react swr
|
|
254
|
-
# or
|
|
255
|
-
yarn add react swr
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
**Usage**:
|
|
259
|
-
|
|
260
|
-
```js
|
|
261
|
-
import { services, hooks } from '@sage-protocol/sdk';
|
|
262
|
-
|
|
263
|
-
// Initialize services (once, typically in a context provider)
|
|
264
|
-
const subgraphService = new services.SubgraphService({
|
|
265
|
-
url: 'https://api.studio.thegraph.com/query/your-subgraph',
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const ipfsService = new services.IPFSService({
|
|
269
|
-
workerBaseUrl: 'https://api.sageprotocol.io',
|
|
270
|
-
gateway: 'https://ipfs.io',
|
|
271
|
-
signer: yourSigner, // Optional for uploads
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
// Use hooks in components
|
|
275
|
-
function SubDAOList() {
|
|
276
|
-
const { data: subdaos, error, isLoading } = hooks.useSubDAOs(subgraphService, {
|
|
277
|
-
limit: 50,
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
if (isLoading) return <div>Loading...</div>;
|
|
281
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
282
|
-
|
|
283
|
-
return (
|
|
284
|
-
<ul>
|
|
285
|
-
{subdaos.map(subdao => (
|
|
286
|
-
<li key={subdao.id}>{subdao.name}</li>
|
|
287
|
-
))}
|
|
288
|
-
</ul>
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function ProposalList({ governorAddress }) {
|
|
293
|
-
const { data: proposals } = hooks.useProposals(subgraphService, {
|
|
294
|
-
governor: governorAddress,
|
|
295
|
-
states: ['ACTIVE', 'PENDING'],
|
|
296
|
-
limit: 20,
|
|
297
|
-
refreshInterval: 30000, // Auto-refresh every 30s
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
return (
|
|
301
|
-
<ul>
|
|
302
|
-
{proposals?.map(proposal => (
|
|
303
|
-
<li key={proposal.id}>{proposal.description}</li>
|
|
304
|
-
))}
|
|
305
|
-
</ul>
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function PromptViewer({ cid }) {
|
|
310
|
-
const { data: content, isLoading } = hooks.useFetchCID(ipfsService, cid, {
|
|
311
|
-
extraGateways: ['https://cloudflare-ipfs.com'],
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
if (isLoading) return <div>Loading prompt...</div>;
|
|
315
|
-
|
|
316
|
-
return <pre>{JSON.stringify(content, null, 2)}</pre>;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function UploadForm() {
|
|
320
|
-
const { upload, isUploading, error, data: cid } = hooks.useUpload(ipfsService);
|
|
321
|
-
|
|
322
|
-
const handleSubmit = async (e) => {
|
|
323
|
-
e.preventDefault();
|
|
324
|
-
const content = { title: 'My Prompt', content: '...' };
|
|
325
|
-
try {
|
|
326
|
-
const newCid = await upload(content, { name: 'prompt.json' });
|
|
327
|
-
console.log('Uploaded:', newCid);
|
|
328
|
-
} catch (err) {
|
|
329
|
-
console.error('Upload failed:', err);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
return (
|
|
334
|
-
<form onSubmit={handleSubmit}>
|
|
335
|
-
<button type="submit" disabled={isUploading}>
|
|
336
|
-
{isUploading ? 'Uploading...' : 'Upload'}
|
|
337
|
-
</button>
|
|
338
|
-
{error && <div>Error: {error.message}</div>}
|
|
339
|
-
{cid && <div>Uploaded to: {cid}</div>}
|
|
340
|
-
</form>
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
93
|
```
|
|
344
94
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
| Hook | Description | Returns |
|
|
348
|
-
|------|-------------|---------|
|
|
349
|
-
| `useSubDAOs(service, options)` | Fetch SubDAOs from subgraph | `{ data, error, isLoading, mutate }` |
|
|
350
|
-
| `useProposals(service, options)` | Fetch proposals from subgraph | `{ data, error, isLoading, mutate }` |
|
|
351
|
-
| `useFetchCID(service, cid, options)` | Fetch IPFS content by CID | `{ data, error, isLoading, mutate }` |
|
|
352
|
-
| `useUpload(service)` | Upload content to IPFS | `{ upload, isUploading, error, data, reset }` |
|
|
353
|
-
|
|
354
|
-
**Hook Options**:
|
|
355
|
-
|
|
356
|
-
All data-fetching hooks support SWR options:
|
|
357
|
-
- `refreshInterval` - Auto-refresh interval in ms
|
|
358
|
-
- `revalidateOnFocus` - Revalidate when window focused
|
|
359
|
-
- `revalidateOnReconnect` - Revalidate on network reconnect
|
|
360
|
-
- `cache` - Use service-level caching
|
|
361
|
-
|
|
362
|
-
**Note**: Hooks are optional and only available when `react` and `swr` peer dependencies are installed.
|
|
363
|
-
|
|
364
|
-
### Examples
|
|
365
|
-
|
|
366
|
-
- [Legacy base mini app hook](examples/base-mini-app/README.md) – React-friendly context mirroring the former in-repo app (now maintained externally).
|
|
367
|
-
- [Eliza agent recipe](examples/agent-eliza/README.md) – exposes SDK helpers to agent toolchains.
|
|
368
|
-
|
|
369
|
-
Important
|
|
370
|
-
- For all write calls to the factory (create/fork/stable-fee flows), pass the SubDAOFactory diamond proxy address as `factoryAddress`. Legacy monolithic factory routes have been removed.
|
|
95
|
+
## Common Operations
|
|
371
96
|
|
|
372
|
-
|
|
373
|
-
------------
|
|
374
|
-
- `resolveGovernanceContext` is still exported for compatibility but now logs a deprecation warning. Use the richer `governance` + `subdao` helpers instead.
|
|
375
|
-
- **Prompt-level forking** is deprecated. On-chain prompt forking via `SubDAO.forkPrompt()` and `SubDAO.forkPromptWithStable()` is no longer supported. Use `forked_from` metadata in prompt frontmatter instead. Library-level forks (entire SubDAOs) remain fully supported via the factory.
|
|
376
|
-
- **Prompt fork fees** (stable fee configuration for prompt forks) have been removed. Per-library SXXX fork fees are now the standard model for monetizing library forks. See the `lineage` module documentation above.
|
|
97
|
+
### Governance
|
|
377
98
|
|
|
378
|
-
Next Phases
|
|
379
|
-
-----------
|
|
380
|
-
Phase 6 focuses on integration polish and packaging. Track progress in the [SDK Improvement Specification](../../docs/SDK_Improvement_Specification.md).
|
|
381
|
-
|
|
382
|
-
New governance/factory/library helpers (2025‑10)
|
|
383
|
-
-----------------------------------------------
|
|
384
|
-
|
|
385
|
-
Proposal ID and preflight
|
|
386
99
|
```js
|
|
387
|
-
|
|
100
|
+
// Compute proposal ID
|
|
388
101
|
const idHex = sdk.governance.computeProposalIdHex({ targets, values, calldatas, description });
|
|
389
|
-
const pre = await sdk.governance.simulatePropose({ provider, governor, targets, values, calldatas, description, sender });
|
|
390
|
-
if (!pre.ok) throw new Error(`preflight failed: ${pre.error?.message}`);
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
Votes at latest‑1 (ERC20Votes)
|
|
394
|
-
```js
|
|
395
|
-
// Token path
|
|
396
|
-
const votes1 = await sdk.governance.getVotesLatestMinusOne({ provider, token: sxxxToken, account: user });
|
|
397
|
-
// Governor path (auto‑resolves token)
|
|
398
|
-
const votes2 = await sdk.governance.getVotesLatestMinusOne({ provider, governor, account: user });
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
Factory‑mapped registry
|
|
402
|
-
```js
|
|
403
|
-
const mapped = await sdk.factory.getSubDAORegistry({ provider, factory, subdao });
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
Registry preflight as timelock
|
|
407
|
-
```js
|
|
408
|
-
const { to, data } = sdk.library.buildUpdateLibraryTx({ registry, subdao, manifestCID, version: '1.0.0' });
|
|
409
|
-
const sim = await sdk.library.simulateAsTimelock({ provider, registry, to, data, timelock });
|
|
410
|
-
if (!sim.ok) throw new Error(`registry preflight failed: ${sim.error?.message}`);
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
Propose by hash (arrays + bytes32)
|
|
414
|
-
```js
|
|
415
|
-
// For governors that prefer descriptionHash (or deterministic IDs)
|
|
416
|
-
const dh = sdk.governance.hashDescription(description);
|
|
417
|
-
const tx = sdk.governance.buildProposeTxByHash({ governor, targets, values, calldatas, descriptionHash: dh });
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
API Notes and Examples
|
|
421
|
-
----------------------
|
|
422
|
-
|
|
423
|
-
Salted Proposal Descriptions
|
|
424
|
-
```js
|
|
425
|
-
import sdk from '@sage-protocol/sdk';
|
|
426
|
-
// buildProposeTx() auto‑salts descriptions unless you pass a bytes32 description hash
|
|
427
|
-
const tx = sdk.governance.buildProposeTx({
|
|
428
|
-
governor: '0xGov',
|
|
429
|
-
targets: ['0xTarget'],
|
|
430
|
-
values: [0n],
|
|
431
|
-
calldatas: ['0x...'],
|
|
432
|
-
descriptionOrHash: 'My Title', // will append \n\n[SALT:0x...] automatically
|
|
433
|
-
});
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
Private Transaction Submission
|
|
437
|
-
```js
|
|
438
|
-
import sdk from '@sage-protocol/sdk';
|
|
439
|
-
// Submit signed private transaction via builder/relay RPC (eth_sendPrivateTransaction)
|
|
440
|
-
const { hash } = await sdk.utils.privateTx.sendTransaction({
|
|
441
|
-
signer, // ethers v6 signer
|
|
442
|
-
tx: { to: '0xGov', data: '0x...', value: 0n },
|
|
443
|
-
privateRpcUrl: process.env.SAGE_PRIVATE_RPC,
|
|
444
|
-
});
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
Subgraph‑First Prompt Reads (with tag filters)
|
|
448
|
-
```js
|
|
449
|
-
import sdk from '@sage-protocol/sdk';
|
|
450
|
-
|
|
451
|
-
// Bulk listing by registry (subgraph)
|
|
452
|
-
const items = await sdk.subgraph.listRegistryPrompts({
|
|
453
|
-
url: process.env.SAGE_SUBGRAPH_URL,
|
|
454
|
-
registry: '0xRegistry',
|
|
455
|
-
first: 50,
|
|
456
|
-
});
|
|
457
102
|
|
|
458
|
-
//
|
|
459
|
-
const
|
|
460
|
-
url: process.env.SAGE_SUBGRAPH_URL,
|
|
461
|
-
tagsHash: '0xabc123...'
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
// On‑chain bounded fallback (IDs): latest prompts
|
|
465
|
-
const onchainLatest = await sdk.prompt.listPrompts({ provider, registry: '0xRegistry', limit: 50 });
|
|
466
|
-
|
|
467
|
-
// On‑chain bounded tag page
|
|
468
|
-
const onchainPage = await sdk.prompt.listByTagPage({ provider, registry: '0xRegistry', tagHash: '0xabc123...', offset: 0, limit: 25 });
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
Environment Controls
|
|
472
|
-
```bash
|
|
473
|
-
# Private txs
|
|
474
|
-
export SAGE_PRIVATE_RPC=https://builder.your-relay.example/rpc
|
|
475
|
-
|
|
476
|
-
# Deterministic proposal salt (for reproducible ids)
|
|
477
|
-
export SAGE_GOV_SALT=0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
478
|
-
|
|
479
|
-
# Disable auto‑salt (not recommended)
|
|
480
|
-
export SAGE_GOV_SALT_DISABLE=1
|
|
481
|
-
```
|
|
482
|
-
# New/Updated Helpers (2025-10)
|
|
483
|
-
|
|
484
|
-
Factory enumeration (CLI parity)
|
|
485
|
-
```js
|
|
486
|
-
// Full list of SubDAO addresses from factory storage
|
|
487
|
-
const addrs = await sdk.factory.listSubDAOs({ provider, factory: FACTORY });
|
|
488
|
-
|
|
489
|
-
// Indexed range with indices returned
|
|
490
|
-
const indexed = await sdk.factory.listSubDAOsIndexed({ provider, factory: FACTORY, from: 0n, to: 99n });
|
|
491
|
-
// → [{ index: 0, address: '0x...' }, ...]
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
Governance overloads and quorum
|
|
495
|
-
```js
|
|
496
|
-
// Build queue/execute using arrays+descriptionHash when supported; else fall back to id-based
|
|
497
|
-
const ol = await sdk.governance.detectGovernorOverloads({ provider, governor });
|
|
498
|
-
if (ol.hasQueueArrays) {
|
|
499
|
-
const q = sdk.governance.buildQueueTx({ governor, targets, values, calldatas, descriptionOrHash: descHash });
|
|
500
|
-
} else if (ol.hasQueueById) {
|
|
501
|
-
const q = sdk.governance.buildQueueByIdTx({ governor, proposalId });
|
|
502
|
-
}
|
|
103
|
+
// Preflight simulation
|
|
104
|
+
const pre = await sdk.governance.simulatePropose({ provider, governor, targets, values, calldatas, description, sender });
|
|
503
105
|
|
|
504
|
-
//
|
|
505
|
-
const
|
|
506
|
-
// Convenience: quorum at latest-1
|
|
507
|
-
const qLatest = await sdk.governance.getQuorumLatestMinusOne({ provider, governor });
|
|
508
|
-
```
|
|
106
|
+
// Get votes
|
|
107
|
+
const votes = await sdk.governance.getVotesLatestMinusOne({ provider, governor, account: user });
|
|
509
108
|
|
|
510
|
-
Self
|
|
511
|
-
```js
|
|
512
|
-
// Build a delegate(self) tx from a Governor (reads sxxxToken address)
|
|
109
|
+
// Self-delegate
|
|
513
110
|
const tx = await sdk.governance.buildDelegateSelfTx({ provider, governor, account: user });
|
|
514
|
-
// send via userOp (CDP) or EOA
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
Votes token resolution and gates
|
|
518
|
-
```js
|
|
519
|
-
// Resolve the IVotes token used for proposals
|
|
520
|
-
const votesToken = await sdk.governance.resolveVotesToken({ provider, governor });
|
|
521
|
-
|
|
522
|
-
// Build preferred self-delegate using resolved token
|
|
523
|
-
const del = await sdk.governance.buildDelegateSelfPreferred({ provider, governor, account: user });
|
|
524
|
-
|
|
525
|
-
// Delegate and verify votes (no throw)
|
|
526
|
-
const res = await sdk.governance.delegateSelfAndVerify({ provider, governor, account: user, signer, minVotes: 1n });
|
|
527
|
-
// → { txHash, ok, votes, payload }
|
|
528
|
-
|
|
529
|
-
// Readiness to propose (preflight)
|
|
530
|
-
const gates = await sdk.governance.ensureProposeGates({ provider, governor, proposer: user });
|
|
531
|
-
// Optionally include execution check (registry update as timelock)
|
|
532
|
-
const ready = await sdk.governance.readinessToPropose({
|
|
533
|
-
provider,
|
|
534
|
-
governor,
|
|
535
|
-
proposer: user,
|
|
536
|
-
execution: { registry, timelock, subdao, libraryId: 'main', manifestCID, promptCount: 12 },
|
|
537
|
-
});
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
Subgraph normalization
|
|
541
|
-
```js
|
|
542
|
-
// listProposalsFiltered now returns both state (string) and stateNum (0–7)
|
|
543
|
-
const items = await sdk.subgraph.listProposalsFiltered({ url: SUBGRAPH_URL, governor });
|
|
544
|
-
// item.state → 'PENDING' | 'ACTIVE' | ... ; item.stateNum → 0..7 or null when unknown
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
Subgraph State Normalization
|
|
548
|
-
----------------------------
|
|
549
|
-
- State Mapping
|
|
550
|
-
- PENDING → 0
|
|
551
|
-
- ACTIVE → 1
|
|
552
|
-
- CANCELED/CANCELLED → 2
|
|
553
|
-
- DEFEATED → 3
|
|
554
|
-
- SUCCEEDED → 4
|
|
555
|
-
- QUEUED → 5
|
|
556
|
-
- EXPIRED → 6
|
|
557
|
-
- EXECUTED → 7
|
|
558
|
-
- Example
|
|
559
|
-
```js
|
|
560
|
-
const items = await sdk.subgraph.listProposalsFiltered({ url: SUBGRAPH_URL, governor });
|
|
561
|
-
console.log(items[0].state); // 'PENDING'
|
|
562
|
-
console.log(items[0].stateNum); // 0
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
New Helper Examples
|
|
566
|
-
-------------------
|
|
567
|
-
|
|
568
|
-
- Votes token + self‑delegate
|
|
569
|
-
```js
|
|
570
|
-
const votesToken = await sdk.governance.resolveVotesToken({ provider, governor });
|
|
571
|
-
const tx = await sdk.governance.buildDelegateSelfPreferred({ provider, governor, account: user });
|
|
572
|
-
const res = await sdk.governance.delegateSelfAndVerify({ provider, governor, account: user, signer, minVotes: 1n });
|
|
573
|
-
console.log(res.ok, res.votes, res.txHash);
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
- Propose gates and execution readiness (one‑shot)
|
|
577
|
-
```js
|
|
578
|
-
const gates = await sdk.governance.ensureProposeGates({ provider, governor, proposer: user });
|
|
579
|
-
const ready = await sdk.governance.readinessToPropose({
|
|
580
|
-
provider, governor, proposer: user,
|
|
581
|
-
execution: { registry, timelock, subdao, libraryId: 'main', manifestCID, promptCount: 12 }
|
|
582
|
-
});
|
|
583
|
-
console.log({ threshold: gates.threshold, votesOk: gates.votesOk, execReady: ready.executionReady });
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
- List active proposals (subgraph)
|
|
587
|
-
```js
|
|
588
|
-
const active = await sdk.governance.listActiveProposals({ url: SUBGRAPH_URL, governor });
|
|
589
|
-
// active[i].state (string), active[i].stateNum (0–7)
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
- Library authorize + readiness
|
|
593
|
-
```js
|
|
594
|
-
const auth = sdk.library.buildAuthorizeTimelockTx({ registry, timelock, subdao });
|
|
595
|
-
const exec = await sdk.library.executionReadiness({ provider, registry, timelock, subdao, libraryId: 'main', manifestCID, promptCount });
|
|
596
|
-
console.log(exec.ok, exec.error);
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
Proposal timeline (subgraph)
|
|
600
|
-
```js
|
|
601
|
-
const t = await sdk.subgraph.getProposalTimeline({ url: SUBGRAPH_URL, id: idHexOrDecimal });
|
|
602
|
-
// { id, createdAt, queuedAt, executedAt, canceledAt, eta, state }
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
Compatibility
|
|
606
|
-
-------------
|
|
607
|
-
- 0.0.8 Compatibility
|
|
608
|
-
- All changes are additive; no removed or renamed exports. Existing callers can continue to rely on string state. stateNum is optional.
|
|
609
|
-
|
|
610
|
-
Troubleshooting
|
|
611
|
-
---------------
|
|
612
|
-
- JSON + BigInt
|
|
613
|
-
- Use a replacer when printing SDK results:
|
|
614
|
-
```js
|
|
615
|
-
JSON.stringify(obj, (_, v) => (typeof v === 'bigint' ? v.toString() : v));
|
|
616
111
|
```
|
|
617
|
-
- Governor filter casing
|
|
618
|
-
- The SDK normalizes governor for Bytes filters; if you issue manual GraphQL, use lowercase governor addresses to avoid case sensitivity pitfalls.
|
|
619
|
-
- Votes snapshot timing
|
|
620
|
-
- getVotesLatestMinusOne reads at latest‑1 to avoid same‑block edge cases on ERC20Votes.
|
|
621
112
|
|
|
622
|
-
|
|
623
|
-
```js
|
|
624
|
-
const totalByTag = await sdk.prompt.getByTagCount({ provider, registry, tagHash });
|
|
625
|
-
const page = await sdk.prompt.listByTagPage({ provider, registry, tagHash, offset: 0, limit: 25 });
|
|
626
|
-
```
|
|
113
|
+
### SubDAO Creation
|
|
627
114
|
|
|
628
|
-
Team SubDAO helpers
|
|
629
115
|
```js
|
|
630
|
-
//
|
|
116
|
+
// Create operator SubDAO (team-controlled)
|
|
631
117
|
const res = await sdk.subdao.createOperatorSubDAO({
|
|
632
118
|
signer,
|
|
633
119
|
factoryAddress: FACTORY,
|
|
634
120
|
operator: '0xSafeOrEOA',
|
|
635
121
|
name: 'My SubDAO',
|
|
636
|
-
description: 'Team controlled
|
|
637
|
-
accessModel: 0,
|
|
638
|
-
minStakeAmount: 0n,
|
|
122
|
+
description: 'Team controlled',
|
|
639
123
|
burnAmount: 1500n * 10n**18n,
|
|
640
124
|
sxxx: SXXX,
|
|
641
125
|
});
|
|
642
|
-
|
|
643
|
-
// Convert existing SubDAO to operator mode
|
|
644
|
-
await sdk.subdao.makeOperator({ signer, subdao: res.subdao, operator: '0xSafeOrEOA', grantAdmin: false });
|
|
645
|
-
|
|
646
|
-
// Ensure SXXX approval for factory burn if needed
|
|
647
|
-
await sdk.subdao.ensureSxxxBurnAllowance({ signer, sxxx: SXXX, spender: FACTORY, amount: 1500n * 10n**18n });
|
|
648
|
-
|
|
649
|
-
// (Optional) Build a setProfileCid tx for newer SubDAO implementations that expose setProfileCid(string)
|
|
650
|
-
// Use this in a Governor/Timelock proposal or Safe Transaction Builder, not as a direct EOA call.
|
|
651
|
-
const profileCid = 'bafy...'; // CID of dao-profile.json on IPFS
|
|
652
|
-
const { to, data, value } = sdk.subdao.buildSetProfileCidTx({ subdao: res.subdao, profileCid });
|
|
653
|
-
// Example: schedule via timelock or propose via Governor, depending on your governance mode
|
|
654
126
|
```
|
|
655
127
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
The `lineage` module provides helpers for tracking library fork relationships and per-library SXXX fork fees.
|
|
659
|
-
|
|
660
|
-
## Fork Ancestry
|
|
661
|
-
|
|
662
|
-
Libraries can be forked from other libraries. The `lineage` module tracks this ancestry:
|
|
128
|
+
### Library Fork Fees
|
|
663
129
|
|
|
664
130
|
```js
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const provider = sdk.getProvider({ rpcUrl: process.env.RPC_URL });
|
|
668
|
-
const registry = '0xLibraryRegistry';
|
|
669
|
-
const subdao = '0xSubDAO';
|
|
670
|
-
|
|
671
|
-
// Check if a library was forked from another
|
|
672
|
-
const isFork = await sdk.lineage.isFork({ provider, registry, subdao });
|
|
673
|
-
// → true if this library has a parent
|
|
674
|
-
|
|
675
|
-
// Get the parent library (null if original)
|
|
676
|
-
const parent = await sdk.lineage.getParentLibrary({ provider, registry, subdao });
|
|
677
|
-
// → '0xParentSubDAO' or null
|
|
678
|
-
|
|
679
|
-
// Get full ancestry chain from root to current
|
|
680
|
-
const chain = await sdk.lineage.getLineageChain({ provider, registry, subdao });
|
|
681
|
-
// → { chain: ['0xRoot', '0xChild', '0xGrandchild'], depth: 2, root: '0xRoot' }
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
## Per-Library Fork Fees
|
|
685
|
-
|
|
686
|
-
Library owners can set an SXXX fee for forking their library. This fee is paid by the forker to the parent library's treasury when creating a forked SubDAO.
|
|
687
|
-
|
|
688
|
-
```js
|
|
689
|
-
// Get the SXXX fork fee for a library (0n = free fork)
|
|
131
|
+
// Get fork fee for a library
|
|
690
132
|
const fee = await sdk.lineage.getLibraryForkFee({ provider, registry, subdao });
|
|
691
|
-
// → 1000000000000000000n (1 SXXX in wei)
|
|
692
133
|
|
|
693
|
-
// Get
|
|
134
|
+
// Get full library info
|
|
694
135
|
const info = await sdk.lineage.getLibraryInfo({ provider, registry, subdao });
|
|
695
|
-
// → {
|
|
696
|
-
// parentDAO: '0xParent' | null,
|
|
697
|
-
// forkFee: 1000000000000000000n,
|
|
698
|
-
// manifestCID: 'Qm...',
|
|
699
|
-
// version: '1.0.0'
|
|
700
|
-
// }
|
|
701
136
|
```
|
|
702
137
|
|
|
703
|
-
##
|
|
138
|
+
## React Hooks
|
|
704
139
|
|
|
705
|
-
|
|
140
|
+
Requires `react` and `swr` peer dependencies:
|
|
706
141
|
|
|
707
142
|
```js
|
|
708
|
-
import {
|
|
709
|
-
|
|
710
|
-
// Build the setLibraryForkFee transaction (for use in a proposal)
|
|
711
|
-
const registryIface = new ethers.Interface([
|
|
712
|
-
'function setLibraryForkFee(address dao, uint256 fee)'
|
|
713
|
-
]);
|
|
714
|
-
|
|
715
|
-
const feeInWei = ethers.parseUnits('10', 18); // 10 SXXX
|
|
716
|
-
const calldata = registryIface.encodeFunctionData('setLibraryForkFee', [subdao, feeInWei]);
|
|
717
|
-
|
|
718
|
-
// Include in a governance proposal
|
|
719
|
-
const proposeTx = sdk.governance.buildProposeTx({
|
|
720
|
-
governor,
|
|
721
|
-
targets: [registry],
|
|
722
|
-
values: [0n],
|
|
723
|
-
calldatas: [calldata],
|
|
724
|
-
descriptionOrHash: 'Set library fork fee to 10 SXXX'
|
|
725
|
-
});
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
## Fork Fee Flow
|
|
729
|
-
|
|
730
|
-
When a SubDAO is forked via the factory:
|
|
731
|
-
|
|
732
|
-
1. Factory reads the parent library's fork fee from LibraryRegistry
|
|
733
|
-
2. If fee > 0, SXXX is transferred from forker to parent's treasury
|
|
734
|
-
3. `LibraryForkFeePaid(parentDAO, forker, amount)` event is emitted
|
|
735
|
-
4. Fork proceeds with standard SubDAO creation
|
|
736
|
-
|
|
737
|
-
Note: Forkers must approve SXXX to the factory before forking a library with a fee set.
|
|
738
|
-
|
|
739
|
-
## Deprecation: Prompt-Level Forks
|
|
143
|
+
import { services, hooks } from '@sage-protocol/sdk';
|
|
740
144
|
|
|
741
|
-
|
|
145
|
+
function SubDAOList() {
|
|
146
|
+
const { data, error, isLoading } = hooks.useSubDAOs(subgraphService, { limit: 50 });
|
|
742
147
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
148
|
+
if (isLoading) return <div>Loading...</div>;
|
|
149
|
+
return <ul>{data.map(s => <li key={s.id}>{s.name}</li>)}</ul>;
|
|
150
|
+
}
|
|
746
151
|
|
|
747
|
-
|
|
152
|
+
function PromptViewer({ cid }) {
|
|
153
|
+
const { data } = hooks.useFetchCID(ipfsService, cid);
|
|
154
|
+
return <pre>{JSON.stringify(data, null, 2)}</pre>;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
748
157
|
|
|
749
|
-
|
|
158
|
+
| Hook | Description |
|
|
159
|
+
|------|-------------|
|
|
160
|
+
| `useSubDAOs` | Fetch SubDAOs from subgraph |
|
|
161
|
+
| `useProposals` | Fetch proposals from subgraph |
|
|
162
|
+
| `useFetchCID` | Fetch IPFS content by CID |
|
|
163
|
+
| `useUpload` | Upload content to IPFS |
|
|
750
164
|
|
|
751
|
-
|
|
165
|
+
## Error Handling
|
|
752
166
|
|
|
753
167
|
```js
|
|
754
|
-
import
|
|
755
|
-
import { getProvider } from '@sage-protocol/sdk';
|
|
168
|
+
import { serviceErrors } from '@sage-protocol/sdk';
|
|
756
169
|
|
|
757
|
-
|
|
758
|
-
const
|
|
170
|
+
try {
|
|
171
|
+
const data = await subgraphService.getSubDAOs({ limit: 50 });
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (error instanceof serviceErrors.SubgraphError) {
|
|
174
|
+
console.error(`[${error.code}]:`, error.message);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
759
178
|
|
|
760
|
-
|
|
761
|
-
const page = await sdk.adapters.governance.openzeppelin.getProposals({
|
|
762
|
-
provider,
|
|
763
|
-
governor: '0xGovernor',
|
|
764
|
-
fromBlock: 0,
|
|
765
|
-
toBlock: 'latest',
|
|
766
|
-
pageSize: 10_000,
|
|
767
|
-
// Optional: improve signature resolution
|
|
768
|
-
// ChainId is auto-detected from provider; you can override via chainId if needed.
|
|
769
|
-
// The adapter will try Sourcify → Etherscan/BaseScan automatically using NEXT_PUBLIC_ETHERSCAN_API_KEY.
|
|
770
|
-
// You may override with your own abiResolver as needed.
|
|
771
|
-
abiResolver: async ({ address, chainId }) => null,
|
|
772
|
-
selectorResolver: async (selector) => null, // plug in 4byte or your mapping
|
|
773
|
-
});
|
|
179
|
+
## Environment Variables
|
|
774
180
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
governor: '0xGovernor',
|
|
779
|
-
id: page.items[0].id,
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
// Signature list (best effort; uses Sourcify → Etherscan/BaseScan (with NEXT_PUBLIC_ETHERSCAN_API_KEY) → 4byte)
|
|
783
|
-
const sigs = await sdk.adapters.governance.openzeppelin.getSignatureList({
|
|
784
|
-
provider,
|
|
785
|
-
targets: page.items[0].targets,
|
|
786
|
-
calldatas: page.items[0].calldatas,
|
|
787
|
-
// optional resolvers (provide only if you have your own fetchers)
|
|
788
|
-
abiResolver: async ({ address, chainId }) => null,
|
|
789
|
-
selectorResolver: async (selector) => null,
|
|
790
|
-
});
|
|
181
|
+
```bash
|
|
182
|
+
# Private transaction relay
|
|
183
|
+
SAGE_PRIVATE_RPC=https://builder.your-relay.example/rpc
|
|
791
184
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
provider,
|
|
795
|
-
governor: '0xGovernor',
|
|
796
|
-
fromBlock: 0,
|
|
797
|
-
toBlock: 'latest',
|
|
798
|
-
pageSize: 20_000,
|
|
799
|
-
});
|
|
800
|
-
// evPage.items: [{ type:'created'|'queued'|'executed'|'canceled', blockNumber, timestamp, txHash, id }]
|
|
801
|
-
// evPage.nextCursor: { fromBlock } | null
|
|
185
|
+
# Explorer API (for ABI resolution)
|
|
186
|
+
NEXT_PUBLIC_ETHERSCAN_API_KEY=YourKey
|
|
802
187
|
```
|
|
803
188
|
|
|
804
|
-
|
|
805
|
-
- id (bigint), proposer (address), createdAt (seconds), startBlock, endBlock, quorum (bigint|null), txHash, targets[], values[], calldatas[], signatures[]
|
|
806
|
-
# Explorer API keys
|
|
189
|
+
## Design Principles
|
|
807
190
|
|
|
808
|
-
|
|
191
|
+
- Pure data in/out; no console prompts or environment coupling
|
|
192
|
+
- Minimal ABI fragments maintained alongside contracts
|
|
193
|
+
- Composable modules - import only what you need
|
|
194
|
+
- Edge runtime compatible (uses `fetch()`)
|
|
809
195
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
196
|
+
## Documentation
|
|
197
|
+
|
|
198
|
+
- [Full Docs](https://docs.sageprotocol.io)
|
|
199
|
+
|
|
200
|
+
## License
|
|
815
201
|
|
|
816
|
-
|
|
817
|
-
- For Base Sepolia (chainId 84532) we prefer BaseScan V2 (chainid=84532) automatically.
|
|
818
|
-
- For Base mainnet (8453) we use BaseScan v1 endpoint by default.
|
|
819
|
-
- For Ethereum mainnet (1) we use Etherscan v1.
|
|
820
|
-
- If Sourcify/Etherscan fail, the adapter falls back to 4byte signatures; unknown selectors appear as their 4‑byte hex.
|
|
202
|
+
Apache 2.0
|
package/dist/browser/index.mjs
CHANGED
|
@@ -14,7 +14,7 @@ var require_package = __commonJS({
|
|
|
14
14
|
"package.json"(exports, module) {
|
|
15
15
|
module.exports = {
|
|
16
16
|
name: "@sage-protocol/sdk",
|
|
17
|
-
version: "0.2.
|
|
17
|
+
version: "0.2.3",
|
|
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",
|
package/dist/index.cjs
CHANGED
|
@@ -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.2.
|
|
17
|
+
version: "0.2.3",
|
|
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",
|
package/dist/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ var require_package = __commonJS({
|
|
|
20
20
|
"package.json"(exports2, module2) {
|
|
21
21
|
module2.exports = {
|
|
22
22
|
name: "@sage-protocol/sdk",
|
|
23
|
-
version: "0.2.
|
|
23
|
+
version: "0.2.3",
|
|
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",
|
package/dist/node/index.cjs
CHANGED
|
@@ -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.2.
|
|
17
|
+
version: "0.2.3",
|
|
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",
|
package/dist/node/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ var require_package = __commonJS({
|
|
|
20
20
|
"package.json"(exports2, module2) {
|
|
21
21
|
module2.exports = {
|
|
22
22
|
name: "@sage-protocol/sdk",
|
|
23
|
-
version: "0.2.
|
|
23
|
+
version: "0.2.3",
|
|
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",
|
package/package.json
CHANGED