Skip to content

Commit 5d32fce

Browse files
authored
Merge pull request #102 from SundaeSwap-finance/luivatra/fix-protocol-fees-management
Move protocol fee management to treasury admin
2 parents 3627b81 + 3091440 commit 5d32fce

File tree

7 files changed

+72
-34
lines changed

7 files changed

+72
-34
lines changed

aiken.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ requirements = []
4646
source = "github"
4747

4848
[etags]
49-
"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1753181341, nanos_since_epoch = 812689792 }, "9843473958e51725a9274b487d2d4aac0395ec1a2e30f090724fa737226bc127"]
49+
"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1758102049, nanos_since_epoch = 210673971 }, "9843473958e51725a9274b487d2d4aac0395ec1a2e30f090724fa737226bc127"]

lib/tests/examples/ex_settings.ak

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use aiken/cbor
21
use cardano/address.{Address, Script, VerificationKey}
32
use cardano/assets
43
use cardano/transaction.{InlineDatum, Input, Output}
54
use sundae/multisig
65
use tests/examples/ex_shared.{mk_output_reference,
76
print_example, script_address}
8-
use types/settings.{SettingsDatum}
7+
use types/settings.{ProtocolFeeBasisPointsExtension, SettingsDatum}
98

109
pub const example_settings_admin =
1110
#"6313a1d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad"
@@ -32,7 +31,14 @@ pub fn mk_valid_settings_datum(scoopers: List<ByteArray>) -> SettingsDatum {
3231
simple_fee: 2_500_000,
3332
strategy_fee: 5_000_000,
3433
pool_creation_fee: 0,
35-
extensions: Void,
34+
extensions: [
35+
Pair(
36+
0,
37+
as_data(
38+
ProtocolFeeBasisPointsExtension { protocol_fee_basis_points: (0, 0) },
39+
),
40+
),
41+
],
3642
}
3743
}
3844

@@ -57,9 +63,7 @@ pub fn mk_valid_settings_input(scoopers: List<ByteArray>, ix: Int) -> Input {
5763
}
5864

5965
test example_settings_datum() {
60-
print_example(
61-
cbor.serialise(mk_valid_settings_datum([example_settings_admin])),
62-
)
66+
print_example(mk_valid_settings_datum([example_settings_admin]))
6367
}
6468

6569
test example_big_settings_datum() {
@@ -207,7 +211,16 @@ test example_mainnet_boot_settings_datum() {
207211
simple_fee: 168_000,
208212
strategy_fee: 168_000,
209213
pool_creation_fee: 0,
210-
extensions: 0,
214+
extensions: [
215+
Pair(
216+
0,
217+
as_data(
218+
ProtocolFeeBasisPointsExtension {
219+
protocol_fee_basis_points: (0, 0),
220+
},
221+
),
222+
),
223+
],
211224
},
212225
)
213226
}

lib/types/settings.ak

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use aiken/builtin
22
use aiken/collection/dict
3+
use aiken/collection/pairs
34
use aiken/crypto.{VerificationKey}
45
use cardano/address.{Address, Credential}
56
use cardano/assets.{AssetName, PolicyId}
@@ -67,7 +68,12 @@ pub type SettingsDatum {
6768
/// - publish data for convenient and canonical access by off-chain actors
6869
/// - expose data to the script conditions in the multisig scripts of the two administrator roles
6970
/// - expose additional settings for more advanced pool and order types to operate off of
70-
extensions: Data,
71+
extensions: Pairs<Int, Data>,
72+
}
73+
74+
pub type ProtocolFeeBasisPointsExtension {
75+
/// The default protocol fee basis points to charge on each trade for bid (A -> B) and ask (B -> A) orders, for pools that don't override it
76+
protocol_fee_basis_points: (Int, Int),
7177
}
7278

