Skip to content

Commit

Permalink
Support receiver for project owner (#30)
Browse files Browse the repository at this point in the history
* support receiver

* revert to use value utils

* fix test

* fix test

* remove receiver datum after creating pool

* pool base fee

* inline datum for receiver

* create pool commision

* Fix Test

* comment base fee

* hardcode number

* fix typo comment

* new function for calculate_allocation

* fix test

* remove comment code

* add comment

* support minimum when collect or redeem multiple thing

* fix test

* add more test for redeem and refund

* revamp ReceiverDatum

---------

Co-authored-by: tony <[email protected]>
  • Loading branch information
m1n999999 and ljttl3q04t authored Jun 15, 2024
1 parent 0c7d40c commit 7479364
Show file tree
Hide file tree
Showing 23 changed files with 890 additions and 137 deletions.
Binary file modified bun.lockb
Binary file not shown.
11 changes: 10 additions & 1 deletion lib/lb_v2/manager_validation.ak
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use lb_v2/types.{
AddSellers, Asset, CollectSellers, ManagerDatum, ManagerRedeemer, SellerDatum,
TreasuryDatum,
}
use lb_v2/utils.{assert, must_get_start_end_validity, seller_auth_an}
use lb_v2/utils.{
assert, minimum_seller_collected, must_get_start_end_validity, seller_auth_an,
}
use lb_v2/validation.{build_default_seller_output}

// Assertions:
Expand Down Expand Up @@ -144,6 +146,13 @@ pub fn validate_manage_seller(
},
@"Collect sellers: Invalid manager datum",
),
or {
// prevent spamming by setting minimum for seller collected
// if this tx is not the last collect action
minimum_seller_collected <= seller_input_count,
// the last collecting
seller_count == seller_input_count,
},
}
}
_ -> False
Expand Down
26 changes: 25 additions & 1 deletion lib/lb_v2/order_validation.ak
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use aiken/transaction/value.{
use lb_v2/types.{Asset, OrderDatum, TreasuryDatum, ValidatorHash}
use lb_v2/utils.{
amm_authen_policy_id, compute_asset_name_from_base_and_raise, fee_ada,
order_auth_an, order_minimum_ada,
minimum_order_collected, minimum_order_redeemed, order_auth_an,
order_minimum_ada,
}
use lb_v2/validation.{get_order_inputs, get_order_outputs, get_treasury_output}

Expand Down Expand Up @@ -37,6 +38,8 @@ pub fn validate_collect_orders(
collected_fund,
base_asset,
raise_asset,
reserve_raise,
total_penalty,
is_manager_collected,
..
} = treasury_in_datum
Expand Down Expand Up @@ -81,6 +84,13 @@ pub fn validate_collect_orders(
value.flatten(mint_value) == [],
// All Sellers, Manager have been collected.
is_manager_collected == True,
or {
// prevent spamming by setting minimum for orders collected
// if this tx is not the last collect action
list.length(order_inputs) >= minimum_order_collected,
// the last collecting
collected_fund + collect_amount == reserve_raise + total_penalty,
},
}
}

Expand Down Expand Up @@ -231,6 +241,13 @@ pub fn validate_redeem(
value.flatten(mint_value) == [
(factory_policy_id, order_auth_an, -list.length(order_inputs)),
],
or {
// prevent spamming by setting minimum for orders redeemed
// if this tx is not the last redeemed action
list.length(order_inputs) >= minimum_order_redeemed,
// the last redeeming
collected_fund == total_fund,
},
}
}

Expand Down Expand Up @@ -382,6 +399,13 @@ pub fn validate_refund(
value.flatten(mint_value) == [
(factory_policy_id, order_auth_an, -list.length(order_inputs)),
],
or {
// prevent spamming by setting minimum for orders redeemed
// if this tx is not the last redeemed action
list.length(order_inputs) >= minimum_order_redeemed,
// the last redeeming
collected_fund == refund_amount,
},
}
}

Expand Down
178 changes: 143 additions & 35 deletions lib/lb_v2/treasury_validation.ak
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
use aiken/list
use aiken/transaction.{InlineDatum, Output}
use aiken/transaction/value.{PolicyId, Value, ada_asset_name, ada_policy_id}
use lb_v2/types.{Asset, PenaltyConfig, PoolDatum, TreasuryDatum, ValidatorHash}
use aiken/builtin
use aiken/dict
use aiken/hash
use aiken/transaction.{DatumHash, InlineDatum, NoDatum, Output}
use aiken/transaction/value.{
AssetName, PolicyId, Value, ada_asset_name, ada_policy_id,
}
use lb_v2/types.{
Asset, CustomDatumHash, DatumMap, PenaltyConfig, PoolDatum, RDatumHash,
RInlineDatum, RNoDatum, TreasuryDatum, ValidatorHash,
}
use lb_v2/utils.{
amm_authen_policy_id, amm_factory_auth_asset_name, amm_pool_auth_asset_name,
default_burn_liquidity, treasury_auth_an, treasury_minimum_ada, two_day_ms,
create_pool_comission, default_burn_liquidity, max_base_fee_numerator,
max_penalty, min_base_fee_numerator, min_pool_allocation, treasury_auth_an,
treasury_minimum_ada, two_day_ms,
}

