Skip to content

Commit 15f443c

Browse files
Merge remote-tracking branch 'origin/main' into pi/SSW-306-efficient-sqrt
2 parents 91c1330 + db3d33e commit 15f443c

File tree

10 files changed

+1035
-706
lines changed

10 files changed

+1035
-706
lines changed

.github/workflows/tests.yml

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,33 @@ jobs:
1313

1414
- uses: aiken-lang/[email protected]
1515
with:
16-
version: v1.0.20-alpha
16+
version: v1.0.24-alpha
1717

18-
- run: aiken check
18+
- run: |
19+
# Run the tests
20+
set -o pipefail
21+
RESULT=0
22+
aiken check 2>&1 | tee aiken.log || RESULT=$?
23+
if [ $RESULT -ne 0 ]; then
24+
{
25+
echo 'FAILING_TESTS<<EOF'
26+
grep "FAIL" aiken.log
27+
echo EOF
28+
} >> "$GITHUB_ENV"
29+
cat $GITHUB_ENV
30+
exit $RESULT
31+
fi
32+
- if: failure()
33+
run: |
34+
echo "$FAILING_TESTS"
1935
- run: aiken build
36+
- uses: actions/github-script@v6
37+
if: failure() && env.FAILING_TESTS != ''
38+
with:
39+
script: |
40+
github.rest.issues.createComment({
41+
issue_number: context.issue.number,
42+
owner: context.repo.owner,
43+
repo: context.repo.repo,
44+
body: `Tests failed:\n\n\`\`\`\n${{ env.FAILING_TESTS }}\n\`\`\``
45+
})

aiken.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ name = "SundaeSwap-finance/aicone"
1111
version = "ae0852d40cc6332437492102451cf331a3c10b0d"
1212
source = "github"
1313

14+
[[requirements]]
15+
name = "aiken-extra/tx_util"
16+
version = "1.170.202312"
17+
source = "github"
18+
1419
[[packages]]
1520
name = "aiken-lang/stdlib"
1621
version = "97cd61345bcc8925c521b30d0f354859eb0148cd"
@@ -23,4 +28,10 @@ version = "ae0852d40cc6332437492102451cf331a3c10b0d"
2328
requirements = []
2429
source = "github"
2530

31+
[[packages]]
32+
name = "aiken-extra/tx_util"
33+
version = "1.170.202312"
34+
requirements = []
35+
source = "github"
36+
2637
[etags]

aiken.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ source = "github"
1717
name = "SundaeSwap-finance/aicone"
1818
version = "ae0852d40cc6332437492102451cf331a3c10b0d"
1919
source = "github"
20+
21+
[[dependencies]]
22+
name = "aiken-extra/tx_util"
23+
version = "1.170.202312"
24+
source = "github"

lib/calculation/donation.ak

Lines changed: 11 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
use aiken/transaction.{NoDatum, Output}
2-
use aiken/transaction/credential.{Address, VerificationKeyCredential}
1+
use aiken/transaction.{Output}
32
use aiken/transaction/value.{Value, ada_policy_id, ada_asset_name}
43
use calculation/shared.{PoolState} as calc_shared
54
use shared.{SingletonValue}
6-
use sundae/multisig
7-
use types/order.{Destination, OrderDatum}
5+
use types/order.{Destination}
86

