uniswapv3 solidity 合约迁移到ink!

  • cloudweisz
  • 更新于 2022-06-06 02:05
  • 阅读 2662

substrate,ink!,solidity,openbrush,patract

1. 概述

  1. ink!合约是波卡官方开发的一种wasm合约。使得普通用户可以在波卡的链上发布自己的应用而创建的一种合约语言。该合约主要使用rust语言进行编译成wasm后发布到链上。
  2. 波卡对ink!有一个专门的tutorials,和一个ink!的文档链接专门介绍ink!。感兴趣的可以去看看。

    2. 与solidity的关系

  3. ink!基本上是完全参考solidity进行设计的。基本的四要素(存储,消息,事件,事务)都是一致的。所以单个合约来说和solidity的结构以及用法是非常相似的。但是由于rust本身的特性(面向过程的语言在面向对象变成主要使用组合的方式),故和solidity这种传统的面向对象的语言还是有很多不同的地方,特别是在多合约方面。后续的文章会陆续讲解solidity和ink!的一些不同的地方以及如何去解决这些问题。
  4. 在代码复用和代码规范方面,因为solidity有openzeppelin,从而使得solidity的使用得到了很大的规范。ink!方面原来有一个patract做了很多ink!规范的工作,也包含了很多规范。但是现在除了redspot这个测试项目外,其余的项目基本上已经不做维护了。
  5. 现在我做ink!合约用到的主要的框架还是openbrush。里面有erc20,erc721,erc1155等对应的标准合约,可以直接将这些合约应用到我们自己的合约上,从而实现继承的功能。例如:
impl PSP34 for PositionMangerContract {}

3. 我们也可以定制自己的父合约,然后应用到我们的合约上。例如常用的WETH9合约。

WETH9合约是可以让你的native token 和指定的wrap eth token进行一比一的兑换。在swap中进行native token 和其他token进行互换的时候,可以先把native token 兑换成wraptoken,从而进行swap。 可以先实现weth9的接口。接口实现如下:

use brush::contracts::psp22::PSP22Error;
/// base info for PSP34
#[brush::wrapper]
pub type Weth9Ref = dyn Weth9;

#[brush::trait_definition]
pub trait Weth9 {
    /// @notice Deposit ether to get wrapped ether
    #[ink(message,payable)]
    fn deposit(&mut self)->Result<(),PSP22Error>;

    /// @notice Withdraw wrapped ether to get ether
    #[ink(message)]
    fn withdraw(&mut self,amount:u128)->Result<(),PSP22Error>;
}

首先定义weth9需要使用的接口,deposit是抵押接口,给这个接口转入native token,就可以得到对应的weth9的token。 第二个接口是赎回,使用weth9可以赎回相同数量的native token。 这个trait需要使用#[brush::trait_definition]过程宏进行标记。

定义好接口后,我们就可以实现这个trait了。

use brush::contracts::psp22::{PSP22Storage, PSP22Internal, PSP22Error};
// use brush::traits::Flush;
use ink_env::{DefaultEnvironment};
pub use crate::traits::periphery::weth9::*;
use ink_prelude::string::String;

impl<T: PSP22Storage + PSP22Internal> Weth9 for T {

    /// @notice Deposit ether to get wrapped ether
    default fn deposit(&mut self)->Result<(),PSP22Error>{
        let transfer_value = ink_env::transferred_value::<DefaultEnvironment>();
        let caller = ink_env::caller::<DefaultEnvironment>();
        let result = PSP22Internal::_mint(self, caller, transfer_value);
        result
    }

    /// @notice Withdraw wrapped ether to get ether
    default fn withdraw(&mut self,amount:u128)->Result<(),PSP22Error>{
        let caller = ink_env::caller::<DefaultEnvironment>();
        let balance = PSP22Internal::_balance_of(self, &caller);
        assert!(balance >= amount,"balance not enough!");
        PSP22Internal::_burn_from(self, caller, amount)?;
        let result = ink_env::transfer::<DefaultEnvironment>(caller, amount);
        result.map_err(|_|PSP22Error::Custom(String::from("transfer error!")))
    }

}

由于weth9需要进行token的转账,所以T要满足PSP22StoragePSP22Internal这两个trait。 然后对T进行default的实现。这样在真正的weth9的合约中直接继承trait的默认方法。 impl Weth9 for Weth9Contract{}

当然weth9的合约数据要满足PSP22DataPSP22MetadataData的数据结构,从而让合约满足PSP22Storage的trait。具体代码如下:

#![cfg_attr(not(feature = "std"), no_std)]
#![feature(min_specialization)]

/// This is a simple `PSP-22` which will be used as a stable coin and a collateral token in our lending contract
#[brush::contract]
pub mod weth9 {
    use brush::contracts::psp22::extensions::metadata::*;
    use ink_prelude::string::String;
    // use lending_project::traits::stable_coin::*;
    use ink_storage::traits::SpreadAllocate;
    use crabswap::impls::weth9::*;

