@sage-protocol/sdk 0.1.6 → 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 +54 -18
- package/dist/index.cjs +1022 -18
- package/dist/index.mjs +1022 -18
- package/dist/node/index.cjs +1022 -18
- package/dist/node/index.mjs +1022 -18
- package/package.json +15 -3
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.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
|
}
|
|
@@ -250,19 +262,20 @@ var require_abi = __commonJS({
|
|
|
250
262
|
var PersonalLicenseReceipt = [
|
|
251
263
|
"function balanceOf(address account, uint256 id) view returns (uint256)"
|
|
252
264
|
];
|
|
253
|
-
var
|
|
254
|
-
"function
|
|
255
|
-
"function
|
|
256
|
-
"function
|
|
257
|
-
"function
|
|
258
|
-
"function
|
|
259
|
-
"function
|
|
260
|
-
"
|
|
261
|
-
"
|
|
262
|
-
"
|
|
263
|
-
"
|
|
264
|
-
"
|
|
265
|
-
"
|
|
265
|
+
var SageTreasury = [
|
|
266
|
+
"function totalReserves() view returns (uint256)",
|
|
267
|
+
"function totalPOL() view returns (uint256)",
|
|
268
|
+
"function totalDebt() view returns (uint256)",
|
|
269
|
+
"function canonicalPool() view returns (address)",
|
|
270
|
+
"function routerOrVault() view returns (address)",
|
|
271
|
+
"function maxWithdrawalRate() view returns (uint256)",
|
|
272
|
+
"function emergencyWithdrawalLimit() view returns (uint256)",
|
|
273
|
+
"function getReserveTokens() view returns (address[])",
|
|
274
|
+
"function getReserve(address token) view returns (address tokenAddress,uint256 amount,uint256 value,bool isLP,bool isActive)",
|
|
275
|
+
"function pendingWithdrawals(uint256) view returns (address token,address recipient,uint256 amount,uint256 value,address requester,uint256 balanceBefore,uint256 recipientBalanceBefore,uint256 depositSnapshot,bool isLP,bool isEmergency,bool exists)",
|
|
276
|
+
"function nextWithdrawalId() view returns (uint256)",
|
|
277
|
+
"function manualPrices(address token) view returns (uint256 price,uint256 expiresAt,bool active)",
|
|
278
|
+
"function lpContributions(address,address) view returns (uint256)"
|
|
266
279
|
];
|
|
267
280
|
var GovernanceBoostMerkle = [
|
|
268
281
|
"function getProposalConfig(uint256) view returns (tuple(uint256 proposalId,address token,uint256 totalAmount,uint64 startTime,uint64 endTime,uint256 merkleRoot))",
|
|
@@ -274,6 +287,29 @@ var require_abi = __commonJS({
|
|
|
274
287
|
"function create(uint256 proposalId, address token, uint256 perVoter, uint256 maxVoters)",
|
|
275
288
|
"function fund(uint256 proposalId, uint256 amount)"
|
|
276
289
|
];
|
|
290
|
+
var BondDepository = [
|
|
291
|
+
// Core getters
|
|
292
|
+
"function payoutToken() view returns (address)",
|
|
293
|
+
"function principalToken() view returns (address)",
|
|
294
|
+
"function treasury() view returns (address)",
|
|
295
|
+
// Terms
|
|
296
|
+
"function terms() view returns (tuple(uint256 controlVariable,uint256 minimumPrice,uint256 maxPayout,uint256 maxDebt,uint256 vestingTerm,uint256 fee))",
|
|
297
|
+
"function totalDebt(address) view returns (uint256)",
|
|
298
|
+
// Pricing
|
|
299
|
+
"function bondPrice() view returns (uint256)",
|
|
300
|
+
"function bondPrice(address) view returns (uint256)",
|
|
301
|
+
"function bondPriceInUSD() view returns (uint256)",
|
|
302
|
+
"function currentDebt() view returns (uint256)",
|
|
303
|
+
"function debtRatio() view returns (uint256)",
|
|
304
|
+
"function standardizedDebtRatio() view returns (uint256)",
|
|
305
|
+
// User views
|
|
306
|
+
"function bondInfo(address) view returns (tuple(uint256 payout,uint256 vesting,uint256 lastBlock,uint256 pricePaid))",
|
|
307
|
+
"function pendingPayout(address) view returns (uint256)",
|
|
308
|
+
"function percentVestedFor(address) view returns (uint256)",
|
|
309
|
+
// Actions
|
|
310
|
+
"function deposit(uint256 _amount, uint256 _maxPrice) returns (uint256 payout_)",
|
|
311
|
+
"function redeem(address _recipient, bool _stake) returns (uint256)"
|
|
312
|
+
];
|
|
277
313
|
var Events = {
|
|
278
314
|
ProposalCreated: "event ProposalCreated(uint256 id, address proposer, address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, uint256 startBlock, uint256 endBlock, string description)"
|
|
279
315
|
};
|
|
@@ -292,10 +328,10 @@ var require_abi = __commonJS({
|
|
|
292
328
|
PersonalLibraryFacet,
|
|
293
329
|
PersonalMarketplace,
|
|
294
330
|
PersonalLicenseReceipt,
|
|
295
|
-
|
|
296
|
-
// Protocol treasury (replaces SageTreasury)
|
|
331
|
+
SageTreasury,
|
|
297
332
|
GovernanceBoostMerkle,
|
|
298
333
|
GovernanceBoostDirect,
|
|
334
|
+
BondDepository,
|
|
299
335
|
Events
|
|
300
336
|
};
|
|
301
337
|
}
|
|
@@ -8093,6 +8129,952 @@ var require_doppler = __commonJS({
|
|
|
8093
8129
|
}
|
|
8094
8130
|
});
|
|
8095
8131
|
|
|
8132
|
+
// src/services/utils/cache.js
|
|
8133
|
+
var require_cache = __commonJS({
|
|
8134
|
+
"src/services/utils/cache.js"(exports2, module2) {
|
|
8135
|
+
var SimpleCache = class {
|
|
8136
|
+
constructor(options = {}) {
|
|
8137
|
+
this.enabled = options.enabled !== false;
|
|
8138
|
+
this.defaultTTL = options.ttl || 3e4;
|
|
8139
|
+
this.maxSize = options.maxSize || 100;
|
|
8140
|
+
this._store = /* @__PURE__ */ new Map();
|
|
8141
|
+
}
|
|
8142
|
+
/**
|
|
8143
|
+
* Get value from cache
|
|
8144
|
+
* @param {string} key - Cache key
|
|
8145
|
+
* @returns {any|null} - Cached value or null if expired/missing
|
|
8146
|
+
*/
|
|
8147
|
+
get(key) {
|
|
8148
|
+
if (!this.enabled) return null;
|
|
8149
|
+
const entry = this._store.get(key);
|
|
8150
|
+
if (!entry) return null;
|
|
8151
|
+
if (Date.now() > entry.expires) {
|
|
8152
|
+
this._store.delete(key);
|
|
8153
|
+
return null;
|
|
8154
|
+
}
|
|
8155
|
+
return entry.data;
|
|
8156
|
+
}
|
|
8157
|
+
/**
|
|
8158
|
+
* Set value in cache with TTL
|
|
8159
|
+
* @param {string} key - Cache key
|
|
8160
|
+
* @param {any} data - Data to cache
|
|
8161
|
+
* @param {number} ttl - Time to live in milliseconds (optional)
|
|
8162
|
+
*/
|
|
8163
|
+
set(key, data, ttl) {
|
|
8164
|
+
if (!this.enabled) return;
|
|
8165
|
+
if (this._store.size >= this.maxSize && !this._store.has(key)) {
|
|
8166
|
+
const firstKey = this._store.keys().next().value;
|
|
8167
|
+
this._store.delete(firstKey);
|
|
8168
|
+
}
|
|
8169
|
+
this._store.set(key, {
|
|
8170
|
+
data,
|
|
8171
|
+
expires: Date.now() + (ttl || this.defaultTTL)
|
|
8172
|
+
});
|
|
8173
|
+
}
|
|
8174
|
+
/**
|
|
8175
|
+
* Clear all cached entries
|
|
8176
|
+
*/
|
|
8177
|
+
clear() {
|
|
8178
|
+
this._store.clear();
|
|
8179
|
+
}
|
|
8180
|
+
/**
|
|
8181
|
+
* Delete specific key
|
|
8182
|
+
* @param {string} key - Cache key to delete
|
|
8183
|
+
*/
|
|
8184
|
+
delete(key) {
|
|
8185
|
+
this._store.delete(key);
|
|
8186
|
+
}
|
|
8187
|
+
/**
|
|
8188
|
+
* Get cache statistics
|
|
8189
|
+
* @returns {object} - Cache stats (size, enabled)
|
|
8190
|
+
*/
|
|
8191
|
+
stats() {
|
|
8192
|
+
return {
|
|
8193
|
+
size: this._store.size,
|
|
8194
|
+
maxSize: this.maxSize,
|
|
8195
|
+
enabled: this.enabled
|
|
8196
|
+
};
|
|
8197
|
+
}
|
|
8198
|
+
};
|
|
8199
|
+
module2.exports = { SimpleCache };
|
|
8200
|
+
}
|
|
8201
|
+
});
|
|
8202
|
+
|
|
8203
|
+
// src/services/utils/retry.js
|
|
8204
|
+
var require_retry = __commonJS({
|
|
8205
|
+
"src/services/utils/retry.js"(exports2, module2) {
|
|
8206
|
+
async function retryWithBackoff(fn, options = {}) {
|
|
8207
|
+
const {
|
|
8208
|
+
attempts = 3,
|
|
8209
|
+
baseDelay = 1e3,
|
|
8210
|
+
maxDelay = 1e4,
|
|
8211
|
+
onRetry = null
|
|
8212
|
+
} = options;
|
|
8213
|
+
let lastError;
|
|
8214
|
+
for (let i = 0; i < attempts; i++) {
|
|
8215
|
+
try {
|
|
8216
|
+
return await fn();
|
|
8217
|
+
} catch (error) {
|
|
8218
|
+
lastError = error;
|
|
8219
|
+
if (i === attempts - 1) {
|
|
8220
|
+
break;
|
|
8221
|
+
}
|
|
8222
|
+
const delay = Math.min(baseDelay * Math.pow(2, i), maxDelay);
|
|
8223
|
+
if (onRetry) {
|
|
8224
|
+
onRetry({
|
|
8225
|
+
attempt: i + 1,
|
|
8226
|
+
totalAttempts: attempts,
|
|
8227
|
+
delay,
|
|
8228
|
+
error
|
|
8229
|
+
});
|
|
8230
|
+
}
|
|
8231
|
+
await sleep(delay);
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
8234
|
+
throw lastError;
|
|
8235
|
+
}
|
|
8236
|
+
function sleep(ms) {
|
|
8237
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8238
|
+
}
|
|
8239
|
+
module2.exports = { retryWithBackoff, sleep };
|
|
8240
|
+
}
|
|
8241
|
+
});
|
|
8242
|
+
|
|
8243
|
+
// src/errors/index.js
|
|
8244
|
+
var require_errors2 = __commonJS({
|
|
8245
|
+
"src/errors/index.js"(exports2, module2) {
|
|
8246
|
+
var SageSDKError = class extends Error {
|
|
8247
|
+
constructor(message, code, retryable = false, originalError = null) {
|
|
8248
|
+
super(message);
|
|
8249
|
+
this.name = this.constructor.name;
|
|
8250
|
+
this.code = code;
|
|
8251
|
+
this.retryable = retryable;
|
|
8252
|
+
this.originalError = originalError;
|
|
8253
|
+
}
|
|
8254
|
+
};
|
|
8255
|
+
var SubgraphError = class extends SageSDKError {
|
|
8256
|
+
/**
|
|
8257
|
+
* @param {string} message - Error message
|
|
8258
|
+
* @param {'TIMEOUT'|'NETWORK'|'INVALID_RESPONSE'|'NOT_FOUND'|'QUERY_FAILED'} code - Error code
|
|
8259
|
+
* @param {boolean} retryable - Whether error is retryable
|
|
8260
|
+
* @param {Error} originalError - Original error (optional)
|
|
8261
|
+
*/
|
|
8262
|
+
constructor(message, code, retryable = false, originalError = null) {
|
|
8263
|
+
super(message, code, retryable, originalError);
|
|
8264
|
+
}
|
|
8265
|
+
};
|
|
8266
|
+
var IPFSError = class extends SageSDKError {
|
|
8267
|
+
/**
|
|
8268
|
+
* @param {string} message - Error message
|
|
8269
|
+
* @param {'TIMEOUT'|'PIN_FAILED'|'INVALID_CID'|'NOT_FOUND'|'GATEWAY_FAILED'|'UPLOAD_FAILED'} code - Error code
|
|
8270
|
+
* @param {boolean} retryable - Whether error is retryable
|
|
8271
|
+
* @param {Error} originalError - Original error (optional)
|
|
8272
|
+
*/
|
|
8273
|
+
constructor(message, code, retryable = false, originalError = null) {
|
|
8274
|
+
super(message, code, retryable, originalError);
|
|
8275
|
+
}
|
|
8276
|
+
};
|
|
8277
|
+
function formatErrorMessage(error) {
|
|
8278
|
+
if (!error) return "An unknown error occurred";
|
|
8279
|
+
if (error instanceof SubgraphError || error instanceof IPFSError) {
|
|
8280
|
+
switch (error.code) {
|
|
8281
|
+
case "TIMEOUT":
|
|
8282
|
+
return "Request timed out. Please try again.";
|
|
8283
|
+
case "NETWORK":
|
|
8284
|
+
return "Network error. Check your connection and try again.";
|
|
8285
|
+
case "INVALID_RESPONSE":
|
|
8286
|
+
return "Received invalid data from server.";
|
|
8287
|
+
case "NOT_FOUND":
|
|
8288
|
+
return "Content not found.";
|
|
8289
|
+
case "PIN_FAILED":
|
|
8290
|
+
return "Failed to pin content to IPFS. Please try again.";
|
|
8291
|
+
case "INVALID_CID":
|
|
8292
|
+
return "Invalid content identifier (CID).";
|
|
8293
|
+
case "GATEWAY_FAILED":
|
|
8294
|
+
return "All IPFS gateways failed. Content may be temporarily unavailable.";
|
|
8295
|
+
case "UPLOAD_FAILED":
|
|
8296
|
+
return "Failed to upload content. Please try again.";
|
|
8297
|
+
case "QUERY_FAILED":
|
|
8298
|
+
return "Failed to query data. Please try again.";
|
|
8299
|
+
default:
|
|
8300
|
+
return error.message || "An error occurred";
|
|
8301
|
+
}
|
|
8302
|
+
}
|
|
8303
|
+
return error.message || "An error occurred";
|
|
8304
|
+
}
|
|
8305
|
+
function isRetryable(error) {
|
|
8306
|
+
if (error instanceof SageSDKError) {
|
|
8307
|
+
return error.retryable;
|
|
8308
|
+
}
|
|
8309
|
+
if (error.name === "AbortError" || error.name === "TimeoutError") {
|
|
8310
|
+
return true;
|
|
8311
|
+
}
|
|
8312
|
+
const message = String(error.message || "").toLowerCase();
|
|
8313
|
+
const retryablePatterns = [
|
|
8314
|
+
"timeout",
|
|
8315
|
+
"network",
|
|
8316
|
+
"econnrefused",
|
|
8317
|
+
"econnreset",
|
|
8318
|
+
"etimedout",
|
|
8319
|
+
"fetch failed"
|
|
8320
|
+
];
|
|
8321
|
+
return retryablePatterns.some((pattern) => message.includes(pattern));
|
|
8322
|
+
}
|
|
8323
|
+
module2.exports = {
|
|
8324
|
+
SageSDKError,
|
|
8325
|
+
SubgraphError,
|
|
8326
|
+
IPFSError,
|
|
8327
|
+
formatErrorMessage,
|
|
8328
|
+
isRetryable
|
|
8329
|
+
};
|
|
8330
|
+
}
|
|
8331
|
+
});
|
|
8332
|
+
|
|
8333
|
+
// src/services/subgraph/client.js
|
|
8334
|
+
var require_client = __commonJS({
|
|
8335
|
+
"src/services/subgraph/client.js"(exports2, module2) {
|
|
8336
|
+
var subgraph = require_subgraph();
|
|
8337
|
+
var { SimpleCache } = require_cache();
|
|
8338
|
+
var { retryWithBackoff } = require_retry();
|
|
8339
|
+
var { SubgraphError } = require_errors2();
|
|
8340
|
+
var SubgraphService = class {
|
|
8341
|
+
/**
|
|
8342
|
+
* @param {object} config - Service configuration
|
|
8343
|
+
* @param {string} config.url - Subgraph GraphQL endpoint
|
|
8344
|
+
* @param {number} [config.timeout=10000] - Request timeout in ms
|
|
8345
|
+
* @param {number} [config.retries=3] - Number of retry attempts
|
|
8346
|
+
* @param {object} [config.cache] - Cache configuration
|
|
8347
|
+
* @param {boolean} [config.cache.enabled=true] - Enable caching
|
|
8348
|
+
* @param {number} [config.cache.ttl=30000] - Cache TTL in ms
|
|
8349
|
+
* @param {number} [config.cache.maxSize=100] - Max cache entries
|
|
8350
|
+
*/
|
|
8351
|
+
constructor(config) {
|
|
8352
|
+
if (!config || !config.url) {
|
|
8353
|
+
throw new Error("SubgraphService requires a url in config");
|
|
8354
|
+
}
|
|
8355
|
+
this.url = config.url;
|
|
8356
|
+
this.timeout = config.timeout || 1e4;
|
|
8357
|
+
this.retries = config.retries || 3;
|
|
8358
|
+
this._cache = new SimpleCache({
|
|
8359
|
+
enabled: config.cache?.enabled !== false,
|
|
8360
|
+
ttl: config.cache?.ttl || 3e4,
|
|
8361
|
+
maxSize: config.cache?.maxSize || 100
|
|
8362
|
+
});
|
|
8363
|
+
}
|
|
8364
|
+
/**
|
|
8365
|
+
* Get list of SubDAOs
|
|
8366
|
+
* @param {object} [options] - Query options
|
|
8367
|
+
* @param {number} [options.limit=50] - Max results
|
|
8368
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8369
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8370
|
+
* @returns {Promise<Array>} - List of SubDAOs
|
|
8371
|
+
*/
|
|
8372
|
+
async getSubDAOs(options = {}) {
|
|
8373
|
+
const { limit = 50, skip = 0, cache = true } = options;
|
|
8374
|
+
const cacheKey = `subdaos:${limit}:${skip}`;
|
|
8375
|
+
if (cache) {
|
|
8376
|
+
const cached = this._cache.get(cacheKey);
|
|
8377
|
+
if (cached) return cached;
|
|
8378
|
+
}
|
|
8379
|
+
try {
|
|
8380
|
+
const result = await retryWithBackoff(
|
|
8381
|
+
() => this._querySubDAOs({ limit, skip }),
|
|
8382
|
+
{ attempts: this.retries }
|
|
8383
|
+
);
|
|
8384
|
+
if (cache) {
|
|
8385
|
+
this._cache.set(cacheKey, result);
|
|
8386
|
+
}
|
|
8387
|
+
return result;
|
|
8388
|
+
} catch (error) {
|
|
8389
|
+
throw new SubgraphError(
|
|
8390
|
+
`Failed to fetch SubDAOs: ${error.message}`,
|
|
8391
|
+
"QUERY_FAILED",
|
|
8392
|
+
true,
|
|
8393
|
+
error
|
|
8394
|
+
);
|
|
8395
|
+
}
|
|
8396
|
+
}
|
|
8397
|
+
/**
|
|
8398
|
+
* Get list of proposals
|
|
8399
|
+
* @param {object} [options] - Query options
|
|
8400
|
+
* @param {string} [options.governor] - Filter by governor address
|
|
8401
|
+
* @param {string} [options.subdao] - Filter by SubDAO address
|
|
8402
|
+
* @param {string[]} [options.states] - Filter by proposal states
|
|
8403
|
+
* @param {number} [options.fromTimestamp] - Filter by creation time (>=)
|
|
8404
|
+
* @param {number} [options.toTimestamp] - Filter by creation time (<=)
|
|
8405
|
+
* @param {number} [options.limit=20] - Max results
|
|
8406
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8407
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8408
|
+
* @returns {Promise<Array>} - List of proposals
|
|
8409
|
+
*/
|
|
8410
|
+
async getProposals(options = {}) {
|
|
8411
|
+
const {
|
|
8412
|
+
governor,
|
|
8413
|
+
subdao,
|
|
8414
|
+
states,
|
|
8415
|
+
fromTimestamp,
|
|
8416
|
+
toTimestamp,
|
|
8417
|
+
limit = 20,
|
|
8418
|
+
skip = 0,
|
|
8419
|
+
cache = true
|
|
8420
|
+
} = options;
|
|
8421
|
+
const cacheKey = `proposals:${JSON.stringify({
|
|
8422
|
+
governor,
|
|
8423
|
+
subdao,
|
|
8424
|
+
states,
|
|
8425
|
+
fromTimestamp,
|
|
8426
|
+
toTimestamp,
|
|
8427
|
+
limit,
|
|
8428
|
+
skip
|
|
8429
|
+
})}`;
|
|
8430
|
+
if (cache) {
|
|
8431
|
+
const cached = this._cache.get(cacheKey);
|
|
8432
|
+
if (cached) return cached;
|
|
8433
|
+
}
|
|
8434
|
+
try {
|
|
8435
|
+
const result = await retryWithBackoff(
|
|
8436
|
+
() => this._queryProposals({
|
|
8437
|
+
governor,
|
|
8438
|
+
subdao,
|
|
8439
|
+
states,
|
|
8440
|
+
fromTimestamp,
|
|
8441
|
+
toTimestamp,
|
|
8442
|
+
limit,
|
|
8443
|
+
skip
|
|
8444
|
+
}),
|
|
8445
|
+
{ attempts: this.retries }
|
|
8446
|
+
);
|
|
8447
|
+
if (cache) {
|
|
8448
|
+
this._cache.set(cacheKey, result);
|
|
8449
|
+
}
|
|
8450
|
+
return result;
|
|
8451
|
+
} catch (error) {
|
|
8452
|
+
throw new SubgraphError(
|
|
8453
|
+
`Failed to fetch proposals: ${error.message}`,
|
|
8454
|
+
"QUERY_FAILED",
|
|
8455
|
+
true,
|
|
8456
|
+
error
|
|
8457
|
+
);
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
/**
|
|
8461
|
+
* Get proposal by ID
|
|
8462
|
+
* @param {string} id - Proposal ID
|
|
8463
|
+
* @param {object} [options] - Query options
|
|
8464
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8465
|
+
* @returns {Promise<object|null>} - Proposal or null
|
|
8466
|
+
*/
|
|
8467
|
+
async getProposalById(id2, options = {}) {
|
|
8468
|
+
const { cache = true } = options;
|
|
8469
|
+
const cacheKey = `proposal:${id2}`;
|
|
8470
|
+
if (cache) {
|
|
8471
|
+
const cached = this._cache.get(cacheKey);
|
|
8472
|
+
if (cached !== null) return cached;
|
|
8473
|
+
}
|
|
8474
|
+
try {
|
|
8475
|
+
const result = await retryWithBackoff(
|
|
8476
|
+
() => subgraph.getProposalById({ url: this.url, id: id2 }),
|
|
8477
|
+
{ attempts: this.retries }
|
|
8478
|
+
);
|
|
8479
|
+
if (cache) {
|
|
8480
|
+
this._cache.set(cacheKey, result);
|
|
8481
|
+
}
|
|
8482
|
+
return result;
|
|
8483
|
+
} catch (error) {
|
|
8484
|
+
throw new SubgraphError(
|
|
8485
|
+
`Failed to fetch proposal ${id2}: ${error.message}`,
|
|
8486
|
+
"QUERY_FAILED",
|
|
8487
|
+
true,
|
|
8488
|
+
error
|
|
8489
|
+
);
|
|
8490
|
+
}
|
|
8491
|
+
}
|
|
8492
|
+
/**
|
|
8493
|
+
* Get libraries
|
|
8494
|
+
* @param {object} [options] - Query options
|
|
8495
|
+
* @param {string} [options.subdao] - Filter by SubDAO address
|
|
8496
|
+
* @param {number} [options.limit=50] - Max results
|
|
8497
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8498
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8499
|
+
* @returns {Promise<Array>} - List of libraries
|
|
8500
|
+
*/
|
|
8501
|
+
async getLibraries(options = {}) {
|
|
8502
|
+
const { subdao, limit = 50, skip = 0, cache = true } = options;
|
|
8503
|
+
const cacheKey = `libraries:${subdao || "all"}:${limit}:${skip}`;
|
|
8504
|
+
if (cache) {
|
|
8505
|
+
const cached = this._cache.get(cacheKey);
|
|
8506
|
+
if (cached) return cached;
|
|
8507
|
+
}
|
|
8508
|
+
try {
|
|
8509
|
+
const result = await retryWithBackoff(
|
|
8510
|
+
() => subgraph.listLibraries({ url: this.url, subdao, first: limit, skip }),
|
|
8511
|
+
{ attempts: this.retries }
|
|
8512
|
+
);
|
|
8513
|
+
if (cache) {
|
|
8514
|
+
this._cache.set(cacheKey, result);
|
|
8515
|
+
}
|
|
8516
|
+
return result;
|
|
8517
|
+
} catch (error) {
|
|
8518
|
+
throw new SubgraphError(
|
|
8519
|
+
`Failed to fetch libraries: ${error.message}`,
|
|
8520
|
+
"QUERY_FAILED",
|
|
8521
|
+
true,
|
|
8522
|
+
error
|
|
8523
|
+
);
|
|
8524
|
+
}
|
|
8525
|
+
}
|
|
8526
|
+
/**
|
|
8527
|
+
* Get prompts by tag
|
|
8528
|
+
* @param {object} options - Query options
|
|
8529
|
+
* @param {string} options.tagsHash - Tag hash to filter by
|
|
8530
|
+
* @param {string} [options.registry] - Filter by registry address
|
|
8531
|
+
* @param {number} [options.limit=50] - Max results
|
|
8532
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8533
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8534
|
+
* @returns {Promise<Array>} - List of prompts
|
|
8535
|
+
*/
|
|
8536
|
+
async getPromptsByTag(options) {
|
|
8537
|
+
if (!options || !options.tagsHash) {
|
|
8538
|
+
throw new Error("tagsHash is required");
|
|
8539
|
+
}
|
|
8540
|
+
const { tagsHash, registry, limit = 50, skip = 0, cache = true } = options;
|
|
8541
|
+
const cacheKey = `prompts-tag:${tagsHash}:${registry || "all"}:${limit}:${skip}`;
|
|
8542
|
+
if (cache) {
|
|
8543
|
+
const cached = this._cache.get(cacheKey);
|
|
8544
|
+
if (cached) return cached;
|
|
8545
|
+
}
|
|
8546
|
+
try {
|
|
8547
|
+
const result = await retryWithBackoff(
|
|
8548
|
+
() => subgraph.listPromptsByTag({
|
|
8549
|
+
url: this.url,
|
|
8550
|
+
tagsHash,
|
|
8551
|
+
registry,
|
|
8552
|
+
first: limit,
|
|
8553
|
+
skip
|
|
8554
|
+
}),
|
|
8555
|
+
{ attempts: this.retries }
|
|
8556
|
+
);
|
|
8557
|
+
if (cache) {
|
|
8558
|
+
this._cache.set(cacheKey, result);
|
|
8559
|
+
}
|
|
8560
|
+
return result;
|
|
8561
|
+
} catch (error) {
|
|
8562
|
+
throw new SubgraphError(
|
|
8563
|
+
`Failed to fetch prompts by tag: ${error.message}`,
|
|
8564
|
+
"QUERY_FAILED",
|
|
8565
|
+
true,
|
|
8566
|
+
error
|
|
8567
|
+
);
|
|
8568
|
+
}
|
|
8569
|
+
}
|
|
8570
|
+
/**
|
|
8571
|
+
* Get registry prompts
|
|
8572
|
+
* @param {object} options - Query options
|
|
8573
|
+
* @param {string} options.registry - Registry address
|
|
8574
|
+
* @param {number} [options.limit=50] - Max results
|
|
8575
|
+
* @param {number} [options.skip=0] - Results to skip
|
|
8576
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8577
|
+
* @returns {Promise<Array>} - List of prompts
|
|
8578
|
+
*/
|
|
8579
|
+
async getRegistryPrompts(options) {
|
|
8580
|
+
if (!options || !options.registry) {
|
|
8581
|
+
throw new Error("registry is required");
|
|
8582
|
+
}
|
|
8583
|
+
const { registry, limit = 50, skip = 0, cache = true } = options;
|
|
8584
|
+
const cacheKey = `registry-prompts:${registry}:${limit}:${skip}`;
|
|
8585
|
+
if (cache) {
|
|
8586
|
+
const cached = this._cache.get(cacheKey);
|
|
8587
|
+
if (cached) return cached;
|
|
8588
|
+
}
|
|
8589
|
+
try {
|
|
8590
|
+
const result = await retryWithBackoff(
|
|
8591
|
+
() => subgraph.listRegistryPrompts({
|
|
8592
|
+
url: this.url,
|
|
8593
|
+
registry,
|
|
8594
|
+
first: limit,
|
|
8595
|
+
skip
|
|
8596
|
+
}),
|
|
8597
|
+
{ attempts: this.retries }
|
|
8598
|
+
);
|
|
8599
|
+
if (cache) {
|
|
8600
|
+
this._cache.set(cacheKey, result);
|
|
8601
|
+
}
|
|
8602
|
+
return result;
|
|
8603
|
+
} catch (error) {
|
|
8604
|
+
throw new SubgraphError(
|
|
8605
|
+
`Failed to fetch registry prompts: ${error.message}`,
|
|
8606
|
+
"QUERY_FAILED",
|
|
8607
|
+
true,
|
|
8608
|
+
error
|
|
8609
|
+
);
|
|
8610
|
+
}
|
|
8611
|
+
}
|
|
8612
|
+
/**
|
|
8613
|
+
* Clear cache
|
|
8614
|
+
*/
|
|
8615
|
+
clearCache() {
|
|
8616
|
+
this._cache.clear();
|
|
8617
|
+
}
|
|
8618
|
+
/**
|
|
8619
|
+
* Get cache statistics
|
|
8620
|
+
* @returns {object} - Cache stats
|
|
8621
|
+
*/
|
|
8622
|
+
getCacheStats() {
|
|
8623
|
+
return this._cache.stats();
|
|
8624
|
+
}
|
|
8625
|
+
// Private methods
|
|
8626
|
+
async _querySubDAOs({ limit, skip }) {
|
|
8627
|
+
const data = await subgraph.query(
|
|
8628
|
+
this.url,
|
|
8629
|
+
`
|
|
8630
|
+
query($first: Int!, $skip: Int!) {
|
|
8631
|
+
subDAOs(first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
|
|
8632
|
+
id
|
|
8633
|
+
address
|
|
8634
|
+
name
|
|
8635
|
+
description
|
|
8636
|
+
governor
|
|
8637
|
+
registry
|
|
8638
|
+
token
|
|
8639
|
+
createdAt
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
`,
|
|
8643
|
+
{ first: limit, skip }
|
|
8644
|
+
);
|
|
8645
|
+
return data?.subDAOs || [];
|
|
8646
|
+
}
|
|
8647
|
+
async _queryProposals({ governor, subdao, states, fromTimestamp, toTimestamp, limit, skip }) {
|
|
8648
|
+
return await subgraph.listProposalsFiltered({
|
|
8649
|
+
url: this.url,
|
|
8650
|
+
governor,
|
|
8651
|
+
states,
|
|
8652
|
+
fromTimestamp,
|
|
8653
|
+
toTimestamp,
|
|
8654
|
+
first: limit,
|
|
8655
|
+
skip
|
|
8656
|
+
});
|
|
8657
|
+
}
|
|
8658
|
+
};
|
|
8659
|
+
module2.exports = { SubgraphService };
|
|
8660
|
+
}
|
|
8661
|
+
});
|
|
8662
|
+
|
|
8663
|
+
// src/services/ipfs/client.js
|
|
8664
|
+
var require_client2 = __commonJS({
|
|
8665
|
+
"src/services/ipfs/client.js"(exports2, module2) {
|
|
8666
|
+
var ipfs = require_ipfs();
|
|
8667
|
+
var { SimpleCache } = require_cache();
|
|
8668
|
+
var { retryWithBackoff } = require_retry();
|
|
8669
|
+
var { IPFSError } = require_errors2();
|
|
8670
|
+
var IPFSService = class {
|
|
8671
|
+
/**
|
|
8672
|
+
* @param {object} config - Service configuration
|
|
8673
|
+
* @param {string} config.workerBaseUrl - IPFS worker base URL
|
|
8674
|
+
* @param {string} config.gateway - Primary IPFS gateway URL
|
|
8675
|
+
* @param {object} [config.signer] - ethers v6 signer for worker auth
|
|
8676
|
+
* @param {Function} [config.getAuth] - Function to get auth credentials
|
|
8677
|
+
* @param {string} [config.workerToken] - Bearer token for worker auth
|
|
8678
|
+
* @param {number} [config.timeout=15000] - Request timeout in ms
|
|
8679
|
+
* @param {number} [config.retries=2] - Number of retry attempts
|
|
8680
|
+
* @param {object} [config.cache] - Cache configuration
|
|
8681
|
+
* @param {boolean} [config.cache.enabled=true] - Enable caching
|
|
8682
|
+
* @param {number} [config.cache.ttl=300000] - Cache TTL in ms (default 5min for immutable CIDs)
|
|
8683
|
+
* @param {number} [config.cache.maxSize=50] - Max cache entries
|
|
8684
|
+
*/
|
|
8685
|
+
constructor(config) {
|
|
8686
|
+
if (!config) {
|
|
8687
|
+
throw new Error("IPFSService requires a config object");
|
|
8688
|
+
}
|
|
8689
|
+
this.workerBaseUrl = config.workerBaseUrl;
|
|
8690
|
+
this.gateway = config.gateway;
|
|
8691
|
+
this.timeout = config.timeout || 15e3;
|
|
8692
|
+
this.retries = config.retries || 2;
|
|
8693
|
+
this._client = ipfs.createClient({
|
|
8694
|
+
workerBaseUrl: config.workerBaseUrl,
|
|
8695
|
+
gateway: config.gateway,
|
|
8696
|
+
workerSigner: config.signer,
|
|
8697
|
+
workerGetAuth: config.getAuth,
|
|
8698
|
+
workerToken: config.workerToken,
|
|
8699
|
+
timeoutMs: this.timeout,
|
|
8700
|
+
retries: this.retries
|
|
8701
|
+
});
|
|
8702
|
+
this._cache = new SimpleCache({
|
|
8703
|
+
enabled: config.cache?.enabled !== false,
|
|
8704
|
+
ttl: config.cache?.ttl || 3e5,
|
|
8705
|
+
// 5 minutes
|
|
8706
|
+
maxSize: config.cache?.maxSize || 50
|
|
8707
|
+
});
|
|
8708
|
+
}
|
|
8709
|
+
/**
|
|
8710
|
+
* Upload content to IPFS worker
|
|
8711
|
+
* @param {any} content - Content to upload (will be JSON stringified)
|
|
8712
|
+
* @param {object} [options] - Upload options
|
|
8713
|
+
* @param {string} [options.name] - Content name
|
|
8714
|
+
* @param {boolean} [options.warm=false] - Warm gateways after upload
|
|
8715
|
+
* @returns {Promise<string>} - CID of uploaded content
|
|
8716
|
+
*/
|
|
8717
|
+
async upload(content, options = {}) {
|
|
8718
|
+
const { name = "upload", warm = false } = options;
|
|
8719
|
+
try {
|
|
8720
|
+
const result = await retryWithBackoff(
|
|
8721
|
+
() => this._client.uploadJson(content, name, {
|
|
8722
|
+
provider: "worker",
|
|
8723
|
+
warm
|
|
8724
|
+
}),
|
|
8725
|
+
{ attempts: this.retries }
|
|
8726
|
+
);
|
|
8727
|
+
return result.cid;
|
|
8728
|
+
} catch (error) {
|
|
8729
|
+
throw new IPFSError(
|
|
8730
|
+
`Failed to upload content: ${error.message}`,
|
|
8731
|
+
"UPLOAD_FAILED",
|
|
8732
|
+
true,
|
|
8733
|
+
error
|
|
8734
|
+
);
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
/**
|
|
8738
|
+
* Fetch JSON content by CID with parallel gateway fetching
|
|
8739
|
+
* @param {string} cid - IPFS CID
|
|
8740
|
+
* @param {object} [options] - Fetch options
|
|
8741
|
+
* @param {boolean} [options.cache=true] - Use cache
|
|
8742
|
+
* @param {number} [options.timeout=5000] - Timeout per gateway in ms
|
|
8743
|
+
* @param {string[]} [options.extraGateways] - Additional gateways to try
|
|
8744
|
+
* @returns {Promise<any|null>} - Parsed JSON content or null
|
|
8745
|
+
*/
|
|
8746
|
+
async fetchByCID(cid, options = {}) {
|
|
8747
|
+
if (!cid) {
|
|
8748
|
+
throw new IPFSError("CID is required", "INVALID_CID", false);
|
|
8749
|
+
}
|
|
8750
|
+
const { cache = true, timeout = 5e3, extraGateways = [] } = options;
|
|
8751
|
+
if (cache) {
|
|
8752
|
+
const cached = this._cache.get(cid);
|
|
8753
|
+
if (cached) return cached;
|
|
8754
|
+
}
|
|
8755
|
+
const urls = this._client.buildGatewayUrls(cid, extraGateways);
|
|
8756
|
+
try {
|
|
8757
|
+
const result = await this._parallelFetch(urls, timeout);
|
|
8758
|
+
if (result === null) {
|
|
8759
|
+
throw new IPFSError(
|
|
8760
|
+
`All gateways failed for CID ${cid}`,
|
|
8761
|
+
"GATEWAY_FAILED",
|
|
8762
|
+
true
|
|
8763
|
+
);
|
|
8764
|
+
}
|
|
8765
|
+
if (cache) {
|
|
8766
|
+
this._cache.set(cid, result);
|
|
8767
|
+
}
|
|
8768
|
+
return result;
|
|
8769
|
+
} catch (error) {
|
|
8770
|
+
if (error instanceof IPFSError) throw error;
|
|
8771
|
+
throw new IPFSError(
|
|
8772
|
+
`Failed to fetch CID ${cid}: ${error.message}`,
|
|
8773
|
+
"GATEWAY_FAILED",
|
|
8774
|
+
true,
|
|
8775
|
+
error
|
|
8776
|
+
);
|
|
8777
|
+
}
|
|
8778
|
+
}
|
|
8779
|
+
/**
|
|
8780
|
+
* Pin CIDs to IPFS worker
|
|
8781
|
+
* @param {string|string[]} cids - CID or array of CIDs to pin
|
|
8782
|
+
* @param {object} [options] - Pin options
|
|
8783
|
+
* @param {boolean} [options.warm=false] - Warm gateways after pinning
|
|
8784
|
+
* @returns {Promise<void>}
|
|
8785
|
+
*/
|
|
8786
|
+
async pin(cids, options = {}) {
|
|
8787
|
+
const { warm = false } = options;
|
|
8788
|
+
const cidList = Array.isArray(cids) ? cids : [cids];
|
|
8789
|
+
if (cidList.length === 0) {
|
|
8790
|
+
throw new IPFSError("At least one CID is required", "INVALID_CID", false);
|
|
8791
|
+
}
|
|
8792
|
+
try {
|
|
8793
|
+
await retryWithBackoff(
|
|
8794
|
+
() => this._client.pin({ cids: cidList, warm }),
|
|
8795
|
+
{ attempts: this.retries }
|
|
8796
|
+
);
|
|
8797
|
+
} catch (error) {
|
|
8798
|
+
throw new IPFSError(
|
|
8799
|
+
`Failed to pin CIDs: ${error.message}`,
|
|
8800
|
+
"PIN_FAILED",
|
|
8801
|
+
true,
|
|
8802
|
+
error
|
|
8803
|
+
);
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
/**
|
|
8807
|
+
* Warm gateways for a CID (prefetch)
|
|
8808
|
+
* @param {string} cid - CID to warm
|
|
8809
|
+
* @param {object} [options] - Warm options
|
|
8810
|
+
* @param {string[]} [options.gateways] - Specific gateways to warm
|
|
8811
|
+
* @returns {Promise<object>} - Warm result
|
|
8812
|
+
*/
|
|
8813
|
+
async warm(cid, options = {}) {
|
|
8814
|
+
const { gateways } = options;
|
|
8815
|
+
try {
|
|
8816
|
+
return await this._client.warmGateways(cid, { gateways });
|
|
8817
|
+
} catch (error) {
|
|
8818
|
+
return { warmed: [] };
|
|
8819
|
+
}
|
|
8820
|
+
}
|
|
8821
|
+
/**
|
|
8822
|
+
* Clear cache
|
|
8823
|
+
*/
|
|
8824
|
+
clearCache() {
|
|
8825
|
+
this._cache.clear();
|
|
8826
|
+
}
|
|
8827
|
+
/**
|
|
8828
|
+
* Get cache statistics
|
|
8829
|
+
* @returns {object} - Cache stats
|
|
8830
|
+
*/
|
|
8831
|
+
getCacheStats() {
|
|
8832
|
+
return this._cache.stats();
|
|
8833
|
+
}
|
|
8834
|
+
// Private methods
|
|
8835
|
+
/**
|
|
8836
|
+
* Fetch from multiple gateways in parallel
|
|
8837
|
+
* @param {string[]} urls - Gateway URLs to try
|
|
8838
|
+
* @param {number} timeout - Timeout per gateway in ms
|
|
8839
|
+
* @returns {Promise<any|null>} - First successful result or null
|
|
8840
|
+
* @private
|
|
8841
|
+
*/
|
|
8842
|
+
async _parallelFetch(urls, timeout) {
|
|
8843
|
+
if (!urls || urls.length === 0) {
|
|
8844
|
+
return null;
|
|
8845
|
+
}
|
|
8846
|
+
const fetchOne = async (url) => {
|
|
8847
|
+
const controller = new AbortController();
|
|
8848
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
8849
|
+
try {
|
|
8850
|
+
const response = await fetch(url, {
|
|
8851
|
+
signal: controller.signal,
|
|
8852
|
+
headers: { Accept: "application/json" }
|
|
8853
|
+
});
|
|
8854
|
+
clearTimeout(timeoutId);
|
|
8855
|
+
if (!response.ok) {
|
|
8856
|
+
return null;
|
|
8857
|
+
}
|
|
8858
|
+
const text = await response.text();
|
|
8859
|
+
try {
|
|
8860
|
+
return JSON.parse(text);
|
|
8861
|
+
} catch {
|
|
8862
|
+
return text;
|
|
8863
|
+
}
|
|
8864
|
+
} catch (error) {
|
|
8865
|
+
clearTimeout(timeoutId);
|
|
8866
|
+
return null;
|
|
8867
|
+
}
|
|
8868
|
+
};
|
|
8869
|
+
const results = await Promise.allSettled(urls.map((url) => fetchOne(url)));
|
|
8870
|
+
for (const result of results) {
|
|
8871
|
+
if (result.status === "fulfilled" && result.value !== null) {
|
|
8872
|
+
return result.value;
|
|
8873
|
+
}
|
|
8874
|
+
}
|
|
8875
|
+
return null;
|
|
8876
|
+
}
|
|
8877
|
+
};
|
|
8878
|
+
module2.exports = { IPFSService };
|
|
8879
|
+
}
|
|
8880
|
+
});
|
|
8881
|
+
|
|
8882
|
+
// src/hooks/useSubDAOs.js
|
|
8883
|
+
var require_useSubDAOs = __commonJS({
|
|
8884
|
+
"src/hooks/useSubDAOs.js"(exports2, module2) {
|
|
8885
|
+
var useSWR = __require("swr");
|
|
8886
|
+
function useSubDAOs(subgraphService, options = {}) {
|
|
8887
|
+
const {
|
|
8888
|
+
limit = 50,
|
|
8889
|
+
skip = 0,
|
|
8890
|
+
cache = true,
|
|
8891
|
+
refreshInterval,
|
|
8892
|
+
revalidateOnFocus = true,
|
|
8893
|
+
revalidateOnReconnect = true
|
|
8894
|
+
} = options;
|
|
8895
|
+
const cacheKey = subgraphService ? ["subdaos", subgraphService.url, limit, skip] : null;
|
|
8896
|
+
const fetcher = async () => {
|
|
8897
|
+
if (!subgraphService) {
|
|
8898
|
+
throw new Error("SubgraphService is required");
|
|
8899
|
+
}
|
|
8900
|
+
return await subgraphService.getSubDAOs({ limit, skip, cache });
|
|
8901
|
+
};
|
|
8902
|
+
return useSWR(cacheKey, fetcher, {
|
|
8903
|
+
refreshInterval,
|
|
8904
|
+
revalidateOnFocus,
|
|
8905
|
+
revalidateOnReconnect,
|
|
8906
|
+
dedupingInterval: cache ? 3e4 : 0
|
|
8907
|
+
// Match service cache TTL
|
|
8908
|
+
});
|
|
8909
|
+
}
|
|
8910
|
+
module2.exports = { useSubDAOs };
|
|
8911
|
+
}
|
|
8912
|
+
});
|
|
8913
|
+
|
|
8914
|
+
// src/hooks/useProposals.js
|
|
8915
|
+
var require_useProposals = __commonJS({
|
|
8916
|
+
"src/hooks/useProposals.js"(exports2, module2) {
|
|
8917
|
+
var useSWR = __require("swr");
|
|
8918
|
+
function useProposals(subgraphService, options = {}) {
|
|
8919
|
+
const {
|
|
8920
|
+
governor,
|
|
8921
|
+
subdao,
|
|
8922
|
+
states,
|
|
8923
|
+
fromTimestamp,
|
|
8924
|
+
toTimestamp,
|
|
8925
|
+
limit = 20,
|
|
8926
|
+
skip = 0,
|
|
8927
|
+
cache = true,
|
|
8928
|
+
refreshInterval,
|
|
8929
|
+
revalidateOnFocus = true,
|
|
8930
|
+
revalidateOnReconnect = true
|
|
8931
|
+
} = options;
|
|
8932
|
+
const cacheKey = subgraphService ? [
|
|
8933
|
+
"proposals",
|
|
8934
|
+
subgraphService.url,
|
|
8935
|
+
governor,
|
|
8936
|
+
subdao,
|
|
8937
|
+
states?.join(","),
|
|
8938
|
+
fromTimestamp,
|
|
8939
|
+
toTimestamp,
|
|
8940
|
+
limit,
|
|
8941
|
+
skip
|
|
8942
|
+
] : null;
|
|
8943
|
+
const fetcher = async () => {
|
|
8944
|
+
if (!subgraphService) {
|
|
8945
|
+
throw new Error("SubgraphService is required");
|
|
8946
|
+
}
|
|
8947
|
+
return await subgraphService.getProposals({
|
|
8948
|
+
governor,
|
|
8949
|
+
subdao,
|
|
8950
|
+
states,
|
|
8951
|
+
fromTimestamp,
|
|
8952
|
+
toTimestamp,
|
|
8953
|
+
limit,
|
|
8954
|
+
skip,
|
|
8955
|
+
cache
|
|
8956
|
+
});
|
|
8957
|
+
};
|
|
8958
|
+
return useSWR(cacheKey, fetcher, {
|
|
8959
|
+
refreshInterval,
|
|
8960
|
+
revalidateOnFocus,
|
|
8961
|
+
revalidateOnReconnect,
|
|
8962
|
+
dedupingInterval: cache ? 3e4 : 0
|
|
8963
|
+
// Match service cache TTL
|
|
8964
|
+
});
|
|
8965
|
+
}
|
|
8966
|
+
module2.exports = { useProposals };
|
|
8967
|
+
}
|
|
8968
|
+
});
|
|
8969
|
+
|
|
8970
|
+
// src/hooks/useFetchCID.js
|
|
8971
|
+
var require_useFetchCID = __commonJS({
|
|
8972
|
+
"src/hooks/useFetchCID.js"(exports2, module2) {
|
|
8973
|
+
var useSWR = __require("swr");
|
|
8974
|
+
function useFetchCID(ipfsService, cid, options = {}) {
|
|
8975
|
+
const {
|
|
8976
|
+
cache = true,
|
|
8977
|
+
timeout = 5e3,
|
|
8978
|
+
extraGateways = [],
|
|
8979
|
+
refreshInterval,
|
|
8980
|
+
revalidateOnFocus = false,
|
|
8981
|
+
// CIDs are immutable
|
|
8982
|
+
revalidateOnReconnect = false
|
|
8983
|
+
// CIDs are immutable
|
|
8984
|
+
} = options;
|
|
8985
|
+
const cacheKey = ipfsService && cid ? ["ipfs-cid", cid] : null;
|
|
8986
|
+
const fetcher = async () => {
|
|
8987
|
+
if (!ipfsService) {
|
|
8988
|
+
throw new Error("IPFSService is required");
|
|
8989
|
+
}
|
|
8990
|
+
if (!cid) {
|
|
8991
|
+
throw new Error("CID is required");
|
|
8992
|
+
}
|
|
8993
|
+
return await ipfsService.fetchByCID(cid, {
|
|
8994
|
+
cache,
|
|
8995
|
+
timeout,
|
|
8996
|
+
extraGateways
|
|
8997
|
+
});
|
|
8998
|
+
};
|
|
8999
|
+
return useSWR(cacheKey, fetcher, {
|
|
9000
|
+
refreshInterval,
|
|
9001
|
+
revalidateOnFocus,
|
|
9002
|
+
revalidateOnReconnect,
|
|
9003
|
+
dedupingInterval: cache ? 3e5 : 0,
|
|
9004
|
+
// Match service cache TTL (5min)
|
|
9005
|
+
// CIDs are immutable, so we can cache errors too
|
|
9006
|
+
shouldRetryOnError: true,
|
|
9007
|
+
errorRetryInterval: 5e3,
|
|
9008
|
+
errorRetryCount: 3
|
|
9009
|
+
});
|
|
9010
|
+
}
|
|
9011
|
+
module2.exports = { useFetchCID };
|
|
9012
|
+
}
|
|
9013
|
+
});
|
|
9014
|
+
|
|
9015
|
+
// src/hooks/useUpload.js
|
|
9016
|
+
var require_useUpload = __commonJS({
|
|
9017
|
+
"src/hooks/useUpload.js"(exports2, module2) {
|
|
9018
|
+
var { useState, useCallback } = __require("react");
|
|
9019
|
+
function useUpload(ipfsService) {
|
|
9020
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
9021
|
+
const [error, setError] = useState(null);
|
|
9022
|
+
const [data, setData] = useState(null);
|
|
9023
|
+
const upload = useCallback(
|
|
9024
|
+
async (content, options = {}) => {
|
|
9025
|
+
if (!ipfsService) {
|
|
9026
|
+
const err = new Error("IPFSService is required");
|
|
9027
|
+
setError(err);
|
|
9028
|
+
throw err;
|
|
9029
|
+
}
|
|
9030
|
+
setIsUploading(true);
|
|
9031
|
+
setError(null);
|
|
9032
|
+
try {
|
|
9033
|
+
const cid = await ipfsService.upload(content, options);
|
|
9034
|
+
setData(cid);
|
|
9035
|
+
return cid;
|
|
9036
|
+
} catch (err) {
|
|
9037
|
+
setError(err);
|
|
9038
|
+
throw err;
|
|
9039
|
+
} finally {
|
|
9040
|
+
setIsUploading(false);
|
|
9041
|
+
}
|
|
9042
|
+
},
|
|
9043
|
+
[ipfsService]
|
|
9044
|
+
);
|
|
9045
|
+
const reset = useCallback(() => {
|
|
9046
|
+
setIsUploading(false);
|
|
9047
|
+
setError(null);
|
|
9048
|
+
setData(null);
|
|
9049
|
+
}, []);
|
|
9050
|
+
return {
|
|
9051
|
+
upload,
|
|
9052
|
+
isUploading,
|
|
9053
|
+
error,
|
|
9054
|
+
data,
|
|
9055
|
+
reset
|
|
9056
|
+
};
|
|
9057
|
+
}
|
|
9058
|
+
module2.exports = { useUpload };
|
|
9059
|
+
}
|
|
9060
|
+
});
|
|
9061
|
+
|
|
9062
|
+
// src/hooks/index.js
|
|
9063
|
+
var require_hooks = __commonJS({
|
|
9064
|
+
"src/hooks/index.js"(exports2, module2) {
|
|
9065
|
+
var { useSubDAOs } = require_useSubDAOs();
|
|
9066
|
+
var { useProposals } = require_useProposals();
|
|
9067
|
+
var { useFetchCID } = require_useFetchCID();
|
|
9068
|
+
var { useUpload } = require_useUpload();
|
|
9069
|
+
module2.exports = {
|
|
9070
|
+
useSubDAOs,
|
|
9071
|
+
useProposals,
|
|
9072
|
+
useFetchCID,
|
|
9073
|
+
useUpload
|
|
9074
|
+
};
|
|
9075
|
+
}
|
|
9076
|
+
});
|
|
9077
|
+
|
|
8096
9078
|
// src/index.js
|
|
8097
9079
|
var require_src = __commonJS({
|
|
8098
9080
|
"src/index.js"(exports2, module2) {
|
|
@@ -8133,6 +9115,16 @@ var require_src = __commonJS({
|
|
|
8133
9115
|
openzeppelin: require_openzeppelin()
|
|
8134
9116
|
}
|
|
8135
9117
|
};
|
|
9118
|
+
var { SubgraphService } = require_client();
|
|
9119
|
+
var { IPFSService } = require_client2();
|
|
9120
|
+
var serviceErrors = require_errors2();
|
|
9121
|
+
var { SimpleCache } = require_cache();
|
|
9122
|
+
var { retryWithBackoff } = require_retry();
|
|
9123
|
+
var hooks = null;
|
|
9124
|
+
try {
|
|
9125
|
+
hooks = require_hooks();
|
|
9126
|
+
} catch (err) {
|
|
9127
|
+
}
|
|
8136
9128
|
module2.exports = {
|
|
8137
9129
|
version: pkg.version,
|
|
8138
9130
|
getProvider: utils.getProvider,
|
|
@@ -8163,6 +9155,18 @@ var require_src = __commonJS({
|
|
|
8163
9155
|
errors,
|
|
8164
9156
|
doppler,
|
|
8165
9157
|
adapters,
|
|
9158
|
+
// New service layer exports
|
|
9159
|
+
services: {
|
|
9160
|
+
SubgraphService,
|
|
9161
|
+
IPFSService
|
|
9162
|
+
},
|
|
9163
|
+
serviceErrors,
|
|
9164
|
+
serviceUtils: {
|
|
9165
|
+
SimpleCache,
|
|
9166
|
+
retryWithBackoff
|
|
9167
|
+
},
|
|
9168
|
+
// React hooks (optional - requires react + swr peer dependencies)
|
|
9169
|
+
hooks,
|
|
8166
9170
|
// Legacy exports (deprecated): maintain compatibility while consumers migrate
|
|
8167
9171
|
resolveGovernanceContext: async function legacyResolveGovernanceContext(args) {
|
|
8168
9172
|
console.warn("[@sage-protocol/sdk] resolveGovernanceContext is deprecated. Use governance helpers instead.");
|