How to Build a Multi-Chain Account Abstraction Wallet

Posted By : Kapil

Oct 01, 2024

Understanding Account Abstraction 

 

After Vitalik presented the idea of account abstraction in 2015, it gained attention. The word "account abstraction" is wide, but to put it briefly, it refers to the abstraction of the inflexibility and inherent structure of user accounts in a blockchain. This allows for network interaction while also increasing their flexibility and adaptability. Instead of relying on the pre-built standard Blockchain rules, suppliers of crypto wallet solutions develop user-friendly accounts with unique logic that allows them to apply their own asset storage and transaction conditions.
 

This lets the wallet control user actions without requiring users to sign transactions or physically hold private keys. Wallet development teams create smart contracts that function as user accounts in these kinds of applications. These contracts have the ability to communicate across programs, work with several accounts, and apply unique logic.


Multi-Chain Support: Bridging or cross-chain communication protocols can be used as a way to communicate with several blockchains.
Smart Contract Architecture: A primary contract and perhaps a factory for generating wallet instances will make up the wallet.
 

Also, Read | ERC 4337 : Account Abstraction for Ethereum Smart Contract Wallets


How to Build a Multi-Chain Account Abstraction Wallet 

 

Step1:  Recognise the Complexities of Account Abstraction


Understanding the ins and outs of account abstraction is crucial before starting the Account Abstraction wallet process. This is the process of providing user-friendly designs while severing the connection between user accounts and Blockchain accounts.
 

Step:2 Choose an Appropriate Blockchain Platform


Choose a blockchain platform that either supports it natively or can be improved to do so. As an alternative, you may think about Ethereum, which comes with a tonne of Ethereum Improvement Proposals, including ERC-4337, a common AA idea.
 

Step:3  Establish a Development Environment
 

Install the necessary development tools, such as Hardhat, Truffle, and Node.js, to create smart contracts. Additionally, the establishment of a blockchain node can be done with Ganache or by connecting it to a testnet, like Rinkeby or Ropsten.
 

Step:4  Make the contracts for smart contracts

 

Make a smart contract that shows which user account is in charge of managing transaction volume and user authentication. It is also advisable to have a central point contract that facilitates communication with account contracts. Utilise proxy patterns to incorporate security measures for contract upgrades.
 

Step:5 Design 
 

Aim for straightforward, user-friendly designs while considering the diverse user bases. Success results from keeping people interested in the wallet. The design is shared for approval when it has been created.
 

Step:6  Security Audits and Testing
 

The smart contracts will undergo extensive testing following the development of the solution. Testing is done in various settings to check for mistakes and defects. For smart contract assessments, vulnerability detection, and remediation, third-party auditors are hired.
 

Step:7  Install on the Mainnet
 

The system is ready for post-launch post-testing and Mainnet security assessments. This stage involves configuring the server environment and deploying the code into the production environment.
 

Step:8  Upkeep and Modifications
 

Examine the systems for problems, and where necessary, apply upgrades. Assist users who may have queries or encounter problems when using the wallet. As a result, the solution will become reliable and capable over time.
 

Step:9 Marketing & Getting User Feedback 

 

To attract consumers' attention, the solution is advertised through various means. This covers joint ventures and collaborations, recommendations, social media marketing, and YouTube advertising. The solution is improved by the collection of user input.

 

Also, Check | How to Build a Cryptocurrency Wallet App Like Exodus

 

Building a Multi-Chain Account Abstraction Wallet 

 

Example: - Set Up Hardhat:-
 

If you haven't set up a Hardhat project yet, you can do so with the following commands:

 

mkdir MultiChainWallet
cd MultiChainWallet
npm init -y
npm install --save-dev hardhat
npx hardhat

Add the Contract
Create a file named MultiChainWallet.sol in the contracts directory and paste the contract code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

contract MultiChainWallet {
    mapping(address => mapping(address => uint256)) private balances;
    mapping(address => bool) private wallets;

    event WalletCreated(address indexed owner);
    event Deposit(address indexed user, address indexed token, uint256 amount);
    event Withdraw(address indexed user, address indexed token, uint256 amount);

    modifier onlyWallet() {
        require(wallets[msg.sender], "Wallet does not exist");
        _;
    }

    function createWallet() external {
        require(!wallets[msg.sender], "Wallet already exists");
        wallets[msg.sender] = true;
        emit WalletCreated(msg.sender);
    }

    function deposit(address token, uint256 amount) external onlyWallet {
        require(amount > 0, "Invalid amount");
        IERC20(token).transferFrom(msg.sender, address(this), amount);
        balances[msg.sender][token] += amount;
        emit Deposit(msg.sender, token, amount);
    }

    function withdraw(address token, uint256 amount) external onlyWallet {
        require(balances[msg.sender][token] >= amount, "Insufficient balance");
        balances[msg.sender][token] -= amount;
        IERC20(token).transfer(msg.sender, amount);
        emit Withdraw(msg.sender, token, amount);
    }

    function getBalance(address token) external view onlyWallet returns (uint256) {
        return balances[msg.sender][token];
    }
}