    /// Define the storage for PSP22 data and Metadata data
    #[ink(storage)]
    #[derive(Default, SpreadAllocate, PSP22Storage, PSP22MetadataStorage)]
    pub struct Weth9Contract {
        #[PSP22StorageField]
        psp22: PSP22Data,
        #[PSP22MetadataStorageField]
        metadata: PSP22MetadataData,
    }

    /// implement PSP22 Trait for our coin
    impl PSP22 for Weth9Contract {}

    /// implement PSP22Metadata Trait for our coin
    impl PSP22Metadata for Weth9Contract {}

    impl Weth9 for Weth9Contract{}

    // It forces the compiler to check that you implemented all super traits
    // impl StableCoin for StableCoinContract {}

    impl Weth9Contract {
        /// constructor with name and symbol
        #[ink(constructor)]
        pub fn new(name: Option<String>, symbol: Option<String>) -> Self {
            ink_lang::codegen::initialize_contract(|instance: &mut Weth9Contract| {
                instance.metadata.name = name;
                instance.metadata.symbol = symbol;
                instance.metadata.decimals = 12;
                let total_supply = 0 * 10_u128.pow(instance.metadata.decimals.into()); //不可手工增发,
                assert!(instance._mint(instance.env().caller(), total_supply).is_ok());
            })
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;
        use ink_env::DefaultEnvironment;
        use ink_lang as ink;

        fn default_accounts(
        ) -> ink_env::test::DefaultAccounts<ink_env::DefaultEnvironment> {
            ink_env::test::default_accounts::<Environment>()
        }

        fn set_next_caller(caller: AccountId) {
            ink_env::test::set_caller::<Environment>(caller);
        }

        #[ink::test]
        fn register_works() {
            let default_accounts = default_accounts();

            set_next_caller(default_accounts.alice);
            let weth9_contract = Weth9Contract::new(Some(String::from("weth9")),Some(String::from("weth91")));
            assert_eq!(weth9_contract.metadata.name,Some(String::from("weth9")));
        }

        #[ink::test]
        fn test_deposit() {
            let accounts = default_accounts();
            set_next_caller(accounts.alice);
            let mut weth9_contract = Weth9Contract::new(Some(String::from("weth9")),Some(String::from("weth91")));
            ink_env::test::set_value_transferred::<ink_env::DefaultEnvironment>(1000);
            assert_eq!(weth9_contract.deposit(),Ok(()));
            let balance = weth9_contract.balance_of(accounts.alice);
            assert_eq!(balance,1000u128,"balance not correct!");
            let contract_account_id = ink_env::test::callee::<ink_env::DefaultEnvironment>();
            let native_balance:Balance = ink_env::test::get_account_balance::<ink_env::DefaultEnvironment>(contract_account_id).unwrap();
            // assert_eq!(native_balance,1000u128,"native balance not correct!");
        }

        #[ink::test]
        fn test_withdraw() {
            let accounts = default_accounts();
            set_next_caller(accounts.alice);
            let mut weth9_contract = Weth9Contract::new(Some(String::from("weth9")),Some(String::from("weth91")));
            ink_env::test::set_value_transferred::<ink_env::DefaultEnvironment>(1000);
            assert_eq!(weth9_contract.deposit(),Ok(()));
            let balance = weth9_contract.balance_of(accounts.alice);
            assert_eq!(balance,1000u128,"balance not correct!");
            assert_eq!(weth9_contract.withdraw(800u128),Ok(()));
            let balance = weth9_contract.balance_of(accounts.alice);
            assert_eq!(balance,1000u128-800u128,"balance not correct!");
        }

        // #[ink::test]
        // fn transfer_works() {
        //     let accounts = default_accounts();
        //     let name = Hash::from([0x99; 32]);

        //     set_next_caller(accounts.alice);

        //     let mut contract = DomainNameService::new();
        //     assert_eq!(contract.register(name), Ok(()));

        //     // Test transfer of owner.
        //     assert_eq!(contract.transfer(name, accounts.bob), Ok(()));

        //     // Owner is bob, alice `set_address` should fail.
        //     assert_eq!(
        //         contract.set_address(name, accounts.bob),
        //         Err(Error::CallerIsNotOwner)
        //     );

        //     set_next_caller(accounts.bob);
        //     // Now owner is bob, `set_address` should be successful.
        //     assert_eq!(contract.set_address(name, accounts.bob), Ok(()));
        //     assert_eq!(contract.get_address(name), accounts.bob);
        // }
    }
}

另外大量的libs库可以直接使用rust语言进行编写。 写完合约后可以使用PATRACT公司的redspot的js框架进行测试。

点赞 2
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
cloudweisz
cloudweisz
十多年的java金融领域经验。后转型做过以太坊的DEFI项目。现专注于web3领域的区块链和合约。