5- dApps Staking Chain Extension

Prerequisites

Cargo contract installed: https://polkaverse.com/9768/technical-guide-install-cargo-contract-37738

At least on the following node is ready :

This tutorial uses Ubuntu 22.04, cargo 1.68, cargo contract 2.0 and swanky node

Create the project

You can use the command “cargo contract new” to create a new contract based on the template “flipper“.

cargo contract new dapps_staking_contract

Use dapps staking extension

Add dapps_staking_extension in your Cargo.toml and to the std features

dapps_staking_extension = { git = "https://github.com/swanky-dapps/chain-extension-contracts", default-features = false }

[features]
default = ["std"]
std = [
    "ink_metadata/std",
    "ink/std",
    "scale/std",
    "scale-info/std",
    "dapps_staking_extension/std"
]

Add use statement in your contract module

pub mod dapp_staking_contract_example{
    use dapps_staking_extension::*;
...

Use struct functions directly in your contract

DappsStaking::read_unbonding_period()
DappsStaking::bond_and_stake(...)
DappsStaking::unbond_and_unstake()

Important: In the following statement

#[ink(message, payable)]
pub fn bond_and_stake(&mut self) -> Result<(), DSError> {
  // make sure the caller is recorded as staker

  let contract = self.env().account_id();
  let value = self.env().transferred_value();
  DappsStaking::bond_and_stake(contract, value)
}

The tokens are tranferred from caller’s address to the contract’s address and the tokens are locked in the contract’s address. It means, in the dAppStaking module, the staker is the contract and not the caller. It’s why the contract must register the caller as staker.

Register the caller as staker

In the storage, save the amount staked by stakers

    #[ink(storage)]
    pub struct DAppsStakingContract {
        stakers: Mapping<AccountId, Balance>,
    }

Save the staked amount before calling the method DappsStaking::bond_and_stake

    let caller = self.env().caller();
    let value = self.env().transferred_value();

    // compute the new stake
    let new_stake = match self.stakers.get(&caller){
      Some(existing) => {existing.checked_add(value).ok_or(ContractError::AddOverFlow)?}
      _ => {value}
    };

    // save the new amount staked by the caller
    self.stakers.insert(&caller, &new_stake);

Remove the unstaked amount before calling the method DappsStaking::unbond_and_unstake

    let caller = self.env().caller();

    // compute the new stake
    let new_stake = match self.stakers.get(&caller){
      Some(existing) => {existing.checked_sub(value).ok_or(ContractError::SubOverFlow)?}
      _ => {value}
    };
    
    // save the new amount staked by the caller
    if new_stake == 0 {
      self.stakers.remove(&caller);
    } else {
      self.stakers.insert(&caller, &new_stake);
    }

Emit the events

Define the following events

    /// Event emitted when a value is staked
    #[ink(event)]
    pub struct Staked {
        #[ink(topic)]
        account: AccountId,
        era: u32,
        amount: Balance,
    }

    /// Event emitted when a value is unstaked
    #[ink(event)]
    pub struct Unstaked {
        #[ink(topic)]
        account: AccountId,
        era: u32,
        amount: Balance,
    }

Emit the event after calling the method DappsStaking::bond_and_stake

    // get the current era
    let era = DappsStaking::read_current_era();
    // emmit the event
    self.env().emit_event(Staked { account: caller, era, amount: value });

Emit the event after calling the method DappsStaking::unbond_and_unstake

    // get the current era
    let era = DappsStaking::read_current_era();
    // emmit the event
    self.env().emit_event(Unstaked { account: caller, era, amount: value });

Build the project

In the directory “dapps_staking_contract“, use the command “cargo contract build” to compile the project.

cargo contract build

Deploy the contract

You can deploy your contract in local node or swanky node or a testnet.

Here we will start a swanky node and use Contracts UI to interact with the contract.

The result in Contracts UI:

Register your contract in the dApps Staking module

Before staking on a contract, this contract must be registered in the module dApps Staking.

We use the menu “Sudo” in “polkadot.js”.

BondAndStake via ContractsUI

And check the data

Conclusion

Here we registered a contract in the module dApps Staking and used the dApps Staking Chain Extension to interact with the module dApps Staking.

This tutorial covers some methods provided by the dApps Staking Chain Extension and you can found the exhaustive list here:

https://github.com/swanky-dapps/chain-extension-contracts/blob/main/crates/dapps-staking/lib.rs

Git repository

https://github.com/GuiGou12358/astar-tutorials/tree/main/tuto5

Source

https://docs.astar.network/docs/build/wasm/contract%5Fenvironment/chain-extension/chain%5Fextensions/

https://github.com/swanky-dapps/chain-extension-contracts

0
GuiGouPost author

Crypto-enthusiast, Defi & NFT believer, Dotsam Fam Astar Tech Amb & Phala Amb Web2 builder gradually migrating to Web3

Tutorials to write Smart Contracts in Rust and Ink!

0 comments

Tutorials to write Smart Contracts in Rust and Ink!