Skip to content

Commit

Permalink
Merge pull request #902 from moonbeam-foundation/themacexpert/hardhat
Browse files Browse the repository at this point in the history
Update Hardhat Tutorials to Use Hardhat Ignition Modules
  • Loading branch information
eshaben committed Apr 23, 2024
2 parents 1c85a64 + d4c8265 commit d379bf5
Show file tree
Hide file tree
Showing 18 changed files with 510 additions and 263 deletions.
@@ -0,0 +1,13 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span>npx hardhat compile</span>
<span data-ty>Compiled 1 Solidity files successfully (evm target: paris).</span>
<span data-ty="input"><span class="file-path"></span>ls -l</span>
<span data-ty>artifacts</span>
<span data-ty>cache</span>
<span data-ty>contracts</span>
<span data-ty>hardhat.config.js</span>
<span data-ty>node_modules</span>
<span data-ty>package.json</span>
<span data-ty>package-lock.json</span>
<span data-ty="input"><span class="file-path"></span></span>
</div>
@@ -0,0 +1,18 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span> npx hardhat ignition deploy ./ignition/modules/Box.js --network moonbase</span>
<br>
<span data-ty>✅ Confirm deploy to network moonbase (1287)? … yes</span>
<span data-ty>Hardhat Ignition 🚀</span>
<br>
<span data-ty>Deploying [ BoxModule ]</span>
<br>
<span data-ty>Batch #1</span>
<span data-ty>Executed BoxModule#Box</span>
<br>
<span data-ty>[ BoxModule ] successfully deployed 🚀</span>
<br>
<span data-ty>Deployed Addresses</span>
<br>
<span data-ty>BoxModule#Box - 0xfBD78CE8C9E1169851119754C4Ea2f70AB159289</span>
<span data-ty="input"><span class="file-path"></span></span>
</div>
@@ -0,0 +1,20 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span>npx hardhat init</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;888</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;888</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;888</span>
<span data-ty>8888888888&nbsp;&nbsp;8888b.&nbsp;&nbsp;888d888&nbsp;.d88888&nbsp;88888b.&nbsp;&nbsp;&nbsp;8888b.&nbsp;&nbsp;888888</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"88b&nbsp;888P"&nbsp;&nbsp;d88"&nbsp;888&nbsp;888&nbsp;"88b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"88b&nbsp;888</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;.d888888&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;888&nbsp;.d888888&nbsp;888</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;Y88b&nbsp;888&nbsp;888&nbsp;&nbsp;888&nbsp;888&nbsp;&nbsp;888&nbsp;Y88b.</span>
<span data-ty>888&nbsp;&nbsp;&nbsp;&nbsp;888&nbsp;"Y888888&nbsp;888&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Y88888&nbsp;888&nbsp;&nbsp;888&nbsp;"Y888888&nbsp;&nbsp;"Y888</span>
<br>
<span data-ty>👷 Welcome to Hardhat v2.22.2 👷‍</span>
<br>
<span data-ty="input" data-ty-prompt="?">&nbsp;What do you want to do? …</span>
<span data-ty>&nbsp;&nbsp;Create a JavaScript project </span>
<span data-ty>&nbsp;&nbsp;Create a TypeScript project </span>
<span data-ty>&nbsp;&nbsp;Create a TypeScript project (with Viem) </span>
<span data-ty="input" data-ty-prompt="❯ Create an empty hardhat.config.js"></span>
<span data-ty>&nbsp;&nbsp;Quit </span>
</div>
@@ -0,0 +1,36 @@
<div id="termynal" data-termynal>
<span data-ty="input">npx hardhat console --network moonbase</span>
<br>
<span data-ty>Welcome to Node.js v20.9.0.</span>
<span data-ty>Type ".help" for more information.</span>
<span data-ty="input" data-ty-prompt=">"> const Box = await ethers.getContractFactory('Box');</span>
<span data-ty>undefined</span>
<br>
<span data-ty="input" data-ty-prompt=">"> const box = await Box.attach('0xfBD78CE8C9E1169851119754C4Ea2f70AB159289');</span>
<span data-ty>undefined</span>
<br>
<span data-ty="input" data-ty-prompt=">"> await box.store(5);</span>
<span data-ty>ContractTransactionResponse {<br>
provider: HardhatEthersProvider { ... },<br>
blockNumber: null,<br>
blockHash: null,<br>
index: undefined,<br>
hash: '0x1c49a64a601fc5dd184f0a368a91130cb49203ec0f533c6fcf20445c68e20264',<br>
type: 2,<br>
to: '0xa84caB60db6541573a091e5C622fB79e175E17be',<br>
from: '0x3B939FeaD1557C741Ff06492FD0127bd287A421e',<br>
nonce: 87,<br>
gasLimit: 45881n,<br>
gasPrice: 1107421875n,<br>
maxPriorityFeePerGas: 1n,<br>
maxFeePerGas: 1107421875n,<br>
data: '0x6057361d0000000000000000000000000000000000000000000000000000000000000005',<br>
value: 0n,<br>
chainId: 5678n,<br>
signature: Signature { r: "0x9233b9cc4ae6879b7e08b9f1a4bfb175c8216eee0099966eca4a305c7f369ecc", s: "0x7663688633006b5a449d02cb08311569fadf2f9696bd7fe65417860a3b5fc57d", yParity: 0, networkV: null },<br>
accessList: [],<br>
blobVersionedHashes: null<br>}</span>
<span data-ty="input" data-ty-prompt=">"> await box.retrieve();</span>
<span data-ty>5n</span>
<br>
</div>
@@ -0,0 +1,26 @@
<div id="termynal" data-termynal>
<span data-ty>Private Key: Oxdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97</span>
<span data-ty>Account #9: Oxa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000 ETH)</span>
<span data-ty>Private Key: 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6</span>
<span data-ty>Account #10: OxBcd4042DE499D14e55001CcbB24a551F3b954096 (10000 ETH)</span>
<span data-ty>Private Key: Oxf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897</span>
<span data-ty>Account #11: 0x71bE63f3384f5fb98995898A86B02Fb2426c5788 (10000 ETH)</span>
<span data-ty>Private Key: 0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82</span>
<span data-ty>Account #12: OxFABBOac9d68B0B445fB7357272F202C5651694a (10000 ETH)</span>
<span data-ty>Private Key: Oxa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1</span>
<span data-ty>Account #13: 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec (10000 ETH)</span>
<span data-ty>Private Key: 0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd</span>
<span data-ty>Account #14: OxdF3e18d64BC6A983f673Ab319CCaE4f1a5707097 (10000 ETH)</span>
<span data-ty>Private Key: Oxc526ee95bf44d8fc405a158bb884d9d1238d990612e9f33d006bb0789009aaa</span>
<span data-ty>Account #15: Oxcd3B766CCDd6AE721141F452C550Ca635964ce71 (10000 ETH)</span>
<span data-ty>Private Key: 0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61</span>
<span data-ty>Account #16: 0×2546BcD3c84621e976D8185a91A922aE77ECEc30 (10000 ETH)</span>
<span data-ty>Private Key: Oxea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0</span>
<span data-ty>Account #17: OxbDA5747bFD65F08deb54cb465eB87D40e51B197E (10000 ETH)</span>
<span data-ty>Private Key: 0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b06166765a93e037fd</span>
<span data-ty>Account #18: OxdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH)</span>
<span data-ty>Private Key: Oxde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0</span>
<span data-ty>Account #19: 0×8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)</span>
<span data-ty>Private Key: Oxdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e</span>
<span data-ty>WARNING: These accounts, and their private keys, are publicly known.<br>Any funds sent to them on Mainnet or any other live network WILL BE LOST.</span>
</div>
@@ -0,0 +1,6 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span>npx hardhat run --network moonbase scripts/set-value.js</span>
<br>
<span data-ty>The new value is: 2</span>
<span data-ty="input"><span class="file-path"></span></span>
</div>
Expand Up @@ -4,11 +4,10 @@ pragma solidity >=0.8.0;

