fn verify_add_liquidity()
if total_liquidity == 0
return fail
let liquidity = outputs[req_index_in_inputs * 2]
let change = outputs[req_index_in_inputs * 2 + 1]
let user_lock_hash = req.lock.args[0..32]
if liquidity.type_hash != info.data.liquidity_sudt_type_hash
|| liquidity.lock_hash != user_lock_hash
Outdated doc. reutrn fail
let user_liquidity = BigUint::from(liquidity.data.amount)
// Check sudt exhaustion
if change.data.size == 0
if !change.type.is_none()
|| change.lock_hash != user_lock_hash
return fail
let sudt_injected = BigUint::from(req.data.amount)
let ckb_injected = BigUint::from(req.capacity - SUDT_CAPACITY - change.capacity) // SUDT_CAPACITY for liquidity cell
if ckb_injected != (sudt_injected * ckb_reserve / sudt_injected) + 1
return fail
// Check ckb_injected > min_ckb_injected
let min_ckb_injected = BigUint::from(req.lock.args[49..57])
if min_ckb_injected == 0
|| ckb_injected < min_ckb_injected
return fail
if user_liquidity != sudt_injected * total_liquidity / sudt_reserve
return fail
// Check ckb exhaustion
else if change.data.size >= 16
if change.type_hash != pool.type_hash
|| change.lock_hash != user_lock_hash
return fail
let sudt_injected = BigUint::from(req.data.amount - change.data.amount)
let ckb_injected = BigUint::from(req.capacity - SUDT_CAPACITY * 2) // Two SUDT_CAPACITY for liquidity cell and sudt change cell
if sudt_injected != (ckb_injected * sudt_reserve / ckb_reserve) + 1
return fail
// Check sudt_injected > min_sudt_injected
let min_sudt_injected = BigUint::from(req.lock.args[33..49])
if min_sudt_injected == 0
|| sudt_injected < min_sudt_injected
return fail
if user_liquidity != ckb_injected * total_liquidity / ckb_reserve
return fail
else
return fail
fi
ckb_collected += ckb_injected
sudt_collected += sudt_injected
user_liquidity_added += user_liquidity
endfn
fn verify_remove_liquidity()
if total_liquidity == 0
return fail
if req.data.amount == 0
return fail
let output_a = outputs[req_index_in_inputs];
let output_b = outputs[req_index_in_inputs + 1];
if output_a.type.is_none() && output_b.type.is_some()
let ckb_out = output_a;
let sudt_out = output_b;
else if output_a.type.is_some() && output_b.type.is_none()
let ckb_out = output_b;
let sudt_out = output_a;
else
return fail
fi
if sudt_out.capacity != SUDT_CAPACITY
return fail
if ckb_out.data.size != 0
|| sudt_out.data.size < 16
return fail
let user_lock_hash = req.lock.args[0..32]
if sudt_out.type_hash != pool.type_hash
|| sudt_out.lock_hash != user_lock_hash
|| ckb_out.lock_hash != user_lock_hash
return fail
let user_ckb_got = BigUint::from(ckb_out.capacity - req.capacity)
let user_sudt_got = BigUint::from(sudt_out.data.amount)
let removed_liquidity = BigUint::from(req.data.amount)
// Check user_ckb_got >= min_ckb_got
let min_ckb_got = req.lock.args[49..57]
if min_ckb_got == 0
|| user_ckb_got < min_ckb_got
return fail
// Check user_sudt_got >= min_sudt_got
let min_sudt_got = req.lock.args[33..49]
if min_sudt_got == 0
|| user_sudt_got < min_sudt_got
return fail
if user_ckb_got != removed_liquidity * ckb_reserve / total_liquidity
return fail
if user_sudt_got != removed_liquidity * sudt_reserve / total_liquidity
return fail
pool_ckb_paid += user_ckb_got
pool_sudt_paid += user_sudt_got
user_liquidity_removed += removed_liquidity
assert(pool_ckb_paid < ckb_reserve)
assert(pool_sudt_paid < sudt_reserve)
assert(user_liquidity_removed < total_liquidity)
endfn
for req in inputs[3..]
let version = req.lock.args[32..33]
if INFO_req_VERSION != version
return fail
let req_info_type_hash = req.lock.args[57..89]
if req.data.size < 16
|| req_info_type_hash != info.type_hash
return fail
if info.data.total_liquidity == 0
// Only allow on req to provide genesis liquidity
if count(inputs) - 3 != 1
return fail
verify_genesis_add_liquidity()
break
match req.type_hash
info.data.liquidity_sudt_type_hash => verify_remove_liquidity(req)
pool.type_hash => verify_add_liquidity(req)
_ => return fail // unknown liquidity req
endfor
if info_out.capacity != INFO_CAPACITY
|| info_out.data.ckb_reserve != info_in.data.ckb_reserve - pool_ckb_paid + ckb_collected
|| info_out.data.sudt_reserve != info_in.data.sudt_reserve - pool_sudt_paid + token_collected
|| BigUint::from(info_out.data.total_liquidity) !=
BigUint::from(info_in.data.total_liquidity) - user_liquidity_removed + user_liquidity_added
return fail
if pool_out.capacity != pool_in.capacity + info_out.data.ckb_reserve - info_in.data.ckb_reserve
|| pool_out.data.amount != info_out.data.sudt_reserve
return fail
return success