👨‍đŸ’ģink! Developers' Guide

This page aims at giving a technical approach to interacting with our smart contracts from other smart contracts using ink! to enable for adding liquidity, removing liquidity, trading, pricing etc.

The table below shows the core smart contracts on our protocol and their associated addresses.

Note that the examples in this guide are written in and are compatible with ink! 4.x.x.

#Smart contractAddress

1

Router

5Dh27CkPzDdxFVZixzDV3az7y5UGvnJ1ZrqtkbqJBBMh6v1D

2

Token Lock/Burn

5DaBmQYeXMiHLqivpC31qjrGJwhDDpgSaiwd2LnAEtZd2MwV

This section will also let you know about the selectors associated with the callable contracts and sample code to demonstrate how cross-contract calls can be made.

There are a number of selectors that will be used throughout this chapter for each method that will be called on the protocol across the various smart contracts that make all the DeFi features possible. They are as follows:

Router

There are four selectors (methods) associated with the router smart contract that will assist throughout this guide in terms of cross-contract calls. They are:

get_associated_azero_pool()

This method takes one argument (AccountId), which is the PSP22 token contract address paired with AZERO and returns the address of the pool (AccountId) if it exists, else the zero address. Its selector is an 8-byte unsigned integer (u8) array: [0x61, 0x1a, 0x30, 0x52].

get_associated_psp22_pool()

This method takes two arguments (AccountId), which are the PSP22 contract addresses associated with the intended PSP22/PSP22 trading pair and returns the address of the pool (AccountId) if it exists, else the zero address. Its selector is an 8-byte unsigned integer (u8) array: [0xa3, 0xc6, 0x28, 0x62].

create_azero_liquidity()

This method takes two arguments: the PSP22 address (AccountId) you want to pair against AZERO in the AZERO/PSP22 pair to be created and an integer (u64) to be used as the salt for generating its contract address. It returns the just-created pair contract address. The selector is [0x23, 0xc6, 0xff, 0xad].

create_psp22_liquidity()

This method takes three arguments, which are the PSP22 addresses (AccountId) of the tokens for which you want to create the PSP22/PSP22 trading pair and an integer (u64) to be used as the salt for generating its contract address. It returns the just-created pair contract address. The selector is [0x50, 0x49, 0x5b, 0x96].

AZERO trading pair

There are four selectors (methods) associated with the AZERO trading pair smart contract that will assist throughout this guide in terms of cross-contract calls. They are:

get_tokens()

This is used to get the tokens associated with the pair in the order of integration into the pool. For the AZERO/PSP22 pools, it returns an array (Vec<AccountId>) of length 1 which stores the associated PSP22 token contract address in the pool. It doesn't take any argument. The selector for this method is [0xba, 0x04, 0x52, 0x71].

get_token_balances()

This returns an array (Vec<u128>) of length 2, that represents the balance in PSP22 tokens and AZERO respectively. It doesn't take any argument. The selector for this method is [0x26, 0xe4, 0x81, 0x45].

get_a0_amount_out_without_deduction()

This method takes one argument (u128), which is the amount of PSP22 you want to use as an input to get an estimated amount of AZERO in return as u128. For instance, if 1 AZERO gives 10 tokens and the method is called with 10 tokens as an argument, the output of this method will be 1. It returns the amount out as Balance. The selector for this method is [0xbd, 0x77, 0x15, 0xef].

get_psp22_amount_out_without_deduction()

This method takes one argument (u128), which is the amount of AZERO you want to use as an input to get an estimated amount of PSP22 in return as u128. For instance, if 1 token gives 10 AZERO and the method is called with 10 AZERO as an argument, the output of this method will be 1. It returns the amount out as Balance. The selector for this method is [0x42, 0xf5, 0xd9, 0x57].

swap_psp22()

This method is used to swap PSP22 tokens in return for AZERO. It takes three arguments: the amount of PSP22 tokens to be swapped against AZERO, the expected amount of AZERO in return for the given tokens and the slippage tolerance in percentage. The slippage is represented as a percentage multiplied by 10 to the power of 12; for example, 10% will be written as 10 * 1e12. The selector for this method is [0x7a, 0xfc, 0x21, 0x94].

swap_a0()

This method is used to swap AZERO tokens in return for PSP22 tokens. It is a payable method that takes two arguments: the expected amount of PSP22 tokens in return for the given AZERO and the slippage tolerance in percentage. The slippage is represented as a percentage multiplied by 10 to the power of 12; for example, 10% will be written as 10 * 1e12. The transferred value sent to the method should be the amount of AZERO expected to be swapped for PSP22 tokens. The selector for this method is [0x92, 0xb0, 0xce, 0x2b].

