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
2 changes: 1 addition & 1 deletion contracts/borrower-v1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
(lp-open-interest-without-principal (contract-call? .math-v1 safe-sub (get lp-open-interest interest-params) total-borrowed-amount))
(lp-part (contract-call? .math-v1 safe-div (* interest-part lp-open-interest-without-principal) open-interest-without-principal))
(protocol-part (contract-call? .math-v1 safe-div (* interest-part (get protocol-open-interest interest-params)) open-interest-without-principal))
(staked-part (contract-call? .math-v1 safe-div (* interest-part (get staked-open-interest interest-params)) open-interest-without-principal))
(staked-part (contract-call? .math-v1 safe-sub interest-part (+ lp-part protocol-part)))
(asset-params (contract-call? .state-v1 get-lp-params))
(staked-lp-tokens (contract-call? .math-v1 convert-to-shares asset-params staked-part false))
(total-user-debt-shares (unwrap! (contract-call? .math-v1 sub (get debt-shares position) shares) ERR-NOT-ENOUGH-SHARES))
Expand Down
25 changes: 16 additions & 9 deletions contracts/liquidator-v1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
(define-constant PRICE-SCALING-FACTOR (contract-call? .constants-v2 get-price-scaling-factor))
;; Must have the same precision as SCALING-FACTOR
(define-constant MINIMUM_HEALTH_RATIO u100000000)
;; Liquidation buffer of 0.50%
(define-constant LIQUIDATION-BUFFER u500000)
;; Liquidation buffer of 2.00%
(define-constant LIQUIDATION-BUFFER u2000000)

;; ERROR VALUES
(define-constant ERR-DIVIDE-BY-ZERO (err u30000))
Expand All @@ -25,8 +25,9 @@
(define-constant ERR-INVALID-ORACLE-PRICE (err u30008))
(define-constant ERR-MISSING-MARKET-PRICE (err u30009))
(define-constant ERR-NON-ZERO-REPAY-AMOUNT (err u30010))
(define-constant ERR-LIQUIDATION-NOT-ALLOWED (err u30011))

