Skip to content

Commit

Permalink
Merge branch 'main' into pi/continuations
Browse files Browse the repository at this point in the history
  • Loading branch information
Quantumplation committed Apr 12, 2024
2 parents 83972ae + a4d38bb commit b1248b3
Show file tree
Hide file tree
Showing 15 changed files with 511 additions and 223 deletions.
8 changes: 8 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@ aiken blueprint apply -v settings.mint $PROTOCOL_BOOT_UTXO > tmp
mv tmp plutus.json

SETTINGS_SCRIPT_HASH="581c$(aiken blueprint policy -v settings.mint)"
aiken blueprint apply -v pool.manage $SETTINGS_SCRIPT_HASH > tmp
mv tmp plutus.json

MANAGE_STAKE_SCRIPT_HASH="581c$(aiken blueprint policy -v pool.manage)"
aiken blueprint apply -v pool.spend $MANAGE_STAKE_SCRIPT_HASH > tmp
mv tmp plutus.json
aiken blueprint apply -v pool.spend $SETTINGS_SCRIPT_HASH > tmp
mv tmp plutus.json

aiken blueprint apply -v pool.mint $MANAGE_STAKE_SCRIPT_HASH > tmp
mv tmp plutus.json
aiken blueprint apply -v pool.mint $SETTINGS_SCRIPT_HASH > tmp
mv tmp plutus.json

Expand Down
18 changes: 10 additions & 8 deletions lib/calculation/deposit.ak
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,16 @@ pub fn do_deposit(

// 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
// If we have more b than a, then we can only take up to an equivalent 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_quantity_b
* (b_in_units_of_a - user_gives_a) // the "surplus" value in units of a
/ pool_quantity_a
(user_gives_a, user_gives_b - change)
// Make sure to do ceiling division here, to round in favor fo the protocol
// That is, when in doubt, take up to one more token from the user than the
// LP tokens we issue would entail
let give_b = ((pool_quantity_b * user_gives_a - 1) / pool_quantity_a) + 1
(user_gives_a, give_b)
} else {
let change = user_gives_a - b_in_units_of_a
(user_gives_a - change, user_gives_b)
(b_in_units_of_a, user_gives_b)
}

