From 2a375a3866fa2c8942d36888c6ae1c10a3d6d08e Mon Sep 17 00:00:00 2001
From: Pi Lanningham <pi.lanningham@gmail.com>
Date: Sat, 13 Jan 2024 00:31:05 -0500
Subject: [PATCH] Resolve SSW-304

The `datum` parameter was redundant, as we were already recursively
passing down each of the fields anyway.

However, as we hyper-optimized the swap operation, we added specific
fields for each swap, and left the other order types unchanged.

By passing in those specific parameters to individual functions, we can
simplify the code and make it easier to reason about during the audit.
---
 lib/calculation/deposit.ak    | 16 +++++++++-------
 lib/calculation/donation.ak   | 21 +++++++++++----------
 lib/calculation/process.ak    | 15 ++++++---------
 lib/calculation/withdrawal.ak | 14 ++++++++------
 4 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/lib/calculation/deposit.ak b/lib/calculation/deposit.ak
index cc7c72d..e6c2d8b 100644
--- a/lib/calculation/deposit.ak
+++ b/lib/calculation/deposit.ak
@@ -4,16 +4,17 @@ use aiken/transaction/credential.{Address, VerificationKeyCredential}
 use aiken/transaction/value.{Value, ada_policy_id, ada_asset_name}
 use calculation/shared.{PoolState} as calc_shared
 use sundae/multisig
