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

Refactor to use continuations #78

Merged
merged 6 commits into from
Apr 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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

- uses: aiken-lang/[email protected]
with:
version: v1.0.24-alpha
version: v1.0.26-alpha

- run: |
# Run the tests
Expand Down
62 changes: 33 additions & 29 deletions lib/calculation/deposit.ak
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,25 @@ use types/order.{Destination, Fixed, Self, OrderDatum}
/// LP tokens are distributed to the destination.
///
pub fn do_deposit(
pool_state: PoolState,
pool_quantity_a: SingletonValue,
pool_quantity_b: SingletonValue,
pool_quantity_lp: SingletonValue,
input_utxo: Output,
assets: (SingletonValue, SingletonValue),
destination: Destination,
actual_protocol_fee: Int,
output: Output,
) -> PoolState {
continuation: fn(SingletonValue, SingletonValue, SingletonValue) -> Bool,
) -> Bool {
let (asset_a, asset_b) = assets
let Output { value: input_value, .. } = input_utxo

// Policy ID and token name of the assets must match the pool, otherwise someone
// could load the pool with junk tokens and freeze the pool.
expect asset_a.1st == pool_state.quantity_a.1st
expect asset_a.2nd == pool_state.quantity_a.2nd
expect asset_b.1st == pool_state.quantity_b.1st
expect asset_b.2nd == pool_state.quantity_b.2nd
expect asset_a.1st == pool_quantity_a.1st
expect asset_a.2nd == pool_quantity_a.2nd
expect asset_b.1st == pool_quantity_b.1st
expect asset_b.2nd == pool_quantity_b.2nd

// A deposit is permitted to have lower funds than the datum claims, so that
// we can compose it as a step in a chain where the exact amount will be unknown.
Expand Down Expand Up @@ -65,17 +68,17 @@ pub fn do_deposit(
// So some small amount of a or b might be returned to the user
// So, calculate how much "b" do we have, in units of asset A, so we can check which is greater
let b_in_units_of_a =
user_gives_b * pool_state.quantity_a.3rd / pool_state.quantity_b.3rd
user_gives_b * pool_quantity_a.3rd / pool_quantity_b.3rd

// Amount that user actually deposits in the pool, after giving back change.
let (deposited_a, deposited_b) =
// If we have more b than a, then we can only take up to an quivalent amount of b, and return the rest
// otherwise if we have more a than b, we can return some amount of `a` to the user instead
if b_in_units_of_a > user_gives_a {
let change =
pool_state.quantity_b.3rd
pool_quantity_b.3rd
* (b_in_units_of_a - user_gives_a) // the "surplus" value in units of a
/ pool_state.quantity_a.3rd
/ pool_quantity_a.3rd
(user_gives_a, user_gives_b - change)
} else {
let change = user_gives_a - b_in_units_of_a
Expand All @@ -91,7 +94,7 @@ pub fn do_deposit(
//
// Solving for `issued_lp_tokens` gives:
let issued_lp_tokens =
deposited_a * pool_state.quantity_lp.3rd / pool_state.quantity_a.3rd
deposited_a * pool_quantity_lp.3rd / pool_quantity_a.3rd

// Calculate what assets we expect on at the destination;
// i.e. it should be whatever assets were on the inputs, minus the amount that was deposited, minus the fee,
Expand All @@ -102,8 +105,8 @@ pub fn do_deposit(
|> value.add(asset_b.1st, asset_b.2nd, -deposited_b)
|> value.add(ada_policy_id, ada_asset_name, -actual_protocol_fee)
|> value.add(
pool_state.quantity_lp.1st,
pool_state.quantity_lp.2nd,
pool_quantity_lp.1st,
pool_quantity_lp.2nd,
issued_lp_tokens,
)

Expand All @@ -128,23 +131,23 @@ pub fn do_deposit(
}

// And construct the final pool state
PoolState {
quantity_a: (
pool_state.quantity_a.1st,
pool_state.quantity_a.2nd,
pool_state.quantity_a.3rd + deposited_a,
continuation(
(
pool_quantity_a.1st,
pool_quantity_a.2nd,
pool_quantity_a.3rd + deposited_a,
),
quantity_b: (
pool_state.quantity_b.1st,
pool_state.quantity_b.2nd,
pool_state.quantity_b.3rd + deposited_b,
(
pool_quantity_b.1st,
pool_quantity_b.2nd,
pool_quantity_b.3rd + deposited_b,
),
quantity_lp: (
pool_state.quantity_lp.1st,
pool_state.quantity_lp.2nd,
pool_state.quantity_lp.3rd + issued_lp_tokens,
(
pool_quantity_lp.1st,
pool_quantity_lp.2nd,
pool_quantity_lp.3rd + issued_lp_tokens,
),
}
)
}

test deposit_test() {
Expand Down Expand Up @@ -195,8 +198,9 @@ test deposit_test() {
datum: InlineDatum(order),
reference_script: None,
}
let final_pool_state = do_deposit(pool_state, input, assets, order.destination, 2_500_000, output)
expect final_pool_state.quantity_a.3rd == 1_010_000_000
expect final_pool_state.quantity_b.3rd == 1_010_000_000
let new_a, new_b, new_lp <- do_deposit(pool_state.quantity_a, pool_state.quantity_b, pool_state.quantity_lp, input, assets, order.destination, 2_500_000, output)
expect new_a.3rd == 1_010_000_000
expect new_b.3rd == 1_010_000_000
expect new_lp.3rd == 1_000_000_000 + 10_000_000
True
}
43 changes: 21 additions & 22 deletions lib/calculation/donation.ak
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use aiken/transaction.{Output}
use aiken/transaction/value.{ada_policy_id, ada_asset_name}
use calculation/shared.{PoolState} as calc_shared
use shared.{SingletonValue}
use types/order.{Destination, Fixed, Self}

Expand All @@ -13,7 +12,8 @@ use types/order.{Destination, Fixed, Self}
// Calculates the new pool state, and whether to consume this output or not
pub fn do_donation(
/// The pool state as a result of processing all orders before this one
pool_state: PoolState,
pool_quantity_a: SingletonValue,
pool_quantity_b: SingletonValue,
/// The total quantity of tokens on this particular input
input_utxo: Output,
/// The amounts of each asset being donated
Expand All @@ -26,15 +26,17 @@ pub fn do_donation(
/// The next output in the list; If there is any change left over from the donation, we would expect this to be that change
/// If there is no change leftover, this output is ignored and used for the next order
output: Output,
) -> (PoolState, Bool) {
// A continuation to call with the new pool balances, and whether to skip an output; more efficient than constructing an object each time
continuation: fn(SingletonValue, SingletonValue, Bool) -> Bool
) -> Bool {
let Output { value: input_value, .. } = input_utxo
let ((asset_a_policy_id, asset_a_asset_name, asset_a_qty), (asset_b_policy_id, asset_b_asset_name, asset_b_qty)) = assets
// Make sure we're actually donating the pool assets; this is to prevent setting
// poolIdent to None, and then filling the pool UTXO with garbage tokens and eventually locking it
expect asset_a_policy_id == pool_state.quantity_a.1st
expect asset_a_asset_name == pool_state.quantity_a.2nd
expect asset_b_policy_id == pool_state.quantity_b.1st
expect asset_b_asset_name == pool_state.quantity_b.2nd
expect asset_a_policy_id == pool_quantity_a.1st
expect asset_a_asset_name == pool_quantity_a.2nd
expect asset_b_policy_id == pool_quantity_b.1st
expect asset_b_asset_name == pool_quantity_b.2nd
// Compute however much of the UTXO value is *left over* after deducting the donation amount from it; If nonzero, this will need to be returned to the user
let remainder =
input_value
Expand Down Expand Up @@ -70,24 +72,21 @@ pub fn do_donation(
}
}

// Compute the new pool state, which is exactly the old pool state, plus the
// Continue with the new pool state, which is exactly the old pool state, plus the
// quantities of assets donated, plus the protocol fee!
// We also returned whether we had a remainder or not, so the calling function knows
// whether to consume the output they gave us or not.
(
PoolState {
quantity_a: (
pool_state.quantity_a.1st,
pool_state.quantity_a.2nd,
pool_state.quantity_a.3rd + assets.1st.3rd,
),
quantity_b: (
pool_state.quantity_b.1st,
pool_state.quantity_b.2nd,
pool_state.quantity_b.3rd + assets.2nd.3rd,
),
quantity_lp: pool_state.quantity_lp,
},
continuation(
(
pool_quantity_a.1st,
pool_quantity_a.2nd,
pool_quantity_a.3rd + assets.1st.3rd,
),
(
pool_quantity_b.1st,
pool_quantity_b.2nd,
pool_quantity_b.3rd + assets.2nd.3rd,
),
has_remainder,
)
}
Loading
Loading