diff --git a/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/compile.md b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/compile.md new file mode 100644 index 000000000..b6fa88cc5 --- /dev/null +++ b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/compile.md @@ -0,0 +1,13 @@ +
+ npx hardhat compile + Compiled 1 Solidity files successfully (evm target: paris). + ls -l + artifacts + cache + contracts + hardhat.config.js + node_modules + package.json + package-lock.json + +
diff --git a/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/deploy-moonbase.md b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/deploy-moonbase.md new file mode 100644 index 000000000..c5678f6c0 --- /dev/null +++ b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/deploy-moonbase.md @@ -0,0 +1,18 @@ +
+ npx hardhat ignition deploy ./ignition/modules/Box.js --network moonbase +
+ βœ… Confirm deploy to network moonbase (1287)? … yes + Hardhat Ignition πŸš€ +
+ Deploying [ BoxModule ] +
+ Batch #1 + Executed BoxModule#Box +
+ [ BoxModule ] successfully deployed πŸš€ +
+ Deployed Addresses +
+ BoxModule#Box - 0xfBD78CE8C9E1169851119754C4Ea2f70AB159289 + +
diff --git a/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/hardhat-create.md b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/hardhat-create.md new file mode 100644 index 000000000..6d319ce09 --- /dev/null +++ b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/hardhat-create.md @@ -0,0 +1,20 @@ +
+ npx hardhat init + 888    888                      888 888               888 + 888    888                      888 888               888 + 888    888                      888 888               888 + 8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888 + 888    888     "88b 888P"  d88" 888 888 "88b     "88b 888 + 888    888 .d888888 888    888  888 888  888 .d888888 888 + 888    888 888  888 888    Y88b 888 888  888 888  888 Y88b. + 888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888 +
+ πŸ‘· Welcome to Hardhat v2.22.2 πŸ‘·β€ +
+  What do you want to do? … +   Create a JavaScript project +   Create a TypeScript project +   Create a TypeScript project (with Viem) + +   Quit +
\ No newline at end of file diff --git a/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/interact.md b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/interact.md new file mode 100644 index 000000000..5236e3d05 --- /dev/null +++ b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/interact.md @@ -0,0 +1,36 @@ +
+ npx hardhat console --network moonbase +
+ Welcome to Node.js v20.9.0. + Type ".help" for more information. + const Box = await ethers.getContractFactory('Box'); + undefined +
+ const box = await Box.attach('0xfBD78CE8C9E1169851119754C4Ea2f70AB159289'); + undefined +
+ await box.store(5); + ContractTransactionResponse {
+ provider: HardhatEthersProvider { ... },
+ blockNumber: null,
+ blockHash: null,
+ index: undefined,
+ hash: '0x1c49a64a601fc5dd184f0a368a91130cb49203ec0f533c6fcf20445c68e20264',
+ type: 2,
+ to: '0xa84caB60db6541573a091e5C622fB79e175E17be',
+ from: '0x3B939FeaD1557C741Ff06492FD0127bd287A421e',
+ nonce: 87,
+ gasLimit: 45881n,
+ gasPrice: 1107421875n,
+ maxPriorityFeePerGas: 1n,
+ maxFeePerGas: 1107421875n,
+ data: '0x6057361d0000000000000000000000000000000000000000000000000000000000000005',
+ value: 0n,
+ chainId: 5678n,
+ signature: Signature { r: "0x9233b9cc4ae6879b7e08b9f1a4bfb175c8216eee0099966eca4a305c7f369ecc", s: "0x7663688633006b5a449d02cb08311569fadf2f9696bd7fe65417860a3b5fc57d", yParity: 0, networkV: null },
+ accessList: [],
+ blobVersionedHashes: null
}
+ await box.retrieve(); + 5n +
+
diff --git a/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/private-keys.md b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/private-keys.md new file mode 100644 index 000000000..15127b6e6 --- /dev/null +++ b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/private-keys.md @@ -0,0 +1,26 @@ +
+ Private Key: Oxdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 + Account #9: Oxa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000 ETH) + Private Key: 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 + Account #10: OxBcd4042DE499D14e55001CcbB24a551F3b954096 (10000 ETH) + Private Key: Oxf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897 + Account #11: 0x71bE63f3384f5fb98995898A86B02Fb2426c5788 (10000 ETH) + Private Key: 0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82 + Account #12: OxFABBOac9d68B0B445fB7357272F202C5651694a (10000 ETH) + Private Key: Oxa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1 + Account #13: 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec (10000 ETH) + Private Key: 0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd + Account #14: OxdF3e18d64BC6A983f673Ab319CCaE4f1a5707097 (10000 ETH) + Private Key: Oxc526ee95bf44d8fc405a158bb884d9d1238d990612e9f33d006bb0789009aaa + Account #15: Oxcd3B766CCDd6AE721141F452C550Ca635964ce71 (10000 ETH) + Private Key: 0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61 + Account #16: 0Γ—2546BcD3c84621e976D8185a91A922aE77ECEc30 (10000 ETH) + Private Key: Oxea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0 + Account #17: OxbDA5747bFD65F08deb54cb465eB87D40e51B197E (10000 ETH) + Private Key: 0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b06166765a93e037fd + Account #18: OxdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH) + Private Key: Oxde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0 + Account #19: 0Γ—8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH) + Private Key: Oxdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e + WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.
+
diff --git a/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/run.md b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/run.md new file mode 100644 index 000000000..492d0d3d3 --- /dev/null +++ b/.snippets/code/builders/build/eth-api/dev-env/hardhat/terminal/run.md @@ -0,0 +1,6 @@ +
+ npx hardhat run --network moonbase scripts/set-value.js +
+ The new value is: 2 + +
diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/DelegationDAO.sol b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/DelegationDAO.sol index 35a27a8a5..3064634f9 100644 --- a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/DelegationDAO.sol +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/DelegationDAO.sol @@ -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"); @@ -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 @@ -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; @@ -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); } @@ -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); + } } diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/dao-js-test-file.js b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/dao-js-test-file.js index 391ada329..d285fa0fc 100644 --- a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/dao-js-test-file.js +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/dao-js-test-file.js @@ -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); }); }); }); diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/compile.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/compile.md new file mode 100644 index 000000000..1774656c6 --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/compile.md @@ -0,0 +1,5 @@ +
+ npx hardhat compile + Compiled 8 Solidity files successfully (evm target: paris). + +
diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbase.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbase.md new file mode 100644 index 000000000..0f392b3a6 --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbase.md @@ -0,0 +1,18 @@ +
+ npx hardhat ignition deploy ./ignition/modules/DelegationDao.js --network moonbase --deployment-id INSERT_YOUR_NAME +
+ βœ… Confirm deploy to network moonbase (1287)? … yes + Hardhat Ignition πŸš€ +
+ Deploying [ DelegationDAOModule ] +
+ Batch #1 + Executed DelegationDAOModule#DelegationDAO +
+ [ DelegationDAOModule ] successfully deployed πŸš€ +
+ Deployed Addresses +
+ DelegationDAOModule#DelegationDAO - 0x69c555fE1A8D0916E6dab0629bd7530D4d2Be4D1 + +
diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbeam.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbeam.md new file mode 100644 index 000000000..e658b4ece --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbeam.md @@ -0,0 +1,18 @@ +
+ npx hardhat ignition deploy ./ignition/modules/DelegationDao.js --network moonbeam --deployment-id INSERT_YOUR_NAME +
+ βœ… Confirm deploy to network moonbeam (1284)? … yes + Hardhat Ignition πŸš€ +
+ Deploying [ DelegationDAOModule ] +
+ Batch #1 + Executed DelegationDAOModule#DelegationDAO +
+ [ DelegationDAOModule ] successfully deployed πŸš€ +
+ Deployed Addresses +
+ DelegationDAOModule#DelegationDAO - 0x6D895A55F5ba31e582bCEe71cae394266F240e9b + +
diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/hardhat-create.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/hardhat-create.md new file mode 100644 index 000000000..6d319ce09 --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/hardhat-create.md @@ -0,0 +1,20 @@ +
+ npx hardhat init + 888    888                      888 888               888 + 888    888                      888 888               888 + 888    888                      888 888               888 + 8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888 + 888    888     "88b 888P"  d88" 888 888 "88b     "88b 888 + 888    888 .d888888 888    888  888 888  888 .d888888 888 + 888    888 888  888 888    Y88b 888 888  888 888  888 Y88b. + 888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888 +
+ πŸ‘· Welcome to Hardhat v2.22.2 πŸ‘·β€ +
+  What do you want to do? … +   Create a JavaScript project +   Create a TypeScript project +   Create a TypeScript project (with Viem) + +   Quit +
\ No newline at end of file diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/test.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/test.md new file mode 100644 index 000000000..5ffe0c324 --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/test.md @@ -0,0 +1,15 @@ +
+ npx hardhat test --network dev tests/Dao.js +
+
+ Dao contract + Deployment + βœ… The DAO should store the correct target collator (1624ms) + βœ… The DAO should initially have 0 funds in it + βœ… Non-admins should not be able to grant membership (150ms) + βœ… DAO members should be able to access member only functions (132ms) +
+
+ βœ… 4 passing (2s) + +
diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbase.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbase.md new file mode 100644 index 000000000..54775a5d9 --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbase.md @@ -0,0 +1,12 @@ +
+ npx hardhat ignition verify INSERT_YOUR_NAME +
+ Nothing to compile + Successfully submitted source code for contract +contracts/DelegationDAO.sol:DelegationDAO at 0x5D788B98E4A90F9642352B0b32694998e77cF4d7 for verification on the block explorer. Waiting for verification result... +
+ Successfully verified contract DelegationDAO on Etherscan. +
+ https://moonbase.moonscan.io/address/0x5D788B98E4A90F9642352B0b32694998e77cF4d7#code + +
diff --git a/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbeam.md b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbeam.md new file mode 100644 index 000000000..332a94c7f --- /dev/null +++ b/.snippets/code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbeam.md @@ -0,0 +1,12 @@ +
+ npx hardhat ignition verify INSERT_YOUR_NAME +
+ Nothing to compile + Successfully submitted source code for contract +contracts/DelegationDAO.sol:DelegationDAO at 0x6D895A55F5ba31e582bCEe71cae394266F240e9b for verification on the block explorer. Waiting for verification result... +
+ Successfully verified contract DelegationDAO on Etherscan. +
+ https://moonbeam.moonscan.io/address/0x6D895A55F5ba31e582bCEe71cae394266F240e9b#code + +
diff --git a/builders/build/eth-api/dev-env/hardhat.md b/builders/build/eth-api/dev-env/hardhat.md index 5be3dd6a3..644ba9319 100644 --- a/builders/build/eth-api/dev-env/hardhat.md +++ b/builders/build/eth-api/dev-env/hardhat.md @@ -60,7 +60,7 @@ You will need to create a Hardhat project if you don't already have one. You can 5. A menu will appear, which will allow you to create a new project or use a sample project. For this example, you can choose **Create an empty hardhat.config.js**, which will create a Hardhat configuration file for your project -![Hardhat Create Project](/images/builders/build/eth-api/dev-env/hardhat/hardhat-1.webp) +--8<-- 'code/builders/build/eth-api/dev-env/hardhat/terminal/hardhat-create.md' ## Hardhat Configuration File {: #hardhat-configuration-file } @@ -153,14 +153,21 @@ If you are planning on using any plugins with your project, you'll need to insta For this example, you can install the `hardhat-ethers` plugin and import it into the configuration file. This plugin provides a convenient way to use the [Ethers.js](/builders/build/eth-api/libraries/ethersjs/){target=\_blank} library to interact with the network. ```bash -npm install @nomicfoundation/hardhat-ethers ethers@6 +npm install @nomicfoundation/hardhat-ethers ethers ``` -To import it, you'll add the following `require` statement to the top of the configuration file: +Additionally, you'll need to install the `hardhat-ignition-ethers` plugin to enable deployment of smart contracts with Hardhat Ignition. You can install it with the following command: -```js hl_lines="2" +```sh +npm install --save-dev @nomicfoundation/hardhat-ignition-ethers +``` + +To import both plugins, add the following `require` statements to the top of the Hardhat configuration file: + +```js hl_lines="2 3" /** @type import('hardhat/config').HardhatUserConfig */ require('@nomicfoundation/hardhat-ethers'); +require('@nomicfoundation/hardhat-ignition-ethers'); const privateKey = 'INSERT_PRIVATE_KEY'; @@ -232,7 +239,7 @@ To use the `compile` task, all you have to do is run: npx hardhat compile ``` -![Hardhat Contract Compile](/images/builders/build/eth-api/dev-env/hardhat/hardhat-2.webp) +--8<-- 'code/builders/build/eth-api/dev-env/hardhat/terminal/compile.md' After compilation, an `artifacts` directory is created that holds the bytecode and metadata of the contract, which are `.json` files. It’s a good idea to add this directory to a `.gitignore` file. @@ -240,60 +247,55 @@ If you make changes to the contract after you've compiled it, you can compile it ## Deploy the Contract {: #deploying-the-contract } -To deploy the contract, you'll create a script that uses [Ethers.js](/builders/build/eth-api/libraries/ethersjs){target=\_blank} to deploy the contract, and you'll run the script using the `run` task. +To deploy the contract, you'll use Hardhat Ignition, a declarative framework for deploying smart contracts. Hardhat Ignition is designed to make it easy to manage recurring tasks surrounding smart contract deployment and testing. For more information, be sure to check out the [Hardhat Ignition docs](https://hardhat.org/ignition/docs/getting-started#overview){target=\_blank}. -You can create a new directory for the script, and name it `scripts`, and add a new file to it called `deploy.js`: +To set up the proper file structure for your Ignition module, create a folder named `ignition` and a subdirectory called `modules`. Then add a new file to it called `Box.js`. You can take all three of these steps with the following command: ```sh -mkdir scripts && touch scripts/deploy.js +mkdir ignition ignition/modules && touch ignition/modules/Box.js ``` -Next, you need to write your deployment script. Because you'll be running it with Hardhat and you've already imported Ethers into your `hardhat.config.js` file, you don't need to import any libraries directly in the script. If you were to run the script with `node` instead, you would need to import Ethers. - -To get started, take the following steps: +Next, you can write your Hardhat Ignition module. To get started, take the following steps: -1. Create a local instance of the contract with the `getContractFactory` method -2. Use the `deploy` method that exists within this instance to instantiate the smart contract -3. Wait for the deployment by using `waitForDeployment` -4. Once deployed, you can fetch the address of the contract using the contract instance. +1. Import the `buildModule` function from the Hardhat Ignition module +2. Export a module using `buildModule` +3. Use the `getAccount` method to select the deployer account +4. Deploy the `Box` contract +5. Return an object from the module. This makes the `Box` contract accessible for interaction in Hardhat tests and scripts ```js -// scripts/deploy.js -async function main() { - // 1. Get the contract to deploy - const Box = await ethers.getContractFactory('Box'); - console.log('Deploying Box...'); - - // 2. Instantiate a new Box smart contract - const box = await Box.deploy(); - - // 3. Wait for the deployment to resolve - await box.waitForDeployment(); +// 1. Import the `buildModule` function from the Hardhat Ignition module +const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); + +// 2. Export a module using `buildModule` +module.exports = buildModule("BoxModule", (m) => { + + // 3. Use the `getAccount` method to select the deployer account + const deployer = m.getAccount(0); + + // 4. Deploy the `Box` contract + const box = m.contract("Box", [], { + from: deployer, + }); - // 4. Use the contract instance to get the contract address - console.log('Box deployed to:', box.target); -} + // 5. Return an object from the module + return { box }; +}); -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); ``` -To run the script and deploy the `Box.sol` contract, use the following command, which requires you to specify the network name as defined in your `hardhat.config.js` to deploy the contract to: +To run the script and deploy the `Box.sol` contract, use the following command, which requires you to specify the network name as defined in your `hardhat.config.js`. If you don't specify a network, hardhat will deploy the contract to a local hardhat network by default. ```sh -npx hardhat run --network moonbase scripts/deploy.js +npx hardhat ignition deploy ./ignition/modules/Box.js --network moonbase ``` !!! note If you're using another Moonbeam network, make sure that you specify the correct network. The network name needs to match how it's defined in the `hardhat.config.js` file. -After a few seconds, the contract is deployed, and you should see the address in the terminal. +You'll be prompted to confirm the network you wish to deploy to. After a few seconds after you confirm, the contract is deployed, and you'll see the contract address in the terminal. -![Hardhat Contract Deploy](/images/builders/build/eth-api/dev-env/hardhat/hardhat-3.webp) +--8<-- 'code/builders/build/eth-api/dev-env/hardhat/terminal/deploy-moonbase.md' Congratulations, your contract is live! Save the address, as you will use it to interact with this contract instance in the next step. @@ -333,7 +335,7 @@ Next, you can take the following steps, entering one line at a time: The transaction will be signed by your account configured in the `hardhat.config.js` file and broadcasted to the network. The output should look similar to: -![Transaction output](/images/builders/build/eth-api/dev-env/hardhat/hardhat-4.webp) +--8<-- 'code/builders/build/eth-api/dev-env/hardhat/terminal/interact.md' Notice your address labeled `from`, the address of the contract, and the `data` that is being passed. Now, you can retrieve the value by running: @@ -350,7 +352,7 @@ Similarly to the deployment script, you can create a script to interact with you To get started, create a `set-value.js` file in the `scripts` directory: ```sh -touch scripts/set-value.js +mkdir scripts && touch scripts/set-value.js ``` Now paste the following contract into the `set-value.js` file: @@ -388,7 +390,7 @@ npx hardhat run --network moonbase scripts/set-value.js The script should return `2` as the value. -![The terminal output from executing the set-value.js script.](/images/builders/build/eth-api/dev-env/hardhat/hardhat-5-new.webp) +--8<-- 'code/builders/build/eth-api/dev-env/hardhat/terminal/run.md' ## Hardhat Forking {: #hardhat-forking } @@ -574,7 +576,7 @@ If you prefer to configure your Hardhat project, you can update your `hardhat.co When you spin up the Hardhat fork, you'll have 20 development accounts that are pre-funded with 10,000 test tokens. The forked instance is available at `http://127.0.0.1:8545/`. The output in your terminal should resemble the following: -![Forking terminal screen](/images/builders/build/eth-api/dev-env/hardhat/hardhat-5.webp) +--8<-- 'code/builders/build/eth-api/dev-env/hardhat/terminal/private-keys.md' To verify you have forked the network, you can query the latest block number: diff --git a/tutorials/eth-api/hardhat-start-to-end.md b/tutorials/eth-api/hardhat-start-to-end.md index 24fd50e22..7f99ba17a 100644 --- a/tutorials/eth-api/hardhat-start-to-end.md +++ b/tutorials/eth-api/hardhat-start-to-end.md @@ -57,9 +57,9 @@ You will need to create a Hardhat project if you don't already have one. You can !!! note `npx` is used to run executables installed locally in your project. Although Hardhat can be installed globally, it is recommended to install it locally in each project so that you can control the version on a project by project basis. -5. A menu will appear which will allow you to create a new project or use a sample project. For this example, you can choose **Create an empty hardhat.config.js** +5. A menu will appear allowing you to create a new project or use a sample project. For this example, you can choose **Create an empty hardhat.config.js** -![Create an empty Hardhat project.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-1.webp) +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/hardhat-create.md' This will create a Hardhat config file (`hardhat.config.js`) in your project directory. @@ -171,8 +171,9 @@ For the examples in this guide, you'll need to add your private keys for your tw ```js // 1. Import the Hardhat Toolbox plugin require('@nomicfoundation/hardhat-toolbox'); +require('@nomicfoundation/hardhat-ignition-ethers'); -// 2. Create variables for your private keys from your pre-funded Moonbase Alpha +// 2. Create variables for your private keys from your pre-funded Moonbase Alpha // testing accounts and your Moonscan API key const privateKey = 'INSERT_PRIVATE_KEY'; const privateKey2 = 'INSERT_ANOTHER_PRIVATE_KEY'; @@ -186,20 +187,20 @@ module.exports = { moonbase: { url: '{{ networks.moonbase.rpc_url }}', chainId: {{ networks.moonbase.chain_id }}, // {{ networks.moonbase.hex_chain_id }} in hex - accounts: [privateKey, privateKey2] + accounts: [privateKey, privateKey2], }, dev: { url: '{{ networks.development.rpc_url }}', - chainId: 1281, // {{ networks.development.hex_chain_id }} in hex + chainId: {{ networks.development.chain_id }}, // {{ networks.development.hex_chain_id }} in hex accounts: [ '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133', // Alice's PK - '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b' // Bob's PK - ] + '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b', // Bob's PK + ], }, moonbeam: { url: '{{ networks.moonbeam.public_rpc_url }}', // Or insert your own RPC URL here - chainId: 1284, // {{ networks.moonbeam.hex_chain_id }} in hex - accounts: [privateKey, privateKey2] + chainId: {{ networks.moonbeam.chain_id }}, // {{ networks.moonbeam.hex_chain_id }} in hex + accounts: [privateKey, privateKey2], }, }, // 5. Set up your Moonscan API key for contract verification @@ -207,9 +208,9 @@ module.exports = { etherscan: { apiKey: { moonbaseAlpha: moonscanAPIKey, // Moonbase Moonscan API Key - moonbeam: moonscanAPIKey, // Moonbeam Moonscan API Key - } - } + moonbeam: moonscanAPIKey, // Moonbeam Moonscan API Key + }, + }, }; ``` @@ -226,15 +227,15 @@ To compile the contract you can simply run: npx hardhat compile ``` -![Learn how to compile your Solidity contracts with Hardhat.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-2.webp) +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/compile.md' -After compilation, an `artifacts` directory is created: it holds the bytecode and metadata of the contract, which are `.json` files. It’s a good idea to add this directory to your `.gitignore`. +After compilation, an `artifacts` directory is created: it holds the bytecode and metadata of the contract, which are `.json` files. Adding this directory to your `.gitignore` is a good idea. ## Testing {: #testing } -A robust smart contract development workflow is incomplete without a testing suite. Hardhat has a number of tools that make it easy to write and run tests. In this section, you'll learn the basics of testing your smart contracts and some more advanced techniques. +A robust smart contract development workflow is complete with a testing suite. Hardhat has a number of tools that make it easy to write and run tests. In this section, you'll learn the basics of testing your smart contracts and some more advanced techniques. -Hardhat tests are typically written with Mocha and Chai. [Mocha](https://mochajs.org/){target=\_blank} is a JavaScript testing framework and [Chai](https://www.chaijs.com/){target=\_blank} is a BDD/TDD JavaScript assertion library. BDD/TDD stands for behavior and test driven development respectively. Effective BDD/TDD necessitates writing your tests *before* writing your smart contract code. The structure of this tutorial doesn't strictly follow these guidelines, but you may wish to adopt these principles in your development workflow. Hardhat recommends using [Hardhat Toolbox](https://hardhat.org/hardhat-runner/docs/guides/migrating-from-hardhat-waffle){target=\_blank}, a plugin that bundles everything you need to get started with Hardhat, including Mocha and Chai. +Hardhat tests are typically written with Mocha and Chai. [Mocha](https://mochajs.org/){target=\_blank} is a JavaScript testing framework and [Chai](https://www.chaijs.com/){target=\_blank} is a BDD/TDD JavaScript assertion library. BDD/TDD stands for behavior and test-driven development respectively. Effective BDD/TDD necessitates writing your tests *before* writing your smart contract code. The structure of this tutorial doesn't strictly follow these guidelines, but you may wish to adopt these principles in your development workflow. Hardhat recommends using [Hardhat Toolbox](https://hardhat.org/hardhat-runner/docs/guides/migrating-from-hardhat-waffle){target=\_blank}, a plugin that bundles everything you need to get started with Hardhat, including Mocha and Chai. Because we will initially be running our tests on a local Moonbeam node, we need to specify Alice's address as the address of our target collator (Alice's account is the only collator for a local development node): @@ -264,7 +265,7 @@ To set up your test file, take the following steps: touch tests/Dao.js ``` -3. Then copy and paste the contents below to set up the initial structure of your test file. Be sure to read the comments as they can clarify the purpose of each line +3. Then copy and paste the contents below to set up the initial structure of your test file. Be sure to read the comments, as they can clarify the purpose of each line ```javascript // Import Ethers @@ -281,55 +282,68 @@ To set up your test file, take the following steps: Before we can run any test cases we'll need to launch a staking DAO with an initial configuration. Our setup here is relatively simple - we'll be deploying a staking DAO with a single administrator (the deployer) and then adding a new member to the DAO. This simple setup is perfect for demonstration purposes, but it's easy to imagine more complex configurations you'd like to test, such as a scenario with 100 DAO members or one with multiple admins of the DAO. -Mocha's `describe` function enables you to organize your tests. Multiple `describe` functions can be nested together. It's entirely optional but can be useful especially in complex projects with a large number of test cases. You can read more about constructing tests and [getting started with Mocha](https://mochajs.org/#getting-started){target=\_blank} on the Mocha docs site. +Mocha's `describe` function enables you to organize your tests. Multiple `describe` functions can be nested together. It's entirely optional but can be useful, especially in complex projects with many test cases. You can read more about constructing tests and [getting started with Mocha](https://mochajs.org/#getting-started){target=\_blank} on the Mocha docs site. -We'll define a function called `deployDao` that will contain the setup steps for our staking DAO. To configure your test file, add the following snippet: +We'll define a function called `deployDao` containing the setup steps for our staking DAO. To configure your test file, add the following snippet: ```javascript -// 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 +// 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 () { + let wallet1, wallet2; + + before(async function () { + // Get signers we defined in Hardhat config + const signers = await ethers.getSigners(); + wallet1 = signers[0]; + wallet2 = signers[1]; + }); + async function deployDao() { - // Get the contract factory and signers here - const [deployer, member1] = await ethers.getSigners(); - const delegationDao = await ethers.getContractFactory('DelegationDAO'); - - // Deploy the staking DAO and wait for the deployment transaction to be confirmed - const deployedDao = await delegationDao.deploy(targetCollator, deployer.address); - await deployedDao.waitForDeployment(); + const delegationDaoFactory = await ethers.getContractFactory( + 'DelegationDAO', + wallet2 + ); - // 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(); // 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 + } } - - // The test cases should be added here -}); + // Insert additional tests here +}); ``` ### Writing your First Test Cases {: #writing-your-first-test-cases } -First, you'll create a subsection called `Deployment` to keep the test file organized. This will be nested within the `Dao contract` describe function. Next you'll define your first test case by using the `it` Mocha function. This first test is simply checking to see that the staking DAO is correctly storing the address of the target collator. +First, you'll create a subsection called `Deployment` to keep the test file organized. This will be nested within the `Dao contract` describe function. Next, you'll define your first test case by using the `it` Mocha function. This first test checks to see that the staking DAO correctly stores the address of the target collator. -Go ahead and add the below snippet to the end of your `Dao contract` function. +Add the snippet below to the end of your `Dao contract` function. ```javascript // 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 + // 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(); + const deployment = await deployDao(); + if (!deployment || !deployment.deployedDao) { + throw new Error('Deployment failed; DAO contract was not deployed.'); + } + const { deployedDao } = deployment; // 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.targetCollator()).to.equal(targetCollator); + expect(await deployedDao.getTarget()).to.equal( + '0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac' + ); }); - // The following test cases should be added here }); ``` @@ -337,9 +351,9 @@ describe('Deployment', function () { Now, add another test case. When a staking DAO is launched, it shouldn't have any funds. This test verifies that is indeed the case. Go ahead and add the following test case to your `Dao.js` file: ```javascript +// 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); }); @@ -347,39 +361,45 @@ it('should initially have 0 funds in the DAO', async function () { ### Function Reverts {: #function-reverts } -Now, you'll implement a more complex test case with a slightly different architecture. In prior examples, you've verified that a function returns an expected value. In this one, you'll be verifying that a function reverts. You'll also change the address of the caller to test an admin-only function. +Now, you'll implement a more complex test case with a slightly different architecture. In prior examples, you've verified that a function returns an expected value. In this one, you'll be verifying that a function reverts. You'll also change the caller's address to test an admin-only function. -In the [staking DAO contract](https://github.com/moonbeam-foundation/moonbeam-intro-course-resources/blob/main/delegation-dao-lesson-one/DelegationDAO.sol){target=\_blank}, only admins are authorized to add new members to the DAO. One could write a test that checks to see if the admin is authorized to add new members but perhaps a more important test is to ensure that *non-admins* can't add new members. To run this test case under a different account, you're going to ask for another address when you call `ethers.getSigners()` and specify the caller in the assertion with `connect(member1)`. Finally, after the function call you'll append `.to.be.reverted` to indicate that the test case is successful if the function reverts. And if it doesn't revert it's a failed test! +In the [staking DAO contract](https://github.com/moonbeam-foundation/moonbeam-intro-course-resources/blob/main/delegation-dao-lesson-one/DelegationDAO.sol){target=\_blank}, only admins are authorized to add new members to the DAO. One could write a test that checks to see if the admin is authorized to add new members but a more important test is to ensure that *non-admins* can't add new members. To run this test case under a different account, you will ask for another address when you call `ethers.getSigners()` and specify the caller in the assertion with `connect(member1)`. Finally, after the function call you'll append `.to.be.reverted` to indicate that the test case is successful if the function reverts. And if it doesn't revert, it's a failed test! ```javascript +// 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(); - - // 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; + const { deployedDao } = await deployDao(); + // Connect the non-admin wallet to the deployed contract + const deployedDaoConnected = deployedDao.connect(wallet1); + const tx = deployedDaoConnected.grant_member( + '0x0000000000000000000000000000000000000000' + ); + + // Check that the transaction reverts, not specifying any particular reason + await expect(tx).to.be.reverted; }); ``` ### Signing Transactions from Other Accounts {: #signing-transactions-from-other-accounts } -For this example, you'll check to verify whether the newly added DAO member can call the `check_free_balance()` function of staking DAO, which has an access modifier such that only members can access it. +For this example, you'll verify whether the newly added DAO member can call the `check_free_balance()` function of staking DAO, which has an access modifier such that only members can access it. ```javascript +// 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); }); ``` @@ -395,12 +415,12 @@ If you've followed all of the prior sections, your [`Dao.js`](https://raw.github --8<-- 'code/tutorials/eth-api/hardhat-start-to-end/dao-js-test-file.js' ``` -Since our test cases encompass mostly configuration and setup of the staking DAO and don't involve actual delegation actions, we'll be running our tests on a Moonbeam development node (local node). Remember that Alice (`0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac`) is the only collator on a local development node. You can use the flag `--network moonbase` to run the tests using Moonbase Alpha. In that case, be sure that your deployer address is sufficiently funded with DEV tokens. +Since our test cases encompass mostly configuration and setup of the staking DAO and don't involve actual delegation actions, we'll be running our tests on a Moonbeam development node (local node). Remember that Alice (`0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac`) is the only collator on a local development node. You can use the flag `--network moonbase` to run the tests using Moonbase Alpha. In that case, ensure your deployer address is sufficiently funded with DEV tokens. !!! challenge - Try to create an additional test case that verifies the staking DAO successfully delegates to a collator once `minDelegationStk` is met. You'll need to test this on Moonbase Alpha rather than a local development node. + Try to create an additional test case that verifies the staking DAO successfully delegates to a collator once `minDelegationStk` is met. You must test this on Moonbase Alpha rather than a local development node. -First, make sure that your local Moonbeam node is running by following the [instructions for launching a local development node](/builders/get-started/networks/moonbeam-dev/){target=\_blank}. Take precautions because you could inadvertently send real funds to the Alice and Bob development accounts, which would result in a loss of those funds. +First, make sure that your local Moonbeam node is running by following the [instructions for launching a local development node](/builders/get-started/networks/moonbeam-dev/){target=\_blank}. Take precautions because you could inadvertently send real funds to the Alice and Bob development accounts, resulting in a loss of those funds. You can run your tests with the following command: @@ -410,79 +430,68 @@ npx hardhat test --network dev tests/Dao.js If everything was set up correctly, you should see output like the following: -![Run your test suite of test cases with Hardhat.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-3.webp) +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/test.md' ## Deploying to Moonbase Alpha {: #deploying-to-moonbase-alpha } -In the following steps, we'll be deploying the `DelegationDAO` to the Moonbase Alpha TestNet. Before deploying to Moonbase Alpha or Moonbeam, double check you're not using the Alice and Bob accounts, which should only be used on a local development node. +In the following steps, we'll deploy the `DelegationDAO` to the Moonbase Alpha TestNet. Before deploying to Moonbase Alpha or Moonbeam, double-check that you're not using the Alice and Bob accounts, which should only be used on a local development node. As a side note, `DelegationDAO` relies on [`StakingInterface.sol`](/builders/pallets-precompiles/precompiles/staking/){target=\_blank}, which is a Substrate-based offering unique to Moonbeam networks. The Hardhat Network and forked networks are simulated EVM environments which do not include the Substrate-based precompiles like `StakingInterface.sol`. Therefore, `DelegationDAO` will not work properly if deployed to the local default Hardhat Network or a [forked network](/builders/build/eth-api/dev-env/hardhat/#forking-moonbeam){target=\_blank}. -To deploy `DelegationDAO.sol`, you can write a simple script. You can create a new directory for the script and name it `scripts`: +To deploy `DelegationDAO`, you'll use Hardhat Ignition, a declarative framework for deploying smart contracts. Hardhat Ignition is designed to make it easy to manage recurring tasks surrounding smart contract deployment and testing. For more information about Hardhat Ignition and its architecture, be sure to check out the [Hardhat Ignition docs](https://hardhat.org/ignition/docs/getting-started#overview){target=\_blank}. -```bash -mkdir scripts -``` +To set up the proper file structure for your Ignition module, create a folder named `ignition` and a subdirectory called `modules`. Then, add a new file to it called `DelegationDao.js`. You can take all three of these steps with the following command: -Then add a new file to it called `deploy.js`: - -```bash -touch scripts/deploy.js +```sh +mkdir ignition ignition/modules && touch ignition/modules/DelegationDao.js ``` -Next, you need to write your deployment script which can be done using `ethers`. Because you'll be running it with Hardhat, you don't need to import any libraries. +Next, you can write your Hardhat Ignition module. To get started, take the following steps: -To get started, take the following steps: - -1. Specify the address of the active collator the DAO intends to delegate to. In this case, we've specified the address of the PS-1 Collator (note: this is different from the address of the Alice collator on a local development node) -2. Specify the deployer address as the admin of the DAO. It's important that the deployer be the admin of the DAO to ensure later tests work as expected -3. Create a local instance of the contract with the `getContractFactory` method -4. Use the `deploy` method that exists within this instance to instantiate the smart contract -5. Once deployed, you can fetch the address of the contract using the contract instance +1. Import the `buildModule` function from the Hardhat Ignition module +2. Export a module using `buildModule` +3. Specify the target collator candidate for the DAO to delegate to +4. Use the `getAccount` method to select the deployer account +5. Deploy `DelegationDAO.sol` +6. Return an object from the module. This makes the `DelegationDao` contract accessible for interaction in Hardhat tests and scripts When all is said and done your deployment script should look similar to the following: ```javascript -// 1. The PS-1 collator on Moonbase Alpha is chosen as the DAO's target -const targetCollator = '{{ networks.moonbase.staking.candidates.address1 }}'; - -async function main() { - // 2. Get the address of the deployer to later be set as the admin of the DAO - const [deployer] = await ethers.getSigners(); - console.log('Deploying contracts with the account:', deployer.address); - - // 3. Get an instance of DelegationDAO - const delegationDao = await ethers.getContractFactory('DelegationDAO'); - - // 4. Deploy the contract specifying two params: the desired collator to - // delegate to and the address of the deployer (the initial DAO admin) - const deployedDao = await delegationDao.deploy( - targetCollator, - deployer.address +// 1. Import the required function from the Hardhat Ignition module +const { buildModule } = require('@nomicfoundation/hardhat-ignition/modules'); + +// 2. Define and export your deployment module using `buildModule` +module.exports = buildModule('DelegationDAOModule', (m) => { + // 3. Specify the target collator address for the DAO + const targetCollator = '{{ networks.moonbase.staking.candidates.address1 }}'; + + // 4. Use the `getAccount` method to select the deployer account + const deployer = m.getAccount(0); + + // 5. Deploy the `DelegationDAO` contract + const delegationDao = m.contract( + 'DelegationDAO', + [targetCollator, deployer], + { + from: deployer, + } ); - await deployedDao.waitForDeployment(); - - // 5. Print out the address of the deployed staking DAO contract - console.log('DAO address:', deployedDao.target); -} -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); + // 6. Return an object from the module including references to deployed contracts, allowing the contract to be accessible for interaction in Hardhat tests and scripts + return { delegationDao }; +}); ``` -Make sure you've funded your accounts with Moonbase Alpha DEV tokens. You can now deploy `DelegationDAO.sol` using the `run` command and specifying `moonbase` as the network (as configured in the `hardhat.config.js` file): +To run the script and deploy the `DelegationDAO.sol` contract, use the following command, which requires you to specify the network name as defined in your `hardhat.config.js`. If you don't specify a network, Hardhat will deploy the contract to a local Hardhat network by default. -```bash -npx hardhat run --network moonbase scripts/deploy.js +```sh +npx hardhat ignition deploy ./ignition/modules/DelegationDao.js --network moonbase --deployment-id INSERT_YOUR_NAME ``` -After a few seconds, the contract is deployed, and you should see the address in the terminal. +You'll be prompted to confirm the network you wish to deploy to. After a few seconds after you confirm, the contract is deployed, and you'll see the contract address in the terminal. -![Deploy a Contract to Moonbase Alpha with Hardhat.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-4.webp) +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbase.md' Congratulations, your contract is live on Moonbase Alpha! Save the address, as you will use it to interact with this contract instance in the next step. @@ -494,68 +503,63 @@ While it's possible to verify smart contracts on the [Moonscan website](https:// Before beginning the contract verification process, you'll need to [acquire a Moonscan API Key](/builders/build/eth-api/verify-contracts/etherscan-plugins/#generating-a-moonscan-api-key){target=\_blank}. Note that Moonbeam and Moonbase Alpha use the same [Moonbeam Moonscan](https://moonscan.io/){target=\_blank} API key, whereas you'll need a distinct API key for [Moonriver](https://moonriver.moonscan.io/){target=\_blank}. -To verify the contract, you will run the `verify` command and pass in the network where the `DelegationDao` contract is deployed, the address of the contract, and the two constructor arguments that you specified in your `deploy.js` file, namely, the address of the target collator and the address you deployed the smart contract with (sourced from the `hardhat.config.js` file). +To verify the contract, you will run the `ignition verify` command and pass the name of your deployment you set in the prior step. ```bash -npx hardhat verify --network moonbase INSERT_CONTRACT_ADDRESS {{ networks.moonbase.staking.candidates.address1 }} INSERT_DEPLOYER_ADDRESS +npx hardhat ignition verify INSERT_YOUR_NAME ``` !!! note If you're deploying `DelegationDAO.sol` verbatim without any changes, you may get an `Already Verified` error because Moonscan automatically recognizes and verifies smart contracts that have matching bytecode. Your contract will still show as verified, so there is nothing else you need to do. However, if you'd prefer to verify your own `DelegationDAO.sol`, you can make a small change to the contract (such as changing a comment) and repeating the compilation, deployment and verification steps. -In your terminal you should see the source code for your contract was successfully submitted for verification. If the verification was successful, you should see **Successfully verified contract** and there will be a link to the contract code on [Moonscan for Moonbase Alpha](https://moonbase.moonscan.io/){target=\_blank}. If the plugin returns an error, double check that your API key is configured correctly and that you have specified all necessary parameters in the verification command. You can refer to the [guide to the Hardhat Etherscan plugin](/builders/build/eth-api/verify-contracts/etherscan-plugins/){target=\_blank} for more information. +In your terminal, you should see the source code for your contract was successfully submitted for verification. If the verification was successful, you should see **Successfully verified contract** and there will be a link to the contract code on [Moonscan for Moonbase Alpha](https://moonbase.moonscan.io/){target=\_blank}. If the plugin returns an error, double check that your API key is configured correctly and that you have specified all necessary parameters in the verification command. You can refer to the [guide to the Hardhat Etherscan plugin](/builders/build/eth-api/verify-contracts/etherscan-plugins/){target=\_blank} for more information. + +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbase.md' -![Verify contracts on Moonbase Alpha using the Hardhat Etherscan plugin.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-5.webp) ## Deploying to Production on Moonbeam Mainnet {: #deploying-to-production-on-moonbeam-mainnet } !!! note `DelegationDAO.sol` is unreviewed and unaudited. It is designed only for demonstration purposes and not intended for production use. It may contain bugs or logic errors that could result in loss of funds. -In the following steps, we'll be deploying the `DelegationDAO` contract to the Moonbeam MainNet network. Remember to add the Moonbeam network to your [`hardhat.config.js`](#hardhat-configuration-file) and update the private keys of your accounts on Moonbeam if you haven't done so already. Before deploying `DelegationDAO` to Moonbeam, we need to change the address of the target collator, since our target collator on Moonbase Alpha does not exist on Moonbeam. Head to your deploy script and change the target collator to `0x1C86E56007FCBF759348dcF0479596a9857Ba105` or [another Moonbeam collator](https://apps.moonbeam.network/moonbeam/staking){target=\_blank} of your choice. Your `deploy.js` script should thus look like the following: +In the following steps, we'll be deploying the `DelegationDAO` contract to the Moonbeam MainNet network. Remember to add the Moonbeam network to your [`hardhat.config.js`](#hardhat-configuration-file) and update the private keys of your accounts on Moonbeam if you haven't done so already. Before deploying `DelegationDAO` to Moonbeam, we need to change the address of the target collator, since our target collator on Moonbase Alpha does not exist on Moonbeam. Head to your deploy script and change the target collator to `{{ networks.moonbeam.staking.candidates.address1 }}` or [another Moonbeam collator](https://apps.moonbeam.network/moonbeam/staking){target=\_blank} of your choice. Your deployment script, named `DelegationDao.js`, should thus look like the following: ```javascript -// 1. The moonbeam-foundation-03 collator on Moonbeam is chosen as the DAO's target -const targetCollator = '0x1C86E56007FCBF759348dcF0479596a9857Ba105'; - -async function main() { - // 2. Get the address of the deployer to later be set as the admin of the DAO - const [deployer] = await ethers.getSigners(); - console.log('Deploying contracts with the account:', deployer.address); - - // 3. Get an instance of DelegationDAO - const delegationDao = await ethers.getContractFactory('DelegationDAO'); - - // 4. Deploy the contract specifying two params: the desired collator to delegate - // to and the address of the deployer (synonymous with initial DAO admin) - const deployedDao = await delegationDao.deploy( - targetCollator, - deployer.address +// 1. Import the required function from the Hardhat Ignition module +const { buildModule } = require('@nomicfoundation/hardhat-ignition/modules'); + +// 2. Define and export your deployment module using `buildModule` +module.exports = buildModule('DelegationDAOModule', (m) => { + // 3. Specify the target collator address for the DAO + const targetCollator = '{{ networks.moonbeam.staking.candidates.address1 }}'; + + // 4. Use the `getAccount` method to select the deployer account + const deployer = m.getAccount(0); + + // 5. Deploy the `DelegationDAO` contract + const delegationDao = m.contract( + 'DelegationDAO', + [targetCollator, deployer], + { + from: deployer, + } ); - await deployedDao.waitForDeployment(); - console.log('DAO address:', deployedDao.target); -} + // 6. Return an object from the module including references to deployed contracts, allowing the contract to be accessible for interaction in Hardhat tests and scripts + return { delegationDao }; +}); -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); ``` -You can now deploy `DelegationDAO.sol` using the `run` command and specifying `moonbeam` as the network: +To run the script and deploy the `DelegationDAO.sol` contract, use the following command, which requires you to specify the network name as defined in your `hardhat.config.js`. If you don't specify a network, Hardhat will deploy the contract to a local Hardhat network by default. -```bash -npx hardhat run --network moonbeam scripts/deploy.js +```sh +npx hardhat ignition deploy ./ignition/modules/DelegationDao.js --network moonbeam --deployment-id INSERT_YOUR_NAME ``` -If you're using another Moonbeam network, make sure that you specify the correct network. The network name needs to match how it's defined in the `hardhat.config.js`. - -After a few seconds, the contract is deployed, and you should see the address in the terminal. +You'll be prompted to confirm the network you wish to deploy to. After a few seconds after you confirm, the contract is deployed, and you'll see the contract address in the terminal. -![Deploy a Contract to Moonbeam with Hardhat.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-6.webp) +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/deploy-moonbeam.md' Congratulations, your contract is live on Moonbeam! Save the address, as you will use it to interact with this contract instance in the next step. @@ -563,10 +567,10 @@ Congratulations, your contract is live on Moonbeam! Save the address, as you wil In this section, we'll be verifying the contract that was just deployed on Moonbeam. Before beginning the contract verification process, you'll need to [acquire a Moonscan API Key](/builders/build/eth-api/verify-contracts/etherscan-plugins/#generating-a-moonscan-api-key){target=\_blank}. Note that Moonbeam and Moonbase Alpha use the same [Moonbeam Moonscan](https://moonscan.io/){target=\_blank} API key, whereas you'll need a distinct API key for [Moonriver](https://moonriver.moonscan.io/){target=\_blank}. -To verify the contract, you will run the `verify` command and pass in the network where the `DelegationDao` contract is deployed, the address of the contract, and the two constructor arguments that you specified in your `deploy.js` file, namely, the address of the target collator and the address you deployed the smart contract with (sourced from your `hardhat.config.js` file). Remember that the target collator of the staking DAO on Moonbeam is different from the target collator of the staking DAO on Moonbase Alpha. +To verify the contract, you will run the `ignition verify` command and pass the name of your deployment you set in the prior step. ```bash -npx hardhat verify --network moonbeam INSERT_CONTRACT_ADDRESS 0x1C86E56007FCBF759348dcF0479596a9857Ba105 INSERT_DEPLOYER_ADDRESS +npx hardhat ignition verify INSERT_YOUR_NAME ``` !!! note @@ -574,9 +578,9 @@ npx hardhat verify --network moonbeam INSERT_CONTRACT_ADDRESS 0x1C86E56007FCBF75 In your terminal you should see the source code for your contract was successfully submitted for verification. If the verification was successful, you should see **Successfully verified contract** and there will be a link to the contract code on [Moonbeam Moonscan](https://moonscan.io/){target=\_blank}. If the plugin returns an error, double check that your API key is configured correctly and that you have specified all necessary parameters in the verification command. You can refer to the [guide to the Hardhat Etherscan plugin](/builders/build/eth-api/verify-contracts/etherscan-plugins/){target=\_blank} for more information. -![Verify contracts on Moonbeam using Hardhat Etherscan plugin.](/images/tutorials/eth-api/hardhat-start-to-end/hardhat-7.webp) +--8<-- 'code/tutorials/eth-api/hardhat-start-to-end/terminal/verify-moonbeam.md' -And that's it! We covered a lot of ground in this tutorial but there's more resources available if you'd like to go deeper, including the following: +And that's it! We covered a lot of ground in this tutorial, but there's more resources available if you'd like to go deeper, including the following: - [Hardhat guide to Testing Contracts](https://hardhat.org/hardhat-runner/docs/guides/test-contracts){target=\_blank} - [Writing tasks and scripts](https://hardhat.org/hardhat-runner/docs/guides/tasks-and-scripts){target=\_blank} diff --git a/variables.yml b/variables.yml index d0398dcb8..15303d20f 100644 --- a/variables.yml +++ b/variables.yml @@ -328,8 +328,8 @@ networks: max_del_per_can: 300 max_del_per_del: 100 candidates: - address1: '0x4c5A56ed5A4FF7B09aA86560AfD7d383F4831Cce' - address2: '0x623c9E50647a049F92090fe55e22cC0509872FB6' + address1: '0x12E7BCCA9b1B15f33585b5fc898B967149BDb9a5' + address2: '0x95651f0777D9c7EF56f2edEe62D5F28743E0635a' treasury: current_council_members: 2 motion_duration_days: 3 @@ -992,6 +992,8 @@ networks: min_del_stake: 50 max_del_per_can: 300 max_del_per_del: 100 + candidates: + address1: '0x1C86E56007FCBF759348dcF0479596a9857Ba105' treasury: current_council_members: 5 motion_duration_days: 3