Create Pair
If you are starting the tutorial from here, please check out this branch and open it in your IDE.
1. Add Create Pair to Factory Trait
We will implement the createPair function of the Factory contract.
In the ./logics/traits/factory.rs file, add the create_pair function to the Factory trait, as well as the internal child function _instantiate_pair that will need to be implemented in the contract crate.
The reason why we need an internal _instantiate_pair function here is because the instantiate builder is not part of the #[openbrush::wrapper]
, so we will need to use the one from ink! by importing the Pair contract as an ink-as-dependancy
.
The create_pair message function returns the address of the instantiated Pair contract.
The function that emits a create_pair event will also have to be implemented in the contract:
pub trait Factory {
...
#[ink(message)]
fn create_pair(
&mut self,
token_a: AccountId,
token_b: AccountId,
) -> Result<AccountId, FactoryError>;
fn _instantiate_pair(&mut self, salt_bytes: &[u8]) -> Result<AccountId, FactoryError>;
...
fn _emit_create_pair_event(
&self,
_token_0: AccountId,
_token_1: AccountId,
_pair: AccountId,
_pair_len: u64,
);
}
2. Implement Create Pair
In the ./logics/impls/factory/factory.rs file, let's implement the create_pair function body:
1. Checks that addresses are not identical
AccountId derives Eq
trait, so comparison operators can be used:
impl<T: Storage<data::Data>> Factory for T {
...
fn create_pair(
&mut self,
token_a: AccountId,
token_b: AccountId,
) -> Result<AccountId, FactoryError> {
if token_a == token_b {
return Err(FactoryError::IdenticalAddresses)
}
}
}
2. Order The Tuple
let token_pair = if token_a < token_b {
(token_a, token_b)
} else {
(token_b, token_a)
};
3. Check if the First Tuple Address is not the ZERO_ADDRESS
if token_pair.0 == ZERO_ADDRESS.into() {
return Err(FactoryError::ZeroAddress)
}
4. Instantiate the Pair Contract
The generate_address function in pallet_contracts
is akin to the formula of ETH's CREATE2 opcode. There is no CREATE equivalent because CREATE2 is strictly more powerful. Formula: hash(deploying_address ++ code_hash ++ salt)
Instantiation of a contract will define its own contract address by using the concatenated hash of:
- salt (in bytes)
- address of deployer
- code_hash
As the code_hash
and deployer
(Factory contract address) values will be unchanged during each call, the salt_bytes
value must be unique for each call. As the Factory contract will instantiate a unique Pair contract for each pair, we will hash over the token address to produce a unique salt:
let salt = Self::env().hash_encoded::<Blake2x256, _>(&token_pair);
let pair_contract = self._instantiate_pair(salt.as_ref())?;
5. Initialize Pair
PairRef::initialize(&pair_contract, token_pair.0, token_pair.1)?;