Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 13 additions & 11 deletions contracts/stableswap.clar
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@
;; Assert that x-amount is less than x10 of current-balance-x
(asserts! (< x-amount (* u10 current-balance-x)) (err "err-x-amount-too-high"))

;; Assert that dy is greater than min-y-amount
(asserts! (> dy min-y-amount) (err "err-min-y-amount"))
;; Assert that dy is at least min-y-amount
(asserts! (>= dy min-y-amount) (err "err-min-y-amount"))

;; Transfer updated-x-balance tokens from tx-sender to this contract
(if (> updated-x-amount u0)
Expand Down Expand Up @@ -459,8 +459,8 @@
;; Assert that y-amount is less than x10 of current-balance-y
(asserts! (< y-amount (* u10 current-balance-y)) (err "err-y-amount-too-high"))

;; Assert that dx is greater than min-x-amount
(asserts! (> dx min-x-amount) (err "err-min-x-amount"))
;; Assert that dx is at least min-x-amount
(asserts! (>= dx min-x-amount) (err "err-min-x-amount"))

;; Transfer updated-y-balance tokens from tx-sender to this contract
(if (> updated-y-amount u0)
Expand Down Expand Up @@ -596,8 +596,8 @@
;; Assert that d2 is greater than d0
(asserts! (> d2 d0) (err "err-d2-less-than-d0"))

;; Assert that derived mint amount is greater than min-lp-amount
(asserts! (> (/ (* current-total-shares (- d2 d0)) d0) min-lp-amount) (err "err-derived-amount-less-than-lp"))
;; Assert that derived mint amount is at least min-lp-amount
(asserts! (>= (/ (* current-total-shares (- d2 d0)) d0) min-lp-amount) (err "err-derived-amount-less-than-lp"))

;; ;; Transfer x-amount-added tokens from tx-sender to this contract
(if (> x-amount-added-updated u0)
Expand Down Expand Up @@ -670,11 +670,14 @@
(new-d (get-D new-balance-x-scaled new-balance-y-scaled current-amplification-coefficient))
)

;; Assert that withdrawal-balance-x is greater than min-x-amount
(asserts! (> withdrawal-balance-x min-x-amount) (err "err-withdrawal-balance-x-less-than-min-x-amount"))
;; Assert that lp-amount is greater than zero
(asserts! (> lp-amount u0) (err "err-lp-amount-zero"))

;; Assert that withdrawal-balance-y is greater than min-y-amount
(asserts! (> withdrawal-balance-y min-y-amount) (err "err-withdrawal-balance-y-less-than-min-y-amount"))
;; Assert that withdrawal-balance-x is at least min-x-amount
(asserts! (>= withdrawal-balance-x min-x-amount) (err "err-withdrawal-balance-x-less-than-min-x-amount"))

;; Assert that withdrawal-balance-y is at least min-y-amount
(asserts! (>= withdrawal-balance-y min-y-amount) (err "err-withdrawal-balance-y-less-than-min-y-amount"))

;; Burn LP tokens from tx-sender
(unwrap! (contract-call? lp-token burn liquidity-remover lp-amount) (err "err-burning-lp-tokens"))
Expand Down Expand Up @@ -1032,4 +1035,3 @@
(ok staking-contract)
)
)

114 changes: 111 additions & 3 deletions tests/stableswap_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,32 @@ Clarinet.test({
},
});

