Creating and Deploying an ERC20 Vault on Shardeum: A Step-by-Step Guide

Creating and Deploying an ERC20 Vault on Shardeum: A Step-by-Step Guide

1. Introduction:

In this comprehensive guide, we'll explore the exciting world of decentralized finance (DeFi) by learning how to create and deploy a customized ERC20 Vault smart contract on the innovative Shardeum platform. A Vault, in the realm of DeFi, acts as a financial hub where users can stake their tokens. These Vaults then employ various lending and yield-generating strategies to maximize profits, which are later distributed among all depositors. (Please note: This guide is focused on creating DeFi Vaults and is distinct from Crypto Vaults, which are a form of cryptocurrency wallets.)

For our journey, we'll harness the power of Remix IDE, a popular web-based Solidity Integrated Development Environment (IDE). Get ready to embark on a practical and engaging journey into the world of DeFi!

2. ERC20 Vault Smart Contract for Decentralized Finance:

In this segment, we will lay the groundwork for a powerful ERC20 Vault smart contract. Our core strategy involves the dynamic minting and burning of shares to represent users' ownership within the vault. We will also import the IERC20 interface for seamless ERC20 token integration.

To get started, let's create a new file called IERC20.sol within the Remix Workspace and import the complete contract from OpenZeppelin's IERC20. Subsequently, we will delve into the implementation of two vital internal functions: mint() and burn(). The mint() function will handle the creation of shares and allocate them to respective addresses, while the burn() function will facilitate the precise removal of shares.

Let's proceed with building this robust foundation for your ERC20 Vault.

2.1 Importing the IERC20 Interface:

Before we dive into crafting the mint and burn functions, it's crucial to establish seamless interoperability with ERC20 tokens. To achieve this, we begin by importing the IERC20 interface, which provides us with essential functions and events necessary for complying with the ERC20 token standard.

// SPDX-License-Identifier: MIT


pragma solidity ^0.8.17;
import "./IERC20.sol";


 contract Vault{
   IERC20 public immutable token;
   uint public totalSupply;
   mapping(address => uint) public balanceOf;


   constructor(address _token){
       token = IERC20(_token);
   }
}

2.2 Crafting Mint and Burn Functions:

Minting Shares (mint() Function):

Our mint() function plays a pivotal role in the Vault's operation. It is responsible for generating shares and assigning them to designated addresses. This process effectively represents the ownership of tokens deposited into the Vault contract. The function takes two crucial inputs:

  • The address where the shares will be minted.

  • The quantity of shares to be created.

function mint(address _to, uint _amount) private {
    // Increase the total supply of shares
    totalSupply += _amount;

    // Allocate shares to the specified address
    balanceOf[_to] += _amount;
}

2.3 Burning Shares (burn() Function):

Conversely, the burn() function serves the purpose of accurately removing shares when a user withdraws tokens from the Vault. It takes the following input:

  • The address from which shares need to be burned.

  • The number of shares to be eliminated.

function burn(address _from, uint _amount) private {
    // Decrease the total supply of shares
    totalSupply -= _amount;

    // Reduce the balance of shares for the specified address
    balanceOf[_from] -= _amount;
}

With the fundamental structure in place, we are now prepared to proceed with the development of deposit and withdrawal functions, enabling users to interact seamlessly with your ERC20 Vault. These functions will facilitate the creation and elimination of shares based on user actions.

2.4 Deposit Function (deposit())

The deposit() function is responsible for allowing users to deposit ERC20 tokens into the Vault. This function includes a special handling mechanism for the scenario when totalSupply is zero to prevent division by zero.

Key Steps in the deposit() Function:

  1. Determine the Number of Shares (shares):

    • We calculate the number of shares to be minted based on the number of tokens being deposited. If totalSupply is zero, this implies the initial deposit, so we simply assign shares equal to the deposited token amount.

    • Otherwise, we calculate the shares based on the deposited token amount, scaled proportionally to the current total supply of shares.

  2. Mint Shares and Transfer Tokens:

    • We call the _mint() function to create the specified number of shares for the user, representing their ownership within the Vault.

    • Tokens are then transferred from the user's address to the Vault contract to complete the deposit.