contract MockERC20 is IERC20 {
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    constructor(string memory _name, string memory _symbol, address initialAccount, uint256 initialBalance) {
        name = _name;
        symbol = _symbol;
        _balances[initialAccount] = initialBalance;
    }

    function transfer(address recipient, uint256 amount) external override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
        require(amount <= _allowances[sender][msg.sender], "Allowance exceeded");
        _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
        _transfer(sender, recipient, amount);
        return true;
    }

    function balanceOf(address account) external view override returns (uint256) {
        return _balances[account];
    }

    function approve(address spender, uint256 amount) external  returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function allowance(address owner, address spender) external   view  returns (uint256) {
        return _allowances[owner][spender];
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "Transfer from the zero address");
        require(recipient != address(0), "Transfer to the zero address");
        require(_balances[sender] >= amount, "Insufficient balance");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "Approve from the zero address");
        require(spender != address(0), "Approve to the zero address");

        _allowances[owner][spender] = amount;
    }
}

 

You may also like | What is the Cost of Creating a Crypto Wallet App in 2024

 

Create the Deployment Script:


Create a new file named deploy.js in the scripts directory and add the following code:

 

// scripts/deploy.js

async function main() {
    const MockERC20 = await ethers.getContractFactory("MockERC20");
    const mockToken = await MockERC20.deploy("Mock Token", "MTK", "0xYourAddressHere", ethers.utils.parseEther("1000"));

    await mockToken.deployed();
    console.log("MockERC20 deployed to:", mockToken.address);
}

// Execute the script
main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error);
        process.exit(1);
    });

 

Configure Hardhat Network

 

Edit the hardhat.config.js file to configure the network you want to deploy to (for example, the Rinkeby testnet or the local Hardhat network):

 

require('@nomiclabs/hardhat-waffle');
module.exports = {
   solidity: "0.8.0",
   networks: {
       rinkeby: {
           url: 'https://rinkeby.infura.io/v3/YOUR_INFURA_PROJECT_ID',
           accounts: [`0x${YOUR_PRIVATE_KEY}`]
       }
   }
};
Create the Test File
Create a new file named MultiChainWallet.test.js in the test directory and add the following test cases:

 

// test/MultiChainWallet.test.js

 

// test/MockERC20.test.js

 

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("MockERC20", function () {
   let mockToken;
   let owner;
   let addr1;
   let addr2;
   beforeEach(async function () {
       const MockERC20 = await ethers.getContractFactory("MockERC20");
       [owner, addr1, addr2] = await ethers.getSigners();
       mockToken = await MockERC20.deploy("Mock Token", "MTK", owner.address, ethers.utils.parseEther("1000"));
       await mockToken.deployed();
   });
   describe("Deployment", function () {
       it("Should set the correct name and symbol", async function () {
           expect(await mockToken.name()).to.equal("Mock Token");
           expect(await mockToken.symbol()).to.equal("MTK");
       });
       it("Should assign the initial balance", async function () {
           const balance = await mockToken.balanceOf(owner.address);
           expect(balance).to.equal(ethers.utils.parseEther("1000"));
       });
   });
   describe("Transactions", function () {
       it("Should transfer tokens between accounts", async function () {
           await mockToken.transfer(addr1.address, ethers.utils.parseEther("100"));
           const addr1Balance = await mockToken.balanceOf(addr1.address);
           expect(addr1Balance).to.equal(ethers.utils.parseEther("100"));
       });
       it("Should approve tokens for spending", async function () {
           await mockToken.approve(addr1.address, ethers.utils.parseEther("50"));
           const allowance = await mockToken.allowance(owner.address, addr1.address);
           expect(allowance).to.equal(ethers.utils.parseEther("50"));
       });
       it("Should transfer tokens from one account to another", async function () {
           await mockToken.approve(addr1.address, ethers.utils.parseEther("50"));
           await mockToken.connect(addr1).transferFrom(owner.address, addr2.address, ethers.utils.parseEther("50"));
           const addr2Balance = await mockToken.balanceOf(addr2.address);
           expect(addr2Balance).to.equal(ethers.utils.parseEther("50"));
       });
   });
});

 

Also, Read | How to Build a Real-Time Wallet Tracker

 

Deploy the Contract

 

