Skip to content

Commit

Permalink
Merge pull request #8 from dusk-network/genesis-event-scripts
Browse files Browse the repository at this point in the history
Genesis event scripts
  • Loading branch information
HDauven authored Dec 31, 2024
2 parents dcd556e + 9fe5f07 commit 43b3d41
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ node_modules

# Hardhat Ignition default folder for deployments against a local node
ignition/deployments/chain-31337

# Ignore the generated genesis.toml
genesis.toml
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DUSK Migration Contract

This project contains the smart contract and related scripts for migrating DUSK tokens from ERC20/BEP20 to native DUSK. The project is built using Hardhat.
This project contains the smart contract and related scripts for migrating DUSK tokens from ERC20/BEP20 to native DUSK. It also contains the Dusk Mainnet Onramp contract. The project is built using Hardhat.

**Migration Flow**:
1. User invokes `migrate()` with ERC20/BEP20 DUSK tokens and their Dusk mainnet Moonlight key.
Expand All @@ -10,8 +10,8 @@ This project contains the smart contract and related scripts for migrating DUSK
## Overview

The DUSK migration contract is designed to lock DUSK into the contract, and provide a receiving address on the DUSK side. It includes:
- **Smart Contracts**: A [Solidity migration contract](./contracts/DUSKMigration.sol) and an [ERC20 mock based on ERC20 DUSK](./contracts/ERC20Mock.sol) for testing.
- **Scripts**: Scripts for compiling contracts, extracting the ABI, listening to the migrate events and collecting past events.
- **Smart Contracts**: A [Solidity migration contract](./contracts/DUSKMigration.sol), an [ERC20 mock based on ERC20 DUSK](./contracts/ERC20Mock.sol) for testing and the [Dusk Mainnet Onramp contract](./contracts/DuskMainnetOnramp.sol).
- **Scripts**: Scripts for compiling contracts, extracting the ABI, listening to the migrate events, collecting past events and gathering genesis deposit/stake events.
- **Tests**: Integration tests that test how the migrate function behaves.

## Clone repo
Expand Down Expand Up @@ -62,6 +62,15 @@ npm run events:past

This will dump an `.abi.json` file in the `contract` folder.

### Get Genesis Onramp events

To get genesis deposit and stake events, and convert it to a `genesis.toml`, set up a `.env` file based on the `example.env` file and run:
```shell
npm run events:genesis
```

This will create a `genesis.toml` file in the root folder.

### Run Tests

To run the tests:
Expand Down
8 changes: 8 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
PROVIDER_URL="https://sepolia.infura.io/v3/"
DUSK_MIGRATION_CONTRACT_ADDRESS="0x"

ETH_MAINNET_PROVIDER_URL="https://mainnet.infura.io/v3/"
ETH_ONRAMP_CONTRACT_ADDRESS="0x8787BbE53920B33411F7C9A91Ac321AF1ea1aa2d"
ETH_ONRAMP_DEPLOY_BLOCK="21445561"

BSC_MAINNET_PROVIDER_URL="https://bsc-mainnet.infura.io/v3/"
BSC_ONRAMP_CONTRACT_ADDRESS="0x3886ab688feBfF60cE21E99251035F8E29Abca31"
BSC_ONRAMP_DEPLOY_BLOCK="45046348"
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"test:gas": "UPDATE_SNAPSHOT=1 yarn test --grep gas",
"clean": "rm -rf ./artifacts ./cache ./typechain",
"events:listen": "node ./scripts/listenEvents.js",
"events:past": "node ./scripts/pastEvents.js"
"events:past": "node ./scripts/pastEvents.js",
"events:genesis": "node ./scripts/genesisEvents.js"
},
"author": "",
"license": "ISC",
Expand All @@ -21,4 +22,4 @@
"dependencies": {
"@dotenvx/dotenvx": "^1.14.0"
}
}
}
148 changes: 148 additions & 0 deletions scripts/genesisEvents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
require('@dotenvx/dotenvx').config();
const { ethers } = require("ethers");
const fs = require("fs");

// Chain metadata to combine both calls to Ethereum and BSC
const chains = [
{
name: "Ethereum",
rpcUrl: process.env.ETH_MAINNET_PROVIDER_URL,
contractAddress: process.env.ETH_ONRAMP_CONTRACT_ADDRESS,
startBlock: process.env.ETH_ONRAMP_DEPLOY_BLOCK
},
{
name: "Binance Smart Chain",
rpcUrl: process.env.BSC_MAINNET_PROVIDER_URL,
contractAddress: process.env.BSC_ONRAMP_CONTRACT_ADDRESS,
startBlock: process.env.BSC_ONRAMP_DEPLOY_BLOCK
},
];

