LogoLogo
  • WELCOME TO ALEPH ZERO
  • EXPLORE
    • About Aleph Zero
    • AlephBFT Consensus
    • The Economy of Aleph Zero
    • Where to Buy AZERO
    • Decentralized Governance on Aleph Zero
    • Ecosystem
    • Aleph Zero Foundation Treasury Management
    • Community
    • Glossary
    • Audit & Research Papers
  • USE
    • Wallets
    • Explorer
    • Ledger
    • Telegram Notifications
    • Aleph Zero Signer
      • General introduction
      • What does Signer do?
      • What are Sub-accounts and Sub-account paths?
      • Why is it critical to store your Secret Phrase in a safe place?
      • How to forget and restore accounts?
      • What are Networks?
      • What are Trusted apps?
    • Dashboard
      • Dashboard basics
      • Overview
    • Stake
      • Staking Basics
      • Staking Menu Overview
      • How to Start Staking with the Aleph Zero Dashboard
      • How to Start Staking With the Developer Wallet
      • How to start staking using Ledger hardware wallet
      • How to Change Nominations
      • How to Stop Staking
      • Staking Rewards
      • Validators
      • Commission and Foundation Nodes
      • Proxy Accounts
    • Validate
      • Validating Overview
      • Hardware requirements
      • Running an Aleph Node on Testnet
        • Downloading and running the node
        • Verifying your setup
        • Customizing your setup
        • Building and running from source [advanced]
          • Building from source
          • Set environment variables
          • Download DB snapshot
          • Running the binary
        • Appendix: Ports, addresses, validators, and archivists
      • Running an Aleph Node on Mainnet
        • Running the node
        • Building and running from source [advanced]
      • Setting your identity
      • Making the node validate
      • Securing your validator
      • Troubleshooting
      • Elections and Rewards Math
      • Testnet Validator Airdrop
      • Foundation Nomination Program
    • Using the EVM-layer
    • Governance
      • Token
      • Multisig Accounts
  • BUILD
    • Aleph Zero smart contracts basics
      • Setting up a Testnet account
      • Installing required tools
      • Creating your first contract
      • Deploying your contract to Aleph Zero Testnet
      • Extending your contract
    • Cross contract calls
      • Using references
      • Using dynamic calls
    • Migrating from Solidity
    • Writing e2e tests with ink-wrapper
    • Aleph Zero Signer integration
    • Front-end app: smart contract interaction
    • Security Course by Kudelski Security
      • ink! Developers Security Guideline
      • Lesson 1 - Getting started with ink!
      • Lesson 2 - Threat Assessment
      • Lesson 3 - Integer Overflow
      • Lesson 4 - Signed-integer
      • Lesson 5 - Role-Based Access Control
      • Lesson 6 - Address Validation
      • Lesson 7 - Smart Contract Control
    • Development on EVM-layer
  • PROTOCOL DETAILS
    • Shielder
      • Overview
      • Design against Bad Actors
      • Preliminaries - ZK-relations
      • Notes and Accounts
      • ZK-ID and Registrars
      • Anonymity Revokers
      • PoW Anonymity Revoking
      • Relayers
      • Deterministic Secret Management
      • SNARK-friendly Symmetric Encryption
      • SNARK-friendly Asymmetric Encryption
      • Cryptography
      • Token shortlist
      • User Wallet
      • Versioning
      • PoC
      • Version 0.1.0
      • Version 0.2.0
    • Common DEX
      • Common Whitepaper - Differences
      • Dutch Auctions
  • FAQ
  • Tutorials
    • Withdrawing coins from exchanges
      • How to withdraw your AZERO coins from KuCoin
      • How to withdraw your AZERO coins from MEXC Global
      • How to withdraw your AZERO coins from HTX
  • Setting up or restoring a wallet
    • How to set up or recover your AZERO account using Aleph Zero Signer
    • How to set up or recover your AZERO account using the official mainnet web wallet
    • How to set up or recover your AZERO account using Nova Wallet
    • How to set up or recover your AZERO account using SubWallet
    • How to set up or recover your AZERO account using Talisman
  • Staking
    • How to stake via a direct nomination using the Aleph Zero Dashboard
    • How to stake via a nomination pool using the Aleph Zero Dashboard
    • How to destroy a nomination pool via the Aleph Zero Dashboard
Powered by GitBook
On this page
  • Introduction
  • Setup
  • Generating contract wrappers
  • Using the wrappers

Was this helpful?

  1. BUILD

Writing e2e tests with ink-wrapper

PreviousMigrating from SolidityNextAleph Zero Signer integration