pub fn validate_creating_treasury_out(
Expand All @@ -16,6 +25,7 @@ pub fn validate_creating_treasury_out(
order_hash: ValidatorHash,
factory_policy_id: PolicyId,
end_valid_time_range: Int,
datums: DatumMap,
) -> Bool {
expect Output {
value: treasury_out_value,
Expand All @@ -33,13 +43,16 @@ pub fn validate_creating_treasury_out(
raise_asset: t_raise_asset,
start_time,
end_time,
receiver_datum,
pool_allocation,
minimum_order_raise,
minimum_raise,
maximum_raise,
reserve_base,
reserve_raise,
total_liquidity,
penalty_config,
pool_base_fee,
total_penalty,
is_cancelled,
is_manager_collected,
Expand All @@ -51,7 +64,11 @@ pub fn validate_creating_treasury_out(
value.zero()
|> value.add(factory_policy_id, treasury_auth_an, 1)
|> value.add(base_asset_pid, base_asset_an, reserve_base)
|> value.add(ada_policy_id, ada_asset_name, treasury_minimum_ada)
|> value.add(
ada_policy_id,
ada_asset_name,
treasury_minimum_ada + create_pool_comission,
)
and {
// treasury datum
base_asset != raise_asset,
Expand All @@ -65,6 +82,15 @@ pub fn validate_creating_treasury_out(
t_raise_asset == raise_asset,
start_time > end_valid_time_range,
start_time < end_time,
// Make sure that datum with datum_hash has appeared on the blockchain, \
// later we can use methods such as db_sync, kupo to rebuild the datum.
when receiver_datum is {
RNoDatum -> True
RDatumHash { hash: datum_hash } -> dict.has_key(datums, datum_hash)
RInlineDatum { hash: datum_hash } -> dict.has_key(datums, datum_hash)
},
pool_allocation >= min_pool_allocation,
pool_allocation <= 100,
when minimum_order_raise is {
Some(min_order) -> min_order > 0
_ -> True
Expand Down Expand Up @@ -92,10 +118,12 @@ pub fn validate_creating_treasury_out(
penalty_start_time >= end_time - two_day_ms,
percent > 0,
// Business requires maximum penalty rate is 25 percent
percent <= 25,
percent <= max_penalty,
}
_ -> True
},
min_base_fee_numerator <= pool_base_fee,
pool_base_fee <= max_base_fee_numerator,
total_penalty == 0,
is_cancelled == False,
is_manager_collected == False,
Expand All @@ -118,19 +146,23 @@ pub fn validate_create_dex_pool(
mint_value: Value,
amm_pool_datum: PoolDatum,
factory_policy_id: PolicyId,
project_owner_outputs: List<Output>,
receiver_output: Output,
) -> Bool {
let TreasuryDatum {
collected_fund,
base_asset,
raise_asset,
receiver,
receiver_datum,
pool_allocation,
minimum_raise,
maximum_raise,
reserve_base,
reserve_raise,
total_penalty,
is_cancelled,
is_manager_collected,
pool_base_fee,
..
} = treasury_in_datum
let final_reserve_raise =
Expand All @@ -157,61 +189,65 @@ pub fn validate_create_dex_pool(
},
}
let (asset_a, asset_b) = utils.sort_two_assets(base_asset, raise_asset)
let (datum_in_reserve_a, datum_in_reserve_b) =
let (lbe_reserve_a, lbe_reserve_b) =
when asset_a == base_asset is {
True -> (reserve_base, final_reserve_raise)
False -> (final_reserve_raise, reserve_base)
}
let Asset { policy_id: asset_a_policy_id, asset_name: asset_a_asset_name } =
asset_a
let Asset { policy_id: asset_b_policy_id, asset_name: asset_b_asset_name } =
asset_b
let PoolDatum {
asset_a: pool_asset_a,
asset_b: pool_asset_b,
reserve_a: pool_reserve_a,
reserve_b: pool_reserve_b,
base_fee_a_numerator,
total_liquidity: pool_total_liquidity,
..
} = amm_pool_datum
let lp_asset_name =
utils.compute_lp_asset_name(
asset_a_policy_id,
asset_a_asset_name,
asset_b_policy_id,
asset_b_asset_name,
let (
expected_datum_reserve_a,
expected_datum_reserve_b,
treasury_lp_amount,
expected_receiver_value,
lp_asset_name,
) =
calculate_allocation(
lbe_reserve_a: lbe_reserve_a,
lbe_reserve_b: lbe_reserve_b,
pool_allocation: pool_allocation,
pool_total_liquidity: pool_total_liquidity,
asset_a: asset_a,
asset_b: asset_b,
amm_authen_policy_id: amm_authen_policy_id,
)
expect and {
// check mint value of this redeemer to make sure tx not burn manager token or mint/burn order tokens,...
// only mint AMM tokens related.
mint_value == (
value.zero()
|> value.add(amm_authen_policy_id, amm_factory_auth_asset_name, 1)
|> value.add(amm_authen_policy_id, amm_pool_auth_asset_name, 1)
|> value.add(amm_authen_policy_id, lp_asset_name, 9223372036854775807)
),
// create pool with correct asset_a, asset_b
asset_a == pool_asset_a,
asset_b == pool_asset_b,
datum_in_reserve_a == pool_reserve_a,
datum_in_reserve_b == pool_reserve_b,
// create pool with correct reserve
pool_reserve_a == expected_datum_reserve_a,
pool_reserve_b == expected_datum_reserve_b,
// create pool with Base Fee correctly!
pool_base_fee == base_fee_a_numerator,
}
expect Output {
value: treasury_out_value,
datum: InlineDatum(treasury_out_datum_raw),
..
} = treasury_output
let total_lbe_lp = pool_total_liquidity - default_burn_liquidity
let expected_owner_lp_outputs = total_lbe_lp / 2
let treasury_lp_amount = total_lbe_lp - expected_owner_lp_outputs
expect treasury_out_datum: TreasuryDatum = treasury_out_datum_raw
let owner_lp_amount =
list.foldl(
project_owner_outputs,
0,
fn(output, acc) {
let Output { value, .. } = output
acc + value.quantity_of(value, amm_authen_policy_id, lp_asset_name)
},
)
let Output {
value: receiver_value,
datum: receiver_datum_out,
address: receiver_address_out,
..
} = receiver_output
and {
treasury_out_value == (
value.zero()
Expand All @@ -224,6 +260,78 @@ pub fn validate_create_dex_pool(
..treasury_in_datum,
total_liquidity: treasury_lp_amount,
},
owner_lp_amount == expected_owner_lp_outputs,
value.without_lovelace(expected_receiver_value) == value.without_lovelace(
receiver_value,
),
value.lovelace_of(expected_receiver_value) <= value.lovelace_of(
receiver_value,
),
when receiver_datum is {
RNoDatum -> receiver_datum_out == NoDatum
RDatumHash { hash: datum_hash } ->
receiver_datum_out == DatumHash(datum_hash)
RInlineDatum { hash: datum_hash } -> {
expect InlineDatum(receiver_datum_raw) = receiver_datum_out
let out_datum_hash: CustomDatumHash =
receiver_datum_raw
|> builtin.serialise_data
|> hash.blake2b_256
out_datum_hash == datum_hash
}
},
receiver_address_out == receiver,
}
}

// return (
// Allocation Data:
// - pool datum reserve a
// - pool datum reserve b
// - lp asset amount for treasury(to distrubute to users)
// - receiver value
// Bonus data
// - lp asset name(calculating lp an in this function is more convenient)
// )
pub fn calculate_allocation(
lbe_reserve_a: Int,
lbe_reserve_b: Int,
pool_allocation: Int,
pool_total_liquidity: Int,
asset_a: Asset,
asset_b: Asset,
amm_authen_policy_id: PolicyId,
) -> (Int, Int, Int, Value, AssetName) {
let datum_reserve_a = lbe_reserve_a * pool_allocation / 100
let datum_reserve_b = lbe_reserve_b * pool_allocation / 100

let Asset(asset_a_pid, asset_a_tn) = asset_a
let Asset(asset_b_pid, asset_b_tn) = asset_b

let lp_asset_name =
utils.compute_lp_asset_name(
asset_a_pid,
asset_a_tn,
asset_b_pid,
asset_b_tn,
)
let total_lbe_lp = pool_total_liquidity - default_burn_liquidity
let expected_receiver_lp_tokens =
total_lbe_lp * ( pool_allocation - 50 ) / pool_allocation
let treasury_lp_amount = total_lbe_lp - expected_receiver_lp_tokens

let expected_receiver_value =
value.from_asset(asset_a_pid, asset_a_tn, lbe_reserve_a - datum_reserve_a)
|> value.add(asset_b_pid, asset_b_tn, lbe_reserve_b - datum_reserve_b)
|> value.add(
amm_authen_policy_id,
lp_asset_name,
expected_receiver_lp_tokens,
)
(
datum_reserve_a,
datum_reserve_b,
treasury_lp_amount,
expected_receiver_value,
lp_asset_name,
)
}
Loading

0 comments on commit 7479364

Please sign in to comment.