7379
/// The settings redeemer can be spent for two different purposes
@@ -81,6 +87,19 @@ pub type SettingsRedeemer {
8187
/// The name of the token that authenticates the settings UTXO
8288
pub const settings_nft_name: AssetName = "settings"
8389

90+
pub fn find_protocol_fee_extension(
91+
settings: SettingsDatum,
92+
) -> Option<ProtocolFeeBasisPointsExtension> {
93+
let maybe_extension = pairs.get_first(settings.extensions, 0)
94+
when maybe_extension is {
95+
Some(extension_data) -> {
96+
expect extension: ProtocolFeeBasisPointsExtension = extension_data
97+
Some(extension)
98+
}
99+
None -> None
100+
}
101+
}
102+
84103
/// Scan over the list of reference inputs to find the settings datum, and ensure it's the correct one
85104
/// Note that this makes the assumption that the settings datum is the first reference input, for performance
86105
/// This means that when storing reference scripts on-chain, there needs to be a small amount of "farming" to select

validators/pool.ak

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ use types/pool.{
2424
MintLP, PoolMintRedeemer, PoolRedeemer, PoolScoop, StablePoolDatum,
2525
UpdatePoolFees, WithdrawFees,
2626
} as types_pool
27-
use types/settings.{SettingsDatum, find_settings_datum}
27+
use types/settings.{
28+
ProtocolFeeBasisPointsExtension, SettingsDatum, find_protocol_fee_extension,
29+
find_settings_datum,
30+
}
2831

2932
/// An implementation of a curve-like "Stableswap" invariant that enables more efficient trades for stablecoin pairs.
3033
///
@@ -461,9 +464,9 @@ validator pool(
461464
reserve_a
462465
}
463466

464-
// We follow Curve's model for initial liquidity, by setting it to the initial sum invariant
467+
// We follow Curve's model for initial liquidity, by setting it to the initial sum invariant, but divide by precision to avoid very large numbers potentially causing overflow
465468
expect
466-
pool_output_datum.sum_invariant == pool_output_datum.circulating_lp
469+
pool_output_datum.sum_invariant / calc_precision == pool_output_datum.circulating_lp
467470

468471
// And check that we mint the correct tokens, and nothing else.
469472
let expected_mint =
@@ -518,6 +521,9 @@ validator pool(
518521
// and potentially even on access UIs for the Sundae protocol
519522
expect metadata_output.datum == InlineDatum(Void)
520523

524+
expect Some(protocol_fee_extension) =
525+
find_protocol_fee_extension(settings_datum)
526+
521527
// And check that the datum is initialized correctly; This is part of why we have a minting policy handling this,
522528
// as it allows us to authenticate the providence of the datum.
523529
// A datum is valid so long as
@@ -541,6 +547,7 @@ validator pool(
541547
shared.fees_in_legal_range(
542548
pool_output_datum.protocol_fee_basis_points.2nd,
543549
),
550+
protocol_fee_extension.protocol_fee_basis_points == pool_output_datum.protocol_fee_basis_points,
544551
pool_output_datum.linear_amplification > 0,
545552
liquidity_invariant(
546553
reserve_a * calc_precision,
@@ -901,7 +908,8 @@ validator manage(settings_policy_id: PolicyId) {
901908
pool_output_address.payment_credential == Script(pool_script_hash)
902909

903910
// As part of withdrawing, we should decrease the protocol fees by the amount we're withdrawing
904-
// but, importantly, *nothing else*; so we construct a datum with everything from the initial datum, plus the protofol fees updated
911+
// but, the treasury admin is also allowed to update the protocol fees basis points, but nothing else in the datum
912+
// is allowed to be changed.
905913
let expected_datum =
906914
StablePoolDatum {
907915
..datum,
@@ -910,6 +918,7 @@ validator manage(settings_policy_id: PolicyId) {
910918
initial_fee_a - amount.2nd,
911919
initial_fee_b - amount.3rd,
912920
),
921+
protocol_fee_basis_points: output_datum.protocol_fee_basis_points,
913922
}
914923
expect output_datum == expected_datum
915924

@@ -957,17 +966,13 @@ validator manage(settings_policy_id: PolicyId) {
957966

958967
let StablePoolDatum {
959968
lp_fee_basis_points,
960-
protocol_fee_basis_points,
961-
protocol_fees,
962969
fee_manager: output_fee_manager,
963970
..
964971
} = pool_output_datum
965972

966973
// Make sure we don't update the fees to negative or above 100%
967974
expect shared.fees_in_legal_range(lp_fee_basis_points.1st)
968975
expect shared.fees_in_legal_range(lp_fee_basis_points.2nd)
969-
expect shared.fees_in_legal_range(protocol_fee_basis_points.1st)
970-
expect shared.fees_in_legal_range(protocol_fee_basis_points.2nd)
971976

972977
// Check that the *current* fee manager approves the update
973978
expect Some(fee_manager) = datum.fee_manager
@@ -983,12 +988,11 @@ validator manage(settings_policy_id: PolicyId) {
983988
StablePoolDatum {
984989
..datum,
985990
protocol_fees: (
986-
protocol_fees.1st + lovelace_diff,
987-
protocol_fees.2nd,
988-
protocol_fees.3rd,
991+
datum.protocol_fees.1st + lovelace_diff,
992+
datum.protocol_fees.2nd,
993+
datum.protocol_fees.3rd,
989994
),
990995
lp_fee_basis_points: lp_fee_basis_points,
991-
protocol_fee_basis_points: protocol_fee_basis_points,
992996
fee_manager: output_fee_manager,
993997
}
994998
pool_output_datum == expected_datum
@@ -1010,7 +1014,6 @@ validator manage(settings_policy_id: PolicyId) {
10101014
// We need the pool output to check that only the fees or fee manager are updated
10111015
let StablePoolDatum {
10121016
assets,
1013-
protocol_fees,
10141017
linear_amplification,
10151018
sum_invariant,
10161019
linear_amplification_manager: output_linear_amplification_manager,
@@ -1020,7 +1023,7 @@ validator manage(settings_policy_id: PolicyId) {
10201023
let
10211024
pool_quantity_a,
10221025
pool_quantity_b,
1023-
<- pool_input_to_asset_reserves(assets, protocol_fees, pool_input)
1026+
<- pool_input_to_asset_reserves(assets, datum.protocol_fees, pool_input)
10241027

10251028
expect
10261029
liquidity_invariant(
@@ -1048,9 +1051,9 @@ validator manage(settings_policy_id: PolicyId) {
10481051
StablePoolDatum {
10491052
..datum,
10501053
protocol_fees: (
1051-
protocol_fees.1st + lovelace_diff,
1052-
protocol_fees.2nd,
1053-
protocol_fees.3rd,
1054+
datum.protocol_fees.1st + lovelace_diff,
1055+
datum.protocol_fees.2nd,
1056+
datum.protocol_fees.3rd,
10541057
),
10551058
linear_amplification: linear_amplification,
10561059
sum_invariant: sum_invariant,

validators/tests/pool.ak

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use cardano/transaction.{
1515
Transaction, Withdraw,
1616
}
1717
use pool as pool_validator
18-
use shared.{get_D, pool_lp_name}
18+
use shared.{calc_precision, get_D, pool_lp_name}
1919
use sundae/multisig
2020
use tests/constants
2121
use tests/examples/ex_settings.{
@@ -36,7 +36,9 @@ use types/pool.{
3636
BurnPool, CreatePool, Manage, PoolMintRedeemer, PoolScoop, StablePoolDatum,
3737
UpdatePoolFees, WithdrawFees,
3838
}
39-
use types/settings.{SettingsDatum, settings_nft_name}
39+
use types/settings.{
40+
SettingsDatum, settings_nft_name,
41+
}
4042

4143
type ScoopTestOptions {
4244
edit_order_1_in_value: Option<Value>,
@@ -381,7 +383,7 @@ test scooper_not_in_settings() fail {
381383
simple_fee: 2_500_000,
382384
strategy_fee: 5_000_000,
383385
pool_creation_fee: 0,
384-
extensions: Void,
386+
extensions: [],
385387
},
386388
),
387389
),
@@ -1345,6 +1347,7 @@ fn mint_test_modify(
13451347
let reserve_b = 1_000_000_000
13461348
let linear_amplification = 10
13471349
let sum_invariant = get_D(linear_amplification, reserve_a, reserve_b)
1350+
let initial_lp = sum_invariant / calc_precision
13481351
let inline_pool_datum =
13491352
modify_datum(
13501353
InlineDatum(
@@ -1354,7 +1357,7 @@ fn mint_test_modify(
13541357
(ada_policy_id, ada_asset_name),
13551358
(constants.rberry_policy, constants.rberry_asset_name),
13561359
),
1357-
circulating_lp: sum_invariant,
1360+
circulating_lp: initial_lp,
13581361
lp_fee_basis_points: (5, 5),
13591362
protocol_fee_basis_points: (0, 0),
13601363
fee_manager: None,
@@ -1384,7 +1387,7 @@ fn mint_test_modify(
13841387
assets.from_asset(
13851388
constants.pool_script_hash,
13861389
pool_lp_name(pool_id),
1387-
sum_invariant,
1390+
initial_lp,
13881391
)
13891392
|> assets.merge(assets.from_lovelace(2_000_000))
13901393
let lp_output =
@@ -1424,7 +1427,7 @@ fn mint_test_modify(
14241427
|> assets.add(
14251428
constants.pool_script_hash,
14261429
new_pool_lp_token,
1427-
sum_invariant,
1430+
initial_lp,
14281431
)
14291432
|> assets.add(constants.pool_script_hash, new_pool_nft_token, 1)
14301433
|> assets.add(constants.pool_script_hash, new_pool_ref_token, 1),

validators/tests/pool.manage.ak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ fn scenario_settings_input_baseline(
388388
simple_fee: 2_500_000,
389389
strategy_fee: 5_000_000,
390390
pool_creation_fee: 0,
391-
extensions: Void,
391+
extensions: [],
392392
}
393393
},
394394
)

validators/tests/settings.ak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ fn mk_valid_settings_datum(scoopers: List<ByteArray>) -> SettingsDatum {
9292
simple_fee: 2_500_000,
9393
strategy_fee: 5_000_000,
9494
pool_creation_fee: 0,
95-
extensions: Void,
95+
extensions: [],
9696
}
9797
}
9898

0 commit comments

Comments
 (0)