function deposit(uint _amount) external {
    uint shares;
    if (totalSupply == 0) {
        shares = _amount;
    } else {
        shares = (_amount * totalSupply) / token.balanceOf(address(this));
    }
    _mint(msg.sender, shares);
    token.transferFrom(msg.sender, address(this), _amount);
}

2.5 Withdraw Function (withdraw())

The withdraw() function enables users to withdraw tokens from the Vault, effectively redeeming their shares. Similar to the deposit() function, it calculates the number of shares to be burned based on the user's input.

Key Steps in the withdraw() Function:

  1. Determine the Amount to Withdraw (amount):

    • We calculate the amount of tokens to be returned to the user based on the number of shares they wish to withdraw, scaled proportionally to the current total supply of shares.
  2. Burn Shares and Transfer Tokens:

    • We call the _burn() function to remove the specified number of shares from the user's balance, accurately representing their reduced ownership.

    • Tokens are then transferred from the Vault contract back to the user, completing the withdrawal process.

function withdraw(uint _shares) external {
    uint amount = (_shares * token.balanceOf(address(this))) / totalSupply;
    _burn(msg.sender, _shares);
    token.transfer(msg.sender, amount);
}

With these deposit and withdrawal functions in place, your ERC20 Vault is now equipped to handle user interactions seamlessly. Users can deposit tokens, mint shares, and later withdraw tokens, burning shares as needed. This fundamental functionality sets the stage for more advanced strategies and features within your DeFi Vault.

3. Creating your ERC20 Token:

In this exciting phase of our DeFi journey, we will craft a standard ERC20 token named "VAULT" with the symbol "VLT." This token will play a pivotal role in our DeFi ecosystem, serving as the primary asset for users to deposit into our Vault smart contract.

Our ERC20 token will adhere to industry standards and provide essential functions for transferring, approving, and managing token balances. Additionally, we've set the token's name, symbol, and decimal precision to create a user-friendly and recognizable asset.

Let's dive into the code and explore the capabilities of the VAULT token, Copy this complete code and paste this on remix ide to deploy your ERC20 vault

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "./IERC20.sol";

// Our ERC20 token contract that implements the IERC20 interface
contract ERC20 is IERC20 {
   // Total supply of tokens
   uint public totalSupply;

   // Mapping to track token balances for each address
   mapping(address => uint) public balanceOf;

   // Mapping to manage delegated spending permissions
   mapping(address => mapping(address => uint)) public allowance;

   // Token name
   string public name = "VAULT";

   // Token symbol
   string public symbol = "VLT";

   // Number of decimal places for token precision
   uint8 public decimals = 18;

   // Transfer tokens from the sender to a recipient
   function transfer(address recipient, uint amount) external returns (bool) {
       // Ensure the sender has enough tokens
       require(balanceOf[msg.sender] >= amount, "Insufficient balance");

       // Update sender and recipient balances
       balanceOf[msg.sender] -= amount;
       balanceOf[recipient] += amount;

       // Emit a Transfer event to log the transaction
       emit Transfer(msg.sender, recipient, amount);
       return true;
   }

   // Approve another address (spender) to spend a specified amount of tokens on your behalf
   function approve(address spender, uint amount) external returns (bool) {
       // Set the spending allowance
       allowance[msg.sender][spender] = amount;

       // Emit an Approval event to log the approval
       emit Approval(msg.sender, spender, amount);
       return true;
   }

   // Transfer tokens on behalf of the token owner (if allowed)
   function transferFrom(address sender, address recipient, uint amount) external returns (bool) {
       // Ensure the sender has enough tokens and the spender is allowed
       require(balanceOf[sender] >= amount, "Insufficient balance");
       require(allowance[sender][msg.sender] >= amount, "Allowance exceeded");

       // Update balances and allowance
       balanceOf[sender] -= amount;
       balanceOf[recipient] += amount;
       allowance[sender][msg.sender] -= amount;

       // Emit a Transfer event to log the transaction
       emit Transfer(sender, recipient, amount);
       return true;
   }

   // Create new tokens and give them to the sender
   function mint(uint amount) external {
       // Increase sender's balance and the total supply
       balanceOf[msg.sender] += amount;
       totalSupply += amount;

       // Emit a Transfer event to log the minting
       emit Transfer(address(0), msg.sender, amount);
   }

   // Destroy tokens owned by the sender
   function burn(uint amount) external {
       // Ensure the sender has enough tokens
       require(balanceOf[msg.sender] >= amount, "Insufficient balance");

       // Reduce sender's balance and decrease the total supply
       balanceOf[msg.sender] -= amount;
       totalSupply -= amount;

       // Emit a Transfer event to log the burning
       emit Transfer(msg.sender, address(0), amount);
   }
}

