Skip to content

Commit d21c551

Browse files
committed
feat: deploy scripts for RPB and governance proposal
- deploy-rpb.js: deploy RPB to any network, linked to a registry - propose-rpb-update.js: governance proposal to update registry pointer and set constitution on-chain through the DAO timelock Deployed RPB v0.1.6 to Etherlink Shadownet: RPB: 0x132169EA108a52d21F168d6906c8F2Eb6Ef1E18A Registry: 0xA2Ec6A1Aa7bd2bfF4f7AFF8d40247F302cFBBb2F DAO: 0x4a988674D4d4372C6cE133858beE2a717957D6a1 Governor: 0x7c83FF7b0356DbE332BFC527F1Ea73283974aEA2 Constitution: UDHR (10738 chars), set via governance proposal
1 parent 30d03fe commit d21c551

2 files changed

Lines changed: 193 additions & 0 deletions

File tree

scripts/deploy-rpb.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const { ethers } = require("hardhat");
2+
3+
async function main() {
4+
const registryAddress = process.env.REGISTRY_ADDRESS;
5+
if (!registryAddress) {
6+
throw new Error("Set REGISTRY_ADDRESS env var");
7+
}
8+
9+
const [deployer] = await ethers.getSigners();
10+
console.log("Deployer:", deployer.address);
11+
console.log("Registry:", registryAddress);
12+
console.log("Network:", (await ethers.provider.getNetwork()).chainId.toString());
13+
14+
const balance = await ethers.provider.getBalance(deployer.address);
15+
console.log("Balance:", ethers.formatEther(balance), "XTZ");
16+
17+
// Deploy RPB
18+
const RPB = await ethers.getContractFactory("RPB");
19+
console.log("\nDeploying RPB...");
20+
const rpb = await RPB.deploy(registryAddress);
21+
await rpb.waitForDeployment();
22+
const rpbAddr = await rpb.getAddress();
23+
console.log("RPB deployed at:", rpbAddr);
24+
25+
// Verify basic state
26+
const name = await rpb.name();
27+
const symbol = await rpb.symbol();
28+
const dao = await rpb.dao();
29+
const totalSupply = await rpb.totalSupply();
30+
const constitution = await rpb.getConstitution();
31+
32+
console.log("\n--- RPB State ---");
33+
console.log("Token:", name, "(" + symbol + ")");
34+
console.log("DAO:", dao);
35+
console.log("Total supply:", ethers.formatEther(totalSupply), "ATN (should be 0)");
36+
console.log("Constitution:", constitution || "(empty)");
37+
console.log("Current epoch:", (await rpb.currentEpoch()).toString());
38+
console.log("Provider share:", (await rpb.inferenceProviderShare()).toString(), "bps");
39+
console.log("Shareholder share:", (await rpb.shareholderShare()).toString(), "bps");
40+
console.log("Treasury share:", (await rpb.treasuryShare()).toString(), "bps");
41+
}
42+
43+
main()
44+
.then(() => process.exit(0))
45+
.catch((error) => {
46+
console.error(error);
47+
process.exit(1);
48+
});