// Genesis event ABIs
const contractABI = [
"event GenesisDeposit(address indexed from, uint256 amount, string targetAddress)",
"event GenesisStake(address indexed from, uint256 amount, string targetAddress)"
];

// Function to merge entries based on events.
// For example, to prevent two stake entries for the same key
function mergeEntries(entries, valueKey) {
const merged = {};

entries.forEach(({ address, [valueKey]: value }) => {
const numericValue = BigInt(value.replace(/_/g, ""));
if (!merged[address]) {
merged[address] = numericValue;
} else {
merged[address] += numericValue;
}
});

// Convert back to the required format
return Object.entries(merged).map(([address, value]) => ({
address,
[valueKey]: value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "_"),
}));
}

// Function to fetch events for a given chain configuration of the Onramp contract
async function fetchEvents(chain) {
const provider = new ethers.JsonRpcProvider(chain.rpcUrl);
const contract = new ethers.Contract(chain.contractAddress, contractABI, provider);

const fromBlock = chain.startBlock;
const toBlock = "latest";

let stakeEntries = [];
let moonlightEntries = [];

try {
// Fetch GenesisDeposit events
const depositFilter = contract.filters.GenesisDeposit();
const depositEvents = await contract.queryFilter(depositFilter, BigInt(fromBlock), toBlock);

// Fetch GenesisStake events
const stakeFilter = contract.filters.GenesisStake();
const stakeEvents = await contract.queryFilter(stakeFilter, BigInt(fromBlock), toBlock);

// Process GenesisDeposit events
depositEvents.forEach((event) => {
moonlightEntries.push({
address: event.args.targetAddress,
balance: parseInt(event.args.amount.toString(), 10).toLocaleString("en-US").replace(/,/g, "_"),
});
});

// Process GenesisStake events
stakeEvents.forEach((event) => {
stakeEntries.push({
address: event.args.targetAddress,
amount: parseInt(event.args.amount.toString(), 10).toLocaleString("en-US").replace(/,/g, "_"),
});
});

} catch (error) {
console.error(`Error fetching events on ${chain.name}:`, error);
}

// Merge duplicate entries on a per event basis
const mergedStakeEntries = mergeEntries(stakeEntries, 'amount');
const mergedMoonlightEntries = mergeEntries(moonlightEntries, 'balance');

return { stakeEntries: mergedStakeEntries, moonlightEntries: mergedMoonlightEntries };
}

// Custom TOML writer to handle our number formatting
function writeTOML(data) {
let tomlContent = "";

if (data.stake) {
data.stake.forEach((entry, index) => {
tomlContent += "[[stake]]\n";
tomlContent += `address = '${entry.address}'\n`;
tomlContent += `amount = ${entry.amount}\n\n`;
});
}

if (data.moonlight_account) {
data.moonlight_account.forEach((entry) => {
tomlContent += "[[moonlight_account]]\n";
tomlContent += `address = '${entry.address}'\n`;
tomlContent += `balance = ${entry.balance}\n\n`;
});
}

return tomlContent.trim();
}

async function main() {
let allStakeEntries = [];
let allMoonlightEntries = [];

// Collect all GenesisDeposit and GenesisStake events for each chain config
for (const chain of chains) {
console.log(`Fetching events on ${chain.name}...`);
const { stakeEntries, moonlightEntries } = await fetchEvents(chain);
allStakeEntries = allStakeEntries.concat(stakeEntries);
allMoonlightEntries = allMoonlightEntries.concat(moonlightEntries);
}

// Combine entries across chains to handle duplicate event entries globally
allStakeEntries = mergeEntries(allStakeEntries, 'amount');
allMoonlightEntries = mergeEntries(allMoonlightEntries, 'balance');

// Create genesis data structure
const genesisData = {
stake: allStakeEntries,
moonlight_account: allMoonlightEntries,
};

// Generate TOML content
const tomlContent = writeTOML(genesisData);

// Write TOML content to a file
fs.writeFileSync("genesis.toml", tomlContent);
console.log("Generated genesis.toml file.");
}

main();

0 comments on commit 43b3d41

Please sign in to comment.