// Test swap x for y accepts an exact min-y bound
Clarinet.test({
name: "Ensure standard swap x-token for y-token accepts exact min-y amount",
async fn(chain: Chain, accounts: Map<string, Account>) {
const deployer = accounts.get("deployer")!;

chain.mineBlock([
Tx.contractCall("usda-token", "mint", [types.uint(100000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("susdt-token", "mint", [types.uint(10000000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("stableswap", "create-pair", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(100), types.ascii("test"), types.uint(1000000000000000), types.uint(10000000000000)], deployer.address)
]);

const block = chain.mineBlock([
Tx.contractCall("stableswap", "swap-x-for-y", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000), types.uint(99949981)], deployer.address)
]);

block.receipts[0].result.expectOk().expectUint(99949981)
},
});

// Test swap x for y
Clarinet.test({
name: "Ensure we can swap x-token for y-token get pair data back",
Expand Down Expand Up @@ -610,6 +636,32 @@ Clarinet.test({
},
});

// Test swap y for x accepts an exact min-x bound
Clarinet.test({
name: "Ensure standard swap y-token for x-token accepts exact min-x amount",
async fn(chain: Chain, accounts: Map<string, Account>) {
const deployer = accounts.get("deployer")!;

chain.mineBlock([
Tx.contractCall("usda-token", "mint", [types.uint(100000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("susdt-token", "mint", [types.uint(10000000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("stableswap", "create-pair", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(100), types.ascii("test"), types.uint(1000000000000000), types.uint(10000000000000)], deployer.address)
]);

const block = chain.mineBlock([
Tx.contractCall("stableswap", "swap-y-for-x", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(100000000), types.uint(9994998022)], deployer.address)
]);

block.receipts[0].result.expectOk().expectUint(9994998022)
},
});

// Test swap y for x
Clarinet.test({
name: "Ensure we can swap y-token for x-token get pair data back",
Expand Down Expand Up @@ -1274,6 +1326,32 @@ Clarinet.test({
},
});

// Test add liquidity accepts an exact min-lp bound
Clarinet.test({
name: "Ensure standard add liquidity accepts exact min-lp amount",
async fn(chain: Chain, accounts: Map<string, Account>) {
const deployer = accounts.get("deployer")!;

chain.mineBlock([
Tx.contractCall("usda-token", "mint", [types.uint(500000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("susdt-token", "mint", [types.uint(50000000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("stableswap", "create-pair", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(100), types.ascii("test"), types.uint(10000000000000000), types.uint(100000000000000)], deployer.address)
]);

const block = chain.mineBlock([
Tx.contractCall("stableswap", "add-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(100000000), types.uint(1000000), types.uint(200000000)], deployer.address)
]);

block.receipts[0].result.expectOk().expectUint(200000000)
},
});

// Test add liquidity
Clarinet.test({
name: "Ensure we can add liquidity of one token to a pool with same previous balance small amount",
Expand Down Expand Up @@ -1577,6 +1655,36 @@ Clarinet.test({
},
});

// Test remove liquidity accepts exact min token bounds
Clarinet.test({
name: "Ensure standard withdraw liquidity accepts exact min token amounts",
async fn(chain: Chain, accounts: Map<string, Account>) {
const deployer = accounts.get("deployer")!;

chain.mineBlock([
Tx.contractCall("usda-token", "mint", [types.uint(500000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("susdt-token", "mint", [types.uint(50000000000000000), types.principal(deployer.address)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("stableswap", "create-pair", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(100), types.ascii("test"), types.uint(10000000000000000), types.uint(100000000000000)], deployer.address)
]);

chain.mineBlock([
Tx.contractCall("stableswap", "add-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000000000), types.uint(100000000000000), types.uint(0)], deployer.address)
]);

const block = chain.mineBlock([
Tx.contractCall("stableswap", "withdraw-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000000), types.uint(5000000000000), types.uint(50000000000)], deployer.address)
]);

block.receipts[0].result.expectOk().expectTuple()
},
});

// Test remove liquidity
Clarinet.test({
name: "Ensure we can NOT withdraw liquidity from a pool if not min x tokens met",
Expand All @@ -1601,7 +1709,7 @@ Clarinet.test({
]);

const block = chain.mineBlock([
Tx.contractCall("stableswap", "withdraw-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000000), types.uint(5000000000000), types.uint(50000000000)], deployer.address)
Tx.contractCall("stableswap", "withdraw-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000000), types.uint(5000000000001), types.uint(0)], deployer.address)
]);

block.receipts[0].result.expectErr()
Expand Down Expand Up @@ -1633,7 +1741,7 @@ Clarinet.test({
]);

const block = chain.mineBlock([
Tx.contractCall("stableswap", "withdraw-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000000), types.uint(5000000000000), types.uint(50000000000)], deployer.address)
Tx.contractCall("stableswap", "withdraw-liquidity", [types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.susdt-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token"), types.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-susdt-lp-token"), types.uint(10000000000000), types.uint(0), types.uint(50000000001)], deployer.address)
]);

block.receipts[0].result.expectErr()
Expand Down Expand Up @@ -1675,4 +1783,4 @@ Clarinet.test({
block.receipts[0].result.expectErr()
console.log(JSON.stringify(block.receipts));
},
});
});