@sage-protocol/sdk 0.1.23 → 0.1.25
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 +1 -1
- package/dist/browser/index.mjs +463 -37
- package/dist/index.cjs +2277 -804
- package/dist/index.mjs +2277 -804
- package/dist/node/index.cjs +2277 -804
- package/dist/node/index.mjs +2277 -804
- package/package.json +1 -1
- package/types/index.d.ts +49 -1
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.1.
|
|
17
|
+
version: "0.1.25",
|
|
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",
|
|
@@ -106,20 +106,21 @@ var require_abi = __commonJS({
|
|
|
106
106
|
var SubDAO = [
|
|
107
107
|
"function governor() view returns (address)",
|
|
108
108
|
"function timelock() view returns (address)",
|
|
109
|
+
// SubDAO ops Safe / treasury (multisig) address. On newer deployments opsSafe() is an alias.
|
|
110
|
+
"function treasury() view returns (address)",
|
|
111
|
+
"function opsSafe() view returns (address)",
|
|
109
112
|
"function getGovernanceMode() view returns (uint8)",
|
|
113
|
+
// New governance profile (3-axis model). May be unavailable on legacy SubDAOs.
|
|
114
|
+
"function profileInitialized() view returns (bool)",
|
|
115
|
+
"function getGovernanceProfile() view returns (uint8 kind, uint8 proposalAccess, uint8 executionAccess)",
|
|
110
116
|
"function promptRegistry() view returns (address)",
|
|
111
117
|
"function sageTreasury() view returns (address)",
|
|
112
|
-
"function stakeToken() view returns (address)",
|
|
113
|
-
"function minStakeAmount() view returns (uint256)",
|
|
114
118
|
"function accessModel() view returns (uint8)",
|
|
115
119
|
"function membershipPolicy() view returns (uint8)",
|
|
116
120
|
"function profileCID() view returns (string)",
|
|
117
121
|
"function factoryDeployer() view returns (address)",
|
|
118
|
-
"function stakedAmount(address) view returns (uint256)",
|
|
119
122
|
"function userForkCount(address) view returns (uint256)",
|
|
120
123
|
"function forkCount(string) view returns (uint256)",
|
|
121
|
-
"function stake(uint256)",
|
|
122
|
-
"function unstake(uint256)",
|
|
123
124
|
// Fork functions
|
|
124
125
|
"function forkSubDAO(string,string)",
|
|
125
126
|
"function forkSubDAO(string,string,bool)",
|
|
@@ -159,7 +160,7 @@ var require_abi = __commonJS({
|
|
|
159
160
|
var FactoryWrite = [
|
|
160
161
|
"function createSubDAO(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount) returns (address subDAO, address registry)",
|
|
161
162
|
"function createSubDAOWithStable(string name, string description, uint8 accessModel, uint256 minStakeAmount, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
|
|
162
|
-
"function createSubDAOWithParams(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, uint256 votingDelay, uint256 votingPeriod, uint256 proposalThreshold, uint256 quorumPercentage, uint8 initialForkPolicy, uint8 initialMembershipPolicy, string profileCID) returns (address subDAO, address registry)",
|
|
163
|
+
"function createSubDAOWithParams(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, uint256 votingDelay, uint256 votingPeriod, uint256 proposalThreshold, uint256 quorumPercentage, uint8 initialForkPolicy, uint8 initialMembershipPolicy, string profileCID, string manifestCID, string manifestVersion) returns (address subDAO, address registry)",
|
|
163
164
|
"function createSubDAOOperatorWithStable(string name, string description, uint8 accessModel, uint256 minStakeAmount, address operatorExecutor, address operatorAdmin, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
|
|
164
165
|
"function createSubDAOOperatorWithStableAdvanced(string name, string description, uint8 accessModel, uint256 minStakeAmount, address operatorExecutor, address operatorAdmin, bool anyoneExec, bool governorProposer, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
|
|
165
166
|
"function createSubDAOOperator(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, address operatorExecutor, address operatorAdmin) returns (address subDAO, address registry)",
|
|
@@ -176,19 +177,21 @@ var require_abi = __commonJS({
|
|
|
176
177
|
"function maxCreationBurn() view returns (uint256)"
|
|
177
178
|
];
|
|
178
179
|
var LibraryRegistry = [
|
|
179
|
-
"function libraryByDAO(address) view returns (string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee)",
|
|
180
|
+
"function libraryByDAO(address) view returns (string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee, uint256 nonce)",
|
|
180
181
|
"function daoTimelock(address) view returns (address)",
|
|
181
|
-
"function getLibrary(address) view returns (tuple(string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee))",
|
|
182
|
+
"function getLibrary(address) view returns (tuple(string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee, uint256 nonce))",
|
|
182
183
|
"function getLibraryForkFee(address dao) view returns (uint256)",
|
|
183
184
|
"function setLibraryForkFee(address dao, uint256 fee)",
|
|
184
185
|
"function updateLibrary(address dao, string manifestCID, string version)",
|
|
186
|
+
"function updateLibraryCAS(address dao, string manifestCID, string version, uint256 expectedNonce)",
|
|
185
187
|
"function registerDAO(address dao, address timelock)",
|
|
186
188
|
"function registerForkedDAO(address childDAO, address childTimelock, address parentDAO, string manifestCID, string version)",
|
|
187
189
|
"function initializeLibraryFromFork(address sourceDao, address forkedDao)",
|
|
188
|
-
"event LibraryUpdated(address indexed dao, string manifestCID, address indexed timelock, string version)",
|
|
190
|
+
"event LibraryUpdated(address indexed dao, string manifestCID, address indexed timelock, string version, uint256 nonce)",
|
|
189
191
|
"event DAORegistered(address indexed dao, address indexed timelock)",
|
|
190
192
|
"event LibraryForked(address indexed sourceDao, address indexed forkedDao, string manifestCID)",
|
|
191
|
-
"event LibraryForkFeeUpdated(address indexed dao, uint256 fee)"
|
|
193
|
+
"event LibraryForkFeeUpdated(address indexed dao, uint256 fee)",
|
|
194
|
+
"error NonceMismatch(uint256 expected, uint256 actual)"
|
|
192
195
|
];
|
|
193
196
|
var PromptRegistry = [
|
|
194
197
|
"function prompts(string key) view returns (string cid, uint256 version, uint256 timestamp, address author, string forkedFromCID, address originalAuthor, bool isFork, uint256 forkDepth)",
|
|
@@ -215,14 +218,23 @@ var require_abi = __commonJS({
|
|
|
215
218
|
];
|
|
216
219
|
var Governor = [
|
|
217
220
|
"function name() view returns (string)",
|
|
221
|
+
// ERC6372 clock (Governor uses this for vote snapshots)
|
|
222
|
+
"function clock() view returns (uint48)",
|
|
218
223
|
"function timelock() view returns (address)",
|
|
219
224
|
"function _executor() view returns (address)",
|
|
220
225
|
"function sxxxToken() view returns (address)",
|
|
221
226
|
"function votingDelay() view returns (uint256)",
|
|
222
227
|
"function votingPeriod() view returns (uint256)",
|
|
223
228
|
"function proposalThreshold() view returns (uint256)",
|
|
229
|
+
"function minVotesToVote() view returns (uint256)",
|
|
230
|
+
// OZ Governor: voting power at a past timepoint
|
|
231
|
+
"function getVotes(address,uint256) view returns (uint256)",
|
|
224
232
|
// OZ Governor: quorum at a past block
|
|
225
233
|
"function quorum(uint256) view returns (uint256)",
|
|
234
|
+
// Fixed quorumVotes model (PromptGovernor v2+)
|
|
235
|
+
"function quorumVotes() view returns (uint256)",
|
|
236
|
+
"function setQuorumVotes(uint256)",
|
|
237
|
+
// Legacy OZ Governor: percentage-based quorum (deprecated)
|
|
226
238
|
"function quorumNumerator() view returns (uint256)",
|
|
227
239
|
"function proposalStakeAmount(uint256) view returns (uint256)",
|
|
228
240
|
"function state(uint256) view returns (uint8)",
|
|
@@ -235,9 +247,24 @@ var require_abi = __commonJS({
|
|
|
235
247
|
"function castVoteWithReason(uint256 proposalId, uint8 support, string reason) returns (uint256)",
|
|
236
248
|
"function queue(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash)",
|
|
237
249
|
"function execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash)",
|
|
250
|
+
"function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns (uint256)",
|
|
238
251
|
// Some Governors expose id-based queue/execute via TimelockController
|
|
239
252
|
"function queue(uint256)",
|
|
240
|
-
"function execute(uint256)"
|
|
253
|
+
"function execute(uint256)",
|
|
254
|
+
// Threshold model (PromptGovernor)
|
|
255
|
+
"function proposalProposer(uint256 proposalId) view returns (address)",
|
|
256
|
+
"function proposalThresholdSnapshot(uint256 proposalId) view returns (uint256)",
|
|
257
|
+
"function proposalCreator(uint256 proposalId) view returns (address)",
|
|
258
|
+
"function proposalCooldown() view returns (uint256)",
|
|
259
|
+
"function lastProposalTimestamp(address) view returns (uint256)",
|
|
260
|
+
"function getProposalCooldownInfo(address) view returns (bool canPropose, uint256 cooldownRemaining)",
|
|
261
|
+
"function canCreateProposal(address) view returns (bool)",
|
|
262
|
+
"function setMinVotesToVote(uint256)",
|
|
263
|
+
// Multiplier helpers
|
|
264
|
+
"function baseVotesToken() view returns (address)",
|
|
265
|
+
"function isMultiplierEnabled() view returns (bool)",
|
|
266
|
+
"function multiplierNFT() view returns (address)",
|
|
267
|
+
"function getVotingBreakdown(address) view returns (uint256 baseVotes, uint256 multiplier, uint256 effectiveVotes)"
|
|
241
268
|
];
|
|
242
269
|
var Timelock = [
|
|
243
270
|
"function getMinDelay() view returns (uint256)",
|
|
@@ -457,13 +484,11 @@ var require_abi = __commonJS({
|
|
|
457
484
|
"function governanceConfig() view returns (address)",
|
|
458
485
|
"function communityTreasury() view returns (address)",
|
|
459
486
|
"function governanceContract() view returns (address)",
|
|
460
|
-
"function libraryRegistry() view returns (address)",
|
|
461
487
|
"function subdao() view returns (address)",
|
|
462
488
|
"function soulboundBadge() view returns (address)",
|
|
463
489
|
// Admin
|
|
464
490
|
"function setGovernanceContract(address _gov)",
|
|
465
491
|
"function setCommunityTreasury(address _treasury)",
|
|
466
|
-
"function setLibraryRegistry(address _registry)",
|
|
467
492
|
"function setSoulboundBadge(address _sbtAddress)",
|
|
468
493
|
"function grantRole(bytes32 role, address account)",
|
|
469
494
|
"function revokeRole(bytes32 role, address account)",
|
|
@@ -477,7 +502,7 @@ var require_abi = __commonJS({
|
|
|
477
502
|
"event VotingStarted(uint256 indexed bountyId, uint256 votingEndTime)",
|
|
478
503
|
"event BountyCompleted(uint256 indexed bountyId, address indexed winner, uint256 reward, string deliverable)",
|
|
479
504
|
"event BountyAutoApproved(uint256 indexed bountyId, address indexed winner)",
|
|
480
|
-
"event
|
|
505
|
+
"event BountyWinnerSelected(uint256 indexed bountyId, address indexed subdao, string libraryKey, string winnerCid, address indexed winner, uint8 libraryAction)",
|
|
481
506
|
"event BountyCancelled(uint256 indexed bountyId, address indexed creator)",
|
|
482
507
|
"event BountyExpired(uint256 indexed bountyId)",
|
|
483
508
|
"event BountyRewardIncreased(uint256 indexed bountyId, uint256 additionalReward, uint256 newTotal)",
|
|
@@ -1316,8 +1341,9 @@ var require_ipfs = __commonJS({
|
|
|
1316
1341
|
baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL || ""),
|
|
1317
1342
|
uploadUrl: removeTrailingSlash(options.workerUploadUrl || env.SAGE_IPFS_UPLOAD_URL || ""),
|
|
1318
1343
|
token: options.workerUploadToken || env.SAGE_IPFS_UPLOAD_TOKEN || "",
|
|
1319
|
-
challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/auth/challenge",
|
|
1344
|
+
challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/ipfs/auth/challenge",
|
|
1320
1345
|
uploadPath: options.workerUploadPath || env.SAGE_IPFS_WORKER_UPLOAD_PATH || "/ipfs/upload",
|
|
1346
|
+
uploadRawPath: options.workerUploadRawPath || env.SAGE_IPFS_WORKER_UPLOAD_RAW_PATH || "/ipfs/upload-raw",
|
|
1321
1347
|
pinPath: options.workerPinPath || env.SAGE_IPFS_WORKER_PIN_PATH || "/ipfs/pin",
|
|
1322
1348
|
warmPath: options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH || "/ipfs/warm",
|
|
1323
1349
|
pinUrl: removeTrailingSlash(options.workerPinUrl || env.SAGE_IPFS_WORKER_PIN_URL || ""),
|
|
@@ -1325,11 +1351,13 @@ var require_ipfs = __commonJS({
|
|
|
1325
1351
|
signer: options.workerSigner || options.signer || null,
|
|
1326
1352
|
getAuth: options.workerGetAuth || null,
|
|
1327
1353
|
address: options.workerAddress || env.SAGE_SANDBOX_ADDRESS || "",
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1354
|
+
// All event types route to /trends/usage - worker tracks usage for trend scoring
|
|
1355
|
+
discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/trends/usage",
|
|
1356
|
+
discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/trends/usage",
|
|
1357
|
+
discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/trends/usage",
|
|
1331
1358
|
discoveryFeedPath: options.workerDiscoveryFeedPath || env.SAGE_IPFS_WORKER_DISCOVERY_FEED_PATH || "/discover",
|
|
1332
|
-
|
|
1359
|
+
// Metrics uses /discover/stats which returns index statistics
|
|
1360
|
+
discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/stats",
|
|
1333
1361
|
governanceReportPath: options.workerGovernanceReportPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORT_PATH || "/governance/report",
|
|
1334
1362
|
governanceReportsPath: options.workerGovernanceReportsPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORTS_PATH || "/governance/reports",
|
|
1335
1363
|
governanceReviewPath: options.workerGovernanceReviewPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REVIEW_PATH || "/governance/report/review",
|
|
@@ -1363,8 +1391,9 @@ var require_ipfs = __commonJS({
|
|
|
1363
1391
|
const base = workerBaseUrl();
|
|
1364
1392
|
const ensure = (path) => path.startsWith("/") ? path : `/${path}`;
|
|
1365
1393
|
if (base) {
|
|
1366
|
-
if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/auth/challenge")}`;
|
|
1394
|
+
if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/ipfs/auth/challenge")}`;
|
|
1367
1395
|
if (kind === "upload") return `${base}${ensure(config.worker.uploadPath || "/ipfs/upload")}`;
|
|
1396
|
+
if (kind === "upload-raw") return `${base}${ensure(config.worker.uploadRawPath || "/ipfs/upload-raw")}`;
|
|
1368
1397
|
if (kind === "pin") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}`;
|
|
1369
1398
|
if (kind === "warm") return `${base}${ensure(config.worker.warmPath || "/ipfs/warm")}`;
|
|
1370
1399
|
if (kind === "status") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}/${cid}`;
|
|
@@ -1399,6 +1428,26 @@ var require_ipfs = __commonJS({
|
|
|
1399
1428
|
const response = await axiosInstance.get(url, { headers, params, timeout: config.timeoutMs });
|
|
1400
1429
|
return response?.data;
|
|
1401
1430
|
}
|
|
1431
|
+
async function withRetry(fn, { retries = 2, baseMs = 500 } = {}) {
|
|
1432
|
+
let attempt = 0;
|
|
1433
|
+
let lastErr;
|
|
1434
|
+
while (attempt <= retries) {
|
|
1435
|
+
try {
|
|
1436
|
+
return await fn();
|
|
1437
|
+
} catch (e) {
|
|
1438
|
+
lastErr = e;
|
|
1439
|
+
const status = e?.response?.status;
|
|
1440
|
+
if (status && status >= 400 && status < 500 && status !== 429) {
|
|
1441
|
+
throw e;
|
|
1442
|
+
}
|
|
1443
|
+
if (attempt === retries) break;
|
|
1444
|
+
const delay = baseMs * Math.pow(2, attempt);
|
|
1445
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1446
|
+
attempt++;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
throw lastErr;
|
|
1450
|
+
}
|
|
1402
1451
|
async function buildWorkerAuthHeaders(extraHeaders = {}) {
|
|
1403
1452
|
if (config.worker.token) {
|
|
1404
1453
|
return { headers: { ...extraHeaders, Authorization: `Bearer ${config.worker.token}` }, auth: null };
|
|
@@ -1471,6 +1520,51 @@ var require_ipfs = __commonJS({
|
|
|
1471
1520
|
if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
|
|
1472
1521
|
return { cid, provider: "worker", response: response?.data || null };
|
|
1473
1522
|
}
|
|
1523
|
+
async function uploadRawViaWorker(content, { name, warm, gateways } = {}) {
|
|
1524
|
+
const { headers, auth } = await buildWorkerAuthHeaders({ "Content-Type": "application/json" });
|
|
1525
|
+
const body = { content };
|
|
1526
|
+
if (auth) body.auth = auth;
|
|
1527
|
+
if (name) body.name = name;
|
|
1528
|
+
const response = await axiosInstance.post(workerUrl("upload-raw"), body, { headers, timeout: config.timeoutMs });
|
|
1529
|
+
const cid = response?.data?.cid || response?.data?.IpfsHash;
|
|
1530
|
+
if (!cid) {
|
|
1531
|
+
throw new Error("Worker response missing cid");
|
|
1532
|
+
}
|
|
1533
|
+
if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
|
|
1534
|
+
return { cid, provider: "worker", response: response?.data || null };
|
|
1535
|
+
}
|
|
1536
|
+
async function uploadRawJson(content, options2 = {}) {
|
|
1537
|
+
if (!content || typeof content !== "object") {
|
|
1538
|
+
throw new Error("content object required");
|
|
1539
|
+
}
|
|
1540
|
+
const { providers, provider, warm, gateways, name = "manifest.json" } = options2;
|
|
1541
|
+
const order = resolveProviderOrder(providers || provider);
|
|
1542
|
+
if (!order.length) {
|
|
1543
|
+
throw new Error("No IPFS providers configured");
|
|
1544
|
+
}
|
|
1545
|
+
const errors = [];
|
|
1546
|
+
for (const candidate of order) {
|
|
1547
|
+
try {
|
|
1548
|
+
if (candidate === "worker") {
|
|
1549
|
+
return await uploadRawViaWorker(content, { name, warm, gateways });
|
|
1550
|
+
}
|
|
1551
|
+
if (candidate === "pinata") {
|
|
1552
|
+
return await uploadToPinata(content, { filename: name, warm, gateways });
|
|
1553
|
+
}
|
|
1554
|
+
if (candidate === "web3") {
|
|
1555
|
+
return await uploadToWeb3Storage(content, { warm, gateways });
|
|
1556
|
+
}
|
|
1557
|
+
if (candidate === "simulate") {
|
|
1558
|
+
return await uploadSimulated(content, { warm, gateways });
|
|
1559
|
+
}
|
|
1560
|
+
} catch (error2) {
|
|
1561
|
+
errors.push({ provider: candidate, error: error2 });
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
const error = new Error("All IPFS providers failed");
|
|
1565
|
+
error.details = errors;
|
|
1566
|
+
throw error;
|
|
1567
|
+
}
|
|
1474
1568
|
async function uploadToPinata(payload, { filename = "payload.json", metadata, warm, gateways } = {}) {
|
|
1475
1569
|
if (!hasPinataCreds()) {
|
|
1476
1570
|
throw new Error("Missing Pinata credentials");
|
|
@@ -1675,28 +1769,28 @@ var require_ipfs = __commonJS({
|
|
|
1675
1769
|
}
|
|
1676
1770
|
async function recordDiscoveryEvent(event = {}) {
|
|
1677
1771
|
if (!event || typeof event !== "object") throw new Error("event required");
|
|
1678
|
-
const
|
|
1679
|
-
if (!
|
|
1680
|
-
const payload = { ...event,
|
|
1681
|
-
return postWorkerJson("discoveryEventsPath", "/
|
|
1682
|
-
}
|
|
1683
|
-
async function recordMcpUsageEvent({ promptId, metadata, address } = {}) {
|
|
1684
|
-
const id = typeof promptId === "string" ? promptId.trim() : "";
|
|
1685
|
-
if (!id) throw new Error("promptId required");
|
|
1686
|
-
const payload = {
|
|
1772
|
+
const cid = typeof event.promptId === "string" ? event.promptId.trim() : typeof event.cid === "string" ? event.cid.trim() : "";
|
|
1773
|
+
if (!cid) throw new Error("promptId or cid required");
|
|
1774
|
+
const payload = { ...event, cid };
|
|
1775
|
+
return withRetry(() => postWorkerJson("discoveryEventsPath", "/trends/usage", payload));
|
|
1776
|
+
}
|
|
1777
|
+
async function recordMcpUsageEvent({ promptId, cid, metadata, address } = {}) {
|
|
1778
|
+
const id = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
|
|
1779
|
+
if (!id) throw new Error("promptId or cid required");
|
|
1780
|
+
const payload = { cid: id };
|
|
1687
1781
|
if (metadata && typeof metadata === "object") payload.metadata = metadata;
|
|
1688
1782
|
const actor = address || config.worker.address;
|
|
1689
1783
|
if (actor) payload.address = actor;
|
|
1690
|
-
return postWorkerJson("discoveryMcpPath", "/
|
|
1784
|
+
return withRetry(() => postWorkerJson("discoveryMcpPath", "/trends/usage", payload));
|
|
1691
1785
|
}
|
|
1692
|
-
async function recordLaunchEvent({ promptId, metadata, address } = {}) {
|
|
1693
|
-
const id = typeof promptId === "string" ? promptId.trim() : "";
|
|
1694
|
-
if (!id) throw new Error("promptId required");
|
|
1695
|
-
const payload = {
|
|
1786
|
+
async function recordLaunchEvent({ promptId, cid, metadata, address } = {}) {
|
|
1787
|
+
const id = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
|
|
1788
|
+
if (!id) throw new Error("promptId or cid required");
|
|
1789
|
+
const payload = { cid: id };
|
|
1696
1790
|
if (metadata && typeof metadata === "object") payload.metadata = metadata;
|
|
1697
1791
|
const actor = address || config.worker.address;
|
|
1698
1792
|
if (actor) payload.address = actor;
|
|
1699
|
-
return postWorkerJson("discoveryLaunchPath", "/
|
|
1793
|
+
return withRetry(() => postWorkerJson("discoveryLaunchPath", "/trends/usage", payload));
|
|
1700
1794
|
}
|
|
1701
1795
|
async function submitGovernanceReport(payload = {}) {
|
|
1702
1796
|
if (!payload || typeof payload !== "object") throw new Error("payload required");
|
|
@@ -1758,6 +1852,7 @@ var require_ipfs = __commonJS({
|
|
|
1758
1852
|
uploadPrompt,
|
|
1759
1853
|
uploadJson,
|
|
1760
1854
|
uploadText,
|
|
1855
|
+
uploadRawJson,
|
|
1761
1856
|
downloadJson,
|
|
1762
1857
|
warmGateways,
|
|
1763
1858
|
buildGatewayUrls: (cid, extra) => buildGatewayUrls(cid, config.gateway, extra),
|
|
@@ -1784,7 +1879,7 @@ var require_ipfs = __commonJS({
|
|
|
1784
1879
|
const timeoutMs = Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3);
|
|
1785
1880
|
const challengePath = ensureLeadingSlash(
|
|
1786
1881
|
options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH,
|
|
1787
|
-
"/auth/challenge"
|
|
1882
|
+
"/ipfs/auth/challenge"
|
|
1788
1883
|
);
|
|
1789
1884
|
const warmPath = ensureLeadingSlash(
|
|
1790
1885
|
options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH,
|
|
@@ -1851,6 +1946,335 @@ var require_ipfs = __commonJS({
|
|
|
1851
1946
|
}
|
|
1852
1947
|
});
|
|
1853
1948
|
|
|
1949
|
+
// src/browser/reputation.js
|
|
1950
|
+
var require_reputation = __commonJS({
|
|
1951
|
+
"src/browser/reputation.js"(exports, module) {
|
|
1952
|
+
var { getAddress } = require_utils();
|
|
1953
|
+
var subgraph = require_subgraph();
|
|
1954
|
+
function safeGetAddress(value) {
|
|
1955
|
+
try {
|
|
1956
|
+
return getAddress(value);
|
|
1957
|
+
} catch {
|
|
1958
|
+
return null;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
function clampInt(value, { min = 1, max = 100, fallback = 100 } = {}) {
|
|
1962
|
+
const n = Number(value);
|
|
1963
|
+
if (!Number.isFinite(n)) return fallback;
|
|
1964
|
+
return Math.min(max, Math.max(min, Math.trunc(n)));
|
|
1965
|
+
}
|
|
1966
|
+
function toBigIntString(value, fallback = "0") {
|
|
1967
|
+
try {
|
|
1968
|
+
if (typeof value === "bigint") return value.toString();
|
|
1969
|
+
if (typeof value === "number") return String(Math.trunc(value));
|
|
1970
|
+
const str = String(value).trim();
|
|
1971
|
+
if (!str) return fallback;
|
|
1972
|
+
if (!/^-?\d+$/.test(str)) return fallback;
|
|
1973
|
+
return str;
|
|
1974
|
+
} catch {
|
|
1975
|
+
return fallback;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
function mapSafe(list, mapper) {
|
|
1979
|
+
const out = [];
|
|
1980
|
+
for (const item of list || []) {
|
|
1981
|
+
try {
|
|
1982
|
+
const v = mapper(item);
|
|
1983
|
+
if (v != null) out.push(v);
|
|
1984
|
+
} catch {
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
return out;
|
|
1988
|
+
}
|
|
1989
|
+
async function getBadgesByRecipient({ url, recipient, first = 50 }) {
|
|
1990
|
+
if (!url) throw new Error("subgraph url required");
|
|
1991
|
+
if (!recipient) throw new Error("recipient required");
|
|
1992
|
+
const addr = safeGetAddress(recipient);
|
|
1993
|
+
if (!addr) throw new Error("invalid recipient address");
|
|
1994
|
+
const doc = `
|
|
1995
|
+
query($recipient: Bytes!, $first: Int!) {
|
|
1996
|
+
soulboundBadges(
|
|
1997
|
+
where: { recipient: $recipient }
|
|
1998
|
+
first: $first
|
|
1999
|
+
orderBy: blockTimestamp
|
|
2000
|
+
orderDirection: desc
|
|
2001
|
+
) {
|
|
2002
|
+
id
|
|
2003
|
+
contract
|
|
2004
|
+
badgeId
|
|
2005
|
+
recipient
|
|
2006
|
+
evidenceURI
|
|
2007
|
+
blockNumber
|
|
2008
|
+
blockTimestamp
|
|
2009
|
+
transactionHash
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
`;
|
|
2013
|
+
const data = await subgraph.query(url, doc, {
|
|
2014
|
+
recipient: addr.toLowerCase(),
|
|
2015
|
+
first: clampInt(first, { min: 1, max: 100, fallback: 50 })
|
|
2016
|
+
});
|
|
2017
|
+
return mapSafe(data?.soulboundBadges, (row) => {
|
|
2018
|
+
const contract = safeGetAddress(row.contract);
|
|
2019
|
+
const recipientAddr = safeGetAddress(row.recipient);
|
|
2020
|
+
if (!contract || !recipientAddr) return null;
|
|
2021
|
+
return {
|
|
2022
|
+
id: String(row.id),
|
|
2023
|
+
contract,
|
|
2024
|
+
badgeId: toBigIntString(row.badgeId, "0"),
|
|
2025
|
+
recipient: recipientAddr,
|
|
2026
|
+
evidenceURI: row.evidenceURI || null,
|
|
2027
|
+
blockNumber: Number(row.blockNumber || 0),
|
|
2028
|
+
blockTimestamp: Number(row.blockTimestamp || 0),
|
|
2029
|
+
transactionHash: row.transactionHash || null
|
|
2030
|
+
};
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
async function listContributions({ url, contributor, subdao, first = 50, orderBy = "updatedAt", orderDirection = "desc" }) {
|
|
2034
|
+
if (!url) throw new Error("subgraph url required");
|
|
2035
|
+
const filters = [];
|
|
2036
|
+
if (contributor) {
|
|
2037
|
+
const addr = safeGetAddress(contributor);
|
|
2038
|
+
if (!addr) throw new Error("invalid contributor address");
|
|
2039
|
+
filters.push(`contributor: "${addr.toLowerCase()}"`);
|
|
2040
|
+
}
|
|
2041
|
+
if (subdao) {
|
|
2042
|
+
const addr = safeGetAddress(subdao);
|
|
2043
|
+
if (!addr) throw new Error("invalid subdao address");
|
|
2044
|
+
filters.push(`dao: "${addr.toLowerCase()}"`);
|
|
2045
|
+
}
|
|
2046
|
+
const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
|
|
2047
|
+
const safeOrderBy = ["updatedAt", "createdAt"].includes(orderBy) ? orderBy : "updatedAt";
|
|
2048
|
+
const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
|
|
2049
|
+
const doc = `
|
|
2050
|
+
query($first: Int!) {
|
|
2051
|
+
promptContributions(
|
|
2052
|
+
${where}
|
|
2053
|
+
first: $first
|
|
2054
|
+
orderBy: ${safeOrderBy}
|
|
2055
|
+
orderDirection: ${safeOrderDirection}
|
|
2056
|
+
) {
|
|
2057
|
+
id
|
|
2058
|
+
dao
|
|
2059
|
+
promptKey
|
|
2060
|
+
contributor
|
|
2061
|
+
cid
|
|
2062
|
+
proposalId
|
|
2063
|
+
forVotes
|
|
2064
|
+
againstVotes
|
|
2065
|
+
abstainVotes
|
|
2066
|
+
uniqueVoters
|
|
2067
|
+
quorum
|
|
2068
|
+
fromBounty
|
|
2069
|
+
bountyId
|
|
2070
|
+
badgeId
|
|
2071
|
+
badgeEvidenceURI
|
|
2072
|
+
createdAt
|
|
2073
|
+
updatedAt
|
|
2074
|
+
transactionHash
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
`;
|
|
2078
|
+
const data = await subgraph.query(url, doc, {
|
|
2079
|
+
first: clampInt(first, { min: 1, max: 100, fallback: 50 })
|
|
2080
|
+
});
|
|
2081
|
+
return mapSafe(data?.promptContributions, (row) => {
|
|
2082
|
+
const dao = safeGetAddress(row.dao);
|
|
2083
|
+
const contributorAddr = safeGetAddress(row.contributor);
|
|
2084
|
+
if (!dao) return null;
|
|
2085
|
+
return {
|
|
2086
|
+
id: String(row.id),
|
|
2087
|
+
dao,
|
|
2088
|
+
promptKey: String(row.promptKey),
|
|
2089
|
+
contributor: contributorAddr || "0x0000000000000000000000000000000000000000",
|
|
2090
|
+
cid: String(row.cid),
|
|
2091
|
+
timestamp: Number(row.updatedAt || 0),
|
|
2092
|
+
createdAt: Number(row.createdAt || 0),
|
|
2093
|
+
updatedAt: Number(row.updatedAt || 0),
|
|
2094
|
+
transactionHash: row.transactionHash || null,
|
|
2095
|
+
proposalId: row.proposalId != null ? toBigIntString(row.proposalId) : null,
|
|
2096
|
+
forVotes: row.forVotes != null ? toBigIntString(row.forVotes) : null,
|
|
2097
|
+
againstVotes: row.againstVotes != null ? toBigIntString(row.againstVotes) : null,
|
|
2098
|
+
abstainVotes: row.abstainVotes != null ? toBigIntString(row.abstainVotes) : null,
|
|
2099
|
+
uniqueVoters: row.uniqueVoters != null ? Number(row.uniqueVoters) : null,
|
|
2100
|
+
quorum: row.quorum != null ? toBigIntString(row.quorum) : null,
|
|
2101
|
+
fromBounty: Boolean(row.fromBounty),
|
|
2102
|
+
bountyId: row.bountyId != null ? toBigIntString(row.bountyId) : null,
|
|
2103
|
+
badgeId: row.badgeId != null ? toBigIntString(row.badgeId) : null,
|
|
2104
|
+
badgeEvidenceURI: row.badgeEvidenceURI || null
|
|
2105
|
+
};
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
function computeAggregates(contributions) {
|
|
2109
|
+
if (!contributions || contributions.length === 0) {
|
|
2110
|
+
return {
|
|
2111
|
+
totalContributions: 0,
|
|
2112
|
+
uniqueContributors: 0,
|
|
2113
|
+
bountyOriginated: 0,
|
|
2114
|
+
governanceOriginated: 0,
|
|
2115
|
+
totalForVotes: "0",
|
|
2116
|
+
totalAgainstVotes: "0",
|
|
2117
|
+
averageVoterCount: 0,
|
|
2118
|
+
badgeCount: 0
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
const contributorSet = /* @__PURE__ */ new Set();
|
|
2122
|
+
let bountyOriginated = 0;
|
|
2123
|
+
let governanceOriginated = 0;
|
|
2124
|
+
let totalForVotes = 0n;
|
|
2125
|
+
let totalAgainstVotes = 0n;
|
|
2126
|
+
let voterCountSum = 0;
|
|
2127
|
+
let voterCountN = 0;
|
|
2128
|
+
let badgeCount = 0;
|
|
2129
|
+
for (const c of contributions) {
|
|
2130
|
+
if (c.contributor) contributorSet.add(c.contributor.toLowerCase());
|
|
2131
|
+
if (c.fromBounty) {
|
|
2132
|
+
bountyOriginated++;
|
|
2133
|
+
} else {
|
|
2134
|
+
governanceOriginated++;
|
|
2135
|
+
}
|
|
2136
|
+
if (c.forVotes != null) {
|
|
2137
|
+
try {
|
|
2138
|
+
totalForVotes += BigInt(c.forVotes);
|
|
2139
|
+
} catch {
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
if (c.againstVotes != null) {
|
|
2143
|
+
try {
|
|
2144
|
+
totalAgainstVotes += BigInt(c.againstVotes);
|
|
2145
|
+
} catch {
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
if (c.uniqueVoters != null) {
|
|
2149
|
+
voterCountSum += c.uniqueVoters;
|
|
2150
|
+
voterCountN++;
|
|
2151
|
+
}
|
|
2152
|
+
if (c.badgeId != null) badgeCount++;
|
|
2153
|
+
}
|
|
2154
|
+
return {
|
|
2155
|
+
totalContributions: contributions.length,
|
|
2156
|
+
uniqueContributors: contributorSet.size,
|
|
2157
|
+
bountyOriginated,
|
|
2158
|
+
governanceOriginated,
|
|
2159
|
+
totalForVotes: totalForVotes.toString(),
|
|
2160
|
+
totalAgainstVotes: totalAgainstVotes.toString(),
|
|
2161
|
+
averageVoterCount: voterCountN > 0 ? Math.round(voterCountSum / voterCountN) : 0,
|
|
2162
|
+
badgeCount
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
async function getByAddress({
|
|
2166
|
+
url,
|
|
2167
|
+
address,
|
|
2168
|
+
subdao,
|
|
2169
|
+
includeBadges = true,
|
|
2170
|
+
includeContributions = true,
|
|
2171
|
+
first = 100
|
|
2172
|
+
} = {}) {
|
|
2173
|
+
if (!url) throw new Error("subgraph url required");
|
|
2174
|
+
if (!address) throw new Error("address required");
|
|
2175
|
+
const addr = safeGetAddress(address);
|
|
2176
|
+
if (!addr) throw new Error("invalid address");
|
|
2177
|
+
const subdaoAddr = subdao ? safeGetAddress(subdao) : null;
|
|
2178
|
+
if (subdao && !subdaoAddr) throw new Error("invalid subdao address");
|
|
2179
|
+
const limit = clampInt(first, { min: 1, max: 200, fallback: 100 });
|
|
2180
|
+
let badges = void 0;
|
|
2181
|
+
if (includeBadges) {
|
|
2182
|
+
badges = await getBadgesByRecipient({ url, recipient: addr, first: limit });
|
|
2183
|
+
}
|
|
2184
|
+
let contributionAggregates = void 0;
|
|
2185
|
+
let lastContributionAt = null;
|
|
2186
|
+
if (includeContributions) {
|
|
2187
|
+
const rows = await listContributions({
|
|
2188
|
+
url,
|
|
2189
|
+
contributor: addr,
|
|
2190
|
+
subdao: subdaoAddr || void 0,
|
|
2191
|
+
first: limit,
|
|
2192
|
+
orderBy: "updatedAt",
|
|
2193
|
+
orderDirection: "desc"
|
|
2194
|
+
});
|
|
2195
|
+
if (rows && rows.length) {
|
|
2196
|
+
const updatedAt = rows[0]?.updatedAt ?? rows[0]?.timestamp ?? null;
|
|
2197
|
+
lastContributionAt = updatedAt != null ? Number(updatedAt) : null;
|
|
2198
|
+
if (!Number.isFinite(lastContributionAt)) lastContributionAt = null;
|
|
2199
|
+
}
|
|
2200
|
+
contributionAggregates = computeAggregates(rows || []);
|
|
2201
|
+
}
|
|
2202
|
+
const signals = {
|
|
2203
|
+
badgeCount: includeBadges ? badges ? badges.length : 0 : null,
|
|
2204
|
+
totalContributions: includeContributions ? contributionAggregates ? contributionAggregates.totalContributions : 0 : null,
|
|
2205
|
+
governanceOriginated: includeContributions ? contributionAggregates ? contributionAggregates.governanceOriginated : 0 : null,
|
|
2206
|
+
bountyOriginated: includeContributions ? contributionAggregates ? contributionAggregates.bountyOriginated : 0 : null,
|
|
2207
|
+
lastContributionAt: includeContributions ? lastContributionAt : null
|
|
2208
|
+
};
|
|
2209
|
+
const out = {
|
|
2210
|
+
address: addr,
|
|
2211
|
+
subdao: subdaoAddr || null,
|
|
2212
|
+
signals
|
|
2213
|
+
};
|
|
2214
|
+
if (includeBadges) out.badges = badges;
|
|
2215
|
+
if (includeContributions) {
|
|
2216
|
+
out.contributionAggregates = contributionAggregates;
|
|
2217
|
+
out.lastContributionAt = lastContributionAt;
|
|
2218
|
+
}
|
|
2219
|
+
return out;
|
|
2220
|
+
}
|
|
2221
|
+
function normalizeGateInt(value, name) {
|
|
2222
|
+
if (value === void 0 || value === null) return null;
|
|
2223
|
+
const n = Number(value);
|
|
2224
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
2225
|
+
throw new Error(`invalid gate: ${name}`);
|
|
2226
|
+
}
|
|
2227
|
+
return Math.trunc(n);
|
|
2228
|
+
}
|
|
2229
|
+
function normalizeRequiredBadgeIds(value) {
|
|
2230
|
+
const list = Array.isArray(value) ? value : value != null ? [value] : [];
|
|
2231
|
+
const out = [];
|
|
2232
|
+
for (const item of list) {
|
|
2233
|
+
const id = toBigIntString(item, "");
|
|
2234
|
+
if (id) out.push(id);
|
|
2235
|
+
}
|
|
2236
|
+
return out;
|
|
2237
|
+
}
|
|
2238
|
+
function evaluate(result, gates = {}) {
|
|
2239
|
+
const reasons = [];
|
|
2240
|
+
const minBadges = normalizeGateInt(gates.minBadges, "minBadges");
|
|
2241
|
+
const minContributions = normalizeGateInt(gates.minContributions, "minContributions");
|
|
2242
|
+
const requiredBadges = normalizeRequiredBadgeIds(gates.requireBadges ?? gates.requireBadge);
|
|
2243
|
+
const badgeCount = Number(result?.signals?.badgeCount ?? (result?.badges?.length ?? 0));
|
|
2244
|
+
const totalContributions = Number(result?.signals?.totalContributions ?? (result?.contributionAggregates?.totalContributions ?? 0));
|
|
2245
|
+
if (minBadges != null) {
|
|
2246
|
+
if (!Number.isFinite(badgeCount)) {
|
|
2247
|
+
reasons.push("badges_unavailable");
|
|
2248
|
+
} else if (badgeCount < minBadges) {
|
|
2249
|
+
reasons.push(`minBadges:${badgeCount}<${minBadges}`);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
if (minContributions != null) {
|
|
2253
|
+
if (!Number.isFinite(totalContributions)) {
|
|
2254
|
+
reasons.push("contributions_unavailable");
|
|
2255
|
+
} else if (totalContributions < minContributions) {
|
|
2256
|
+
reasons.push(`minContributions:${totalContributions}<${minContributions}`);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
if (requiredBadges.length) {
|
|
2260
|
+
const have = new Set((result?.badges || []).map((b) => toBigIntString(b?.badgeId, "")).filter(Boolean));
|
|
2261
|
+
for (const req of requiredBadges) {
|
|
2262
|
+
if (!have.has(req)) reasons.push(`missingBadge:${req}`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return { ok: reasons.length === 0, reasons };
|
|
2266
|
+
}
|
|
2267
|
+
module.exports = {
|
|
2268
|
+
getByAddress,
|
|
2269
|
+
evaluate,
|
|
2270
|
+
// Expose lower-level functions for advanced usage
|
|
2271
|
+
getBadgesByRecipient,
|
|
2272
|
+
listContributions,
|
|
2273
|
+
computeAggregates
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
|
|
1854
2278
|
// src/errors/index.js
|
|
1855
2279
|
var require_errors = __commonJS({
|
|
1856
2280
|
"src/errors/index.js"(exports, module) {
|
|
@@ -2256,6 +2680,7 @@ var require_browser = __commonJS({
|
|
|
2256
2680
|
var governance = require_governance();
|
|
2257
2681
|
var subgraph = require_subgraph();
|
|
2258
2682
|
var ipfs = require_ipfs();
|
|
2683
|
+
var reputation = require_reputation();
|
|
2259
2684
|
var serviceErrors = require_errors();
|
|
2260
2685
|
var { SimpleCache } = require_cache();
|
|
2261
2686
|
var { retryWithBackoff } = require_retry();
|
|
@@ -2276,6 +2701,7 @@ var require_browser = __commonJS({
|
|
|
2276
2701
|
// Browser-safe modules (no ethers dependencies)
|
|
2277
2702
|
subgraph,
|
|
2278
2703
|
ipfs,
|
|
2704
|
+
reputation,
|
|
2279
2705
|
// Service utilities
|
|
2280
2706
|
utils: {
|
|
2281
2707
|
SimpleCache,
|