import "./StakingInterface.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Address.sol";


contract DelegationDAO is AccessControl {
using SafeMath for uint256;

// Role definition for contract members
bytes32 public constant MEMBER = keccak256("MEMBER");
Expand Down Expand Up @@ -56,20 +55,25 @@ contract DelegationDAO is AccessControl {

// Initialize a new DelegationDao dedicated to delegating to the given collator target.
constructor(address _target, address admin) {
// Directly grant roles
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(MEMBER, admin);

//Sets the collator that this DAO nominating
target = _target;

// Initializes Moonbeam's parachain staking precompile
staking = ParachainStaking(stakingPrecompileAddress);

//Initializes Roles
_setupRole(DEFAULT_ADMIN_ROLE, admin);
_setupRole(MEMBER, admin);

//Initialize the DAO state
currentState = daoState.COLLECTING;
}

// Simple getter to return the target collator of the DAO
function getTarget() public view returns (address) {
return target;
}

// Grant a user the role of admin
function grant_admin(
address newAdmin
Expand Down Expand Up @@ -99,13 +103,13 @@ contract DelegationDAO is AccessControl {
if (!staking.isDelegator(address(this))) {
revert("The DAO is in an inconsistent state.");
}
memberStakes[msg.sender] = memberStakes[msg.sender].add(msg.value);
totalStake = totalStake.add(msg.value);
memberStakes[msg.sender] = memberStakes[msg.sender] + msg.value;
totalStake = totalStake + msg.value;
emit deposit(msg.sender, msg.value);
staking.delegatorBondMore(target, msg.value);
} else if (currentState == daoState.COLLECTING) {
memberStakes[msg.sender] = memberStakes[msg.sender].add(msg.value);
totalStake = totalStake.add(msg.value);
memberStakes[msg.sender] = memberStakes[msg.sender] + msg.value;
totalStake = totalStake + msg.value;
emit deposit(msg.sender, msg.value);
if (totalStake < minDelegationStk) {
return;
Expand Down Expand Up @@ -144,16 +148,13 @@ contract DelegationDAO is AccessControl {
}
require(totalStake != 0, "Cannot divide by zero.");
//Calculate the withdrawal amount including staking rewards
uint amount = address(this)
.balance
.mul(memberStakes[msg.sender])
.div(totalStake);
uint amount = address(this).balance * memberStakes[msg.sender] / totalStake;
require(
check_free_balance() >= amount,
"Not enough free balance for withdrawal."
);
Address.sendValue(account, amount);
totalStake = totalStake.sub(memberStakes[msg.sender]);
totalStake = totalStake - (memberStakes[msg.sender]);
memberStakes[msg.sender] = 0;
emit withdrawal(msg.sender, account, amount);
}
Expand Down Expand Up @@ -210,4 +211,9 @@ contract DelegationDAO is AccessControl {
function reset_dao() public onlyRole(DEFAULT_ADMIN_ROLE) {
currentState = daoState.COLLECTING;
}

// Override _setupRole to use grantRole as _setupRole does not exist in AccessControl anymore
function _setupRole(bytes32 role, address account) internal virtual {
grantRole(role, account);
}
}
Expand Up @@ -4,76 +4,90 @@ const { ethers } = require('hardhat');
const { expect } = require('chai');

// Indicate the collator the DAO wants to delegate to
const targetCollator = '0x4c5A56ed5A4FF7B09aA86560AfD7d383F4831Cce';
// For Moonbase Local Node, use: 0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac
// For Moonbase Alpha, use: 0x12E7BCCA9b1B15f33585b5fc898B967149BDb9a5
const targetCollator = 'INSERT_COLLATOR_ADDRESS';

// The describe function receives the name of a section of your test suite, and a
// callback. The callback must define the tests of that section. This callback
// can't be an async function
describe('Dao contract', function () {
async function deployDao() {
// Get the contract factory and signers here
const [deployer, member1] = await ethers.getSigners();
const delegationDao = await ethers.getContractFactory('DelegationDAO');
let wallet1, wallet2;

// Deploy the staking DAO and wait for the deployment transaction to be confirmed
const deployedDao = await delegationDao.deploy(
targetCollator,
deployer.address
before(async function () {
// Get signers we defined in Hardhat config
const signers = await ethers.getSigners();
wallet1 = signers[0];
wallet2 = signers[1];
});

async function deployDao() {
const delegationDaoFactory = await ethers.getContractFactory(
'DelegationDAO',
wallet2
);
await deployedDao.waitForDeployment();

// Return the deployed DAO and the first member of the DAO to allow the tests to
// access and interact with them
return { deployedDao, member1 };
// Deploy the staking DAO and wait for the deployment transaction to be confirmed
try {
const deployedDao = await delegationDaoFactory.deploy(
targetCollator,
wallet2.address
);
await deployedDao.waitForDeployment(); // Correct way to wait for the transaction to be mined
return { deployedDao };
} catch (error) {
console.error('Failed to deploy contract:', error);
return null; // Return null to indicate failure
}
}

// You can nest calls to create subsections
describe('Deployment', function () {
// Mocha's it function is used to define each of your tests.
// It receives the test name, and a callback function
//
// If the callback function is async, Mocha will await it
// Test case to check that the correct target collator is stored
it('should store the correct target collator in the DAO', async function () {
// Set up our test environment by calling deployDao
const { deployedDao } = await deployDao();

// The expect function receives a value and wraps it in an assertion object.
// This test will pass if the DAO stored the correct target collator
expect(await deployedDao.target()).to.equal(targetCollator);
const deployment = await deployDao();
if (!deployment || !deployment.deployedDao) {
throw new Error('Deployment failed; DAO contract was not deployed.');
}
const { deployedDao } = deployment;
expect(await deployedDao.getTarget()).to.equal(
'0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac'
);
});

// The following test cases should be added here
// Test case to check that the DAO has 0 funds at inception
it('should initially have 0 funds in the DAO', async function () {
const { deployedDao } = await deployDao();

// This test will pass if the DAO has no funds as expected before any contributions
expect(await deployedDao.totalStake()).to.equal(0);
});

// Test case to check that non-admins cannot grant membership
it('should not allow non-admins to grant membership', async function () {
const { deployedDao, member1 } = await deployDao();
const { deployedDao } = await deployDao();
// Connect the non-admin wallet to the deployed contract
const deployedDaoConnected = deployedDao.connect(wallet1);
const tx = deployedDaoConnected.grant_member(
'0x0000000000000000000000000000000000000000'
);

// We use connect to call grant_member from member1's account instead of admin.
// This test will succeed if the function call reverts and fails if the call succeeds
await expect(
deployedDao
.connect(member1)
.grant_member('0x0000000000000000000000000000000000000000')
).to.be.reverted;
// Check that the transaction reverts, not specifying any particular reason
await expect(tx).to.be.reverted;
});

// Test case to check that members can access member only functions
it('should only allow members to access member-only functions', async function () {
const { deployedDao, member1 } = await deployDao();
const { deployedDao } = await deployDao();

// Add a new member to the DAO
const transaction = await deployedDao.grant_member(member1.address);
await transaction.wait();
// Connect the wallet1 to the deployed contract and grant membership
const deployedDaoConnected = deployedDao.connect(wallet2);
const grantTx = await deployedDaoConnected.grant_member(wallet1.address);
await grantTx.wait();

// This test will succeed if the DAO member can call the member-only function.
// We use connect here to call the function from the account of the new member
expect(await deployedDao.connect(member1).check_free_balance()).to.equal(
0
);
// Check the free balance using the member's credentials
const checkTx = deployedDaoConnected.check_free_balance();

// Since check_free_balance() does not modify state, we expect it not to be reverted and check the balance
await expect(checkTx).to.not.be.reverted;
expect(await checkTx).to.equal(0);
});
});
});
@@ -0,0 +1,5 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span>npx hardhat compile</span>
<span data-ty>Compiled 8 Solidity files successfully (evm target: paris).</span>
<span data-ty="input"><span class="file-path"></span></span>
</div>
@@ -0,0 +1,18 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span> npx hardhat ignition deploy ./ignition/modules/DelegationDao.js --network moonbase --deployment-id INSERT_YOUR_NAME</span>
<br>
<span data-ty>✅ Confirm deploy to network moonbase (1287)? … yes</span>
<span data-ty>Hardhat Ignition 🚀</span>
<br>
<span data-ty>Deploying [ DelegationDAOModule ]</span>
<br>
<span data-ty>Batch #1</span>
<span data-ty>Executed DelegationDAOModule#DelegationDAO</span>
<br>
<span data-ty>[ DelegationDAOModule ] successfully deployed 🚀</span>
<br>
<span data-ty>Deployed Addresses</span>
<br>
<span data-ty>DelegationDAOModule#DelegationDAO - 0x69c555fE1A8D0916E6dab0629bd7530D4d2Be4D1</span>
<span data-ty="input"><span class="file-path"></span></span>
</div>
@@ -0,0 +1,18 @@
<div id="termynal" data-termynal>
<span data-ty="input"><span class="file-path"></span> npx hardhat ignition deploy ./ignition/modules/DelegationDao.js --network moonbeam --deployment-id INSERT_YOUR_NAME</span>
<br>
<span data-ty>✅ Confirm deploy to network moonbeam (1284)? … yes</span>
<span data-ty>Hardhat Ignition 🚀</span>
<br>
<span data-ty>Deploying [ DelegationDAOModule ]</span>
<br>
<span data-ty>Batch #1</span>
<span data-ty>Executed DelegationDAOModule#DelegationDAO</span>
<br>
<span data-ty>[ DelegationDAOModule ] successfully deployed 🚀</span>
<br>
<span data-ty>Deployed Addresses</span>
<br>
<span data-ty>DelegationDAOModule#DelegationDAO - 0x6D895A55F5ba31e582bCEe71cae394266F240e9b</span>
<span data-ty="input"><span class="file-path"></span></span>
</div>

0 comments on commit d379bf5

Please sign in to comment.