Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change the Address type in GlobalSetting to PoolAuthorizationMethod and do not allow Factory & Global Setting contains other assets #56

Merged
merged 4 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion amm-v2-docs/amm-v2-specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ Pool Batching validator is the sub logic of `Pool Validator`, it will ensure Bat
#### 3.3.6.2 Redeemer

- _batcher_index_: Index of the the batcher in authorized batchers list.
- _license_index_: Index of the UTxO holding Batcher License Token in the Transaction Inputs.
- _orders_fee_: Batcher fee will be deducted from orders' fund. Batcher can decide the amount of fee for each order. The Batcher Fee can not exceed the maximum batcher fee.
- _input_indexes_: The Indexes of Orders are processing (it will be explained below)
- _pool_input_indexes_opt_: The Indexes of Pools are processing (it will be explained below)
Expand Down
Binary file modified amm-v2-docs/pics/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 15 additions & 46 deletions lib/amm_dex_v2/pool_validation.ak
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use aiken/dict.{Dict}
use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction.{Input, Output}
use aiken/transaction/credential.{
Address, Inline, PaymentCredential, ScriptCredential, StakeCredential,
VerificationKey, VerificationKeyCredential,
Address, PaymentCredential, ScriptCredential, StakeCredential,
VerificationKeyCredential,
}
use aiken/transaction/value.{
AssetName, PolicyId, Value, ada_asset_name, ada_policy_id,
}
use amm_dex_v2/types.{
Asset, BatchingPool, GlobalSetting, PoolDatum, UpdateDynamicFee, UpdatePoolFee,
Asset, BatchingPool, GlobalSetting, PAMSpendScript, PAMWithdrawScript,
PoolAuthorizationMethod, PoolDatum, UpdateDynamicFee, UpdatePoolFee,
UpdatePoolParametersAction, UpdatePoolStakeCredential,
}
use amm_dex_v2/utils
Expand Down Expand Up @@ -56,42 +55,6 @@ pub fn get_and_validate_global_setting(
global_setting_datum
}

pub fn authorize_license_holder(
license_address: Address,
transaction_inputs: List<Input>,
withdrawals: Dict<StakeCredential, Int>,
extra_signatories: List<Hash<Blake2b_224, VerificationKey>>,
) -> Bool {
let Address { payment_credential: license_holder_payment_cred, .. } =
license_address
when license_holder_payment_cred is {
//If the Authorizer is controlled by a PubKey wallet,
// the owners of the UTxO must sign the transaction with their private key. Otherwise, such action must be prohibited.
VerificationKeyCredential(pub_key_hash) ->
list.has(extra_signatories, pub_key_hash)
// If the Authorizer is controlled by a Script wallet,
// the smart contract must incorporate a Withdrawal or a Spend function and execute sufficient conditions to facilitate its operation
ScriptCredential(script_hash) -> {
let authorized_via_withdrawal =
dict.has_key(withdrawals, Inline(ScriptCredential(script_hash)))
if authorized_via_withdrawal {
True
} else {
list.any(
transaction_inputs,
fn(input) {
let Input { output, .. } = input
let Output { address: out_address, .. } = output
let Address { payment_credential: out_payment_credential, .. } =
out_address
out_payment_credential == license_holder_payment_cred
},
)
}
}
}
}

