A Simple Staking dApp
This tutorial guides you through the process of building a simple staking dApp using ink! smart contract. In this demo dApp, user can stake their ICZ and earn a staking reward at the end of the staking period.
Staking tokens have become essential to many DeFi protocols, allowing developers to create complex financial derivative products. If you’re unfamiliar with crypto/token staking, it’s best described as the process of locking/depositing crypto holdings into a DeFi protocol or a smart contract to earn interest.
What will you be doing
- Deploy ink! staking contract to Arctic testnet
- Run the Staking dApp frontend
- Stake your ICZ and earn rewards
Prerequisite
- Node.js >= v16.13.0 (NVM)
- Polakadot.js wallet extension
- An account with testnet tokens (Faucet)
NOTE: For compiling/building contracts make sure you have installed Rust and configured your Rust environment.
Setting up the project
Fork the GitHub repository where our code for ink! staking contract and frontend are available for public use.
Building the contract
For building the contract you can visit our article on Ink! smart contract and follow the steps given there to build and compile the smart contract code.
Or, you can just use the staking_contract.contract
file that is already available on the repository.
Let us take a look at the staking contract code that we will be using in this tutorial.
Ink! contract definition begins with the attribute #[ink::contract]
. Any method annotated with the #[ink(message)]
attribute will be exposed as API. You can know more about ink! attributes by visiting this link. Let us go through the API available for calling our smart contract:
- add_stake: This method is used to stake the ICZ amount.
- early_withdraw: This method is used to withdraw ICZ before the maturity period (staking period). On early withdrawal, user will not receive any reward.
- redeem_stake: This method redeems the staked ICZ, adding interest to the user’s originally staked amount. This method can only be called after the locking period is over. The reward calculation formula applied in this contract is shown below:
const reward= (interest_rate * stake_amount)/100
- get_stake_for_account: This method returns the details of staked ICZ for a given account. Information will include:
stakedAmount, depositTime, releaseTime
User actions such as early withdrawal and redemption, transfers balance out of the smart contract. It is the responsibility of the contract owner to ensure that the smart contract has enough funds to pay the users at any given time.
Deploy staking contract
Now let us deploy the staking contract into the Arctic testnet using the polkadot.js app.
- Head over to Polkadot.js app.
NOTE: Visit the Arctic Faucet to get Arctic testnet ICZ tokens.
2. Uploading contract code
- Click on the upload & deploy code button on the contracts page.
- On the popup, select the wallet you want to deploy the contract from. Then select the
staking_contract.contract
file and click on Next.
- After clicking on Next, please enter the following parameters:
interestRatePerCent = 10 (in %)
stakingDuration = 7776000000 (in milliseconds)
- After clicking on deploy, click the Sign and Submit button and confirm the transaction from your wallet.
- Finally, your deployed contract will appear on the same page.
The contract address of the staking contract that we have deployed is: npMxzzNTBTTG3qHMFiUsvG6fV6vfRrfKTqQRnX5ibCDnCKA4s
Running the frontend
The frontend is developed using React.js. The folder structure for the frontend can be found in simple_staking_dapp/frontend/.
First of all, navigate to the frontend folder.
cd frontend
Create a .env
file in the root of the frontend folder. Inside the .env
file, add the contract address and Arctic testnet node web-socket URL.
REACT_APP_CONTRACT_ADDRESS= "npMxzzNTBTTG3qHMFiUsvG6fV6vfRrfKTqQRnX5ibCDnCKA4s"
REACT_APP_RPC_URL="wss://arctic-rpc.icenetwork.io:9944"
Now run the following command to start the frontend:
npm install && npm start
The DEMO staking dApp will be launched and it can be accessed on browser at http://localhost:3000.
Let’s take a look at the UI.
Now let us stake some ICZ to earn rewards.
To stake ICZ, adjust the amount by moving the slider or typing the amount in the stake field. Now, hit the STAKE button which will open a pop-up for confirming your transaction.
All the codes related to the contract API interaction can be found on the
frontend/src/services/contract.js
file.
Snippet for calling stake method from the frontend:
const stake = async (api, contract, accountId, stakeAmount, gasLimit) => {
try {
const injector = await web3FromAddress(accountId);
// get signer
const txObj = await contract.tx.addStake({
storageDepositLimit: null,
gasLimit,
dest: contract?.address?.toHuman(),
value: stakeAmount,
});
// execute transaction
const res = await postTransaction(txObj, accountId, { signer: injector.signer }, api);
if (res?.errorMsg) {
return {
isOk: false,
error: res?.errorMsg,
};
}
return {
isOk: true,
response: res,
};
} catch (e) {
console.log(e);
return {
isOk: false,
error: e.message || 'Unknown error occurred',
};
}
};
Once you have successfully staked your ICZ, you can view your position in the Staked Position
section.
Note: In this demo dApp, one account can stake only once in a staking contract
Now you can either Withdraw Early
OR wait for the locking period to be over and Redeem
.
Snippets for invoking the earlyWithdraw and redeem functions can be found in contract.js
file.
Note: You will only receive rewards once the locking period has ended. If you withdraw earlier, you will only receive the amount that you had staked.
Conclusion
In this tutorial, we have deployed ink! staking smart contract to the Arctic testnet and staked some ICZ using the Demo Staking App user interface.