mirror of
https://github.com/logos-messaging/waku-rlnv1-contract.git
synced 2026-01-06 00:03:08 +00:00
chore: add v1 to v2 migrator
This commit is contained in:
parent
6c7e59df6d
commit
886891b57a
21
README.md
21
README.md
@ -51,6 +51,27 @@ yarn deploy:sepolia
|
||||
yarn verify:sepolia # Ensure you have set ETHERSCAN_API_KEY in your env
|
||||
```
|
||||
|
||||
## Migrating v1 to v2
|
||||
|
||||
Refer to the [script](./scripts/migrate/v1-v2.ts) for more information.
|
||||
|
||||
### Locally
|
||||
|
||||
- To migrate on a local node, first start the local node and then run the migrate script
|
||||
|
||||
```shell
|
||||
yarn node
|
||||
yarn migrate:v1-v2:localhost
|
||||
```
|
||||
|
||||
### Sepolia
|
||||
|
||||
- To migrate to an target network (like Sepolia), use the name as mentioned in the Hardhat config file.
|
||||
|
||||
```shell
|
||||
yarn migrate:v1-v2:sepolia
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
For more information, see https://hardhat.org/hardhat-runner/docs/guides/project-setup
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"chainId": "11155111",
|
||||
"contracts": {
|
||||
"WakuRlnRegistry_Implementation": {
|
||||
"address": "0x451dE04f6C219EFDE9432a3B34D8eaac54C43589",
|
||||
"address": "0xC58900Eeb0bE8C7f54DdCD40488B30cDeEBCcbD8",
|
||||
"abi": [
|
||||
{
|
||||
"inputs": [],
|
||||
@ -22,17 +22,6 @@
|
||||
"name": "NoStorageContractAvailable",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "storageAddress",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "StorageAlreadyExists",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
@ -358,7 +347,7 @@
|
||||
]
|
||||
},
|
||||
"WakuRlnRegistry_Proxy": {
|
||||
"address": "0xE784b13B443b4a60557C8D5AF5942b16E434047B",
|
||||
"address": "0xABd93f37833107eDbAb58633ad5463aDEa1F95F4",
|
||||
"abi": [
|
||||
{
|
||||
"inputs": [
|
||||
@ -432,7 +421,7 @@
|
||||
]
|
||||
},
|
||||
"WakuRlnStorage_0": {
|
||||
"address": "0xd0AB35EdF257B2790274d84D485b4B91D90D6144",
|
||||
"address": "0xbd250d26E9dc468592f51bc892488450BfCb22b7",
|
||||
"abi": [
|
||||
{
|
||||
"inputs": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -14,6 +14,8 @@
|
||||
"deploy:sepolia": "yarn deploy sepolia",
|
||||
"deploy:localhost": "yarn deploy localhost",
|
||||
"verify:sepolia": "hardhat --network sepolia etherscan-verify",
|
||||
"migrate:v1-v2:sepolia": "hardhat --network sepolia run scripts/migrate/v1-v2.ts",
|
||||
"migrate:v1-v2:localhost": "hardhat --network localhost run scripts/migrate/v1-v2.ts",
|
||||
"coverage": "forge coverage --report lcov",
|
||||
"fmt": "prettier --write \"**/*.{js,ts}\"",
|
||||
"lint": "prettier --check \"**/*.{js,ts}\"",
|
||||
|
||||
157
scripts/migrate/v1-v2.ts
Normal file
157
scripts/migrate/v1-v2.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import { BigNumber } from "ethers";
|
||||
import hre from "hardhat";
|
||||
import { Provider } from "@ethersproject/providers";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
import { Contract } from "ethers";
|
||||
|
||||
async function getRlnV1Registry(provider: Provider) {
|
||||
const rlnV1Abi = [
|
||||
"function storages(uint16 index) public view returns (address)",
|
||||
];
|
||||
|
||||
const rlnV1Address = process.env.WAKU_RLNV1_REGISTRY_ADDRESS;
|
||||
if (!rlnV1Address) {
|
||||
throw new Error("WAKU_RLNV1_REGISTRY_ADDRESS env variable is not set");
|
||||
}
|
||||
const rlnV1Registry = new hre.ethers.Contract(
|
||||
rlnV1Address,
|
||||
rlnV1Abi,
|
||||
provider
|
||||
);
|
||||
return rlnV1Registry;
|
||||
}
|
||||
|
||||
async function getRlnV1Storage(rlnV1Registry: Contract) {
|
||||
const storageIndex = process.env.WAKU_RLNV1_STORAGE_INDEX;
|
||||
if (!storageIndex) {
|
||||
throw new Error("WAKU_RLNV1_STORAGE_INDEX env variable is not set");
|
||||
}
|
||||
const storageAddress = await rlnV1Registry.storages(storageIndex);
|
||||
const rlnV1StorageAbi = [
|
||||
"event MemberRegistered(uint256 idCommitment, uint256 index)",
|
||||
"function deployedBlockNumber() public view returns (uint32)",
|
||||
];
|
||||
const rlnV1Storage = new hre.ethers.Contract(
|
||||
storageAddress,
|
||||
rlnV1StorageAbi,
|
||||
rlnV1Registry.provider
|
||||
);
|
||||
return rlnV1Storage;
|
||||
}
|
||||
|
||||
async function getRlnV2Registry(signer: SignerWithAddress) {
|
||||
const rlnV2Abi = [
|
||||
"function register(uint256[] calldata commitments, uint256[] calldata limits) public",
|
||||
"function usingStorageIndex() public view returns (uint16)",
|
||||
"function storages(uint16 index) public view returns (address)",
|
||||
];
|
||||
const rlnV2Address = process.env.WAKU_RLNV2_REGISTRY_ADDRESS;
|
||||
|
||||
if (!rlnV2Address) {
|
||||
throw new Error("WAKU_RLNV2_REGISTRY_ADDRESS env variable is not set");
|
||||
}
|
||||
const rlnV2Registry = new hre.ethers.Contract(rlnV2Address, rlnV2Abi, signer);
|
||||
return rlnV2Registry;
|
||||
}
|
||||
|
||||
async function getRlnV2Storage(rlnV2Registry: Contract) {
|
||||
const storageIndex = await rlnV2Registry.usingStorageIndex();
|
||||
const storageAddress = await rlnV2Registry.storages(storageIndex);
|
||||
const rlnV2StorageAbi = [
|
||||
"event MemberRegistered(uint256 idCommitment, uint256 index)",
|
||||
"function deployedBlockNumber() public view returns (uint32)",
|
||||
];
|
||||
const rlnV2Storage = new hre.ethers.Contract(
|
||||
storageAddress,
|
||||
rlnV2StorageAbi,
|
||||
rlnV2Registry.provider
|
||||
);
|
||||
return rlnV2Storage;
|
||||
}
|
||||
|
||||
async function getRlnV1Commitments(rlnV1Storage: Contract) {
|
||||
// iteratively loop from deployedBlockNumber to current block
|
||||
// collect commitments from MemberRegistered events
|
||||
const deployedBlockNumber = await rlnV1Storage.deployedBlockNumber();
|
||||
const currentBlockNumber = await rlnV1Storage.provider.getBlockNumber();
|
||||
if (!currentBlockNumber) {
|
||||
throw new Error("Could not get current block number");
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Fetching commitments from block ${deployedBlockNumber} to ${currentBlockNumber}`
|
||||
);
|
||||
|
||||
// chunk in batches of 10_000
|
||||
const batchSize = 10_000;
|
||||
// fetch commitments by listening to events on rln-v1
|
||||
const commitments: BigNumber[] = [];
|
||||
for (let i = deployedBlockNumber; i < currentBlockNumber; i += batchSize) {
|
||||
const normalizedBatch = Math.min(currentBlockNumber, i + batchSize);
|
||||
console.log(`Fetching commitments from block ${i} to ${normalizedBatch}`);
|
||||
const events = await rlnV1Storage.queryFilter(
|
||||
"MemberRegistered",
|
||||
i,
|
||||
normalizedBatch
|
||||
);
|
||||
for (const event of events) {
|
||||
commitments.push(event.args?.idCommitment);
|
||||
}
|
||||
}
|
||||
|
||||
return commitments;
|
||||
}
|
||||
|
||||
async function registerRlnV2Commitments(
|
||||
rlnV2Registry: Contract,
|
||||
commitments: BigNumber[]
|
||||
) {
|
||||
// register commitments on rln-v2, with a default limit of 1, in batches of 20
|
||||
const limit = 1;
|
||||
const batch = 10;
|
||||
const total = commitments.length;
|
||||
for (let i = 0; i < total; i += batch) {
|
||||
const normalizedBatch = Math.min(total, i + batch);
|
||||
const commitmentsBatch = commitments.slice(i, normalizedBatch);
|
||||
const limits = Array(commitmentsBatch.length).fill(limit);
|
||||
console.log(
|
||||
`Registering commitments ${i} to ${normalizedBatch} of ${total}`
|
||||
);
|
||||
const tx = await rlnV2Registry.register(commitmentsBatch, limits);
|
||||
await tx.wait();
|
||||
}
|
||||
}
|
||||
|
||||
// this script is used to migrate from rln-v1 to rln-v2
|
||||
// rln-v1 commitments are poseidon([identitySecret]),
|
||||
// rln-v2 commitments are poseidon([rlnV1Commitment, userMessageLimit])
|
||||
// we set a default userMessageLimit to 1 for all migrating users,
|
||||
// to preserve the same message rate as in rln-v1
|
||||
async function main() {
|
||||
const [deployer] = await hre.ethers.getSigners();
|
||||
|
||||
const rlnV1Registry = await getRlnV1Registry(deployer.provider!);
|
||||
const rlnV2Registry = await getRlnV2Registry(deployer);
|
||||
const rlnV1Storage = await getRlnV1Storage(rlnV1Registry);
|
||||
const rlnV2Storage = await getRlnV2Storage(rlnV2Registry);
|
||||
|
||||
console.log(
|
||||
`Migrating from ${rlnV1Registry.address} registry to ${rlnV2Registry.address} registry`
|
||||
);
|
||||
console.log(
|
||||
`Migrating from ${rlnV1Storage.address} storage to ${rlnV2Storage.address} storage`
|
||||
);
|
||||
|
||||
const commitments = await getRlnV1Commitments(rlnV1Storage);
|
||||
|
||||
// register commitments on rln-v2, with a default limit of 1, in batches of 20
|
||||
await registerRlnV2Commitments(rlnV2Registry, commitments);
|
||||
console.log(`Migrated ${commitments.length} commitments`);
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user