808 lines
24 KiB
Plaintext
808 lines
24 KiB
Plaintext
///! # 流动性池
|
||
///!
|
||
///! Liquidity Pool (AMM)
|
||
///! 提供自动做市商(AMM)机制和流动性管理
|
||
///!
|
||
///! **版本**: v1.0
|
||
///! **模块**: charter-std/defi/liquidity.ch
|
||
|
||
use utils::math::{safe_mul, safe_div, safe_add, safe_sub, sqrt};
|
||
use utils::crypto::sha3_384_hash;
|
||
|
||
// ============================================================================
|
||
// 流动性池结构
|
||
// ============================================================================
|
||
|
||
/// 流动性池
|
||
struct LiquidityPool {
|
||
/// 池ID
|
||
pool_id: Hash,
|
||
|
||
/// 资产A
|
||
asset_a: Address,
|
||
|
||
/// 资产B
|
||
asset_b: Address,
|
||
|
||
/// 资产A储备量
|
||
reserve_a: u256,
|
||
|
||
/// 资产B储备量
|
||
reserve_b: u256,
|
||
|
||
/// LP代币总供应量
|
||
total_lp_supply: u256,
|
||
|
||
/// 手续费率(基点,例如30表示0.3%)
|
||
fee_rate: u16,
|
||
|
||
/// 累计手续费A
|
||
accumulated_fee_a: u256,
|
||
|
||
/// 累计手续费B
|
||
accumulated_fee_b: u256,
|
||
|
||
/// 最后更新时间
|
||
last_updated: Timestamp,
|
||
|
||
/// K值(恒定乘积)
|
||
k_last: u256
|
||
}
|
||
|
||
/// 流动性提供者信息
|
||
struct LiquidityProvider {
|
||
/// 提供者地址
|
||
provider: Address,
|
||
|
||
/// 池ID
|
||
pool_id: Hash,
|
||
|
||
/// LP代币数量
|
||
lp_tokens: u256,
|
||
|
||
/// 提供时间
|
||
provided_at: Timestamp,
|
||
|
||
/// 已领取奖励A
|
||
claimed_reward_a: u256,
|
||
|
||
/// 已领取奖励B
|
||
claimed_reward_b: u256
|
||
}
|
||
|
||
/// 交换记录
|
||
struct SwapRecord {
|
||
/// 交换ID
|
||
swap_id: Hash,
|
||
|
||
/// 池ID
|
||
pool_id: Hash,
|
||
|
||
/// 交换者
|
||
swapper: Address,
|
||
|
||
/// 输入资产
|
||
asset_in: Address,
|
||
|
||
/// 输入数量
|
||
amount_in: u256,
|
||
|
||
/// 输出资产
|
||
asset_out: Address,
|
||
|
||
/// 输出数量
|
||
amount_out: u256,
|
||
|
||
/// 手续费
|
||
fee: u256,
|
||
|
||
/// 时间戳
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
// ============================================================================
|
||
// 流动性事件
|
||
// ============================================================================
|
||
|
||
/// 添加流动性事件
|
||
event AddLiquidity {
|
||
pool_id: Hash,
|
||
provider: Address,
|
||
amount_a: u256,
|
||
amount_b: u256,
|
||
lp_tokens: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 移除流动性事件
|
||
event RemoveLiquidity {
|
||
pool_id: Hash,
|
||
provider: Address,
|
||
amount_a: u256,
|
||
amount_b: u256,
|
||
lp_tokens: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 交换事件
|
||
event Swap {
|
||
swap_id: Hash,
|
||
pool_id: Hash,
|
||
swapper: Address,
|
||
asset_in: Address,
|
||
amount_in: u256,
|
||
asset_out: Address,
|
||
amount_out: u256,
|
||
fee: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
/// 领取奖励事件
|
||
event ClaimRewards {
|
||
pool_id: Hash,
|
||
provider: Address,
|
||
reward_a: u256,
|
||
reward_b: u256,
|
||
timestamp: Timestamp
|
||
}
|
||
|
||
// ============================================================================
|
||
// 流动性池协议
|
||
// ============================================================================
|
||
|
||
/// 流动性池协议
|
||
certificate LiquidityPoolProtocol {
|
||
/// 流动性池 (pool_id => pool)
|
||
let _pools: Map<Hash, LiquidityPool>;
|
||
|
||
/// 流动性提供者 (provider => pool_id => lp_info)
|
||
let _providers: Map<Address, Map<Hash, LiquidityProvider>>;
|
||
|
||
/// 交换记录 (swap_id => record)
|
||
let _swaps: Map<Hash, SwapRecord>;
|
||
|
||
/// 池索引 (asset_a => asset_b => pool_id)
|
||
let _pool_index: Map<Address, Map<Address, Hash>>;
|
||
|
||
/// 管理员地址
|
||
let _admin: Address;
|
||
|
||
/// 默认手续费率(基点)
|
||
let _default_fee_rate: u16;
|
||
|
||
/// 最小流动性(防止除零)
|
||
let _minimum_liquidity: u256;
|
||
|
||
// ========== 构造函数 ==========
|
||
|
||
constructor(fee_rate: u16) {
|
||
require(fee_rate <= 1000, "Fee rate too high"); // 最高10%
|
||
|
||
self._admin = msg.sender;
|
||
self._default_fee_rate = fee_rate;
|
||
self._minimum_liquidity = 1000; // 最小流动性锁定
|
||
}
|
||
|
||
// ========== 池管理 ==========
|
||
|
||
/// 创建流动性池
|
||
///
|
||
/// # 参数
|
||
/// - `asset_a`: 资产A地址
|
||
/// - `asset_b`: 资产B地址
|
||
/// - `initial_a`: 初始资产A数量
|
||
/// - `initial_b`: 初始资产B数量
|
||
///
|
||
/// # 返回
|
||
/// - `Hash`: 池ID
|
||
pub fn create_pool(
|
||
asset_a: Address,
|
||
asset_b: Address,
|
||
initial_a: u256,
|
||
initial_b: u256
|
||
) -> Hash {
|
||
require(!asset_a.is_zero(), "Invalid asset A");
|
||
require(!asset_b.is_zero(), "Invalid asset B");
|
||
require(asset_a != asset_b, "Assets must be different");
|
||
require(initial_a > 0, "Initial A must be positive");
|
||
require(initial_b > 0, "Initial B must be positive");
|
||
|
||
// 确保资产顺序一致(A < B)
|
||
let (token0, token1, amount0, amount1) = if asset_a < asset_b {
|
||
(asset_a, asset_b, initial_a, initial_b)
|
||
} else {
|
||
(asset_b, asset_a, initial_b, initial_a)
|
||
};
|
||
|
||
// 检查池是否已存在
|
||
if let Some(existing_pools) = self._pool_index.get(token0) {
|
||
require(!existing_pools.contains_key(token1), "Pool already exists");
|
||
}
|
||
|
||
// 生成池ID
|
||
let pool_id = self._generate_pool_id(token0, token1);
|
||
|
||
// 计算初始LP代币数量(几何平均数)
|
||
let initial_lp = sqrt(safe_mul(amount0, amount1));
|
||
require(initial_lp > self._minimum_liquidity, "Insufficient initial liquidity");
|
||
|
||
// 锁定最小流动性
|
||
let lp_to_provider = safe_sub(initial_lp, self._minimum_liquidity);
|
||
|
||
let pool = LiquidityPool {
|
||
pool_id: pool_id,
|
||
asset_a: token0,
|
||
asset_b: token1,
|
||
reserve_a: amount0,
|
||
reserve_b: amount1,
|
||
total_lp_supply: initial_lp,
|
||
fee_rate: self._default_fee_rate,
|
||
accumulated_fee_a: 0,
|
||
accumulated_fee_b: 0,
|
||
last_updated: block.timestamp,
|
||
k_last: safe_mul(amount0, amount1)
|
||
};
|
||
|
||
self._pools[pool_id] = pool;
|
||
|
||
// 更新索引
|
||
if !self._pool_index.contains_key(token0) {
|
||
self._pool_index[token0] = Map::new();
|
||
}
|
||
self._pool_index[token0][token1] = pool_id;
|
||
|
||
// 记录流动性提供者
|
||
if !self._providers.contains_key(msg.sender) {
|
||
self._providers[msg.sender] = Map::new();
|
||
}
|
||
|
||
let lp_info = LiquidityProvider {
|
||
provider: msg.sender,
|
||
pool_id: pool_id,
|
||
lp_tokens: lp_to_provider,
|
||
provided_at: block.timestamp,
|
||
claimed_reward_a: 0,
|
||
claimed_reward_b: 0
|
||
};
|
||
|
||
self._providers[msg.sender][pool_id] = lp_info;
|
||
|
||
// 实际需要转移资产到池中
|
||
|
||
emit AddLiquidity {
|
||
pool_id: pool_id,
|
||
provider: msg.sender,
|
||
amount_a: amount0,
|
||
amount_b: amount1,
|
||
lp_tokens: lp_to_provider,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return pool_id;
|
||
}
|
||
|
||
/// 获取池信息
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
///
|
||
/// # 返回
|
||
/// - `LiquidityPool`: 池信息
|
||
pub fn get_pool(pool_id: Hash) -> LiquidityPool {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
return self._pools[pool_id];
|
||
}
|
||
|
||
/// 通过资产对查找池
|
||
///
|
||
/// # 参数
|
||
/// - `asset_a`: 资产A地址
|
||
/// - `asset_b`: 资产B地址
|
||
///
|
||
/// # 返回
|
||
/// - `Option<Hash>`: 池ID
|
||
pub fn find_pool(asset_a: Address, asset_b: Address) -> Option<Hash> {
|
||
let (token0, token1) = if asset_a < asset_b {
|
||
(asset_a, asset_b)
|
||
} else {
|
||
(asset_b, asset_a)
|
||
};
|
||
|
||
return self._pool_index.get(token0)
|
||
.and_then(|m| m.get(token1));
|
||
}
|
||
|
||
// ========== 添加和移除流动性 ==========
|
||
|
||
/// 添加流动性
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
/// - `amount_a`: 资产A数量
|
||
/// - `amount_b`: 资产B数量
|
||
/// - `min_lp_tokens`: 最小LP代币数量(滑点保护)
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 获得的LP代币数量
|
||
pub fn add_liquidity(
|
||
pool_id: Hash,
|
||
amount_a: u256,
|
||
amount_b: u256,
|
||
min_lp_tokens: u256
|
||
) -> u256 {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
require(amount_a > 0, "Amount A must be positive");
|
||
require(amount_b > 0, "Amount B must be positive");
|
||
|
||
let mut pool = self._pools[pool_id];
|
||
|
||
// 计算最优添加比例
|
||
let optimal_b = safe_mul(amount_a, pool.reserve_b) / pool.reserve_a;
|
||
|
||
let (final_a, final_b) = if optimal_b <= amount_b {
|
||
(amount_a, optimal_b)
|
||
} else {
|
||
let optimal_a = safe_mul(amount_b, pool.reserve_a) / pool.reserve_b;
|
||
(optimal_a, amount_b)
|
||
};
|
||
|
||
// 计算LP代币数量
|
||
let lp_tokens = if pool.total_lp_supply == 0 {
|
||
sqrt(safe_mul(final_a, final_b))
|
||
} else {
|
||
let lp_a = safe_mul(final_a, pool.total_lp_supply) / pool.reserve_a;
|
||
let lp_b = safe_mul(final_b, pool.total_lp_supply) / pool.reserve_b;
|
||
if lp_a < lp_b { lp_a } else { lp_b }
|
||
};
|
||
|
||
require(lp_tokens >= min_lp_tokens, "Slippage too high");
|
||
|
||
// 更新池状态
|
||
pool.reserve_a = safe_add(pool.reserve_a, final_a);
|
||
pool.reserve_b = safe_add(pool.reserve_b, final_b);
|
||
pool.total_lp_supply = safe_add(pool.total_lp_supply, lp_tokens);
|
||
pool.k_last = safe_mul(pool.reserve_a, pool.reserve_b);
|
||
pool.last_updated = block.timestamp;
|
||
|
||
self._pools[pool_id] = pool;
|
||
|
||
// 更新流动性提供者信息
|
||
if !self._providers.contains_key(msg.sender) {
|
||
self._providers[msg.sender] = Map::new();
|
||
}
|
||
|
||
if let Some(mut lp_info) = self._providers[msg.sender].get(pool_id) {
|
||
lp_info.lp_tokens = safe_add(lp_info.lp_tokens, lp_tokens);
|
||
self._providers[msg.sender][pool_id] = lp_info;
|
||
} else {
|
||
let lp_info = LiquidityProvider {
|
||
provider: msg.sender,
|
||
pool_id: pool_id,
|
||
lp_tokens: lp_tokens,
|
||
provided_at: block.timestamp,
|
||
claimed_reward_a: 0,
|
||
claimed_reward_b: 0
|
||
};
|
||
self._providers[msg.sender][pool_id] = lp_info;
|
||
}
|
||
|
||
// 实际需要转移资产到池中
|
||
|
||
emit AddLiquidity {
|
||
pool_id: pool_id,
|
||
provider: msg.sender,
|
||
amount_a: final_a,
|
||
amount_b: final_b,
|
||
lp_tokens: lp_tokens,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return lp_tokens;
|
||
}
|
||
|
||
/// 移除流动性
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
/// - `lp_tokens`: LP代币数量
|
||
/// - `min_amount_a`: 最小资产A数量(滑点保护)
|
||
/// - `min_amount_b`: 最小资产B数量(滑点保护)
|
||
///
|
||
/// # 返回
|
||
/// - `(u256, u256)`: (资产A数量, 资产B数量)
|
||
pub fn remove_liquidity(
|
||
pool_id: Hash,
|
||
lp_tokens: u256,
|
||
min_amount_a: u256,
|
||
min_amount_b: u256
|
||
) -> (u256, u256) {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
require(lp_tokens > 0, "LP tokens must be positive");
|
||
require(self._providers.contains_key(msg.sender), "No liquidity provided");
|
||
require(
|
||
self._providers[msg.sender].contains_key(pool_id),
|
||
"No liquidity in this pool"
|
||
);
|
||
|
||
let mut lp_info = self._providers[msg.sender][pool_id];
|
||
require(lp_info.lp_tokens >= lp_tokens, "Insufficient LP tokens");
|
||
|
||
let mut pool = self._pools[pool_id];
|
||
|
||
// 计算可取回的资产数量
|
||
let amount_a = safe_mul(lp_tokens, pool.reserve_a) / pool.total_lp_supply;
|
||
let amount_b = safe_mul(lp_tokens, pool.reserve_b) / pool.total_lp_supply;
|
||
|
||
require(amount_a >= min_amount_a, "Slippage too high for A");
|
||
require(amount_b >= min_amount_b, "Slippage too high for B");
|
||
|
||
// 更新池状态
|
||
pool.reserve_a = safe_sub(pool.reserve_a, amount_a);
|
||
pool.reserve_b = safe_sub(pool.reserve_b, amount_b);
|
||
pool.total_lp_supply = safe_sub(pool.total_lp_supply, lp_tokens);
|
||
pool.k_last = safe_mul(pool.reserve_a, pool.reserve_b);
|
||
pool.last_updated = block.timestamp;
|
||
|
||
self._pools[pool_id] = pool;
|
||
|
||
// 更新流动性提供者信息
|
||
lp_info.lp_tokens = safe_sub(lp_info.lp_tokens, lp_tokens);
|
||
self._providers[msg.sender][pool_id] = lp_info;
|
||
|
||
// 实际需要转移资产给提供者
|
||
|
||
emit RemoveLiquidity {
|
||
pool_id: pool_id,
|
||
provider: msg.sender,
|
||
amount_a: amount_a,
|
||
amount_b: amount_b,
|
||
lp_tokens: lp_tokens,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return (amount_a, amount_b);
|
||
}
|
||
|
||
// ========== 交换 ==========
|
||
|
||
/// 交换(精确输入)
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
/// - `asset_in`: 输入资产
|
||
/// - `amount_in`: 输入数量
|
||
/// - `min_amount_out`: 最小输出数量(滑点保护)
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 输出数量
|
||
pub fn swap_exact_input(
|
||
pool_id: Hash,
|
||
asset_in: Address,
|
||
amount_in: u256,
|
||
min_amount_out: u256
|
||
) -> u256 {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
require(amount_in > 0, "Amount in must be positive");
|
||
|
||
let mut pool = self._pools[pool_id];
|
||
|
||
// 确定输入输出资产
|
||
let (reserve_in, reserve_out, asset_out) = if asset_in == pool.asset_a {
|
||
(pool.reserve_a, pool.reserve_b, pool.asset_b)
|
||
} else if asset_in == pool.asset_b {
|
||
(pool.reserve_b, pool.reserve_a, pool.asset_a)
|
||
} else {
|
||
revert("Invalid asset");
|
||
};
|
||
|
||
// 计算输出数量(扣除手续费)
|
||
let amount_in_with_fee = safe_mul(amount_in, (10000 - pool.fee_rate) as u256);
|
||
let numerator = safe_mul(amount_in_with_fee, reserve_out);
|
||
let denominator = safe_add(safe_mul(reserve_in, 10000), amount_in_with_fee);
|
||
let amount_out = numerator / denominator;
|
||
|
||
require(amount_out >= min_amount_out, "Slippage too high");
|
||
require(amount_out < reserve_out, "Insufficient liquidity");
|
||
|
||
// 计算手续费
|
||
let fee = safe_mul(amount_in, pool.fee_rate as u256) / 10000;
|
||
|
||
// 更新储备量
|
||
if asset_in == pool.asset_a {
|
||
pool.reserve_a = safe_add(pool.reserve_a, amount_in);
|
||
pool.reserve_b = safe_sub(pool.reserve_b, amount_out);
|
||
pool.accumulated_fee_a = safe_add(pool.accumulated_fee_a, fee);
|
||
} else {
|
||
pool.reserve_b = safe_add(pool.reserve_b, amount_in);
|
||
pool.reserve_a = safe_sub(pool.reserve_a, amount_out);
|
||
pool.accumulated_fee_b = safe_add(pool.accumulated_fee_b, fee);
|
||
}
|
||
|
||
pool.k_last = safe_mul(pool.reserve_a, pool.reserve_b);
|
||
pool.last_updated = block.timestamp;
|
||
|
||
self._pools[pool_id] = pool;
|
||
|
||
// 生成交换ID
|
||
let swap_id = self._generate_swap_id(pool_id, msg.sender);
|
||
|
||
// 记录交换
|
||
let swap_record = SwapRecord {
|
||
swap_id: swap_id,
|
||
pool_id: pool_id,
|
||
swapper: msg.sender,
|
||
asset_in: asset_in,
|
||
amount_in: amount_in,
|
||
asset_out: asset_out,
|
||
amount_out: amount_out,
|
||
fee: fee,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
self._swaps[swap_id] = swap_record;
|
||
|
||
// 实际需要转移资产
|
||
|
||
emit Swap {
|
||
swap_id: swap_id,
|
||
pool_id: pool_id,
|
||
swapper: msg.sender,
|
||
asset_in: asset_in,
|
||
amount_in: amount_in,
|
||
asset_out: asset_out,
|
||
amount_out: amount_out,
|
||
fee: fee,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return amount_out;
|
||
}
|
||
|
||
/// 交换(精确输出)
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
/// - `asset_out`: 输出资产
|
||
/// - `amount_out`: 输出数量
|
||
/// - `max_amount_in`: 最大输入数量(滑点保护)
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 输入数量
|
||
pub fn swap_exact_output(
|
||
pool_id: Hash,
|
||
asset_out: Address,
|
||
amount_out: u256,
|
||
max_amount_in: u256
|
||
) -> u256 {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
require(amount_out > 0, "Amount out must be positive");
|
||
|
||
let mut pool = self._pools[pool_id];
|
||
|
||
// 确定输入输出资产
|
||
let (reserve_in, reserve_out, asset_in) = if asset_out == pool.asset_a {
|
||
(pool.reserve_b, pool.reserve_a, pool.asset_b)
|
||
} else if asset_out == pool.asset_b {
|
||
(pool.reserve_a, pool.reserve_b, pool.asset_a)
|
||
} else {
|
||
revert("Invalid asset");
|
||
};
|
||
|
||
require(amount_out < reserve_out, "Insufficient liquidity");
|
||
|
||
// 计算输入数量(包含手续费)
|
||
let numerator = safe_mul(safe_mul(reserve_in, amount_out), 10000);
|
||
let denominator = safe_mul(
|
||
safe_sub(reserve_out, amount_out),
|
||
(10000 - pool.fee_rate) as u256
|
||
);
|
||
let amount_in = safe_add(numerator / denominator, 1); // 向上取整
|
||
|
||
require(amount_in <= max_amount_in, "Slippage too high");
|
||
|
||
// 计算手续费
|
||
let fee = safe_mul(amount_in, pool.fee_rate as u256) / 10000;
|
||
|
||
// 更新储备量
|
||
if asset_out == pool.asset_a {
|
||
pool.reserve_b = safe_add(pool.reserve_b, amount_in);
|
||
pool.reserve_a = safe_sub(pool.reserve_a, amount_out);
|
||
pool.accumulated_fee_b = safe_add(pool.accumulated_fee_b, fee);
|
||
} else {
|
||
pool.reserve_a = safe_add(pool.reserve_a, amount_in);
|
||
pool.reserve_b = safe_sub(pool.reserve_b, amount_out);
|
||
pool.accumulated_fee_a = safe_add(pool.accumulated_fee_a, fee);
|
||
}
|
||
|
||
pool.k_last = safe_mul(pool.reserve_a, pool.reserve_b);
|
||
pool.last_updated = block.timestamp;
|
||
|
||
self._pools[pool_id] = pool;
|
||
|
||
// 生成交换ID
|
||
let swap_id = self._generate_swap_id(pool_id, msg.sender);
|
||
|
||
// 记录交换
|
||
let swap_record = SwapRecord {
|
||
swap_id: swap_id,
|
||
pool_id: pool_id,
|
||
swapper: msg.sender,
|
||
asset_in: asset_in,
|
||
amount_in: amount_in,
|
||
asset_out: asset_out,
|
||
amount_out: amount_out,
|
||
fee: fee,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
self._swaps[swap_id] = swap_record;
|
||
|
||
emit Swap {
|
||
swap_id: swap_id,
|
||
pool_id: pool_id,
|
||
swapper: msg.sender,
|
||
asset_in: asset_in,
|
||
amount_in: amount_in,
|
||
asset_out: asset_out,
|
||
amount_out: amount_out,
|
||
fee: fee,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return amount_in;
|
||
}
|
||
|
||
/// 获取输出数量(不执行交换)
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
/// - `asset_in`: 输入资产
|
||
/// - `amount_in`: 输入数量
|
||
///
|
||
/// # 返回
|
||
/// - `u256`: 输出数量
|
||
pub fn get_amount_out(
|
||
pool_id: Hash,
|
||
asset_in: Address,
|
||
amount_in: u256
|
||
) -> u256 {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
|
||
let pool = self._pools[pool_id];
|
||
|
||
let (reserve_in, reserve_out) = if asset_in == pool.asset_a {
|
||
(pool.reserve_a, pool.reserve_b)
|
||
} else {
|
||
(pool.reserve_b, pool.reserve_a)
|
||
};
|
||
|
||
let amount_in_with_fee = safe_mul(amount_in, (10000 - pool.fee_rate) as u256);
|
||
let numerator = safe_mul(amount_in_with_fee, reserve_out);
|
||
let denominator = safe_add(safe_mul(reserve_in, 10000), amount_in_with_fee);
|
||
|
||
return numerator / denominator;
|
||
}
|
||
|
||
// ========== 奖励管理 ==========
|
||
|
||
/// 计算待领取奖励
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
/// - `provider`: 提供者地址
|
||
///
|
||
/// # 返回
|
||
/// - `(u256, u256)`: (奖励A, 奖励B)
|
||
pub fn calculate_rewards(
|
||
pool_id: Hash,
|
||
provider: Address
|
||
) -> (u256, u256) {
|
||
require(self._pools.contains_key(pool_id), "Pool not found");
|
||
require(self._providers.contains_key(provider), "No liquidity provided");
|
||
require(
|
||
self._providers[provider].contains_key(pool_id),
|
||
"No liquidity in this pool"
|
||
);
|
||
|
||
let pool = self._pools[pool_id];
|
||
let lp_info = self._providers[provider][pool_id];
|
||
|
||
// 计算份额
|
||
let share = safe_mul(lp_info.lp_tokens, 1e18) / pool.total_lp_supply;
|
||
|
||
// 计算奖励
|
||
let reward_a = safe_sub(
|
||
safe_mul(pool.accumulated_fee_a, share) / 1e18,
|
||
lp_info.claimed_reward_a
|
||
);
|
||
|
||
let reward_b = safe_sub(
|
||
safe_mul(pool.accumulated_fee_b, share) / 1e18,
|
||
lp_info.claimed_reward_b
|
||
);
|
||
|
||
return (reward_a, reward_b);
|
||
}
|
||
|
||
/// 领取奖励
|
||
///
|
||
/// # 参数
|
||
/// - `pool_id`: 池ID
|
||
///
|
||
/// # 返回
|
||
/// - `(u256, u256)`: (奖励A, 奖励B)
|
||
pub fn claim_rewards(pool_id: Hash) -> (u256, u256) {
|
||
let (reward_a, reward_b) = self.calculate_rewards(pool_id, msg.sender);
|
||
|
||
require(reward_a > 0 || reward_b > 0, "No rewards to claim");
|
||
|
||
// 更新已领取奖励
|
||
let mut lp_info = self._providers[msg.sender][pool_id];
|
||
lp_info.claimed_reward_a = safe_add(lp_info.claimed_reward_a, reward_a);
|
||
lp_info.claimed_reward_b = safe_add(lp_info.claimed_reward_b, reward_b);
|
||
self._providers[msg.sender][pool_id] = lp_info;
|
||
|
||
// 实际需要转移奖励
|
||
|
||
emit ClaimRewards {
|
||
pool_id: pool_id,
|
||
provider: msg.sender,
|
||
reward_a: reward_a,
|
||
reward_b: reward_b,
|
||
timestamp: block.timestamp
|
||
};
|
||
|
||
return (reward_a, reward_b);
|
||
}
|
||
|
||
// ========== 查询函数 ==========
|
||
|
||
/// 获取流动性提供者信息
|
||
///
|
||
/// # 参数
|
||
/// - `provider`: 提供者地址
|
||
/// - `pool_id`: 池ID
|
||
///
|
||
/// # 返回
|
||
/// - `Option<LiquidityProvider>`: 提供者信息
|
||
pub fn get_provider_info(
|
||
provider: Address,
|
||
pool_id: Hash
|
||
) -> Option<LiquidityProvider> {
|
||
return self._providers.get(provider)
|
||
.and_then(|m| m.get(pool_id));
|
||
}
|
||
|
||
/// 获取交换记录
|
||
///
|
||
/// # 参数
|
||
/// - `swap_id`: 交换ID
|
||
///
|
||
/// # 返回
|
||
/// - `SwapRecord`: 交换记录
|
||
pub fn get_swap(swap_id: Hash) -> SwapRecord {
|
||
require(self._swaps.contains_key(swap_id), "Swap not found");
|
||
return self._swaps[swap_id];
|
||
}
|
||
|
||
// ========== 内部函数 ==========
|
||
|
||
/// 生成池ID
|
||
fn _generate_pool_id(asset_a: Address, asset_b: Address) -> Hash {
|
||
let mut data = Bytes::new();
|
||
data.extend(asset_a.as_bytes());
|
||
data.extend(asset_b.as_bytes());
|
||
return sha3_384_hash(data);
|
||
}
|
||
|
||
/// 生成交换ID
|
||
fn _generate_swap_id(pool_id: Hash, swapper: Address) -> Hash {
|
||
let mut data = Bytes::new();
|
||
data.extend(pool_id.as_bytes());
|
||
data.extend(swapper.as_bytes());
|
||
data.extend(block.timestamp.to_bytes());
|
||
data.extend(tx.hash.as_bytes());
|
||
return sha3_384_hash(data);
|
||
}
|
||
}
|