From 41c6948fd6d7a96d8552229c663b6e326a30e3d5 Mon Sep 17 00:00:00 2001 From: Richard Nguyen Date: Mon, 20 Nov 2023 14:42:27 +0700 Subject: [PATCH] implement Pool Redeemers for Admin role and using withdrawal script to reduce order spending cost (#12) * implement Pool Redeemers for Admin role * using withdrawal script to reduce order spending cost (#11) --- aiken.lock | 4 +- aiken.toml | 2 +- lib/amm_dex_v2/order_validation.ak | 393 ++++++------- lib/amm_dex_v2/pool_validation.ak | 871 ++++++++++++++++++++++++++-- lib/amm_dex_v2/types.ak | 25 +- lib/amm_dex_v2/utils.ak | 128 ++-- plutus.json | 128 ++-- validators/authen_minting_policy.ak | 40 +- validators/factory_validator.ak | 12 +- validators/order_validator.ak | 65 +-- validators/pool_validator.ak | 585 +++---------------- 11 files changed, 1306 insertions(+), 947 deletions(-) diff --git a/aiken.lock b/aiken.lock index ea3e8c8..0a72eb3 100644 --- a/aiken.lock +++ b/aiken.lock @@ -3,12 +3,12 @@ [[requirements]] name = "aiken-lang/stdlib" -version = "1.6.0" +version = "1.7.0" source = "github" [[packages]] name = "aiken-lang/stdlib" -version = "1.6.0" +version = "1.7.0" requirements = [] source = "github" diff --git a/aiken.toml b/aiken.toml index 33b3cd1..1219079 100644 --- a/aiken.toml +++ b/aiken.toml @@ -10,5 +10,5 @@ platform = "github" [[dependencies]] name = "aiken-lang/stdlib" -version = "1.6.0" +version = "1.7.0" source = "github" diff --git a/lib/amm_dex_v2/order_validation.ak b/lib/amm_dex_v2/order_validation.ak index 0caf4cb..85b4bb0 100644 --- a/lib/amm_dex_v2/order_validation.ak +++ b/lib/amm_dex_v2/order_validation.ak @@ -51,7 +51,6 @@ fn validate_swap_exact_in( } else { temp_amount_in } - let has_enough_amount_in = amount_in > 0 let amount_out = math.calculate_amount_out( reserve_in: reserve_in, @@ -72,22 +71,15 @@ fn validate_swap_exact_in( ) None -> 0 } - let not_over_slippage = checking_slippage_fn(amount_out) - let actual_amount_out = - value.quantity_of( - order_out_value, - asset_out_policy_id, - asset_out_asset_name, - ) - let is_valid_amount_out = - if utils.is_ada_asset(asset_out_policy_id, asset_out_asset_name) { - amount_out + output_ada == actual_amount_out - } else { - let ada_amount = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - amount_out == actual_amount_out && ada_amount == output_ada + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, output_ada) + |> value.add(asset_out_policy_id, asset_out_asset_name, amount_out) + expect and { + amount_in > 0, + checking_slippage_fn(amount_out), + expect_order_value_out == order_out_value, } - expect has_enough_amount_in && not_over_slippage && is_valid_amount_out if a_to_b_direction { ( datum_reserve_a + amount_in - earned_fee_in, @@ -146,7 +138,6 @@ fn validate_swap_exact_out( } else { temp_amount_in } - let has_enough_amount_in = maximum_amount_in > 0 let necessary_amount_in = math.calculate_amount_in( reserve_in, @@ -167,28 +158,20 @@ fn validate_swap_exact_out( ) None -> 0 } - let not_over_slippage = necessary_amount_in <= maximum_amount_in - let actual_amount_out = - value.quantity_of( - order_out_value, - asset_out_policy_id, - asset_out_asset_name, - ) - let ada_amount = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - let change_amount_in = - value.quantity_of(order_out_value, asset_in_policy_id, asset_in_asset_name) - let is_valid_amount_out = - if utils.is_ada_asset(asset_in_policy_id, asset_in_asset_name) { - actual_amount_out == expected_receive && ada_amount == output_ada + maximum_amount_in - necessary_amount_in - } else if - utils.is_ada_asset(asset_out_policy_id, asset_out_asset_name){ - - actual_amount_out == expected_receive + output_ada && change_amount_in == maximum_amount_in - necessary_amount_in - } else { - actual_amount_out == expected_receive && change_amount_in == maximum_amount_in - necessary_amount_in && ada_amount == output_ada + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, output_ada) + |> value.add(asset_out_policy_id, asset_out_asset_name, expected_receive) + |> value.add( + asset_in_policy_id, + asset_in_asset_name, + maximum_amount_in - necessary_amount_in, + ) + expect and { + maximum_amount_in > 0, + necessary_amount_in <= maximum_amount_in, + expect_order_value_out == order_out_value, } - expect has_enough_amount_in && not_over_slippage && is_valid_amount_out if a_to_b_direction { ( datum_reserve_a + necessary_amount_in - earned_fee_in, @@ -245,8 +228,6 @@ fn validate_deposit( } else { temp_amount_a } - let has_enough_amount_in = - amount_a >= 0 && amount_b >= 0 && amount_a + amount_b > 0 let (earned_fee_a, earned_fee_b, lp_amount) = math.calculate_deposit_amount( amount_a: amount_a, @@ -258,15 +239,17 @@ fn validate_deposit( trading_fee_denominator: trading_fee_denominator, profit_sharing_opt: profit_sharing_opt, ) - let not_over_slippage = lp_amount >= minimum_lp - let lp_amount_out = - value.quantity_of(order_out_value, lp_asset_policy_id, lp_asset_asset_name) - let ada_amount_out = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - - let is_valid_amount_out = - ada_amount_out == output_ada && lp_amount_out == lp_amount - expect has_enough_amount_in && not_over_slippage && is_valid_amount_out + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, output_ada) + |> value.add(lp_asset_policy_id, lp_asset_asset_name, lp_amount) + expect and { + amount_a >= 0, + amount_b >= 0, + amount_a + amount_b > 0, + lp_amount >= minimum_lp, + expect_order_value_out == order_out_value, + } ( datum_reserve_a + amount_a - earned_fee_a, datum_reserve_b + amount_b - earned_fee_b, @@ -301,7 +284,6 @@ fn validate_withdraw( let Asset { policy_id: lp_policy_id, asset_name: lp_asset_name } = lp_asset let withdrawal_lp_amount = value.quantity_of(order_in_value, lp_policy_id, lp_asset_name) - let has_enough_amount_in = withdrawal_lp_amount > 0 let (amount_a_out, amount_b_out) = math.calculate_withdraw( datum_reserve_a, @@ -309,21 +291,17 @@ fn validate_withdraw( withdrawal_lp_amount, total_liquidity, ) - let not_over_slippage = - amount_a_out >= minimum_amount_a && amount_b_out >= minimum_amount_b - let actual_amount_a_out = - value.quantity_of(order_out_value, asset_a_policy_id, asset_a_asset_name) - let actual_amount_b_out = - value.quantity_of(order_out_value, asset_b_policy_id, asset_b_asset_name) - let ada_amount_out = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - let is_valid_amount_out = - if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { - actual_amount_a_out == amount_a_out + output_ada && actual_amount_b_out == amount_b_out - } else { - ada_amount_out == output_ada && actual_amount_a_out == amount_a_out && actual_amount_b_out == amount_b_out + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, output_ada) + |> value.add(asset_a_policy_id, asset_a_asset_name, amount_a_out) + |> value.add(asset_b_policy_id, asset_b_asset_name, amount_b_out) + expect and { + withdrawal_lp_amount > 0, + amount_a_out >= minimum_amount_a, + amount_b_out >= minimum_amount_b, + expect_order_value_out == order_out_value, } - expect has_enough_amount_in && not_over_slippage && is_valid_amount_out ( datum_reserve_a - amount_a_out, datum_reserve_b - amount_b_out, @@ -369,7 +347,6 @@ fn validate_zap_out( policy_id: lp_policy_id, asset_name: lp_asset_name, ) - let has_enough_amount_in = withdrawal_lp_amount > 0 let (earned_fee_a, earned_fee_b, amount_out) = math.calculate_zap_out( @@ -382,22 +359,15 @@ fn validate_zap_out( trading_fee_denominator: trading_fee_denominator, profit_sharing_opt: profit_sharing_opt, ) - let not_over_slippage = amount_out >= minimum_receive - let ada_amount = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - let actual_amount_out = - value.quantity_of( - order_out_value, - asset_out_policy_id, - asset_out_asset_name, - ) - let is_valid_amount_out = - if utils.is_ada_asset(asset_out_policy_id, asset_out_asset_name) { - actual_amount_out == output_ada + amount_out - } else { - actual_amount_out == amount_out && output_ada == ada_amount + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, output_ada) + |> value.add(asset_out_policy_id, asset_out_asset_name, amount_out) + expect and { + withdrawal_lp_amount > 0, + amount_out >= minimum_receive, + expect_order_value_out == order_out_value, } - expect has_enough_amount_in && not_over_slippage && is_valid_amount_out if a_to_b_direction { ( datum_reserve_a - earned_fee_a, @@ -460,7 +430,6 @@ fn validate_partial_swap( } else { temp_amount_in } - let has_enough_amount_in = amount_in > 0 let max_in_swap = math.calculate_max_in_swap( reserve_in: reserve_in, @@ -470,13 +439,13 @@ fn validate_partial_swap( io_ratio_numerator: io_ratio_numerator, io_ratio_denominator: io_ratio_denominator, ) - let (swap_amount_in, remaining_amount_in) = + let swap_amount_in = if amount_in <= max_in_swap { - (amount_in, 0) + amount_in } else { - (max_in_swap, amount_in - max_in_swap) + max_in_swap } - expect swap_amount_in > 0 && swap_amount_in >= minimum_swap_amount_required + let remaining_amount_in = amount_in - swap_amount_in let amount_out = math.calculate_amount_out( reserve_in: reserve_in, @@ -497,30 +466,24 @@ fn validate_partial_swap( ) None -> 0 } - let ada_out_require = batcher_fee * ( hops - 1 ) + output_ada let old_amount_out = value.quantity_of(order_in_value, asset_out_policy_id, asset_out_asset_name) - let new_amount_out = - value.quantity_of( - order_out_value, - asset_out_policy_id, - asset_out_asset_name, - ) - let new_amount_in = - value.quantity_of(order_out_value, asset_in_policy_id, asset_in_asset_name) - let is_valid_out_value = - if utils.is_ada_asset(asset_in_policy_id, asset_in_asset_name) { - new_amount_in == remaining_amount_in + ada_out_require && new_amount_out == old_amount_out + amount_out - } else if - utils.is_ada_asset(asset_out_policy_id, asset_out_asset_name){ - - new_amount_in == remaining_amount_in && new_amount_out == old_amount_out + amount_out * ada_out_require - } else { - let ada_amount = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - new_amount_in == remaining_amount_in && new_amount_out == old_amount_out + amount_out && ada_out_require == ada_amount + let ada_out_require = batcher_fee * ( hops - 1 ) + output_ada + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, ada_out_require) + |> value.add( + asset_out_policy_id, + asset_out_asset_name, + old_amount_out + amount_out, + ) + |> value.add(asset_in_policy_id, asset_in_asset_name, remaining_amount_in) + expect and { + amount_in > 0, + swap_amount_in > 0, + swap_amount_in >= minimum_swap_amount_required, + expect_order_value_out == order_out_value, } - expect has_enough_amount_in && is_valid_out_value let has_next_swap = hops - 1 > 0 && remaining_amount_in >= minimum_swap_amount_required if a_to_b_direction { @@ -573,7 +536,6 @@ fn validate_withdraw_imbalance( let Asset { policy_id: lp_policy_id, asset_name: lp_asset_name } = lp_asset let withdrawal_lp_amount = value.quantity_of(order_in_value, lp_policy_id, lp_asset_name) - let has_enough_amount_in = withdrawal_lp_amount > 0 let (earned_fee_a, earned_fee_b, amount_a_out, amount_b_out) = math.calculate_withdraw_imbalance( expect_ab_ratio_numerator: ratio_asset_a, @@ -586,20 +548,16 @@ fn validate_withdraw_imbalance( total_liquidity: total_liquidity, profit_sharing_opt: profit_sharing_opt, ) - let not_over_slippage = amount_a_out >= minimum_amount_a - let actual_amount_a_out = - value.quantity_of(order_out_value, asset_a_policy_id, asset_a_asset_name) - let actual_amount_b_out = - value.quantity_of(order_out_value, asset_b_policy_id, asset_b_asset_name) - let ada_amount_out = - value.quantity_of(order_out_value, ada_policy_id, ada_asset_name) - let is_valid_amount_out = - if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { - actual_amount_a_out == amount_a_out + output_ada && actual_amount_b_out == amount_b_out - } else { - ada_amount_out == output_ada && actual_amount_a_out == amount_a_out && actual_amount_b_out == amount_b_out + let expect_order_value_out = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, output_ada) + |> value.add(asset_a_policy_id, asset_a_asset_name, amount_a_out) + |> value.add(asset_b_policy_id, asset_b_asset_name, amount_b_out) + expect and { + withdrawal_lp_amount > 0, + amount_a_out >= minimum_amount_a, + expect_order_value_out == order_out_value, } - expect has_enough_amount_in && not_over_slippage && is_valid_amount_out ( datum_reserve_a - amount_a_out - earned_fee_a, datum_reserve_b - amount_b_out - earned_fee_b, @@ -669,19 +627,19 @@ fn get_multi_routing_swap_amount_out( expect amount_out > 0 expect when direction is { - AToB -> - old_datum_reserve_a + amount_in - earned_fee_in == new_datum_reserve_a && // hihi - old_datum_reserve_b - amount_out == new_datum_reserve_b && // hihi - old_value_reserve_a + amount_in == new_value_reserve_a && // hihi - old_value_reserve_b - amount_out == new_value_reserve_b - // hihi - BToA -> - old_datum_reserve_a - amount_out == new_datum_reserve_a && // hihi - old_datum_reserve_b + amount_in - earned_fee_in == new_datum_reserve_b && // hihi - old_value_reserve_a - amount_out == new_value_reserve_a && // hihi - old_value_reserve_b + amount_in == new_value_reserve_b + AToB -> and { + old_datum_reserve_a + amount_in - earned_fee_in == new_datum_reserve_a, + old_datum_reserve_b - amount_out == new_datum_reserve_b, + old_value_reserve_a + amount_in == new_value_reserve_a, + old_value_reserve_b - amount_out == new_value_reserve_b, + } + BToA -> and { + old_datum_reserve_a - amount_out == new_datum_reserve_a, + old_datum_reserve_b + amount_in - earned_fee_in == new_datum_reserve_b, + old_value_reserve_a - amount_out == new_value_reserve_a, + old_value_reserve_b + amount_in == new_value_reserve_b, + } } - // hihi amount_out } @@ -802,8 +760,10 @@ pub fn validate_order_receiver( } None -> raw_order_output_datum == NoDatum } - expect receiver == output_address && is_valid_receiver_datum - True + and { + receiver == output_address, + is_valid_receiver_datum, + } } // TODO: validate order input value size @@ -835,8 +795,11 @@ pub fn apply_orders( .. } = input let Output { value: order_out_value, .. } = output - let order_in_datum = - utils.must_find_order_datum(datum_map, raw_order_in_datum) + expect order_in_datum: OrderDatum = + when raw_order_in_datum is { + InlineDatum(d) -> d + _ -> utils.must_find_script_datum(datum_map, raw_order_in_datum) + } let OrderDatum { sender, receiver, @@ -850,13 +813,14 @@ pub fn apply_orders( let new_state = when order_step is { SwapExactIn(direction, minimum_receive) -> { - expect minimum_receive > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + minimum_receive > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } let a_to_b_direction = when direction is { AToB -> True @@ -880,13 +844,14 @@ pub fn apply_orders( ) } StopLoss(direction, stop_loss_receive) -> { - expect stop_loss_receive > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + stop_loss_receive > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } let a_to_b_direction = when direction is { AToB -> True @@ -910,13 +875,15 @@ pub fn apply_orders( ) } OCO(direction, minimum_receive, stop_loss_receive) -> { - expect minimum_receive > 0 && stop_loss_receive > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + minimum_receive > 0, + stop_loss_receive > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } let a_to_b_direction = when direction is { AToB -> True @@ -940,13 +907,14 @@ pub fn apply_orders( ) } SwapExactOut(direction, expected_receive) -> { - expect expected_receive > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + expected_receive > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } let a_to_b_direction = when direction is { AToB -> True @@ -968,13 +936,14 @@ pub fn apply_orders( ) } Deposit(minimum_lp) -> { - expect minimum_lp > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + minimum_lp > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } validate_deposit( order_in_value: order_in_value, order_out_value: order_out_value, @@ -991,13 +960,15 @@ pub fn apply_orders( ) } Withdraw(minimum_asset_a, minimum_asset_b) -> { - expect minimum_asset_a > 0 && minimum_asset_b > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + minimum_asset_a > 0, + minimum_asset_b > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } validate_withdraw( order_in_value: order_in_value, order_out_value: order_out_value, @@ -1011,13 +982,14 @@ pub fn apply_orders( ) } ZapOut(direction, minimum_receive) -> { - expect minimum_receive > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + minimum_receive > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } let a_to_b_direction = when direction is { AToB -> True @@ -1045,8 +1017,11 @@ pub fn apply_orders( hops, minimum_swap_amount_required, ) -> { - expect - io_ratio_numerator > 0 && io_ratio_numerator > 0 && hops > 0 + expect and { + io_ratio_numerator > 0, + io_ratio_numerator > 0, + hops > 0, + } let a_to_b_direction = when direction is { AToB -> True @@ -1109,18 +1084,20 @@ pub fn apply_orders( order_out_hops, order_out_minimum_swap_amount_required, ) = order_out_step - order_out_script_hash == order_hash && // hihi - sender == order_out_sender && // hihi - receiver == order_out_receiver && // hihi - receiver_datum_hash_opt == order_out_receiver_datum_hash_opt && // hihi - batcher_fee == order_out_batcher_fee && // hihi - output_ada == order_out_output_ada && // hihi - order_lp_asset == order_out_lp_asset && // hihi - direction == order_out_step_direction && // hihi - io_ratio_numerator == order_out_io_ratio_numerator && // hihi - io_ratio_denominator == order_out_io_ratio_denominator && // hihi - hops - 1 == order_out_hops && // hihi - minimum_swap_amount_required == order_out_minimum_swap_amount_required + and { + order_out_script_hash == order_hash, + sender == order_out_sender, + receiver == order_out_receiver, + receiver_datum_hash_opt == order_out_receiver_datum_hash_opt, + batcher_fee == order_out_batcher_fee, + output_ada == order_out_output_ada, + order_lp_asset == order_out_lp_asset, + direction == order_out_step_direction, + io_ratio_numerator == order_out_io_ratio_numerator, + io_ratio_denominator == order_out_io_ratio_denominator, + hops - 1 == order_out_hops, + minimum_swap_amount_required == order_out_minimum_swap_amount_required, + } } else { validate_order_receiver( receiver: receiver, @@ -1137,14 +1114,16 @@ pub fn apply_orders( ) } WithdrawImbalance(ratio_asset_a, ratio_asset_b, minimum_asset_a) -> { - expect - ratio_asset_a > 0 && ratio_asset_b > 0 && minimum_asset_a > 0 - expect - validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: output, - ) + expect and { + ratio_asset_a > 0, + ratio_asset_b > 0, + minimum_asset_a > 0, + validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: output, + ), + } validate_withdraw_imbalance( order_in_value: order_in_value, order_out_value: order_out_value, diff --git a/lib/amm_dex_v2/pool_validation.ak b/lib/amm_dex_v2/pool_validation.ak index 29db1fd..74a8554 100644 --- a/lib/amm_dex_v2/pool_validation.ak +++ b/lib/amm_dex_v2/pool_validation.ak @@ -1,64 +1,847 @@ +use aiken/dict.{Dict} +use aiken/interval.{Finite, Interval, IntervalBound} use aiken/list -use aiken/transaction.{Input, Output, ValidityRange} -use aiken/transaction/credential.{Address, VerificationKeyCredential} -use aiken/transaction/value.{MintedValue, PolicyId} -use amm_dex_v2/types.{PubKeyHash} +use aiken/transaction.{ + InlineDatum, Input, Output, OutputReference, Redeemer, ScriptPurpose, + ValidityRange, +} +use aiken/transaction/credential.{Address, ScriptCredential} +use aiken/transaction/value.{ + AssetName, MintedValue, PolicyId, ada_asset_name, ada_policy_id, +} +use amm_dex_v2/order_validation +use amm_dex_v2/types.{ + Asset, BatchingPool, DatumMap, OrderDatum, PoolDatum, SwapMultiRouting, + UpdatePoolFee, UpdatePoolFeeOrStakeCredentialAction, UpdatePoolStakeCredential, +} use amm_dex_v2/utils -fn validate_batcher( - inputs: List, +pub fn validate_batcher_license( + license_input: Input, validity_range: ValidityRange, - extra_signatories: List, - batcher_address: Address, license_policy_id: PolicyId, - license_index: Int, maximum_deadline_range: Int, ) -> Bool { - // Batching Redeemer provides @license_index which help save calculation cost - let Input { output: Output { value: license_value, .. }, .. } = - utils.must_parse_option(list.at(inputs, license_index)) - let license_deadline = - utils.find_license_deadline(license_policy_id, license_value) - let (start_valid_time_range, end_valid_time_range) = - utils.find_posix_time_range(validity_range) + let Input { output: Output { value: license_value, .. }, .. } = license_input + expect [(license_asset_name, license_amount)] = + license_value + |> value.tokens(license_policy_id) + |> dict.to_list() + let license_deadline = utils.bytearray_to_int(license_asset_name) + let Interval { + lower_bound: IntervalBound { bound_type: lower_bound_type, .. }, + upper_bound: IntervalBound { bound_type: upper_bound_type, .. }, + } = validity_range + expect Finite(start_valid_time_range) = lower_bound_type + expect Finite(end_valid_time_range) = upper_bound_type // - Only Batcher with valid license token can trigger @ApplyPool redeemer validation. // - A valid license token is the token with @tokenName (representing for expired milliseconds) // and must be within the time range from current_time to current_time + maximum_deadline_range - let Address { payment_credential: batcher_payment_credential, .. } = - batcher_address - expect VerificationKeyCredential(batcher_pkh) = batcher_payment_credential - // Verify Batcher with valid license token must be a signer of transaction + and { + license_amount == 1, + license_deadline >= end_valid_time_range, + license_deadline <= start_valid_time_range + maximum_deadline_range, + } +} + +fn find_pool_input_and_output_in_batching( + pool_input_ref: OutputReference, + inputs: List, + outputs: List, +) -> (Input, Output) { + expect Some(pool_input) = + list.find( + inputs, + fn(input) { + let Input { output_reference: out_ref, .. } = input + pool_input_ref == out_ref + }, + ) + let Input { output: Output { address: pool_in_address, .. }, .. } = pool_input + let Address { payment_credential: pool_payment_credential, .. } = + pool_in_address + // Batching transaction requires single Pool UTxO in both Inputs and Outputs + expect [pool_output] = + list.filter( + outputs, + fn(output) { + let Output { address: addr, .. } = output + let Address { payment_credential: payment_cred, .. } = addr + pool_payment_credential == payment_cred + }, + ) + + let Output { address: pool_out_address, .. } = pool_output + expect pool_out_address == pool_in_address + // Due to combination with Minswap Stake Address, Pool Contract can have multiple Base Addresses + // This logic will verify that the transaction is processing in single Pool + // Verify that having only one Pool Input and Pool Output in the transaction + expect [_] = + list.filter( + inputs, + fn(input) { + let Input { output: o, .. } = input + let Output { address: addr, .. } = o + let Address { payment_credential: payment_cred, .. } = addr + payment_cred == pool_payment_credential + }, + ) + (pool_input, pool_output) +} + +// fn get_batching_pool( +// pool_input: Input, +// authen_policy_id: PolicyId, +// pool_auth_asset_name: AssetName, +// pool_in_datum: PoolDatum, +// ) -> BatchingPool { +// let Input { +// output: Output { address: pool_in_address, value: pool_in_value, .. }, +// .. +// } = pool_input + +// let PoolDatum { +// asset_a, +// asset_b, +// total_liquidity, +// reserve_a: datum_reserve_a, +// reserve_b: datum_reserve_b, +// trading_fee_numerator, +// trading_fee_denominator, +// order_hash, +// profit_sharing_opt, +// } = pool_in_datum + +// 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 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 lp_asset = +// Asset { policy_id: authen_policy_id, asset_name: lp_asset_name } +// let estimate_value_reserve_a = +// value.quantity_of(pool_in_value, asset_a_policy_id, asset_a_asset_name) +// let value_reserve_a = +// if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { +// estimate_value_reserve_a - 3000000 +// } else { +// estimate_value_reserve_a +// } +// let value_reserve_b = +// value.quantity_of(pool_in_value, asset_b_policy_id, asset_b_asset_name) +// let remaining_liquidity_supply = +// value.quantity_of(pool_in_value, authen_policy_id, lp_asset_name) +// let estimate_pool_in_value = +// value.zero() +// |> value.add(ada_policy_id, ada_asset_name, 3000000) +// |> value.add(authen_policy_id, pool_auth_asset_name, 1) +// |> value.add(asset_a_policy_id, asset_a_asset_name, value_reserve_a) +// |> value.add(asset_b_policy_id, asset_b_asset_name, value_reserve_b) +// |> value.add(authen_policy_id, lp_asset_name, remaining_liquidity_supply) +// expect estimate_pool_in_value == pool_in_value +// // let estimate_pool_in_value = +// // value.zero() +// // |> value.add(ada_policy_id, ada_asset_name, 3000000) +// // |> value.add(authen_policy_id, pool_auth_asset_name, 1) +// // |> value.add(asset_a_policy_id, asset_a_asset_name, value_reserve_a_in) +// // |> value.add(asset_b_policy_id, asset_b_asset_name, value_reserve_b_in) +// // |> value.add(authen_policy_id, lp_asset_name, value_remaining_liquidity_in) +// // expect estimate_pool_in_value == pool_in_value +// // let estimate_pool_out_value = +// // value.zero() +// // |> value.add(ada_policy_id, ada_asset_name, 3000000) +// // |> value.add(authen_policy_id, pool_auth_asset_name, 1) +// // |> value.add(asset_a_policy_id, asset_a_asset_name, value_reserve_a_out) +// // |> value.add(asset_b_policy_id, asset_b_asset_name, value_reserve_b_out) +// // |> value.add(authen_policy_id, lp_asset_name, value_remaining_liquidity_out) +// // expect estimate_pool_out_value == pool_out_value +// // expect +// // value_remaining_liquidity_out - value_remaining_liquidity_in == pool_in_total_liquidity - pool_out_total_liquidity +// BatchingPool { +// asset_a, +// asset_b, +// lp_asset, +// trading_fee_numerator, +// trading_fee_denominator, +// profit_sharing: profit_sharing_opt, +// order_hash, +// address: pool_in_address, +// pool_state_in: ( +// datum_reserve_a, +// datum_reserve_b, +// value_reserve_a, +// value_reserve_b, +// total_liquidity, +// ), +// } +// } + +fn get_batching_pool( + pool_input: Input, + pool_output: Output, + authen_policy_id: PolicyId, + pool_auth_asset_name: AssetName, + pool_in_datum: PoolDatum, + datum_map: DatumMap, + require_total_liquidity_unchange: Bool, +) -> BatchingPool { + let Input { + output: Output { address: pool_in_address, value: pool_in_value, .. }, + .. + } = pool_input + let Output { value: pool_out_value, datum: pool_out_datum_raw, .. } = + pool_output + + let PoolDatum { + asset_a: pool_in_asset_a, + asset_b: pool_in_asset_b, + total_liquidity: pool_in_total_liquidity, + reserve_a: pool_in_datum_reserve_a, + reserve_b: pool_in_datum_reserve_b, + trading_fee_numerator: pool_in_trading_fee_numerator, + trading_fee_denominator: pool_in_trading_fee_denominator, + order_hash: pool_in_order_hash, + profit_sharing_opt: pool_in_profit_sharing_opt, + } = pool_in_datum + expect pool_out_datum: PoolDatum = + when pool_out_datum_raw is { + InlineDatum(d) -> d + _ -> utils.must_find_script_datum(datum_map, pool_out_datum_raw) + } + let PoolDatum { + asset_a: pool_out_asset_a, + asset_b: pool_out_asset_b, + total_liquidity: pool_out_total_liquidity, + reserve_a: pool_out_datum_reserve_a, + reserve_b: pool_out_datum_reserve_b, + trading_fee_numerator: pool_out_trading_fee_numerator, + trading_fee_denominator: pool_out_trading_fee_denominator, + order_hash: pool_out_order_hash, + profit_sharing_opt: pool_out_profit_sharing_opt, + } = pool_out_datum + expect - list.has(extra_signatories, batcher_pkh) && license_deadline >= end_valid_time_range && license_deadline <= start_valid_time_range + maximum_deadline_range - True + if require_total_liquidity_unchange { + pool_in_total_liquidity == pool_out_total_liquidity + } else { + True + } + expect and { + pool_in_asset_a == pool_out_asset_a, + pool_in_asset_b == pool_out_asset_b, + pool_in_trading_fee_numerator == pool_out_trading_fee_numerator, + pool_in_trading_fee_denominator == pool_out_trading_fee_denominator, + pool_in_order_hash == pool_out_order_hash, + pool_in_profit_sharing_opt == pool_out_profit_sharing_opt, + } + let Asset { policy_id: asset_a_policy_id, asset_name: asset_a_asset_name } = + pool_in_asset_a + let Asset { policy_id: asset_b_policy_id, asset_name: asset_b_asset_name } = + pool_in_asset_b + + 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 lp_asset = + Asset { policy_id: authen_policy_id, asset_name: lp_asset_name } + + // Each Pool UTxO has a Pool Authen Asset (authen_policy_id + pool_auth_asset_name) + // This asset must be only stay in Pool UTxO + // Verify Pool Authen Asset must be existed in Pool Input and Output value + // expect and { + // value.quantity_of(pool_in_value, authen_policy_id, pool_auth_asset_name) == 1, + // value.quantity_of(pool_out_value, authen_policy_id, pool_auth_asset_name) == 1, + // } + let estimate_value_reserve_a_in = + value.quantity_of(pool_in_value, asset_a_policy_id, asset_a_asset_name) + let estimate_value_reserve_a_out = + value.quantity_of(pool_out_value, asset_a_policy_id, asset_a_asset_name) + let (value_reserve_a_in, value_reserve_a_out) = + if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { + ( + estimate_value_reserve_a_in - 3000000, + estimate_value_reserve_a_out - 3000000, + ) + } else { + (estimate_value_reserve_a_in, estimate_value_reserve_a_out) + } + let value_reserve_b_in = + value.quantity_of(pool_in_value, asset_b_policy_id, asset_b_asset_name) + let value_reserve_b_out = + value.quantity_of(pool_out_value, asset_b_policy_id, asset_b_asset_name) + + let remaining_liquidity_supply_in = + value.quantity_of(pool_in_value, authen_policy_id, lp_asset_name) + let remaining_liquidity_supply_out = + value.quantity_of(pool_out_value, authen_policy_id, lp_asset_name) + let estimate_pool_in_value = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, 3000000) + |> value.add(authen_policy_id, pool_auth_asset_name, 1) + |> value.add(asset_a_policy_id, asset_a_asset_name, value_reserve_a_in) + |> value.add(asset_b_policy_id, asset_b_asset_name, value_reserve_b_in) + |> value.add(authen_policy_id, lp_asset_name, remaining_liquidity_supply_in) + let estimate_pool_out_value = + value.zero() + |> value.add(ada_policy_id, ada_asset_name, 3000000) + |> value.add(authen_policy_id, pool_auth_asset_name, 1) + |> value.add(asset_a_policy_id, asset_a_asset_name, value_reserve_a_out) + |> value.add(asset_b_policy_id, asset_b_asset_name, value_reserve_b_out) + |> value.add(authen_policy_id, lp_asset_name, remaining_liquidity_supply_out) + expect and { + estimate_pool_in_value == pool_in_value, + estimate_pool_out_value == pool_out_value, + remaining_liquidity_supply_out - remaining_liquidity_supply_in == pool_in_total_liquidity - pool_out_total_liquidity, + } + BatchingPool { + asset_a: pool_in_asset_a, + asset_b: pool_in_asset_b, + lp_asset, + trading_fee_numerator: pool_in_trading_fee_numerator, + trading_fee_denominator: pool_in_trading_fee_denominator, + profit_sharing: pool_in_profit_sharing_opt, + order_hash: pool_in_order_hash, + address: pool_in_address, + pool_state_in: ( + pool_in_datum_reserve_a, + pool_in_datum_reserve_b, + value_reserve_a_in, + value_reserve_b_in, + pool_in_total_liquidity, + ), + pool_state_out: ( + pool_out_datum_reserve_a, + pool_out_datum_reserve_b, + value_reserve_a_out, + value_reserve_b_out, + pool_out_total_liquidity, + ), + } } -fn validate_mint(mint: MintedValue) -> Bool { - let mint_value = value.from_minted_value(mint) - expect [] = value.flatten(mint_value) - True +pub fn validate_batching( + authen_policy_id: PolicyId, + pool_auth_asset_name: AssetName, + all_inputs: List, + all_outputs: List, + all_datums: DatumMap, + pool_input_ref: OutputReference, + pool_in_datum: PoolDatum, + batcher_address: Address, + input_indexes: List, +) -> Bool { + let (pool_input, pool_output) = + find_pool_input_and_output_in_batching( + pool_input_ref: pool_input_ref, + inputs: all_inputs, + outputs: all_outputs, + ) + let BatchingPool { + asset_a, + asset_b, + lp_asset, + trading_fee_numerator, + trading_fee_denominator, + profit_sharing, + order_hash, + address: pool_address, + pool_state_in, + pool_state_out, + } = + get_batching_pool( + pool_input: pool_input, + pool_output: pool_output, + authen_policy_id: authen_policy_id, + pool_auth_asset_name: pool_auth_asset_name, + pool_in_datum: pool_in_datum, + datum_map: all_datums, + require_total_liquidity_unchange: False, + ) + let user_inputs = + list.filter( + all_inputs, + fn(input) { + let Input { output: out, .. } = input + let Output { address: addr, .. } = out + let Address { payment_credential: payment_cred, .. } = addr + when payment_cred is { + ScriptCredential(hash) -> hash == order_hash + _ -> False + } + }, + ) + // Currently, transaction inputs will be sorted by TxId and TxIndex of UTxO. + // We have to calculate indexes of orders inputs sorting by the ASC created time + // on the off-chain and on-chain will sort the TxIns by the indexes + // Input Indexes in parameter will be reversed indexs of @order_inputs to reduce calculate step in On-Chain + let sorted_user_inputs = + list.foldl( + input_indexes, + [], + fn(idx, ips) { list.push(ips, utils.list_at_index(user_inputs, idx)) }, + ) + + // Order Outputs are Outputs which aren't Pool & Batcher UTxOs + // Their ordering must be the same with @sorted_order_inputs, so @order_outputs at index i is the output if @sorted_order_inputs at index i + let user_outputs = + list.filter( + all_outputs, + fn(output) { + let Output { address: addr, .. } = output + addr != pool_address && addr != batcher_address + }, + ) + + // User Inputs and Outputs must have the same length + expect utils.compare_list_length(sorted_user_inputs, user_outputs) + pool_state_out == order_validation.apply_orders( + datum_map: all_datums, + asset_a: asset_a, + asset_b: asset_b, + lp_asset: lp_asset, + trading_fee_numerator: trading_fee_numerator, + trading_fee_denominator: trading_fee_denominator, + order_hash: order_hash, + profit_sharing_opt: profit_sharing, + order_inputs: sorted_user_inputs, + order_outputs: user_outputs, + pool_state: pool_state_in, + ) } -pub fn validate_common_batching( - inputs: List, - validity_range: ValidityRange, - extra_signatories: List, +pub fn validate_swap_multi_routing( + authen_policy_id: PolicyId, + pool_auth_asset_name: AssetName, + all_inputs: List, + all_outputs: List, + all_datums: DatumMap, + pool_input_ref: OutputReference, batcher_address: Address, + routing_in_indexes: List, + routing_out_indexes: List, +) -> Bool { + let batching_pools = + find_multi_routing_pools( + authen_policy_id: authen_policy_id, + pool_auth_asset_name: pool_auth_asset_name, + pool_input_ref: pool_input_ref, + inputs: all_inputs, + outputs: all_outputs, + routing_in_indexes: routing_in_indexes, + routing_out_indexes: routing_out_indexes, + datum_map: all_datums, + ) + + let BatchingPool { + order_hash, + address: Address { payment_credential: pool_payment_credential, .. }, + lp_asset: pool_lp_asset, + .. + } = utils.list_at_index(batching_pools, 0) + + let order_inputs = + list.filter( + all_inputs, + fn(input) { + let Input { + output: Output { + address: Address { payment_credential: payment_cred, .. }, + .. + }, + .. + } = input + when payment_cred is { + ScriptCredential(hash) -> hash == order_hash + _ -> False + } + }, + ) + expect [order_input] = order_inputs + let order_outputs = + list.filter( + all_outputs, + fn(output) { + let Output { address: addr, .. } = output + let Address { payment_credential: payment_cred, .. } = addr + addr != batcher_address && payment_cred != pool_payment_credential + }, + ) + expect [order_output] = order_outputs + let Input { + output: Output { value: order_in_value, datum: raw_order_in_datum, .. }, + .. + } = order_input + let Output { value: order_out_value, .. } = order_output + let OrderDatum { + receiver, + receiver_datum_hash_opt, + step: order_step, + batcher_fee, + output_ada, + lp_asset: order_lp_asset, + .. + } = utils.must_find_order_datum(all_datums, raw_order_in_datum) + let is_valid_receiver = + order_validation.validate_order_receiver( + receiver: receiver, + receiver_datum_hash_opt: receiver_datum_hash_opt, + output: order_output, + ) + expect + batcher_fee > 0 && output_ada > 0 && is_valid_receiver && pool_lp_asset == order_lp_asset + when order_step is { + SwapMultiRouting(routings, minimum_receive) -> { + expect minimum_receive > 0 + order_validation.validate_swap_multi_routing_order( + pools: batching_pools, + routings: routings, + order_in_value: order_in_value, + order_out_value: order_out_value, + minimum_receive: minimum_receive, + batcher_fee: batcher_fee, + output_ada: output_ada, + ) + } + _ -> False + } +} + +// TODO: We need carefully verify these logic below +fn find_multi_routing_pools( + authen_policy_id: PolicyId, + pool_auth_asset_name: AssetName, + pool_input_ref: OutputReference, + inputs: List, + outputs: List, + routing_in_indexes: List, + routing_out_indexes: List, + datum_map: DatumMap, +) -> List { + let pool_ins = + list.map(routing_in_indexes, fn(idx) { utils.list_at_index(inputs, idx) }) + + let pool_outs = + list.map(routing_out_indexes, fn(idx) { utils.list_at_index(outputs, idx) }) + + let pool_len = list.length(pool_ins) + + // find input of current script + expect [own_pool_input] = + list.filter( + inputs, + fn(input) { + let Input { output_reference: out_ref, .. } = input + pool_input_ref == out_ref + }, + ) + let Input { + output: Output { + address: Address { payment_credential: own_pool_payment_credential, .. }, + .. + }, + .. + } = own_pool_input + // verify that there's only 2 pools the inputs + expect + list.length( + list.filter( + inputs, + fn(input) { + let Input { + output: Output { address: Address { payment_credential, .. }, .. }, + .. + } = input + payment_credential == own_pool_payment_credential + }, + ), + ) == pool_len + // verify that there's only 2 pools the outputs + expect + list.length( + list.filter( + outputs, + fn(output) { + let Output { address: Address { payment_credential, .. }, .. } = + output + payment_credential == own_pool_payment_credential + }, + ), + ) == pool_len + utils.zip_with( + pool_ins, + pool_outs, + fn(pool_in, pool_out) { + let Input { + output: Output { + address: pool_in_address, + datum: pool_in_datum_raw, + .. + }, + .. + } = pool_in + let Output { address: pool_out_address, .. } = pool_out + let pool_in_datum = + utils.must_find_pool_datum(datum_map, pool_in_datum_raw) + + expect pool_in_address == pool_out_address + get_batching_pool( + pool_input: pool_in, + pool_output: pool_out, + authen_policy_id: authen_policy_id, + pool_auth_asset_name: pool_auth_asset_name, + pool_in_datum: pool_in_datum, + datum_map: datum_map, + require_total_liquidity_unchange: True, + ) + }, + ) +} + +pub fn validate_update_pool_datum_or_stake_credential( + action: UpdatePoolFeeOrStakeCredentialAction, + authen_policy_id: PolicyId, + pool_auth_asset_name: AssetName, license_policy_id: PolicyId, - license_index: Int, - maximum_deadline_range: Int, - mint: MintedValue, + admin_asset_name: AssetName, + admin_index: Int, + pool_input: Input, + pool_in_datum: PoolDatum, + all_inputs: List, + all_outputs: List, + all_datums: DatumMap, + all_mints: MintedValue, + all_redeemers: Dict, +) -> Bool { + let Input { output: pool_in_output, .. } = pool_input + let Output { address: pool_in_address, value: pool_in_value, .. } = + pool_in_output + let Address { payment_credential: pool_in_address_payment_credential, .. } = + pool_in_address + + expect + value.quantity_of(pool_in_value, authen_policy_id, pool_auth_asset_name) == 1 + let admin_input = utils.list_at_index(all_inputs, admin_index) + let Input { output: Output { value: admin_value, .. }, .. } = admin_input + // Only Admin token can trigger this redeemer + expect + value.quantity_of(admin_value, license_policy_id, admin_asset_name) == 1 + // This Redeemer won't mint anything + expect value.is_zero(value.from_minted_value(all_mints)) + expect [pool_output] = + list.filter( + all_outputs, + fn(o) { + let Output { address: addr, .. } = o + let Address { payment_credential, .. } = addr + payment_credential == pool_in_address_payment_credential + }, + ) + let Output { + address: pool_out_address, + value: pool_out_value, + datum: raw_pool_out_datum, + .. + } = pool_output + expect dict.size(all_redeemers) == 1 + let pool_out_datum = + utils.must_find_pool_datum(all_datums, raw_pool_out_datum) + let PoolDatum { + asset_a: pool_in_asset_a, + asset_b: pool_in_asset_b, + total_liquidity: pool_in_total_liquidity, + reserve_a: pool_in_reserve_a, + reserve_b: pool_in_reserve_b, + trading_fee_numerator: pool_in_trading_fee_numerator, + trading_fee_denominator: pool_in_trading_fee_denominator, + order_hash: pool_in_order_hash, + profit_sharing_opt: pool_in_profit_sharing_opt, + } = pool_in_datum + let PoolDatum { + asset_a: pool_out_asset_a, + asset_b: pool_out_asset_b, + total_liquidity: pool_out_total_liquidity, + reserve_a: pool_out_reserve_a, + reserve_b: pool_out_reserve_b, + trading_fee_numerator: pool_out_trading_fee_numerator, + trading_fee_denominator: pool_out_trading_fee_denominator, + order_hash: pool_out_order_hash, + profit_sharing_opt: pool_out_profit_sharing_opt, + } = pool_out_datum + let irrelevant_pool_data_unchanged = + pool_in_asset_a == pool_out_asset_a && // hihi + pool_in_asset_b == pool_out_asset_b && // hihi + pool_in_total_liquidity == pool_out_total_liquidity && // hihi + pool_in_reserve_a == pool_out_reserve_a && // hihi + pool_in_reserve_b == pool_out_reserve_b && // hihi + pool_in_order_hash == pool_out_order_hash && // hihi + pool_in_value == pool_out_value + // hihi + expect irrelevant_pool_data_unchanged + when action is { + UpdatePoolFee -> { + // only trading fee or fee sharing can be changed + let is_valid_trading_fee = + validate_trading_fee_percent( + trading_fee_numerator: pool_out_trading_fee_numerator, + trading_fee_denominator: pool_out_trading_fee_denominator, + ) + let is_valid_fee_sharing = + when pool_out_profit_sharing_opt is { + None -> True + Some(pool_out_profit_sharing) -> { + let ( + pool_out_fee_sharing_numerator, + pool_out_fee_sharing_denominator, + ) = pool_out_profit_sharing + validate_fee_sharing_percent( + fee_sharing_numerator: pool_out_fee_sharing_numerator, + fee_sharing_denominator: pool_out_fee_sharing_denominator, + ) + } + } + pool_in_address == pool_out_address && is_valid_trading_fee && is_valid_fee_sharing + } + UpdatePoolStakeCredential -> { + let is_valid_trading_fee = + pool_in_trading_fee_numerator == pool_out_trading_fee_numerator && // hihi + pool_in_trading_fee_denominator == pool_out_trading_fee_denominator + let is_valid_fee_sharing = + pool_in_profit_sharing_opt == pool_out_profit_sharing_opt + is_valid_trading_fee && is_valid_fee_sharing + } + } +} + +pub fn validate_trading_fee_percent( + trading_fee_numerator: Int, + trading_fee_denominator: Int, +) -> Bool { + // Max 10%, Min 0.05% + let max_trading_fee_numerator = 1 + let max_trading_fee_denominator = 10 + let min_trading_fee_numerator = 5 + let min_trading_fee_denominator = 10000 + + let less_than_max = + trading_fee_numerator * max_trading_fee_denominator <= trading_fee_denominator * max_trading_fee_numerator + let greater_than_min = + trading_fee_numerator * min_trading_fee_denominator >= trading_fee_denominator * min_trading_fee_numerator + less_than_max && greater_than_min +} + +pub fn validate_fee_sharing_percent( + fee_sharing_numerator: Int, + fee_sharing_denominator: Int, ) -> Bool { - expect validate_mint(mint) + // Max 10%, Min 0.05% + let max_fee_sharing_numerator = 1 + let max_fee_sharing_denominator = 2 + let min_fee_sharing_numerator = 1 + let min_fee_sharing_denominator = 6 + + let less_than_max = + fee_sharing_numerator * max_fee_sharing_denominator <= fee_sharing_denominator * max_fee_sharing_numerator + let greater_than_min = + fee_sharing_numerator * min_fee_sharing_denominator >= fee_sharing_denominator * min_fee_sharing_numerator + less_than_max && greater_than_min +} + +pub fn validate_withdraw_liquidity_share( + authen_policy_id: PolicyId, + pool_auth_asset_name: AssetName, + license_policy_id: PolicyId, + admin_asset_name: AssetName, + admin_index: Int, + pool_input: Input, + pool_in_datum: PoolDatum, + all_inputs: List, + all_outputs: List, + all_datums: DatumMap, + all_mints: MintedValue, + all_redeemers: Dict, +) -> Bool { + let Input { output: pool_in_output, .. } = pool_input + let Output { address: pool_in_address, value: pool_in_value, .. } = + pool_in_output + let Address { payment_credential: pool_in_payment_credential, .. } = + pool_in_address + expect [pool_output] = + list.filter( + all_outputs, + fn(o) { + let Output { address: addr, .. } = o + let Address { payment_credential, .. } = addr + payment_credential == pool_in_payment_credential + }, + ) + let Output { + address: pool_out_address, + value: pool_out_value, + datum: raw_pool_out_datum, + .. + } = pool_output + expect pool_in_address == pool_out_address + expect dict.size(all_redeemers) == 1 + let pool_out_datum = + utils.must_find_pool_datum(all_datums, raw_pool_out_datum) + expect pool_in_datum == pool_out_datum + expect + value.quantity_of(pool_in_value, authen_policy_id, pool_auth_asset_name) == 1 + let admin_input = utils.list_at_index(all_inputs, admin_index) + let Input { output: Output { value: admin_value, .. }, .. } = admin_input + // Only Admin token can trigger this redeemer expect - validate_batcher( - inputs: inputs, - validity_range: validity_range, - extra_signatories: extra_signatories, - batcher_address: batcher_address, - license_policy_id: license_policy_id, - license_index: license_index, - maximum_deadline_range: maximum_deadline_range, - ) - True + value.quantity_of(admin_value, license_policy_id, admin_asset_name) == 1 + // This Redeemer won't mint anything + expect value.is_zero(value.from_minted_value(all_mints)) + let PoolDatum { + asset_a, + asset_b, + reserve_a: datum_reserve_a_in, + reserve_b: datum_reserve_b_in, + .. + } = pool_in_datum + 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 temp_value_reserve_a_in = + value.quantity_of(pool_in_value, asset_a_policy_id, asset_a_asset_name) + let value_reserve_a_in = + if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { + temp_value_reserve_a_in - 3000000 + } else { + temp_value_reserve_a_in + } + let value_reserve_b_in = + value.quantity_of(pool_in_value, asset_b_policy_id, asset_b_asset_name) + let earned_a = value_reserve_a_in - datum_reserve_a_in + let earned_b = value_reserve_b_in - datum_reserve_b_in + let pool_out_sub_earned_a = + value.add( + self: pool_out_value, + policy_id: asset_a_policy_id, + asset_name: asset_a_asset_name, + quantity: earned_a * -1, + ) + let pool_out_sub_earned_b = + value.add( + self: pool_out_sub_earned_a, + policy_id: asset_b_policy_id, + asset_name: asset_b_asset_name, + quantity: earned_b * -1, + ) + pool_in_value == pool_out_sub_earned_b } diff --git a/lib/amm_dex_v2/types.ak b/lib/amm_dex_v2/types.ak index 123f125..4e55cae 100644 --- a/lib/amm_dex_v2/types.ak +++ b/lib/amm_dex_v2/types.ak @@ -20,12 +20,9 @@ pub type Asset { pub type DatumMap = Dict, Data> -pub type ProfitSharing { - fee_sharing_numerator: Int, - fee_sharing_denominator: Int, - fee_to: Address, - fee_to_datum_hash_opt: Option, -} +// Profit Sharing Numerator & Denominator +pub type ProfitSharing = + (Int, Int) pub type PoolDatum { asset_a: Asset, @@ -39,6 +36,11 @@ pub type PoolDatum { profit_sharing_opt: Option, } +pub type UpdatePoolFeeOrStakeCredentialAction { + UpdatePoolFee + UpdatePoolStakeCredential +} + pub type PoolRedeemer { Batching { batcher_address: Address, @@ -51,8 +53,11 @@ pub type PoolRedeemer { routing_in_indexes: List, routing_out_indexes: List, } - UpdateFeeTo { admin_index: Int } - WithdrawLiquidityShare { admin_index: Int, fee_to_index: Int } + UpdatePoolFeeOrStakeCredential { + action: UpdatePoolFeeOrStakeCredentialAction, + admin_index: Int, + } + WithdrawLiquidityShare { admin_index: Int } } pub type OrderDirection { @@ -126,6 +131,10 @@ pub type AuthenRedeemer { MintLiquidity } +pub type OrderBatchingRedeemer { + pool_input_index: Int, +} + pub type BatchingPool { asset_a: Asset, asset_b: Asset, diff --git a/lib/amm_dex_v2/utils.ak b/lib/amm_dex_v2/utils.ak index d6559e7..3075a04 100644 --- a/lib/amm_dex_v2/utils.ak +++ b/lib/amm_dex_v2/utils.ak @@ -3,13 +3,10 @@ use aiken/bytearray use aiken/cbor use aiken/dict use aiken/hash -use aiken/interval.{Finite} use aiken/list use aiken/string -use aiken/transaction.{Datum, DatumHash, InlineDatum, NoDatum, ValidityRange} -use aiken/transaction/value.{ - AssetName, PolicyId, Value, ada_asset_name, ada_policy_id, -} +use aiken/transaction.{Datum, DatumHash, InlineDatum, NoDatum} +use aiken/transaction/value.{AssetName, PolicyId, ada_asset_name, ada_policy_id} use amm_dex_v2/types.{Asset, DatumMap, FactoryDatum, OrderDatum, PoolDatum} const minus_ascii_code = 45 @@ -69,47 +66,6 @@ pub fn is_ada_asset(pid: PolicyId, name: AssetName) -> Bool { pid == ada_policy_id && name == ada_asset_name } -pub fn verify_pool_datum_in_batch( - datum1: PoolDatum, - datum2: PoolDatum, - require_total_liquidity_unchange: Bool, -) -> Bool { - let PoolDatum { - asset_a: datum_1_asset_a, - asset_b: datum_1_asset_b, - total_liquidity: datum_1_total_liquidity, - trading_fee_numerator: datum_1_trading_fee_numerator, - trading_fee_denominator: datum_1_trading_fee_denominator, - order_hash: datum_1_order_hash, - profit_sharing_opt: datum_1_profit_sharing_opt, - .. - } = datum1 - - let PoolDatum { - asset_a: datum_2_asset_a, - asset_b: datum_2_asset_b, - total_liquidity: datum_2_total_liquidity, - trading_fee_numerator: datum_2_trading_fee_numerator, - trading_fee_denominator: datum_2_trading_fee_denominator, - order_hash: datum_2_order_hash, - profit_sharing_opt: datum_2_profit_sharing_opt, - .. - } = datum2 - expect - if require_total_liquidity_unchange { - datum_1_total_liquidity == datum_2_total_liquidity - } else { - True - } - datum_1_asset_a == datum_2_asset_a && // hehe - datum_1_asset_b == datum_2_asset_b && // hehe - datum_1_trading_fee_numerator == datum_2_trading_fee_numerator && // hehe - datum_1_trading_fee_denominator == datum_2_trading_fee_denominator && // hehe - datum_1_order_hash == datum_2_order_hash && // hehe - datum_1_profit_sharing_opt == datum_2_profit_sharing_opt - // hehe -} - pub fn compute_lp_asset_name( asset_a_policy_id: PolicyId, asset_a_asset_name: AssetName, @@ -145,18 +101,6 @@ pub fn must_parse_option(op: Option) -> a { t } -pub fn find_license_deadline(license_symbol: PolicyId, val: Value) -> Int { - expect [(tn, am)] = - val - |> value.tokens(license_symbol) - |> dict.to_list() - if am == 1 { - bytearray_to_int(tn) - } else { - fail - } -} - pub fn bytearray_to_int(byte_arr: ByteArray) -> Int { let byte_len = bytearray.length(byte_arr) do_bytearray_to_int(byte_arr, 0, 0, byte_len) @@ -193,12 +137,6 @@ test test_bytearray_to_int() { r1 == 123 && r2 == 111 } -pub fn find_posix_time_range(time_rage: ValidityRange) -> (Int, Int) { - expect Finite(t1) = time_rage.lower_bound.bound_type - expect Finite(t2) = time_rage.upper_bound.bound_type - (t1, t2) -} - pub fn find_script_datum(datums: DatumMap, datum: Datum) -> Option { when datum is { NoDatum -> None @@ -209,7 +147,7 @@ pub fn find_script_datum(datums: DatumMap, datum: Datum) -> Option { } } -fn must_find_script_datum(datums: DatumMap, datum: Datum) -> Data { +pub fn must_find_script_datum(datums: DatumMap, datum: Datum) -> Data { let datum_opt = find_script_datum(datums, datum) must_parse_option(datum_opt) } @@ -252,7 +190,7 @@ fn list_at_index_step(outputs: List, current_index: Int) -> a { /// Small utility to skip 10 items in a list. /// Used by `list_at_index`. -fn skip_10_items(some_list: List) -> List { +pub fn skip_10_items(some_list: List) -> List { some_list |> builtin.tail_list |> builtin.tail_list @@ -323,3 +261,61 @@ test test_zip_with_throw_err() fail { [4, 5, 6, 6] zip_with(arr1, arr2, fn(a1, a2) { a1 + a2 }) == [] } + +pub fn compare_list_length(arr1: List, arr2: List) -> Bool { + when arr1 is { + [] -> arr2 == [] + [_, ..xs] -> + when arr2 is { + [] -> False + [_, ..ys] -> compare_list_length(xs, ys) + } + } +} + +test test_compare_list_length() { + let arr1 = + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + let arr2 = + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + compare_list_length(arr1, arr2) +} + +test test_compare_list_length_1() { + let arr1 = + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + let arr2 = + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] + !compare_list_length(arr1, arr2) +} + +test test_compare_list_length_2() { + let arr1 = + [] + let arr2 = + [] + compare_list_length(arr1, arr2) +} + +test test_compare_list_length_3() { + let arr1 = + [1] + let arr2 = + [] + !compare_list_length(arr1, arr2) +} + +fn contains_element(list: List, elem: a) -> Bool { + when list is { + [] -> False + [x, ..xs] -> elem == x || contains_element(xs, elem) + } +} + +pub fn is_list_unique(list: List) -> Bool { + when list is { + [] -> True + [_] -> True + [x, ..xs] -> !contains_element(xs, x) && is_list_unique(xs) + } +} diff --git a/plutus.json b/plutus.json index efa6d5d..8a321ac 100644 --- a/plutus.json +++ b/plutus.json @@ -39,8 +39,8 @@ } } ], - "compiledCode": "5906cd01000032323232323232323222322322232533300b32323232533300f3370e90001807000899191919191919299980b19b87480000144c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc0a4c0b00044c8c8c94ccc0b0c0bc0084c8c8c8c8c94ccc0b94ccc0b94ccc0b8cdc780280f099b8f00302914a0266e1c005200214a026464a666066606c00426464646464646464a666070a66607066e3c00d22101000013371e00291121ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000014a0294458dd7181e000981e0011bae303a001303232320023253330363370e9000000899191919299981e98200010a4c2c6eb8c0f8004c0f8008dd7181e000981a0010b181a00099299981a99b8748000c0d00044c0e8c0cc00458c94ccc0d4cdc3a4000002298103d87a8000153330353370e9001000899191980080080991299981d8008a6103d87a8000132323232533303c3371e00e004266e952000330400014bd70099803003001981e8019bae303b002303f002303d001375c60746066004266e95200033039303a30330024bd701819800981c000981c000981b80098170008b181a0009980c80a119b8733301d37566068606a605a00203e05490010b1bad30320013032002375c606000260600046eb8c0b800458dd61816800980a00098090048b19808007119baf302b30240010233756605200260520026050002604e002604c002604a00260480046eacc088004c088004c084008dd6180f800980f800980f0011bac301c001301400a153330163370e90010028991919191919191919191919191919299981418158010991919299981419b873330160010180234800854ccc0a0cdc399980b00080c010a400426464a66605a606000426464646464646464646464646464a6660706464646464646464a66608066e3c01c00c4cdc8002800899b90007003375c608800260880046eb8c108004c0e8034dd7182000098200011bae303e001303600b13253330393370e66604e024052002907f7fffffffffffffff808a99981c99b87332300100122533303e0011480004cdc024004660040046082002604c02490030a511616333332322222300633005300633005004003300633005002001223371400400246e4c00401c01400c00458dd7181e000981e0011bae303a0013032005375c607000260700046eb8c0d8004c0b800cc0d0004c0d0008c0c8004c0a8008cc88c94ccc0b8cdc3a4000002264646464a66606a60700042646493180400118038018b181b000981b001181a00098160010b1816000919299981699b87480000044c8c8c8c94ccc0d0c0dc00852616375c606a002606a0046eb8c0cc004c0ac00858c0ac004004c0b000458c0b8004c8cc00400401c894ccc0b400452f5bded8c026464a6660586464a66605c66e1d200200113375e010606660580042940c0b0004c0b40084cc0c0008cc0100100044cc010010004c0c4008c0bc0045858c04c02cc0ac004c08c00458c0a4004cc0380308cdc39998091bab3029302a30223029302a302200101401f48008dd59813800981380098130009812800981200098118011bab302100130210013020001301f001301e0023758603800260280142c44646600200200644a666038002297ae013232533301b300500213301f002330040040011330040040013020002301e0012323300100100222533301a00114bd6f7b630099191919299980d99b8f488100002100313301f337606ea4008dd3000998030030019bab301c003375c6034004603c00460380024646600200200444a666032002297ae0132333222323300100100322533301f001100313233322233024374e660486ea4024cc090dd4801998121ba80024bd700009bae301e001375a603e00266006006604600460420026eb8c060004dd5980c80099801801980e801180d80091119199119299980c99b874800800440084dd6980f180b801980b80119299980b99b87480080045300103d87a8000132323300100100222533301d00114c103d87a8000132323232533301e3371e014004266e95200033022375000297ae0133006006003375a603e0066eb8c074008c084008c07c004dd5980e180a801180a800a4000646600200200844a6660340022980103d87a8000132323232533301b3371e010004266e9520003301f374c00297ae0133006006003375660380066eb8c068008c078008c070004c040024dd7180a80098068008b18098009809801180880098048010a4c26cac64a66601666e1d20000011533300e300900314985854ccc02ccdc3a40040022a66601c60120062930b0a99980599b874801000454ccc038c02400c52616163009002375c0026eb80048c014dd5000918019baa0015734aae7555cf2ab9f5740ae855d11", - "hash": "52dd658d5fe68707f9afc3fd90003202ad235d8cfb4344e5f972dadf" + "compiledCode": "5908a401000032323232323232323222322322232533300b32323232533300f3370e900018068008991919191919299980a99b87480000104c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc0a0c0ac0044c8c8c94ccc0acc0b80084c8c8c8c8c94ccc0b54ccc0b4cdc780280e8a99981699b8f00302813370e00290010a5014a026464a666064606a00426464646464646464a66606e66e3c00d220101000013371e00291121ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000014a06eb8c0ec004c0ec008dd7181c8009819191900119299981a99b87480000044c8c8c8c94ccc0f0c0fc00852616375c607a002607a0046eb8c0ec004c0d000858c0c8004c94ccc0d0cdc3a400060640022607260660022c64a66606866e1d200000114c103d87a8000153330343370e9001000899191980080080991299981d0008a6103d87a8000132323232533303b3371e00e004266e9520003303f0014bd70099803003001981e0019bae303a002303e002303c001375c60726066004266e95200033038303930330024bd701818800981b800981b800981b00098170008b18198009980c80a119b8733301c375660666068605a00203c05290010b1bad30310013031002375c605e002605e0046eb8c0b400458dd6181600099198008008011129998158008a5eb804c8ccc888c8cc00400400c894ccc0c4004400c4c8ccc888cc0d8dd39981b1ba90093303637520066606c6ea00092f5c00026eb8c0c0004dd6981880099801801981a80118198009bae302a0013756605600266006006605e004605a00260240122c6602001c466ebcc0a8c090004088dd598140009814000981380098130009812800981200098118011bab3021001302100130200023758603c002603c002603a0046eb0c06c004c05002454ccc054cdc3a40040082646464646464646464646464646464a66604e6054004264646464a666056605c00426464646464646464646464646464a66606c6464646464646464a66607c66e3c01c00c4cdc8002800899b90007003375c608400260840046eb8c100004c0e4034dd7181f000981f0011bae303c001303500b132323375e6e98c8c8cccc004cccc004cccc0052f5bded8c005406a9001015019a4004054008907f7fffffffffffffff809111299981e99b870014800040104ccc888c8cc004004020894ccc1140044cc118cdd81ba9008374c00a97adef6c6013232323253330463375e6600e01800498103d879800013304a337606ea4030dd30048028a99982319b8f00c0021323253330483370e900000089982619bb0375201c609a608e00400a200a608a00266601001800201226609466ec0dd48011ba6001330060060033756608e0066eb8c114008c124008c11c004cc88c800cc8cc00400400c894ccc110004526132533304500114984c8c8c8c8c8c8c94ccc124cdc3a40000022660140146609a00c00a2c608c002660120040026eb8c11c00cdd7182300198250019824001182380118238009982099bb037520046ea00052f5bded8c000a44464a666082a66608800229445280a6103d87a800013374a9000198229ba60014bd70191980080080191299982280089982319bb0375200e6ea00192f5bded8c0264646464a66608c66ebccc03802c00930103d879800013304a337606ea402cdd40050028a99982319b8f00b0021323253330483370e900000089982619bb0375201a609a608e00400a200a608a00264a66608e66e1c005200014c103d87a800013374a9000198259ba80014bd7019b8000100a13304a337606ea4008dd4000998030030019bad3047003375c608a0046092004608e00200a44a66607466e40008004530103d87980001533303a3371e0040022980103d87a800014c103d87b8000374c0026048038666664644444600c6600a600c6600a008006600c6600a0040024466e280080048dc98008038028018008b1bae303a001303a002375c6070002606200a6eb8c0d8004c0d8008dd7181a00098168019819000981900118180009814801199119299981619b87480000044c8c8c8c94ccc0ccc0d80084c8c9263008002300700316303400130340023032001302b002163029001232533302b3370e90000008991919192999819181a8010a4c2c6eb8c0cc004c0cc008dd7181880098150010b181400080098150008b181600099198008008031129998158008a5eb7bdb1804c8c94ccc0a8c8c94ccc0b0cdc3a4004002266ebc01cc0c4c0ac008528181480098158010998170011980200200089980200200098178011816800981500098118008b181400099807006119b87333011375660506052604460506052604400202603c90011bab302600130260013025001302400130230013022002375660400026040002603e002603c002603a0046eb0c06c004c0500245888c8cc00400400c894ccc06c00452f5c026464a666034600a00426603c00466008008002266008008002603e004603a0024646600200200444a666032002297adef6c60132323232533301a3371e91100002100313301e337606ea4008dd3000998030030019bab301b003375c6032004603a004603600244464664464a66603266e1d200200110021375a603c6030006602c00464a66602e66e1d200200114c103d87a8000132323300100100222533301d00114c103d87a8000132323232533301e3371e014004266e95200033022375000297ae0133006006003375a603e0066eb8c074008c084008c07c004dd5980e180b001180a000a4000646600200200844a6660340022980103d87a8000132323232533301b3371e010004266e9520003301f374c00297ae0133006006003375660380066eb8c068008c078008c070004c03c024dd7180a80098070008b18098009809801180880098050010a4c26cac64a66601666e1d20000011533300e300a00314985854ccc02ccdc3a40040022a66601c60140062930b0a99980599b874801000454ccc038c02800c52616163008002375c0026eb80048c010dd5000918021baa0015734aae7555cf2ab9f5740ae855d11", + "hash": "ed66b87af3866a8fe40e12764b0b6ef9aa8943c166750605a4e7a975" }, { "title": "factory_validator.validate_factory", @@ -88,8 +88,8 @@ } } ], - "compiledCode": "590b450100003232323232323232322322322322322322223232323253330143232323253330183370e9001180b0008991919191919191919191919191919191919191919191919191919191919191919299981c9919191919191919299982099b8f00700313372000a002266e4001c00cdd7182280098228011bae3043001303c011375c608200260820046eb8c0fc004c0e003c4c8c94ccc0eccdc3a40006072002264646464646464646464a66608a66e1cccc0040081040ed200213232323232533304d30500021323232323232323232323232323232323232533305c533305c533305c533305c533305c533305c3372000e00a266e4000c004528099b8f00702e14a0266e3c0040b0528099b8f02300514a0266e3c08c00c528099192999830983200109919191919191919191919191919191919191919191919299983a99b8748008c1cc0044c8c8c8c94ccc1e54ccc1e54ccc1e54ccc1e54ccc1e54ccc1e54ccc1e54ccc1e54ccc1e4cdd780a827899baf01304d14a0266e1c044004528099b8700f00314a0266e1c034008528099b884800002c528099b8848000024528099b8800b00914a0266e3c01c1c4528099299983d19b8733303601a0760410011533307a32323370e646600200200444a666102020022900009991191919191929998428099b8f0050810115333085015333085013371e0060f6266e1c005200214a0266e00019200215333085015333085013371e0060f2266e1c005200214a0266e00019200215333085015333085013371e006098266e1c00520feffffffffffffffff0114a0266e0001920021616375a6112020026112020046eb8c21c04004c21c0400cdd71842808011bac3083010013300200230840100148018c8cc004004008894ccc2000400452f5c0264666444646600200200644a66610c02002200626466644466116026e9ccc22c04dd480499845809ba90033308b01375000497ae0001375c610a020026eb4c21804004cc00c00cc22804008c22004004dd7183f8009bab30800100133003003308401002308201001323300100105a22533307f00114bd6f7b63009919191929998400099b8f488100002100313308401337606ea4008dd3000998030030019bab308101003375c60fe00461060200461020200229445858cdc0a41fdfffffffffffffffe020022c6464a6660f466e20cdc1000800801099b800014800840054ccc1e4cdc4000a40002c2a6660f266e1c005200014800054ccc1e4cdc3800a4004290010a99983c99b87001480105200213232333001001003002222533307d33710002004266600600600266e0ccdc019b83005001001480104008cdc019b830014801120023370400400266606602e08207ea6660eca6660ec66e3c11122010013371e0849110014a0266e0400520809bee0210013330310150430411630790013079002375c60ee00260ee0046eb4c1d4004c1d4008dd6983980098398011bad30710013071002375a60de00260de0046eb4c1b4004c1b4008c1ac004c1ac008c1a4004c188ccc888c8c8008c94ccc1a0cdc3a40000022646464646464646464646464646464646464a6660fa61000200426464649319299983e99b87480000044c8c94ccc20804c214040084c926325333080013370e9000000899191919191919192999845809847008010991924c64a6661140266e1d200000113232533308f01309201002149858dd71848008009844808018a9998450099b874800800454ccc23404c2240400c5261616308701002325333089013370e90000008991919192999848009849808010991924c64a66611e0266e1d200000113232533309401309701002132498c94ccc24804cdc3a400000226464a66612e026134020042649318188008b184c008009848808010a9998490099b87480080044c8c8c8c8c8c94ccc26c04c2780400852616375a6138020026138020046eb4c26804004c26804008dd6984c008009848808010b1847808008b184a808009847008018a9998478099b874800800454ccc24804c2380400c5261616308c01002302a00316309101001309101002308f010013088010041630860100316308c01001308c01002308a01001308a01002375a6110020026110020046eb4c21804004c1fc00858c1f400458c20c04004c1f001054ccc1f4cdc3a40040022a6661000260f80082930b0b183d001983480818340088b183f000983f0011bae307c001307c002375a60f400260f40046eb4c1e0004c1e0008dd6983b000983b0011bad30740013074002375a60e400260e400460e000260e000460dc00260ce0042c60ca00266028004002464a6660ca66e1d200000113232533306a306d002149858dd7183580098320010a99983299b87480080044c8c94ccc1a8c1b400852616375c60d600260c80042c60c400207600260ce00260ce0046eacc194004c194004c17400458c188004cc05c0f88c8c8c8c8c8c8c94ccc190cdc3a400400226464a6660cc66e3c1800044cdc399981100303102d240042940dd7183500098318010a50306100130670013060003375660ca00260ca00460c600260b80022c6eb8c180004c180008dd7182f000982b998028180039bae305c001305c002375c60b400260a66600205800e446464004608c002660060040024464a6660aa66e1d200030530011305a3054001163253330553370e90000008a60103d87a8000153330553370e9001000899191980080080291299982d8008a6103d87a8000132323232533305c3371e00e004266e952000330600014bd70099803003001982e8019bae305b002305f002305d001375c60b460a8004266e95200033059305a30540024bd701829000982b000982b000982a800982680298290009829000982880098248018b18270009827001182600099800814119191919299982599baf00300a13370e66600e00208e08290010a503756609e002609e004609a002608c00244646600200200644a666098002297ae013232533304b300500213304f002330040040011330040040013050002304e0011622232332232533304b3370e9001000880109bad3050304a00330480023253330493370e90010008a6103d87a8000132323300100100222533304f00114c103d87a800013232323253330503371e014004266e95200033054375000297ae0133006006003375a60a20066eb8c13c008c14c008c144004dd5982718240011823000a4000646600200200844a6660980022980103d87a8000132323232533304d3371e010004266e95200033051374c00297ae01330060060033756609c0066eb8c130008c140008c138004dd5982400098240011823000981f80098220009822000981e0009820800981d0008b191980080081011299981f8008a60103d87a800013232533303e3375e6086607a00404a266e952000330420024bd70099802002000982180118208009999991911111803198029803198028020019803198028010009119b8a0020012372600200e00a0060022c6eb8c0f4004c0f4008dd7181d800981a0049bae30390013039002375c606e002606000e6eb8c0d4004c0d4008dd718198009816010181880098188011817800981400d9bab302d001302d001302c001302b001302a001302900130280023756604c002604c002604a0046eb0c08c004c08c004c088008dd61810000980c802980f000980b8008b180e000980e001180d00098098028a4c26cac64a66602866e1d2000001132323232533301b301e00213232498c01c008c01800c58c070004c070008c068004c04c01858c0440148c94ccc050cdc3a4000002264646464a666036603c0042930b1bae301c001301c002375c603400260260042c60220026002008464a66602466e1d20000011323232325333019301c002149858dd7180d000980d0011bae3018001301100216300f001375c0026eb8004dd70009bae001375c002460086ea80048c010dd5000ab9a5573aaae7955cfaba05742ae89", - "hash": "130d79e40f5b52f2d952b77be70ff6e69cbf152b02cc7a0a5a2e4c32" + "compiledCode": "590a020100003232323232323232322322322322322322223232323253330143232323253330183370e9001180b0008991919191919191919191919191919191919191919191919191919191919191919299981c9919191919191919299982099b8f00700313372000a002266e4001c00cdd7182280098228011bae3043001303c011375c608200260820046eb8c0fc004c0e003c4c8c94ccc0eccdc3a40006072002264646464646464646464a66608a66e1cccc0040081040ed200213232323232533304d30500021323232323232323232323232323232323232533305c533305c533305c533305c533305c533305c3372000e00a266e4000c004528099b8f00702e14a0266e3c0040b0528099b8f02300514a0266e3c08c00c528099192999830983200109919191919191919191919191919191919191919191919299983a99b8748008c1cc0044c8c8c8c8c94ccc1e94ccc1e94ccc1e94ccc1e94ccc1e94ccc1e94ccc1e94ccc1e94ccc1e8cdd780b028099baf01404e14a0266e1c048004528099b8701000314a0266e1c038008528099b8848000030528099b8848000028528099b8800c00a14a0266e3c0201c8528099299983d99b8733303701b07704200113232533307d533307d3300704b049132323253330830130860100114a22c610a02002610802002610602002264646464a66610802610e02002294458c21804004c21404004c21004004c20c0400454ccc1f4c8c8cdc39919800800801112999842008008a4000266446464646464a6661100266e3c0142100454ccc220054ccc22004cdc780183f099b8700148008528099b800064800854ccc220054ccc22004cdc780183e099b8700148008528099b800064800854ccc220054ccc22004cdc7801827899b87001483fbfffffffffffffffc04528099b80006480085858dd69846008009846008011bae308a01001308a01003375c6110020046eb0c21804004cc008008c21c0400520063003001323300100105d2253330820100114bd6f7b63009919191929998418099b8f488100002100313308701337606ea4008dd3000998030030019bab308401003375c610402004610c0200461080200229445858c0040708c8cc004004008894ccc2040400452f5c0264666444646600200200644a66610e02002200626466644466118026e9ccc23004dd480499846009ba90033308c01375000497ae0001375c610c020026eb4c21c04004cc00c00cc22c04008c22404004dd71840008009bab308101001330030033085010023083010011633702907f7fffffffffffffff808008b1919299983d99b8833704002002004266e0000520021001533307a3371000290000b0a99983d19b8700148000520001533307a3370e00290010a40042a6660f466e1c00520041480084c8c8ccc00400400c0088894ccc1f8cdc4000801099980180180099b833370066e0c014004005200410023370066e0c005200448008cdc100100099981a00c021020299983b99800822821899b81002482026fb8084008894ccc1e0cdc78012450013371e0029110014a066606202a0860822c60f200260f20046eb8c1dc004c1dc008dd6983a800983a8011bad30730013073002375a60e200260e20046eb4c1bc004c1bc008dd6983680098368011835800983580118348009831191900119299983299b87480000044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc1e8c1f40084c8c8c92632533307a3370e900000089919299983f98410080109924c646eb4c1fc008dd6983e8008b1919bb0308101001308101308201001375861000200260f20082a6660f466e1d20020011533307d307900414985858c1dc00cc198040c19404458c1ec004c1ec008dd7183c800983c8011bad30770013077002375a60ea00260ea0046eb4c1cc004c1cc008dd6983880098388011bad306f001306f002306d001306d002306b00130640021630620013301103b00130670013067002375660ca00260ca00260ba0022c60c40026602e07c464646464646464a6660c866e1d20020011323253330663371e0c0002266e1cccc088018188169200214a06eb8c1a8004c18c0085281830800983380098300019bab306500130650023063001305c00116375c60c000260c00046eb8c178004c15ccc0140c001cdd7182e000982e0011bae305a00130533300102c007223232002304600133003002001223253330553370e900018298008982d182a0008b19299982a99b8748000004530103d87a8000153330553370e9001000899191980080080291299982d8008a6103d87a8000132323232533305c3371e00e004266e952000330600014bd70099803003001982e8019bae305b002305f002305d001375c60b460a8004266e95200033059305a30540024bd701829000982b000982b000982a800982680298290009829000982880098248018b18270009827001182600099800814119191919299982599baf00300a13370e66600e00208e08290010a503756609e002609e004609a002608c00244646600200200644a666098002297ae013232533304b300500213304f002330040040011330040040013050002304e0011622232332232533304b3370e9001000880109bad3050304a00330480023253330493370e90010008a6103d87a8000132323300100100222533304f00114c103d87a800013232323253330503371e014004266e95200033054375000297ae0133006006003375a60a20066eb8c13c008c14c008c144004dd5982718240011823000a4000646600200200844a6660980022980103d87a8000132323232533304d3371e010004266e95200033051374c00297ae01330060060033756609c0066eb8c130008c140008c138004dd5982400098240011823000981f80098220009822000981e0009820800981d0008b191980080081011299981f8008a60103d87a800013232533303e3375e6086607a00404a266e952000330420024bd70099802002000982180118208009999991911111803198029803198028020019803198028010009119b8a0020012372600200e00a0060022c6eb8c0f4004c0f4008dd7181d800981a0049bae30390013039002375c606e002606000e6eb8c0d4004c0d4008dd718198009816010181880098188011817800981400d9bab302d001302d001302c001302b001302a001302900130280023756604c002604c002604a0046eb0c08c004c08c004c088008dd61810000980c802980f000980b8008b180e000980e001180d00098098028a4c26cac64a66602866e1d2000001132323232533301b301e00213232498c01c008c01800c58c070004c070008c068004c04c01858c0440148c94ccc050cdc3a4000002264646464a666036603c0042930b1bae301c001301c002375c603400260260042c60220026002008464a66602466e1d20000011323232325333019301c002149858dd7180d000980d0011bae3018001301100216300f001375c0026eb8004dd70009bae001375c002460086ea80048c010dd5000ab9a5573aaae7955cfaba05742ae89", + "hash": "6533ac6037abf3a3955d9816f651be1fc2b57d9b28ea3fb6a9cdb1be" }, { "title": "order_validator.validate_order", @@ -99,10 +99,29 @@ "$ref": "#/definitions/Data" } }, + "redeemer": { + "title": "raw_redeemer", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "parameters": [ + { + "title": "stake_credential", + "schema": { + "$ref": "#/definitions/aiken~1transaction~1credential~1Referenced$aiken~1transaction~1credential~1Credential" + } + } + ], + "compiledCode": "5905af01000032323232323232322222533300732323232533300b3370e900118050008991919299980719b87480000044c8c8c8c8c8c8c8c8c8cc004004008894ccc06c0045280991919299980d99baf01800114a226600a00a0046036004603c00460380026eacc064004c064004c060004c05c004c058004c054004c050004c0300184c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc074cdc3a4000603800226464646600200201244a66604800229404c8c94ccc08ccdc78010028a511330040040013027002375c604a0026eb8c088004c06c00458c080004c064004c078004c05c054cc8c8c8c88c94ccc078cdc3a400000226464646464646464646464646464a66605e6062004264646464649319299981899b87480000044c8c8c8c94ccc0e0c0e80084c926301c00316375a60700026070004606c002605e0142a66606266e1d20020011323232325333038303a002132498c07000c58dd6981c000981c001181b00098178050a99981899b87480100044c8c8c8c8c8c94ccc0e8c0f00084c926301e00516375a607400260740046eb4c0e0004c0e0008c0d8004c0bc02854ccc0c4cdc3a400c002264646464a666070607400426493180e0018b1bad303800130380023036001302f00a153330313370e900400089919299981b181c0010a4c2c6eb4c0d8004c0bc02854ccc0c4cdc3a4014002264646464a66607060740042930b1bad30380013038002375a606c002605e0142a66606266e1d200c0011323232325333038303a002132498c07000c58dd6981c000981c001181b00098178050a99981899b87480380044c8c8c8c8c8c8c8c8c8c94ccc0f8c1000084c926302200916375a607c002607c0046eb4c0f0004c0f0008dd6981d000981d0011bad303800130380023036001302f00a153330313370e90080008991919191919299981d181e0010a4c2c6eb4c0e8004c0e8008dd6981c000981c0011bad3036001302f00a153330313370e9009000899191919299981c181d00109924c646600200200844a66607400229309919801801981e8011919299981c99b87480000044c8c8c8c94ccc100c1080084c8c926302500230230031630400013040002303e0013037002163037001303b00116375a607000260700046eb0c0d8004c0bc02858c0bc024c054028c94ccc0bccdc3a400000226464a666068606c0042930b1bae3034001302d00c1533302f3370e90010008a99981918168060a4c2c2c605a0166024018602201a2c6eb4c0bc004c0bc008dd698168009816801181580098158011814800981480118138009813801181280098128011811800980e0010b180e000919299980e99b87480000044c8c8c8c94ccc090c0980084c8c9263253330233370e9000000899192999814181500109924c64a66604c66e1d200000113232533302b302d002132498c04000458c0ac004c09000854ccc098cdc3a40040022646464646464a66605e60620042930b1bad302f001302f002375a605a002605a0046eb4c0ac004c09000858c09000458c0a0004c08400c54ccc08ccdc3a40040022a66604c60420062930b0b181080118048018b181200098120011811000980d8010b180d800919299980e19b87480000044c8c8c8c94ccc08cc09400852616375c604600260460046eb8c084004c06800858c0680048c94ccc06ccdc3a40000022a66603c60320042930b0a99980d99b874800800454ccc078c06400852616163019001232533301a3370e900000089919299980f98108010a4c2c6eb8c07c004c06000854ccc068cdc3a400400226464a66603e60420042930b1bae301f0013018002163018001014375860360026036002603400260320026030002602e002602c002602a0026028002601800c601801064a66601a66e1d200000115333010300b00814985854ccc034cdc3a40040022a66602060160102930b0b180580398048008b18070009807001180600098028008a4c26cac4600a6ea80048c00cdd5000ab9a5573aaae7955cfaba157441", + "hash": "b4a8d138cd2be5e57dce59f2c0d799bf6c40142d66e7ab9630c66442" + }, + { + "title": "order_validator.validate_order_spending_in_batching", "redeemer": { "title": "redeemer", "schema": { - "$ref": "#/definitions/amm_dex_v2~1types~1OrderRedeemer" + "$ref": "#/definitions/amm_dex_v2~1types~1OrderBatchingRedeemer" } }, "parameters": [ @@ -113,8 +132,8 @@ } } ], - "compiledCode": "5905f501000032323232323232323223222232533300932323232533300d3370e9001180600089919299980799b87480000044c8c8cdc424000664600200244a66602c00229000099b8048008cc008008c064004c8cc004004008894ccc05800452f5c026464a66602a6464646464646464a66603a66e1d200200113371e0326eb8c088c06c008528180d8009810000980c000980f000980b000980e000980e000980980109980c80119802002000899802002000980d001180c0009bac3015001300d0051323232323232323232323232323232533301e3370e9000180e8008991919198008008049129998128008a501323253330243371e00400a29444cc010010004c0a4008dd718138009bae3024001301c001163022001301a00130200013018015332323232232533301f3370e900000089919191919191919191919191919299981818198010991919191924c64a66606466e1d20000011323232325333039303c002132498c07000c58dd6981d000981d001181c00098180050a99981919b87480080044c8c8c8c94ccc0e4c0f00084c926301c00316375a60740026074004607000260600142a66606466e1d20040011323232323232533303b303e002132498c07801458dd6981e000981e0011bad303a001303a0023038001303000a153330323370e9003000899191919299981c981e00109924c60380062c6eb4c0e8004c0e8008c0e0004c0c002854ccc0c8cdc3a401000226464a66606e60740042930b1bad3038001303000a153330323370e9005000899191919299981c981e0010a4c2c6eb4c0e8004c0e8008dd6981c00098180050a99981919b87480300044c8c8c8c94ccc0e4c0f00084c926301c00316375a60740026074004607000260600142a66606466e1d200e001132323232323232323232533303f3042002132498c08802458dd6982000098200011bad303e001303e002375a607800260780046eb4c0e8004c0e8008c0e0004c0c002854ccc0c8cdc3a40200022646464646464a666076607c0042930b1bad303c001303c002375a607400260740046eb4c0e0004c0c002854ccc0c8cdc3a4024002264646464a666072607800426493191980080080211299981d8008a4c26466006006607e0046464a66607466e1d20000011323232325333041304400213232498c094008c08c00c58c108004c108008c100004c0e000858c0e0004c0f400458dd6981d000981d0011bac3038001303000a163030009301500a3253330303370e900000089919299981a981c0010a4c2c6eb8c0d8004c0b803054ccc0c0cdc3a40040022a666066605c0182930b0b1817005980900618088068b1bad30310013031002375a605e002605e004605a002605a0046056002605600460520026052004604e002604e004604a002603a0042c603a002464a66603c66e1d20000011323232325333025302800213232498c94ccc090cdc3a400000226464a66605260580042649319299981399b87480000044c8c94ccc0b0c0bc0084c926301000116302d0013025002153330273370e90010008991919191919299981818198010a4c2c6eb4c0c4004c0c4008dd6981780098178011bad302d001302500216302500116302a0013022003153330243370e90010008a99981398110018a4c2c2c604400460120062c604c002604c004604800260380042c6038002464a66603a66e1d200000113232323253330243027002149858dd7181280098128011bae3023001301b00216301b001232533301c3370e90000008a99980f980d0010a4c2c2a66603866e1d20020011533301f301a00214985858c0680048c94ccc06ccdc3a400000226464a66604060460042930b1bae302100130190021533301b3370e900100089919299981018118010a4c2c6eb8c084004c06400858c064004050dd6180e800980e800980e000980d800980d000980c800980c000980b800980b0009806802980680418058008b18088009808801180780098038010a4c26cac64a66601266e1d20000011533300c300700314985854ccc024cdc3a40040022a666018600e0062930b0b18038011bae001230053754002460066ea80055cd2ab9d5573caae7d5d02ba157441", - "hash": "cee7a9a94ca1c6e5cd8bfa719f2506dcc7dc02e862687c24b8c876b3" + "compiledCode": "59012401000032323232323232322322232533300832323232533300c3370e900218058008991919191919191919191919299980c19b8748008c05c0044cdc780a1bae301c301600116301b00130140013019001301200130170013017001300f333232323001001222533301633712900a0008999801801980d980d980d980d980d980d980d980d980d980d80119b81001480504cc010008004c0040048894ccc050cdc4800a4000260300042666006006603200466e04005200200100337586028002601a00c6eb4c048004c02c020c02800458c03c004c03c008c034004c018008526136563253330083370e900000089919299980698078010a4c2c6eb4c034004c01800c58c018008dd7000918029baa001230033754002ae6955ceaab9e5573eae855d11", + "hash": "cb9442a45c5c23faed740128beb5ada61bb39a5b84d4952f112cf4e2" }, { "title": "pool_validator.validate_pool", @@ -150,7 +169,7 @@ } }, { - "title": "_admin_asset_name", + "title": "admin_asset_name", "schema": { "$ref": "#/definitions/ByteArray" } @@ -162,8 +181,8 @@ } } ], - "compiledCode": "593500010000323232323232323232322322322322322322223232323232323232533301832323232533301c3370e9001180d80089919191919191919191919191919191919191919191919191919299981b19b87480000604c8c8c8c8c8c94ccc0f14ccc0f0cdc4a400000226660786086006941288a5013232323232323232323232323232323232533304d3333333302901000600401604701204100a133333333332323232323232323232222222222323232323232323232323232323232323232323232323232323232533307b533307b533307b3375e6e9c00cdd380e099b87304d004304b00314a0266e1cc134008c1340045280a99983d99b87304d002304d00113375e6e9c014dd399199980080080180100411112999841008018800899192999842008020801899191919191919191919191919191919191919191919191919191919299984e80a99984e80a99984e8099b884800000c4cdc42400000229404cdd781a8038a501333302202201d01b32533309e013370e900000089919191929998510099b884800000454ccc28804ccc1bc0400380844c8cccccccccccc13c06c0580040fc0f402401c0e40dc0d40948cdc48018009929998518099b87480000045288a5030a1010031616375a614c02002614c0200461480200261380200c2a66613c0266e1d200200113232323253330a2013371090000008a9998510099983780800701089919999999999982780d80b00081f81e80480381c81b81a812919b890010033253330a3013370e90000008a5114a06142020062c2c6eb4c29804004c29804008c29004004c2700401854ccc27804cdc3a40080022646464646464a66614802a6661480266e21200000313371090000008a50153330a40133307101201002313233333333333305101d01800104103f00b00903b039037027253330a6013371200a00229444cdc48008019929998528099b87480000045288a5030a3010051616375a6150020026150020046eb4c29804004c29804008c29004004c2700401854ccc27804cdc3a400c002264646464a6661440266e212000001153330a20133306f01000e021133333333332222222222323232323232323232323232323232323232323232323232323232323232323232323253330cf0153330cf0153330cf01008100514a02002294054ccc33c040b44cc34c04dd419b813370004600e00c661a6026ea0cdc081081619869809ba83370003e00e661a6026ea0cdc080e81619869809ba801b4bd70099869809ba833702046058661a6026ea0cdc099b80021007006330d301375066e0407c0b0cc34c04dd419b8001d007330d301375003697ae01653330ce01330a40101000e153330ce013370e006056266e1c008cdc099b8002700800614a02a66619c0266148020180142a66619c0266e1c00ccdc0015813899b870013370201000c294054ccc338054ccc33804cdc3801815899b870013370201000c29404cdc38010138a503330a40104000f00d3330a30103f4881004881003330a20103e0090073371200400864a6661940266e1d2000001132323333309701004024023375a619a020026eb4c33804004c8cdd81868808009868809869008009bac30d00100130c801020148000c3200407d4ccc32004cdc4012806099b803370666e08cdc100701280f99b823370201804a66e0407c081200216337109000000a999863009984e00804003099b810013370004003e20026661380207a00e00a6eb8c32404004c32404008dd7186380800985f808049bae30c50100130c501002375c61860200261760200e6eb4c30404004c30404008dd6985f80800985f80801185e80800985e80801185d80800a99985a8080989985c808089985c808081985c809ba8009330b901375000e97ae01330b901010330b901011330b901375000e66172026ea00252f5c06eb4c2e404004c2e404008dd6985b80800985b808011bad30b50100130b501002375a6166020026166020046eb4c2c404004c94ccc28c04cdc3a4000002294452818508080180081f01e00400301c01b01a0120b0b1bad30a60100130a60100230a401001309c010061533309e013370e90040008991929998500099b884800000454ccc28004ccc1b403803007c4c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc30c054ccc30c054ccc30c0402c401052808008a501330c701375066e04cdc001180600499863809ba83370266e0008403401ccc31c04dd419b8001f00c330c701375066e00074034cc31c04dd419b8001b0054bd700b29998610099b8700102613370e0040082940ccc260040d1221004881003330970103300e00c337120400026eb4c30c04004c30c04008dd69860808009860808011bad30bf01001323253330bb013371000200426464646464646618a026ea0c94ccc30804cdc3a4000002264646666660e601000e0b40b06eb4c31404004dd69863008009919bb030c90100130c90130ca0100137586190020026180020a8290001860008299986280a601010000330c501375066e0ccdc119b803370401600200403266e04cdc100f80080125eb80dd69861008011bad30c00100133333306c01e01c002001054052375a617e020046eb4c2f404004cccccc1a001001406c06414413c54ccc2ec04cdc400100089919191919191986280a61010000330c501375064a6661840266e1d20000011323233333307300800705a058375a618a020026eb4c31804004c8cdd81864808009864809865008009bac30c80100130c001054148000c3000414ccc31404dd419b833370466e00cdc100500080100c99b813370404200200497ae0375a6184020046eb4c30004004cccccc1b0070078008004150148dd6985f808011bad30bd0100133333306800500401901b05104f1330bf014c1010000330bf014c1010000330bf01375000497ae03370666e08010048060cdc199b8200201101953330b80153330b801337129000000899b8948000008528099b8848000cdc00008010a5053330b7013308d0100d00b13370200466e0007406c4008ccc234040b8020018ccc230040b402c024dd7185c80800985c808011bae30b70100130af01049375c616a02002616a020046eb8c2cc04004c2ac0411cdd71858808009858808011bae30af0100130a701045375a615a02002615a020046eb4c2ac04004c2ac04008dd69854808009854808011bad30a70100130a701023375a614a020442c2c6eb4c29004004c2700401854ccc27804cdc3a4014002264646464a66614402a6661440266e21200000313371090000008a50153330a20133306f01000e02113232323232323232323232323232323232323232323232323232323232323253330c10153330c10153330c101009100514a0200229404cc31404dd419b8101f007330c501375066e04074018cc31404dd419b8101b007330c501375066e04064018cc31404dd419b8101700a4bd700b2999860009984b0080a0090a9998600099b870033370000c048266e1c0080145280a99986000a9998600099b8700102413370e00600c29404cdc38010028a5033309601032489004881003330950103100e00c3330940103001100f53330bc013371203a004266e2406c0045281bad30bd01002375a61760200266660d202e02a00401e66e21200000133308e0102f003001375c6176020026176020046eb8c2e404004c2c40412cdd7185b80800985b808011bae30b50100130ad01049375c6166020026166020046eb8c2c404004c2a40411cdd69857808009857808011bad30ad0100130ad01002375a6156020026156020046eb4c2a404004c2a404094dd69853808120b0b1bad30a60100130a601002375a61480200261380200c2a66613c0266e1d200c00113232323253330a2013371090000008a999851009998378080070108999999999911111111191919191919191919191919191919191919191919191919191919191919299986480a99986480a9998648080588020a50100114a02a6661920204e26619a026ea0cdc080f00499866809ba83370266e0407001c014cc33404dd400d19866809ba83370203000a6619a026ea0cdc080b00625eb804cc33404dd419b813370203c00a0126619a026ea0cdc080e00399866809ba83370203400a6619a026ea0060cc33404dd419b8101600c4bd700b2999864009984f00809008099b87001337000440082a6661900266e1c0040104cdc38110010a5033309e0103a01100f33309d0103948900488100337120480026eb4c32404004c32404008dd69863808009863808011bad30c501001323232323253330c401022132330c901375064a66618c0266e1d200000113232333330930100802001f375a6192020026eb4c32804004c8cdd81866808009866809867008009bac30cc0100130c40101f148000c31004078cc324053001010000330c901375066e000100052f5c0666661200200400200803803626466192029801010000330c901375064a66618c0266e1d200000113232333330930100702001f375a6192020026eb4c32804004c8cdd81866808009866809867008009bac30cc0100130c40101f148000c31004078cc32404dd419b800050014bd70199998480080080100180e00d99b810160023370202e0046eb4c30c04008dd6986080800999983780a00900100619b8848000004ccc250040d400c004dd71860808009860808011bae30bf0100130b701014375c617a02002617a020046eb8c2ec04004c2cc040054ccc2d00404840404044dd6985c00800985c008011bad30b60100130b601002375a6168020026168020046eb4c2c804004c2c804008dd69858008009929998518099b87480000045288a5030a10100303e03c03a0060340380360241616375a614c02002614c0200461480200261380200c2a66613c0266e1d200e00113232323232323232323253330a80153330a80153330a801337109000003899b884800001c528099b884800000c528099191919191919191919191919299985a80a99985a80800899191919191919299985e0099b8748008c2ec040044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc33004cdc3a401c61960200a264646464646464646464a6661ac02a6661ac02a6661ac02a6661ac02a6661ac02a6661ac02a6661ac02a6661ac02a6661ac02a6661ac02a6661ac0266e3c0641984cdd782300b8a5013375e08802a29404cdd78210098a5013370e07801a29404cdc381d0058a5013375e08002229404cdd781b8048a5013370e06a00e29404cdc38198028a5013370e66e040c5200200314a0266e1c0bc0045281bad30da0100130da01002375a61b00200261b0020046eb4c35804004c35804008dd6986a00800986a008011869008009865008028b1bad30d00100130d001002375a619c02002619c02004619802002619802004619402002619402004619002002619002004618c02002618c0200461880200261780266118020c600a6eb8c30804004c2e80400458c30004004c2e004010c2f804004c2f804004c2f404008c2ec04004c2cc040d04ccc2080408c0840d04cc2e404dd40059985c809ba8009330b901375000e66172026ea0014cc2e404dd4001a5eb8058cdc3a4004616a026ea8c2e404004c2e404008dd6985b80800985b808011bad30b50100130b501002375a6166020026166020046eb4c2c404004c2c404008dd6985780800991919191919191919191919191919191919191919191919191919191919191919299986500a9998650099b88480000084cdc48118010a5013232323232323253330d10153330d10100c100114a0264a6661a4020542661ac026ea0cdc099b8002900a007330d601375066e0409c020cc35804dd419b8002500a330d601375066e0408c020cc35804dd40109986b0099986900800a6103d87a80004c0103d87980004bd7009986b009ba833702052010661ac026ea0cdc099b8002700a007330d601375066e04094020cc35804dd419b8002300a330d6013750042661ac026661a402002980103d87a80004c0103d87980004bd7029998688099b8848000cdc081624004266e240a80205280b299986800998530080a8098a9998680099b870013370000e008266e1c008cdc00018030a50153330d001330a60101100f153330d0013370e00200e266e1c008cdc000199b8200600414a0264a6661a202a6661a20266e1c0080204cdc380199b8000400714a0266e1c01400452819985380821a45004881003330a6010420140123330a50104100f00d3330a40104500e00c3370066e080c8cdc0813a400406064a6661980266e1d2000001132323333309901006064062375a619e020026eb4c34004004c8cdd8186980800986980986a008009bac30d20100130ca0105e148000c32804174ccccc2580404c04400818017858dd69865808011bad30c90100153330c70133712006002266ec0dd4001a610100001337606ea0004dd419b810030013232323253330ca0133710900000088008b19b830020013370404a00466e04cdc119b8202600100e3370466e0809016c040cdc082d02e19b88480000054ccc31004cc2680402401c4cdc0800801080099984d0081d80400319b803370405003a04c6eb8c31804004c31804008dd7186200800985e008049bae30c20100130c201002375c61800200261700200e6eb4c2f804004c2f804008dd6985e00800985e00801185d00800985d00801185c00800a9998590080509985b008271985b008261985b009ba8009330b601375000e97ae01330b60104c330b60104e330b601375000e6616c026ea00252f5c06eb4c2d804004c2d804008dd6985a00800985a008011bad30b20100130b201002375a6160020026160020586eb4c2b8040acc94ccc2a404cdc3a400000229445281853808048b1bad30ac0100130ac01002375a6154020026154020046eb4c2a004004c2a004008dd6985300800985300801185200800984e008030a99984f0099b87480400044c8c8c8c8c8c94ccc290054ccc290054ccc29004cdc42400000a266e21200000314a0266e21200000114a02a666148026660e20240200462646464646464646464646464646464646464646464646464646464646464646464646464a66619002a66619002a6661900201c200a29404004528099866009ba83370266e04090020030cc33004dd419b813370204400c01466198026ea0cdc081000419866009ba83370203c00c66198026ea0cdc080e007a5eb80594ccc31c04cc2740406405c54ccc31c04cdc380199b8000702b13370e00400a294054ccc31c054ccc31c04cdc3800815899b8700300714a0266e1c00801452819984e8081ca4410048810033309c0103801301133309b01037016014337120400066eb4c31c04004c31c04008dd69862808009862808011bad30c30100130c301002375a61820200264646464646464a6661840266e200040084c8c8cc32004dd41929998628099b87480000044c8c8ccccc2480401417416cdd69864008009bad30c9010013233760619802002619802619a020026eb0c32c04004c30c0415c5200030c301056330c8014c1010000330c801375066e04020008cc32004dd419b800070014bd70199998478080280200082c82b9999999983980300280200181181082c02b0a9998610099b8800200113232330c8014c1010000330c801375064a66618a0266e1d200000113232333330920100505d05b375a6190020026eb4c32404004c8cdd81866008009866009866808009bac30cb0100130c301057148000c30c04158cc32004dd419b80008001330c801375066e0401c0092f5c06666611e0200800a0020b20ae666666660e600a00c0060080420460b00ac26618c02981010000330c6014c1010000330c601375000c6618c026ea00152f5c066e08010088cdc100200f99b81019002337020340046eb4c2fc04008dd6985e80800999983580b80a80100799b8848000004ccc240040c400c004dd7185e80800985e808011bae30bb0100130b30104d375c6172020026172020046eb8c2dc04004c2bc0412cdd7185a80800985a808011bae30b30100130ab01049375a6162020026162020046eb4c2bc04004c2bc04008dd69856808009856808011bad30ab0100130ab01027375a61520204c2c2c6eb4c2a004004c2a004008dd69853008009853008011bad30a401001309c0100616309c0100516375a6142020026142020046eb4c27c04004c27c04008c27404004c27404008c26c04004c26c04008c26404004c26404008c25c04004c25c04008c25404004c23404004cc1700cc010dd59849008009849008009844808051847808009847808011bab308d01001308d01001308401001308a01001308a0100130810100430880100530860100430860100430840100316163304e02023232533307d33307d3375e00201694128899983e99baf00101f4a0944528184080800983c8009919980080080125eb808894ccc1fc00840044ccc00c00cc20804008cc88cc20804c16ccc17001c008004dd6984080801000981100c9982580f1191919191919191929998400099b87480080044cdc79bae308501307e00201014a060fc00261060200260f600261020200260f200260fe00260fe00260ec0026eb0c1ec004c1ec008dd6183c800983c801183b800983b8011bae3075001307500230730013073002375a60e200260e20046eb4c1bc004c1bc008c1b4004c1b4008c1ac004c1ac008c1a4004c184ccccccc0e000800403002c018021281832001183100099192999832983400109919191919191919192999837183880109919299983699baf0010071325333071307400113376001a0082c66082028466ebcc1ccc1b0c1ccc1b0c1ccc1d0c1b000401858c1c4004c1a400458c1bc004cc0f403c8cdd7801183798341837983400098368009832800983580098318009834800983480098300008b18330009981a003919baf0053066305f001300100122533305a00114bd70099191982e9ba800233004004323300100100222533305e00114bd7009919299982e99982e99b870020064a09444cc184dd40011980200200089980200200098310011bad3060001305e002375a60b8002444444444444646464646464646464646464646464646464646464646464646464646464646464a66610202a66610202a6661020200c2006294040045280a99984080815899842809ba83370266e0008401c010cc21404dd419b8101f0053308501375066e0007401ccc21404dd419b8101b0053308501375003297ae013308501375066e04084014cc21404dd419b813370003e00e0086610a026ea0cdc080e80299842809ba83370003600e6610a026ea00652f5c02ca66610002660ac014010266e1ccdc000201300089929998408099b8700500213370e00204e2940ccc15c0ad22010048810033305602a009007301f00232533307e3370e900000089919199998258030128121bad308101001375a6104020026466ec0c21404004c21404c21804004dd6184200800983e0108a400060f80406666609001c01800404204066e212000001533307a3305000800613370200266e000840804004ccc14009401c014dd7183e800983e8011bae307b0013073009375c60f200260f20046eb8c1dc004c1bc01cdd6983a800983a8011bad3073001307300230710013071002306f001533306901313306d0123306d0113306d3750012660da6ea001d2f5c02660da022660da024660da6ea001ccc1b4dd4004a5eb80dd6983680098368011bad306b001306b002375a60d200260d20046eb4c19c004c19c00cdd698328011111111919191919bb0375066e04c040cdc0980780099b823370400600a66e04014018004dd419b820023370200a00c66e08cdc099b824801001001400ccdc12400866e0001c014cdc119b8248020cdc000300219b813370400c601800a66e08cdc100380280219b823370000a0060084444446466ec0dd419b823370400a00200c6ea0cdc019b823370400400800e66e08014004cdc080080111111119b833370466e08018010008cdc119b820050030012222337606ea0cdc199b82002004001375066e0ccdc10010018009111111119191919b8333702601a66e04cdc100080099b8233704900400119b823370400801266e04cdc100380519b8200600b00133704900200119b803370466e08018008cdc000380499b820053370266e0802000ccdc100100519b8200400133702002004466e0800400494ccc138cdc4000a40002c2a66609c66e1c005200014800054ccc138cdc3800a4004290010a99982719b87001480105200213232333001001003002222533305233710002004266600600600266e0ccdc019b83005001001480104008cdc019b8300148011200204904501000d00103004001601416375660a200260a200260a00046eb0c138004c138008c130004c130004c12c004c128008dd59824000982400098238011bac3045001304500130440023758608400260740462c6eb4c100004c100008dd6181f000981f001181e000981a0140a99981b19b87480080604c8c8c8c8c8c8c8c8c8c94ccc1014ccc1014ccc100cdc4a400000e266e1c008004528099b89480100045280991919191919191919191919191919191929998289999999981680800300200d02580c02280509999999999919191919191911111111119191919191919191919191919191919299983a183b80109919299983b183c801099191919191919191919191919191919191919191919191919299984600a99984600a99984600a9998460099b88480000104cdc42400000429404004528099baf02700814a0264a66611a0266e1d201200113232323253330910133710900000089919191919191919191919191919191919191919191919191919191919191929998580099b890200011323253330b20100114a22ca666162026610e0200e00a266e1ccdc000101380089929998590099b8700300213370e0020502940ccc220040d1221004881003330870103300600416333305a001480001380854ccc2b804cc210040200184cdc080099b80026024100133308401035007005375c6162020026162020046eb8c2bc04004c29c04014dd71856808009856808011bae30ab0100130a3010023253330a5013370e9000000880188029851808051929998520099b874800000440204018c28804030c29c04004c29c04008c29404004c2740402cc28c04004c28c04008c28404004c26404020c27c04004c27c04004c25804018c27004004c27004004c24c04010cc0f40cccdc09833019a40046607806490001981d80219b81306400448008cc0e800d200016375a612a02002612a020046eb0c24c04004c22c0401c5281845808030b19982c00580480c9bad308f01001308f01002375a611a02002611a02004611602002611602004611202002611202004610e02002610e02004610a02002610a0200260f8660980440086eacc20804004c20804004c1e4024c1fc004c1fc008dd5983e800983e800983a000983d000983d00098388018b183b8009982280b119191919299983b19983b19baf0030184a09444ccc1d8cdd78008042504a22940c1e8004c1c8004c1e0004c1c000458c1d4004cc10c0548c8c8c8c8c8c8c8c94ccc1e0cdc3a4004002266e3cdd7183e983b0010070a503076001307b00130730013079001307100130770013077001306e0013073001306b00130710013071002375c60de00260de00260dc00260da00260d800460d400260d400260d200260c0660160029000191919191929998349836001099191919191919299983699b87303f3304101323375e60e660d860e660d860e660e860d80020040142a6660da66e1cc0fccc1040488cdd7983998361839983600080100509919980080080680611129998398010a9998398008a5eb80584c8c94ccc1d400c52f5c02660ec646464646464646464646464a6660fc66ebc01c0084ccccccc14c038030098094004089288b1982a810801984080800983c804983f800983f800983f001183e000983a000983d000983d0009838801983b801999802802800983c001983b801983a8010b0b18388009834800983780098338009836800983680098320008b18350009981c005119baf008306a306300130350023300f00223300c0080013300e00223300b0080013001001222533305933712900a0008999801801982f982f982f982f982f982f982f982f982f982f80119b81001480504cc01c008004c004004888894ccc170008400c4c8c94ccc17800c40144c8c8c8ccccc028028004004010008c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc1fccdd780f801899191929998410099b88480000b44c8c94ccc21004cdc4240000042a6661080264a66610a0266e1d200000115333085015333085015333085013370e66e04cdc000c018001008899b873370202c00601e29404cdc399b8001403000d14a0266e1ccdc08090018058a5015333085015333085015333085013370e66e0406000c0444cdc399b813370002c06000401e29404cdc399b8101400300d14a0266e1ccdc00090180058a5030830100610021616325333084013370e900000089919199998288188120111bad308701001375a6110020026466ec0c22c04004c22c04c23004004dd618450080098410080f0a400061040203a6666609c00400205a04003c2c6eb4c20c04008dd69840808009929998400099b87480000044cdd81ba80133750022266ec0dd40089ba8013307e001163083010013083010023081010013079020375a60fe00260fe0046eb4c1f4004c1f4008dd6983d800983d8049bad3079008375a60f000260f00046eb4c1d8004c1d8008dd6983a000983a0021bad3072003375860e200260e20046eb0c1bc004c1bc004c1b8004c1b4008c1ac004c1ac008dd6983480098348011bad30670013067002306500130650013064001305b004306200430600033060003305e00222323300100100322533305a00114bd7009982d98019bad305c00133002002305d001300100122253330543371200290000982c8010999801801982d00119b81001480081341240400340040d006805805058dd5982a800982a800982a0011bac3052001305200230500013050001304f001304e00237566098002609800260960046eb0c124004c124004c120008dd61823000981f0138b180780118070019bac304200130420023758608000260800046eb4c0f8004c0f8008c0f0004c0d00a054ccc0d8cdc3a400803029445289111119b833370466e08014010008cdc100180091111191919b833370400200c66e00cdc100180380099b8200100433702002004444646464646464a666078a66607866ebc02401440045280a511632533303c3370e90000008991919299981f99b87480000045854ccc0fccdc3a4004002266e3cdd71822181e802801099b8f37286eccc110c0f4014008c0f4010dd71821000981d004099baf0024c0103d8798000303a007303f001303f001303e002303c00130340012232320023253330363370e900000089919191919191919191919191919299982398250010991919191924c64a66609266e1d200000113232323253330503053002132498c07000c58dd698288009828801182780098238050a99982499b87480080044c8c8c8c94ccc140c14c0084c926301c00316375a60a200260a2004609e002608e0142a66609266e1d2004001132323232323253330523055002132498c07801458dd6982980098298011bad30510013051002304f001304700a153330493370e90030008991919192999828182980109924c60380062c6eb4c144004c144008c13c004c11c02854ccc124cdc3a401000226464a66609c60a20042930b1bad304f001304700a153330493370e9005000899191919299982818298010a4c2c6eb4c144004c144008dd6982780098238050a99982499b87480300044c8c8c8c94ccc140c14c0084c926301c00316375a60a200260a2004609e002608e0142a66609266e1d200e00113232323232323232323253330563059002132498c08802458dd6982b800982b8011bad30550013055002375a60a600260a60046eb4c144004c144008c13c004c11c02854ccc124cdc3a40200022646464646464a6660a460aa0042930b1bad30530013053002375a60a200260a20046eb4c13c004c11c02854ccc124cdc3a4024002264646464a6660a060a6004264931981b801919299982799b87480000044c8c8c8c94ccc158c1640084c8c9263023002304000316305700130570023055001304d00216304d00116375a60a200260a20046eb0c13c004c11c02858c11c024c0d0028c0d402cc0cc030c0c803458dd6982400098240011bad30460013046002304400130440023042001304200230400013040002303e001303e002303c00130340021630340013300f00200123253330333370e90000008a99981b18188010a4c2c2a66606666e1d200200115333036303100214985858c0c4004c004004894ccc0d000452000133700900119801001181b80098008009129998190008a4000266e01200233002002303500122323300100100322533303300114bd70099192999819180280109981b00119802002000899802002000981b801181a8009111111191919191919191919191919191919191919191919191919191919191919191919191919191919299982c9919191919191919191919191919191919191919191919191919191919191919299983ca99983c824099b8701b00b14a22a6660f2a6660f2a6660f2a6660f2a6660f266ebc07c03c4cdd780e8068a5013370e02e00e29404cdc380a8028a5013371e02600629404cdd78088008a5016307d001307d002375c60f600260f60046eb4c1e4004c1e4008dd6983b800983b800983b000983a8011bad3073001307300230710013071002306f0013067019306d001306d002375c60d600260d60046eb4c1a4004c1a4008dd698338009833800983300098328011bad3063001306300230610013061002305f001305702a13232323232323232323253330633370e66607405606c06a90010a99983199b8733303a028036035480084c8c8c8c8c8c8c8c8c94ccc1b0cdc399b8100100233702052030266e9520003307002d3307002b3307000a330703750046660e06ea0084cc1c0c94ccc1b4cdc3a4000002264646464646466e9520003307732374e660f060ea002660f060ec00297ae0337606ea000cdd4000a5eb80dd6983b800983b8011bad3075001306d0013073001306b01e14c103d87a8000306b01d33070375203e660e006c660e06e9ccc1c0dd4013998381ba802533070375000c660e06ea0010cc1c0dd4014a5eb80cc1c0dd3998381ba8016330703750028660e06ea0014cc1c0dd4001998381ba80184bd7025eb8058ccc1080c00f8028ccc1040c80f4024ccc1000b802c024ccc0fc0c0028020dd698348011bad306700153330653303b00b0091337606ea0cdc08012410137dc046ea0cdc0800a410137dc04266ec0dd40011ba800133303b02900a00833303a02b00900716163374a9000198331ba903533066375200297ae0303a3303b303a3303b007005303a3303b003001375c60ca00260ca0046eb8c18c004c16c070dd7183080098308011bae305f001305701a16375a60ba00260ba0046eb4c16c004c16c008dd6982c800982c800982c000982780099814010009982a000982a0011bae30520013052002375a60a000260a00046eb4c138004c138008dd6982600098260011bad304a001304a002375a60900026090004608c002608c0046088002607801e608400260840046eacc100004c100004c0dc034dd5981e800981e801181d8009819800981c800981c800981800391299981619b8f0024890013371e0029110014a044464664464a66606066e1d200200110021375a606a605c006605c00464a66605c66e1d200200114c103d87a8000132323300100100222533303400114c103d87a800013232323253330353371e014004266e95200033039375000297ae0133006006003375a606c0066eb8c0d0008c0e0008c0d8004dd5981998160011816000a40006601a006004446464004602e0026600a00400246e4c00488cdc500100091180219299981419b8748000004530103d87a8000153330283370e900100089919198008008029129998170008a6103d87a8000132323232533302f3371e00e004266e952000330330014bd7009980300300198180019bae302e00230320023030001375c605a604c004266e9520003302c302d30260024bd70181300091111111299981619299981819198008008011129998190008a5eb804c8ccc888c8cc00400400c894ccc0e0004400c4c8ccc888cc0f4dd39981e9ba90093303d37520066607a6ea00092f5c00026eb8c0dc004dd6981c00099801801981e001181d0009bae30310013756606400266006006606c0046068002294458c8cc004004008894ccc0c400452f5bded8c0264646464a66606466e3d2201000021003133036337606ea4008dd3000998030030019bab3033003375c6062004606a00460660022a666058646464646464646464646464a66607066e1d2000303700113232533303a533303a533303a323300100101522533303f00114a026464a66607c66e3c00801452889980200200098218011bae304100113371200a01029404cdc480419b8000601014a0294458dd7181f000981b0008b181e000981a0079bad3037002375a606a00264a66606866e1d20023033001132323253330373370e9001181b000899bb037500046078606a0022c602c60686076607860680226eb4c0e8004c0c800458c04cc0c4c04cc0c4038c8c94ccc0dcc0e80084c8c94ccc0d8cdc3800a400426464666002002900024000444a66607466e1c00801040044c94ccc0ed4ccc0eccdc3801a4000266e1c005205a14a0266e08ccc010010cdc0001a400400490008a99981da99981da99981d99b8800148180528899b8800148180528899b88337009030240240022c266600800866e0000d20023370066e08009201433702002903019b8e006002371a0042c6eb4c0dc008dd7181a8008b181c000999119299981a99b874800800440084dd5981d181980198198011980980080525eb7bdb180dd5981b000981b00098168009819800981980098151804998050040018a511616253330243370e900018118008981498110008b180080091129998138010a60103d87a80001323253330263370e0069000099ba548000cc0a80092f5c0266600a00a00266e0400d2002302b00330290022302600122323300100100322533302600114c103d87a800013232323253330273371e00e004266e9520003302b374c00297ae0133006006003375660500066eb8c098008c0a8008c0a0004c074040c088004c06800458c080004c080008c078004c058024526136563253330183370e90000008991919191919299981098120010991924c6601200846eb4004c03001458dd6981100098110011bac30200013020002301e001301600a153330183370e90010008991919191919191929998119813001099191924c6601800646eb4004cc02c0108dd680098070038b1bac302400130240023758604400260440046eb4c080004c080008c078004c05802854ccc060cdc3a400800226464a66603a60400042930b1bad301e001301600a153330183370e9003000899191919299980f98110010a4c2c6eb4c080004c080008dd6980f000980b0050b180b00491191980080080191299980e8008a4c2646600600660420046006603e0026002010464a66602c66e1d20000011323232323232323232323232323232323232533302b302e0021323232498c94ccc0accdc3a400000226464a66606060660042649319299981719b87480000044c8c8c8c8c8c8c8c94ccc0e4c0f00084c8c9263026002302400316303a001303a00230380013038002375a606c002606c0046eb4c0d0004c0b000858c0b000458c0c4004c0a401054ccc0accdc3a40040022a66605c60520082930b0b1814801980b008180a8088b181600098160011bae302a001302a002375a605000260500046eb4c098004c098008dd6981200098120011bad30220013022002375a60400026040004603c002603c004603800260280042c6028002464a66602a66e1d2000001132323232533301c301f002149858dd7180e800980e8011bae301b001301300216301300123253330143370e9000000899191919299980d980f0010991924c64a66603466e1d200000113232533301f3022002132498c94ccc074cdc3a400000226464a666044604a0042649318078008b1811800980d8010a99980e99b87480080044c8c8c8c8c8c94ccc098c0a400852616375a604e002604e0046eb4c094004c094008dd69811800980d8010b180d8008b1810000980c0018a99980d19b874800800454ccc074c06000c52616163018002300800316301c001301c002301a001301200216301200123253330133370e900000089919299980c180d8010a4c2c6eb8c064004c04400854ccc04ccdc3a40040022a66602c60220042930b0b1808800919299980919b87480000044c8c94ccc05cc06800852616375c603000260200042a66602466e1d2002001132325333017301a002149858dd7180c00098080010b18080009bad001375c0026eb8004dd70009bae001230053754002460066ea80055cd2ab9d5573caae7d5d02ba15744ae8d", - "hash": "8eca59694d6b6e98692de1f825b8c75b9d448ca82c62eea85dabd71d" + "compiledCode": "593aaa0100003232323232323232323223223223223223222232323232323232533301732323232533301b3370e9001180d000899191919191919191919191919191919191919191919191919191919299981b99b87480000684c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc140cdc3a4000609e0022646464a6660a66660a660b4032941288a999829999180080091299982c0008a51132533305900114a226464a6660b06660b0646600200200444a6660ba00229404c8c94ccc170cdc38030010a511330040040013061002375a60be002941288998028028008a50305d003375a60b600460b60020322a6660a66062606401e2a6660a6660660120042a6660a6666606800201609a08e2666666666646464646464646464644444444446464646464646464646464646464646464646464646464646464a6661000266048004002266ebcdd38021ba7323333001001003002007222253330870100310011323253330890100410031323232323232323232323232323232323232323232323232323232323253330a30153330a30153330a301337109000001899b8848000004528099baf03500714a02666604604603c03864a6661480266e1d200000113232323253330a80153330a801337109000000899983b8080070110a5013233333333333304f01c01700103f03d00900703903703502623371200600264a6661520266e1d200000114a22940c29c0400c58dd69856008009856008011855008009851008030a9998520099b87480080044c8c8c8c94ccc2a0054ccc2a004cdc42400000226660ee02001c04429404c8cccccccccccc13c07005c0040fc0f402401c0e40dc0d40988cdc48008019929998548099b87480000045288a5030a70100316375a61580200261580200461540200261440200c2a6661480266e1d2004001132323232323253330aa0153330aa013371090000018a9998550099b88480000044ccc1e40480400905280a5013233333333333305101e01900104103f00b00903b039037028253330ac013371200a00229444cdc48008019929998558099b87480000045288a5030a90100516375a615c02002615c020046eb4c2b004004c2b004008c2a804004c2880401854ccc29004cdc3a400c002264646464a66615002a6661500266e212000001133307701000e02214a0266666666664444444444646464646464646464646464646464646464646464646464646464646464a6661a002a6661a00266e212000004153330d00133712006008266ebcdd30009ba603e14a0294054ccc340040a04cc35004dd419b813370003c006004661a8026ea0cdc080e0139986a009ba833700034006661a8026ea0cdc080c0139986a009ba80164bd7009986a009ba83370203c04e661a8026ea0cdc099b8001c003002330d401375066e0406809ccc35004dd419b80018003330d401375002c97ae01633330a60133330a60133330a6014bd6f7b6302450048810002200700502600b0093370200600464a66619e0266e1d2000001132323333309b01004023022375a61a4020026eb4c34c04004c8cdd8186b00800986b00986b808009bac30d50100130cd0101f148000c334040794ccc33404cdc4012005899b803370666e08cdc100681200f19b823370201604866e0407807d20021653330cc01330a40100800613370200266e0008007c4004ccc290040f801c014dd71867808009867808011bae30cd0100130c501009375c6196020026196020046eb8c32404004c3040401cdd69863808009863808011bad30c50100130c50100230c30100130c30100230c10100153330bb010131330bf01011330bf01010330bf0137500126617e026ea001d2f5c026617e020206617e020226617e026ea001ccc2fc04dd4004a5eb80dd6985f80800985f808011bad30bd0100130bd01002375a6176020026176020046eb4c2e404004c2e404008dd6985b808009929998548099b87480000045288a5030a70100300103e03c00800603803603402516375a61580200261580200461540200261440200c2a6661480266e1d20080011323253330a60153330a601337109000000899983a8070060100a5013232323232323232323232323232323232323232323232323232323232323253330c50153330c5013371290000040a9998628099b894800002454ccc31404cdc42400066e0002002454ccc31404cdc4810001099baf374c0026e980cc5280a5014a029404cc32404dd419b813370003e01000c66192026ea0cdc099b8001d009004330c901375066e0006c020cc32404dd419b80019009330c901375066e0005c0092f5c02c66661360266661360297adef6c604890048810002200c00a001375a6190020026190020046eb4c31804004c31804008dd6986200800991929998600099b880010021323232323232330ca01375064a66618e0266e1d200000113232333333072008007059057375a6194020026eb4c32c04004c8cdd81867008009867009867808009bac30cd0100130c501053148000c31404148cc32805301010000330ca01375066e0ccdc119b803370401400200403066e04cdc100f00080125eb80dd69863808011bad30c50100133333306b01d01b002001053051375a6188020046eb4c30804004cccccc19c00c01006806014013854ccc30004cdc400100089919191919191986500a61010000330ca01375064a66618e0266e1d200000113232333333072008007059057375a6194020026eb4c32c04004c8cdd81867008009867009867808009bac30cd0100130c501053148000c31404148cc32804dd419b833370466e00cdc100480080100c19b813370404000200497ae0375a618e020046eb4c31404004cccccc1ac06c07400800414c144dd69862008011bad30c20100133333306700400301801a05004e1330c4014c1010000330c4014c1010000330c401375000497ae03370666e0800c04405ccdc199b8200101001853330bd01330950100d00b13370200466e0007406c4008ccc254040bc020018ccc250040b802c024dd7185f80800985f808011bae30bd0100130b501049375c6176020026176020046eb8c2e404004c2c40411cdd7185b80800985b808011bae30b50100130ad01045375a6166020026166020046eb4c2c404004c2c404008dd69857808009857808011bad30ad0100130ad01024375a6156020462c6eb4c2a804004c2880401854ccc29004cdc3a4014002264646464a66615002a6661500266e212000003153330a801337109000000899983b8080070110a5014a026464646464646464646464646464646464646464646464646464a66618402a6661840266e212000005153330c2013371203a0062a6661840266e2406c0084cdd79ba6001374c06029405280a501330c601375066e0406800ccc31804dd419b81018002330c601375066e0405800ccc31804dd419b81014002330c601375066e040480152f5c02c66661300266661300266661300297adef6c604890048810001f00f00d00200b009001375a6184020046eb4c30004004cccc1a0058050004038ccc258040c000c004dd71860808009860808011bae30bf0100130b70104b375c617a02002617a020046eb8c2ec04004c2cc04124dd7185c80800985c808011bae30b70100130af01047375a616a02002616a020046eb4c2cc04004c2cc04008dd69858808009858808011bad30af0100130af01026375a615a0204a2c6eb4c2b004004c2b004008dd69855008009851008030a9998520099b87480300044c8c8c8c94ccc2a0054ccc2a004cdc42400000226660ee02001c04429404ccccccccc888888888c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc32c054ccc32c04cdc4240000102a6661960266e240900084cdd79ba6001374c07229405280a99986580811899867809ba83370203400c6619e026ea0cdc099b81018004002330cf01375002c6619e026ea0cdc080a00119867809ba83370202401097ae01330cf01375066e04cdc080d00100319867809ba8337020300086619e026ea0cdc080b00119867809ba8014330cf01375066e040480212f5c02c66661420266661420297adef6c604890048810001e00e00c001375a619c02002619c020046eb4c33004004c33004008dd6986500800991919191929998648081089919867009ba83253330cb013370e9000000899191999984b8080400f80f1bad30ce01001375a619e020026466ec0c34804004c34804c34c04004dd618688080098648080f0a400061920203a6619c02981010000330ce01375066e000100052f5c066666128020040020080360342646619c029801010000330ce01375064a6661960266e1d200000113232333330970100701f01e375a619c020026eb4c33c04004c8cdd81869008009869009869808009bac30d10100130c90101e148000c32404074cc33804dd419b800050014bd701999984a0080080100180d80d19b810150023370202c0046eb4c32004008dd6986300800999983700980880080599984e0081b0018009bae30c70100130c701002375c618a02002617a020286eb8c30c04004c30c04008dd7186080800985c80800a99985d00809080808089bad30be0100130be01002375a6178020026178020046eb4c2e804004c2e804008dd6985c00800985c008011bad30b6010013253330a9013370e90000008a5114a0614e0200607c07807400c06807006c04a2c6eb4c2b004004c2b004008c2a804004c2880401854ccc29004cdc3a401c002264646464646464646464a66615c02a66615c0266e212000007153330ae01337109000003899b884800000c5280a5013232323232323232323232323253330bb0153330bb0100113232323232323253330c2013370e900118608080089919191919191919191919191919191929998690099b8748038c344040144c8c8c8c8c8c8c8c8c8c94ccc37004cdc780c8330a99986e0099baf046017153330dc013375e08802a2a6661b80266ebc10804c54ccc37004cdc381e0068a99986e0099b8703a00b153330dc013375e0800222a6661b80266ebc0dc02454ccc37004cdc381a8038a99986e0099b87033005153330dc013370e66e040c5200200313370e05e00229405280a5014a029405280a5014a029405280a50375a61c00200261c0020046eb4c37804004c37804008dd6986e00800986e008011bad30da0100130da0100230d80100130d00100516375a61ac0200261ac020046eb4c35004004c35004008c34804004c34804008c34004004c34004008c33804004c33804008c33004004c33004008c32804004c30804cc2400418c014dd71864008009860008008b186300800985f00802186200800986200800986180801186080800985c8081a89998450081181081a89985f809ba800b330bf0137500126617e026ea001ccc2fc04dd40029985f809ba80034bd700b19b8748008c2ec04dd5185f80800985f808011bad30bd0100130bd01002375a6176020026176020046eb4c2e404004c2e404008dd6985b80800985b808011bad30b50100132323232323232323232323232323232323232323232323232323232323232323232323253330d30153330d3013371090000048a9998698099b884800001c54ccc34c04cdc4813003899baf374c0026e981045280a5014a0264a6661a80204c2661b0026ea0cdc099b80025008005330d801375066e0408c018cc36004dd419b80021008330d801375066e0407c018cc36004dd400e9986c0099986a00800a6103d87a80004c0103d87980004bd7009986c009ba83370204a00c661b0026ea0cdc099b80023008005330d801375066e04084018cc36004dd419b8001f008330d801375003a661b0026661a802002980103d87a80004c0103d87980004bd7029998698099b8848000cdc081424004266e240980185280b199985480999985480999985480a5eb7bdb1812210048810000100d00b3370000400802201e00a66e00cdc101899b81026480080bcccc2a40410c02c024c94ccc34004cdc3a400000226464666661380200c0c40c06eb4c34c04004dd6986a008009919bb030d70100130d70130d801001375861ac02002619c020b82900018670082d9999984c8080880780102f02e19b8100300153330cc01337120040022004200264646464a66619e0266e2120000011001163370600400266e08090008cdc099b823370404a00201a66e08cdc101182d00799b8105905b53330ca01330a20100900713370200200420026661440207801000c66e00cdc101400e8131bae30cc0100130cc01002375c6194020026184020126eb8c32004004c32004008dd7186300800985f008039bad30c40100130c401002375a618402002618402004618002002618002004617c02002a666170020142661780209c661780209866178026ea0024cc2f004dd4003a5eb804cc2f004130cc2f004138cc2f004dd40039985e009ba80094bd701bad30bc0100130bc01002375a6174020026174020046eb4c2e004004c2e004008dd6985b00800985b008169bad30b40102c3253330af013370e90000008a5114a0615a020122c6eb4c2c804004c2c804008dd69858008009858008011bad30ae0100130ae01002375a61580200261580200461540200261440200c2a6661480266e1d2010001132323232323253330aa0153330aa013371090000028a9998550099b884800000c54ccc2a804cdc42400000226660f202402004829405280a5013232323232323232323232323232323232323232323232323232323232323253330c90153330c9013371090000050a9998648099b8902000413375e6e98004dd301b8a5014a026619a026ea0cdc099b8101f004008330cd01375066e04cdc080e80100319866809ba8337020360086619a026ea0cdc080c80119866809ba83370202e01497ae016333309f01333309f01333309f014bd6f7b6302450048810002601401200301000e001375a6198020026198020046eb4c32804004c32804008dd69864008009864008011bad30c6010013232323232323253330c70133710002004264646619a026ea0c94ccc32804cdc3a4000002264646666612c0200a0b80b46eb4c33404004dd69867008009919bb030d10100130d10130d201001375861a0020026190020ac2900018640082a9986680a61010000330cd01375066e04020008cc33404dd419b800070014bd70199998498080280200082c02b1999999983900300280200181101002b82a8a9998638099b8800200113232330cd014c1010000330cd01375064a6661940266e1d200000113232333330960100505c05a375a619a020026eb4c33804004c8cdd81868808009868809869008009bac30d00100130c801056148000c32004154cc33404dd419b80008001330cd01375066e0401c0092f5c0666661260200800a0020b00ac666666660e400a00c0060080400440ae0aa26619602981010000330cb014c1010000330cb01375000c66196026ea00152f5c066e08010084cdc100200f19b81018002337020320046eb4c31004008dd6986100800999983500b00a00080719984c008190018009bae30c30100130c301002375c61820200261720209a6eb8c2fc04004c2fc04008dd7185e80800985a808259bae30bb0100130bb01002375c6172020026162020926eb4c2dc04004c2dc04008dd6985a80800985a808011bad30b30100130b301002375a6162020026162020506eb4c2bc0409c58dd69857008009857008011bad30ac0100130ac01002375a61540200261440200c2c61440200a2c6eb4c29c04004c29c04008dd6985280800985280801185180800985180801185080800985080801184f80800984f80801184e80800984e80801184d80800984980801183080099299984a0099b87480100044c26404c248040144cc1b80cc014c24804010dd5984b80800984b80800984700805184a00800984a008011bab309201001309201001308901001308f01001308f01001308601004308d01005308b01004308b01004308901003163305401f23232533308201333082013375e0020149412889998410099baf00101e4a0944528184300800983f0009919980080080d25eb808894ccc2100400840044ccc00c00cc21c04008cc88cc21c04cc150018008004dd69843008010009982900f1191919191919191929998430099b87480080044cdc79bae308b0130840100201014a0610802002611202002610202002610e0200260fe002610a02002610a0200260f80026eb0c20404004c20404008dd6183f800983f801183e800983e8011bae307b001307b00230790013079002375a60ee00260ee0046eb4c1d4004c1d4008c1cc004c1cc008c1c4004c1c4008c1bc004c19cccccccc0fc00800403002c018021281835001183400099299983399b8748000c1980044c8c8c8c8c8c8c8c8c8c8c94ccc1d4c1e00084c8c94ccc1d0cdd7800803899299983c183d800899bb000d004163304901523375e60f460e660f460e660f460f660e600200c2c60f000260e00022c60ec0026608a020466ebc008c1d8c1bcc1d8c1bc004c1d0004c1b0004c1c8004c1a8004c1c0004c1c0004c19c004c1b4004c19400458cc0d801c8cdd78029836183280098008009112999830801099baf374e0029801018000132533306200214a0266600800800260ca00460c8004444444444444646464646464646464646464646464646464646464646464646464646464a66610802a6661080266e2120000041533308401301f00313375e6e98004dd30148a5014a02a66610802050266110026ea0cdc099b8001e0040023308801375066e0407000ccc22004dd419b8001a0043308801375066e0406000ccc22004dd400b25eb804cc22004dd419b8101e0033308801375066e04cdc000e00200119844009ba83370203400666110026ea0cdc000c00219844009ba80164bd700b199982d199982d25eb7bdb18122100488100023007005002325333083013370e900000089919199998278028120119bad308601001375a610e020026466ec0c22804004c22804c22c04004dd61844808009840808100a400061020203e6666609801a01600204003ea66610002660b001000c266e04004cdc0010810080099982c0128038029bae308301001308301002375c61020200260f20126eb8c1fc004c1fc008dd7183e800983a8039bad307b001307b002375a60f200260f200460ee00260ee00460ea002a6660de0262660e6024660e6022660e66ea0024cc1ccdd4003a5eb804cc1cc044cc1cc048cc1ccdd4003998399ba80094bd701bad30730013073002375a60e200260e20046eb4c1bc004c1bc008dd6983680098368019bad306b00222222232323232337606ea0cdc0980819b81300f0013370466e0800c014cdc08028030009ba83370400466e04014018cdc119b8133704900200200280199b8248010cdc000380299b8233704900419b800060043370266e08018c030014cdc119b820070050043370466e0001400c010888888c8cdd81ba83370466e08014004018dd419b803370466e0800801001ccdc100280099b810010022222223370666e08cdc100300200119b823370400a006002444466ec0dd419b83337040040080026ea0cdc199b82002003001222222223232323370666e04c034cdc099b820010013370466e0920080023370466e08010024cdc099b8200700a3370400c01600266e0920040023370066e08cdc100300119b800070093370400a66e04cdc100400199b8200200a3370400800266e040040088cdc10008009299982a19b88001480005854ccc150cdc3800a4000290000a99982a19b870014800852002153330543370e00290020a400426464666002002006004444a6660b066e200040084ccc00c00c004cdc199b803370600a0020029002080119b803370600290022400409e09602a02400c06e08c03603229405280a5014a02940cc08c050058dd7182b00098270008b182a000982600b1bab3052001305200130510023758609e002609e004609a002609a002609800260960046eacc124004c124004c120008dd61823000982300098228011bac3043001303b025375a608200260820046eb0c0fc004c0fc008c0f4004c0d40a44c94ccc0e0cdc3a40040362646464646464646464646464a666088a66608866e1c0080044cdc4a400800229404c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc154c0ccc0d00284c8c94ccc15ccdc3a400060ac00226464a6660b266072010002264a6660b466660760020160a809c2666666666646464646444444444464646464646464646464646464646464a6660f660fc00426464a6660fa610002004264646464646464646464646464646464646464646464646464a66612602a66612602a66612602a6661260266e21200000413371090000010a50100114a0266ebc09c020528099299984a0099b87480480044c8c8c8c94ccc26004cdc424000002264646464646464646464646464646464646464646464646464646464646464a66616e0266e240800044c8c94ccc2e4040045288b299985c009984800803802899b873370000404e002264a6661720266e1c00c0084cdc38008140a50333091010344881004881003330900103300600416333305a001480001380854ccc2d404cc234040200184cdc080099b80026024100133308d01035007005375c6170020026170020046eb8c2d804004c2b804014dd7185a00800985a008011bae30b20100130aa010023253330ac013370e9000000880188029855008051929998558099b874800000440204018c2a404030c2b804004c2b804008c2b004004c2900402cc2a804004c2a804008c2a004004c28004020c29804004c29804004c27404018c28c04004c28c04004c26804010cc1b00cccdc0981e819a4004660d606490001983500219b81303b00448008cc1a400d200016375a6138020026138020046eb0c26804004c2480401c5281849008030b19983080580480c9bad309601001309601002375a612802002612802004612402002612402004612002002612002004611c02002611c02004611802002611802002610602660a20440086eacc22404004c22404004c20004024c21804004c21804008dd5984200800984200800983d800984080800984080800983c0018b183f0009982680b119191919299983e99983e99baf0030184a09444ccc1f4cdd78008042504a22940c20404004c1e4004c1fc004c1dc00458c1f0004cc12c0548c8c8c8c8c8c8c8c94ccc1fccdc3a4004002266e3cdd7184200983e8010070a50307d001308201001307a0013080010013078001307e001307e0013075001307a001307200130780013078002375c60ec00260ec00260ea00260e800260e600460e200260e200260e000260ce660740029000191919191929998381839801099191919191919299983a19b8730163304901323375e60f460e660f460e660f460f660e60020040142a6660e866e1cc058cc1240488cdd7983d1839983d1839800801005099199800800806806111299983d0010a99983d0008a5eb80584c8c94ccc1f000c52f5c02660fa646464646464646464646464a66610a0266ebc01c0084ccccccc16c038030098094004089288b19826810801984400800984000804984300800984300800984280801184180800983d800984080800984080800983c001983f001999802802800983f801983f001983e0010b0b183c0009838000983b0009837000983a000983a00098358008b183880099820005119baf0083071306a001300c0023300f00223303b0080013300e00223303a00800130010012253330620011480004cdc0240046600400460ca002600200244444a6660c6004200626464a6660ca006200a26464646666601401400200200800464646464646464646464646464646464646464646464646464646464646464646464a66610c0266ebc07c00c4c8c8c94ccc22404cdc42400005a26464a6661160266e2120000021533308b0132533308c013370e90000008a9998460099b873370266e000600c000804454ccc23004cdc399b8101600300f1533308c013370e66e000500c00344cdc399b8101200300b14a029405280a9998460099b87337020300060222a6661180266e1ccdc099b8001603000200f1533308c013370e66e0405000c0344cdc399b8001203000b14a0294052818450080308010b0b1929998458099b87480000044c8c8ccccc15c0c4090088dd69847008009bad308f0100132337606124020026124026126020026eb0c24404004c224040785200030890101d3333305400200102d02001e16375a6114020046eb4c22004004c94ccc21c04cdc3a4000002266ec0dd40099ba80111337606ea0044dd40099842808008b1845008009845008011844008009840008101bad308601001308601002375a6108020026108020046eb4c20804004c20804024dd69840008041bad307f001307f002375a60fa00260fa0046eb4c1ec004c1ec010dd6983c8019bac30780013078002375860ec00260ec00260ea00260e800460e400260e40046eb4c1c0004c1c0008dd698370009837001183600098360009835800983100218348021833801983380198328011119198008008019129998308008a5eb804cc188c00cdd6983180099801001183200082b02900a80900301f01080e80d8b1981500a00f0b1bae305d001305500116305b001305301c16375660b200260b200260b00046eb0c158004c158008c150004c150004c14c004c148008dd59828000982800098278011bac304d001304d001304c0023758609400260840582c6004008600200a600200244a66608a00229000099b8048008cc008008c120004dd6182200098220011bac30420013042002375a60800026080004607c002606c054264646464a66607866e1d200401f132323232323232323232323232323232323232323253330513370e9000182800089919191919191919191919299982e19b87333035003058054480084c8c8c8c8c8c8c94ccc18ccdc399981e00082e82ca40042a6660c66082608403826464a6660d060d60042646464646464a6660d666e1cc0c00792002132323232323232323232323232323232323232323232323232323232323232323232323232325333091010011325333092013370e900000089919299984a00a99984a0099baf03d02e100214a020022940c94ccc25004cdc3a400400229444c8c8cc88c8c94ccc26804008400452819b8933704004900119b8200348030cdc499b8200248010cdc1000a40046eb4c25c04004dd6984c008009919bb0309b01001309b01309c01001375861340200261240200a6124020086464a6661280200420022940cdc499b8200848028cdc100524141380266e24cdc1004a402866e0801d200213232533309401002100114a066ebc0580114ccc24804cdc380d804899b8701900714a06120020a82ca66612002a66612002a66612002a66612002a66612002a6661200266ebc08c0444cdd78108078a5013370e03e01a29404cdc380e8058a5013370e03601229404cdc780a8018a5013375e6e980dcdd30140a50309401001309401002375c6124020026124020046eb4c24004004c24004008dd69847008009847008011bad308c01001308c01002375a6114020026114020046eb4c22004004c22004008c21804004c21804008c21004004c1f004cc20804004c20804008dd71840008009840008011bad307e001307e002375a60f800260f80046eb4c1e8004c1e8008dd6983c000983c0011bad30760013076002307400130740023072001306a05f3303401c00116306f001306f002375660da00260da00460d600260c60022c60d20026607003e466ebcc1a4c188c1a4c1880040245858dd598338009833800982f00098320009832000982d8009981680d80e8b1830000982c0019bab305e001305e002305c0013054001305a001305a00130510013057001304f001163301800f034375660a800260a80046eacc148004c148004c144004c140004c13c004c138008dd59826000982600098258011bac3049001304900130480023758608c002607c0506eb4c110004c110008c108004c0e80b84c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc13ccdc3a4000609c002264646464646464646464646464a6660be60c40042646464646464a6660c466ebc03401454ccc188cdc3981380ba4004264a6660c666ebc15800454ccc18ccdc399981e00602f82da4004264646464646464a6660d466e1cccc10c00419018120021533306a3048304902513232323232323232323232323232323232323375e6e98094dd31999829999982980e80400319b8233702a6660f8660a801000c266e0400520809bee02100100c48004010008cdc119b8133305502500400200a48004ccc15009001c014dd7183f800983f8011bae307d001307500a375c60f600260f60046eb8c1e4004c1c4020dd6983b800983b8011bad307500130750013074002307200130720023070001306805d1616375660dc00260dc00260ca00260d600260d600260c40026606804804c2c2c6605602a0022c2c60cc00260cc0046eacc190004c190008c188004c16800458c180004cc0bc0608cdd79830182c9830182c800801182f000982b0019bab305c001305c002305a001305200130580013058001304f0013055001304d001163301600f032375660a400260a40046eacc140004c140004c13c004c138004c134004c130008dd59825000982500098248011bac30470013047001304600237586088002607804c6eb4c108004c0e80b88c008004c004004894ccc0f80045200013370090011980100118208009119805001119baf30403039001002223232002302900133013002001222223370666e08cdc100280200119b820030012222232323370666e08004018cdc019b820030070013370400200866e0400400888c8c8008c010004cc0400080048c94ccc0d4cdc3a400000226464646464646464646464646464a66608c6092004264646464649319299982419b87480000044c8c8c8c94ccc13cc1480084c926301a00316375a60a000260a0004609c002608c0142a66609066e1d2002001132323232533304f3052002132498c06800c58dd698280009828001182700098230050a99982419b87480100044c8c8c8c8c8c94ccc144c1500084c926301c00516375a60a400260a40046eb4c140004c140008c138004c11802854ccc120cdc3a400c002264646464a66609e60a400426493180d0018b1bad30500013050002304e001304600a153330483370e900400089919299982698280010a4c2c6eb4c138004c11802854ccc120cdc3a4014002264646464a66609e60a40042930b1bad30500013050002375a609c002608c0142a66609066e1d200c001132323232533304f3052002132498c06800c58dd698280009828001182700098230050a99982419b87480380044c8c8c8c8c8c8c8c8c8c94ccc154c1600084c926302000916375a60ac00260ac0046eb4c150004c150008dd6982900098290011bad30500013050002304e001304600a153330483370e900800089919191919192999828982a0010a4c2c6eb4c148004c148008dd6982800098280011bad304e001304600a153330483370e90090008991919192999827982900109924c6606e006464a66609c66e1d20000011323232325333055305800213232498c084008c10800c58c158004c158008c150004c13000858c13000458dd6982800098280011bac304e001304600a163046009303600a3253330463370e900000089919299982598270010a4c2c6eb8c130004c11003054ccc118cdc3a40040022a66609260880182930b0b1822005981800618178068b1bad30470013047002375a608a002608a0046086002608600460820026082004607e002607e004607a002607a004607600260660042c6066002464a66606866e1d200000115333037303200214985854ccc0d0cdc3a40040022a66606e60640042930b0b1819000911191919191919299981d19baf009005100114a064a66607466e1d20000011323232533303d3370e90000008b0a99981e99b87480080044cdc79bae3042303b00500213371e6e50dd99821181d802801181d8021bae3040001303800813375e004980103d87980003038007303d001303d001303c002303a001303200122323300100100322533303700114c0103d87a8000132325333036300500213374a90001981d00125eb804cc010010004c0ec008c0e4004c0040048894ccc0c4cdc4a40280022666006006606e606e606e606e606e606e606e606e606e606e00466e0400520141330040020013001001222533302f3371200290000981a0010999801801981a80119b810014800888c8cc00400400c894ccc0c800452f5c026464a666062600a00426606a00466008008002266008008002606c004606800244444446464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464a6660c6a6660c6066266e1c0840345288a999831a99983199baf025011153330633375e04601e2a6660c666e1c06c01c54ccc18ccdc380c8028a99983199b8f01700313375e02a00229405280a5014a029404c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc1e14ccc1e0cdd79ba6002374c0822a6660f066ebcdd30009ba603e13370e66e0400c010cdc081b0110a5014a0266e9520003307c03a3307c0383307c00c3307c3750060660f86ea00b8cc1f00a8cc1f0dd48161983e0219983e1ba73307c3750068660f86ea00c8cc1f0dd40041983e1ba80063307c375006c97ae03307c374e660f86ea0080cc1f0dd400f1983e1ba80073307c375000a660f86ea00892f5c097ae016333304e333304e333304e333304e333304e4bd6f7b630244100488100482026fb80812c129200201301100600f00d00404b00c002333304d333304d333304d333304d333304d4bd6f7b63024500488100482026fb808128125200201201000600e00c00404a00b00233304e03b04900a33304d03d04800933304c03900b00933304b03b00a008375a60e60046eb4c1c40054ccc1bccc11c02c0244cdd81ba83370200490404df7011ba83370200290404df701099bb037500046ea0004ccc11c0d0028020ccc1180d802401ccdd2a4000660e06ea4100cc1c0dd4800a5eb80c11ccc120c11ccc12001c014c11ccc12000c004dd7183780098378011bae306d0013065027375c60d600260d60046eb8c1a4004c1840945858c19c004c19c008dd7183280098328011bad30630013063002375a60c200260c20046eb4c17c004c17c008dd6982e800982e8011bad305b001305b002305900130590023057001304f002303f0013253330503370e90020008982a982700a09981501080a1827009982980098298011bae30510013051002375a609e002609e0046eb4c134004c134008dd6982580098258011bad30490013049002375a608e002608e004608a002608a0046086002607601e608200260820046eacc0fc004c0fc004c0d8034dd5981e000981e001181d0009819000981c000981c00098178039111299981699b870014800040104ccc888c8cc004004020894ccc0d40044cc0d8cdd81ba9008374c00a97adef6c6013232323253330363375e6600e018004980103d879800013303a337606ea4030dd30048028a99981b19b8f00c0021323253330383370e900000089981e19bb0375201c607a606c00400a200a606c00266601001800201226607466ec0dd48011ba6001330060060033756606e0066eb8c0d4008c0e4008c0dc004cc88c800cc8cc00400400c894ccc0d0004526132533303500114984c8c8c8c8c8c8c94ccc0e4cdc3a40000022660140146607a00c00a2c606e002660120040026eb8c0dc00cdd7181b001981d001981c001181b801181b8009981899bb037520046ea00052f5bded8c001044464a666062a66606800229445280a6103d87a800013374a90001981a9ba60014bd70191980080080191299981a80089981b19bb0375200e6ea00192f5bded8c0264646464a66606c66ebccc04402c00930103d879800013303a337606ea402cdd40050028a99981b19b8f00b0021323253330383370e900000089981e19bb0375201a607a606c00400a200a606c00264a66606e66e1c005200014c103d87a800013374a90001981d9ba80014bd7019b8000100a13303a337606ea4008dd4000998030030019bad3037003375c606a0046072004606e00201044a66605466e3c0092210013371e0029110014a044464664464a66605c66e1d200200110021375a60666058006605800464a66605866e1d200200114c103d87a8000132323300100100222533303200114c103d87a800013232323253330333371e014004266e95200033037375000297ae0133006006003375a60680066eb8c0c8008c0d8008c0d0004dd5981898150011815000a4000660180060044464a66605266e1d200030280011302e3027001163253330293370e90000008a60103d87a8000153330293370e900100089919198008008029129998178008a6103d87a800013232323253330303371e00e004266e952000330340014bd7009980300300198188019bae302f00230330023031001375c605c604e004266e9520003302d302e30270024bd70181380091299981399b9000200114c0103d8798000153330273371e0040022980103d87a800014c103d87b8000237260024466e280080048cdd79ba60014c101a0002323300100100222533302700114bd6f7b630099191919299981419b8f488100002100313302c337606ea4008dd3000998030030019bab3029003375c604e0046056004605200244646600200200644a66604e00229404c8c94ccc098cdc78010028a51133004004001302b002375c605200244446464646464646464a66605c606200426464646464646464646464a66606c66e1d200230350031323253330383370e9001181b80189919299981d19b8700e4800854ccc0e8cdc4800806899b8900d3370000603029405281bad303e001303600316375a607800260680062c6074002606400660700026060006606c002606c0046068002605801c6464666002002900024000444a66606266e1c00801040044c94ccc0c94ccc0c8cdc3801a4000266e1c005205a14a0266e08ccc010010cdc0001a400400490008a9998192999819299981919b8800148180528899b8800148180528899b88337009030240240022c266600800866e0000d20023370066e08009201433702002903019b8e006002371a0046eb4c0b8008dd718160008b1817800999119299981619b874800800440084dd59818981500198150011980580080425eb7bdb180dd59816800981680098120009815000981500098108021119198008008019129998128008a60103d87a800013232323253330263371e00e004266e9520003302a374c00297ae01330060060033756604e0066eb8c094008c0a4008c09c004c07003cc084004c06400458c07c004c07c008c074004c054020526136563253330173370e90000008991919191919299981018118010991924c6601200846eb4004c02401458dd6981080098108011bac301f001301f002301d0013015009153330173370e90010008991919191919191929998111812801099191924c6601800646eb4004cc02c0108dd680098058038b1bac302300130230023758604200260420046eb4c07c004c07c008c074004c05402454ccc05ccdc3a4008002264646464a66603c60420042649319299980e19b874800000454ccc07cc068010526161533301c3370e90010008a99980f980d0020a4c2c2c60340062c6eb4c07c004c07c008c074004c05402454ccc05ccdc3a400c00226464a666038603e0042930b1bad301d001301500916301500822323300100100322533301c00114984c8cc00c00cc080008c00cc0780048c94ccc058cdc3a4000002264646464a66603a6040004264649319299980e19b87480000044c8c94ccc084c0900084c92632533301f3370e9000000899192999812181380109924c601c0022c604a002603a0042a66603e66e1d200200113232323232325333028302b002149858dd6981480098148011bad30270013027002375a604a002603a0042c603a0022c604400260340062a66603866e1d20020011533301f301a00314985858c068008c01c00c58c078004c078008c070004c05000858c0500048c94ccc054cdc3a400000226464a666034603a0042930b1bae301b0013013002153330153370e900100089919299980d180e8010a4c2c6eb8c06c004c04c00858c04c004c0040148c94ccc04ccdc3a40000022646464646464646464646464646464646464a666050605600426464649319299981419b87480000044c8c94ccc0b4c0c00084c92632375a605a0046eb4c0ac00458c8cdd81817800981798180009bac302e0013026004153330283370e90010008a99981598130020a4c2c2c604c006602c020602a0222c605200260520046eb8c09c004c09c008dd6981280098128011bad30230013023002375a604200260420046eb4c07c004c07c008dd6980e800980e801180d800980d801180c80098088010b1808800919299980919b87480000044c8c8c8c94ccc064c07000852616375c603400260340046eb8c060004c04000858c040004dd68009bae001375c0026eb8004dd7000918029baa001230033754002ae6955ceaab9e5573eae815d0aba257461", + "hash": "c1a00854b5939641df12cdcc15daefe8423bac7faa0974f0b30711cf" } ], "definitions": { @@ -183,7 +202,7 @@ "$ref": "#/definitions/Int" } }, - "Option$ByteArray": { + "Option$Tuple$Int_Int": { "title": "Optional", "anyOf": [ { @@ -193,7 +212,7 @@ "index": 0, "fields": [ { - "$ref": "#/definitions/ByteArray" + "$ref": "#/definitions/Tuple$Int_Int" } ] }, @@ -229,26 +248,15 @@ } ] }, - "Option$amm_dex_v2/types/ProfitSharing": { - "title": "Optional", - "anyOf": [ + "Tuple$Int_Int": { + "title": "Tuple", + "dataType": "list", + "items": [ { - "title": "Some", - "description": "An optional value.", - "dataType": "constructor", - "index": 0, - "fields": [ - { - "$ref": "#/definitions/amm_dex_v2~1types~1ProfitSharing" - } - ] + "$ref": "#/definitions/Int" }, { - "title": "None", - "description": "Nothing.", - "dataType": "constructor", - "index": 1, - "fields": [] + "$ref": "#/definitions/Int" } ] }, @@ -455,20 +463,19 @@ } ] }, - "amm_dex_v2/types/OrderRedeemer": { - "title": "OrderRedeemer", + "amm_dex_v2/types/OrderBatchingRedeemer": { + "title": "OrderBatchingRedeemer", "anyOf": [ { - "title": "ApplyOrder", + "title": "OrderBatchingRedeemer", "dataType": "constructor", "index": 0, - "fields": [] - }, - { - "title": "CancelOrder", - "dataType": "constructor", - "index": 1, - "fields": [] + "fields": [ + { + "title": "pool_input_index", + "$ref": "#/definitions/Int" + } + ] } ] }, @@ -514,7 +521,7 @@ }, { "title": "profit_sharing_opt", - "$ref": "#/definitions/Option$amm_dex_v2~1types~1ProfitSharing" + "$ref": "#/definitions/Option$Tuple$Int_Int" } ] } @@ -566,10 +573,14 @@ ] }, { - "title": "UpdateFeeTo", + "title": "UpdatePoolFeeOrStakeCredential", "dataType": "constructor", "index": 2, "fields": [ + { + "title": "action", + "$ref": "#/definitions/amm_dex_v2~1types~1UpdatePoolFeeOrStakeCredentialAction" + }, { "title": "admin_index", "$ref": "#/definitions/Int" @@ -584,40 +595,25 @@ { "title": "admin_index", "$ref": "#/definitions/Int" - }, - { - "title": "fee_to_index", - "$ref": "#/definitions/Int" } ] } ] }, - "amm_dex_v2/types/ProfitSharing": { - "title": "ProfitSharing", + "amm_dex_v2/types/UpdatePoolFeeOrStakeCredentialAction": { + "title": "UpdatePoolFeeOrStakeCredentialAction", "anyOf": [ { - "title": "ProfitSharing", + "title": "UpdatePoolFee", "dataType": "constructor", "index": 0, - "fields": [ - { - "title": "fee_sharing_numerator", - "$ref": "#/definitions/Int" - }, - { - "title": "fee_sharing_denominator", - "$ref": "#/definitions/Int" - }, - { - "title": "fee_to", - "$ref": "#/definitions/aiken~1transaction~1credential~1Address" - }, - { - "title": "fee_to_datum_hash_opt", - "$ref": "#/definitions/Option$ByteArray" - } - ] + "fields": [] + }, + { + "title": "UpdatePoolStakeCredential", + "dataType": "constructor", + "index": 1, + "fields": [] } ] } diff --git a/validators/authen_minting_policy.ak b/validators/authen_minting_policy.ak index df5a969..8b7e212 100644 --- a/validators/authen_minting_policy.ak +++ b/validators/authen_minting_policy.ak @@ -35,9 +35,12 @@ validator( expect [(minted_pid, minted_an, minted_amount)] = value.flatten(mint_value) // Transaction must mint only 1 Factory Auth Asset - expect - minted_pid == authen_policy_id && minted_an == factory_auth_asset_name && minted_amount == 1 - let factory_outputs = + expect and { + minted_pid == authen_policy_id, + minted_an == factory_auth_asset_name, + minted_amount == 1, + } + expect [factory_output] = list.filter( outputs, fn(output) { @@ -49,17 +52,17 @@ validator( ) == 1 }, ) - expect [factory_output] = factory_outputs let Output { datum: factory_raw_datum, .. } = factory_output let FactoryDatum { head, tail } = utils.must_find_factory_datum(datums, factory_raw_datum) - expect - head == #"00" && tail == #"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00" - True + and { + head == #"00", + tail == #"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + } } CreatePool -> { let Transaction { inputs, mint, redeemers, .. } = transaction - let factory_inputs = + expect [factory_input] = list.filter( inputs, fn(input) { @@ -72,17 +75,7 @@ validator( ) == 1 }, ) - expect [factory_input] = factory_inputs let Input { output_reference: factory_input_ref, .. } = factory_input - let mint_value = value.from_minted_value(mint) - expect - value.quantity_of( - mint_value, - authen_policy_id, - factory_auth_asset_name, - ) == 1 - expect - value.quantity_of(mint_value, authen_policy_id, pool_auth_asset_name) == 1 let redeemer_list = dict.to_list(redeemers) expect [(_, raw_factory_redeemer)] = list.filter( @@ -114,10 +107,13 @@ validator( asset_b_policy_id, asset_b_asset_name, ) - expect - value.quantity_of(mint_value, authen_policy_id, lp_asset_name) == 9223372036854775807 - expect list.length(value.flatten(mint_value)) == 3 - True + let mint_value = value.from_minted_value(mint) + let expected_mint = + value.zero() + |> value.add(authen_policy_id, factory_auth_asset_name, 1) + |> value.add(authen_policy_id, pool_auth_asset_name, 1) + |> value.add(authen_policy_id, lp_asset_name, 9223372036854775807) + expected_mint == mint_value } MintLiquidity -> fail } diff --git a/validators/factory_validator.ak b/validators/factory_validator.ak index 2706f5a..84d7eb0 100644 --- a/validators/factory_validator.ak +++ b/validators/factory_validator.ak @@ -39,7 +39,7 @@ validator( asset_b_policy_id, asset_b_asset_name, ) - let factory_input_opt = + expect Some(factory_input) = list.find( inputs, fn(input) { @@ -47,7 +47,6 @@ validator( out_ref == factory_ref }, ) - expect Some(factory_input) = factory_input_opt let Input { output: factory_input_out, .. } = factory_input let Output { value: factory_input_value, address: factory_address, .. } = factory_input_out @@ -145,6 +144,15 @@ validator( // Remaining Liquidity must be MAX_LIQUIDITY - total_liquidity expect value.quantity_of(pool_output_value, authen_policy_id, lp_asset_name) == remaining_liquidity + let flatted_value = value.flatten(pool_output_value) + expect + if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { + expect [_, _, _, _] = flatted_value + True + } else { + expect [_, _, _, _, _] = flatted_value + True + } // Transaction must mint only 1 Factory Asset + 1 Pool Asset + MAX INT64 LP Asset expect validate_mint( diff --git a/validators/order_validator.ak b/validators/order_validator.ak index 264f923..6d29811 100644 --- a/validators/order_validator.ak +++ b/validators/order_validator.ak @@ -1,44 +1,25 @@ +use aiken/dict use aiken/list -use aiken/transaction.{Input, Output, ScriptContext, Spend, Transaction} +use aiken/transaction.{ + Input, Output, ScriptContext, Spend, Transaction, WithdrawFrom, +} use aiken/transaction/credential.{ - Address, ScriptCredential, VerificationKeyCredential, + Address, ScriptCredential, StakeCredential, VerificationKeyCredential, } use amm_dex_v2/types.{ - ApplyOrder, CancelOrder, OrderDatum, OrderRedeemer, ValidatorHash, + ApplyOrder, CancelOrder, OrderBatchingRedeemer, OrderDatum, OrderRedeemer, + ValidatorHash, } +use amm_dex_v2/utils -validator(pool_hash: ValidatorHash) { - fn validate_order( - raw_datum: Data, - redeemer: OrderRedeemer, - context: ScriptContext, - ) { - let ScriptContext { transaction, purpose } = context - expect Spend(_) = purpose +validator(stake_credential: StakeCredential) { + fn validate_order(raw_datum: Data, raw_redeemer: Data, context: ScriptContext) { + expect ScriptContext { transaction, purpose: Spend(_) } = context + expect redeemer: OrderRedeemer = raw_redeemer when redeemer is { ApplyOrder -> { - let Transaction { inputs, .. } = transaction - // TODO: Consider this line - let has_pool_input = - list.length( - list.filter( - inputs, - fn(i) { - let Input { - output: Output { - address: Address { payment_credential, .. }, - .. - }, - .. - } = i - when payment_credential is { - ScriptCredential(hash) -> pool_hash == hash - _ -> False - } - }, - ), - ) > 0 - has_pool_input + let Transaction { withdrawals, .. } = transaction + dict.has_key(withdrawals, stake_credential) } CancelOrder -> { let Transaction { extra_signatories, .. } = transaction @@ -51,3 +32,21 @@ validator(pool_hash: ValidatorHash) { } } } + +validator(pool_hash: ValidatorHash) { + fn validate_order_spending_in_batching( + redeemer: OrderBatchingRedeemer, + context: ScriptContext, + ) -> Bool { + expect ScriptContext { transaction, purpose: WithdrawFrom(_) } = context + let OrderBatchingRedeemer { pool_input_index } = redeemer + + let Transaction { inputs, .. } = transaction + let Input { + output: Output { address: Address { payment_credential, .. }, .. }, + .. + } = utils.list_at_index(inputs, pool_input_index) + expect ScriptCredential(hash) = payment_credential + pool_hash == hash + } +} diff --git a/validators/pool_validator.ak b/validators/pool_validator.ak index 7b398e6..51a80ff 100644 --- a/validators/pool_validator.ak +++ b/validators/pool_validator.ak @@ -1,16 +1,12 @@ use aiken/builtin use aiken/list -use aiken/transaction.{ - Input, Output, OutputReference, ScriptContext, Spend, Transaction, -} -use aiken/transaction/credential.{Address, ScriptCredential} +use aiken/transaction.{ScriptContext, Spend, Transaction} +use aiken/transaction/credential.{Address, VerificationKeyCredential} use aiken/transaction/value.{AssetName, PolicyId} -use amm_dex_v2/order_validation use amm_dex_v2/pool_validation use amm_dex_v2/types.{ - Asset, Batching, BatchingPool, DatumMap, MultiRouting, OrderDatum, PoolDatum, - PoolRedeemer, ProfitSharing, SwapMultiRouting, UpdateFeeTo, - WithdrawLiquidityShare, + Batching, MultiRouting, PoolDatum, PoolRedeemer, + UpdatePoolFeeOrStakeCredential, WithdrawLiquidityShare, } use amm_dex_v2/utils @@ -18,7 +14,7 @@ validator( authen_policy_id: PolicyId, license_policy_id: PolicyId, pool_auth_asset_name: AssetName, - _admin_asset_name: AssetName, + admin_asset_name: AssetName, maximum_deadline_range: Int, ) { fn validate_pool( @@ -26,11 +22,10 @@ validator( redeemer: PoolRedeemer, context: ScriptContext, ) { - let ScriptContext { transaction, purpose } = context - expect Spend(pool_input_ref) = purpose + expect ScriptContext { transaction, purpose: Spend(pool_input_ref) } = + context when redeemer is { Batching(batcher_address, input_indexes, license_index) -> { - expect license_index >= 0 && !builtin.null_list(input_indexes) let Transaction { inputs, outputs, @@ -40,28 +35,39 @@ validator( mint, .. } = transaction - expect - pool_validation.validate_common_batching( - inputs: inputs, + let Address { payment_credential: batcher_payment_credential, .. } = + batcher_address + expect VerificationKeyCredential(batcher_pkh) = + batcher_payment_credential + // Batching Redeemer provides @license_index which help save calculation cost + let license_input = utils.list_at_index(inputs, license_index) + and { + // Input indexes must not be empty list + !builtin.null_list(input_indexes), + // Input indexes must be unique + utils.is_list_unique(input_indexes), + // Batching transaction won't mint anything + value.is_zero(value.from_minted_value(mint)), + // Batcher with valid license token must be a signer of transaction + list.has(extra_signatories, batcher_pkh), + pool_validation.validate_batcher_license( + license_input: license_input, validity_range: validity_range, - extra_signatories: extra_signatories, - batcher_address: batcher_address, license_policy_id: license_policy_id, - license_index: license_index, maximum_deadline_range: maximum_deadline_range, - mint: mint, - ) - validate_batching( - authen_policy_id: authen_policy_id, - pool_auth_asset_name: pool_auth_asset_name, - all_inputs: inputs, - all_outputs: outputs, - all_datums: datums, - pool_input_ref: pool_input_ref, - pool_in_datum: datum, - batcher_address: batcher_address, - input_indexes: input_indexes, - ) + ), + pool_validation.validate_batching( + authen_policy_id: authen_policy_id, + pool_auth_asset_name: pool_auth_asset_name, + all_inputs: inputs, + all_outputs: outputs, + all_datums: datums, + pool_input_ref: pool_input_ref, + pool_in_datum: datum, + batcher_address: batcher_address, + input_indexes: input_indexes, + ), + } } MultiRouting( batcher_address, @@ -73,7 +79,7 @@ validator( let routing_out_indexes_len = list.length(routing_out_indexes) // TODO: Validate distinct indexes expect - license_index >= 0 && routing_in_indexes_len == routing_out_indexes_len && routing_out_indexes_len >= 2 + routing_in_indexes_len == routing_out_indexes_len && routing_out_indexes_len >= 2 let Transaction { inputs, outputs, @@ -83,18 +89,24 @@ validator( mint, .. } = transaction + // Doesn't mint anything + expect value.is_zero(value.from_minted_value(mint)) + let Address { payment_credential: batcher_payment_credential, .. } = + batcher_address + expect VerificationKeyCredential(batcher_pkh) = + batcher_payment_credential + // Verify Batcher with valid license token must be a signer of transaction + expect list.has(extra_signatories, batcher_pkh) + // Batching Redeemer provides @license_index which help save calculation cost + let license_input = utils.list_at_index(inputs, license_index) expect - pool_validation.validate_common_batching( - inputs: inputs, + pool_validation.validate_batcher_license( + license_input: license_input, validity_range: validity_range, - extra_signatories: extra_signatories, - batcher_address: batcher_address, license_policy_id: license_policy_id, - license_index: license_index, maximum_deadline_range: maximum_deadline_range, - mint: mint, ) - validate_swap_multi_routing( + pool_validation.validate_swap_multi_routing( authen_policy_id: authen_policy_id, pool_auth_asset_name: pool_auth_asset_name, all_inputs: inputs, @@ -106,464 +118,45 @@ validator( routing_out_indexes: routing_out_indexes, ) } - UpdateFeeTo(_admin_index) -> True - WithdrawLiquidityShare(_admin_index, _fee_to_index) -> True - } - } -} - -fn find_pool_input_and_output_in_batching( - pool_input_ref: OutputReference, - inputs: List, - outputs: List, -) -> (Input, Output) { - // TODO: validate pool size - let pool_inputs = - list.filter( - inputs, - fn(input) { - let Input { output_reference: out_ref, .. } = input - pool_input_ref == out_ref - }, - ) - expect [pool_input] = pool_inputs - let Input { output: Output { address: pool_in_address, .. }, .. } = pool_input - let Address { payment_credential: pool_payment_credential, .. } = - pool_in_address - // Batching transaction requires single Pool UTxO in both Inputs and Outputs - expect [pool_output] = - list.filter( - outputs, - fn(output) { - let Output { address: addr, .. } = output - let Address { payment_credential: payment_cred, .. } = addr - pool_payment_credential == payment_cred - }, - ) - - let Output { address: pool_out_address, .. } = pool_output - expect pool_out_address == pool_in_address - // Due to combination with Minswap Stake Address, Pool Contract can have multiple Base Addresses - // This logic will verify that the transaction is processing in single Pool - // Verify that having only one Pool Input and Pool Output in the transaction - expect [_] = - list.filter( - inputs, - fn(input) { - let Input { output: o, .. } = input - let Output { address: addr, .. } = o - let Address { payment_credential: payment_cred, .. } = addr - payment_cred == pool_payment_credential - }, - ) - (pool_input, pool_output) -} - -fn get_batching_pool( - pool_input: Input, - pool_output: Output, - authen_policy_id: PolicyId, - pool_auth_asset_name: AssetName, - pool_in_datum: PoolDatum, - datum_map: DatumMap, - require_total_liquidity_unchange: Bool, -) -> BatchingPool { - let Input { - output: Output { address: pool_in_address, value: pool_in_value, .. }, - .. - } = pool_input - let Output { value: pool_out_value, datum: pool_out_datum_raw, .. } = - pool_output - - let PoolDatum { - asset_a: pool_in_asset_a, - asset_b: pool_in_asset_b, - total_liquidity: pool_in_total_liquidity, - reserve_a: pool_in_reserve_a, - reserve_b: pool_in_reserve_b, - trading_fee_numerator: pool_in_trading_fee_numerator, - trading_fee_denominator: pool_in_trading_fee_denominator, - order_hash: pool_in_order_hash, - profit_sharing_opt: pool_in_profit_sharing_opt, - } = pool_in_datum - let pool_out_datum = utils.must_find_pool_datum(datum_map, pool_out_datum_raw) - let PoolDatum { - total_liquidity: pool_out_total_liquidity, - reserve_a: pool_out_reserve_a, - reserve_b: pool_out_reserve_b, - .. - } = pool_out_datum - - expect - utils.verify_pool_datum_in_batch( - datum1: pool_in_datum, - datum2: pool_out_datum, - require_total_liquidity_unchange: require_total_liquidity_unchange, - ) - let Asset { policy_id: asset_a_policy_id, asset_name: asset_a_asset_name } = - pool_in_asset_a - let Asset { policy_id: asset_b_policy_id, asset_name: asset_b_asset_name } = - pool_in_asset_b - 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 lp_asset = - Asset { policy_id: authen_policy_id, asset_name: lp_asset_name } - - // Each Pool UTxO has a Pool Authen Asset (authen_policy_id + pool_auth_asset_name) - // This asset must be only stay in Pool UTxO - // Verify Pool Authen Asset must be existed in Pool Input and Output value - expect - value.quantity_of(pool_in_value, authen_policy_id, pool_auth_asset_name) == 1 - expect - value.quantity_of(pool_out_value, authen_policy_id, pool_auth_asset_name) == 1 - let temp_reserve_a_in = - value.quantity_of(pool_in_value, asset_a_policy_id, asset_a_asset_name) - let temp_reserve_a_out = - value.quantity_of(pool_out_value, asset_a_policy_id, asset_a_asset_name) - let (reserve_a_in, reserve_a_out) = - if utils.is_ada_asset(asset_a_policy_id, asset_a_asset_name) { - (temp_reserve_a_in - 3000000, temp_reserve_a_out - 3000000) - } else { - (temp_reserve_a_in, temp_reserve_a_out) - } - let reserve_b_in = - value.quantity_of(pool_in_value, asset_b_policy_id, asset_b_asset_name) - let reserve_b_out = - value.quantity_of(pool_out_value, asset_b_policy_id, asset_b_asset_name) - - let remaining_liquidity_supply_in = - value.quantity_of(pool_in_value, authen_policy_id, lp_asset_name) - let remaining_liquidity_supply_out = - value.quantity_of(pool_out_value, authen_policy_id, lp_asset_name) - expect - remaining_liquidity_supply_out - remaining_liquidity_supply_in == pool_in_total_liquidity - pool_out_total_liquidity - let profit_sharing_opt: Option<(Int, Int)> = - when pool_in_profit_sharing_opt is { - Some(profit_sharing) -> { - let ProfitSharing { fee_sharing_numerator, fee_sharing_denominator, .. } = - profit_sharing - Some((fee_sharing_numerator, fee_sharing_denominator)) + UpdatePoolFeeOrStakeCredential(action, admin_index) -> { + let Transaction { inputs, outputs, datums, redeemers, mint, .. } = + transaction + expect Some(pool_input) = transaction.find_input(inputs, pool_input_ref) + pool_validation.validate_update_pool_datum_or_stake_credential( + action: action, + authen_policy_id: authen_policy_id, + pool_auth_asset_name: pool_auth_asset_name, + license_policy_id: license_policy_id, + admin_asset_name: admin_asset_name, + admin_index: admin_index, + pool_input: pool_input, + pool_in_datum: datum, + all_inputs: inputs, + all_outputs: outputs, + all_datums: datums, + all_mints: mint, + all_redeemers: redeemers, + ) + } + WithdrawLiquidityShare(admin_index) -> { + let Transaction { inputs, outputs, datums, redeemers, mint, .. } = + transaction + expect Some(pool_input) = transaction.find_input(inputs, pool_input_ref) + pool_validation.validate_withdraw_liquidity_share( + authen_policy_id: authen_policy_id, + pool_auth_asset_name: pool_auth_asset_name, + license_policy_id: license_policy_id, + admin_asset_name: admin_asset_name, + admin_index: admin_index, + pool_input: pool_input, + pool_in_datum: datum, + all_inputs: inputs, + all_outputs: outputs, + all_datums: datums, + all_mints: mint, + all_redeemers: redeemers, + ) } - None -> None - } - BatchingPool { - asset_a: pool_in_asset_a, - asset_b: pool_in_asset_b, - lp_asset, - trading_fee_numerator: pool_in_trading_fee_numerator, - trading_fee_denominator: pool_in_trading_fee_denominator, - profit_sharing: profit_sharing_opt, - order_hash: pool_in_order_hash, - address: pool_in_address, - pool_state_in: ( - pool_in_reserve_a, - pool_in_reserve_b, - reserve_a_in, - reserve_b_in, - pool_in_total_liquidity, - ), - pool_state_out: ( - pool_out_reserve_a, - pool_out_reserve_b, - reserve_a_out, - reserve_b_out, - pool_out_total_liquidity, - ), - } -} - -fn validate_batching( - authen_policy_id: PolicyId, - pool_auth_asset_name: AssetName, - all_inputs: List, - all_outputs: List, - all_datums: DatumMap, - pool_input_ref: OutputReference, - pool_in_datum: PoolDatum, - batcher_address: Address, - input_indexes: List, -) -> Bool { - let (pool_input, pool_output) = - find_pool_input_and_output_in_batching( - pool_input_ref: pool_input_ref, - inputs: all_inputs, - outputs: all_outputs, - ) - let BatchingPool { - asset_a, - asset_b, - lp_asset, - trading_fee_numerator, - trading_fee_denominator, - profit_sharing, - order_hash, - address: pool_address, - pool_state_in, - pool_state_out, - } = - get_batching_pool( - pool_input: pool_input, - pool_output: pool_output, - authen_policy_id: authen_policy_id, - pool_auth_asset_name: pool_auth_asset_name, - pool_in_datum: pool_in_datum, - datum_map: all_datums, - require_total_liquidity_unchange: False, - ) - let user_inputs = - list.filter( - all_inputs, - fn(input) { - let Input { output: out, .. } = input - let Output { address: addr, .. } = out - let Address { payment_credential: payment_cred, .. } = addr - when payment_cred is { - ScriptCredential(hash) -> hash == order_hash - _ -> False - } - }, - ) - let unique_input_indexes = list.unique(input_indexes) - // Currently, transaction inputs will be sorted by TxId and TxIndex of UTxO. - // We have to calculate indexes of orders inputs sorting by the ASC created time - // on the off-chain and on-chain will sort the TxIns by the indexes - // Input Indexes in parameter will be reversed indexs of @order_inputs to reduce calculate step in On-Chain - let sorted_user_inputs = - list.foldl( - unique_input_indexes, - [], - fn(idx, ips) { - list.push(ips, utils.must_parse_option(list.at(user_inputs, idx))) - }, - ) - - // Order Outputs are Outputs which aren't Pool & Batcher UTxOs - // Their ordering must be the same with @sorted_order_inputs, so @order_outputs at index i is the output if @sorted_order_inputs at index i - let user_outputs = - list.filter( - all_outputs, - fn(output) { - let Output { address: addr, .. } = output - addr != pool_address && addr != batcher_address - }, - ) - - expect - unique_input_indexes == input_indexes && // @input_indexes which is provided by Batcher must be unique array - list.length(user_inputs) == list.length(unique_input_indexes) && // @input_indexes and @user_inputs must have the same length - // User Inputs and Outputs must have the same length - list.length(sorted_user_inputs) == list.length(user_outputs) - expect list.length(sorted_user_inputs) == list.length(user_outputs) - pool_state_out == order_validation.apply_orders( - datum_map: all_datums, - asset_a: asset_a, - asset_b: asset_b, - lp_asset: lp_asset, - trading_fee_numerator: trading_fee_numerator, - trading_fee_denominator: trading_fee_denominator, - order_hash: order_hash, - profit_sharing_opt: profit_sharing, - order_inputs: sorted_user_inputs, - order_outputs: user_outputs, - pool_state: pool_state_in, - ) -} - -fn validate_swap_multi_routing( - authen_policy_id: PolicyId, - pool_auth_asset_name: AssetName, - all_inputs: List, - all_outputs: List, - all_datums: DatumMap, - pool_input_ref: OutputReference, - batcher_address: Address, - routing_in_indexes: List, - routing_out_indexes: List, -) -> Bool { - // 1. Find Pool Payment Credential - // 2. Find all Pool Inputs and expect only 2 pool Inputs - // 3. Find All Pool Outputs and expect only 2 Pool Outputs - // 4. Find SwapMultiRouting Order and expect only 1 Order - // 5. Verify that LP Asset of 2 Pools in order is matched - // 6. Detect Pool 1 & Pool 2 of these pools above and apply Order via them - let batching_pools = - find_multi_routing_pools( - authen_policy_id: authen_policy_id, - pool_auth_asset_name: pool_auth_asset_name, - pool_input_ref: pool_input_ref, - inputs: all_inputs, - outputs: all_outputs, - routing_in_indexes: routing_in_indexes, - routing_out_indexes: routing_out_indexes, - datum_map: all_datums, - ) - - let BatchingPool { - order_hash, - address: Address { payment_credential: pool_payment_credential, .. }, - lp_asset: pool_lp_asset, - .. - } = utils.list_at_index(batching_pools, 0) - - let order_inputs = - list.filter( - all_inputs, - fn(input) { - let Input { - output: Output { - address: Address { payment_credential: payment_cred, .. }, - .. - }, - .. - } = input - when payment_cred is { - ScriptCredential(hash) -> hash == order_hash - _ -> False - } - }, - ) - expect [order_input] = order_inputs - let order_outputs = - list.filter( - all_outputs, - fn(output) { - let Output { address: addr, .. } = output - let Address { payment_credential: payment_cred, .. } = addr - addr != batcher_address && payment_cred != pool_payment_credential - }, - ) - expect [order_output] = order_outputs - let Input { - output: Output { value: order_in_value, datum: raw_order_in_datum, .. }, - .. - } = order_input - let Output { value: order_out_value, .. } = order_output - let OrderDatum { - receiver, - receiver_datum_hash_opt, - step: order_step, - batcher_fee, - output_ada, - lp_asset: order_lp_asset, - .. - } = utils.must_find_order_datum(all_datums, raw_order_in_datum) - let is_valid_receiver = - order_validation.validate_order_receiver( - receiver: receiver, - receiver_datum_hash_opt: receiver_datum_hash_opt, - output: order_output, - ) - expect - batcher_fee > 0 && output_ada > 0 && is_valid_receiver && pool_lp_asset == order_lp_asset - when order_step is { - SwapMultiRouting(routings, minimum_receive) -> { - expect minimum_receive > 0 - order_validation.validate_swap_multi_routing_order( - pools: batching_pools, - routings: routings, - order_in_value: order_in_value, - order_out_value: order_out_value, - minimum_receive: minimum_receive, - batcher_fee: batcher_fee, - output_ada: output_ada, - ) } - _ -> False } } - -// TODO: We need carefully verify these logic below -fn find_multi_routing_pools( - authen_policy_id: PolicyId, - pool_auth_asset_name: AssetName, - pool_input_ref: OutputReference, - inputs: List, - outputs: List, - routing_in_indexes: List, - routing_out_indexes: List, - datum_map: DatumMap, -) -> List { - let pool_ins = - list.map(routing_in_indexes, fn(idx) { utils.list_at_index(inputs, idx) }) - - let pool_outs = - list.map(routing_out_indexes, fn(idx) { utils.list_at_index(outputs, idx) }) - - let pool_len = list.length(pool_ins) - - // find input of current script - expect [own_pool_input] = - list.filter( - inputs, - fn(input) { - let Input { output_reference: out_ref, .. } = input - pool_input_ref == out_ref - }, - ) - let Input { - output: Output { - address: Address { payment_credential: own_pool_payment_credential, .. }, - .. - }, - .. - } = own_pool_input - // verify that there's only 2 pools the inputs - expect - list.length( - list.filter( - inputs, - fn(input) { - let Input { - output: Output { address: Address { payment_credential, .. }, .. }, - .. - } = input - payment_credential == own_pool_payment_credential - }, - ), - ) == pool_len - // verify that there's only 2 pools the outputs - expect - list.length( - list.filter( - outputs, - fn(output) { - let Output { address: Address { payment_credential, .. }, .. } = - output - payment_credential == own_pool_payment_credential - }, - ), - ) == pool_len - utils.zip_with( - pool_ins, - pool_outs, - fn(pool_in, pool_out) { - let Input { - output: Output { - address: pool_in_address, - datum: pool_in_datum_raw, - .. - }, - .. - } = pool_in - let Output { address: pool_out_address, .. } = pool_out - let pool_in_datum = - utils.must_find_pool_datum(datum_map, pool_in_datum_raw) - - expect pool_in_address == pool_out_address - get_batching_pool( - pool_input: pool_in, - pool_output: pool_out, - authen_policy_id: authen_policy_id, - pool_auth_asset_name: pool_auth_asset_name, - pool_in_datum: pool_in_datum, - datum_map: datum_map, - require_total_liquidity_unchange: True, - ) - }, - ) -}