add_liquidity()

This method is payable and is used to add liquidity to any given AZERO/PSP22 pair. It takes two arguments: the PSP22 token deposit amount (u128) and the slippage (u128; as defined earlier). It also uses the transferred value to determine the amount of AZERO to deposit alongside the PSP22 tokens. The user gets shares in liquidity tokens in return. The selector for this method is [0x26, 0x4c, 0xd0, 0x4b].

remove_liquidity()

This method is payable and is used to remove liquidity from any given AZERO/PSP22 pair. It takes one argument, which is the number of liquidity tokens (shares) to exchange for their share of both tokens in the pool (u128); it must be less than or equal to the balance of the liquidity pool tokens. The selector for this method is [0xbd, 0xd1, 0x6b, 0xfa].

PSP22 trading pair

There are four selectors (methods) associated with the PSP22 trading pair smart contract that will assist throughout this guide in terms of cross-contract calls. They are:

get_tokens()

This is used to get the tokens associated with the pair in the order of integration into the pool. For the PSP22/PSP22 pools, it returns an array (Vec<AccountId>) of length 2 which stores the associated PSP22 token contract addresses in the pool ([first PSP22 token, second PSP22 token]). It doesn't take any argument. The selector for this method is [0xba, 0x04, 0x52, 0x71].

get_token_balances()

This returns an array (Vec<u128>) of length 2, that represents the balance in PSP22 A tokens and PSP22 B tokens respectively. It doesn't take any argument. The selector for this method is [0x26, 0xe4, 0x81, 0x45].

get_psp22_b_amount_out_without_deduction()

This method takes one argument (u128), which is the amount of the first PSP22 tokens you want to use as input to get an estimated amount of the second PSP22 tokens in return as u128. For instance, if 1 PSP22B gives 10 PSP22A and the method is called with 10 PSP22A as an argument, the output of this method will be 1. It returns the amount out as Balance. The selector for this method is [0x71, 0xe9, 0xaf, 0x3a].

get_psp22_a_amount_out_without_deduction()

This method takes one argument (u128), which is the amount of the second PSP22 tokens you want to use as input to get an estimated amount of the first PSP22 tokens in return as u128. For instance, if 1 PSP22A gives 10 PSP22B and the method is called with 10 PSP22B as an argument, the output of this method will be 1. It returns the amount out as Balance. The selector for this method is [0x7d, 0x22, 0x96, 0x8c].

swap_psp22_token_a()

This method is used to swap the first PSP22 tokens in return for the second PSP22 tokens. It takes three arguments: the amount of the first PSP22 tokens to be swapped against the second PSP22 tokens, the expected amount of the second PSP22 tokens in return for the given tokens and the slippage tolerance in percentage. The slippage is represented as a percentage multiplied by 10 to the power of 12; for example, 10% will be written as 10 * 1e12. The selector for this method is [0x45, 0xc4, 0x33, 0x39].

swap_psp22_token_b()

This method is used to swap the second PSP22 tokens in return for the first PSP22 tokens. It takes three arguments: the amount of the second PSP22 tokens to be swapped against the first PSP22 tokens, the expected amount of the first PSP22 tokens in return for the given tokens and the slippage tolerance in percentage. The slippage is represented as a percentage multiplied by 10 to the power of 12; for example, 10% will be written as 10 * 1e12. The selector for this method is [0xd9, 0xa2, 0x28, 0xb3].

add_liquidity()

This method is payable and is used to add liquidity to any given PSP22/PSP22 pair. It takes three arguments: the first PSP22 token deposit amount (u128), the second PSP22 token deposit amount (u128) and the slippage (u128; as defined earlier). The user gets shares in liquidity tokens in return. The selector for this method is [0x26, 0x4c, 0xd0, 0x4b].

remove_liquidity()

This method is payable and is used to remove liquidity from any given PSP22/PSP22 pair. It takes one argument, which is the number of liquidity tokens (shares) to exchange for their share of both tokens (AZERO and PSP22 tokens) in the pool (u128); it must be less than or equal to the balance of the liquidity pool tokens. The selector for this method is [0xbd, 0xd1, 0x6b, 0xfa].

Token Lock/Burn

There are eight selectors (methods) associated with the Token Lock/Burn smart contract that will assist throughout this guide in terms of cross-contract calls. They are:

