我的SUI Move挑战合约——【overmind】Suiss Bank
https://overmind.xyz/quests/suiss-bank
Suiss Bank
是一个简单的借贷协议。
该挑战是一个该简单的借贷协议的一部分。该模块为用户提供存款和提取抵押品、借入和偿还代币以及计算其借贷健康因子的功能。
deposit
):让贷款人把抵押品存入流动资金池withdraw
):让贷款人提取抵押品borrow
):从借贷池中借出代币repay
):借款人偿还债务calculate_health_factor
):计算用户债务与抵押品比率的健康因素https://naviprotocol.gitbook.io/navi-protocol-docs/getting-started/liquidations
https://naviprotocol.gitbook.io/navi-protocol-docs/protocol-parameters/lending-and-borrowing
https://github.com/howtosui/my_quest_overmind_suiss_bank/blob/main/sources/lender.move
合约创建者将持有该对象,用于创建池子。
struct AdminCap has key, store {
id: UID,
}
该对象在合约创建时创建,是全局共享对象。
成员变量说明:
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<address, UserData> // All user data of the protocol.
}
池子中包括资产编号及其资产余额。
成员变量说明:
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<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<CoinType>
}
该对象是用户数据对象,存储了抵押和借出的资产数量。
成员变量说明:
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<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<u64, u64>,
}
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<address, UserData>(ctx);
transfer::share_object(ProtocolState{
id: object::new(ctx),
number_of_pools: 0,
users,
});
}
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<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<CoinType>(),
});
state.number_of_pools = state.number_of_pools + 1;
}
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<CoinType>(
coin_to_deposit: Coin<CoinType>,
pool: &mut Pool<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<u64, u64>(ctx);
let borrowed_amount = table::new<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);
}
}
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<CoinType>(
amount_to_withdraw: u64,
pool: &mut Pool<CoinType>,
state: &mut ProtocolState,
ctx: &mut TxContext
): Coin<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)
}
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<CoinType>(
amount_to_borrow: u64,
pool: &mut Pool<CoinType>,
state: &mut ProtocolState,
ctx: &mut TxContext
): Coin<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)
}
/*
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<CoinType>(
coin_to_repay: Coin<CoinType>,
pool: &mut Pool<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;
}
};
}
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 < 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
}
欢迎关注微信公众号:Move中文,开启你的 Sui Move 之旅!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!