<Let's Move>我的SUI Move挑战合约——【overmind】Suiss Bank

  • rzexin
  • 更新于 2024-03-29 10:32
  • 阅读 226

我的SUI Move挑战合约——【overmind】Suiss Bank

我的SUI Move挑战合约——【overmind】Suiss Bank

1 挑战说明

1.1 功能介绍

(1)挑战地址

https://overmind.xyz/quests/suiss-bank

(2)挑战说明

Suiss Bank是一个简单的借贷协议。

该挑战是一个该简单的借贷协议的一部分。该模块为用户提供存款和提取抵押品、借入和偿还代币以及计算其借贷健康因子的功能。

(3)需完成功能

  • 存款(deposit:让贷款人把抵押品存入流动资金池
  • 提款(withdraw:让贷款人提取抵押品
  • 借(borrow:从借贷池中借出代币
  • 还(repay:借款人偿还债务
  • 计算健康因子(calculate_health_factor:计算用户债务与抵押品比率的健康因素

(4)健康因子计算方法

https://naviprotocol.gitbook.io/navi-protocol-docs/getting-started/liquidations

image.png

https://naviprotocol.gitbook.io/navi-protocol-docs/protocol-parameters/lending-and-borrowing

image.png

2 合约开发

2.1 完整合约地址

https://github.com/howtosui/my_quest_overmind_suiss_bank/blob/main/sources/lender.move

2.2 数据结构说明

(1)管理员Cap对象

合约创建者将持有该对象,用于创建池子。

    struct AdminCap has key, store {
        id: UID,
    }

(2)协议状态对象

该对象在合约创建时创建,是全局共享对象。

成员变量说明:

  • number_of_pools:池子数量
  • users:协议使用用户及其数据
    struct ProtocolState has key {
        id: UID, 
        number_of_pools: u64, // The number of pools in the protocol. Default is 0.
        users: Table&lt;address, UserData> // All user data of the protocol.
    }

(3)池子对象

池子中包括资产编号及其资产余额。

成员变量说明:

  • asset_number:资产编号
  • reserve:资产余额
    /*
        This is the pool resource. It contains the asset number of the pool, and the reserve of the pool.
        When a pool is created, it should be shared globally.
    */
    struct Pool&lt;phantom CoinType> has key {
        id: UID, 
        /* 
            The asset number of the pool. This aligns with the index of collateral and borrow amounts in 
            the user data. This is also used to fetch the price and decimal precision of the coin from
            the price feed with the dummy_oracle::get_price_and_decimals function.
        */
        asset_number: u64, 
        /*
            The reserve of the pool. This is the total amount of the coin in the pool that are 
            available for borrowing or withdrawing.
        */
        reserve: Balance&lt;CoinType>
    }

(4)用户数据对象

该对象是用户数据对象,存储了抵押和借出的资产数量。

成员变量说明:

  • collateral_amount:抵押资产数量
  • borrowed_amount:借出资产数量
    /* 
        This is the user data resource. It contains the collateral and borrowed amounts of the user.
    */
    struct UserData has store {
        /* 
            The amount of collateral the user has in each pool. the index of the collateral amount
            aligns with the asset number of the pool.
        */
        collateral_amount: Table&lt;u64, u64>, 
        /* 
            The amount of coins the user has borrowed in each pool. the index of the borrowed amount
            aligns with the asset number of the pool.
        */
        borrowed_amount: Table&lt;u64, u64>,
    }

2.3 接口说明

(1)初始化函数(init

在初始化函数中将执行:

  • 创建AdminCap对象,并转发给合约部署者
  • 创建全局共享的协议状态对象(ProtocolState
    /*
        Initializes the protocol by creating the admin capability and the protocol state.
    */
    fun init(ctx: &mut TxContext) {
        let sender = tx_context::sender(ctx);

        transfer::transfer(AdminCap {
            id: object::new(ctx)
        }, sender);

        let users = table::new&lt;address, UserData>(ctx);

        transfer::share_object(ProtocolState{
            id: object::new(ctx),
            number_of_pools: 0,
            users,
        });
    }

(2)创建池子(create_pool

  • AdminCap的持有人可以创建指定代币的共享对象池子(Pool),池子的资产编号即协议状态对象中的当前池子数
  • 并将协议状态对象的池子数量变量增加
    /*
        Creates a new pool for a new coin type. This function can only be called by the admin.
    */
    public fun create_pool&lt;CoinType>(
        _: &mut AdminCap,
        state: &mut ProtocolState,
        ctx: &mut TxContext 
    ) {
        transfer::share_object(Pool{
            id: object::new(ctx),
            asset_number: state.number_of_pools,
            reserve: balance::zero&lt;CoinType>(),
        });

        state.number_of_pools = state.number_of_pools + 1;
    }

(3)存入资产(deposit

该接口会:

  • 存入特定资产到池子中
  • 会增加ProtocolState中,该用户对应资产金额(如果该类型资产没有创建,将进行创建)
    /*
        Deposits a coin to a pool. This function increases the user's collateral amount in the pool
        and adds the coin to the pool's reserve.
    */
    public fun deposit&lt;CoinType>(
        coin_to_deposit: Coin&lt;CoinType>,
        pool: &mut Pool&lt;CoinType>,
        state: &mut ProtocolState,
        ctx: &mut TxContext
    ) { 
        let coin_to_deposit_balance = coin::into_balance(coin_to_deposit);
        let value = balance::value(&coin_to_deposit_balance);
        balance::join(&mut pool.reserve,  coin_to_deposit_balance);

        let sender = tx_context::sender(ctx);

        if (table::contains(&state.users, sender)) {
            let userData = table::borrow_mut(&mut state.users, sender);

            if (table::contains(&userData.collateral_amount, pool.asset_number)) {
                let amount = table::borrow_mut(&mut userData.collateral_amount, 
                    pool.asset_number);
                *amount = *amount + value;
            } else {
                table::add(&mut userData.collateral_amount, pool.asset_number, value);
            }
        } else {
            let collateral_amount = table::new&lt;u64, u64>(ctx);
            let borrowed_amount = table::new&lt;u64, u64>(ctx);
            table::add(&mut collateral_amount, pool.asset_number, value);

            let userData = UserData{
                collateral_amount,
                borrowed_amount,
            };

            table::add(&mut state.users, sender, userData);
        }
    }

(4)取款(withdraw

  • 用户可以从池子中进行取款
  • 将减小用户的抵押存款数量
  • 并响应减少池子的代币余额
    /*
        Withdraws a coin from a pool. This function decreases the user's collateral amount in the pool
        and removes the coin from the pool's reserve.
    */
    public fun withdraw&lt;CoinType>(
        amount_to_withdraw: u64, 
        pool: &mut Pool&lt;CoinType>,
        state: &mut ProtocolState,
        ctx: &mut TxContext
    ): Coin&lt;CoinType> {

        let sender = tx_context::sender(ctx);

        let real_amount_to_withdraw = 0;

        if (table::contains(&state.users, sender)) {

            let userData = table::borrow_mut(&mut state.users, sender);

            if (table::contains(&userData.collateral_amount, pool.asset_number)) {
                let amount = table::borrow_mut(&mut userData.collateral_amount, pool.asset_number);

                assert!(*amount >= amount_to_withdraw, 1);

                *amount = *amount - amount_to_withdraw;

                real_amount_to_withdraw = amount_to_withdraw;
            }
        };

        let amount_to_withdraw_balance = balance::split(
            &mut pool.reserve, real_amount_to_withdraw);

        coin::from_balance(amount_to_withdraw_balance, ctx)
    }

(5)借款(borrow

  • 用户可以从池子中借款
  • 调用该方法将增加用户的借款数量
  • 并响应减少池子中的余额
    /*
        Borrows a coin from a pool. This function increases the user's borrowed amount in the pool
        and removes and returns the coin from the pool's reserve.
    */
    public fun borrow&lt;CoinType>(
        amount_to_borrow: u64, 
        pool: &mut Pool&lt;CoinType>,
        state: &mut ProtocolState,
        ctx: &mut TxContext
    ): Coin&lt;CoinType> {

         let sender = tx_context::sender(ctx);

         let real_amount_to_borrow = 0;

         if (table::contains(&state.users, sender)) {
            let userData = table::borrow_mut(&mut state.users, sender);

            if (table::contains(&userData.borrowed_amount, pool.asset_number)) {
                let amount = table::borrow_mut(&mut userData.borrowed_amount, pool.asset_number);
                *amount = *amount + amount_to_borrow
            } else {
                table::add(&mut userData.borrowed_amount, pool.asset_number, amount_to_borrow);
            };

            real_amount_to_borrow = amount_to_borrow;
         };

         let amount_to_borrow_balance = balance::split(
            &mut pool.reserve, real_amount_to_borrow);

         coin::from_balance(amount_to_borrow_balance, ctx)
    }

(6)还款(repay)

  • 用户可以进行还款
  • 还款后将用户借款数量
  • 并响应增加池子中的代币余额
    /*
        Repays a coin to a pool. This function decreases the user's borrowed amount in the pool
        and adds the coin to the pool's reserve.
    */
    public fun repay&lt;CoinType>(
        coin_to_repay: Coin&lt;CoinType>,
        pool: &mut Pool&lt;CoinType>,
        state: &mut ProtocolState,
        ctx: &mut TxContext
    ) {

        let sender = tx_context::sender(ctx);
        let coin_to_repay_balance = coin::into_balance(coin_to_repay);
        let value = balance::value(&coin_to_repay_balance);
        balance::join(&mut pool.reserve, coin_to_repay_balance);

        if (table::contains(&state.users, sender)) {
            let userData = table::borrow_mut(&mut state.users, sender);

            if (table::contains(&userData.borrowed_amount, pool.asset_number)) {
                let amount = table::borrow_mut(
                    &mut userData.borrowed_amount, pool.asset_number);

                assert!(*amount >= value, 2);
                *amount = *amount - value;
            }
        };
    }

(7)计算健康因子(calculate_health_factor

    /*  
        Calculates the health factor of a user. The health factor is the ratio of the user's collateral
        to the user's borrowed amount. The health factor is calculated with a decimal precision of 2. 
        This means that a health factor of 1.34 should be represented as 134, and a health factor of 0.34
        should be represented as 34.

        See above for more information on how to calculate the health factor.
    */
    public fun calculate_health_factor(
        user: address,
        state: &ProtocolState,
        price_feed: &dummy_oracle::PriceFeed
    ): u64 {
        let collateral_total_amount = 0;
        let borrowed_total_amount = 0;

        if (table::contains(&state.users, user)) {

            let userData = table::borrow(&state.users, user);

            let i = 0;
            while(i &lt; state.number_of_pools) {

                let (price, decimals) = dummy_oracle::get_price_and_decimals(i, price_feed);

                if (table::contains(&userData.collateral_amount, i)) {
                    let amount = table::borrow(&userData.collateral_amount, i);
                    collateral_total_amount = collateral_total_amount + *amount / math::pow(10, decimals) * price ;
                };

                if (table::contains(&userData.borrowed_amount, i)) {
                    let amount = table::borrow(&userData.borrowed_amount, i);
                    borrowed_total_amount = borrowed_total_amount + *amount / math::pow(10, decimals) * price;
                };

                i = i+1;
            }
        };

        assert!(borrowed_total_amount != 0, 3);

        collateral_total_amount * 80 / borrowed_total_amount
    }

3 挑战合约提交

image-20240322080633609.png

4 更多

欢迎关注微信公众号:Move中文,开启你的 Sui Move 之旅!

image.png

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

0 条评论

请先 登录 后评论
rzexin
rzexin
0x2Dc5...52c1
江湖只有他的大名,没有他的介绍。