@sage-protocol/sdk 0.1.23 → 0.1.24
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 +1916 -774
- package/dist/index.mjs +1916 -774
- package/dist/node/index.cjs +1916 -774
- package/dist/node/index.mjs +1916 -774
- package/package.json +1 -1
- package/types/index.d.ts +49 -1
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.1.
|
|
17
|
+
version: "0.1.24",
|
|
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)",
|
|
@@ -1571,193 +1596,496 @@ var require_transports = __commonJS({
|
|
|
1571
1596
|
}
|
|
1572
1597
|
});
|
|
1573
1598
|
|
|
1574
|
-
// src/
|
|
1575
|
-
var
|
|
1576
|
-
"src/
|
|
1577
|
-
function
|
|
1578
|
-
if (!
|
|
1579
|
-
const
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1588
|
-
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1589
|
-
return `0x${hashHex}`;
|
|
1590
|
-
}
|
|
1591
|
-
function keccak256Sync(data) {
|
|
1592
|
-
const str = typeof data === "string" ? data : JSON.stringify(data);
|
|
1593
|
-
let hash = 0;
|
|
1594
|
-
for (let i = 0; i < str.length; i++) {
|
|
1595
|
-
const char = str.charCodeAt(i);
|
|
1596
|
-
hash = (hash << 5) - hash + char;
|
|
1597
|
-
hash = hash & hash;
|
|
1599
|
+
// src/utils/logs.js
|
|
1600
|
+
var require_logs = __commonJS({
|
|
1601
|
+
"src/utils/logs.js"(exports2, module2) {
|
|
1602
|
+
async function fetchLogsChunked({ provider, address, topics = [], fromBlock = 0, toBlock = "latest", chunkSize = 5e4 }) {
|
|
1603
|
+
if (!provider) throw new Error("provider required");
|
|
1604
|
+
const latest = toBlock === "latest" ? await provider.getBlockNumber() : Number(toBlock);
|
|
1605
|
+
let start = Number(fromBlock);
|
|
1606
|
+
const logs = [];
|
|
1607
|
+
while (start <= latest) {
|
|
1608
|
+
const end = Math.min(start + chunkSize - 1, latest);
|
|
1609
|
+
const chunk = await provider.getLogs({ address, topics, fromBlock: start, toBlock: end });
|
|
1610
|
+
logs.push(...chunk);
|
|
1611
|
+
start = end + 1;
|
|
1598
1612
|
}
|
|
1599
|
-
|
|
1600
|
-
return `0x${hex.repeat(8)}`.slice(0, 66);
|
|
1613
|
+
return logs;
|
|
1601
1614
|
}
|
|
1602
1615
|
module2.exports = {
|
|
1603
|
-
|
|
1604
|
-
keccak256Async,
|
|
1605
|
-
keccak256Sync
|
|
1616
|
+
fetchLogsChunked
|
|
1606
1617
|
};
|
|
1607
1618
|
}
|
|
1608
1619
|
});
|
|
1609
1620
|
|
|
1610
|
-
// src/
|
|
1611
|
-
var
|
|
1612
|
-
"src/
|
|
1613
|
-
var { getAddress } =
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
const v = (direction || "").toString().toLowerCase();
|
|
1620
|
-
return v === "asc" ? "asc" : "desc";
|
|
1621
|
-
}
|
|
1622
|
-
function safeGetAddress(value) {
|
|
1621
|
+
// src/subdao/index.js
|
|
1622
|
+
var require_subdao = __commonJS({
|
|
1623
|
+
"src/subdao/index.js"(exports2, module2) {
|
|
1624
|
+
var { Contract, Interface, getAddress } = require("ethers");
|
|
1625
|
+
var ABI = require_abi();
|
|
1626
|
+
var { fetchLogsChunked } = require_logs();
|
|
1627
|
+
var { SageSDKError, CODES } = require_errors();
|
|
1628
|
+
function normalise(address, label) {
|
|
1629
|
+
if (!address) throw new SageSDKError(CODES.INVALID_ARGS, `${label} required`);
|
|
1623
1630
|
try {
|
|
1624
|
-
return getAddress(
|
|
1625
|
-
} catch {
|
|
1626
|
-
|
|
1631
|
+
return getAddress(address);
|
|
1632
|
+
} catch (err) {
|
|
1633
|
+
throw new SageSDKError(CODES.INVALID_ARGS, `invalid ${label}`, { cause: err });
|
|
1627
1634
|
}
|
|
1628
1635
|
}
|
|
1629
|
-
function
|
|
1636
|
+
async function discoverSubDAOs({ provider, factoryAddress, fromBlock = 0, toBlock = "latest", limit = 5e3, chunkSize = 5e4 }) {
|
|
1637
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1638
|
+
const fac = normalise(factoryAddress, "factoryAddress");
|
|
1639
|
+
const iface = new Interface(ABI.Factory);
|
|
1640
|
+
const topic = iface.getEvent("SubDAOGovernanceDeployed").topicHash;
|
|
1641
|
+
const logs = await fetchLogsChunked({ provider, address: fac, topics: [topic], fromBlock, toBlock, chunkSize });
|
|
1630
1642
|
const out = [];
|
|
1631
|
-
|
|
1643
|
+
const slice = limit > 0 ? logs.slice(-limit) : logs;
|
|
1644
|
+
for (const log of slice) {
|
|
1632
1645
|
try {
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1646
|
+
const decoded = iface.decodeEventLog("SubDAOGovernanceDeployed", log.data, log.topics);
|
|
1647
|
+
out.push({
|
|
1648
|
+
subdao: getAddress(decoded[0]),
|
|
1649
|
+
governor: getAddress(decoded[1]),
|
|
1650
|
+
timelock: getAddress(decoded[2]),
|
|
1651
|
+
treasury: decoded[3] ? getAddress(decoded[3]) : null,
|
|
1652
|
+
txHash: log.transactionHash,
|
|
1653
|
+
blockNumber: log.blockNumber
|
|
1654
|
+
});
|
|
1655
|
+
} catch (err) {
|
|
1656
|
+
continue;
|
|
1636
1657
|
}
|
|
1637
1658
|
}
|
|
1638
1659
|
return out;
|
|
1639
1660
|
}
|
|
1640
|
-
async function
|
|
1641
|
-
if (!
|
|
1642
|
-
const
|
|
1643
|
-
const
|
|
1661
|
+
async function getSubDAOInfo({ provider, subdao: subdao2 }) {
|
|
1662
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1663
|
+
const addr = normalise(subdao2, "subdao");
|
|
1664
|
+
const contract = new Contract(addr, ABI.SubDAO, provider);
|
|
1665
|
+
const [governor, timelock2, treasury2, mode, promptRegistry, sageTreasury, accessModel, membershipPolicy, profileCID, factoryDeployer] = await Promise.all([
|
|
1666
|
+
contract.governor(),
|
|
1667
|
+
contract.timelock(),
|
|
1668
|
+
// The ops Safe address (multisig) controlling treasury/admin ops.
|
|
1669
|
+
contract.treasury().catch(() => null),
|
|
1670
|
+
contract.getGovernanceMode().catch(() => null),
|
|
1671
|
+
contract.promptRegistry().catch(() => null),
|
|
1672
|
+
contract.sageTreasury().catch(() => null),
|
|
1673
|
+
contract.accessModel().catch(() => null),
|
|
1674
|
+
contract.membershipPolicy().catch(() => null),
|
|
1675
|
+
contract.profileCID().catch(() => null),
|
|
1676
|
+
contract.factoryDeployer().catch(() => null)
|
|
1677
|
+
]);
|
|
1678
|
+
return {
|
|
1679
|
+
address: addr,
|
|
1680
|
+
governor: getAddress(governor),
|
|
1681
|
+
timelock: getAddress(timelock2),
|
|
1682
|
+
treasury: treasury2 && treasury2 !== "0x0000000000000000000000000000000000000000" ? getAddress(treasury2) : null,
|
|
1683
|
+
governanceMode: mode,
|
|
1684
|
+
promptRegistry: promptRegistry ? getAddress(promptRegistry) : null,
|
|
1685
|
+
sageTreasury: sageTreasury ? getAddress(sageTreasury) : null,
|
|
1686
|
+
accessModel: accessModel != null ? Number(accessModel) : null,
|
|
1687
|
+
membershipPolicy: membershipPolicy != null ? Number(membershipPolicy) : null,
|
|
1688
|
+
profileCID: profileCID || null,
|
|
1689
|
+
factoryDeployer: factoryDeployer && factoryDeployer !== "0x0000000000000000000000000000000000000000" ? getAddress(factoryDeployer) : null
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function decodeGovernanceProfile({ kind, proposalAccess, executionAccess }) {
|
|
1693
|
+
const kindLabel = Number(kind) === 0 ? "OPERATOR" : Number(kind) === 1 ? "TOKEN" : null;
|
|
1694
|
+
const proposalLabel = Number(proposalAccess) === 0 ? "COUNCIL_ONLY" : Number(proposalAccess) === 1 ? "COMMUNITY_THRESHOLD" : null;
|
|
1695
|
+
const execLabel = Number(executionAccess) === 0 ? "COUNCIL_ONLY" : Number(executionAccess) === 1 ? "ANYONE" : null;
|
|
1696
|
+
return {
|
|
1697
|
+
kind: kindLabel,
|
|
1698
|
+
proposalAccess: proposalLabel,
|
|
1699
|
+
executionAccess: execLabel
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
async function getGovernanceProfile({ provider, subdao: subdao2 }) {
|
|
1703
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1704
|
+
const addr = normalise(subdao2, "subdao");
|
|
1705
|
+
const contract = new Contract(addr, ABI.SubDAO, provider);
|
|
1706
|
+
let profileInitialized = false;
|
|
1707
|
+
let source = "legacy";
|
|
1644
1708
|
try {
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1709
|
+
profileInitialized = !!await contract.profileInitialized();
|
|
1710
|
+
source = profileInitialized ? "profile" : "legacy";
|
|
1711
|
+
} catch (_) {
|
|
1712
|
+
profileInitialized = false;
|
|
1713
|
+
source = "legacy";
|
|
1714
|
+
}
|
|
1715
|
+
if (profileInitialized) {
|
|
1716
|
+
try {
|
|
1717
|
+
const [kind, proposalAccess, executionAccess] = await contract.getGovernanceProfile();
|
|
1718
|
+
return {
|
|
1719
|
+
...decodeGovernanceProfile({ kind, proposalAccess, executionAccess }),
|
|
1720
|
+
profileInitialized: true,
|
|
1721
|
+
source
|
|
1722
|
+
};
|
|
1723
|
+
} catch (_) {
|
|
1724
|
+
profileInitialized = false;
|
|
1725
|
+
source = "legacy";
|
|
1658
1726
|
}
|
|
1659
|
-
return data.data;
|
|
1660
|
-
} catch (error) {
|
|
1661
|
-
clearTimeout(timeoutId);
|
|
1662
|
-
throw error;
|
|
1663
1727
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
proposals(where: { governor: $governor }, first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
|
|
1669
|
-
id
|
|
1670
|
-
proposer
|
|
1671
|
-
description
|
|
1672
|
-
createdAt
|
|
1673
|
-
targets
|
|
1674
|
-
values
|
|
1675
|
-
calldatas
|
|
1728
|
+
let mode = null;
|
|
1729
|
+
try {
|
|
1730
|
+
mode = await contract.getGovernanceMode();
|
|
1731
|
+
} catch (_) {
|
|
1676
1732
|
}
|
|
1677
|
-
|
|
1678
|
-
`, { governor: String(governor).toLowerCase(), first, skip });
|
|
1679
|
-
return mapSafe(data?.proposals, (p) => {
|
|
1680
|
-
const proposer = safeGetAddress(p.proposer);
|
|
1681
|
-
if (!proposer) return null;
|
|
1682
|
-
const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
|
|
1683
|
-
if (!targets.length && (p.targets || []).length) return null;
|
|
1733
|
+
if (mode != null && Number(mode) === 0) {
|
|
1684
1734
|
return {
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
values: (p.values || []).map((value) => BigInt(String(value))),
|
|
1691
|
-
calldatas: p.calldatas || []
|
|
1735
|
+
kind: "OPERATOR",
|
|
1736
|
+
proposalAccess: "COUNCIL_ONLY",
|
|
1737
|
+
executionAccess: "COUNCIL_ONLY",
|
|
1738
|
+
profileInitialized: false,
|
|
1739
|
+
source
|
|
1692
1740
|
};
|
|
1693
|
-
});
|
|
1694
|
-
}
|
|
1695
|
-
var STATE_STRING_TO_NUMBER = {
|
|
1696
|
-
PENDING: 0,
|
|
1697
|
-
ACTIVE: 1,
|
|
1698
|
-
CANCELED: 2,
|
|
1699
|
-
CANCELLED: 2,
|
|
1700
|
-
// some indexes may use British spelling
|
|
1701
|
-
DEFEATED: 3,
|
|
1702
|
-
SUCCEEDED: 4,
|
|
1703
|
-
QUEUED: 5,
|
|
1704
|
-
EXPIRED: 6,
|
|
1705
|
-
EXECUTED: 7
|
|
1706
|
-
};
|
|
1707
|
-
async function listProposalsFiltered({ url, governor, states, fromTimestamp, toTimestamp, first = 20, skip = 0, orderBy = "createdAt", orderDirection = "desc" }) {
|
|
1708
|
-
const govLower = governor ? String(governor).toLowerCase() : null;
|
|
1709
|
-
const statesUpper = Array.isArray(states) && states.length ? states.map((s) => String(s).toUpperCase()) : null;
|
|
1710
|
-
const safeOrderBy = sanitizeOrderBy(orderBy, ["createdAt", "updatedAt", "eta"], "createdAt");
|
|
1711
|
-
const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
|
|
1712
|
-
const doc = `
|
|
1713
|
-
query($first: Int!, $skip: Int!, $governor: Bytes, $states: [String!], $from: Int, $to: Int) {
|
|
1714
|
-
proposals(
|
|
1715
|
-
where: {
|
|
1716
|
-
${governor ? "governor: $governor," : ""}
|
|
1717
|
-
${statesUpper ? "state_in: $states," : ""}
|
|
1718
|
-
${fromTimestamp !== void 0 ? "createdAt_gte: $from," : ""}
|
|
1719
|
-
${toTimestamp !== void 0 ? "createdAt_lte: $to," : ""}
|
|
1720
|
-
}
|
|
1721
|
-
first: $first
|
|
1722
|
-
skip: $skip
|
|
1723
|
-
orderBy: ${safeOrderBy}
|
|
1724
|
-
orderDirection: ${safeOrderDirection}
|
|
1725
|
-
) {
|
|
1726
|
-
id
|
|
1727
|
-
proposer
|
|
1728
|
-
description
|
|
1729
|
-
createdAt
|
|
1730
|
-
updatedAt
|
|
1731
|
-
state
|
|
1732
|
-
eta
|
|
1733
|
-
targets
|
|
1734
|
-
values
|
|
1735
|
-
calldatas
|
|
1736
1741
|
}
|
|
1737
|
-
|
|
1738
|
-
`;
|
|
1739
|
-
const variables = { first, skip };
|
|
1740
|
-
if (govLower) variables.governor = govLower;
|
|
1741
|
-
if (statesUpper) variables.states = statesUpper;
|
|
1742
|
-
if (fromTimestamp !== void 0) variables.from = Number(fromTimestamp);
|
|
1743
|
-
if (toTimestamp !== void 0) variables.to = Number(toTimestamp);
|
|
1744
|
-
const data = await query(url, doc, variables);
|
|
1745
|
-
return mapSafe(data?.proposals, (p) => {
|
|
1746
|
-
const stateStr = String(p.state || "").toUpperCase();
|
|
1747
|
-
const stateNum = STATE_STRING_TO_NUMBER[stateStr] != null ? STATE_STRING_TO_NUMBER[stateStr] : null;
|
|
1748
|
-
const proposer = safeGetAddress(p.proposer);
|
|
1749
|
-
if (!proposer) return null;
|
|
1750
|
-
const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
|
|
1751
|
-
if (!targets.length && (p.targets || []).length) return null;
|
|
1742
|
+
if (mode != null && Number(mode) === 1) {
|
|
1752
1743
|
return {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1744
|
+
kind: "TOKEN",
|
|
1745
|
+
proposalAccess: "COMMUNITY_THRESHOLD",
|
|
1746
|
+
executionAccess: "ANYONE",
|
|
1747
|
+
profileInitialized: false,
|
|
1748
|
+
source
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
return {
|
|
1752
|
+
kind: null,
|
|
1753
|
+
proposalAccess: null,
|
|
1754
|
+
executionAccess: null,
|
|
1755
|
+
profileInitialized: false,
|
|
1756
|
+
source
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
async function getSubDAOUserStats({ provider, subdao: subdao2, account }) {
|
|
1760
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
1761
|
+
const addr = normalise(subdao2, "subdao");
|
|
1762
|
+
const user = normalise(account, "account");
|
|
1763
|
+
const contract = new Contract(addr, ABI.SubDAO, provider);
|
|
1764
|
+
const userForkCount = await contract.userForkCount(user).catch(() => 0n);
|
|
1765
|
+
return {
|
|
1766
|
+
userForkCount: Number(userForkCount)
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
var SubDAOInterface = new Interface([
|
|
1770
|
+
// Optional on newer SubDAO implementations; provided for tx building only.
|
|
1771
|
+
"function setProfileCid(string)"
|
|
1772
|
+
]);
|
|
1773
|
+
function buildSetProfileCidTx({ subdao: subdao2, profileCid }) {
|
|
1774
|
+
const addr = normalise(subdao2, "subdao");
|
|
1775
|
+
if (!profileCid || typeof profileCid !== "string" || !profileCid.trim()) {
|
|
1776
|
+
throw new SageSDKError(CODES.INVALID_ARGS, "profileCid (CID string) required");
|
|
1777
|
+
}
|
|
1778
|
+
const data = SubDAOInterface.encodeFunctionData("setProfileCid", [String(profileCid)]);
|
|
1779
|
+
return { to: addr, data, value: 0n };
|
|
1780
|
+
}
|
|
1781
|
+
module2.exports = {
|
|
1782
|
+
discoverSubDAOs,
|
|
1783
|
+
getSubDAOInfo,
|
|
1784
|
+
getGovernanceProfile,
|
|
1785
|
+
getSubDAOUserStats,
|
|
1786
|
+
buildSetProfileCidTx
|
|
1787
|
+
};
|
|
1788
|
+
async function ensureSxxxBurnAllowance({ signer, sxxx, owner, spender, amount }) {
|
|
1789
|
+
if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
|
|
1790
|
+
if (!sxxx) throw new SageSDKError(CODES.INVALID_ARGS, "sxxx address required");
|
|
1791
|
+
if (!spender) throw new SageSDKError(CODES.INVALID_ARGS, "spender (factory) required");
|
|
1792
|
+
const sxxxAddr = getAddress(sxxx);
|
|
1793
|
+
const spenderAddr = getAddress(spender);
|
|
1794
|
+
const erc = new Contract(sxxxAddr, ABI.SXXX, signer);
|
|
1795
|
+
const ownerAddr = owner ? getAddress(owner) : await signer.getAddress();
|
|
1796
|
+
const need = BigInt(amount || 0n);
|
|
1797
|
+
if (need === 0n) return { approved: false, tx: null };
|
|
1798
|
+
const current = await erc.allowance(ownerAddr, spenderAddr);
|
|
1799
|
+
const cur = BigInt(current.toString());
|
|
1800
|
+
if (cur >= need) return { approved: false, tx: null };
|
|
1801
|
+
const tx = await erc.approve(spenderAddr, need);
|
|
1802
|
+
const rcpt = await tx.wait();
|
|
1803
|
+
return { approved: true, tx, receipt: rcpt };
|
|
1804
|
+
}
|
|
1805
|
+
async function createSubDAO({ signer, factoryAddress, name, description, accessModel, burnAmount, sxxx }) {
|
|
1806
|
+
if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
|
|
1807
|
+
if (!factoryAddress) throw new SageSDKError(CODES.INVALID_ARGS, "factoryAddress required");
|
|
1808
|
+
const provider = signer.provider;
|
|
1809
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
|
|
1810
|
+
if (sxxx && BigInt(burnAmount || 0n) > 0n) {
|
|
1811
|
+
await ensureSxxxBurnAllowance({ signer, sxxx, spender: factoryAddress, amount: BigInt(burnAmount) });
|
|
1812
|
+
}
|
|
1813
|
+
const fac = new Contract(getAddress(factoryAddress), ABI.FactoryWrite, signer);
|
|
1814
|
+
const args = [
|
|
1815
|
+
String(name || ""),
|
|
1816
|
+
String(description || ""),
|
|
1817
|
+
Number(accessModel ?? 0),
|
|
1818
|
+
0n,
|
|
1819
|
+
// minStakeAmount deprecated
|
|
1820
|
+
BigInt(burnAmount ?? 0n)
|
|
1821
|
+
];
|
|
1822
|
+
let tx;
|
|
1823
|
+
try {
|
|
1824
|
+
tx = await fac.createSubDAO(...args);
|
|
1825
|
+
} catch (err) {
|
|
1826
|
+
tx = await fac.createSubDAO(...args, { gasLimit: 8e6 });
|
|
1827
|
+
}
|
|
1828
|
+
const receipt = await tx.wait();
|
|
1829
|
+
const iface = new Interface(ABI.Factory);
|
|
1830
|
+
let subdao2 = null, governor = null, timelock2 = null, treasury2 = null, registry = null;
|
|
1831
|
+
for (const log of receipt.logs || []) {
|
|
1832
|
+
try {
|
|
1833
|
+
const parsed = iface.parseLog(log);
|
|
1834
|
+
if (parsed && parsed.name === "SubDAOGovernanceDeployed") {
|
|
1835
|
+
subdao2 = getAddress(parsed.args.subDAO || parsed.args[0]);
|
|
1836
|
+
governor = getAddress(parsed.args.governor || parsed.args[1]);
|
|
1837
|
+
timelock2 = getAddress(parsed.args.timelock || parsed.args[2]);
|
|
1838
|
+
treasury2 = getAddress(parsed.args.treasury || parsed.args[3]);
|
|
1839
|
+
} else if (parsed && parsed.name === "SubDAOCreated") {
|
|
1840
|
+
const maybe = parsed.args.registry || parsed.args[2];
|
|
1841
|
+
if (maybe) registry = getAddress(maybe);
|
|
1842
|
+
if (!subdao2) subdao2 = getAddress(parsed.args.subDAO || parsed.args[0]);
|
|
1843
|
+
}
|
|
1844
|
+
} catch (_) {
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
try {
|
|
1848
|
+
if (subdao2 && !registry) {
|
|
1849
|
+
const sub = new Contract(subdao2, ABI.SubDAO, provider);
|
|
1850
|
+
const reg = await sub.promptRegistry().catch(() => null);
|
|
1851
|
+
if (reg && /^0x/.test(String(reg))) registry = getAddress(reg);
|
|
1852
|
+
}
|
|
1853
|
+
} catch (_) {
|
|
1854
|
+
}
|
|
1855
|
+
return { tx, receipt, subdao: subdao2, governor, timelock: timelock2, treasury: treasury2, registry };
|
|
1856
|
+
}
|
|
1857
|
+
async function makeOperator({ signer, subdao: subdao2, operator, grantAdmin = false }) {
|
|
1858
|
+
if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
|
|
1859
|
+
const provider = signer.provider;
|
|
1860
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
|
|
1861
|
+
const addr = normalise(subdao2, "subdao");
|
|
1862
|
+
const sub = new Contract(addr, ABI.SubDAO, provider);
|
|
1863
|
+
const governor = getAddress(await sub.governor());
|
|
1864
|
+
const timelock2 = getAddress(await sub.timelock());
|
|
1865
|
+
const TL_ABI = [
|
|
1866
|
+
"function PROPOSER_ROLE() view returns (bytes32)",
|
|
1867
|
+
"function EXECUTOR_ROLE() view returns (bytes32)",
|
|
1868
|
+
"function DEFAULT_ADMIN_ROLE() view returns (bytes32)",
|
|
1869
|
+
"function grantRole(bytes32,address)",
|
|
1870
|
+
"function revokeRole(bytes32,address)"
|
|
1871
|
+
];
|
|
1872
|
+
const tl = new Contract(timelock2, TL_ABI, signer);
|
|
1873
|
+
const [PR, ER, AR] = await Promise.all([
|
|
1874
|
+
tl.PROPOSER_ROLE(),
|
|
1875
|
+
tl.EXECUTOR_ROLE(),
|
|
1876
|
+
tl.DEFAULT_ADMIN_ROLE().catch(() => null)
|
|
1877
|
+
]);
|
|
1878
|
+
const ops = [];
|
|
1879
|
+
ops.push(await tl.grantRole(PR, getAddress(operator)));
|
|
1880
|
+
ops.push(await tl.grantRole(ER, getAddress(operator)));
|
|
1881
|
+
ops.push(await tl.revokeRole(PR, governor));
|
|
1882
|
+
if (grantAdmin && AR) ops.push(await tl.grantRole(AR, getAddress(operator)));
|
|
1883
|
+
const receipts = [];
|
|
1884
|
+
for (const tx of ops) {
|
|
1885
|
+
receipts.push(await tx.wait());
|
|
1886
|
+
}
|
|
1887
|
+
return { governor, timelock: timelock2, operator: getAddress(operator), txs: ops, receipts };
|
|
1888
|
+
}
|
|
1889
|
+
async function createOperatorSubDAO({ signer, factoryAddress, operator, name, description, accessModel, burnAmount, sxxx, grantAdmin = false }) {
|
|
1890
|
+
const created = await createSubDAO({ signer, factoryAddress, name, description, accessModel, burnAmount, sxxx });
|
|
1891
|
+
if (!created.subdao) throw new SageSDKError(CODES.UNKNOWN, "createSubDAO did not emit SubDAO address");
|
|
1892
|
+
const op = await makeOperator({ signer, subdao: created.subdao, operator, grantAdmin });
|
|
1893
|
+
return { ...created, operatorResult: op };
|
|
1894
|
+
}
|
|
1895
|
+
module2.exports.ensureSxxxBurnAllowance = ensureSxxxBurnAllowance;
|
|
1896
|
+
module2.exports.createSubDAO = createSubDAO;
|
|
1897
|
+
module2.exports.makeOperator = makeOperator;
|
|
1898
|
+
module2.exports.createOperatorSubDAO = createOperatorSubDAO;
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
// src/browser/utils.js
|
|
1903
|
+
var require_utils = __commonJS({
|
|
1904
|
+
"src/browser/utils.js"(exports2, module2) {
|
|
1905
|
+
function getAddress(address) {
|
|
1906
|
+
if (!address) return address;
|
|
1907
|
+
const addr = String(address).trim();
|
|
1908
|
+
const normalized = addr.startsWith("0x") ? addr.slice(2).toLowerCase() : addr.toLowerCase();
|
|
1909
|
+
return `0x${normalized}`;
|
|
1910
|
+
}
|
|
1911
|
+
async function keccak256Async(data) {
|
|
1912
|
+
const encoder = new TextEncoder();
|
|
1913
|
+
const dataBuffer = typeof data === "string" ? encoder.encode(data) : data;
|
|
1914
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
|
|
1915
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1916
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1917
|
+
return `0x${hashHex}`;
|
|
1918
|
+
}
|
|
1919
|
+
function keccak256Sync(data) {
|
|
1920
|
+
const str = typeof data === "string" ? data : JSON.stringify(data);
|
|
1921
|
+
let hash = 0;
|
|
1922
|
+
for (let i = 0; i < str.length; i++) {
|
|
1923
|
+
const char = str.charCodeAt(i);
|
|
1924
|
+
hash = (hash << 5) - hash + char;
|
|
1925
|
+
hash = hash & hash;
|
|
1926
|
+
}
|
|
1927
|
+
const hex = Math.abs(hash).toString(16).padStart(8, "0");
|
|
1928
|
+
return `0x${hex.repeat(8)}`.slice(0, 66);
|
|
1929
|
+
}
|
|
1930
|
+
module2.exports = {
|
|
1931
|
+
getAddress,
|
|
1932
|
+
keccak256Async,
|
|
1933
|
+
keccak256Sync
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
});
|
|
1937
|
+
|
|
1938
|
+
// src/browser/subgraph.js
|
|
1939
|
+
var require_subgraph2 = __commonJS({
|
|
1940
|
+
"src/browser/subgraph.js"(exports2, module2) {
|
|
1941
|
+
var { getAddress } = require_utils();
|
|
1942
|
+
function sanitizeOrderBy(orderBy, allowed, fallback) {
|
|
1943
|
+
const value = (orderBy || "").toString();
|
|
1944
|
+
return allowed.includes(value) ? value : fallback;
|
|
1945
|
+
}
|
|
1946
|
+
function sanitizeOrderDirection(direction, fallback = "desc") {
|
|
1947
|
+
const v = (direction || "").toString().toLowerCase();
|
|
1948
|
+
return v === "asc" ? "asc" : "desc";
|
|
1949
|
+
}
|
|
1950
|
+
function safeGetAddress(value) {
|
|
1951
|
+
try {
|
|
1952
|
+
return getAddress(value);
|
|
1953
|
+
} catch {
|
|
1954
|
+
return null;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
function mapSafe(list, mapper) {
|
|
1958
|
+
const out = [];
|
|
1959
|
+
for (const item of list || []) {
|
|
1960
|
+
try {
|
|
1961
|
+
const v = mapper(item);
|
|
1962
|
+
if (v != null) out.push(v);
|
|
1963
|
+
} catch {
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return out;
|
|
1967
|
+
}
|
|
1968
|
+
async function query(url, document, variables) {
|
|
1969
|
+
if (!url) throw new Error("subgraph url required");
|
|
1970
|
+
const controller = new AbortController();
|
|
1971
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
1972
|
+
try {
|
|
1973
|
+
const resp = await fetch(url, {
|
|
1974
|
+
method: "POST",
|
|
1975
|
+
headers: { "Content-Type": "application/json" },
|
|
1976
|
+
body: JSON.stringify({ query: document, variables }),
|
|
1977
|
+
signal: controller.signal
|
|
1978
|
+
});
|
|
1979
|
+
clearTimeout(timeoutId);
|
|
1980
|
+
if (!resp.ok) {
|
|
1981
|
+
throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
|
|
1982
|
+
}
|
|
1983
|
+
const data = await resp.json();
|
|
1984
|
+
if (data && data.errors) {
|
|
1985
|
+
throw new Error(data.errors.map((e) => e.message).join("; "));
|
|
1986
|
+
}
|
|
1987
|
+
return data.data;
|
|
1988
|
+
} catch (error) {
|
|
1989
|
+
clearTimeout(timeoutId);
|
|
1990
|
+
throw error;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
async function listProposals({ url, governor, first = 20, skip = 0 }) {
|
|
1994
|
+
const data = await query(url, `
|
|
1995
|
+
query($governor: Bytes!, $first: Int!, $skip: Int!) {
|
|
1996
|
+
proposals(where: { governor: $governor }, first: $first, skip: $skip, orderBy: createdAt, orderDirection: desc) {
|
|
1997
|
+
id
|
|
1998
|
+
proposer
|
|
1999
|
+
description
|
|
2000
|
+
createdAt
|
|
2001
|
+
targets
|
|
2002
|
+
values
|
|
2003
|
+
calldatas
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
`, { governor: String(governor).toLowerCase(), first, skip });
|
|
2007
|
+
return mapSafe(data?.proposals, (p) => {
|
|
2008
|
+
const proposer = safeGetAddress(p.proposer);
|
|
2009
|
+
if (!proposer) return null;
|
|
2010
|
+
const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
|
|
2011
|
+
if (!targets.length && (p.targets || []).length) return null;
|
|
2012
|
+
return {
|
|
2013
|
+
id: BigInt(p.id),
|
|
2014
|
+
proposer,
|
|
2015
|
+
description: p.description,
|
|
2016
|
+
createdAt: Number(p.createdAt || 0),
|
|
2017
|
+
targets,
|
|
2018
|
+
values: (p.values || []).map((value) => BigInt(String(value))),
|
|
2019
|
+
calldatas: p.calldatas || []
|
|
2020
|
+
};
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
var STATE_STRING_TO_NUMBER = {
|
|
2024
|
+
PENDING: 0,
|
|
2025
|
+
ACTIVE: 1,
|
|
2026
|
+
CANCELED: 2,
|
|
2027
|
+
CANCELLED: 2,
|
|
2028
|
+
// some indexes may use British spelling
|
|
2029
|
+
DEFEATED: 3,
|
|
2030
|
+
SUCCEEDED: 4,
|
|
2031
|
+
QUEUED: 5,
|
|
2032
|
+
EXPIRED: 6,
|
|
2033
|
+
EXECUTED: 7
|
|
2034
|
+
};
|
|
2035
|
+
async function listProposalsFiltered({ url, governor, states, fromTimestamp, toTimestamp, first = 20, skip = 0, orderBy = "createdAt", orderDirection = "desc" }) {
|
|
2036
|
+
const govLower = governor ? String(governor).toLowerCase() : null;
|
|
2037
|
+
const statesUpper = Array.isArray(states) && states.length ? states.map((s) => String(s).toUpperCase()) : null;
|
|
2038
|
+
const safeOrderBy = sanitizeOrderBy(orderBy, ["createdAt", "updatedAt", "eta"], "createdAt");
|
|
2039
|
+
const safeOrderDirection = sanitizeOrderDirection(orderDirection, "desc");
|
|
2040
|
+
const doc = `
|
|
2041
|
+
query($first: Int!, $skip: Int!, $governor: Bytes, $states: [String!], $from: Int, $to: Int) {
|
|
2042
|
+
proposals(
|
|
2043
|
+
where: {
|
|
2044
|
+
${governor ? "governor: $governor," : ""}
|
|
2045
|
+
${statesUpper ? "state_in: $states," : ""}
|
|
2046
|
+
${fromTimestamp !== void 0 ? "createdAt_gte: $from," : ""}
|
|
2047
|
+
${toTimestamp !== void 0 ? "createdAt_lte: $to," : ""}
|
|
2048
|
+
}
|
|
2049
|
+
first: $first
|
|
2050
|
+
skip: $skip
|
|
2051
|
+
orderBy: ${safeOrderBy}
|
|
2052
|
+
orderDirection: ${safeOrderDirection}
|
|
2053
|
+
) {
|
|
2054
|
+
id
|
|
2055
|
+
proposer
|
|
2056
|
+
description
|
|
2057
|
+
createdAt
|
|
2058
|
+
updatedAt
|
|
2059
|
+
state
|
|
2060
|
+
eta
|
|
2061
|
+
targets
|
|
2062
|
+
values
|
|
2063
|
+
calldatas
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
`;
|
|
2067
|
+
const variables = { first, skip };
|
|
2068
|
+
if (govLower) variables.governor = govLower;
|
|
2069
|
+
if (statesUpper) variables.states = statesUpper;
|
|
2070
|
+
if (fromTimestamp !== void 0) variables.from = Number(fromTimestamp);
|
|
2071
|
+
if (toTimestamp !== void 0) variables.to = Number(toTimestamp);
|
|
2072
|
+
const data = await query(url, doc, variables);
|
|
2073
|
+
return mapSafe(data?.proposals, (p) => {
|
|
2074
|
+
const stateStr = String(p.state || "").toUpperCase();
|
|
2075
|
+
const stateNum = STATE_STRING_TO_NUMBER[stateStr] != null ? STATE_STRING_TO_NUMBER[stateStr] : null;
|
|
2076
|
+
const proposer = safeGetAddress(p.proposer);
|
|
2077
|
+
if (!proposer) return null;
|
|
2078
|
+
const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
|
|
2079
|
+
if (!targets.length && (p.targets || []).length) return null;
|
|
2080
|
+
return {
|
|
2081
|
+
id: BigInt(p.id),
|
|
2082
|
+
proposer,
|
|
2083
|
+
description: p.description || "",
|
|
2084
|
+
createdAt: Number(p.createdAt || 0),
|
|
2085
|
+
updatedAt: Number(p.updatedAt || 0),
|
|
2086
|
+
state: stateStr,
|
|
2087
|
+
stateNum,
|
|
2088
|
+
eta: p.eta ? BigInt(String(p.eta)) : null,
|
|
1761
2089
|
targets,
|
|
1762
2090
|
values: (p.values || []).map((value) => BigInt(String(value))),
|
|
1763
2091
|
calldatas: p.calldatas || []
|
|
@@ -2192,6 +2520,7 @@ var require_ipfs = __commonJS({
|
|
|
2192
2520
|
var FormData = require("form-data");
|
|
2193
2521
|
var { ethers: ethers2 } = require("ethers");
|
|
2194
2522
|
var DEFAULT_GATEWAY = "https://ipfs.dev.sageprotocol.io/ipfs";
|
|
2523
|
+
var DEFAULT_WORKER_BASE = "https://api.sageprotocol.io";
|
|
2195
2524
|
function toLowerSafe(value, fallback = "") {
|
|
2196
2525
|
if (value == null) return fallback;
|
|
2197
2526
|
try {
|
|
@@ -2263,7 +2592,8 @@ var require_ipfs = __commonJS({
|
|
|
2263
2592
|
const formDataFactory = options.formDataFactory || (() => new FormData());
|
|
2264
2593
|
const generateCid = options.generateCid || generateDeterministicCid;
|
|
2265
2594
|
const config = {
|
|
2266
|
-
|
|
2595
|
+
// Default to 'worker' to ensure uploads go through worker for indexing
|
|
2596
|
+
provider: toLowerSafe(options.provider ?? env.SAGE_IPFS_PROVIDER ?? "worker"),
|
|
2267
2597
|
gateway: options.gateway || env.SAGE_IPFS_GATEWAY || DEFAULT_GATEWAY,
|
|
2268
2598
|
timeoutMs: Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3),
|
|
2269
2599
|
retries: Math.max(0, Number(options.retries ?? env.SAGE_IPFS_RETRIES ?? 2)),
|
|
@@ -2276,11 +2606,12 @@ var require_ipfs = __commonJS({
|
|
|
2276
2606
|
jwt: options.pinataJwt || env.PINATA_JWT || null
|
|
2277
2607
|
},
|
|
2278
2608
|
worker: {
|
|
2279
|
-
baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL ||
|
|
2609
|
+
baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL || DEFAULT_WORKER_BASE),
|
|
2280
2610
|
uploadUrl: removeTrailingSlash(options.workerUploadUrl || env.SAGE_IPFS_UPLOAD_URL || ""),
|
|
2281
2611
|
token: options.workerUploadToken || env.SAGE_IPFS_UPLOAD_TOKEN || "",
|
|
2282
|
-
challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/auth/challenge",
|
|
2612
|
+
challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/ipfs/auth/challenge",
|
|
2283
2613
|
uploadPath: options.workerUploadPath || env.SAGE_IPFS_WORKER_UPLOAD_PATH || "/ipfs/upload",
|
|
2614
|
+
uploadRawPath: options.workerUploadRawPath || env.SAGE_IPFS_WORKER_UPLOAD_RAW_PATH || "/ipfs/upload-raw",
|
|
2284
2615
|
pinPath: options.workerPinPath || env.SAGE_IPFS_WORKER_PIN_PATH || "/ipfs/pin",
|
|
2285
2616
|
warmPath: options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH || "/ipfs/warm",
|
|
2286
2617
|
pinUrl: removeTrailingSlash(options.workerPinUrl || env.SAGE_IPFS_WORKER_PIN_URL || ""),
|
|
@@ -2288,11 +2619,15 @@ var require_ipfs = __commonJS({
|
|
|
2288
2619
|
signer: options.workerSigner || options.signer || null,
|
|
2289
2620
|
getAuth: options.workerGetAuth || null,
|
|
2290
2621
|
address: options.workerAddress || env.SAGE_SANDBOX_ADDRESS || "",
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2622
|
+
// All event types route to /trends/usage - worker tracks usage for trend scoring
|
|
2623
|
+
discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/trends/usage",
|
|
2624
|
+
discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/trends/usage",
|
|
2625
|
+
discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/trends/usage",
|
|
2626
|
+
// Install events track skill installations separately for trend scoring
|
|
2627
|
+
discoveryInstallPath: options.workerDiscoveryInstallPath || env.SAGE_IPFS_WORKER_DISCOVERY_INSTALL_PATH || "/trends/install",
|
|
2294
2628
|
discoveryFeedPath: options.workerDiscoveryFeedPath || env.SAGE_IPFS_WORKER_DISCOVERY_FEED_PATH || "/discover",
|
|
2295
|
-
|
|
2629
|
+
// Metrics uses /discover/stats which returns index statistics
|
|
2630
|
+
discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/stats",
|
|
2296
2631
|
governanceReportPath: options.workerGovernanceReportPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORT_PATH || "/governance/report",
|
|
2297
2632
|
governanceReportsPath: options.workerGovernanceReportsPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORTS_PATH || "/governance/reports",
|
|
2298
2633
|
governanceReviewPath: options.workerGovernanceReviewPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REVIEW_PATH || "/governance/report/review",
|
|
@@ -2326,8 +2661,9 @@ var require_ipfs = __commonJS({
|
|
|
2326
2661
|
const base = workerBaseUrl();
|
|
2327
2662
|
const ensure = (path2) => path2.startsWith("/") ? path2 : `/${path2}`;
|
|
2328
2663
|
if (base) {
|
|
2329
|
-
if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/auth/challenge")}`;
|
|
2664
|
+
if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/ipfs/auth/challenge")}`;
|
|
2330
2665
|
if (kind === "upload") return `${base}${ensure(config.worker.uploadPath || "/ipfs/upload")}`;
|
|
2666
|
+
if (kind === "upload-raw") return `${base}${ensure(config.worker.uploadRawPath || "/ipfs/upload-raw")}`;
|
|
2331
2667
|
if (kind === "pin") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}`;
|
|
2332
2668
|
if (kind === "warm") return `${base}${ensure(config.worker.warmPath || "/ipfs/warm")}`;
|
|
2333
2669
|
if (kind === "status") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}/${cid}`;
|
|
@@ -2362,6 +2698,26 @@ var require_ipfs = __commonJS({
|
|
|
2362
2698
|
const response = await axiosInstance.get(url, { headers, params, timeout: config.timeoutMs });
|
|
2363
2699
|
return response?.data;
|
|
2364
2700
|
}
|
|
2701
|
+
async function withRetry(fn, { retries = 2, baseMs = 500 } = {}) {
|
|
2702
|
+
let attempt = 0;
|
|
2703
|
+
let lastErr;
|
|
2704
|
+
while (attempt <= retries) {
|
|
2705
|
+
try {
|
|
2706
|
+
return await fn();
|
|
2707
|
+
} catch (e) {
|
|
2708
|
+
lastErr = e;
|
|
2709
|
+
const status = e?.response?.status;
|
|
2710
|
+
if (status && status >= 400 && status < 500 && status !== 429) {
|
|
2711
|
+
throw e;
|
|
2712
|
+
}
|
|
2713
|
+
if (attempt === retries) break;
|
|
2714
|
+
const delay = baseMs * Math.pow(2, attempt);
|
|
2715
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
2716
|
+
attempt++;
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
throw lastErr;
|
|
2720
|
+
}
|
|
2365
2721
|
async function buildWorkerAuthHeaders(extraHeaders = {}) {
|
|
2366
2722
|
if (config.worker.token) {
|
|
2367
2723
|
return { headers: { ...extraHeaders, Authorization: `Bearer ${config.worker.token}` }, auth: null };
|
|
@@ -2404,9 +2760,7 @@ var require_ipfs = __commonJS({
|
|
|
2404
2760
|
const hasEndpoint = workerBaseUrl() || config.worker.uploadUrl;
|
|
2405
2761
|
if (!hasEndpoint) return false;
|
|
2406
2762
|
if (!workerHasAuth()) return false;
|
|
2407
|
-
|
|
2408
|
-
if (config.provider === "auto") return true;
|
|
2409
|
-
return false;
|
|
2763
|
+
return true;
|
|
2410
2764
|
}
|
|
2411
2765
|
function resolveProviderOrder(preference) {
|
|
2412
2766
|
const order = [];
|
|
@@ -2422,8 +2776,8 @@ var require_ipfs = __commonJS({
|
|
|
2422
2776
|
console.log("[SDK DEBUG] shouldUseWorker():", shouldUseWorker());
|
|
2423
2777
|
console.log("[SDK DEBUG] hasPinataCreds():", hasPinataCreds());
|
|
2424
2778
|
}
|
|
2425
|
-
normalizedPreference.forEach(append);
|
|
2426
2779
|
if (shouldUseWorker()) append("worker");
|
|
2780
|
+
normalizedPreference.forEach(append);
|
|
2427
2781
|
if (hasPinataCreds()) append("pinata");
|
|
2428
2782
|
if (config.web3.token) append("web3");
|
|
2429
2783
|
if (config.simulate) append("simulate");
|
|
@@ -2514,6 +2868,107 @@ var require_ipfs = __commonJS({
|
|
|
2514
2868
|
}
|
|
2515
2869
|
return result;
|
|
2516
2870
|
}
|
|
2871
|
+
async function uploadRawViaWorker(content, { name, warm, gateways } = {}) {
|
|
2872
|
+
const uploadUrl = workerUrl("upload-raw");
|
|
2873
|
+
if (process.env.SAGE_DEBUG_WORKER === "1") {
|
|
2874
|
+
console.log("[SDK DEBUG] uploadRawViaWorker called");
|
|
2875
|
+
console.log("[SDK DEBUG] uploadUrl:", uploadUrl);
|
|
2876
|
+
}
|
|
2877
|
+
const { headers, auth } = await buildWorkerAuthHeaders({ "Content-Type": "application/json" });
|
|
2878
|
+
const body = { content };
|
|
2879
|
+
if (auth) body.auth = auth;
|
|
2880
|
+
if (name) body.name = name;
|
|
2881
|
+
if (process.env.SAGE_DEBUG_WORKER === "1") {
|
|
2882
|
+
console.log("[SDK DEBUG] headers:", Object.keys(headers));
|
|
2883
|
+
console.log("[SDK DEBUG] hasAuth:", !!auth);
|
|
2884
|
+
}
|
|
2885
|
+
let response;
|
|
2886
|
+
try {
|
|
2887
|
+
response = await axiosInstance.post(uploadUrl, body, { headers, timeout: config.timeoutMs });
|
|
2888
|
+
if (process.env.SAGE_DEBUG_WORKER === "1") {
|
|
2889
|
+
console.log("[SDK DEBUG] Upload response status:", response?.status);
|
|
2890
|
+
console.log("[SDK DEBUG] Upload response data:", JSON.stringify(response?.data || {}));
|
|
2891
|
+
}
|
|
2892
|
+
} catch (uploadErr) {
|
|
2893
|
+
if (process.env.SAGE_DEBUG_WORKER === "1") {
|
|
2894
|
+
console.log("[SDK DEBUG] Upload error:", uploadErr.message);
|
|
2895
|
+
console.log("[SDK DEBUG] Response status:", uploadErr.response?.status);
|
|
2896
|
+
console.log("[SDK DEBUG] Response data:", JSON.stringify(uploadErr.response?.data || {}));
|
|
2897
|
+
}
|
|
2898
|
+
const status = uploadErr.response?.status;
|
|
2899
|
+
const data2 = uploadErr.response?.data;
|
|
2900
|
+
if (status === 400 && data2?.error === "content_blocked") {
|
|
2901
|
+
const error = new Error("content_blocked");
|
|
2902
|
+
error.code = "CONTENT_BLOCKED";
|
|
2903
|
+
error.categories = data2.categories || [];
|
|
2904
|
+
error.reason = data2.reason;
|
|
2905
|
+
error.response = data2;
|
|
2906
|
+
throw error;
|
|
2907
|
+
}
|
|
2908
|
+
if (status === 402) {
|
|
2909
|
+
const error = new Error("payment_required");
|
|
2910
|
+
error.code = "PAYMENT_REQUIRED";
|
|
2911
|
+
error.creditsNeeded = data2?.creditsNeeded;
|
|
2912
|
+
error.balance = data2?.balance;
|
|
2913
|
+
error.accepts = data2?.accepts;
|
|
2914
|
+
error.response = data2;
|
|
2915
|
+
throw error;
|
|
2916
|
+
}
|
|
2917
|
+
throw uploadErr;
|
|
2918
|
+
}
|
|
2919
|
+
const data = response?.data || {};
|
|
2920
|
+
const cid = data.cid || data.IpfsHash;
|
|
2921
|
+
if (!cid) {
|
|
2922
|
+
throw new Error("Worker response missing cid");
|
|
2923
|
+
}
|
|
2924
|
+
if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
|
|
2925
|
+
return {
|
|
2926
|
+
cid,
|
|
2927
|
+
provider: "worker",
|
|
2928
|
+
response: data
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
async function uploadRawJson(content, options2 = {}) {
|
|
2932
|
+
if (!content || typeof content !== "object") {
|
|
2933
|
+
throw new Error("content object required");
|
|
2934
|
+
}
|
|
2935
|
+
const { providers, provider, warm, gateways, name = "manifest.json" } = options2;
|
|
2936
|
+
const order = resolveProviderOrder(providers || provider);
|
|
2937
|
+
if (!order.length) {
|
|
2938
|
+
throw new Error("No IPFS providers configured");
|
|
2939
|
+
}
|
|
2940
|
+
const errors2 = [];
|
|
2941
|
+
for (const candidate of order) {
|
|
2942
|
+
try {
|
|
2943
|
+
if (candidate === "worker") {
|
|
2944
|
+
return await uploadRawViaWorker(content, { name, warm, gateways });
|
|
2945
|
+
}
|
|
2946
|
+
if (candidate === "pinata") {
|
|
2947
|
+
if (process.env.SAGE_DEBUG_WORKER === "1") {
|
|
2948
|
+
console.log("[SDK WARN] uploadRawJson falling back to pinata - content will be wrapped");
|
|
2949
|
+
}
|
|
2950
|
+
return await uploadToPinata(content, { filename: name, warm, gateways });
|
|
2951
|
+
}
|
|
2952
|
+
if (candidate === "web3") {
|
|
2953
|
+
if (process.env.SAGE_DEBUG_WORKER === "1") {
|
|
2954
|
+
console.log("[SDK WARN] uploadRawJson falling back to web3 - content will be wrapped");
|
|
2955
|
+
}
|
|
2956
|
+
return await uploadToWeb3Storage(content, { warm, gateways });
|
|
2957
|
+
}
|
|
2958
|
+
if (candidate === "simulate") {
|
|
2959
|
+
return await uploadSimulated(content, { warm, gateways });
|
|
2960
|
+
}
|
|
2961
|
+
} catch (error2) {
|
|
2962
|
+
if (error2.code === "CONTENT_BLOCKED" || error2.code === "PAYMENT_REQUIRED") {
|
|
2963
|
+
throw error2;
|
|
2964
|
+
}
|
|
2965
|
+
errors2.push({ provider: candidate, error: error2 });
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
const error = new Error("All IPFS providers failed");
|
|
2969
|
+
error.details = errors2;
|
|
2970
|
+
throw error;
|
|
2971
|
+
}
|
|
2517
2972
|
async function uploadToPinata(payload, { filename = "payload.json", metadata, warm, gateways } = {}) {
|
|
2518
2973
|
if (!hasPinataCreds()) {
|
|
2519
2974
|
throw new Error("Missing Pinata credentials");
|
|
@@ -2742,28 +3197,37 @@ var require_ipfs = __commonJS({
|
|
|
2742
3197
|
}
|
|
2743
3198
|
async function recordDiscoveryEvent(event = {}) {
|
|
2744
3199
|
if (!event || typeof event !== "object") throw new Error("event required");
|
|
2745
|
-
const
|
|
2746
|
-
if (!
|
|
2747
|
-
const payload = { ...event,
|
|
2748
|
-
return postWorkerJson("discoveryEventsPath", "/
|
|
3200
|
+
const cid = typeof event.promptId === "string" ? event.promptId.trim() : typeof event.cid === "string" ? event.cid.trim() : "";
|
|
3201
|
+
if (!cid) throw new Error("promptId or cid required");
|
|
3202
|
+
const payload = { ...event, cid };
|
|
3203
|
+
return withRetry(() => postWorkerJson("discoveryEventsPath", "/trends/usage", payload));
|
|
3204
|
+
}
|
|
3205
|
+
async function recordMcpUsageEvent({ promptId, cid, metadata, address } = {}) {
|
|
3206
|
+
const id2 = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
|
|
3207
|
+
if (!id2) throw new Error("promptId or cid required");
|
|
3208
|
+
const payload = { cid: id2 };
|
|
3209
|
+
if (metadata && typeof metadata === "object") payload.metadata = metadata;
|
|
3210
|
+
const actor = address || config.worker.address;
|
|
3211
|
+
if (actor) payload.address = actor;
|
|
3212
|
+
return withRetry(() => postWorkerJson("discoveryMcpPath", "/trends/usage", payload));
|
|
2749
3213
|
}
|
|
2750
|
-
async function
|
|
2751
|
-
const id2 = typeof promptId === "string" ? promptId.trim() : "";
|
|
2752
|
-
if (!id2) throw new Error("promptId required");
|
|
2753
|
-
const payload = {
|
|
3214
|
+
async function recordLaunchEvent({ promptId, cid, metadata, address } = {}) {
|
|
3215
|
+
const id2 = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
|
|
3216
|
+
if (!id2) throw new Error("promptId or cid required");
|
|
3217
|
+
const payload = { cid: id2 };
|
|
2754
3218
|
if (metadata && typeof metadata === "object") payload.metadata = metadata;
|
|
2755
3219
|
const actor = address || config.worker.address;
|
|
2756
3220
|
if (actor) payload.address = actor;
|
|
2757
|
-
return postWorkerJson("
|
|
3221
|
+
return withRetry(() => postWorkerJson("discoveryLaunchPath", "/trends/usage", payload));
|
|
2758
3222
|
}
|
|
2759
|
-
async function
|
|
2760
|
-
const id2 = typeof
|
|
2761
|
-
if (!id2) throw new Error("
|
|
2762
|
-
const payload = {
|
|
3223
|
+
async function recordInstallEvent({ skillCid, cid, metadata, address } = {}) {
|
|
3224
|
+
const id2 = typeof skillCid === "string" ? skillCid.trim() : typeof cid === "string" ? cid.trim() : "";
|
|
3225
|
+
if (!id2) throw new Error("skillCid or cid required");
|
|
3226
|
+
const payload = { cid: id2 };
|
|
2763
3227
|
if (metadata && typeof metadata === "object") payload.metadata = metadata;
|
|
2764
3228
|
const actor = address || config.worker.address;
|
|
2765
3229
|
if (actor) payload.address = actor;
|
|
2766
|
-
return postWorkerJson("
|
|
3230
|
+
return withRetry(() => postWorkerJson("discoveryInstallPath", "/trends/install", payload));
|
|
2767
3231
|
}
|
|
2768
3232
|
async function submitGovernanceReport(payload = {}) {
|
|
2769
3233
|
if (!payload || typeof payload !== "object") throw new Error("payload required");
|
|
@@ -2825,6 +3289,7 @@ var require_ipfs = __commonJS({
|
|
|
2825
3289
|
uploadPrompt,
|
|
2826
3290
|
uploadJson,
|
|
2827
3291
|
uploadText,
|
|
3292
|
+
uploadRawJson,
|
|
2828
3293
|
downloadJson,
|
|
2829
3294
|
warmGateways,
|
|
2830
3295
|
buildGatewayUrls: (cid, extra) => buildGatewayUrls(cid, config.gateway, extra),
|
|
@@ -2832,6 +3297,7 @@ var require_ipfs = __commonJS({
|
|
|
2832
3297
|
recordDiscoveryEvent,
|
|
2833
3298
|
recordMcpUsage: recordMcpUsageEvent,
|
|
2834
3299
|
recordLaunchEvent,
|
|
3300
|
+
recordInstallEvent,
|
|
2835
3301
|
submitGovernanceReport,
|
|
2836
3302
|
listGovernanceReports,
|
|
2837
3303
|
reviewGovernanceReport,
|
|
@@ -2851,7 +3317,7 @@ var require_ipfs = __commonJS({
|
|
|
2851
3317
|
const timeoutMs = Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3);
|
|
2852
3318
|
const challengePath = ensureLeadingSlash(
|
|
2853
3319
|
options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH,
|
|
2854
|
-
"/auth/challenge"
|
|
3320
|
+
"/ipfs/auth/challenge"
|
|
2855
3321
|
);
|
|
2856
3322
|
const warmPath = ensureLeadingSlash(
|
|
2857
3323
|
options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH,
|
|
@@ -3383,7 +3849,10 @@ var require_library = __commonJS({
|
|
|
3383
3849
|
proposer: getAddress(info.lastUpdater),
|
|
3384
3850
|
promptCount: 0,
|
|
3385
3851
|
// Not tracked on-chain in V4
|
|
3386
|
-
version: info.version
|
|
3852
|
+
version: info.version,
|
|
3853
|
+
nonce: BigInt(info.nonce.toString()),
|
|
3854
|
+
forkedFromDAO: info.forkedFromDAO && info.forkedFromDAO !== "0x0000000000000000000000000000000000000000" ? getAddress(info.forkedFromDAO) : null,
|
|
3855
|
+
sxxxForkFee: BigInt(info.sxxxForkFee.toString())
|
|
3387
3856
|
};
|
|
3388
3857
|
}
|
|
3389
3858
|
async function getManifestInfo({ provider, registry, manifestCID }) {
|
|
@@ -3419,6 +3888,21 @@ var require_library = __commonJS({
|
|
|
3419
3888
|
]);
|
|
3420
3889
|
return { to, data, value: 0n };
|
|
3421
3890
|
}
|
|
3891
|
+
function buildUpdateLibraryCAS({ registry, subdao: subdao2, manifestCID, version = "1.0.0", expectedNonce }) {
|
|
3892
|
+
const to = normalise(registry, "registry");
|
|
3893
|
+
if (!manifestCID) throw new SageSDKError(CODES.INVALID_ARGS, "manifestCID required");
|
|
3894
|
+
if (expectedNonce === void 0 || expectedNonce === null) {
|
|
3895
|
+
throw new SageSDKError(CODES.INVALID_ARGS, "expectedNonce required for CAS update");
|
|
3896
|
+
}
|
|
3897
|
+
const iface = new Interface(["function updateLibraryCAS(address,string,string,uint256)"]);
|
|
3898
|
+
const data = iface.encodeFunctionData("updateLibraryCAS", [
|
|
3899
|
+
normalise(subdao2, "subdao"),
|
|
3900
|
+
String(manifestCID),
|
|
3901
|
+
String(version),
|
|
3902
|
+
BigInt(expectedNonce)
|
|
3903
|
+
]);
|
|
3904
|
+
return { to, data, value: 0n };
|
|
3905
|
+
}
|
|
3422
3906
|
module2.exports = {
|
|
3423
3907
|
listManifests,
|
|
3424
3908
|
getManifestInfo,
|
|
@@ -3426,6 +3910,7 @@ var require_library = __commonJS({
|
|
|
3426
3910
|
getLatestLibrary,
|
|
3427
3911
|
hasScopedOwnership,
|
|
3428
3912
|
buildUpdateLibraryTx,
|
|
3913
|
+
buildUpdateLibraryCAS,
|
|
3429
3914
|
_computeLibraryKey,
|
|
3430
3915
|
getBeforeAfterForUpdate,
|
|
3431
3916
|
/** Build an authorizeTimelock(timelock, subdao) call for a LibraryRegistry. */
|
|
@@ -3444,73 +3929,305 @@ var require_library = __commonJS({
|
|
|
3444
3929
|
const tgt = to ? normalise(to, "to") : reg;
|
|
3445
3930
|
const from = normalise(timelock2, "timelock");
|
|
3446
3931
|
try {
|
|
3447
|
-
await provider.call({ to: tgt, data, from });
|
|
3448
|
-
return { ok: true };
|
|
3449
|
-
} catch (err) {
|
|
3450
|
-
return { ok: false, error: { type: "Revert", message: String(err && err.message || err) } };
|
|
3932
|
+
await provider.call({ to: tgt, data, from });
|
|
3933
|
+
return { ok: true };
|
|
3934
|
+
} catch (err) {
|
|
3935
|
+
return { ok: false, error: { type: "Revert", message: String(err && err.message || err) } };
|
|
3936
|
+
}
|
|
3937
|
+
},
|
|
3938
|
+
executionReadiness: async function executionReadiness({ provider, registry, timelock: timelock2, subdao: subdao2, manifestCID = "", version = "1.0.0" }) {
|
|
3939
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
3940
|
+
const payload = buildUpdateLibraryTx({ registry, subdao: subdao2, manifestCID, version });
|
|
3941
|
+
const sim = await this.simulateAsTimelock({ provider, registry, to: payload.to, data: payload.data, timelock: timelock2 });
|
|
3942
|
+
const out = { ok: !!sim.ok, error: sim.ok ? null : sim.error?.message || "revert", missingRole: null };
|
|
3943
|
+
return out;
|
|
3944
|
+
}
|
|
3945
|
+
};
|
|
3946
|
+
}
|
|
3947
|
+
});
|
|
3948
|
+
|
|
3949
|
+
// src/governance/index.js
|
|
3950
|
+
var require_governance = __commonJS({
|
|
3951
|
+
"src/governance/index.js"(exports2, module2) {
|
|
3952
|
+
var { Contract, Interface, AbiCoder, ZeroAddress, getAddress, hexlify, keccak256, toUtf8Bytes } = require("ethers");
|
|
3953
|
+
var ABI = require_abi();
|
|
3954
|
+
var { BigIntZero } = require_types();
|
|
3955
|
+
var { SageSDKError, CODES } = require_errors();
|
|
3956
|
+
var { makeProposalDescription, stripProposalSalt } = require_description();
|
|
3957
|
+
var subgraph2 = require_subgraph();
|
|
3958
|
+
var adapters2 = require_transports();
|
|
3959
|
+
var subdao2 = require_subdao();
|
|
3960
|
+
function normaliseGovernor(governor) {
|
|
3961
|
+
if (!governor) throw new SageSDKError(CODES.INVALID_ARGS, "governor required");
|
|
3962
|
+
try {
|
|
3963
|
+
return getAddress(governor);
|
|
3964
|
+
} catch (err) {
|
|
3965
|
+
throw new SageSDKError(CODES.INVALID_ARGS, "invalid governor address", { cause: err });
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
async function getGovernorInfo({ provider, governor }) {
|
|
3969
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
3970
|
+
const addr = normaliseGovernor(governor);
|
|
3971
|
+
const g = new Contract(addr, ABI.Governor, provider);
|
|
3972
|
+
const results = await Promise.all([
|
|
3973
|
+
g.name().catch(() => null),
|
|
3974
|
+
g.votingDelay().catch(() => null),
|
|
3975
|
+
g.votingPeriod().catch(() => null),
|
|
3976
|
+
g.proposalThreshold().catch(() => null),
|
|
3977
|
+
g.timelock().catch(async () => {
|
|
3978
|
+
try {
|
|
3979
|
+
return await g._executor();
|
|
3980
|
+
} catch {
|
|
3981
|
+
return null;
|
|
3982
|
+
}
|
|
3983
|
+
}),
|
|
3984
|
+
g.sxxxToken().catch(() => null),
|
|
3985
|
+
// Try quorumVotes first (new fixed model), fall back to quorumNumerator (legacy OZ)
|
|
3986
|
+
g.quorumVotes ? g.quorumVotes().catch(() => null) : null,
|
|
3987
|
+
g.quorumNumerator ? g.quorumNumerator().catch(() => null) : null
|
|
3988
|
+
]);
|
|
3989
|
+
const [name, votingDelay, votingPeriod, proposalThreshold, timelockRaw, sxxxToken, quorumVotes, quorumNumerator] = results;
|
|
3990
|
+
const timelock2 = timelockRaw ? getAddress(timelockRaw) : null;
|
|
3991
|
+
return {
|
|
3992
|
+
address: addr,
|
|
3993
|
+
name,
|
|
3994
|
+
votingDelay,
|
|
3995
|
+
votingPeriod,
|
|
3996
|
+
proposalThreshold,
|
|
3997
|
+
timelock: timelock2,
|
|
3998
|
+
sxxxToken: sxxxToken && /^0x/.test(String(sxxxToken)) ? getAddress(sxxxToken) : null,
|
|
3999
|
+
// Primary: quorumVotes (fixed absolute amount)
|
|
4000
|
+
quorumVotes,
|
|
4001
|
+
// Legacy alias (deprecated)
|
|
4002
|
+
quorumNumerator: quorumNumerator ?? quorumVotes
|
|
4003
|
+
};
|
|
4004
|
+
}
|
|
4005
|
+
async function getGovernorTimepointMinusOne({ provider, governor }) {
|
|
4006
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4007
|
+
const govAddr = normaliseGovernor(governor);
|
|
4008
|
+
try {
|
|
4009
|
+
const iface = new Interface(["function clock() view returns (uint48)"]);
|
|
4010
|
+
const data = iface.encodeFunctionData("clock", []);
|
|
4011
|
+
const ret = await provider.call({ to: govAddr, data });
|
|
4012
|
+
if (ret && ret !== "0x") {
|
|
4013
|
+
const [clk] = AbiCoder.defaultAbiCoder().decode(["uint48"], ret);
|
|
4014
|
+
const now = BigInt(clk.toString());
|
|
4015
|
+
return now > 0n ? now - 1n : 0n;
|
|
4016
|
+
}
|
|
4017
|
+
} catch (_) {
|
|
4018
|
+
}
|
|
4019
|
+
try {
|
|
4020
|
+
const latest = await provider.getBlockNumber();
|
|
4021
|
+
const now = BigInt(latest);
|
|
4022
|
+
return now > 0n ? now - 1n : 0n;
|
|
4023
|
+
} catch (_) {
|
|
4024
|
+
}
|
|
4025
|
+
return null;
|
|
4026
|
+
}
|
|
4027
|
+
async function getGovernorVotesAt({ provider, governor, account, timepoint }) {
|
|
4028
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4029
|
+
if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
|
|
4030
|
+
if (timepoint == null) throw new SageSDKError(CODES.INVALID_ARGS, "timepoint required");
|
|
4031
|
+
const govAddr = normaliseGovernor(governor);
|
|
4032
|
+
const acct = getAddress(account);
|
|
4033
|
+
const tp = typeof timepoint === "bigint" ? timepoint : BigInt(String(timepoint));
|
|
4034
|
+
try {
|
|
4035
|
+
const iface = new Interface(["function getVotes(address,uint256) view returns (uint256)"]);
|
|
4036
|
+
const data = iface.encodeFunctionData("getVotes", [acct, tp]);
|
|
4037
|
+
const ret = await provider.call({ to: govAddr, data });
|
|
4038
|
+
if (ret && ret !== "0x") {
|
|
4039
|
+
const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
|
|
4040
|
+
return BigInt(val.toString());
|
|
4041
|
+
}
|
|
4042
|
+
} catch (_) {
|
|
4043
|
+
}
|
|
4044
|
+
try {
|
|
4045
|
+
const { votingToken } = await resolveVotesTokenChain({ provider, governor: govAddr });
|
|
4046
|
+
const tokenAddr = getAddress(votingToken);
|
|
4047
|
+
const iface = new Interface(["function getPastVotes(address,uint256) view returns (uint256)"]);
|
|
4048
|
+
const data = iface.encodeFunctionData("getPastVotes", [acct, tp]);
|
|
4049
|
+
const ret = await provider.call({ to: tokenAddr, data });
|
|
4050
|
+
if (ret && ret !== "0x") {
|
|
4051
|
+
const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
|
|
4052
|
+
return BigInt(val.toString());
|
|
4053
|
+
}
|
|
4054
|
+
} catch (_) {
|
|
4055
|
+
}
|
|
4056
|
+
return null;
|
|
4057
|
+
}
|
|
4058
|
+
async function getProposalThresholdStatus({ provider, governor, proposer }) {
|
|
4059
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4060
|
+
if (!proposer) throw new SageSDKError(CODES.INVALID_ARGS, "proposer required");
|
|
4061
|
+
const govAddr = normaliseGovernor(governor);
|
|
4062
|
+
const user = getAddress(proposer);
|
|
4063
|
+
const g = new Contract(govAddr, ABI.Governor, provider);
|
|
4064
|
+
const [threshold, sxxxTokenAddr] = await Promise.all([
|
|
4065
|
+
g.proposalThreshold().catch(() => null),
|
|
4066
|
+
g.sxxxToken().catch(() => null)
|
|
4067
|
+
]);
|
|
4068
|
+
const proposalThreshold = threshold != null ? BigInt(threshold.toString()) : null;
|
|
4069
|
+
const sxxxToken = sxxxTokenAddr && /^0x[0-9a-fA-F]{40}$/.test(String(sxxxTokenAddr)) ? getAddress(sxxxTokenAddr) : null;
|
|
4070
|
+
const votesTimepoint = await getGovernorTimepointMinusOne({ provider, governor: govAddr });
|
|
4071
|
+
const proposerVotes = votesTimepoint != null ? await getGovernorVotesAt({ provider, governor: govAddr, account: user, timepoint: votesTimepoint }) : null;
|
|
4072
|
+
let sxxxBalance = null;
|
|
4073
|
+
if (sxxxToken) {
|
|
4074
|
+
try {
|
|
4075
|
+
const iface = new Interface(["function balanceOf(address) view returns (uint256)"]);
|
|
4076
|
+
const data = iface.encodeFunctionData("balanceOf", [user]);
|
|
4077
|
+
const ret = await provider.call({ to: sxxxToken, data });
|
|
4078
|
+
if (ret && ret !== "0x") {
|
|
4079
|
+
const [bal] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
|
|
4080
|
+
sxxxBalance = BigInt(bal.toString());
|
|
4081
|
+
}
|
|
4082
|
+
} catch (_) {
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
const thresholdMet = proposalThreshold != null && proposerVotes != null ? proposerVotes >= proposalThreshold : null;
|
|
4086
|
+
let cooldownRemaining = null;
|
|
4087
|
+
let canPropose = null;
|
|
4088
|
+
try {
|
|
4089
|
+
const iface = new Interface(["function getProposalCooldownInfo(address) view returns (bool canPropose, uint256 cooldownRemaining)"]);
|
|
4090
|
+
const data = iface.encodeFunctionData("getProposalCooldownInfo", [user]);
|
|
4091
|
+
const ret = await provider.call({ to: govAddr, data });
|
|
4092
|
+
if (ret && ret !== "0x") {
|
|
4093
|
+
const decoded = AbiCoder.defaultAbiCoder().decode(["bool", "uint256"], ret);
|
|
4094
|
+
canPropose = decoded[0];
|
|
4095
|
+
cooldownRemaining = BigInt(decoded[1].toString());
|
|
4096
|
+
}
|
|
4097
|
+
} catch (_) {
|
|
4098
|
+
try {
|
|
4099
|
+
const cooldownIface = new Interface(["function proposalCooldown() view returns (uint256)"]);
|
|
4100
|
+
const lastIface = new Interface(["function lastProposalTimestamp(address) view returns (uint256)"]);
|
|
4101
|
+
const [cooldownRet, lastRet] = await Promise.all([
|
|
4102
|
+
provider.call({ to: govAddr, data: cooldownIface.encodeFunctionData("proposalCooldown", []) }).catch(() => null),
|
|
4103
|
+
provider.call({ to: govAddr, data: lastIface.encodeFunctionData("lastProposalTimestamp", [user]) }).catch(() => null)
|
|
4104
|
+
]);
|
|
4105
|
+
if (cooldownRet && cooldownRet !== "0x" && lastRet && lastRet !== "0x") {
|
|
4106
|
+
const [cooldown] = AbiCoder.defaultAbiCoder().decode(["uint256"], cooldownRet);
|
|
4107
|
+
const [last] = AbiCoder.defaultAbiCoder().decode(["uint256"], lastRet);
|
|
4108
|
+
const block = await provider.getBlock("latest").catch(() => null);
|
|
4109
|
+
if (block && block.timestamp != null) {
|
|
4110
|
+
const now = BigInt(block.timestamp);
|
|
4111
|
+
const endsAt = BigInt(last.toString()) + BigInt(cooldown.toString());
|
|
4112
|
+
cooldownRemaining = endsAt > now ? endsAt - now : 0n;
|
|
4113
|
+
canPropose = cooldownRemaining === 0n;
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
} catch (_2) {
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
const canProposeOverall = thresholdMet === true && (canPropose === true || canPropose === null);
|
|
4120
|
+
return {
|
|
4121
|
+
governor: govAddr,
|
|
4122
|
+
proposer: user,
|
|
4123
|
+
proposalThreshold,
|
|
4124
|
+
sxxxToken,
|
|
4125
|
+
sxxxBalance,
|
|
4126
|
+
proposerVotes,
|
|
4127
|
+
votesTimepoint,
|
|
4128
|
+
thresholdMet,
|
|
4129
|
+
cooldownRemaining,
|
|
4130
|
+
cooldownElapsed: cooldownRemaining != null ? cooldownRemaining === 0n : null,
|
|
4131
|
+
canPropose: canProposeOverall
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
async function getProposalCancelability({ provider, governor, proposalId }) {
|
|
4135
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4136
|
+
if (proposalId == null) throw new SageSDKError(CODES.INVALID_ARGS, "proposalId required");
|
|
4137
|
+
const govAddr = normaliseGovernor(governor);
|
|
4138
|
+
const pid = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(proposalId) : BigInt(String(proposalId));
|
|
4139
|
+
let proposalProposer = null;
|
|
4140
|
+
try {
|
|
4141
|
+
const iface = new Interface(["function proposalProposer(uint256) view returns (address)"]);
|
|
4142
|
+
const data = iface.encodeFunctionData("proposalProposer", [pid]);
|
|
4143
|
+
const ret = await provider.call({ to: govAddr, data });
|
|
4144
|
+
if (ret && ret !== "0x") {
|
|
4145
|
+
const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], ret);
|
|
4146
|
+
if (addr && /^0x[0-9a-fA-F]{40}$/.test(String(addr)) && addr !== ZeroAddress) {
|
|
4147
|
+
proposalProposer = getAddress(addr);
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
} catch (_) {
|
|
4151
|
+
}
|
|
4152
|
+
let proposalCreator = null;
|
|
4153
|
+
if (proposalProposer) {
|
|
4154
|
+
proposalCreator = proposalProposer;
|
|
4155
|
+
} else {
|
|
4156
|
+
try {
|
|
4157
|
+
const iface = new Interface(["function proposalCreator(uint256) view returns (address)"]);
|
|
4158
|
+
const data = iface.encodeFunctionData("proposalCreator", [pid]);
|
|
4159
|
+
const ret = await provider.call({ to: govAddr, data });
|
|
4160
|
+
if (ret && ret !== "0x") {
|
|
4161
|
+
const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], ret);
|
|
4162
|
+
if (addr && /^0x[0-9a-fA-F]{40}$/.test(String(addr)) && addr !== ZeroAddress) {
|
|
4163
|
+
proposalCreator = getAddress(addr);
|
|
4164
|
+
proposalProposer = proposalCreator;
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
} catch (_) {
|
|
3451
4168
|
}
|
|
3452
|
-
},
|
|
3453
|
-
executionReadiness: async function executionReadiness({ provider, registry, timelock: timelock2, subdao: subdao2, manifestCID = "", version = "1.0.0" }) {
|
|
3454
|
-
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
3455
|
-
const payload = buildUpdateLibraryTx({ registry, subdao: subdao2, manifestCID, version });
|
|
3456
|
-
const sim = await this.simulateAsTimelock({ provider, registry, to: payload.to, data: payload.data, timelock: timelock2 });
|
|
3457
|
-
const out = { ok: !!sim.ok, error: sim.ok ? null : sim.error?.message || "revert", missingRole: null };
|
|
3458
|
-
return out;
|
|
3459
4169
|
}
|
|
3460
|
-
|
|
3461
|
-
}
|
|
3462
|
-
});
|
|
3463
|
-
|
|
3464
|
-
// src/governance/index.js
|
|
3465
|
-
var require_governance = __commonJS({
|
|
3466
|
-
"src/governance/index.js"(exports2, module2) {
|
|
3467
|
-
var { Contract, Interface, AbiCoder, getAddress, hexlify, keccak256, toUtf8Bytes } = require("ethers");
|
|
3468
|
-
var ABI = require_abi();
|
|
3469
|
-
var { BigIntZero } = require_types();
|
|
3470
|
-
var { SageSDKError, CODES } = require_errors();
|
|
3471
|
-
var { makeProposalDescription, stripProposalSalt } = require_description();
|
|
3472
|
-
var subgraph2 = require_subgraph();
|
|
3473
|
-
var adapters2 = require_transports();
|
|
3474
|
-
function normaliseGovernor(governor) {
|
|
3475
|
-
if (!governor) throw new SageSDKError(CODES.INVALID_ARGS, "governor required");
|
|
4170
|
+
let proposalThresholdSnapshot = null;
|
|
3476
4171
|
try {
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
4172
|
+
const iface = new Interface(["function proposalThresholdSnapshot(uint256) view returns (uint256)"]);
|
|
4173
|
+
const data = iface.encodeFunctionData("proposalThresholdSnapshot", [pid]);
|
|
4174
|
+
const ret = await provider.call({ to: govAddr, data });
|
|
4175
|
+
if (ret && ret !== "0x") {
|
|
4176
|
+
const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
|
|
4177
|
+
proposalThresholdSnapshot = BigInt(val.toString());
|
|
4178
|
+
}
|
|
4179
|
+
} catch (_) {
|
|
3480
4180
|
}
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
const addr = normaliseGovernor(governor);
|
|
3485
|
-
const g = new Contract(addr, ABI.Governor, provider);
|
|
3486
|
-
const results = await Promise.all([
|
|
3487
|
-
g.name().catch(() => null),
|
|
3488
|
-
g.votingDelay().catch(() => null),
|
|
3489
|
-
g.votingPeriod().catch(() => null),
|
|
4181
|
+
const g = new Contract(govAddr, ABI.Governor, provider);
|
|
4182
|
+
const [state, threshold, sxxxTokenAddr] = await Promise.all([
|
|
4183
|
+
g.state(pid).catch(() => null),
|
|
3490
4184
|
g.proposalThreshold().catch(() => null),
|
|
3491
|
-
g.
|
|
3492
|
-
try {
|
|
3493
|
-
return await g._executor();
|
|
3494
|
-
} catch {
|
|
3495
|
-
return null;
|
|
3496
|
-
}
|
|
3497
|
-
}),
|
|
3498
|
-
g.sxxxToken().catch(() => null),
|
|
3499
|
-
g.quorumNumerator ? g.quorumNumerator().catch(() => null) : null,
|
|
3500
|
-
g.stakeAmount ? g.stakeAmount().catch(() => null) : null
|
|
4185
|
+
g.sxxxToken().catch(() => null)
|
|
3501
4186
|
]);
|
|
3502
|
-
const
|
|
3503
|
-
const
|
|
4187
|
+
const proposalState = state != null ? Number(state) : null;
|
|
4188
|
+
const proposalThresholdCurrent = threshold != null ? BigInt(threshold.toString()) : null;
|
|
4189
|
+
const sxxxToken = sxxxTokenAddr && /^0x[0-9a-fA-F]{40}$/.test(String(sxxxTokenAddr)) ? getAddress(sxxxTokenAddr) : null;
|
|
4190
|
+
const cancelThreshold = proposalThresholdSnapshot != null ? proposalThresholdSnapshot : proposalThresholdCurrent;
|
|
4191
|
+
const votesTimepoint = await getGovernorTimepointMinusOne({ provider, governor: govAddr });
|
|
4192
|
+
const proposerVotes = proposalProposer && votesTimepoint != null ? await getGovernorVotesAt({ provider, governor: govAddr, account: proposalProposer, timepoint: votesTimepoint }) : null;
|
|
4193
|
+
let proposerBalance = null;
|
|
4194
|
+
if (proposalProposer && sxxxToken) {
|
|
4195
|
+
try {
|
|
4196
|
+
const iface = new Interface(["function balanceOf(address) view returns (uint256)"]);
|
|
4197
|
+
const data = iface.encodeFunctionData("balanceOf", [proposalProposer]);
|
|
4198
|
+
const ret = await provider.call({ to: sxxxToken, data });
|
|
4199
|
+
if (ret && ret !== "0x") {
|
|
4200
|
+
const [bal] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
|
|
4201
|
+
proposerBalance = BigInt(bal.toString());
|
|
4202
|
+
}
|
|
4203
|
+
} catch (_) {
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
const cancelableStates = /* @__PURE__ */ new Set([0, 1]);
|
|
4207
|
+
const isInCancelableState = proposalState != null && cancelableStates.has(proposalState);
|
|
4208
|
+
const proposerBelowThreshold = cancelThreshold != null && proposerVotes != null ? proposerVotes < cancelThreshold : null;
|
|
4209
|
+
const isCancelableByAnyone = isInCancelableState && proposerBelowThreshold === true;
|
|
3504
4210
|
return {
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
4211
|
+
governor: govAddr,
|
|
4212
|
+
proposalId: pid,
|
|
4213
|
+
proposalProposer,
|
|
4214
|
+
proposalCreator,
|
|
4215
|
+
proposalState,
|
|
4216
|
+
proposalThresholdSnapshot,
|
|
4217
|
+
proposalThresholdCurrent,
|
|
4218
|
+
cancelThreshold,
|
|
4219
|
+
sxxxToken,
|
|
4220
|
+
proposerBalance,
|
|
4221
|
+
votesTimepoint,
|
|
4222
|
+
proposerVotes,
|
|
4223
|
+
proposerBelowThreshold,
|
|
4224
|
+
isInCancelableState,
|
|
4225
|
+
isCancelableByAnyone,
|
|
4226
|
+
cancelReason: isCancelableByAnyone ? "Proposer voting power dropped below proposal threshold" : isInCancelableState ? "Only proposer can cancel (above threshold)" : "Proposal not in cancelable state",
|
|
4227
|
+
// Backward-compat aliases
|
|
4228
|
+
proposalThreshold: cancelThreshold,
|
|
4229
|
+
creatorBalance: proposerBalance,
|
|
4230
|
+
creatorBelowThreshold: proposerBelowThreshold
|
|
3514
4231
|
};
|
|
3515
4232
|
}
|
|
3516
4233
|
async function getProposal({ provider, governor, id: id2 }) {
|
|
@@ -3707,6 +4424,7 @@ var require_governance = __commonJS({
|
|
|
3707
4424
|
}
|
|
3708
4425
|
var buildQueueTx = (args) => cueTx("queue(address[],uint256[],bytes[],bytes32)", args);
|
|
3709
4426
|
var buildExecuteTx = (args) => cueTx("execute(address[],uint256[],bytes[],bytes32)", args);
|
|
4427
|
+
var buildCancelTx = (args) => cueTx("cancel(address[],uint256[],bytes[],bytes32)", args);
|
|
3710
4428
|
function buildQueueByIdTx({ governor, proposalId }) {
|
|
3711
4429
|
const addr = normaliseGovernor(governor);
|
|
3712
4430
|
const iface = new Interface(ABI.Governor);
|
|
@@ -3853,6 +4571,47 @@ var require_governance = __commonJS({
|
|
|
3853
4571
|
}
|
|
3854
4572
|
return 0n;
|
|
3855
4573
|
}
|
|
4574
|
+
async function getVotesLatestMinusOneWithTimepoint({ provider, token: token2, governor, account }) {
|
|
4575
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4576
|
+
if (!token2 && !governor) throw new SageSDKError(CODES.INVALID_ARGS, "token or governor required");
|
|
4577
|
+
if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
|
|
4578
|
+
let addr;
|
|
4579
|
+
if (token2) {
|
|
4580
|
+
addr = getAddress(token2);
|
|
4581
|
+
} else {
|
|
4582
|
+
const govAddr = normaliseGovernor(governor);
|
|
4583
|
+
try {
|
|
4584
|
+
const chain = await resolveVotesTokenChain({ provider, governor: govAddr });
|
|
4585
|
+
addr = getAddress(chain.votingToken);
|
|
4586
|
+
} catch (err) {
|
|
4587
|
+
throw new SageSDKError(CODES.NOT_FOUND, "failed to resolve votes token from governor", { cause: err });
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
const user = getAddress(account);
|
|
4591
|
+
const latest = await provider.getBlockNumber();
|
|
4592
|
+
const timepoint = latest > 0 ? BigInt(latest - 1) : 0n;
|
|
4593
|
+
try {
|
|
4594
|
+
const iface = new Interface(["function getPastVotes(address,uint256) view returns (uint256)"]);
|
|
4595
|
+
const data = iface.encodeFunctionData("getPastVotes", [user, timepoint]);
|
|
4596
|
+
const ret = await provider.call({ to: addr, data });
|
|
4597
|
+
if (ret && ret !== "0x") {
|
|
4598
|
+
const [val] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret);
|
|
4599
|
+
return { votes: BigInt(val.toString()), timepoint };
|
|
4600
|
+
}
|
|
4601
|
+
} catch (_) {
|
|
4602
|
+
}
|
|
4603
|
+
try {
|
|
4604
|
+
const iface2 = new Interface(["function getVotes(address) view returns (uint256)"]);
|
|
4605
|
+
const d2 = iface2.encodeFunctionData("getVotes", [user]);
|
|
4606
|
+
const ret2 = await provider.call({ to: addr, data: d2 });
|
|
4607
|
+
if (ret2 && ret2 !== "0x") {
|
|
4608
|
+
const [val2] = AbiCoder.defaultAbiCoder().decode(["uint256"], ret2);
|
|
4609
|
+
return { votes: BigInt(val2.toString()), timepoint };
|
|
4610
|
+
}
|
|
4611
|
+
} catch (_) {
|
|
4612
|
+
}
|
|
4613
|
+
return { votes: 0n, timepoint };
|
|
4614
|
+
}
|
|
3856
4615
|
async function simulatePropose({ provider, governor, targets = [], values = [], calldatas = [], description = "", sender }) {
|
|
3857
4616
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
3858
4617
|
const addr = normaliseGovernor(governor);
|
|
@@ -3902,13 +4661,18 @@ var require_governance = __commonJS({
|
|
|
3902
4661
|
async function buildDelegateSelfTx({ provider, governor, account }) {
|
|
3903
4662
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
3904
4663
|
const govAddr = normaliseGovernor(governor);
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
4664
|
+
try {
|
|
4665
|
+
const { baseToken } = await resolveVotesTokenChain({ provider, governor: govAddr });
|
|
4666
|
+
return buildDelegateTx({ token: baseToken, delegatee: account });
|
|
4667
|
+
} catch (_) {
|
|
4668
|
+
const g = new Contract(govAddr, ABI.Governor, provider);
|
|
4669
|
+
const token2 = await g.sxxxToken();
|
|
4670
|
+
return buildDelegateTx({ token: token2, delegatee: account });
|
|
4671
|
+
}
|
|
3908
4672
|
}
|
|
3909
|
-
async function resolveVotesTokenChain({ provider, subdao:
|
|
4673
|
+
async function resolveVotesTokenChain({ provider, subdao: subdao3, governor }) {
|
|
3910
4674
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
3911
|
-
if (!
|
|
4675
|
+
if (!subdao3 && !governor) throw new SageSDKError(CODES.INVALID_ARGS, "subdao or governor required");
|
|
3912
4676
|
let votingToken = null;
|
|
3913
4677
|
if (governor) {
|
|
3914
4678
|
const govAddr = normaliseGovernor(governor);
|
|
@@ -3935,19 +4699,6 @@ var require_governance = __commonJS({
|
|
|
3935
4699
|
}
|
|
3936
4700
|
}
|
|
3937
4701
|
}
|
|
3938
|
-
if (!votingToken && subdao2) {
|
|
3939
|
-
try {
|
|
3940
|
-
const subAddr = getAddress(subdao2);
|
|
3941
|
-
const iSub = new Interface(["function stakeToken() view returns (address)"]);
|
|
3942
|
-
const d3 = iSub.encodeFunctionData("stakeToken", []);
|
|
3943
|
-
const r3 = await provider.call({ to: subAddr, data: d3 });
|
|
3944
|
-
if (r3 && r3 !== "0x") {
|
|
3945
|
-
const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], r3);
|
|
3946
|
-
votingToken = addr;
|
|
3947
|
-
}
|
|
3948
|
-
} catch (_) {
|
|
3949
|
-
}
|
|
3950
|
-
}
|
|
3951
4702
|
if (!votingToken) {
|
|
3952
4703
|
throw new SageSDKError(CODES.NOT_FOUND, "failed to resolve votes token");
|
|
3953
4704
|
}
|
|
@@ -3968,7 +4719,7 @@ var require_governance = __commonJS({
|
|
|
3968
4719
|
const [decodedDao] = AbiCoder.defaultAbiCoder().decode(["address"], rawDao);
|
|
3969
4720
|
const baseNorm = getAddress(decodedBase);
|
|
3970
4721
|
const daoNorm = getAddress(decodedDao);
|
|
3971
|
-
if (!
|
|
4722
|
+
if (!subdao3 || daoNorm === getAddress(subdao3)) {
|
|
3972
4723
|
baseToken = baseNorm;
|
|
3973
4724
|
isWrapper = true;
|
|
3974
4725
|
}
|
|
@@ -3977,8 +4728,8 @@ var require_governance = __commonJS({
|
|
|
3977
4728
|
}
|
|
3978
4729
|
return { votingToken: votingTokenNorm, baseToken, isWrapper };
|
|
3979
4730
|
}
|
|
3980
|
-
async function describeVotesToken({ provider, subdao:
|
|
3981
|
-
const { votingToken, baseToken, isWrapper } = await resolveVotesTokenChain({ provider, subdao:
|
|
4731
|
+
async function describeVotesToken({ provider, subdao: subdao3, governor }) {
|
|
4732
|
+
const { votingToken, baseToken, isWrapper } = await resolveVotesTokenChain({ provider, subdao: subdao3, governor });
|
|
3982
4733
|
let multiplierNFT = null;
|
|
3983
4734
|
let basis = null;
|
|
3984
4735
|
if (isWrapper) {
|
|
@@ -3999,8 +4750,8 @@ var require_governance = __commonJS({
|
|
|
3999
4750
|
} else {
|
|
4000
4751
|
const basisStr = basisBig != null ? basisBig.toString() : "10000";
|
|
4001
4752
|
description = `Voting token = MultipliedVotes(base=${baseNorm}, multiplierNFT=${multiplierNorm || "unknown"}, BASIS=${basisStr})`;
|
|
4002
|
-
if (
|
|
4003
|
-
description += ` for DAO ${getAddress(
|
|
4753
|
+
if (subdao3) {
|
|
4754
|
+
description += ` for DAO ${getAddress(subdao3)}`;
|
|
4004
4755
|
}
|
|
4005
4756
|
}
|
|
4006
4757
|
return {
|
|
@@ -4012,9 +4763,9 @@ var require_governance = __commonJS({
|
|
|
4012
4763
|
description
|
|
4013
4764
|
};
|
|
4014
4765
|
}
|
|
4015
|
-
async function getVotingStatus({ provider, subdao:
|
|
4766
|
+
async function getVotingStatus({ provider, subdao: subdao3, governor, account }) {
|
|
4016
4767
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4017
|
-
if (!governor && !
|
|
4768
|
+
if (!governor && !subdao3) throw new SageSDKError(CODES.INVALID_ARGS, "governor or subdao required");
|
|
4018
4769
|
if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
|
|
4019
4770
|
const acct = getAddress(account);
|
|
4020
4771
|
let govAddr = null;
|
|
@@ -4022,8 +4773,8 @@ var require_governance = __commonJS({
|
|
|
4022
4773
|
if (governor) {
|
|
4023
4774
|
govAddr = normaliseGovernor(governor);
|
|
4024
4775
|
}
|
|
4025
|
-
if (
|
|
4026
|
-
subdaoAddr = getAddress(
|
|
4776
|
+
if (subdao3) {
|
|
4777
|
+
subdaoAddr = getAddress(subdao3);
|
|
4027
4778
|
if (!govAddr) {
|
|
4028
4779
|
try {
|
|
4029
4780
|
const sub = new Contract(subdaoAddr, ["function governor() view returns (address)"], provider);
|
|
@@ -4041,17 +4792,6 @@ var require_governance = __commonJS({
|
|
|
4041
4792
|
} catch (_) {
|
|
4042
4793
|
}
|
|
4043
4794
|
const desc = await describeVotesToken({ provider, subdao: subdaoAddr || void 0, governor: govAddr });
|
|
4044
|
-
let stakeToken = null;
|
|
4045
|
-
if (subdaoAddr) {
|
|
4046
|
-
try {
|
|
4047
|
-
const sub = new Contract(subdaoAddr, ["function stakeToken() view returns (address)"], provider);
|
|
4048
|
-
const st = await sub.stakeToken();
|
|
4049
|
-
if (st && /^0x[0-9a-fA-F]{40}$/.test(String(st))) {
|
|
4050
|
-
stakeToken = getAddress(st);
|
|
4051
|
-
}
|
|
4052
|
-
} catch (_) {
|
|
4053
|
-
}
|
|
4054
|
-
}
|
|
4055
4795
|
const VotesABI = [
|
|
4056
4796
|
"function getVotes(address) view returns (uint256)",
|
|
4057
4797
|
"function delegates(address) view returns (address)",
|
|
@@ -4071,28 +4811,9 @@ var require_governance = __commonJS({
|
|
|
4071
4811
|
} catch (_) {
|
|
4072
4812
|
}
|
|
4073
4813
|
const canPropose = threshold != null && votingPower != null ? votingPower >= threshold : null;
|
|
4074
|
-
let stakeTokenVotes = null;
|
|
4075
|
-
let stakeTokenDelegate = null;
|
|
4076
|
-
let stakeTokenBalance = null;
|
|
4077
|
-
const tokenMismatch = !!stakeToken && stakeToken.toLowerCase() !== desc.votingToken.toLowerCase();
|
|
4078
|
-
if (stakeToken && tokenMismatch) {
|
|
4079
|
-
try {
|
|
4080
|
-
const stakeC = new Contract(stakeToken, VotesABI, provider);
|
|
4081
|
-
const sv = await stakeC.getVotes(acct).catch(() => null);
|
|
4082
|
-
if (sv != null) stakeTokenVotes = BigInt(sv.toString());
|
|
4083
|
-
const sd = await stakeC.delegates(acct).catch(() => null);
|
|
4084
|
-
if (sd && /^0x[0-9a-fA-F]{40}$/.test(String(sd))) {
|
|
4085
|
-
stakeTokenDelegate = getAddress(sd);
|
|
4086
|
-
}
|
|
4087
|
-
const sb = await stakeC.balanceOf(acct).catch(() => null);
|
|
4088
|
-
if (sb != null) stakeTokenBalance = BigInt(sb.toString());
|
|
4089
|
-
} catch (_) {
|
|
4090
|
-
}
|
|
4091
|
-
}
|
|
4092
4814
|
return {
|
|
4093
4815
|
subdao: subdaoAddr,
|
|
4094
4816
|
governor: govAddr,
|
|
4095
|
-
stakeToken,
|
|
4096
4817
|
votingToken: desc.votingToken,
|
|
4097
4818
|
baseToken: desc.baseToken,
|
|
4098
4819
|
isWrapper: desc.isWrapper,
|
|
@@ -4103,10 +4824,6 @@ var require_governance = __commonJS({
|
|
|
4103
4824
|
tokenBalance,
|
|
4104
4825
|
delegate,
|
|
4105
4826
|
canPropose,
|
|
4106
|
-
tokenMismatch,
|
|
4107
|
-
stakeTokenVotes,
|
|
4108
|
-
stakeTokenDelegate,
|
|
4109
|
-
stakeTokenBalance,
|
|
4110
4827
|
description: desc.description
|
|
4111
4828
|
};
|
|
4112
4829
|
}
|
|
@@ -4120,6 +4837,7 @@ var require_governance = __commonJS({
|
|
|
4120
4837
|
buildCastVoteTx,
|
|
4121
4838
|
buildQueueTx,
|
|
4122
4839
|
buildExecuteTx,
|
|
4840
|
+
buildCancelTx,
|
|
4123
4841
|
buildQueueByIdTx,
|
|
4124
4842
|
buildExecuteByIdTx,
|
|
4125
4843
|
decodeProposalEffects,
|
|
@@ -4132,16 +4850,19 @@ var require_governance = __commonJS({
|
|
|
4132
4850
|
buildDelegateSelfTx,
|
|
4133
4851
|
computeProposalIdHex,
|
|
4134
4852
|
getVotesLatestMinusOne,
|
|
4853
|
+
getVotesLatestMinusOneWithTimepoint,
|
|
4135
4854
|
simulatePropose,
|
|
4855
|
+
// Threshold model (PromptGovernor)
|
|
4856
|
+
getProposalThresholdStatus,
|
|
4857
|
+
getProposalCancelability,
|
|
4136
4858
|
/**
|
|
4137
4859
|
* Resolve the IVotes token used for governance voting.
|
|
4138
4860
|
* Preference order:
|
|
4139
4861
|
* - Governor.token() (if exposed)
|
|
4140
4862
|
* - Governor.sxxxToken()
|
|
4141
|
-
* - SubDAO.stakeToken() (when subdao provided)
|
|
4142
4863
|
*/
|
|
4143
|
-
resolveVotesToken: async function resolveVotesToken({ provider, subdao:
|
|
4144
|
-
const { votingToken } = await resolveVotesTokenChain({ provider, subdao:
|
|
4864
|
+
resolveVotesToken: async function resolveVotesToken({ provider, subdao: subdao3, governor }) {
|
|
4865
|
+
const { votingToken } = await resolveVotesTokenChain({ provider, subdao: subdao3, governor });
|
|
4145
4866
|
return votingToken;
|
|
4146
4867
|
},
|
|
4147
4868
|
/**
|
|
@@ -4158,20 +4879,20 @@ var require_governance = __commonJS({
|
|
|
4158
4879
|
*/
|
|
4159
4880
|
getVotingStatus,
|
|
4160
4881
|
/** Build a delegate(self) tx using the preferred votes token resolution path. */
|
|
4161
|
-
buildDelegateSelfPreferred: async function buildDelegateSelfPreferred({ provider, subdao:
|
|
4882
|
+
buildDelegateSelfPreferred: async function buildDelegateSelfPreferred({ provider, subdao: subdao3, governor, account }) {
|
|
4162
4883
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4163
4884
|
if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
|
|
4164
|
-
const { baseToken } = await resolveVotesTokenChain({ provider, subdao:
|
|
4885
|
+
const { baseToken } = await resolveVotesTokenChain({ provider, subdao: subdao3, governor });
|
|
4165
4886
|
return buildDelegateTx({ token: baseToken, delegatee: account });
|
|
4166
4887
|
},
|
|
4167
4888
|
/**
|
|
4168
4889
|
* Send a delegate(self) and verify votes at latest-1.
|
|
4169
4890
|
* If signer is omitted, returns payload and current votes without sending.
|
|
4170
4891
|
*/
|
|
4171
|
-
delegateSelfAndVerify: async function delegateSelfAndVerify({ provider, subdao:
|
|
4892
|
+
delegateSelfAndVerify: async function delegateSelfAndVerify({ provider, subdao: subdao3, governor, account, signer = null, minVotes = null }) {
|
|
4172
4893
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4173
4894
|
if (!account) throw new SageSDKError(CODES.INVALID_ARGS, "account required");
|
|
4174
|
-
const { votingToken, baseToken } = await resolveVotesTokenChain({ provider, subdao:
|
|
4895
|
+
const { votingToken, baseToken } = await resolveVotesTokenChain({ provider, subdao: subdao3, governor });
|
|
4175
4896
|
const payload = buildDelegateTx({ token: baseToken, delegatee: account });
|
|
4176
4897
|
let txHash = null;
|
|
4177
4898
|
if (signer && typeof signer.sendTransaction === "function") {
|
|
@@ -4184,7 +4905,8 @@ var require_governance = __commonJS({
|
|
|
4184
4905
|
return { txHash, ok, votes, payload };
|
|
4185
4906
|
},
|
|
4186
4907
|
/**
|
|
4187
|
-
* Ensure propose gates based on
|
|
4908
|
+
* Ensure propose gates based on THRESHOLD MODEL (votes-based threshold + cooldown).
|
|
4909
|
+
* No stake or allowance checks - proposer just needs sufficient voting power at clock()-1.
|
|
4188
4910
|
* Returns a non-throwing summary.
|
|
4189
4911
|
*/
|
|
4190
4912
|
ensureProposeGates: async function ensureProposeGates({ provider, governor, proposer }) {
|
|
@@ -4192,90 +4914,488 @@ var require_governance = __commonJS({
|
|
|
4192
4914
|
const govAddr = normaliseGovernor(governor);
|
|
4193
4915
|
const user = getAddress(proposer);
|
|
4194
4916
|
const g = new Contract(govAddr, ABI.Governor, provider);
|
|
4195
|
-
const
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4917
|
+
const status = await getProposalThresholdStatus({ provider, governor: govAddr, proposer: user });
|
|
4918
|
+
return {
|
|
4919
|
+
// Core threshold model
|
|
4920
|
+
threshold: status.proposalThreshold,
|
|
4921
|
+
sxxxBalance: status.sxxxBalance,
|
|
4922
|
+
thresholdMet: status.thresholdMet,
|
|
4923
|
+
cooldownRemaining: status.cooldownRemaining,
|
|
4924
|
+
cooldownElapsed: status.cooldownElapsed,
|
|
4925
|
+
canPropose: status.canPropose,
|
|
4926
|
+
// Voting power (canonical: clock()-1)
|
|
4927
|
+
votesTimepoint: status.votesTimepoint,
|
|
4928
|
+
votingPower: status.proposerVotes,
|
|
4929
|
+
votesOk: status.thresholdMet,
|
|
4930
|
+
// Details
|
|
4931
|
+
details: { sxxxToken: status.sxxxToken, proposer: user, governor: govAddr },
|
|
4932
|
+
cooldown: status.cooldownRemaining
|
|
4933
|
+
// alias for cooldownRemaining
|
|
4934
|
+
};
|
|
4935
|
+
},
|
|
4936
|
+
/**
|
|
4937
|
+
* Compose propose readiness with optional execution readiness checks.
|
|
4938
|
+
* execution?: { provider, registry, timelock, subdao, manifestCID, version }
|
|
4939
|
+
*/
|
|
4940
|
+
readinessToPropose: async function readinessToPropose({ provider, governor, proposer, execution = null }) {
|
|
4941
|
+
const gates = await this.ensureProposeGates({ provider, governor, proposer });
|
|
4942
|
+
let executionReady = null;
|
|
4943
|
+
let executionError = null;
|
|
4944
|
+
if (execution && execution.registry && execution.timelock && execution.subdao) {
|
|
4945
|
+
try {
|
|
4946
|
+
const library2 = require_library();
|
|
4947
|
+
const { to, data } = library2.buildUpdateLibraryTx({
|
|
4948
|
+
registry: execution.registry,
|
|
4949
|
+
subdao: execution.subdao,
|
|
4950
|
+
manifestCID: execution.manifestCID || "",
|
|
4951
|
+
version: execution.version || "1.0.0"
|
|
4952
|
+
});
|
|
4953
|
+
const sim = await library2.simulateAsTimelock({ provider, registry: execution.registry, to, data, timelock: execution.timelock });
|
|
4954
|
+
executionReady = !!sim.ok;
|
|
4955
|
+
executionError = sim.ok ? null : sim.error?.message || "revert";
|
|
4956
|
+
} catch (err) {
|
|
4957
|
+
executionReady = false;
|
|
4958
|
+
executionError = String(err && err.message || err);
|
|
4959
|
+
}
|
|
4202
4960
|
}
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4961
|
+
return { ...gates, executionReady, executionError };
|
|
4962
|
+
},
|
|
4963
|
+
/**
|
|
4964
|
+
* Unified authority + readiness envelope for agent-native governance UX.
|
|
4965
|
+
*
|
|
4966
|
+
* This is intentionally **read-only**: it returns diagnostics and optional tx payloads/CLI commands,
|
|
4967
|
+
* but never sends transactions.
|
|
4968
|
+
*
|
|
4969
|
+
* Inputs:
|
|
4970
|
+
* - provider (required)
|
|
4971
|
+
* - subdao OR governor (required)
|
|
4972
|
+
* - account (optional, enables per-account readiness checks)
|
|
4973
|
+
* - action: propose | vote | queue | execute | timelock-schedule | update-library
|
|
4974
|
+
* - proposalId (optional, for queue/execute diagnostics)
|
|
4975
|
+
* - libraryRegistry / dao / manifestCID / version (optional, for update-library diagnostics)
|
|
4976
|
+
*/
|
|
4977
|
+
getAuthorityAndReadiness: async function getAuthorityAndReadiness({
|
|
4978
|
+
provider,
|
|
4979
|
+
subdao: subdaoAddress = null,
|
|
4980
|
+
governor: governorAddress = null,
|
|
4981
|
+
account = null,
|
|
4982
|
+
action,
|
|
4983
|
+
proposalId = null,
|
|
4984
|
+
libraryRegistry = null,
|
|
4985
|
+
dao = null,
|
|
4986
|
+
manifestCID = null,
|
|
4987
|
+
version = null
|
|
4988
|
+
} = {}) {
|
|
4989
|
+
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4990
|
+
if (!subdaoAddress && !governorAddress) throw new SageSDKError(CODES.INVALID_ARGS, "subdao or governor required");
|
|
4991
|
+
const actionNorm = String(action || "propose").trim().toLowerCase();
|
|
4992
|
+
const allowed = /* @__PURE__ */ new Set(["propose", "vote", "queue", "execute", "timelock-schedule", "update-library"]);
|
|
4993
|
+
if (!allowed.has(actionNorm)) {
|
|
4994
|
+
throw new SageSDKError(CODES.INVALID_ARGS, `unsupported action: ${String(action || "")}`);
|
|
4995
|
+
}
|
|
4996
|
+
const readiness = [];
|
|
4997
|
+
const push = (item) => readiness.push(item);
|
|
4998
|
+
const normalizeAccount = () => {
|
|
4999
|
+
if (!account) return null;
|
|
5000
|
+
try {
|
|
5001
|
+
return getAddress(account);
|
|
5002
|
+
} catch (err) {
|
|
5003
|
+
throw new SageSDKError(CODES.INVALID_ARGS, "invalid account address", { cause: err });
|
|
5004
|
+
}
|
|
5005
|
+
};
|
|
5006
|
+
const acct = normalizeAccount();
|
|
5007
|
+
const proposal = {
|
|
5008
|
+
proposalThreshold: null,
|
|
5009
|
+
sxxxToken: null,
|
|
5010
|
+
proposerSxxxBalance: null,
|
|
5011
|
+
thresholdMet: null,
|
|
5012
|
+
cooldownRemaining: null,
|
|
5013
|
+
cooldownElapsed: null,
|
|
5014
|
+
proposerVotes: null,
|
|
5015
|
+
votesTimepoint: null,
|
|
5016
|
+
cancelPolicy: {
|
|
5017
|
+
enabled: null,
|
|
5018
|
+
cancelableStates: ["Pending", "Active"],
|
|
5019
|
+
thresholdSource: "proposalThresholdSnapshot",
|
|
5020
|
+
note: "Keep voting power (delegation + multipliers) above proposalThreshold while proposal is active, or it may be canceled by anyone"
|
|
5021
|
+
},
|
|
5022
|
+
cancelability: null
|
|
5023
|
+
};
|
|
5024
|
+
let subdaoAddr = subdaoAddress ? getAddress(subdaoAddress) : null;
|
|
5025
|
+
let governorAddr = governorAddress ? normaliseGovernor(governorAddress) : null;
|
|
5026
|
+
let timelockAddr = null;
|
|
5027
|
+
let opsSafe = null;
|
|
5028
|
+
if (subdaoAddr) {
|
|
4209
5029
|
try {
|
|
4210
|
-
const
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
5030
|
+
const info = await subdao2.getSubDAOInfo({ provider, subdao: subdaoAddr });
|
|
5031
|
+
governorAddr = governorAddr || info.governor;
|
|
5032
|
+
timelockAddr = info.timelock || timelockAddr;
|
|
5033
|
+
opsSafe = info.treasury || null;
|
|
5034
|
+
} catch (err) {
|
|
5035
|
+
push({
|
|
5036
|
+
item: "Resolve SubDAO context",
|
|
5037
|
+
met: false,
|
|
5038
|
+
severity: "warn",
|
|
5039
|
+
evidence: { subdao: subdaoAddr, error: String(err?.message || err) }
|
|
5040
|
+
});
|
|
4214
5041
|
}
|
|
4215
5042
|
}
|
|
4216
|
-
|
|
4217
|
-
|
|
5043
|
+
if (!governorAddr) {
|
|
5044
|
+
throw new SageSDKError(CODES.INVALID_ARGS, "could not resolve governor address");
|
|
5045
|
+
}
|
|
5046
|
+
let govInfo = null;
|
|
4218
5047
|
try {
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
const [addr] = AbiCoder.defaultAbiCoder().decode(["address"], r4);
|
|
4222
|
-
sxxx = getAddress(addr);
|
|
5048
|
+
govInfo = await getGovernorInfo({ provider, governor: governorAddr });
|
|
5049
|
+
timelockAddr = timelockAddr || govInfo.timelock || null;
|
|
4223
5050
|
} catch (_) {
|
|
4224
5051
|
}
|
|
4225
|
-
|
|
5052
|
+
let governanceProfile = { kind: null, proposalAccess: null, executionAccess: null, profileInitialized: false };
|
|
5053
|
+
if (subdaoAddr) {
|
|
4226
5054
|
try {
|
|
4227
|
-
const
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
5055
|
+
const prof = await subdao2.getGovernanceProfile({ provider, subdao: subdaoAddr });
|
|
5056
|
+
governanceProfile = {
|
|
5057
|
+
kind: prof.kind,
|
|
5058
|
+
proposalAccess: prof.proposalAccess,
|
|
5059
|
+
executionAccess: prof.executionAccess,
|
|
5060
|
+
profileInitialized: !!prof.profileInitialized
|
|
5061
|
+
};
|
|
4231
5062
|
} catch (_) {
|
|
4232
5063
|
}
|
|
4233
5064
|
}
|
|
4234
|
-
let
|
|
5065
|
+
let votesChain = null;
|
|
4235
5066
|
try {
|
|
4236
|
-
|
|
4237
|
-
const votes = await getVotesLatestMinusOne({ provider, token: votingToken, account: user });
|
|
4238
|
-
const th = threshold != null ? BigInt(threshold) : 0n;
|
|
4239
|
-
votesOk = votes >= th;
|
|
5067
|
+
votesChain = await resolveVotesTokenChain({ provider, subdao: subdaoAddr || void 0, governor: governorAddr });
|
|
4240
5068
|
} catch (_) {
|
|
5069
|
+
votesChain = null;
|
|
5070
|
+
}
|
|
5071
|
+
if (proposalId != null) {
|
|
5072
|
+
try {
|
|
5073
|
+
proposal.cancelability = await getProposalCancelability({ provider, governor: governorAddr, proposalId });
|
|
5074
|
+
} catch (_) {
|
|
5075
|
+
proposal.cancelability = null;
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
let timelockInfo = {
|
|
5079
|
+
proposerRole: null,
|
|
5080
|
+
executorRole: null,
|
|
5081
|
+
governorIsProposer: null,
|
|
5082
|
+
governorIsExecutor: null,
|
|
5083
|
+
governorCanExecute: null,
|
|
5084
|
+
anyoneCanExecute: null,
|
|
5085
|
+
accountIsProposer: null,
|
|
5086
|
+
accountIsExecutor: null
|
|
5087
|
+
};
|
|
5088
|
+
if (timelockAddr) {
|
|
5089
|
+
try {
|
|
5090
|
+
const tl = new Contract(timelockAddr, ABI.Timelock, provider);
|
|
5091
|
+
const [PR, ER] = await Promise.all([
|
|
5092
|
+
tl.PROPOSER_ROLE().catch(() => null),
|
|
5093
|
+
tl.EXECUTOR_ROLE().catch(() => null)
|
|
5094
|
+
]);
|
|
5095
|
+
timelockInfo.proposerRole = PR;
|
|
5096
|
+
timelockInfo.executorRole = ER;
|
|
5097
|
+
if (PR && governorAddr) {
|
|
5098
|
+
timelockInfo.governorIsProposer = await tl.hasRole(PR, governorAddr).catch(() => null);
|
|
5099
|
+
}
|
|
5100
|
+
if (ER && governorAddr) {
|
|
5101
|
+
timelockInfo.governorIsExecutor = await tl.hasRole(ER, governorAddr).catch(() => null);
|
|
5102
|
+
}
|
|
5103
|
+
if (ER) {
|
|
5104
|
+
timelockInfo.anyoneCanExecute = await tl.hasRole(ER, ZeroAddress).catch(() => null);
|
|
5105
|
+
}
|
|
5106
|
+
timelockInfo.governorCanExecute = timelockInfo.anyoneCanExecute === true || timelockInfo.governorIsExecutor === true;
|
|
5107
|
+
if (acct && PR) {
|
|
5108
|
+
timelockInfo.accountIsProposer = await tl.hasRole(PR, acct).catch(() => null);
|
|
5109
|
+
}
|
|
5110
|
+
if (acct && ER) {
|
|
5111
|
+
timelockInfo.accountIsExecutor = await tl.hasRole(ER, acct).catch(() => null);
|
|
5112
|
+
}
|
|
5113
|
+
} catch (_) {
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
const governorCanQueue = timelockInfo.governorIsProposer === true;
|
|
5117
|
+
const governorCanExecute = timelockInfo.governorCanExecute === true;
|
|
5118
|
+
const authority = { route: "unknown", rationale: "insufficient context", requiredActor: "unknown" };
|
|
5119
|
+
const requireCouncil = governanceProfile.proposalAccess === "COUNCIL_ONLY";
|
|
5120
|
+
const requireTokenThreshold = governanceProfile.proposalAccess === "COMMUNITY_THRESHOLD";
|
|
5121
|
+
const execIsAnyone = governanceProfile.executionAccess === "ANYONE";
|
|
5122
|
+
const execIsCouncilOnly = governanceProfile.executionAccess === "COUNCIL_ONLY";
|
|
5123
|
+
const safeOrCouncil = opsSafe ? `opsSafe ${opsSafe}` : "council/safe";
|
|
5124
|
+
if (actionNorm === "timelock-schedule") {
|
|
5125
|
+
authority.route = "timelock-schedule";
|
|
5126
|
+
authority.rationale = "Direct timelock scheduling/execution path (operator/council governance)";
|
|
5127
|
+
authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
|
|
5128
|
+
} else if (actionNorm === "update-library") {
|
|
5129
|
+
if (governorCanQueue && governanceProfile.kind === "TOKEN") {
|
|
5130
|
+
authority.route = "governor-propose";
|
|
5131
|
+
authority.rationale = "LibraryRegistry updates are executed by the DAO timelock; token DAOs route via Governor \u2192 Timelock";
|
|
5132
|
+
authority.requiredActor = requireCouncil ? `COUNCIL_ROLE (${safeOrCouncil})` : "Proposer meeting proposalThreshold";
|
|
5133
|
+
} else {
|
|
5134
|
+
authority.route = "timelock-schedule";
|
|
5135
|
+
authority.rationale = "LibraryRegistry updates are executed by the DAO timelock; operator/council DAOs schedule directly on Timelock";
|
|
5136
|
+
authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
|
|
5137
|
+
}
|
|
5138
|
+
} else if (actionNorm === "propose") {
|
|
5139
|
+
if (!governorCanQueue && timelockAddr) {
|
|
5140
|
+
authority.route = "timelock-schedule";
|
|
5141
|
+
authority.rationale = "Governor is not a Timelock proposer; governance changes must be scheduled directly on Timelock (operator/council mode)";
|
|
5142
|
+
authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
|
|
5143
|
+
} else {
|
|
5144
|
+
authority.route = "governor-propose";
|
|
5145
|
+
authority.rationale = "Standard token-governance proposal flow (Governor \u2192 Timelock)";
|
|
5146
|
+
authority.requiredActor = requireCouncil ? `COUNCIL_ROLE (${safeOrCouncil})` : requireTokenThreshold ? "Proposer meeting proposalThreshold" : "Proposer meeting proposalThreshold";
|
|
5147
|
+
}
|
|
5148
|
+
} else if (actionNorm === "vote") {
|
|
5149
|
+
if (governanceProfile.kind === "OPERATOR") {
|
|
5150
|
+
authority.route = "not-applicable";
|
|
5151
|
+
authority.rationale = "Operator governance: token voting is not required for changes";
|
|
5152
|
+
authority.requiredActor = `Operator/council (${safeOrCouncil})`;
|
|
5153
|
+
} else {
|
|
5154
|
+
authority.route = "governor-vote";
|
|
5155
|
+
authority.rationale = "Token governance voting on Governor proposals";
|
|
5156
|
+
authority.requiredActor = "Voter with delegated voting power";
|
|
5157
|
+
}
|
|
5158
|
+
} else if (actionNorm === "queue") {
|
|
5159
|
+
if (governorCanQueue) {
|
|
5160
|
+
authority.route = "governor-queue";
|
|
5161
|
+
authority.rationale = "Queue via GovernorTimelockControl (Governor schedules on Timelock)";
|
|
5162
|
+
authority.requiredActor = "Any account (Governor performs the Timelock schedule as proposer)";
|
|
5163
|
+
} else {
|
|
5164
|
+
authority.route = "timelock-schedule";
|
|
5165
|
+
authority.rationale = "Queue by scheduling directly on Timelock (operator/council mode)";
|
|
5166
|
+
authority.requiredActor = `Timelock PROPOSER_ROLE holder (often ${safeOrCouncil})`;
|
|
5167
|
+
}
|
|
5168
|
+
} else if (actionNorm === "execute") {
|
|
5169
|
+
if (governorCanQueue && governorCanExecute) {
|
|
5170
|
+
authority.route = "governor-execute";
|
|
5171
|
+
authority.rationale = "Execute via GovernorTimelockControl (Timelock.execute called by Governor)";
|
|
5172
|
+
authority.requiredActor = "Any account (Governor performs the Timelock execute)";
|
|
5173
|
+
} else {
|
|
5174
|
+
authority.route = "timelock-execute";
|
|
5175
|
+
authority.rationale = "Execute by calling Timelock.execute directly (restricted executor or operator/council mode)";
|
|
5176
|
+
authority.requiredActor = execIsCouncilOnly ? `Timelock EXECUTOR_ROLE holder (${safeOrCouncil})` : `Timelock EXECUTOR_ROLE holder (often ${safeOrCouncil})`;
|
|
5177
|
+
}
|
|
5178
|
+
}
|
|
5179
|
+
if (!acct) {
|
|
5180
|
+
push({
|
|
5181
|
+
item: "Account provided for readiness checks",
|
|
5182
|
+
met: false,
|
|
5183
|
+
severity: "info",
|
|
5184
|
+
evidence: { note: "Pass account to enable per-user readiness checks" }
|
|
5185
|
+
});
|
|
5186
|
+
} else {
|
|
5187
|
+
if ((actionNorm === "propose" || actionNorm === "vote") && votesChain && votesChain.votingToken) {
|
|
5188
|
+
try {
|
|
5189
|
+
const res = await getVotesLatestMinusOneWithTimepoint({ provider, token: votesChain.votingToken, account: acct });
|
|
5190
|
+
proposal.proposerVotes = res.votes;
|
|
5191
|
+
proposal.votesTimepoint = res.timepoint;
|
|
5192
|
+
} catch (_) {
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
if (actionNorm === "propose" || actionNorm === "vote") {
|
|
5196
|
+
let voting = null;
|
|
5197
|
+
try {
|
|
5198
|
+
voting = await getVotingStatus({ provider, subdao: subdaoAddr || void 0, governor: governorAddr, account: acct });
|
|
5199
|
+
} catch (_) {
|
|
5200
|
+
voting = null;
|
|
5201
|
+
}
|
|
5202
|
+
if (!voting || !voting.baseToken) {
|
|
5203
|
+
push({ item: "Resolve voting token wiring", met: false, severity: "warn" });
|
|
5204
|
+
} else {
|
|
5205
|
+
const threshold = voting.threshold != null ? BigInt(voting.threshold) : null;
|
|
5206
|
+
const votingPower = voting.votingPower != null ? BigInt(voting.votingPower) : null;
|
|
5207
|
+
let baseBalance = null;
|
|
5208
|
+
let baseDelegate = null;
|
|
5209
|
+
try {
|
|
5210
|
+
const base = new Contract(voting.baseToken, ABI.ERC20Votes, provider);
|
|
5211
|
+
const [bal, del] = await Promise.all([
|
|
5212
|
+
base.balanceOf(acct).catch(() => null),
|
|
5213
|
+
base.delegates(acct).catch(() => null)
|
|
5214
|
+
]);
|
|
5215
|
+
if (bal != null) baseBalance = BigInt(bal.toString());
|
|
5216
|
+
if (del && /^0x[0-9a-fA-F]{40}$/.test(String(del))) baseDelegate = getAddress(del);
|
|
5217
|
+
} catch (_) {
|
|
5218
|
+
}
|
|
5219
|
+
const hasVotes = votingPower != null ? votingPower > 0n : null;
|
|
5220
|
+
push({
|
|
5221
|
+
item: "Voting power is non-zero (delegated)",
|
|
5222
|
+
met: hasVotes,
|
|
5223
|
+
severity: hasVotes === false ? "error" : "info",
|
|
5224
|
+
evidence: {
|
|
5225
|
+
votingPower: votingPower?.toString?.() ?? null,
|
|
5226
|
+
votingToken: voting.votingToken,
|
|
5227
|
+
baseToken: voting.baseToken,
|
|
5228
|
+
note: threshold != null ? `proposalThreshold=${threshold.toString()}` : void 0
|
|
5229
|
+
}
|
|
5230
|
+
});
|
|
5231
|
+
const noDelegateSet = !baseDelegate || baseDelegate === ZeroAddress;
|
|
5232
|
+
if ((actionNorm === "propose" || actionNorm === "vote") && baseBalance != null && baseBalance > 0n && (votingPower === 0n || votingPower === null) && noDelegateSet) {
|
|
5233
|
+
try {
|
|
5234
|
+
const payload = buildDelegateTx({ token: voting.baseToken, delegatee: acct });
|
|
5235
|
+
push({
|
|
5236
|
+
item: "Delegation set (ERC20Votes)",
|
|
5237
|
+
met: false,
|
|
5238
|
+
severity: "error",
|
|
5239
|
+
evidence: { baseToken: voting.baseToken, delegate: baseDelegate || ZeroAddress, baseBalance: baseBalance.toString() },
|
|
5240
|
+
fix: { kind: "tx", commandOrCalldata: payload }
|
|
5241
|
+
});
|
|
5242
|
+
} catch (_) {
|
|
5243
|
+
push({
|
|
5244
|
+
item: "Delegation set (ERC20Votes)",
|
|
5245
|
+
met: false,
|
|
5246
|
+
severity: "error",
|
|
5247
|
+
evidence: { baseToken: voting.baseToken, delegate: baseDelegate || ZeroAddress, baseBalance: baseBalance?.toString?.() ?? null }
|
|
5248
|
+
});
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
if (actionNorm === "propose" && authority.route === "governor-propose") {
|
|
5254
|
+
let thresholdStatus = null;
|
|
5255
|
+
try {
|
|
5256
|
+
thresholdStatus = await getProposalThresholdStatus({ provider, governor: governorAddr, proposer: acct });
|
|
5257
|
+
} catch (_) {
|
|
5258
|
+
}
|
|
5259
|
+
if (thresholdStatus) {
|
|
5260
|
+
const {
|
|
5261
|
+
proposalThreshold,
|
|
5262
|
+
proposerVotes,
|
|
5263
|
+
votesTimepoint,
|
|
5264
|
+
sxxxBalance,
|
|
5265
|
+
thresholdMet,
|
|
5266
|
+
cooldownRemaining,
|
|
5267
|
+
cooldownElapsed,
|
|
5268
|
+
sxxxToken
|
|
5269
|
+
} = thresholdStatus;
|
|
5270
|
+
proposal.proposalThreshold = proposalThreshold;
|
|
5271
|
+
proposal.sxxxToken = sxxxToken;
|
|
5272
|
+
proposal.proposerSxxxBalance = sxxxBalance;
|
|
5273
|
+
proposal.proposerVotes = proposerVotes;
|
|
5274
|
+
proposal.votesTimepoint = votesTimepoint;
|
|
5275
|
+
proposal.thresholdMet = thresholdMet;
|
|
5276
|
+
proposal.cooldownRemaining = cooldownRemaining;
|
|
5277
|
+
proposal.cooldownElapsed = cooldownElapsed;
|
|
5278
|
+
proposal.cancelPolicy.enabled = proposalThreshold != null ? proposalThreshold > 0n : null;
|
|
5279
|
+
if (proposalThreshold != null) {
|
|
5280
|
+
push({
|
|
5281
|
+
item: "Voting power meets proposalThreshold (clock()-1)",
|
|
5282
|
+
met: thresholdMet,
|
|
5283
|
+
severity: thresholdMet === false ? "error" : "info",
|
|
5284
|
+
evidence: {
|
|
5285
|
+
proposerVotes: proposerVotes?.toString?.() ?? null,
|
|
5286
|
+
votesTimepoint: votesTimepoint?.toString?.() ?? null,
|
|
5287
|
+
proposalThreshold: proposalThreshold.toString(),
|
|
5288
|
+
sxxxToken,
|
|
5289
|
+
note: "No approve/escrow needed - delegate and keep sufficient voting power"
|
|
5290
|
+
}
|
|
5291
|
+
});
|
|
5292
|
+
}
|
|
5293
|
+
if (cooldownRemaining != null) {
|
|
5294
|
+
push({
|
|
5295
|
+
item: "Proposal cooldown elapsed",
|
|
5296
|
+
met: cooldownElapsed,
|
|
5297
|
+
severity: cooldownElapsed === false ? "warn" : "info",
|
|
5298
|
+
evidence: { cooldownRemainingSeconds: cooldownRemaining.toString() }
|
|
5299
|
+
});
|
|
5300
|
+
}
|
|
5301
|
+
if (proposalThreshold != null && proposalThreshold > 0n) {
|
|
5302
|
+
push({
|
|
5303
|
+
item: "Note: Proposal cancelability",
|
|
5304
|
+
met: true,
|
|
5305
|
+
severity: "info",
|
|
5306
|
+
evidence: {
|
|
5307
|
+
note: "Keep voting power (delegation + multipliers) above threshold while proposal is active, or it may be canceled by anyone",
|
|
5308
|
+
proposalThreshold: proposalThreshold.toString()
|
|
5309
|
+
}
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
if (actionNorm === "timelock-schedule" || authority.route === "timelock-schedule") {
|
|
5315
|
+
if (!timelockAddr) {
|
|
5316
|
+
push({ item: "Timelock resolved", met: false, severity: "error" });
|
|
5317
|
+
} else if (timelockInfo.proposerRole) {
|
|
5318
|
+
push({
|
|
5319
|
+
item: "Account has Timelock PROPOSER_ROLE",
|
|
5320
|
+
met: timelockInfo.accountIsProposer,
|
|
5321
|
+
severity: timelockInfo.accountIsProposer === false ? "error" : "info",
|
|
5322
|
+
evidence: { timelock: timelockAddr, account: acct }
|
|
5323
|
+
});
|
|
5324
|
+
}
|
|
5325
|
+
}
|
|
5326
|
+
if ((actionNorm === "queue" || actionNorm === "execute") && proposalId != null) {
|
|
5327
|
+
try {
|
|
5328
|
+
const g = new Contract(governorAddr, ABI.Governor, provider);
|
|
5329
|
+
const pid = typeof proposalId === "bigint" ? proposalId : String(proposalId).startsWith("0x") ? BigInt(String(proposalId)) : BigInt(String(proposalId));
|
|
5330
|
+
const st = await g.state(pid).catch(() => null);
|
|
5331
|
+
if (st != null) {
|
|
5332
|
+
push({
|
|
5333
|
+
item: "Proposal state readable",
|
|
5334
|
+
met: true,
|
|
5335
|
+
severity: "info",
|
|
5336
|
+
evidence: { proposalId: pid.toString(), state: Number(st) }
|
|
5337
|
+
});
|
|
5338
|
+
}
|
|
5339
|
+
} catch (_) {
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
if (actionNorm === "update-library") {
|
|
5343
|
+
const regAddr = libraryRegistry || process.env.LIBRARY_REGISTRY_ADDRESS || process.env.SAGE_LIBRARY_REGISTRY_ADDRESS || null;
|
|
5344
|
+
const daoAddr = dao ? getAddress(dao) : subdaoAddr || null;
|
|
5345
|
+
if (!regAddr) {
|
|
5346
|
+
push({ item: "LibraryRegistry address provided", met: false, severity: "error" });
|
|
5347
|
+
} else if (!daoAddr) {
|
|
5348
|
+
push({ item: "DAO/SubDAO address provided", met: false, severity: "error" });
|
|
5349
|
+
} else if (!timelockAddr) {
|
|
5350
|
+
push({ item: "Timelock resolved for DAO", met: false, severity: "error" });
|
|
5351
|
+
} else {
|
|
5352
|
+
try {
|
|
5353
|
+
const reg = new Contract(getAddress(regAddr), ABI.LibraryRegistry, provider);
|
|
5354
|
+
const daoTl = await reg.daoTimelock(daoAddr).catch(() => null);
|
|
5355
|
+
const daoTimelock = daoTl && /^0x[0-9a-fA-F]{40}$/.test(String(daoTl)) ? getAddress(daoTl) : null;
|
|
5356
|
+
const match = daoTimelock ? daoTimelock.toLowerCase() === timelockAddr.toLowerCase() : null;
|
|
5357
|
+
push({
|
|
5358
|
+
item: "LibraryRegistry daoTimelock matches resolved Timelock",
|
|
5359
|
+
met: match,
|
|
5360
|
+
severity: match === false ? "error" : "info",
|
|
5361
|
+
evidence: { libraryRegistry: getAddress(regAddr), dao: daoAddr, daoTimelock, timelock: timelockAddr }
|
|
5362
|
+
});
|
|
5363
|
+
if (match === true && manifestCID) {
|
|
5364
|
+
const library2 = require_library();
|
|
5365
|
+
const payload = library2.buildUpdateLibraryTx({
|
|
5366
|
+
registry: getAddress(regAddr),
|
|
5367
|
+
subdao: daoAddr,
|
|
5368
|
+
manifestCID: String(manifestCID),
|
|
5369
|
+
version: String(version || "1.0.0")
|
|
5370
|
+
});
|
|
5371
|
+
const sim = await library2.simulateAsTimelock({ provider, registry: getAddress(regAddr), to: payload.to, data: payload.data, timelock: timelockAddr });
|
|
5372
|
+
push({
|
|
5373
|
+
item: "Simulate LibraryRegistry.updateLibrary as Timelock",
|
|
5374
|
+
met: !!sim.ok,
|
|
5375
|
+
severity: sim.ok ? "info" : "error",
|
|
5376
|
+
evidence: sim.ok ? { ok: true } : { ok: false, error: sim.error?.message || "revert" }
|
|
5377
|
+
});
|
|
5378
|
+
}
|
|
5379
|
+
} catch (_) {
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
4241
5383
|
}
|
|
4242
5384
|
return {
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
5385
|
+
governanceProfile,
|
|
5386
|
+
proposal,
|
|
5387
|
+
addresses: {
|
|
5388
|
+
subdao: subdaoAddr,
|
|
5389
|
+
governor: governorAddr,
|
|
5390
|
+
timelock: timelockAddr,
|
|
5391
|
+
opsSafe,
|
|
5392
|
+
votingToken: votesChain ? votesChain.votingToken : null,
|
|
5393
|
+
baseVotingToken: votesChain ? votesChain.baseToken : null
|
|
5394
|
+
},
|
|
5395
|
+
authority,
|
|
5396
|
+
readiness
|
|
4250
5397
|
};
|
|
4251
5398
|
},
|
|
4252
|
-
/**
|
|
4253
|
-
* Compose propose readiness with optional execution readiness checks.
|
|
4254
|
-
* execution?: { provider, registry, timelock, subdao, manifestCID, version }
|
|
4255
|
-
*/
|
|
4256
|
-
readinessToPropose: async function readinessToPropose({ provider, governor, proposer, execution = null }) {
|
|
4257
|
-
const gates = await this.ensureProposeGates({ provider, governor, proposer });
|
|
4258
|
-
let executionReady = null;
|
|
4259
|
-
let executionError = null;
|
|
4260
|
-
if (execution && execution.registry && execution.timelock && execution.subdao) {
|
|
4261
|
-
try {
|
|
4262
|
-
const library2 = require_library();
|
|
4263
|
-
const { to, data } = library2.buildUpdateLibraryTx({
|
|
4264
|
-
registry: execution.registry,
|
|
4265
|
-
subdao: execution.subdao,
|
|
4266
|
-
manifestCID: execution.manifestCID || "",
|
|
4267
|
-
version: execution.version || "1.0.0"
|
|
4268
|
-
});
|
|
4269
|
-
const sim = await library2.simulateAsTimelock({ provider, registry: execution.registry, to, data, timelock: execution.timelock });
|
|
4270
|
-
executionReady = !!sim.ok;
|
|
4271
|
-
executionError = sim.ok ? null : sim.error?.message || "revert";
|
|
4272
|
-
} catch (err) {
|
|
4273
|
-
executionReady = false;
|
|
4274
|
-
executionError = String(err && err.message || err);
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
return { ...gates, executionReady, executionError };
|
|
4278
|
-
},
|
|
4279
5399
|
/** List active proposals (default states: PENDING, ACTIVE) via subgraph. */
|
|
4280
5400
|
listActiveProposals: async function listActiveProposals({ url, governor, first = 20, skip = 0 }) {
|
|
4281
5401
|
if (!url) throw new SageSDKError(CODES.INVALID_ARGS, "subgraph url required");
|
|
@@ -4301,22 +5421,21 @@ var require_governance = __commonJS({
|
|
|
4301
5421
|
/**
|
|
4302
5422
|
* Detect governance mode and operator status.
|
|
4303
5423
|
* Heuristics mirror CLI detectGovMode.
|
|
4304
|
-
* Returns { operator: boolean, governance: 'Personal'|'Community'|'Unknown', governor, timelock, subdao,
|
|
5424
|
+
* Returns { operator: boolean, governance: 'Personal'|'Community'|'Unknown', governor, timelock, subdao, depositWei }
|
|
4305
5425
|
*/
|
|
4306
|
-
detectMode: async function detectMode({ provider, governor, timelock: timelock2, subdao:
|
|
5426
|
+
detectMode: async function detectMode({ provider, governor, timelock: timelock2, subdao: subdao3 }) {
|
|
4307
5427
|
const out = {
|
|
4308
5428
|
operator: false,
|
|
4309
5429
|
governance: "Unknown",
|
|
4310
5430
|
governor: governor || null,
|
|
4311
5431
|
timelock: timelock2 || null,
|
|
4312
|
-
subdao:
|
|
4313
|
-
stakeRequired: null,
|
|
5432
|
+
subdao: subdao3 || null,
|
|
4314
5433
|
depositWei: null
|
|
4315
5434
|
};
|
|
4316
5435
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4317
5436
|
try {
|
|
4318
|
-
if (
|
|
4319
|
-
const sub = new Contract(getAddress(
|
|
5437
|
+
if (subdao3 && (!governor || !timelock2)) {
|
|
5438
|
+
const sub = new Contract(getAddress(subdao3), ["function governor() view returns (address)", "function timelock() view returns (address)", "function getGovernanceMode() view returns (uint8)"], provider);
|
|
4320
5439
|
if (!governor) out.governor = await sub.governor();
|
|
4321
5440
|
if (!timelock2) out.timelock = await sub.timelock();
|
|
4322
5441
|
try {
|
|
@@ -4328,323 +5447,71 @@ var require_governance = __commonJS({
|
|
|
4328
5447
|
} catch (_) {
|
|
4329
5448
|
}
|
|
4330
5449
|
try {
|
|
4331
|
-
if (out.timelock && out.governor) {
|
|
4332
|
-
const tl = new Contract(getAddress(out.timelock), ["function PROPOSER_ROLE() view returns (bytes32)", "function hasRole(bytes32,address) view returns (bool)"], provider);
|
|
4333
|
-
const PR = await tl.PROPOSER_ROLE();
|
|
4334
|
-
const has = await tl.hasRole(PR, getAddress(out.governor));
|
|
4335
|
-
out.operator = !has;
|
|
4336
|
-
}
|
|
4337
|
-
} catch (_) {
|
|
4338
|
-
}
|
|
4339
|
-
try {
|
|
4340
|
-
if (out.governor) {
|
|
4341
|
-
const ifaceStake = new Interface(["function proposalStakeRequired() view returns (uint256)"]);
|
|
4342
|
-
const retStake = await provider.call({ to: getAddress(out.governor), data: ifaceStake.encodeFunctionData("proposalStakeRequired", []) }).catch(() => null);
|
|
4343
|
-
if (retStake && retStake !== "0x") out.stakeRequired = BigInt(retStake);
|
|
4344
|
-
const ifaceDep = new Interface(["function bootstrapDepositWei() view returns (uint256)"]);
|
|
4345
|
-
const retDep = await provider.call({ to: getAddress(out.governor), data: ifaceDep.encodeFunctionData("bootstrapDepositWei", []) }).catch(() => null);
|
|
4346
|
-
if (retDep && retDep !== "0x") out.depositWei = BigInt(retDep);
|
|
4347
|
-
}
|
|
4348
|
-
} catch (_) {
|
|
4349
|
-
}
|
|
4350
|
-
return out;
|
|
4351
|
-
}
|
|
4352
|
-
};
|
|
4353
|
-
}
|
|
4354
|
-
});
|
|
4355
|
-
|
|
4356
|
-
// src/governance/intents.js
|
|
4357
|
-
var require_intents = __commonJS({
|
|
4358
|
-
"src/governance/intents.js"(exports2, module2) {
|
|
4359
|
-
var { Interface, getAddress } = require("ethers");
|
|
4360
|
-
var { SageSDKError, CODES } = require_errors();
|
|
4361
|
-
function normalise(addr, label) {
|
|
4362
|
-
try {
|
|
4363
|
-
return getAddress(addr);
|
|
4364
|
-
} catch {
|
|
4365
|
-
throw new SageSDKError(CODES.INVALID_ARGS, `${label} required/invalid`);
|
|
4366
|
-
}
|
|
4367
|
-
}
|
|
4368
|
-
var GovIface = new Interface([
|
|
4369
|
-
"function updateQuorumNumerator(uint256)",
|
|
4370
|
-
"function setVotingPeriodBlocks(uint32)",
|
|
4371
|
-
"function setDepositOnlyMode(bool)"
|
|
4372
|
-
]);
|
|
4373
|
-
var RegIface = new Interface([
|
|
4374
|
-
"function setForkPolicy(uint8)",
|
|
4375
|
-
"function updatePromptByGovernance(string,string)",
|
|
4376
|
-
"function addPrompt(string,bytes32[],string,string,uint8)"
|
|
4377
|
-
]);
|
|
4378
|
-
var CfgIface = new Interface([
|
|
4379
|
-
"function setPremiumRevSplit(address,uint16,uint16,uint16)",
|
|
4380
|
-
"function setCreatorStakeBps(address,uint256)"
|
|
4381
|
-
]);
|
|
4382
|
-
module2.exports = {
|
|
4383
|
-
// Governor targets
|
|
4384
|
-
buildUpdateQuorumTx: ({ governor, quorumPercent }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("updateQuorumNumerator", [Number(quorumPercent)]), value: 0n }),
|
|
4385
|
-
buildSetVotingPeriodTx: ({ governor, blocks }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setVotingPeriodBlocks", [Number(blocks)]), value: 0n }),
|
|
4386
|
-
buildSetDepositOnlyTx: ({ governor, enabled }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setDepositOnlyMode", [!!enabled]), value: 0n }),
|
|
4387
|
-
// Registry targets
|
|
4388
|
-
buildSetForkPolicyTx: ({ registry, policy }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("setForkPolicy", [Number(policy)]), value: 0n }),
|
|
4389
|
-
buildUpdatePromptByGovernanceTx: ({ registry, key, cid }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("updatePromptByGovernance", [String(key), String(cid)]), value: 0n }),
|
|
4390
|
-
buildAddPromptTx: ({ registry, title, tags = [], contentCID, metadataCID = "", category = 0 }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("addPrompt", [String(title), tags, String(contentCID), String(metadataCID), Number(category)]), value: 0n }),
|
|
4391
|
-
// GovernanceConfig targets
|
|
4392
|
-
buildSetPremiumSplitTx: ({ config, subdao: subdao2, treasuryBps, protocolBps, creatorBps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setPremiumRevSplit", [normalise(subdao2, "subdao"), Number(treasuryBps), Number(protocolBps), Number(creatorBps)]), value: 0n }),
|
|
4393
|
-
buildSetCreatorStakeBpsTx: ({ config, subdao: subdao2, bps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setCreatorStakeBps", [normalise(subdao2, "subdao"), Number(bps)]), value: 0n })
|
|
4394
|
-
};
|
|
4395
|
-
}
|
|
4396
|
-
});
|
|
4397
|
-
|
|
4398
|
-
// src/utils/logs.js
|
|
4399
|
-
var require_logs = __commonJS({
|
|
4400
|
-
"src/utils/logs.js"(exports2, module2) {
|
|
4401
|
-
async function fetchLogsChunked({ provider, address, topics = [], fromBlock = 0, toBlock = "latest", chunkSize = 5e4 }) {
|
|
4402
|
-
if (!provider) throw new Error("provider required");
|
|
4403
|
-
const latest = toBlock === "latest" ? await provider.getBlockNumber() : Number(toBlock);
|
|
4404
|
-
let start = Number(fromBlock);
|
|
4405
|
-
const logs = [];
|
|
4406
|
-
while (start <= latest) {
|
|
4407
|
-
const end = Math.min(start + chunkSize - 1, latest);
|
|
4408
|
-
const chunk = await provider.getLogs({ address, topics, fromBlock: start, toBlock: end });
|
|
4409
|
-
logs.push(...chunk);
|
|
4410
|
-
start = end + 1;
|
|
4411
|
-
}
|
|
4412
|
-
return logs;
|
|
4413
|
-
}
|
|
4414
|
-
module2.exports = {
|
|
4415
|
-
fetchLogsChunked
|
|
4416
|
-
};
|
|
4417
|
-
}
|
|
4418
|
-
});
|
|
4419
|
-
|
|
4420
|
-
// src/subdao/index.js
|
|
4421
|
-
var require_subdao = __commonJS({
|
|
4422
|
-
"src/subdao/index.js"(exports2, module2) {
|
|
4423
|
-
var { Contract, Interface, getAddress } = require("ethers");
|
|
4424
|
-
var ABI = require_abi();
|
|
4425
|
-
var { fetchLogsChunked } = require_logs();
|
|
4426
|
-
var { SageSDKError, CODES } = require_errors();
|
|
4427
|
-
function normalise(address, label) {
|
|
4428
|
-
if (!address) throw new SageSDKError(CODES.INVALID_ARGS, `${label} required`);
|
|
4429
|
-
try {
|
|
4430
|
-
return getAddress(address);
|
|
4431
|
-
} catch (err) {
|
|
4432
|
-
throw new SageSDKError(CODES.INVALID_ARGS, `invalid ${label}`, { cause: err });
|
|
4433
|
-
}
|
|
4434
|
-
}
|
|
4435
|
-
async function discoverSubDAOs({ provider, factoryAddress, fromBlock = 0, toBlock = "latest", limit = 5e3, chunkSize = 5e4 }) {
|
|
4436
|
-
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4437
|
-
const fac = normalise(factoryAddress, "factoryAddress");
|
|
4438
|
-
const iface = new Interface(ABI.Factory);
|
|
4439
|
-
const topic = iface.getEvent("SubDAOGovernanceDeployed").topicHash;
|
|
4440
|
-
const logs = await fetchLogsChunked({ provider, address: fac, topics: [topic], fromBlock, toBlock, chunkSize });
|
|
4441
|
-
const out = [];
|
|
4442
|
-
const slice = limit > 0 ? logs.slice(-limit) : logs;
|
|
4443
|
-
for (const log of slice) {
|
|
4444
|
-
try {
|
|
4445
|
-
const decoded = iface.decodeEventLog("SubDAOGovernanceDeployed", log.data, log.topics);
|
|
4446
|
-
out.push({
|
|
4447
|
-
subdao: getAddress(decoded[0]),
|
|
4448
|
-
governor: getAddress(decoded[1]),
|
|
4449
|
-
timelock: getAddress(decoded[2]),
|
|
4450
|
-
treasury: decoded[3] ? getAddress(decoded[3]) : null,
|
|
4451
|
-
txHash: log.transactionHash,
|
|
4452
|
-
blockNumber: log.blockNumber
|
|
4453
|
-
});
|
|
4454
|
-
} catch (err) {
|
|
4455
|
-
continue;
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
return out;
|
|
4459
|
-
}
|
|
4460
|
-
async function getSubDAOInfo({ provider, subdao: subdao2 }) {
|
|
4461
|
-
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4462
|
-
const addr = normalise(subdao2, "subdao");
|
|
4463
|
-
const contract = new Contract(addr, ABI.SubDAO, provider);
|
|
4464
|
-
const [governor, timelock2, mode, promptRegistry, sageTreasury, stakeToken, minStakeAmount, accessModel, membershipPolicy, profileCID, factoryDeployer] = await Promise.all([
|
|
4465
|
-
contract.governor(),
|
|
4466
|
-
contract.timelock(),
|
|
4467
|
-
contract.getGovernanceMode().catch(() => null),
|
|
4468
|
-
contract.promptRegistry().catch(() => null),
|
|
4469
|
-
contract.sageTreasury().catch(() => null),
|
|
4470
|
-
contract.stakeToken().catch(() => null),
|
|
4471
|
-
contract.minStakeAmount().catch(() => null),
|
|
4472
|
-
contract.accessModel().catch(() => null),
|
|
4473
|
-
contract.membershipPolicy().catch(() => null),
|
|
4474
|
-
contract.profileCID().catch(() => null),
|
|
4475
|
-
contract.factoryDeployer().catch(() => null)
|
|
4476
|
-
]);
|
|
4477
|
-
return {
|
|
4478
|
-
address: addr,
|
|
4479
|
-
governor: getAddress(governor),
|
|
4480
|
-
timelock: getAddress(timelock2),
|
|
4481
|
-
governanceMode: mode,
|
|
4482
|
-
promptRegistry: promptRegistry ? getAddress(promptRegistry) : null,
|
|
4483
|
-
sageTreasury: sageTreasury ? getAddress(sageTreasury) : null,
|
|
4484
|
-
stakeToken: stakeToken && stakeToken !== "0x0000000000000000000000000000000000000000" ? getAddress(stakeToken) : null,
|
|
4485
|
-
minStakeAmount: minStakeAmount != null ? BigInt(minStakeAmount.toString()) : null,
|
|
4486
|
-
accessModel: accessModel != null ? Number(accessModel) : null,
|
|
4487
|
-
membershipPolicy: membershipPolicy != null ? Number(membershipPolicy) : null,
|
|
4488
|
-
profileCID: profileCID || null,
|
|
4489
|
-
factoryDeployer: factoryDeployer && factoryDeployer !== "0x0000000000000000000000000000000000000000" ? getAddress(factoryDeployer) : null
|
|
4490
|
-
};
|
|
4491
|
-
}
|
|
4492
|
-
async function getSubDAOUserStats({ provider, subdao: subdao2, account }) {
|
|
4493
|
-
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
4494
|
-
const addr = normalise(subdao2, "subdao");
|
|
4495
|
-
const user = normalise(account, "account");
|
|
4496
|
-
const contract = new Contract(addr, ABI.SubDAO, provider);
|
|
4497
|
-
const [staked, userForkCount] = await Promise.all([
|
|
4498
|
-
contract.stakedAmount(user).catch(() => 0n),
|
|
4499
|
-
contract.userForkCount(user).catch(() => 0n)
|
|
4500
|
-
]);
|
|
4501
|
-
return {
|
|
4502
|
-
stakedAmount: BigInt(staked.toString()),
|
|
4503
|
-
userForkCount: Number(userForkCount)
|
|
4504
|
-
};
|
|
4505
|
-
}
|
|
4506
|
-
var SubDAOInterface = new Interface([
|
|
4507
|
-
"function stake(uint256)",
|
|
4508
|
-
"function unstake(uint256)",
|
|
4509
|
-
// Optional on newer SubDAO implementations; provided for tx building only.
|
|
4510
|
-
"function setProfileCid(string)"
|
|
4511
|
-
]);
|
|
4512
|
-
function buildStakeTx({ subdao: subdao2, amount }) {
|
|
4513
|
-
const addr = normalise(subdao2, "subdao");
|
|
4514
|
-
const data = SubDAOInterface.encodeFunctionData("stake", [BigInt(amount)]);
|
|
4515
|
-
return { to: addr, data, value: 0n };
|
|
4516
|
-
}
|
|
4517
|
-
function buildUnstakeTx({ subdao: subdao2, amount }) {
|
|
4518
|
-
const addr = normalise(subdao2, "subdao");
|
|
4519
|
-
const data = SubDAOInterface.encodeFunctionData("unstake", [BigInt(amount)]);
|
|
4520
|
-
return { to: addr, data, value: 0n };
|
|
4521
|
-
}
|
|
4522
|
-
function buildSetProfileCidTx({ subdao: subdao2, profileCid }) {
|
|
4523
|
-
const addr = normalise(subdao2, "subdao");
|
|
4524
|
-
if (!profileCid || typeof profileCid !== "string" || !profileCid.trim()) {
|
|
4525
|
-
throw new SageSDKError(CODES.INVALID_ARGS, "profileCid (CID string) required");
|
|
4526
|
-
}
|
|
4527
|
-
const data = SubDAOInterface.encodeFunctionData("setProfileCid", [String(profileCid)]);
|
|
4528
|
-
return { to: addr, data, value: 0n };
|
|
4529
|
-
}
|
|
4530
|
-
module2.exports = {
|
|
4531
|
-
discoverSubDAOs,
|
|
4532
|
-
getSubDAOInfo,
|
|
4533
|
-
getSubDAOUserStats,
|
|
4534
|
-
buildStakeTx,
|
|
4535
|
-
buildUnstakeTx,
|
|
4536
|
-
buildSetProfileCidTx
|
|
4537
|
-
};
|
|
4538
|
-
async function ensureSxxxBurnAllowance({ signer, sxxx, owner, spender, amount }) {
|
|
4539
|
-
if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
|
|
4540
|
-
if (!sxxx) throw new SageSDKError(CODES.INVALID_ARGS, "sxxx address required");
|
|
4541
|
-
if (!spender) throw new SageSDKError(CODES.INVALID_ARGS, "spender (factory) required");
|
|
4542
|
-
const sxxxAddr = getAddress(sxxx);
|
|
4543
|
-
const spenderAddr = getAddress(spender);
|
|
4544
|
-
const erc = new Contract(sxxxAddr, ABI.SXXX, signer);
|
|
4545
|
-
const ownerAddr = owner ? getAddress(owner) : await signer.getAddress();
|
|
4546
|
-
const need = BigInt(amount || 0n);
|
|
4547
|
-
if (need === 0n) return { approved: false, tx: null };
|
|
4548
|
-
const current = await erc.allowance(ownerAddr, spenderAddr);
|
|
4549
|
-
const cur = BigInt(current.toString());
|
|
4550
|
-
if (cur >= need) return { approved: false, tx: null };
|
|
4551
|
-
const tx = await erc.approve(spenderAddr, need);
|
|
4552
|
-
const rcpt = await tx.wait();
|
|
4553
|
-
return { approved: true, tx, receipt: rcpt };
|
|
4554
|
-
}
|
|
4555
|
-
async function createSubDAO({ signer, factoryAddress, name, description, accessModel, minStakeAmount, burnAmount, sxxx }) {
|
|
4556
|
-
if (!signer) throw new SageSDKError(CODES.INVALID_ARGS, "signer required");
|
|
4557
|
-
if (!factoryAddress) throw new SageSDKError(CODES.INVALID_ARGS, "factoryAddress required");
|
|
4558
|
-
const provider = signer.provider;
|
|
4559
|
-
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "signer.provider required");
|
|
4560
|
-
if (sxxx && BigInt(burnAmount || 0n) > 0n) {
|
|
4561
|
-
await ensureSxxxBurnAllowance({ signer, sxxx, spender: factoryAddress, amount: BigInt(burnAmount) });
|
|
4562
|
-
}
|
|
4563
|
-
const fac = new Contract(getAddress(factoryAddress), ABI.FactoryWrite, signer);
|
|
4564
|
-
const args = [
|
|
4565
|
-
String(name || ""),
|
|
4566
|
-
String(description || ""),
|
|
4567
|
-
Number(accessModel ?? 0),
|
|
4568
|
-
BigInt(minStakeAmount ?? 0n),
|
|
4569
|
-
BigInt(burnAmount ?? 0n)
|
|
4570
|
-
];
|
|
4571
|
-
let tx;
|
|
4572
|
-
try {
|
|
4573
|
-
tx = await fac.createSubDAO(...args);
|
|
4574
|
-
} catch (err) {
|
|
4575
|
-
tx = await fac.createSubDAO(...args, { gasLimit: 8e6 });
|
|
4576
|
-
}
|
|
4577
|
-
const receipt = await tx.wait();
|
|
4578
|
-
const iface = new Interface(ABI.Factory);
|
|
4579
|
-
let subdao2 = null, governor = null, timelock2 = null, treasury2 = null, registry = null;
|
|
4580
|
-
for (const log of receipt.logs || []) {
|
|
4581
|
-
try {
|
|
4582
|
-
const parsed = iface.parseLog(log);
|
|
4583
|
-
if (parsed && parsed.name === "SubDAOGovernanceDeployed") {
|
|
4584
|
-
subdao2 = getAddress(parsed.args.subDAO || parsed.args[0]);
|
|
4585
|
-
governor = getAddress(parsed.args.governor || parsed.args[1]);
|
|
4586
|
-
timelock2 = getAddress(parsed.args.timelock || parsed.args[2]);
|
|
4587
|
-
treasury2 = getAddress(parsed.args.treasury || parsed.args[3]);
|
|
4588
|
-
} else if (parsed && parsed.name === "SubDAOCreated") {
|
|
4589
|
-
const maybe = parsed.args.registry || parsed.args[2];
|
|
4590
|
-
if (maybe) registry = getAddress(maybe);
|
|
4591
|
-
if (!subdao2) subdao2 = getAddress(parsed.args.subDAO || parsed.args[0]);
|
|
5450
|
+
if (out.timelock && out.governor) {
|
|
5451
|
+
const tl = new Contract(getAddress(out.timelock), ["function PROPOSER_ROLE() view returns (bytes32)", "function hasRole(bytes32,address) view returns (bool)"], provider);
|
|
5452
|
+
const PR = await tl.PROPOSER_ROLE();
|
|
5453
|
+
const has = await tl.hasRole(PR, getAddress(out.governor));
|
|
5454
|
+
out.operator = !has;
|
|
4592
5455
|
}
|
|
4593
5456
|
} catch (_) {
|
|
4594
5457
|
}
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
5458
|
+
try {
|
|
5459
|
+
if (out.governor) {
|
|
5460
|
+
const ifaceDep = new Interface(["function bootstrapDepositWei() view returns (uint256)"]);
|
|
5461
|
+
const retDep = await provider.call({ to: getAddress(out.governor), data: ifaceDep.encodeFunctionData("bootstrapDepositWei", []) }).catch(() => null);
|
|
5462
|
+
if (retDep && retDep !== "0x") out.depositWei = BigInt(retDep);
|
|
5463
|
+
}
|
|
5464
|
+
} catch (_) {
|
|
4601
5465
|
}
|
|
4602
|
-
|
|
5466
|
+
return out;
|
|
4603
5467
|
}
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
"function grantRole(bytes32,address)",
|
|
4619
|
-
"function revokeRole(bytes32,address)"
|
|
4620
|
-
];
|
|
4621
|
-
const tl = new Contract(timelock2, TL_ABI, signer);
|
|
4622
|
-
const [PR, ER, AR] = await Promise.all([
|
|
4623
|
-
tl.PROPOSER_ROLE(),
|
|
4624
|
-
tl.EXECUTOR_ROLE(),
|
|
4625
|
-
tl.DEFAULT_ADMIN_ROLE().catch(() => null)
|
|
4626
|
-
]);
|
|
4627
|
-
const ops = [];
|
|
4628
|
-
ops.push(await tl.grantRole(PR, getAddress(operator)));
|
|
4629
|
-
ops.push(await tl.grantRole(ER, getAddress(operator)));
|
|
4630
|
-
ops.push(await tl.revokeRole(PR, governor));
|
|
4631
|
-
if (grantAdmin && AR) ops.push(await tl.grantRole(AR, getAddress(operator)));
|
|
4632
|
-
const receipts = [];
|
|
4633
|
-
for (const tx of ops) {
|
|
4634
|
-
receipts.push(await tx.wait());
|
|
5468
|
+
};
|
|
5469
|
+
}
|
|
5470
|
+
});
|
|
5471
|
+
|
|
5472
|
+
// src/governance/intents.js
|
|
5473
|
+
var require_intents = __commonJS({
|
|
5474
|
+
"src/governance/intents.js"(exports2, module2) {
|
|
5475
|
+
var { Interface, getAddress } = require("ethers");
|
|
5476
|
+
var { SageSDKError, CODES } = require_errors();
|
|
5477
|
+
function normalise(addr, label) {
|
|
5478
|
+
try {
|
|
5479
|
+
return getAddress(addr);
|
|
5480
|
+
} catch {
|
|
5481
|
+
throw new SageSDKError(CODES.INVALID_ARGS, `${label} required/invalid`);
|
|
4635
5482
|
}
|
|
4636
|
-
return { governor, timelock: timelock2, operator: getAddress(operator), txs: ops, receipts };
|
|
4637
|
-
}
|
|
4638
|
-
async function createOperatorSubDAO({ signer, factoryAddress, operator, name, description, accessModel, minStakeAmount, burnAmount, sxxx, grantAdmin = false }) {
|
|
4639
|
-
const created = await createSubDAO({ signer, factoryAddress, name, description, accessModel, minStakeAmount, burnAmount, sxxx });
|
|
4640
|
-
if (!created.subdao) throw new SageSDKError(CODES.UNKNOWN, "createSubDAO did not emit SubDAO address");
|
|
4641
|
-
const op = await makeOperator({ signer, subdao: created.subdao, operator, grantAdmin });
|
|
4642
|
-
return { ...created, operatorResult: op };
|
|
4643
5483
|
}
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
5484
|
+
var GovIface = new Interface([
|
|
5485
|
+
"function setQuorumVotes(uint256)",
|
|
5486
|
+
"function setVotingPeriodBlocks(uint32)",
|
|
5487
|
+
"function setDepositOnlyMode(bool)",
|
|
5488
|
+
// Legacy (deprecated)
|
|
5489
|
+
"function updateQuorumNumerator(uint256)"
|
|
5490
|
+
]);
|
|
5491
|
+
var RegIface = new Interface([
|
|
5492
|
+
"function setForkPolicy(uint8)",
|
|
5493
|
+
"function updatePromptByGovernance(string,string)",
|
|
5494
|
+
"function addPrompt(string,bytes32[],string,string,uint8)"
|
|
5495
|
+
]);
|
|
5496
|
+
var CfgIface = new Interface([
|
|
5497
|
+
"function setPremiumRevSplit(address,uint16,uint16,uint16)",
|
|
5498
|
+
"function setCreatorStakeBps(address,uint256)"
|
|
5499
|
+
]);
|
|
5500
|
+
module2.exports = {
|
|
5501
|
+
// Governor targets
|
|
5502
|
+
buildSetQuorumVotesTx: ({ governor, quorumVotes }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setQuorumVotes", [BigInt(quorumVotes)]), value: 0n }),
|
|
5503
|
+
buildSetVotingPeriodTx: ({ governor, blocks }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setVotingPeriodBlocks", [Number(blocks)]), value: 0n }),
|
|
5504
|
+
buildSetDepositOnlyTx: ({ governor, enabled }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("setDepositOnlyMode", [!!enabled]), value: 0n }),
|
|
5505
|
+
// Legacy (deprecated) - use buildSetQuorumVotesTx instead
|
|
5506
|
+
buildUpdateQuorumTx: ({ governor, quorumPercent }) => ({ to: normalise(governor, "governor"), data: GovIface.encodeFunctionData("updateQuorumNumerator", [Number(quorumPercent)]), value: 0n }),
|
|
5507
|
+
// Registry targets
|
|
5508
|
+
buildSetForkPolicyTx: ({ registry, policy }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("setForkPolicy", [Number(policy)]), value: 0n }),
|
|
5509
|
+
buildUpdatePromptByGovernanceTx: ({ registry, key, cid }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("updatePromptByGovernance", [String(key), String(cid)]), value: 0n }),
|
|
5510
|
+
buildAddPromptTx: ({ registry, title, tags = [], contentCID, metadataCID = "", category = 0 }) => ({ to: normalise(registry, "registry"), data: RegIface.encodeFunctionData("addPrompt", [String(title), tags, String(contentCID), String(metadataCID), Number(category)]), value: 0n }),
|
|
5511
|
+
// GovernanceConfig targets
|
|
5512
|
+
buildSetPremiumSplitTx: ({ config, subdao: subdao2, treasuryBps, protocolBps, creatorBps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setPremiumRevSplit", [normalise(subdao2, "subdao"), Number(treasuryBps), Number(protocolBps), Number(creatorBps)]), value: 0n }),
|
|
5513
|
+
buildSetCreatorStakeBpsTx: ({ config, subdao: subdao2, bps }) => ({ to: normalise(config, "config"), data: CfgIface.encodeFunctionData("setCreatorStakeBps", [normalise(subdao2, "subdao"), Number(bps)]), value: 0n })
|
|
5514
|
+
};
|
|
4648
5515
|
}
|
|
4649
5516
|
});
|
|
4650
5517
|
|
|
@@ -4690,7 +5557,6 @@ var require_templates = __commonJS({
|
|
|
4690
5557
|
signer,
|
|
4691
5558
|
subdao: subdaoAddr,
|
|
4692
5559
|
operator,
|
|
4693
|
-
stakeAmount,
|
|
4694
5560
|
cooldownSeconds,
|
|
4695
5561
|
depositOnly = true
|
|
4696
5562
|
}) {
|
|
@@ -4708,10 +5574,6 @@ var require_templates = __commonJS({
|
|
|
4708
5574
|
const operatorHasExecutor = await tl.hasRole(executorRole, operatorAddress).catch(() => false);
|
|
4709
5575
|
if (!operatorHasExecutor) actions.push(() => tl.grantRole(executorRole, operatorAddress));
|
|
4710
5576
|
const governorContract = new Contract(governor, abi2.Governor, signer);
|
|
4711
|
-
if (stakeAmount != null) {
|
|
4712
|
-
const value = BigInt(stakeAmount);
|
|
4713
|
-
actions.push(() => governorContract.setProposalStakeRequired(value));
|
|
4714
|
-
}
|
|
4715
5577
|
if (cooldownSeconds != null) {
|
|
4716
5578
|
const value = BigInt(cooldownSeconds);
|
|
4717
5579
|
actions.push(() => governorContract.setProposalCooldown(value));
|
|
@@ -4726,7 +5588,6 @@ var require_templates = __commonJS({
|
|
|
4726
5588
|
provider,
|
|
4727
5589
|
signer,
|
|
4728
5590
|
subdao: subdaoAddr,
|
|
4729
|
-
stakeAmount,
|
|
4730
5591
|
cooldownSeconds,
|
|
4731
5592
|
anyoneExec = true,
|
|
4732
5593
|
depositOnly = false
|
|
@@ -4746,10 +5607,6 @@ var require_templates = __commonJS({
|
|
|
4746
5607
|
if (!hasZeroExec) actions.push(() => tl.grantRole(executorRole, ZeroAddress));
|
|
4747
5608
|
}
|
|
4748
5609
|
const governorContract = new Contract(governor, abi2.Governor, signer);
|
|
4749
|
-
if (stakeAmount != null) {
|
|
4750
|
-
const value = BigInt(stakeAmount);
|
|
4751
|
-
actions.push(() => governorContract.setProposalStakeRequired(value));
|
|
4752
|
-
}
|
|
4753
5610
|
if (cooldownSeconds != null) {
|
|
4754
5611
|
const value = BigInt(cooldownSeconds);
|
|
4755
5612
|
actions.push(() => governorContract.setProposalCooldown(value));
|
|
@@ -5354,8 +6211,7 @@ var require_operations = __commonJS({
|
|
|
5354
6211
|
signer,
|
|
5355
6212
|
governor,
|
|
5356
6213
|
timelock: timelock2 = null,
|
|
5357
|
-
subdao: subdao2 = null
|
|
5358
|
-
sxxxToken = null
|
|
6214
|
+
subdao: subdao2 = null
|
|
5359
6215
|
}) {
|
|
5360
6216
|
if (!provider) throw new SageSDKError(CODES.INVALID_ARGS, "provider required");
|
|
5361
6217
|
if (!governor) throw new SageSDKError(CODES.INVALID_ARGS, "governor required");
|
|
@@ -5382,29 +6238,11 @@ var require_operations = __commonJS({
|
|
|
5382
6238
|
let mode = null;
|
|
5383
6239
|
let eligible = null;
|
|
5384
6240
|
if (resolvedSubDAO) {
|
|
5385
|
-
const subdaoContract = new Contract(resolvedSubDAO, ["function getGovernanceMode() view returns (uint8)", "function isEligibleMember(address) view returns (bool)"
|
|
6241
|
+
const subdaoContract = new Contract(resolvedSubDAO, ["function getGovernanceMode() view returns (uint8)", "function isEligibleMember(address) view returns (bool)"], provider);
|
|
5386
6242
|
mode = await subdaoContract.getGovernanceMode().catch(() => null);
|
|
5387
6243
|
if (mode === 0 && signerAddr) {
|
|
5388
6244
|
eligible = await subdaoContract.isEligibleMember(signerAddr).catch(() => null);
|
|
5389
6245
|
}
|
|
5390
|
-
if (!sxxxToken) {
|
|
5391
|
-
sxxxToken = await subdaoContract.stakeToken().catch(() => null);
|
|
5392
|
-
if (sxxxToken === ZeroAddress) sxxxToken = null;
|
|
5393
|
-
}
|
|
5394
|
-
}
|
|
5395
|
-
if (!sxxxToken) {
|
|
5396
|
-
sxxxToken = await governorContract.sxxxToken().catch(() => null);
|
|
5397
|
-
if (sxxxToken === ZeroAddress) sxxxToken = null;
|
|
5398
|
-
}
|
|
5399
|
-
let balance = null;
|
|
5400
|
-
let allowance = null;
|
|
5401
|
-
let authorizedBurner = null;
|
|
5402
|
-
const stakeRequired = sxxxToken ? BigInt(50) * BigInt(10) ** BigInt(18) : null;
|
|
5403
|
-
if (sxxxToken && signerAddr) {
|
|
5404
|
-
const tokenContract = new Contract(sxxxToken, ["function balanceOf(address) view returns (uint256)", "function allowance(address,address) view returns (uint256)", "function authorizedBurners(address) view returns (bool)"], provider);
|
|
5405
|
-
balance = await tokenContract.balanceOf(signerAddr).catch(() => null);
|
|
5406
|
-
allowance = await tokenContract.allowance(signerAddr, govAddr).catch(() => null);
|
|
5407
|
-
authorizedBurner = await tokenContract.authorizedBurners(govAddr).catch(() => null);
|
|
5408
6246
|
}
|
|
5409
6247
|
let cooldownSeconds = null;
|
|
5410
6248
|
let cooldownRemaining = null;
|
|
@@ -5434,13 +6272,6 @@ var require_operations = __commonJS({
|
|
|
5434
6272
|
subdao: resolvedSubDAO,
|
|
5435
6273
|
governanceMode: mode,
|
|
5436
6274
|
eligibleMember: eligible,
|
|
5437
|
-
stake: {
|
|
5438
|
-
token: sxxxToken,
|
|
5439
|
-
requiredWei: stakeRequired,
|
|
5440
|
-
balanceWei: balance,
|
|
5441
|
-
allowanceWei: allowance,
|
|
5442
|
-
governorAuthorizedBurner: authorizedBurner
|
|
5443
|
-
},
|
|
5444
6275
|
timelock: timelock2
|
|
5445
6276
|
};
|
|
5446
6277
|
}
|
|
@@ -5875,13 +6706,16 @@ var require_factory = __commonJS({
|
|
|
5875
6706
|
accessModel: Number(template[2]),
|
|
5876
6707
|
forkPolicy: Number(template[3]),
|
|
5877
6708
|
membershipPolicy: Number(template[4]),
|
|
5878
|
-
|
|
6709
|
+
// template[5] was minStakeAmount (deprecated)
|
|
5879
6710
|
burnAmount: BigInt(template[6].toString()),
|
|
5880
6711
|
votingDelay: BigInt(template[7].toString()),
|
|
5881
6712
|
votingPeriod: BigInt(template[8].toString()),
|
|
5882
6713
|
proposalThreshold: BigInt(template[9].toString()),
|
|
6714
|
+
// template[10] is quorumVotes (absolute token amount, e.g., 100e18 = 100 SXXX)
|
|
6715
|
+
quorumVotes: BigInt(template[10].toString()),
|
|
5883
6716
|
quorumPercentage: Number(template[10]),
|
|
5884
|
-
|
|
6717
|
+
// deprecated alias
|
|
6718
|
+
// template[11] was proposalStakeRequired (deprecated)
|
|
5885
6719
|
proposalCooldownSeconds: BigInt(template[12].toString()),
|
|
5886
6720
|
isActive: Boolean(template[13])
|
|
5887
6721
|
});
|
|
@@ -5902,13 +6736,16 @@ var require_factory = __commonJS({
|
|
|
5902
6736
|
accessModel: Number(template[2]),
|
|
5903
6737
|
forkPolicy: Number(template[3]),
|
|
5904
6738
|
membershipPolicy: Number(template[4]),
|
|
5905
|
-
|
|
6739
|
+
// template[5] was minStakeAmount (deprecated)
|
|
5906
6740
|
burnAmount: BigInt(template[6].toString()),
|
|
5907
6741
|
votingDelay: BigInt(template[7].toString()),
|
|
5908
6742
|
votingPeriod: BigInt(template[8].toString()),
|
|
5909
6743
|
proposalThreshold: BigInt(template[9].toString()),
|
|
6744
|
+
// template[10] is quorumVotes (absolute token amount, e.g., 100e18 = 100 SXXX)
|
|
6745
|
+
quorumVotes: BigInt(template[10].toString()),
|
|
5910
6746
|
quorumPercentage: Number(template[10]),
|
|
5911
|
-
|
|
6747
|
+
// deprecated alias
|
|
6748
|
+
// template[11] was proposalStakeRequired (deprecated)
|
|
5912
6749
|
proposalCooldownSeconds: BigInt(template[12].toString()),
|
|
5913
6750
|
isActive: Boolean(template[13]),
|
|
5914
6751
|
usage: usage != null ? BigInt(usage.toString()) : null,
|
|
@@ -5916,18 +6753,19 @@ var require_factory = __commonJS({
|
|
|
5916
6753
|
};
|
|
5917
6754
|
}
|
|
5918
6755
|
var FactoryWriteInterface = new Interface(ABI.FactoryWrite);
|
|
5919
|
-
function buildCreateSubDAOTx({ factory: factory2, name, description, accessModel,
|
|
6756
|
+
function buildCreateSubDAOTx({ factory: factory2, name, description, accessModel, burnAmount }) {
|
|
5920
6757
|
const addr = normalise(factory2, "factory");
|
|
5921
6758
|
const payload = FactoryWriteInterface.encodeFunctionData("createSubDAO", [
|
|
5922
6759
|
String(name),
|
|
5923
6760
|
String(description),
|
|
5924
6761
|
Number(accessModel),
|
|
5925
|
-
|
|
6762
|
+
0n,
|
|
6763
|
+
// minStakeAmount deprecated
|
|
5926
6764
|
BigInt(burnAmount)
|
|
5927
6765
|
]);
|
|
5928
6766
|
return { to: addr, data: payload, value: 0n };
|
|
5929
6767
|
}
|
|
5930
|
-
function buildCreateSubDAOWithStableTx({ factory: factory2, name, description, accessModel,
|
|
6768
|
+
function buildCreateSubDAOWithStableTx({ factory: factory2, name, description, accessModel, permit }) {
|
|
5931
6769
|
const addr = normalise(factory2, "factory");
|
|
5932
6770
|
if (!permit) throw new SageSDKError(CODES.INVALID_ARGS, "permit required for stable creation");
|
|
5933
6771
|
const tuple = [
|
|
@@ -5941,7 +6779,8 @@ var require_factory = __commonJS({
|
|
|
5941
6779
|
String(name),
|
|
5942
6780
|
String(description),
|
|
5943
6781
|
Number(accessModel),
|
|
5944
|
-
|
|
6782
|
+
0n,
|
|
6783
|
+
// minStakeAmount deprecated
|
|
5945
6784
|
tuple
|
|
5946
6785
|
]);
|
|
5947
6786
|
return { to: addr, data: payload, value: 0n };
|
|
@@ -5983,30 +6822,37 @@ var require_factory = __commonJS({
|
|
|
5983
6822
|
name,
|
|
5984
6823
|
description,
|
|
5985
6824
|
accessModel,
|
|
5986
|
-
minStakeAmount,
|
|
5987
6825
|
burnAmount,
|
|
5988
6826
|
votingDelay,
|
|
5989
6827
|
votingPeriod,
|
|
5990
6828
|
proposalThreshold,
|
|
6829
|
+
quorumVotes,
|
|
5991
6830
|
quorumPercentage,
|
|
6831
|
+
// deprecated alias for quorumVotes
|
|
5992
6832
|
initialForkPolicy,
|
|
5993
6833
|
initialMembershipPolicy,
|
|
5994
|
-
profileCID
|
|
6834
|
+
profileCID,
|
|
6835
|
+
manifestCID,
|
|
6836
|
+
manifestVersion
|
|
5995
6837
|
}) {
|
|
5996
6838
|
const addr = normalise(factory2, "factory");
|
|
6839
|
+
const quorum = quorumVotes ?? quorumPercentage;
|
|
5997
6840
|
const payload = FactoryWriteInterface.encodeFunctionData("createSubDAOWithParams", [
|
|
5998
6841
|
String(name),
|
|
5999
6842
|
String(description),
|
|
6000
6843
|
Number(accessModel),
|
|
6001
|
-
|
|
6844
|
+
0n,
|
|
6845
|
+
// minStakeAmount deprecated
|
|
6002
6846
|
BigInt(burnAmount),
|
|
6003
6847
|
BigInt(votingDelay),
|
|
6004
6848
|
BigInt(votingPeriod),
|
|
6005
6849
|
BigInt(proposalThreshold),
|
|
6006
|
-
BigInt(
|
|
6850
|
+
BigInt(quorum),
|
|
6007
6851
|
Number(initialForkPolicy),
|
|
6008
6852
|
Number(initialMembershipPolicy),
|
|
6009
|
-
String(profileCID ?? "")
|
|
6853
|
+
String(profileCID ?? ""),
|
|
6854
|
+
String(manifestCID ?? ""),
|
|
6855
|
+
String(manifestVersion ?? "")
|
|
6010
6856
|
]);
|
|
6011
6857
|
return { to: addr, data: payload, value: 0n };
|
|
6012
6858
|
}
|
|
@@ -6015,7 +6861,6 @@ var require_factory = __commonJS({
|
|
|
6015
6861
|
name,
|
|
6016
6862
|
description,
|
|
6017
6863
|
accessModel,
|
|
6018
|
-
minStakeAmount,
|
|
6019
6864
|
operatorExecutor,
|
|
6020
6865
|
operatorAdmin,
|
|
6021
6866
|
permit
|
|
@@ -6033,7 +6878,8 @@ var require_factory = __commonJS({
|
|
|
6033
6878
|
String(name),
|
|
6034
6879
|
String(description),
|
|
6035
6880
|
Number(accessModel),
|
|
6036
|
-
|
|
6881
|
+
0n,
|
|
6882
|
+
// minStakeAmount deprecated
|
|
6037
6883
|
normalise(operatorExecutor, "operatorExecutor"),
|
|
6038
6884
|
normalise(operatorAdmin, "operatorAdmin"),
|
|
6039
6885
|
tuple
|
|
@@ -6045,7 +6891,6 @@ var require_factory = __commonJS({
|
|
|
6045
6891
|
name,
|
|
6046
6892
|
description,
|
|
6047
6893
|
accessModel,
|
|
6048
|
-
minStakeAmount,
|
|
6049
6894
|
operatorExecutor,
|
|
6050
6895
|
operatorAdmin,
|
|
6051
6896
|
anyoneExec = false,
|
|
@@ -6065,7 +6910,8 @@ var require_factory = __commonJS({
|
|
|
6065
6910
|
String(name),
|
|
6066
6911
|
String(description),
|
|
6067
6912
|
Number(accessModel),
|
|
6068
|
-
|
|
6913
|
+
0n,
|
|
6914
|
+
// minStakeAmount deprecated
|
|
6069
6915
|
normalise(operatorExecutor, "operatorExecutor"),
|
|
6070
6916
|
normalise(operatorAdmin, "operatorAdmin"),
|
|
6071
6917
|
Boolean(anyoneExec),
|
|
@@ -7001,7 +7847,7 @@ var require_ipns = __commonJS({
|
|
|
7001
7847
|
}
|
|
7002
7848
|
const worker = workerConfig ? {
|
|
7003
7849
|
baseUrl: removeTrailingSlash(workerConfig.baseUrl || workerConfig.url || workerConfig.workerBaseUrl || ""),
|
|
7004
|
-
challengePath: ensureLeadingSlash(workerConfig.challengePath || workerConfig.challenge || "/auth/challenge"),
|
|
7850
|
+
challengePath: ensureLeadingSlash(workerConfig.challengePath || workerConfig.challenge || "/ipfs/auth/challenge"),
|
|
7005
7851
|
publishPath: ensureLeadingSlash(workerConfig.publishPath || workerConfig.publish || "/ipns/publish"),
|
|
7006
7852
|
token: workerConfig.token || null,
|
|
7007
7853
|
getAuth: workerConfig.getAuth || null,
|
|
@@ -7482,6 +8328,300 @@ var require_personal = __commonJS({
|
|
|
7482
8328
|
}
|
|
7483
8329
|
});
|
|
7484
8330
|
|
|
8331
|
+
// src/premium/index.js
|
|
8332
|
+
var require_premium = __commonJS({
|
|
8333
|
+
"src/premium/index.js"(exports2, module2) {
|
|
8334
|
+
var axios = require("axios");
|
|
8335
|
+
var { getAddress } = require("ethers");
|
|
8336
|
+
function safeGetAddress(value) {
|
|
8337
|
+
try {
|
|
8338
|
+
return getAddress(value);
|
|
8339
|
+
} catch {
|
|
8340
|
+
return null;
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8343
|
+
function mapSafe(list, mapper) {
|
|
8344
|
+
const out = [];
|
|
8345
|
+
for (const item of list || []) {
|
|
8346
|
+
try {
|
|
8347
|
+
const v = mapper(item);
|
|
8348
|
+
if (v != null) out.push(v);
|
|
8349
|
+
} catch {
|
|
8350
|
+
}
|
|
8351
|
+
}
|
|
8352
|
+
return out;
|
|
8353
|
+
}
|
|
8354
|
+
async function query(url, document, variables) {
|
|
8355
|
+
if (!url) throw new Error("subgraph url required");
|
|
8356
|
+
const resp = await axios.post(url, { query: document, variables }, { timeout: 1e4 });
|
|
8357
|
+
if (resp.data && resp.data.errors) {
|
|
8358
|
+
throw new Error(resp.data.errors.map((e) => e.message).join("; "));
|
|
8359
|
+
}
|
|
8360
|
+
return resp.data.data;
|
|
8361
|
+
}
|
|
8362
|
+
async function listPremiumPrompts({ url, subdao: subdao2 = null, first = 50, skip = 0, orderBy = "blockTimestamp", orderDirection = "desc" }) {
|
|
8363
|
+
const safeOrderBy = ["blockTimestamp", "price"].includes(orderBy) ? orderBy : "blockTimestamp";
|
|
8364
|
+
const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
|
|
8365
|
+
let doc;
|
|
8366
|
+
const variables = {
|
|
8367
|
+
first: Math.min(Math.max(1, Number(first || 50)), 200),
|
|
8368
|
+
skip: Number(skip || 0)
|
|
8369
|
+
};
|
|
8370
|
+
if (subdao2) {
|
|
8371
|
+
const addr = safeGetAddress(subdao2);
|
|
8372
|
+
if (!addr) throw new Error("invalid subdao address");
|
|
8373
|
+
variables.subdao = addr.toLowerCase();
|
|
8374
|
+
doc = `
|
|
8375
|
+
query($subdao: Bytes!, $first: Int!, $skip: Int!) {
|
|
8376
|
+
premiumPrompts(
|
|
8377
|
+
where: { subdao: $subdao }
|
|
8378
|
+
first: $first
|
|
8379
|
+
skip: $skip
|
|
8380
|
+
orderBy: ${safeOrderBy}
|
|
8381
|
+
orderDirection: ${safeOrderDirection}
|
|
8382
|
+
) {
|
|
8383
|
+
id
|
|
8384
|
+
cidHash
|
|
8385
|
+
subdao
|
|
8386
|
+
price
|
|
8387
|
+
manifestCID
|
|
8388
|
+
blockNumber
|
|
8389
|
+
blockTimestamp
|
|
8390
|
+
transactionHash
|
|
8391
|
+
}
|
|
8392
|
+
}
|
|
8393
|
+
`;
|
|
8394
|
+
} else {
|
|
8395
|
+
doc = `
|
|
8396
|
+
query($first: Int!, $skip: Int!) {
|
|
8397
|
+
premiumPrompts(
|
|
8398
|
+
first: $first
|
|
8399
|
+
skip: $skip
|
|
8400
|
+
orderBy: ${safeOrderBy}
|
|
8401
|
+
orderDirection: ${safeOrderDirection}
|
|
8402
|
+
) {
|
|
8403
|
+
id
|
|
8404
|
+
cidHash
|
|
8405
|
+
subdao
|
|
8406
|
+
price
|
|
8407
|
+
manifestCID
|
|
8408
|
+
blockNumber
|
|
8409
|
+
blockTimestamp
|
|
8410
|
+
transactionHash
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
`;
|
|
8414
|
+
}
|
|
8415
|
+
const data = await query(url, doc, variables);
|
|
8416
|
+
return mapSafe(data?.premiumPrompts, (p) => {
|
|
8417
|
+
const sub = safeGetAddress(p.subdao);
|
|
8418
|
+
if (!sub) return null;
|
|
8419
|
+
return {
|
|
8420
|
+
id: String(p.id),
|
|
8421
|
+
cidHash: String(p.cidHash),
|
|
8422
|
+
subdao: sub,
|
|
8423
|
+
price: BigInt(String(p.price || "0")),
|
|
8424
|
+
manifestCID: p.manifestCID || null,
|
|
8425
|
+
blockNumber: Number(p.blockNumber || 0),
|
|
8426
|
+
blockTimestamp: Number(p.blockTimestamp || 0),
|
|
8427
|
+
transactionHash: p.transactionHash || null
|
|
8428
|
+
};
|
|
8429
|
+
});
|
|
8430
|
+
}
|
|
8431
|
+
async function getPremiumPrompt({ url, cidHash }) {
|
|
8432
|
+
const id2 = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
|
|
8433
|
+
const doc = `
|
|
8434
|
+
query($id: ID!) {
|
|
8435
|
+
premiumPrompt(id: $id) {
|
|
8436
|
+
id
|
|
8437
|
+
cidHash
|
|
8438
|
+
subdao
|
|
8439
|
+
price
|
|
8440
|
+
manifestCID
|
|
8441
|
+
blockNumber
|
|
8442
|
+
blockTimestamp
|
|
8443
|
+
transactionHash
|
|
8444
|
+
}
|
|
8445
|
+
premiumPromptStats(id: $id) {
|
|
8446
|
+
totalRevenue
|
|
8447
|
+
purchaseCount
|
|
8448
|
+
uniqueBuyers
|
|
8449
|
+
currentHolders
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
`;
|
|
8453
|
+
const data = await query(url, doc, { id: id2 });
|
|
8454
|
+
const p = data?.premiumPrompt;
|
|
8455
|
+
if (!p) return null;
|
|
8456
|
+
const sub = safeGetAddress(p.subdao);
|
|
8457
|
+
if (!sub) return null;
|
|
8458
|
+
const stats = data?.premiumPromptStats || {};
|
|
8459
|
+
return {
|
|
8460
|
+
id: String(p.id),
|
|
8461
|
+
cidHash: String(p.cidHash),
|
|
8462
|
+
subdao: sub,
|
|
8463
|
+
price: BigInt(String(p.price || "0")),
|
|
8464
|
+
manifestCID: p.manifestCID || null,
|
|
8465
|
+
blockNumber: Number(p.blockNumber || 0),
|
|
8466
|
+
blockTimestamp: Number(p.blockTimestamp || 0),
|
|
8467
|
+
transactionHash: p.transactionHash || null,
|
|
8468
|
+
// Stats
|
|
8469
|
+
totalRevenue: stats.totalRevenue ? BigInt(String(stats.totalRevenue)) : null,
|
|
8470
|
+
purchaseCount: stats.purchaseCount ? BigInt(String(stats.purchaseCount)) : null,
|
|
8471
|
+
uniqueBuyers: stats.uniqueBuyers ? BigInt(String(stats.uniqueBuyers)) : null,
|
|
8472
|
+
currentHolders: stats.currentHolders ? BigInt(String(stats.currentHolders)) : null
|
|
8473
|
+
};
|
|
8474
|
+
}
|
|
8475
|
+
async function listPremiumPurchases({ url, cidHash, first = 50, skip = 0 }) {
|
|
8476
|
+
const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
|
|
8477
|
+
const doc = `
|
|
8478
|
+
query($cidHash: Bytes!, $first: Int!, $skip: Int!) {
|
|
8479
|
+
premiumPurchases(
|
|
8480
|
+
where: { cidHash: $cidHash }
|
|
8481
|
+
first: $first
|
|
8482
|
+
skip: $skip
|
|
8483
|
+
orderBy: blockTimestamp
|
|
8484
|
+
orderDirection: desc
|
|
8485
|
+
) {
|
|
8486
|
+
id
|
|
8487
|
+
cidHash
|
|
8488
|
+
buyer
|
|
8489
|
+
price
|
|
8490
|
+
treasury
|
|
8491
|
+
blockNumber
|
|
8492
|
+
blockTimestamp
|
|
8493
|
+
transactionHash
|
|
8494
|
+
}
|
|
8495
|
+
}
|
|
8496
|
+
`;
|
|
8497
|
+
const data = await query(url, doc, {
|
|
8498
|
+
cidHash: normalizedCidHash,
|
|
8499
|
+
first: Math.min(Math.max(1, Number(first || 50)), 200),
|
|
8500
|
+
skip: Number(skip || 0)
|
|
8501
|
+
});
|
|
8502
|
+
return mapSafe(data?.premiumPurchases, (p) => {
|
|
8503
|
+
const buyer = safeGetAddress(p.buyer);
|
|
8504
|
+
const treasury2 = safeGetAddress(p.treasury);
|
|
8505
|
+
if (!buyer || !treasury2) return null;
|
|
8506
|
+
return {
|
|
8507
|
+
id: String(p.id),
|
|
8508
|
+
cidHash: String(p.cidHash),
|
|
8509
|
+
buyer,
|
|
8510
|
+
price: BigInt(String(p.price || "0")),
|
|
8511
|
+
treasury: treasury2,
|
|
8512
|
+
blockNumber: Number(p.blockNumber || 0),
|
|
8513
|
+
blockTimestamp: Number(p.blockTimestamp || 0),
|
|
8514
|
+
transactionHash: p.transactionHash || null
|
|
8515
|
+
};
|
|
8516
|
+
});
|
|
8517
|
+
}
|
|
8518
|
+
async function hasPurchased({ url, cidHash, holder }) {
|
|
8519
|
+
const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
|
|
8520
|
+
const normalizedHolder = holder.toLowerCase();
|
|
8521
|
+
const id2 = `${normalizedCidHash}-${normalizedHolder}`;
|
|
8522
|
+
const doc = `
|
|
8523
|
+
query($id: ID!) {
|
|
8524
|
+
premiumHolderBalance(id: $id) {
|
|
8525
|
+
balance
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
`;
|
|
8529
|
+
const data = await query(url, doc, { id: id2 });
|
|
8530
|
+
const balance = data?.premiumHolderBalance?.balance;
|
|
8531
|
+
return balance ? BigInt(String(balance)) > 0n : false;
|
|
8532
|
+
}
|
|
8533
|
+
async function getHolderBalance({ url, cidHash, holder }) {
|
|
8534
|
+
const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
|
|
8535
|
+
const normalizedHolder = holder.toLowerCase();
|
|
8536
|
+
const id2 = `${normalizedCidHash}-${normalizedHolder}`;
|
|
8537
|
+
const doc = `
|
|
8538
|
+
query($id: ID!) {
|
|
8539
|
+
premiumHolderBalance(id: $id) {
|
|
8540
|
+
balance
|
|
8541
|
+
updatedAt
|
|
8542
|
+
}
|
|
8543
|
+
}
|
|
8544
|
+
`;
|
|
8545
|
+
const data = await query(url, doc, { id: id2 });
|
|
8546
|
+
const row = data?.premiumHolderBalance;
|
|
8547
|
+
if (!row) return 0n;
|
|
8548
|
+
return BigInt(String(row.balance || "0"));
|
|
8549
|
+
}
|
|
8550
|
+
async function listHolders({ url, cidHash, first = 100, skip = 0 }) {
|
|
8551
|
+
const normalizedCidHash = cidHash.toLowerCase().startsWith("0x") ? cidHash.toLowerCase() : `0x${cidHash.toLowerCase()}`;
|
|
8552
|
+
const doc = `
|
|
8553
|
+
query($cidHash: Bytes!, $first: Int!, $skip: Int!) {
|
|
8554
|
+
premiumHolderBalances(
|
|
8555
|
+
where: { cidHash: $cidHash, balance_gt: "0" }
|
|
8556
|
+
first: $first
|
|
8557
|
+
skip: $skip
|
|
8558
|
+
orderBy: balance
|
|
8559
|
+
orderDirection: desc
|
|
8560
|
+
) {
|
|
8561
|
+
id
|
|
8562
|
+
cidHash
|
|
8563
|
+
holder
|
|
8564
|
+
balance
|
|
8565
|
+
updatedAt
|
|
8566
|
+
}
|
|
8567
|
+
}
|
|
8568
|
+
`;
|
|
8569
|
+
const data = await query(url, doc, {
|
|
8570
|
+
cidHash: normalizedCidHash,
|
|
8571
|
+
first: Math.min(Math.max(1, Number(first || 100)), 500),
|
|
8572
|
+
skip: Number(skip || 0)
|
|
8573
|
+
});
|
|
8574
|
+
return mapSafe(data?.premiumHolderBalances, (h) => {
|
|
8575
|
+
const holder = safeGetAddress(h.holder);
|
|
8576
|
+
if (!holder) return null;
|
|
8577
|
+
return {
|
|
8578
|
+
id: String(h.id),
|
|
8579
|
+
cidHash: String(h.cidHash),
|
|
8580
|
+
holder,
|
|
8581
|
+
balance: BigInt(String(h.balance || "0")),
|
|
8582
|
+
updatedAt: Number(h.updatedAt || 0)
|
|
8583
|
+
};
|
|
8584
|
+
});
|
|
8585
|
+
}
|
|
8586
|
+
async function getRevSplit({ url, subdao: subdao2 }) {
|
|
8587
|
+
const addr = safeGetAddress(subdao2);
|
|
8588
|
+
if (!addr) throw new Error("invalid subdao address");
|
|
8589
|
+
const doc = `
|
|
8590
|
+
query($id: ID!) {
|
|
8591
|
+
premiumRevSplit(id: $id) {
|
|
8592
|
+
id
|
|
8593
|
+
subdao
|
|
8594
|
+
treasuryBps
|
|
8595
|
+
protocolBps
|
|
8596
|
+
creatorBps
|
|
8597
|
+
updatedAt
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
`;
|
|
8601
|
+
const data = await query(url, doc, { id: addr.toLowerCase() });
|
|
8602
|
+
const split = data?.premiumRevSplit;
|
|
8603
|
+
if (!split) return null;
|
|
8604
|
+
return {
|
|
8605
|
+
id: String(split.id),
|
|
8606
|
+
subdao: safeGetAddress(split.subdao) || addr,
|
|
8607
|
+
treasuryBps: Number(split.treasuryBps || 0),
|
|
8608
|
+
protocolBps: Number(split.protocolBps || 0),
|
|
8609
|
+
creatorBps: Number(split.creatorBps || 0),
|
|
8610
|
+
updatedAt: Number(split.updatedAt || 0)
|
|
8611
|
+
};
|
|
8612
|
+
}
|
|
8613
|
+
module2.exports = {
|
|
8614
|
+
listPremiumPrompts,
|
|
8615
|
+
getPremiumPrompt,
|
|
8616
|
+
listPremiumPurchases,
|
|
8617
|
+
hasPurchased,
|
|
8618
|
+
getHolderBalance,
|
|
8619
|
+
listHolders,
|
|
8620
|
+
getRevSplit
|
|
8621
|
+
};
|
|
8622
|
+
}
|
|
8623
|
+
});
|
|
8624
|
+
|
|
7485
8625
|
// src/utils/privateTx.js
|
|
7486
8626
|
var require_privateTx = __commonJS({
|
|
7487
8627
|
"src/utils/privateTx.js"(exports2, module2) {
|
|
@@ -12892,6 +14032,7 @@ var ipfs = require_ipfs();
|
|
|
12892
14032
|
var ipns = require_ipns();
|
|
12893
14033
|
var token = require_token();
|
|
12894
14034
|
var personal = require_personal();
|
|
14035
|
+
var premium = require_premium();
|
|
12895
14036
|
var subgraph = require_subgraph();
|
|
12896
14037
|
var privateTx = require_privateTx();
|
|
12897
14038
|
var safe = require_safe();
|
|
@@ -12945,6 +14086,7 @@ module.exports = {
|
|
|
12945
14086
|
SageEchoExecutor,
|
|
12946
14087
|
token,
|
|
12947
14088
|
personal,
|
|
14089
|
+
premium,
|
|
12948
14090
|
treasury,
|
|
12949
14091
|
boost,
|
|
12950
14092
|
// bond module removed; bonds deprecated in CLI/SDK
|