pub fn get_batching_pool(
stake_credential: StakeCredential,
pool_input: Output,
Expand Down Expand Up @@ -199,18 +162,18 @@ pub fn get_batching_pool(
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, utils.min_pool_ada)
|> value.add(authen_policy_id, utils.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)
|> value.add(ada_policy_id, ada_asset_name, utils.min_pool_ada)
let estimate_pool_out_value =
value.zero()
|> value.add(ada_policy_id, ada_asset_name, utils.min_pool_ada)
|> value.add(authen_policy_id, utils.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)
|> value.add(ada_policy_id, ada_asset_name, utils.min_pool_ada)
expect and {
estimate_pool_in_value == pool_in_value,
estimate_pool_out_value == pool_out_value,
Expand Down Expand Up @@ -573,7 +536,7 @@ test test_get_trading_fee_numerator_fail_3() fail {
pub fn has_only_pool_and_author(
inputs: List<Input>,
pool_address: Address,
author_address: Address,
pool_author: PoolAuthorizationMethod,
) -> Bool {
let Address { payment_credential: pool_payment_cred, .. } = pool_address
// Having single pool input
Expand All @@ -598,8 +561,14 @@ pub fn has_only_pool_and_author(
let Input { output: Output { address: addr, .. }, .. } = input
let Address { payment_credential: payment_cred, .. } = addr
when payment_cred is {
ScriptCredential(_) ->
payment_cred == pool_payment_cred || addr == author_address
ScriptCredential(sh) -> or {
payment_cred == pool_payment_cred,
when pool_author is {
PAMSpendScript(author_hash) -> sh == author_hash
PAMWithdrawScript(author_hash) -> sh == author_hash
_ -> False
},
}
VerificationKeyCredential(_) -> True
}
},
Expand Down
43 changes: 37 additions & 6 deletions lib/amm_dex_v2/types.ak
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,26 @@ pub type DatumMap =
Dict<Hash<Blake2b_256, Data>, Data>

pub type PoolDatum {
// Stake Credential of `Pool Batching` validator
pool_batching_stake_credential: StakeCredential,
// The Pool's Asset A
asset_a: Asset,
// The Pool's Asset B
asset_b: Asset,
// Total Share of Liquidity Providers
total_liquidity: Int,
// Asset A's balance of Liquidity Providers
reserve_a: Int,
// Asset B's balance of Liquidity Providers
reserve_b: Int,
// Numerator of Trading Fee on Asset A side
base_fee_a_numerator: Int,
// Numerator of Trading Fee on Asset B side
base_fee_b_numerator: Int,
// (Optional) Numerator of Fee Sharing percentage.
// This is the percentage of Trading Fee. (eg, Trading Fee is 3%, Profit Sharing is 1/6 -> Profit Sharing = 1/6 * 3%)
fee_sharing_numerator_opt: Option<Int>,
// Allow Batcher can decide volatility fee for each batch transaction
allow_dynamic_fee: Bool,
}

Expand All @@ -51,10 +62,15 @@ pub type PoolRedeemer {
}

pub type PoolBatchingRedeemer {
// Index of the the batcher in authorized batchers list
batcher_index: Int,
// Batcher fee will be deducted from orders' fund. Batcher can decide the amount of fee for each order. The Batcher Fee can not exceed the maximum batcher fee.
orders_fee: List<Int>,
// The Indexes of Orders are processing
input_indexes: ByteArray,
// The Indexes of Pools are processing
pool_input_indexes_opt: Option<ByteArray>,
// The Volatility Fee for each Pool. Batcher can charge more fee to each batch through off-chain calculation and it's only affected if Pool enables `allow_dynamic_fee`
vol_fees: List<Option<Int>>,
}

Expand Down Expand Up @@ -223,10 +239,15 @@ pub type ExtraOrderDatum {
EODInlineDatum { hash: CustomDatumHash }
}

// Authorization methods for an Order owner to unlock funds from the Order's Utxo
pub type OrderAuthorizationMethod {
// Requires a signature from a PubKey wallet.
OAMSignature { pub_key_hash: PubKeyHash }
// For a Script-based wallet with a Spend method, the associated Utxo must be present in the transaction inputs.
OAMSpendScript { script_hash: ValidatorHash }
// For a Script-based wallet with a Withdrawal method, a corresponding withdrawal must be present in the transaction.
OAMWithdrawScript { script_hash: ValidatorHash }
// For a Script-based wallet with a Minting method, it must mint tokens with a defined policy ID, included in the transaction minting, with a non-zero quantity.
OAMMintScript { script_hash: ValidatorHash }
}

Expand Down Expand Up @@ -275,6 +296,16 @@ pub type AuthenRedeemer {
CreatePool
}

// Defines authorization methods for interacting with Liquidity Pool settings and operations.
pub type PoolAuthorizationMethod {
// Requires a signature from a PubKey wallet.
PAMSignature { pub_key_hash: PubKeyHash }
// For a Script-based wallet with a Spend method, the associated Utxo must be present in the transaction inputs.
PAMSpendScript { script_hash: ValidatorHash }
// For a Script-based wallet with a Withdrawal method, a corresponding withdrawal must be present in the transaction.
PAMWithdrawScript { script_hash: ValidatorHash }
}

pub type BatchingPool {
asset_a: Asset,
asset_b: Asset,
Expand All @@ -297,16 +328,16 @@ pub type PoolState =
// This setting grants permissions to authorized actors who can interact with Liquidity Pool features.
pub type GlobalSetting {
// List of authorized batchers who can process orders.
batchers: List<Address>,
batchers: List<PoolAuthorizationMethod>,
// The actor who can update the Pool's base fee and fee sharing.
pool_fee_updater: Address,
pool_fee_updater: PoolAuthorizationMethod,
// The actor who can withdraw the Pool's fee sharing.
fee_sharing_taker: Address,
fee_sharing_taker: PoolAuthorizationMethod,
// The actor who can change the Pool's stake key.
pool_stake_key_updater: Address,
pool_stake_key_updater: PoolAuthorizationMethod,
// The actor who can update the Pool's dynamic fee.
pool_dynamic_fee_updater: Address,
pool_dynamic_fee_updater: PoolAuthorizationMethod,
// The actor who can update the addresses mentioned above.
// This admin can be transferred to another wallet and should be stored in the most secure location.
admin: Address,
admin: PoolAuthorizationMethod,
}
99 changes: 94 additions & 5 deletions lib/amm_dex_v2/utils.ak
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
use aiken/builtin
use aiken/bytearray
use aiken/dict
use aiken/hash
use aiken/dict.{Dict}
use aiken/hash.{Blake2b_224, Hash}
use aiken/interval.{Finite, Interval, IntervalBound}
use aiken/list
use aiken/transaction.{Datum, DatumHash, InlineDatum, NoDatum, ValidityRange}
use aiken/transaction.{
Datum, DatumHash, InlineDatum, Input, NoDatum, Output, ValidityRange,
}
use aiken/transaction/credential.{
Address, Inline, ScriptCredential, StakeCredential, VerificationKey,
}
use aiken/transaction/value.{
AssetName, PolicyId, Value, ada_asset_name, ada_policy_id,
AssetName, MintedValue, PolicyId, Value, ada_asset_name, ada_policy_id,
}
use amm_dex_v2/types.{
Asset, DatumMap, OAMMintScript, OAMSignature, OAMSpendScript,
OAMWithdrawScript, OrderAuthorizationMethod, PAMSignature, PAMSpendScript,
PAMWithdrawScript, PoolAuthorizationMethod, SortedValueList,
}
use amm_dex_v2/types.{Asset, DatumMap, SortedValueList}

// the legitimate Pool TokenName
pub const pool_auth_asset_name = #"4d5350"
Expand Down Expand Up @@ -504,3 +513,83 @@ pub fn value_to_list(val: Value) -> SortedValueList {
},
)
}

// Pool Author can be one of 3 authorization methods
pub fn authorize_pool_license(
author: PoolAuthorizationMethod,
transaction_inputs: List<Input>,
withdrawals: Dict<StakeCredential, Int>,
extra_signatories: List<Hash<Blake2b_224, VerificationKey>>,
) -> Bool {
when author is {
// If the authorization method is PubKey, validate using the provided signature.
PAMSignature(pub_key_hash) -> list.has(extra_signatories, pub_key_hash)
// If the authorization method is SpendScript, ensure the Utxo is present in the transaction inputs.
PAMSpendScript(script_hash) -> {
let auth_cred = ScriptCredential(script_hash)
!builtin.null_list(
h2physics marked this conversation as resolved.
Show resolved Hide resolved
list.filter(
transaction_inputs,
fn(input) {
let Input { output, .. } = input
let Output { address: out_address, .. } = output
let Address { payment_credential: out_payment_credential, .. } =
out_address
out_payment_credential == auth_cred
},
),
)
}
// If the authorization method is WithdrawScript, validate the presence of a withdrawal in the transaction.
PAMWithdrawScript(script_hash) -> {
let credential = Inline(ScriptCredential(script_hash))
dict.has_key(withdrawals, credential)
}
}
}

// Validates an order based on the specified authorization method.
pub fn authorize_order_license(
author: OrderAuthorizationMethod,
transaction_inputs: List<Input>,
withdrawals: Dict<StakeCredential, Int>,
extra_signatories: List<Hash<Blake2b_224, VerificationKey>>,
transaction_mint: MintedValue,
) -> Bool {
when author is {
// If the authorization method is PubKey, validate using the provided signature.
OAMSignature(pub_key_hash) -> list.has(extra_signatories, pub_key_hash)
// If the authorization method is SpendScript, ensure the Utxo is present in the transaction inputs.
OAMSpendScript(script_hash) -> {
let auth_cred = ScriptCredential(script_hash)
!builtin.null_list(
h2physics marked this conversation as resolved.
Show resolved Hide resolved
list.filter(
transaction_inputs,
fn(input) {
let Input { output, .. } = input
let Output { address: out_address, .. } = output
let Address { payment_credential: out_payment_credential, .. } =
out_address
out_payment_credential == auth_cred
},
),
)
}
// If the authorization method is WithdrawScript, validate the presence of a withdrawal in the transaction.
OAMWithdrawScript(script_hash) -> {
let credential = Inline(ScriptCredential(script_hash))
dict.has_key(withdrawals, credential)
}
// If the authorization method is MintScript, ensure the minted tokens meet the policy ID requirements and have a non-zero quantity.
OAMMintScript(script_hash) -> {
let fst_token_quantity =
transaction_mint
|> value.from_minted_value()
|> value.tokens(script_hash)
|> dict.to_list()
|> builtin.head_list
|> builtin.snd_pair
fst_token_quantity != 0
}
}
}
Loading
Loading