// We can then calculate how much LP tokens this user should get;
Expand All @@ -102,6 +101,9 @@ pub fn do_deposit(
let issued_lp_tokens =
deposited_a * pool_quantity_lp / pool_quantity_a

// Make sure we don't ever allow this to round to zero, which would just eat some of the users assets
expect issued_lp_tokens > 0

// 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,
// plus the relevant LP tokens
Expand Down
9 changes: 9 additions & 0 deletions lib/shared.ak
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,12 @@ test count_orders_test() {
pub fn oracle_sft_name() {
"oracle"
}

pub fn fees_in_legal_range(fees: (Int, Int)) {
and {
fees.1st >= 0,
fees.2nd >= 0,
fees.1st <= 10000,
fees.2nd <= 10000,
}
}
13 changes: 12 additions & 1 deletion lib/tests/aiken/deposit.ak
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ test deposit_test() {
)
}

test deposit_test_minimum() fail {
deposit_test_schema(
661_278_808_416,
188_253_559_159_646,
153_000_000,
150,
151_171_875,
150,
value.from_lovelace(152_000_000)
)
}

fn deposit_test_schema(qa: Int, qb: Int, has_a: Int, has_b: Int, gives_a: Int, gives_b: Int, out_value: value.Value) {
let addr =
Address(
Expand Down Expand Up @@ -82,4 +94,3 @@ fn deposit_test_schema(qa: Int, qb: Int, has_a: Int, has_b: Int, gives_a: Int, g
// Test should pass as long as do_deposit didn't throw
True
}

6 changes: 3 additions & 3 deletions lib/tests/examples/ex_pool.ak
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use types/pool.{PoolDatum, PoolRedeemer, PoolMintRedeemer, PoolScoop, WithdrawFees, CreatePool}
use types/pool.{PoolDatum, PoolRedeemer, PoolMintRedeemer, PoolScoop, ManageRedeemer, CreatePool, WithdrawFees}
use tests/examples/ex_shared.{print_example}

fn mk_pool_datum() -> PoolDatum {
Expand Down Expand Up @@ -33,8 +33,8 @@ test example_pool_scoop_redeemer() {
print_example(mk_pool_scoop())
}

pub fn mk_withdraw_fees_redeemer() -> PoolRedeemer {
WithdrawFees { amount: 100, treasury_output: 1 }
pub fn mk_withdraw_fees_redeemer() -> ManageRedeemer {
WithdrawFees { amount: 100, treasury_output: 1, pool_input: 0 }
}

test example_pool_withdraw_fees_redeemer() {
Expand Down
16 changes: 15 additions & 1 deletion lib/tests/examples/ex_shared.ak
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use aiken/bytearray
use aiken/cbor
use aiken/transaction.{OutputReference, TransactionId}
use aiken/transaction/credential.{Address, ScriptCredential, VerificationKeyCredential}
use aiken/transaction/credential.{Address, Inline, StakeCredential, ScriptCredential, VerificationKeyCredential}

pub const examples_enabled: Int = 1
pub fn print_example(d: Data) -> Bool {
Expand Down Expand Up @@ -34,3 +34,17 @@ pub fn script_address(hash: ByteArray) -> Address {
pub fn wallet_address(hash: ByteArray) -> Address {
Address(VerificationKeyCredential(hash), None)
}

pub fn compare_stake(left: StakeCredential, right: StakeCredential) -> Ordering {
let left = when left is {
Inline(ScriptCredential(x)) -> x
Inline(VerificationKeyCredential(x)) -> x
_ -> fail
}
let right = when right is {
Inline(ScriptCredential(x)) -> x
Inline(VerificationKeyCredential(x)) -> x
_ -> fail
}
bytearray.compare(left, right)
}
25 changes: 14 additions & 11 deletions lib/types/pool.ak
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,9 @@ pub type PoolRedeemer {
/// Much of the complexity of the protocol comes from ensuring this list is processed both efficiently and safely.
input_order: List<(Int, Option<SignedStrategyExecution>, Int)>,
}
/// Withdraw the earned protocol fees into the treasury
WithdrawFees {
/// The amount of earned protocol fees to withdraw
/// Note that we *don't* have to withdraw everything, to allow the minUTXO amount to be left behind on exotic pools.
amount: Int,
/// The index in the transaction outputs that corresponds to the treasury address
/// We do this to efficiently skip to that output,
/// and it is safe to do so because that output must be to the treasury address from the settings datum
treasury_output: Int,
}
UpdatePoolFees
/// Withdraw the earned protocol fees into the treasury, or update the pool
/// fees
Manage
}

/// We use the pool mint script for two different purposes
Expand All @@ -94,4 +86,15 @@ pub type PoolMintRedeemer {
/// This is safe because we validate that the token is paid to the metadata admin
metadata_output: Int
}
/// to burn the pool NFT (when permitted by the spending validator)
BurnPool { identifier: Ident }
}

pub type ManageRedeemer {
WithdrawFees {
amount: Int,
treasury_output: Int,
pool_input: Int,
}
UpdatePoolFees { pool_input: Int, }
}
2 changes: 1 addition & 1 deletion lucid/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,7 @@ emulator.ledger["000000000000000000000000000000000000000000000000000000000000000
const listOrdersChange = emulator.ledger["00000000000000000000000000000000000000000000000000000000000000001"].utxo;

const { listedHash, utxos: orders } =
await testListOrder(lucid, emulator, scripts, "lovelace", rberry, listOrdersChange, poolId, 60n);
await testListOrder(lucid, emulator, scripts, "lovelace", rberry, listOrdersChange, poolId, 40n);

const scoopPoolChange = await findChange(emulator, userAddress);

Expand Down
5 changes: 1 addition & 4 deletions lucid/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,7 @@ export const PoolSpendRedeemerSchema = Data.Enum([
}),
}),
Data.Object({
WithdrawFees: Data.Object({
amount: Data.Integer(),
treasuryOutput: Data.Integer(),
}),
Manage: Data.Tuple([]),
}),
]);

Expand Down
95 changes: 68 additions & 27 deletions plutus.json

Large diffs are not rendered by default.

24 changes: 10 additions & 14 deletions validators/order.ak
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use types/order.{Cancel, OrderDatum, OrderRedeemer, Scoop}
/// That stake_script then checks that a pool NFT is present on the UTXO, where most of the subtle and important logic is implemented.
validator(stake_script_hash: Hash<Blake2b_224, Script>) {
// For the purposes of spending the order, we don't care what the datum is, so avoid deserializing it
fn spend(datum: Data, redeemer: OrderRedeemer, ctx: ScriptContext) -> Bool {
pub fn spend(datum: Data, redeemer: OrderRedeemer, ctx: ScriptContext) -> Bool {
when redeemer is {
Cancel -> {
// We only expect the datum here, to avoid any related costs in the other branch, which doesn't need it
Expand All @@ -41,19 +41,15 @@ validator(stake_script_hash: Hash<Blake2b_224, Script>) {
)
}
Scoop -> {
// Assume that the stake_script_hash is the first withdrawal in the list
// Since the ledger doesn't reorder withdrawals (TODO: confirm this!!)
// If we ever have other withdrawals (for example, additional constraints enforced by a different order)
// then that script can look for the appropriate one, and we can just ensure this one is first in the list.
expect [head] = dict.to_list(ctx.transaction.withdrawals)
when head is {
// We match against the script hash like this to ignore the withdrawal amount
// TODO: we could make this more efficient by CBOR encoding the `Inline(ScriptCredential())` into the `stake_script_hash` parameter
// or perhaps even the whole withdrawal list to compare it all at once!
// and just doing a direct equaltiy comparison. Since this runs for each order, this could represent significant savings!
(Inline(ScriptCredential(script)), _) -> script == stake_script_hash
_ -> False
}
dict.foldl(ctx.transaction.withdrawals, False, fn(withdrawal, _amt, acc) {
when withdrawal is {
// TODO: we could make this more efficient by CBOR encoding the `Inline(ScriptCredential())` into the `stake_script_hash` parameter
// or perhaps even the whole withdrawal list to compare it all at once!
// and just doing a direct equaltiy comparison. Since this runs for each order, this could represent significant savings!
Inline(ScriptCredential(script)) -> acc || script == stake_script_hash
_ -> acc
}
})
}
}
}
Expand Down
Loading

0 comments on commit b1248b3

Please sign in to comment.