get_fees()

This method doesn't take any arguments but returns the amount of ANS required to be paid as fees to perform locks on the protocol at any given point in time (it varies from time to time). It returns u128 (Balance). The selector for this method is [0x26, 0xc0, 0x66, 0x84].

get_unlock_time()

This method returns the unlock time for any given token on the protocol by a given account (AccountId). It takes two arguments: the wallet address (AccountId) and the contract address (AccountId) of the token. The return type is u64 (the block timestamp in milliseconds in which the given token is meant to be unlocked). The selector for this method is [0xae, 0xce, 0x11, 0x44].

get_locked_tokens()

This method returns the list (array) of tokens locked by a given account (AccountId). It takes one single argument as the wallet address of the intended account (AccountId). The return type is Vec<AccountId>. The method selector for this method is [0x32, 0x86, 0x45, 0x2f].

get_amount_locked()

This method returns the total amount of a given token locked by a given account (AccountId). It takes two arguments: the wallet address (AccountId) and the contract address (AccountId) of the token to check. The return type is u128 (Balance). The selector for this method is [0xf3, 0xa9, 0x47, 0x20].

get_burned_tokens()

This method returns the list (array) of tokens burnt by a given account (AccountId). It takes one single argument as the wallet address of the intended account (AccountId). The return type is Vec<AccountId>. The method selector for this method is [0xb3, 0x78, 0xeb, 0x7f].

get_amount_burned()

This method returns the total amount of a given token burnt by a given account (AccountId). It takes two arguments: the wallet address (AccountId) and the contract address (AccountId) of the token to check. The return type is u128 (Balance). The selector for this method is [0x78, 0x7e, 0x12, 0xf2].

burn_tokens()

This is used to burn a given amount of tokens from the caller. It takes two arguments: the contract address of the token to burn (AccountId) and the number of tokens to burn (u128). The selector for this method is [0x0a, 0x1a, 0xb7, 0xf5].

lock_tokens()

This is used to lock a given amount of tokens from the caller for a given duration of time. It takes three arguments: the contract address of the token to lock (AccountId), the number of tokens to lock (u128) and the total duration to lock the tokens in milliseconds (u64). The selector for this method is [0x56, 0x7c, 0x1e, 0xae].

unlock_tokens()

This is used to unlock previously locked tokens and send them to the caller on the condition that the unlock date has elapsed. It takes the contract address of the token to unlock (AccountId) as its only argument. The selector for this method is [0x02, 0x0c, 0xbb, 0x8e].

Note that for some of these methods, you'll have to set the call flags of the CallBuilder to allow_reentrancy depending on how you're using them.

A number of these methods have to work hand in hand in order to function as intended (most especially approvals to the designated contracts to spend a number of tokens from their contract balances as the case may be).

This guide assumes that you're familiar with Openbrush by Supercolony and will stick solely to the traits and extensions offered by the toolkit.

The core method that will be used in both scenarios is that which will be used to approve the tokens for any of the given contracts.

The PSP22Ref *Ref will be used from Openbrush and can be imported thus for PSP22 token interactions:

use openbrush::contracts::traits::psp22::PSP22Ref;

We will first create a method get_allowance() to get the spendable amount of a given token for a spender (one of our smart contracts in this case). This method would take two arguments: the contract address of the intended token (AccountId) and the spender (AccountId) and return the amount in u128 (Balance) spendable (or allowance) by the provided spender. It could be implemented thus:

fn get_allowance(&self, token:AccountId, spender:AccountId) -> Balance {
    let allowance = PSP22Ref::allowance(&token, self.env().account_id(), spender);
    allowance
}

We can then create a method named approve_tokens() which will take three arguments: the spender (contract to be called for psp22::transferFrom) contract address (AccountId), the token contract address (AccountId) and the amount to offer as a spending allowance (u128). It will return a boolean value to indicate if it goes as intended or not.

fn approve_tokens(&self, token:AccountId, spender:AccountId, amount:Balance) -> bool {
    PSP22Ref::approve(&token, spender, amount).unwrap_or_else(|error| {
        return false;
    });
    return true;
}

The methods above will be used throughout this series to achieve a lot of other interactions associated with cross-contract calls to the smart contracts on our protocol.

To make complete smart contract calls, you need to import the following:

use ink::{
    env::{
        call::{build_call, Call, ExecutionInput, Selector},
        DefaultEnvironment, Error as InkEnvError,
        CallFlags
    },
    LangError,
};

Last updated