;; PUBLIC FUNCTIONS
;; PUBLIC FUNCTIONS
(define-public (batch-liquidate (pyth-price-feed-data (optional (buff 8192))) (collateral <token-trait>) (batch (list 20 (optional {
user: principal,
liquidator-repay-amount: uint,
Expand Down Expand Up @@ -251,7 +252,10 @@
(define-private (execute-liquidation (user principal) (collateral <token-trait>) (liquidator-repay-amount uint) (min-collateral-expected uint))
(let
(
(collateral-token (contract-of collateral))
(collateral-token (contract-of collateral))
;; L-36: enforce minimum 6-block gap between borrowing and liquidation
(user-position-data (contract-call? .state-v1 get-borrow-repay-params user))
(position-for-block-check (unwrap! (get user-position user-position-data) ERR-NO-POSITION))
;; get liquidation info for the user
(liquidation-res (try! (get-liquidation-info user collateral liquidator-repay-amount none none none none)))
(liquidation-info (get liquidation-info liquidation-res))
Expand Down Expand Up @@ -283,7 +287,7 @@
;; calculate liquidity provider and protocol debt repaid
(lp-part (contract-call? .math-v1 safe-div (* interest-part lp-open-interest-without-principal) open-interest-without-principal))
(protocol-part (contract-call? .math-v1 safe-div (* interest-part (get protocol-open-interest open-interest-info)) open-interest-without-principal))
(staked-part (contract-call? .math-v1 safe-div (* interest-part (get staked-open-interest open-interest-info)) open-interest-without-principal))
(staked-part (contract-call? .math-v1 safe-sub interest-part (+ lp-part protocol-part)))
(asset-params (contract-call? .state-v1 get-lp-params))
(staked-lp-tokens (contract-call? .math-v1 convert-to-shares asset-params staked-part false))
(updated-borrowed-amount (contract-call? .math-v1 safe-sub user-borrowed-amount principal-part))
Expand All @@ -294,6 +298,8 @@
(get collaterals position-data)
))
)
;; L-36: require at least 6 blocks (~1 min) between borrowing and liquidation
(asserts! (>= stacks-block-height (+ (get borrowed-block position-for-block-check) u6)) ERR-LIQUIDATION-NOT-ALLOWED)
(try! (ensure-non-zero-repay-amount liquidator-repay-amount collateral-price))
;; update state
(try! (contract-call? .state-v1 update-liquidate-collateral-state collateral {
Expand Down Expand Up @@ -369,7 +375,7 @@
)
(let
(
(denominator (-
(denominator (contract-call? .math-v1 safe-sub
SCALING-FACTOR
(try! (safe-div (* (+ SCALING-FACTOR liquidation-discount) collateral-liquid-ltv) SCALING-FACTOR))
))
Expand Down Expand Up @@ -532,9 +538,10 @@
(interest-part (get interest-part interest-portion))
(open-interest-without-principal (contract-call? .math-v1 safe-sub total-open-interest total-borrowed-amount))
(lp-open-interest-without-principal (contract-call? .math-v1 safe-sub lp-open-interest-val total-borrowed-amount))
(lp-part (+ principal-part (contract-call? .math-v1 safe-div (* interest-part lp-open-interest-without-principal) open-interest-without-principal)))
(lp-interest-part (contract-call? .math-v1 safe-div (* interest-part lp-open-interest-without-principal) open-interest-without-principal))
(protocol-part (contract-call? .math-v1 safe-div (* interest-part protocol-open-interest-val) open-interest-without-principal))
(staked-part (contract-call? .math-v1 safe-div (* interest-part staked-open-interest-val) open-interest-without-principal))
(staked-part (contract-call? .math-v1 safe-sub interest-part (+ lp-interest-part protocol-part)))
(lp-part (+ principal-part lp-interest-part))
(updated-total-borrowed-amount (contract-call? .math-v1 safe-sub total-borrowed-amount principal-part))
(staked-lp-tokens (contract-call? .staking-v1 get-total-staked-lp-tokens))
(burned-staking-lp-tokens (try! (contract-call? .state-v1 socialize-user-bad-debt user remaining-debt lp-part staked-part protocol-part updated-total-borrowed-amount .staking-v1 staked-lp-tokens)))
Expand All @@ -549,7 +556,7 @@
updated-total-borrowed-amount: updated-total-borrowed-amount,
burned-staking-lp-tokens: burned-staking-lp-tokens
})
(asserts! (<= burned-staking-lp-tokens u0) (contract-call? .staking-v1 slash-total-staked-lp-tokens burned-staking-lp-tokens))
(if (> burned-staking-lp-tokens u0) (try! (contract-call? .staking-v1 slash-total-staked-lp-tokens burned-staking-lp-tokens)) true)
SUCCESS
))
)
Expand Down
2 changes: 2 additions & 0 deletions contracts/lp-incentives-v2.clar
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
(define-constant ERR-USER-REWARDS-CLAIMED (err u100009))
(define-constant ERR-REWARDS-NOT-CLAIMED (err u100010))
(define-constant ERR-INVALID-SNAPSHOT-TIME (err u100011))
(define-constant ERR-NOT-AUTHORIZED (err u100012))

;; Read-only functions
(define-read-only (get-epoch-details)
Expand Down Expand Up @@ -121,6 +122,7 @@
(define-public (claim-rewards (on-behalf-of (optional principal)))
(let (
(user (default-to contract-caller on-behalf-of))
(auth-check (asserts! (or (is-eq contract-caller user) (is-none on-behalf-of)) ERR-NOT-AUTHORIZED))
(rewards (unwrap! (map-get? user-rewards user) ERR-NO-USER-REWARDS))
(reward-amount (get earned-rewards rewards))
)
Expand Down
16 changes: 13 additions & 3 deletions contracts/modules/linear-kinked-ir-v1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
(define-constant ERR-NOT-INITIALIZED (err u70002))
(define-constant ERR-NOT-GOVERNANCE (err u70003))
(define-constant ERR-INVALID-UTILIZATION-KINK (err u70004))
(define-constant ERR-INVALID-IR-PARAMS (err u70005))
(define-constant ERR-TAYLOR-INPUT-TOO-LARGE (err u70006))

;; CONSTANTS
(define-constant one-8 u100000000)
Expand All @@ -20,6 +22,8 @@
(define-constant fact_5 u120000000000000)
(define-constant fact_6 u720000000000000)
(define-constant seconds-in-year u31536000)
(define-constant MAX-IR-PARAM u100000000000000)
(define-constant MAX-TAYLOR-INPUT u2000000000000)
(define-constant STACKS_BLOCK_TIME (contract-call? .constants-v1 get-stacks-block-time ))

;; DATA-VARS
Expand Down Expand Up @@ -47,6 +51,9 @@
)

(asserts! (< utilization-kink-val one-12) ERR-INVALID-UTILIZATION-KINK)
(asserts! (<= ir-slope-1-val MAX-IR-PARAM) ERR-INVALID-IR-PARAMS)
(asserts! (<= ir-slope-2-val MAX-IR-PARAM) ERR-INVALID-IR-PARAMS)
(asserts! (<= base-ir-val MAX-IR-PARAM) ERR-INVALID-IR-PARAMS)
(print {
old-ir-slope-1: (var-get ir-slope-1),
new-ir-slope-1: ir-slope-1-val,
Expand Down Expand Up @@ -126,7 +133,10 @@
(define-read-only (compounded-interest (current-interest-rate uint) (elapsed-block-time uint))
(begin
(asserts! (var-get is-initialized) ERR-NOT-INITIALIZED)
(ok (taylor-6 (get-rt-by-block current-interest-rate elapsed-block-time)))
(let ((rt (get-rt-by-block current-interest-rate elapsed-block-time)))
(asserts! (<= rt MAX-TAYLOR-INPUT) ERR-TAYLOR-INPUT-TOO-LARGE)
(ok (taylor-6 rt))
)
))

(define-read-only (get-ir-params)
Expand Down Expand Up @@ -171,7 +181,7 @@
))

(define-private (mul (x uint) (y uint))
(/ (+ (* x y) (/ one-12 u2)) one-12)
(/ (* x y) one-12)
)

(define-private (div (x uint) (y uint))
Expand All @@ -181,7 +191,7 @@
;; rate in 12-fixed
;; n-blocks
(define-read-only (get-rt-by-block (rate uint) (elapsed-block-time uint))
(/ (* rate (/ (* elapsed-block-time one-12) seconds-in-year)) one-12)
(/ (* rate elapsed-block-time) seconds-in-year)
)

;; taylor series expansion to the 6th degree to estimate e^x
Expand Down
11 changes: 6 additions & 5 deletions contracts/modules/withdrawal-caps-v1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
(define-constant ERR-INVALID-CAP-FACTOR (err u120005))
(define-constant ERR-NOT-AUTHORIZED (err u120006))
(define-constant ERR-SYNC-FAILED (err u120007))
(define-constant ERR-BLOCK-INFO (err u120008))

;; VARIABLES

Expand Down Expand Up @@ -67,7 +68,7 @@
;; PRIVATE FUNCTIONS

(define-private (get-time-now)
(unwrap-panic (get-stacks-block-info? time (- stacks-block-height u1)))
(ok (unwrap! (get-stacks-block-info? time (- stacks-block-height u1)) ERR-BLOCK-INFO))
)

(define-private (refill-bucket-amount (last-updated-at uint) (time-now uint) (max-bucket uint) (current-bucket uint) (inflow uint))
Expand All @@ -84,7 +85,7 @@
(let (
(extra-bucket-amount (- current-bucket max-bucket))
(decay-window (get-decay-time-window))
(elapsed (if (is-eq last-updated-at u0) decay-window (- time-now last-updated-at)))
(elapsed (if (is-eq last-updated-at u0) decay-window (if (> time-now last-updated-at) (- time-now last-updated-at) u0)))
(decayed-amount (if (>= elapsed decay-window) extra-bucket-amount (/ (* extra-bucket-amount elapsed) decay-window)))
(new-bucket (- current-bucket decayed-amount))
)
Expand All @@ -94,7 +95,7 @@
(define-private (sync-lp-bucket (inflow uint))
(let
(
(time-now (get-time-now))
(time-now (try! (get-time-now)))
(last-ts (var-get last-lp-bucket-update))
(total-liquidity (unwrap! (contract-call? .mock-usdc get-balance .state-v1) ERR-FAILED-TO-GET-BALANCE))
(max-lp-bucket (/ (* total-liquidity (var-get lp-cap-factor)) SCALING-FACTOR))
Expand All @@ -120,7 +121,7 @@
(define-private (sync-debt-bucket (inflow uint))
(let
(
(time-now (get-time-now))
(time-now (try! (get-time-now)))
(last-ts (var-get last-debt-bucket-update))
(total-liquidity (contract-call? .state-v1 get-borrowable-balance))
(max-debt-bucket (/ (* total-liquidity (var-get debt-cap-factor)) SCALING-FACTOR))
Expand All @@ -145,7 +146,7 @@
(define-private (sync-collateral-bucket (collateral <token-trait>) (inflow uint))
(let
(
(time-now (get-time-now))
(time-now (try! (get-time-now)))
(collateral-token (contract-of collateral))
(last-ts (default-to u0 (map-get? last-collateral-bucket-update collateral-token)))
(total-liquidity (unwrap! (contract-call? collateral get-balance .state-v1) ERR-FAILED-TO-GET-BALANCE))
Expand Down
2 changes: 1 addition & 1 deletion contracts/staking-v1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
(active-staked-lp-tokens-to-slash (- lp-tokens withdrawal-lp-tokens-to-slash))
)
(try! (contract-call? .state-v1 is-allowed-contract contract-caller))
(var-set total-lp-tokens-staked (- (var-get total-lp-tokens-staked) active-staked-lp-tokens-to-slash))
(var-set total-lp-tokens-staked (contract-call? .math-v1 safe-sub (var-get total-lp-tokens-staked) active-staked-lp-tokens-to-slash))
(if (is-eq withdrawal-lp-tokens withdrawal-lp-tokens-to-slash)
(var-set unfinalized-withdrawals {
lp-tokens: u0,
Expand Down
10 changes: 10 additions & 0 deletions tests/borrower_flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,11 @@ describe("Borrower User flow tests", () => {
);
expect(depositorBalance.result.value.value).toBe(5000n);

simnet.mineEmptyBlocks(6);
// refresh prices after mining blocks
await set_price("mock-usdc", 1n, deployer);
await set_price("mock-btc", 10n, deployer);
await set_price("mock-eth", 65n, deployer);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -449,6 +454,11 @@ describe("Borrower User flow tests", () => {
);
expect(userDebtShares.result.value.data["debt-shares"].value).toEqual(796n);

simnet.mineEmptyBlocks(6);
// refresh prices after mining blocks
await set_price("mock-usdc", 1n, deployer);
await set_price("mock-btc", 10n, deployer);
await set_price("mock-eth", 65n, deployer);
liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down
18 changes: 15 additions & 3 deletions tests/liquidation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 18181818181, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -457,6 +458,7 @@ describe("liquidation tests", () => {
borrower1
).result.value.value;

simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -552,7 +554,7 @@ describe("liquidation tests", () => {
let length = block.length;
block.forEach((txn, index) => {
if (index == length - 1) {
expect(txn.result).toBeErr(Cl.uint(113)); // ERR-LIQUIDATION-NOT-ALLOWED
expect(txn.result).toBeErr(Cl.uint(30011)); // ERR-LIQUIDATION-TIME-LOCK
} else {
expect(txn.result).toBeOk(Cl.bool(true));
}
Expand Down Expand Up @@ -627,6 +629,7 @@ describe("liquidation tests", () => {

mint_token("mock-usdc", 17777777777, depositor);

simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -759,6 +762,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 18181818181, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -841,6 +845,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 39000013190, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -978,6 +983,7 @@ describe("liquidation tests", () => {
liquidateData2,
...new Array(18).fill(Cl.none()),
];
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"batch-liquidate",
Expand Down Expand Up @@ -1077,6 +1083,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 30000000000000, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand All @@ -1098,7 +1105,7 @@ describe("liquidation tests", () => {
deployer
);
expect(accounthealthRes.result.value.data["position-health"]).toEqual(
Cl.uint(100001972n)
Cl.uint(100001973n)
);
});

Expand Down Expand Up @@ -1152,6 +1159,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 18181818181, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand All @@ -1173,7 +1181,7 @@ describe("liquidation tests", () => {
deployer
);
expect(accounthealthRes.result.value.data["position-health"]).toEqual(
Cl.uint(100000002n)
Cl.uint(100000000n)
);
});

Expand Down Expand Up @@ -1227,6 +1235,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 18181818181, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -1302,6 +1311,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 18181818181, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -1377,6 +1387,7 @@ describe("liquidation tests", () => {
);

mint_token("mock-usdc", 18181818181, depositor);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"liquidator-v1",
"liquidate-collateral",
Expand Down Expand Up @@ -1492,6 +1503,7 @@ describe("liquidation tests", () => {
);

state_set_governance_contract(deployer);
simnet.mineEmptyBlocks(6);
let liquidate = simnet.callPublicFn(
"mock-liquidator-with-flash-loan",
"liquidate-collateral",
Expand Down
Loading
Loading