-use types/order.{Destination, Deposit, OrderDatum}
+use shared.{SingletonValue}
+use types/order.{Destination, OrderDatum}
 
 pub fn do_deposit(
   pool_state: PoolState,
   input_value: Value,
-  order: OrderDatum,
+  assets: (SingletonValue, SingletonValue),
+  destination: Destination,
   actual_protocol_fee: Int,
   output: Output,
 ) -> PoolState {
-  expect order.Deposit { assets } = order.details
   let (asset_a, asset_b) = assets
 
   // Policy ID and token name of the assets must match the pool.
@@ -74,8 +75,8 @@ pub fn do_deposit(
           issued_lp_tokens,
         )
 
-  expect output.address == order.destination.address
-  expect output.datum == order.destination.datum
+  expect output.address == destination.address
+  expect output.datum == destination.datum
   expect output.value == out_value
   PoolState {
     quantity_a: (
@@ -119,6 +120,7 @@ test deposit_test() {
   let input_value =
     value.from_lovelace(14_500_000)
       |> value.add(rberry.1st, rberry.2nd, 10_000_000)
+  let assets = ((ada.1st, ada.2nd, 10_000_000), (rberry.1st, rberry.2nd, 10_000_000))
   let order = OrderDatum {
     pool_ident: None,
     owner: multisig.Signature(
@@ -130,7 +132,7 @@ test deposit_test() {
       datum: NoDatum,
     },
     details: order.Deposit {
-      assets: ((ada.1st, ada.2nd, 10_000_000), (rberry.1st, rberry.2nd, 10_000_000)),
+      assets: assets,
     },
     extension: Void,
   }
@@ -141,7 +143,7 @@ test deposit_test() {
     datum: NoDatum,
     reference_script: None,
   }
-  let final_pool_state = do_deposit(pool_state, input_value, order, 2_500_000, output)
+  let final_pool_state = do_deposit(pool_state, input_value, 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
   expect final_pool_state.protocol_fees == 4_500_000
diff --git a/lib/calculation/donation.ak b/lib/calculation/donation.ak
index cfb2937..6b15c4d 100644
--- a/lib/calculation/donation.ak
+++ b/lib/calculation/donation.ak
@@ -2,18 +2,18 @@ use aiken/transaction.{NoDatum, Output}
 use aiken/transaction/credential.{Address, VerificationKeyCredential}
 use aiken/transaction/value.{Value}
 use calculation/shared.{PoolState} as calc_shared
-use shared
+use shared.{SingletonValue}
 use sundae/multisig
 use types/order.{Destination, OrderDatum}
 
 pub fn do_donation(
   pool_state: PoolState,
   input_value: Value,
-  order: OrderDatum,
+  assets: (SingletonValue, SingletonValue),
+  destination: Destination,
   actual_protocol_fee: Int,
   output: Output,
 ) -> (PoolState, Bool) {
-  expect order.Donation { assets } = order.details
   expect assets.1st.1st == pool_state.quantity_a.1st
   expect assets.1st.2nd == pool_state.quantity_a.2nd
   expect assets.2nd.1st == pool_state.quantity_b.1st
@@ -27,8 +27,8 @@ pub fn do_donation(
   let has_remainder = remainder != value.zero()
   let Void =
     if has_remainder {
-      expect output.address == order.destination.address
-      expect output.datum == order.destination.datum
+      expect output.address == destination.address
+      expect output.datum == destination.datum
       expect output.value == remainder
       Void
     } else {
@@ -77,6 +77,10 @@ test donation() {
   let input_value =
     value.from_lovelace(1_000_000)
       |> value.add(rberry.1st, rberry.2nd, 1_000_000)
+  let assets = (
+          (ada.1st, ada.2nd, 1_000_000),
+          (rberry.1st, rberry.2nd, 1_000_000),
+        )
   let order =
     OrderDatum {
       pool_ident: None,
@@ -86,10 +90,7 @@ test donation() {
       max_protocol_fee: 2_500_000,
       destination: Destination { address: addr, datum: NoDatum },
       details: order.Donation {
-        assets: (
-          (ada.1st, ada.2nd, 1_000_000),
-          (rberry.1st, rberry.2nd, 1_000_000),
-        ),
+        assets: assets,
       },
       extension: Void,
     }
@@ -102,7 +103,7 @@ test donation() {
       reference_script: None,
     }
   let (final_pool_state, has_remainder) =
-    do_donation(pool_state, input_value, order, 2_500_000, output)
+    do_donation(pool_state, input_value, assets, order.destination, 2_500_000, output)
   expect !has_remainder
   expect final_pool_state.quantity_a.3rd == 1_001_000_000
   expect final_pool_state.quantity_b.3rd == 1_001_000_000
diff --git a/lib/calculation/process.ak b/lib/calculation/process.ak
index 4c455b6..ce610c4 100644
--- a/lib/calculation/process.ak
+++ b/lib/calculation/process.ak
@@ -96,7 +96,6 @@ pub fn process_order(
   value: Value,
   details: Order,
   max_protocol_fee: Int,
-  datum: OrderDatum,
   destination: Destination,
   amortized_base_fee: Int,
   simple_fee: Int,
@@ -122,7 +121,6 @@ pub fn process_order(
         value,
         details,
         max_protocol_fee,
-        datum,
         destination,
         amortized_base_fee,
         // We pass strategy_fee here, instead of simple_fee,
@@ -150,28 +148,28 @@ pub fn process_order(
         )
       (next, rest_outputs, fee)
     }
-    order.Deposit(..) -> {
+    order.Deposit(assets) -> {
       // Make sure the scooper can only take up to the max fee the user has agreed to
       let fee = amortized_base_fee + simple_fee
       expect max_protocol_fee >= fee
-      let next = deposit.do_deposit(initial, value, datum, fee, output)
+      let next = deposit.do_deposit(initial, value, assets, destination, fee, output)
       (next, rest_outputs, fee)
     }
-    order.Withdrawal(..) -> {
+    order.Withdrawal(amount) -> {
       // Make sure the scooper can only take up to the max fee the user has agreed to
       let fee = amortized_base_fee + simple_fee
       expect max_protocol_fee >= fee
       let next =
-        withdrawal.do_withdrawal(initial, value, datum, fee, output)
+        withdrawal.do_withdrawal(initial, value, amount, destination, fee, output)
       (next, rest_outputs, fee)
     }
     // order.Zap(..) -> do_zap(initial, input, datum)
-    order.Donation(..) -> {
+    order.Donation(assets) -> {
       // Make sure the scooper can only take up to the max fee the user has agreed to
       let fee = amortized_base_fee + simple_fee
       expect max_protocol_fee >= fee
       let (next, used_output) =
-        donation.do_donation(initial, value, datum, fee, output)
+        donation.do_donation(initial, value, assets, destination, fee, output)
       if used_output {
         (next, rest_outputs, fee)
       } else {
@@ -238,7 +236,6 @@ pub fn process_orders(
         order.value,
         details,
         max_protocol_fee,
-        datum, // TODO: can we get rid of this?
         destination,
         amortized_base_fee,
         simple_fee,
diff --git a/lib/calculation/withdrawal.ak b/lib/calculation/withdrawal.ak
index 4588d62..393a509 100644
--- a/lib/calculation/withdrawal.ak
+++ b/lib/calculation/withdrawal.ak
@@ -5,16 +5,17 @@ use aiken/transaction/credential.{Address, VerificationKeyCredential}
 use aiken/transaction/value.{Value, ada_policy_id, ada_asset_name}
 use calculation/shared.{PoolState} as calc_shared
 use sundae/multisig
+use shared.{SingletonValue}
 use types/order.{Destination, OrderDatum}
 
 pub fn do_withdrawal(
   pool_state: PoolState,
   input_value: Value,
-  order: OrderDatum,
+  amount: SingletonValue,
+  destination: Destination,
   actual_protocol_fee: Int,
   output: Output,
 ) -> PoolState {
-  expect order.Withdrawal { amount } = order.details
   let (lp_policy, lp_asset_name, amount) = amount
 
   expect lp_policy == pool_state.quantity_lp.1st
@@ -41,8 +42,8 @@ pub fn do_withdrawal(
         )
 
   // Check that the payout is satisfied by the next output
-  expect output.address == order.destination.address
-  expect output.datum == order.destination.datum
+  expect output.address == destination.address
+  expect output.datum == destination.datum
   expect output.value == remainder
   PoolState {
     quantity_a: (
@@ -125,6 +126,7 @@ fn withdrawal_test(options: WithdrawalTestOptions) {
   let input_value =
     value.from_lovelace(4_500_000)
       |> value.add(lp.1st, lp.2nd, 10_000_000)
+  let amount = (lp.1st, lp.2nd, 10_000_000)
   let order = OrderDatum {
     pool_ident: None,
     owner: multisig.Signature(
@@ -136,7 +138,7 @@ fn withdrawal_test(options: WithdrawalTestOptions) {
       datum: NoDatum,
     },
     details: order.Withdrawal {
-      amount: (lp.1st, lp.2nd, 10_000_000),
+      amount
     },
     extension: Void,
   }
@@ -150,7 +152,7 @@ fn withdrawal_test(options: WithdrawalTestOptions) {
     datum: NoDatum,
     reference_script: None,
   }
-  let final_pool_state = do_withdrawal(pool_state, input_value, order, 2_500_000, output)
+  let final_pool_state = do_withdrawal(pool_state, input_value, amount, order.destination, 2_500_000, output)
   expect final_pool_state.quantity_a.3rd == 1_000_000_000
   expect final_pool_state.quantity_b.3rd == 1_000_000_000
   expect final_pool_state.protocol_fees == 4_500_000