4. Let's Launch Your DeFi Empire: It's Deployment Time!

Certainly, let's break down the deployment and interaction process into clear and easy-to-follow points:

Configuring Metamask for Shardeum Testnet:

  1. Set up your Metamask wallet to connect to the Shardeum Sphinx testnet.

  2. Acquire some testnet SHM tokens from the Shardeum testnet faucet.

Compiling Smart Contracts:

3. Compile the following smart contracts in the Remix Solidity Compiler:

  • ERC20.sol

  • IERC20.sol

  • Vault.sol

Switching to Shardeum Testnet in Remix:

4. Navigate to the "Deploy and Run Transactions" section in Remix.

5. Change the Remix environment from "Remix VM" to "Injected Web3." You should see the option for 'Custom (8082) Network' if your Metamask is properly configured.

Deploying the ERC20 Token:

6. Deploy the ERC20.sol contract and make note of the token contract address once it's deployed.

Deploying the Vault Contract:

7. Select the Vault.sol contract and paste the previously copied ERC20 token address next to the deploy button. 8. Deploy the Vault contract.

Minting Tokens:

9. Use the mint function of the ERC20 token contract to mint 100 VLT tokens.

Approving Spending:

10. In the Vault contract, paste your wallet address and the number of tokens you'd like to approve beside the approve() function. This step allows the Vault contract to spend tokens on your behalf.

Depositing Tokens into the Vault:

11. Deposit 100 tokens into the Vault contract using the deposit() function.

Checking Vault Balance:

12. Use the balanceOf() function in the Vault contract, passing your wallet address, to confirm that you now have 100 tokens in the Vault.

Assuming Profit and Direct Deposit:

13. As an example, you can send an additional 100 tokens directly to the Vault contract, assuming that the Vault made some profit.

Withdrawing Tokens:

14. Call the withdraw() function in the Vault contract to withdraw tokens.

15. Check your balance on the ERC20 token contract, where you should now see 200 tokens.

That's it! We've successfully created a vault, deposited tokens, and made a withdrawal, with the assumption of profit. Keep in mind that this is a simplified example, and real-world DeFi vaults involve more complex strategies for profit generation using various trading and lending techniques.

5. Conclusion:

In conclusion, we've embarked on an exciting journey into the world of decentralized finance, building and deploying our own vault on the Shardeum platform. We've explored the fundamental steps of creating an ERC20 token, deploying it, and interacting with it through a vault. This experience has been made possible thanks to Shardeum, our trusted platform for testing and experimenting with DeFi concepts.

If you are interested in learning more about Shardeum or getting involved in the project, you can join the community on Telegram channel, follow the Twitter page and join the Discord channel. You can also find more information on the Shardeum Website.

Shardeum Rewards Developers for Their Contributions.

Shardeum, the decentralized application platform, is committed to rewarding developers for their contributions to the project. The company offers a variety of community reward programs.

To learn more about the available community reward programs, please visit the Community Reward Programs page.

Thank you for Reading! ❤️