To deploy the contract, run the following command in your terminal:

 

npx hardhat run scripts/deploy.js --network <network_name>

 

Verify the Deployment:-


Once deployed, you should see the contract address in the terminal output. You can verify the deployment on Etherscan (for public networks) or through your local Hardhat node.

 

Overview of the Above Contract:-


Deposit Function: The wallet allows users to deposit Ether, with the balance being linked to a particular chain ID.
Withdraw Function: Users are able to take their remaining Ether balance for a certain chain out.  
Execute Function: Using a signature-based verification system, this function enables the owner to carry out transactions on other contracts.
Events: For tracking purposes, send out events for deposits, withdrawals, and completed transactions.


Explanation of the Tests


Deposit Tests: Tests that users can deposit Ether and that their balance is updated accordingly. Checks that the Deposited event is emitted.
 

Withdraw Tests: Ensures that users can withdraw their Ether. Validates that trying to withdraw more than the balance reverts the transaction. Checks that the Withdrawn event is emitted.


Execute Tests: Validates that the owner can successfully execute a transaction. Tests that an invalid signature reverts the transaction.


Interactions Across Chains: Cross-chain interactions are not specifically handled by this contract. You could utilise bridging mechanisms like Wormhole or LayerZero, oracles, to establish communication between various chains in order to do this.


Security: Whenever you work with different chains, make sure to audit your contracts and take into account possible attack routes.
 

Gas Efficiency: When building your functions, especially for cross-chain calls, keep gas expenses in mind.

 

Also, Check | Create an Externally Owned Wallet using Web3J and Spring Boot


Testing and Deployment:-
 

You can utilise test networks for the individual chains you wish to support together with frameworks like Hardhat or Truffle to deploy and test this contract.
 

Boost Functionality: Include extra features such as role-based access control, support for ERC20 tokens, and recovery methods.
 

Cross-Chain Communication: To move assets between chains, investigate and put into practice cross-chain protocols.
 

User Interface: Using Web3.js or Ethers.js frameworks, create a front-end interface for interacting with the wallet. This ought to provide you with a basic idea of how to construct a Solidity wallet that abstracts multiple chains of accounts. Be sure to modify and enlarge the code in accordance with the specifications of your project!


Monetary Benefits of Investing in Account Abstraction Wallet:-
 

Cost is a significant component of Account Abstraction wallet development. It is impacted by numerous factors that bring these apps to life. Let us spotlight these factors in detail:


Development Team Size
 

The expertise and experience of the development team affect the wallet cost. Hire a skilled team with Blockchain background and consider the cost of developers, designers, blockchain experts and security professionals.
 

Features and Complexity
 

The features integrated within the application have a direct influence on the cost. The charges of basic wallets are less, while the advanced ones escalate the cost.
 

Security Measures
 

The significance of powerful security mechanisms can't be understated. The higher the security, the higher the development charges. Make sure that the advanced security mechanisms are integrated, which is a significant investment but gives you peace of mind.
 

Legal and Compliance Costs
 

Addressing complaint measures involves legal consultations and ensuring that the application adheres to local and global regulations. These costs are included in the overall budget.

 

Also, Discover | How to Sign Ledger using Solana Wallet Adapter


Account Abstraction Wallets Drawbacks


Complexity: Compared to standard wallets, the architecture may be more intricate, which might increase the likelihood of errors or implementation flaws.
 

Experience of the User: Those who are only familiar with conventional wallets may find it difficult to grasp new ideas like transaction signature off-chain.
Difficulties with Onboarding: It might be difficult for novice users to set up and use these wallets efficiently.
 

Smart Contract Weaknesses: The usage of smart contracts exposes users to more risks, including those related to reentrancy attacks, vulnerabilities, and exploits that might result in financial loss.
 

Signature Management: Insecure implementation of off-chain signing techniques may result in compromised private keys.
 

Reliance on Oracles and Bridges: Multi-chain functionality often depends on external oracles and bridging services, which can introduce additional points of failure.
 

Potential Latency: Cross-chain transactions might be slower due to the need for confirmations and interactions with multiple networks.
 

KYC/AML Concerns: Implementing features like KYC for account abstraction wallets could complicate user privacy and lead to regulatory scrutiny.
Compliance Complexity: Ensuring compliance across multiple jurisdictions can be challenging and resource-intensive.
 

Interoperability Challenges: Different chains may have varying standards and functionalities, complicating interoperability and the overall user experience.
Limited Support: Not all decentralized applications (dApps) may support account abstraction wallets, limiting their usability.

 

If you are looking for assistance to build your blockchain-based project, connect with our skilled blockchain developers to get started. 

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

November 18, 2024 at 01:31 pm

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button

Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text