scripts/propose-rpb-update.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* Governance proposal: update rpb.contract in Registry + set constitution on new RPB.
3+
* Only works on testnets with short voting periods and deployer holding majority tokens.
4+
*/
5+
const { ethers } = require("hardhat");
6+
const fs = require("fs");
7+
const path = require("path");
8+
9+
const GOVERNOR = "0x7c83FF7b0356DbE332BFC527F1Ea73283974aEA2";
10+
const TIMELOCK = "0x4a988674D4d4372C6cE133858beE2a717957D6a1";
11+
const REGISTRY = "0xA2Ec6A1Aa7bd2bfF4f7AFF8d40247F302cFBBb2F";
12+
const NEW_RPB = "0x132169EA108a52d21F168d6906c8F2Eb6Ef1E18A";
13+
14+
async function main() {
15+
const [deployer] = await ethers.getSigners();
16+
console.log("Proposer:", deployer.address);
17+
18+
// Load constitution
19+
const constitution = fs.readFileSync(
20+
path.join(__dirname, "../constitution/v1_udhr.txt"), "utf8"
21+
).trim();
22+
console.log("Constitution length:", constitution.length, "chars");
23+
24+
// Governor contract
25+
const governorAbi = [
26+
"function propose(address[],uint256[],bytes[],string) returns (uint256)",
27+
"function castVote(uint256,uint8) returns (uint256)",
28+
"function queue(address[],uint256[],bytes[],bytes32) returns (uint256)",
29+
"function execute(address[],uint256[],bytes[],bytes32) payable returns (uint256)",
30+
"function state(uint256) view returns (uint8)",
31+
"function proposalDeadline(uint256) view returns (uint256)",
32+
"function hashProposal(address[],uint256[],bytes[],bytes32) view returns (uint256)",
33+
"function votingPeriod() view returns (uint256)",
34+
"function votingDelay() view returns (uint256)",
35+
];
36+
const governor = new ethers.Contract(GOVERNOR, governorAbi, deployer);
37+
38+
// Encode calldata for 2 actions:
39+
// 1. Registry.setRegistryValue("rpb.contract", NEW_RPB)
40+
// 2. RPB.setConstitution(constitution)
41+
const registryIface = new ethers.Interface([
42+
"function editRegistry(string,string)"
43+
]);
44+
const rpbIface = new ethers.Interface([
45+
"function setConstitution(string)"
46+
]);
47+
48+
const targets = [REGISTRY, NEW_RPB];
49+
const values = [0, 0];
50+
const calldatas = [
51+
registryIface.encodeFunctionData("editRegistry", ["rpb.contract", NEW_RPB]),
52+
rpbIface.encodeFunctionData("setConstitution", [constitution]),
53+
];
54+
const description = "RPB v0.1.6: update registry pointer + set UDHR constitution on-chain";
55+
const descHash = ethers.keccak256(ethers.toUtf8Bytes(description));
56+
57+
// Compute proposal ID
58+
const proposalId = await governor.hashProposal(targets, values, calldatas, descHash);
59+
60+
// 1. Propose (skip if already proposed)
61+
const currentState = await governor.state(proposalId).catch(() => null);
62+
if (currentState === null || currentState === undefined) {
63+
console.log("\n1. Creating proposal...");
64+
const proposeTx = await governor.propose(targets, values, calldatas, description);
65+
await proposeTx.wait();
66+
console.log("Proposal created.");
67+
} else {
68+
console.log("\n1. Proposal already exists, state:", ["Pending","Active","Canceled","Defeated","Succeeded","Queued","Expired","Executed"][Number(currentState)]);
69+
}
70+
console.log("Proposal ID:", proposalId.toString());
71+
72+
// Wait for voting delay (if any)
73+
const delay = await governor.votingDelay();
74+
if (delay > 0n) {
75+
console.log(`Waiting ${delay} blocks for voting delay...`);
76+
for (let i = 0; i < Number(delay) + 1; i++) {
77+
await ethers.provider.send("evm_mine", []);
78+
}
79+
}
80+
81+
// 2. Vote (skip if already voted or not active)
82+
const stateBeforeVote = await governor.state(proposalId);
83+
if (stateBeforeVote === 1n) { // Active
84+
console.log("\n2. Casting vote (For)...");
85+
try {
86+
const voteTx = await governor.castVote(proposalId, 1); // 1 = For
87+
await voteTx.wait();
88+
console.log("Vote cast.");
89+
} catch (e) {
90+
console.log("Vote may already be cast:", e.reason || e.message.slice(0, 100));
91+
}
92+
} else {
93+
console.log("\n2. Skipping vote, state:", stateBeforeVote.toString());
94+
}
95+
96+
// Wait for voting period to end (deadline is a timestamp on OZ5 Governor)
97+
const deadline = await governor.proposalDeadline(proposalId);
98+
console.log("Proposal deadline (timestamp):", deadline.toString());
99+
100+
while (true) {
101+
const block = await ethers.provider.getBlock("latest");
102+
const now = BigInt(block.timestamp);
103+
if (now > deadline) break;
104+
const secsLeft = Number(deadline - now);
105+
console.log(`Waiting ${secsLeft}s for voting period to end...`);
106+
await new Promise(r => setTimeout(r, Math.min(secsLeft * 1000, 10000)));
107+
}
108+
console.log("Voting period ended.");
109+
110+
// Check state: 4 = Succeeded
111+
const state = await governor.state(proposalId);
112+
console.log("Proposal state:", ["Pending","Active","Canceled","Defeated","Succeeded","Queued","Expired","Executed"][Number(state)]);
113+
if (state !== 4n) {
114+
console.error("Proposal did not succeed, state:", state.toString());
115+
process.exit(1);
116+
}
117+
118+
// 3. Queue
119+
console.log("\n3. Queueing...");
120+
const queueTx = await governor.queue(targets, values, calldatas, descHash);
121+
await queueTx.wait();
122+
console.log("Queued.");
123+
124+
// 4. Execute (delay is 0 on testnet)
125+
console.log("\n4. Executing...");
126+
const execTx = await governor.execute(targets, values, calldatas, descHash);
127+
await execTx.wait();
128+
console.log("Executed!");
129+
130+
// Verify
131+
const rpbAbi = ["function constitution() view returns (string)", "function getConstitution() view returns (string)"];
132+
const rpb = new ethers.Contract(NEW_RPB, rpbAbi, deployer);
133+
const onChain = await rpb.getConstitution();
134+
console.log("\n--- Verification ---");
135+
console.log("Constitution on-chain:", onChain.slice(0, 100) + "...");
136+
console.log("Length:", onChain.length);
137+
console.log("Matches file:", onChain === constitution);
138+
}
139+
140+
main()
141+
.then(() => process.exit(0))
142+
.catch((error) => {
143+
console.error(error);
144+
process.exit(1);
145+
});

0 commit comments

Comments
 (0)