Last updated 1 year ago

Was this helpful?

Introduction

While this tutorial focuses on writing e2e (end-to-end) tests, the same approach can be utilized to call your contracts from any backend application written in Rust.

The default tooling for calling contracts provided by and similar tools relies on runtime checks on input data and received responses. In order to provide compile-time checks as well as easy access to code completion and documentation for contracts, we've developed ink-wrapper. Simply put, it's a tool that generates a bunch of strongly-typed wrapper code around submitting contract-related transactions. You can include this generated code in your project and use it, instead of calling primitives like subxt directly.

Setup

To get started you will need to install ink-wrapper itself (this tutorial uses 0.4.1 so please install the same version to follow along):

cargo install ink-wrapper --locked --force --version 0.4.1

In this guide we will write e2e tests for an example PSP22 contract that we use to test ink-wrapper itself. You will need to run a chain for testing and use the tooling provided in the repo to compile contracts. With that:

git clone git@github.com:Cardinal-Cryptography/ink-wrapper.git

Finally, let's create a new project that will house our tests:

cargo new --lib psp22-tests

Generating contract wrappers

The ink-wrapper takes a .json metadata file generated while compiling the contract and produces a Rust file based on that. First, setup a node with the contract in question compiled and deployed. This will also run ink-wrapper's own tests at the end - just ignore that.

cd ink-wrapper
make all-dockerized

Then, invoke ink-wrapper on the produced .json metadata file and put the results in the src directory in the psp22-tests project:

ink-wrapper -m test-project/psp22_contract/target/ink/psp22_contract.json \
  | rustfmt --edition 2021 > ../psp22-tests/src/psp22_contract.rs

Notice that we're piping the output of ink-wrapper through rustfmt - the output is not guaranteed to be formatted, so in order to commit nicely formatted code into your repo, it's recommended to use this method when regenerating the wrapper files.

Using the wrappers

Now, let's move to the psp22-tests project. We will need to add some dependencies to make the wrappers compile:

Cargo.toml
# ...

[dependencies]
ink-wrapper-types = "0.4.0"
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
ink_primitives = "4.0.1"
aleph_client = "3.0.0"
async-trait = "0.1.68"

# These are a couple dependencies we will use to write our tests
tokio = { version = "1.25.0", features = ["macros"] }
rand = "0.8.5"
anyhow = "1.0.71"

As well as switch to nightly by putting the following in rust-toolchain.toml:

rust-toolchain.toml
[toolchain]
channel = "nightly-2023-04-20"
components = ["rustfmt", "rust-src", "clippy"]

Attach the module produced by ink-wrapper to psp22-tests:

lib.rs
mod psp22_contract;

Now add the following test code:

lib.rs
#[cfg(test)]
mod tests {
    use crate::psp22_contract;
    // The PSP22-specific methods of the contract are hidden behind a trait.
    // This will only happen for contract methods with names like "PSP22::transfer".
    // Other contract methods will just be available on the contract instance without
    // any extra trait.
    use crate::psp22_contract::PSP22 as _;
    use aleph_client::keypair_from_string;
    use aleph_client::Connection;
    use aleph_client::SignedConnection;
    use anyhow::Result;
    // This is just a convenience helper for converting any AsRef<[u8; 32]> to
    // ink_primitives::AccountId - the datatype used by the generated code to
    // represent account ids.
    use ink_wrapper_types::util::ToAccountId as _;
    use rand::RngCore as _;

    #[tokio::test]
    async fn it_works() -> Result<()> {
        // Connect to the node launched earlier.
        let conn = Connection::new("ws://localhost:9944").await;
        let conn = SignedConnection::from_connection(conn, keypair_from_string("//Alice"));
        let bob = keypair_from_string("//Bob");

        // We're using a random salt here so that each test run is independent.
        let mut salt = vec![0; 32];
        rand::thread_rng().fill_bytes(&mut salt);
        let total_supply = 1000;
        // Constructors take a connection, the salt, and any arguments
        // the actual constructor requires afterwards.
        let contract = psp22_contract::Instance::new(&conn, salt, total_supply).await?;

        // A mutating method takes a signed connection and any arguments afterwards.
        contract
            .transfer(&conn, bob.account_id().to_account_id(), 100, vec![])
            .await?;

        // A reader method takes a connection (may be unsigned) and any arguments afterwards.
        let balance = contract
            .balance_of(&conn, bob.account_id().to_account_id())
            .await??;
        assert_eq!(balance, 100);

        Ok(())
    }
}

With that, you should be able to run cargo test and have it pass with 1 test run!

subxt
docker