97
/// A donation describes an amount of assets to deposit into the pool, receiving nothing in return (except for the extra change on the UTXO).
108
/// Because every LP token holder has an entitlement to a percentage of the assets in the pool, the donation is distributed to all LP token holders
@@ -29,19 +27,19 @@ pub fn do_donation(
2927
/// If there is no change leftover, this output is ignored and used for the next order
3028
output: Output,
3129
) -> (PoolState, Bool) {
30+
let ((asset_a_policy_id, asset_a_asset_name, asset_a_qty), (asset_b_policy_id, asset_b_asset_name, asset_b_qty)) = assets
3231
// Make sure we're actually donating the pool assets; this is to prevent setting
3332
// poolIdent to None, and then filling the pool UTXO with garbage tokens and eventually locking it
34-
expect assets.1st.1st == pool_state.quantity_a.1st
35-
expect assets.1st.2nd == pool_state.quantity_a.2nd
36-
expect assets.2nd.1st == pool_state.quantity_b.1st
37-
expect assets.2nd.2nd == pool_state.quantity_b.2nd
33+
expect asset_a_policy_id == pool_state.quantity_a.1st
34+
expect asset_a_asset_name == pool_state.quantity_a.2nd
35+
expect asset_b_policy_id == pool_state.quantity_b.1st
36+
expect asset_b_asset_name == pool_state.quantity_b.2nd
3837
// 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
3938
let remainder =
40-
shared.to_value(assets.1st)
41-
|> value.merge(shared.to_value(assets.2nd))
42-
|> value.add(ada_policy_id, ada_asset_name, actual_protocol_fee)
43-
|> value.negate
44-
|> value.merge(input_value)
39+
input_value
40+
|> value.add(ada_policy_id, ada_asset_name, -actual_protocol_fee)
41+
|> value.add(asset_a_policy_id, asset_a_asset_name, -asset_a_qty)
42+
|> value.add(asset_b_policy_id, asset_b_asset_name, -asset_b_qty)
4543

4644
let has_remainder = remainder != value.zero()
4745
// If we have a remainder, then we need to check the details of the output; this awkward structure
@@ -76,57 +74,3 @@ pub fn do_donation(
7674
has_remainder,
7775
)
7876
}
79-
80-
test donation() {
81-
let addr =
82-
Address(
83-
VerificationKeyCredential(
84-
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513",
85-
),
86-
None,
87-
)
88-
let ada = (#"", #"")
89-
let rberry =
90-
(#"01010101010101010101010101010101010101010101010101010101", "RBERRY")
91-
let lp = (#"99999999999999999999999999999999999999999999999999999999", "LP")
92-
let pool_state =
93-
PoolState {
94-
quantity_a: (#"", #"", 1_000_000_000),
95-
quantity_b: (rberry.1st, rberry.2nd, 1_000_000_000),
96-
quantity_lp: (lp.1st, lp.2nd, 1_000_000_000),
97-
}
98-
let input_value =
99-
value.from_lovelace(3_500_000)
100-
|> value.add(rberry.1st, rberry.2nd, 1_000_000)
101-
let assets = (
102-
(ada.1st, ada.2nd, 1_000_000),
103-
(rberry.1st, rberry.2nd, 1_000_000),
104-
)
105-
let order =
106-
OrderDatum {
107-
pool_ident: None,
108-
owner: multisig.Signature(
109-
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513",
110-
),
111-
max_protocol_fee: 2_500_000,
112-
destination: Destination { address: addr, datum: NoDatum },
113-
details: order.Donation {
114-
assets: assets,
115-
},
116-
extension: Void,
117-
}
118-
// There's no remainder so do_donation totally ignores this Output record
119-
let output =
120-
Output {
121-
address: addr,
122-
value: value.from_lovelace(999_999_999_999_999_999),
123-
datum: NoDatum,
124-
reference_script: None,
125-
}
126-
let (final_pool_state, has_remainder) =
127-
do_donation(pool_state, input_value, assets, order.destination, 2_500_000, output)
128-
expect !has_remainder
129-
expect final_pool_state.quantity_a.3rd == 1_001_000_000
130-
expect final_pool_state.quantity_b.3rd == 1_001_000_000
131-
True
132-
}

lib/shared.ak

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ use aiken/transaction.{
1010
use aiken/transaction/credential.{Credential, ScriptCredential}
1111
use aiken/transaction/value.{AssetName, PolicyId}
1212

13+
use tests/examples/ex_shared.{
14+
script_address, wallet_address, mk_output_reference
15+
}
16+
1317
/// An alias type for the pool identifier, to make it slightly easier to reason about
1418
pub type Ident = ByteArray
1519

@@ -112,20 +116,20 @@ pub fn is_script(credential: Credential) -> Bool {
112116
/// without knowing the exact script address they will use
113117
/// This is quite subtle, so see the note above
114118
pub fn count_orders(tx_inputs: List<Input>) -> Int {
115-
// Can be done as a simple fold
116-
list.foldl(
117-
tx_inputs,
118-
// Note: by starting at -exact_non_order_script_inputs, it's the same as if we subtracted this at the end,
119-
// but saves one mathematical operation.
120-
-exact_non_order_script_inputs,
121-
fn(input, total) {
119+
when tx_inputs is {
120+
// Note: by using -exact_non_order_script_inputs for the base case,
121+
// it's equivalent to subtracting at the end
122+
[] -> -exact_non_order_script_inputs
123+
[input, ..rest] ->
122124
// The reason this is important is twofold:
123125
// - We count them in the first place to ensure that we process every order by the end of the transaction
124126
// - and we compare the script this way so that if we implement a new order script, with additional logic,
125127
// and the correct datum format, it can be processed the pool just fine
126-
total + if is_script(input.output.address.payment_credential) { 1 } else { 0 }
127-
},
128-
)
128+
when input.output.address.payment_credential is {
129+
ScriptCredential(_) -> count_orders(rest) + 1
130+
_ -> count_orders(rest)
131+
}
132+
}
129133
}
130134

131135
// Taken from unmerged PR: https://github.com/aiken-lang/stdlib/pull/73/files
@@ -205,4 +209,61 @@ pub fn pool_token_names(pool_ident: Ident) {
205209
pool_nft_name(pool_ident),
206210
pool_lp_name(pool_ident),
207211
)
208-
}
212+
}
213+
214+
// Test for count_orders, with 16 inputs of which 10 are orders.
215+
test count_orders_test() {
216+
let hash_of_pool_script =
217+
#"00000000000000000000000000000000000000000000000000000000"
218+
let pool_address = script_address(hash_of_pool_script)
219+
let pool_input =
220+
Input {
221+
output_reference: mk_output_reference(0),
222+
output: Output {
223+
address: pool_address,
224+
value: value.from_lovelace(0),
225+
datum: NoDatum,
226+
reference_script: None,
227+
},
228+
}
229+
230+
let hash_of_escrow_script =
231+
#"00000000000000000000000000000000000000000000000000000000"
232+
let escrow_address = script_address(hash_of_escrow_script)
233+
let escrow1_in = Input {
234+
output_reference: mk_output_reference(2),
235+
output: Output {
236+
address: escrow_address,
237+
value: value.from_lovelace(0),
238+
datum: NoDatum,
239+
reference_script: None,
240+
},
241+
}
242+
let escrow2_in = Input {
243+
output_reference: mk_output_reference(3),
244+
output: Output {
245+
address: escrow_address,
246+
value: value.from_lovelace(0),
247+
datum: NoDatum,
248+
reference_script: None,
249+
},
250+
}
251+
let other_address = wallet_address(#"fede0000000000000000000000000000000000000000000000000000")
252+
let other_in = Input {
253+
output_reference: mk_output_reference(4),
254+
output: Output {
255+
address: other_address,
256+
value: value.from_lovelace(0),
257+
datum: NoDatum,
258+
reference_script: None,
259+
},
260+
}
261+
262+
let inputs = [
263+
pool_input, other_in, escrow1_in, escrow2_in, other_in, escrow1_in,
264+
escrow2_in, other_in, escrow1_in, escrow2_in, other_in, escrow1_in,
265+
escrow2_in, other_in, escrow1_in, escrow2_in
266+
]
267+
268+
count_orders(inputs) == 10
269+
}

lib/tests/aiken/donation.ak

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use aiken/transaction.{NoDatum, Output}
2+
use aiken/transaction/credential.{Address, VerificationKeyCredential}
3+
use aiken/transaction/value
4+
use calculation/shared.{PoolState} as calc_shared
5+
use calculation/donation.{do_donation}
6+
use sundae/multisig
7+
use types/order.{Destination, OrderDatum}
8+
9+
test donation() {
10+
let addr =
11+
Address(
12+
VerificationKeyCredential(
13+
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513",
14+
),
15+
None,
16+
)
17+
let ada = (#"", #"")
18+
let rberry =
19+
(#"01010101010101010101010101010101010101010101010101010101", "RBERRY")
20+
let lp = (#"99999999999999999999999999999999999999999999999999999999", "LP")
21+
let pool_state =
22+
PoolState {
23+
quantity_a: (#"", #"", 1_000_000_000),
24+
quantity_b: (rberry.1st, rberry.2nd, 1_000_000_000),
25+
quantity_lp: (lp.1st, lp.2nd, 1_000_000_000),
26+
}
27+
let input_value =
28+
value.from_lovelace(3_500_000)
29+
|> value.add(rberry.1st, rberry.2nd, 1_000_000)
30+
let assets = (
31+
(ada.1st, ada.2nd, 1_000_000),
32+
(rberry.1st, rberry.2nd, 1_000_000),
33+
)
34+
let order =
35+
OrderDatum {
36+
pool_ident: None,
37+
owner: multisig.Signature(
38+
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513",
39+
),
40+
max_protocol_fee: 2_500_000,
41+
destination: Destination { address: addr, datum: NoDatum },
42+
details: order.Donation {
43+
assets: assets,
44+
},
45+
extension: Void,
46+
}
47+
// There's no remainder so do_donation totally ignores this Output record
48+
let output =
49+
Output {
50+
address: addr,
51+
value: value.from_lovelace(999_999_999_999_999_999),
52+
datum: NoDatum,
53+
reference_script: None,
54+
}
55+
let (final_pool_state, has_remainder) =
56+
do_donation(pool_state, input_value, assets, order.destination, 2_500_000, output)
57+
expect !has_remainder
58+
expect final_pool_state.quantity_a.3rd == 1_001_000_000
59+
expect final_pool_state.quantity_b.3rd == 1_001_000_000
60+
True
61+
}

0 commit comments

Comments
 (0)