From 8554455edc7962940220d0bd9b52e2d82ade5512 Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:38:40 +0100 Subject: [PATCH 01/11] Add constants and error codes to Yield Aggregator contract - Implemented SIP-010 token standard for token interactions. - Added constants for contract name and various error codes. - Included error handling for authorization, invalid amounts, insufficient balance, strategy management, and more. --- Clarinet.toml | 22 +++++++++++--------- contracts/yield-aggregator.clar | 36 +++++++++++++++++++++++++++++++++ tests/yield-aggregator_test.ts | 26 ++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 contracts/yield-aggregator.clar create mode 100644 tests/yield-aggregator_test.ts diff --git a/Clarinet.toml b/Clarinet.toml index 7bd1cc1..2ae4533 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -1,20 +1,22 @@ - [project] name = "yield-aggregator-protocol" authors = [] +description = "" telemetry = true +requirements = [] +[contracts.yield-aggregator] +path = "contracts/yield-aggregator.clar" +depends_on = [] + +[repl] +costs_version = 2 +parser_version = 2 + [repl.analysis] passes = ["check_checker"] + [repl.analysis.check_checker] -# If true, inputs are trusted after tx_sender has been checked. +strict = false trusted_sender = false -# If true, inputs are trusted after contract-caller has been checked. trusted_caller = false -# If true, untrusted data may be passed into a private function without a -# warning, if it gets checked inside. This check will also propagate up to the -# caller. callee_filter = false - -# [contracts.counter] -# path = "contracts/counter.clar" -# depends_on = [] diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar new file mode 100644 index 0000000..061e09e --- /dev/null +++ b/contracts/yield-aggregator.clar @@ -0,0 +1,36 @@ +;; Yield Aggregator Smart Contract + +;; Description: + +;; The Yield Aggregator smart contract is designed to optimize the yield on deposited tokens by allocating them to various strategies. +;; It implements the SIP-010 token standard for token interactions and provides functionalities for depositing and withdrawing tokens, +;; managing strategies, and calculating the best strategy for maximizing yield. +;; The contract includes robust error handling, administrative controls, and read-only functions for querying contract state. +;; Key features include emergency shutdown, performance and management fees, and strategy allocation management. + +;; Define contract name +(use-trait sip-010-token .sip-010-trait.sip-010-token) + +;; Define contract name +(impl-trait .sip-010-trait.sip-010-token) + +;; Constants and Error Codes +(define-constant contract-name "yield-aggregator") +(define-constant ERR-NOT-AUTHORIZED (err u100)) +(define-constant ERR-INVALID-AMOUNT (err u101)) +(define-constant ERR-INSUFFICIENT-BALANCE (err u102)) +(define-constant ERR-STRATEGY-EXISTS (err u103)) +(define-constant ERR-STRATEGY-NOT-FOUND (err u104)) +(define-constant ERR-STRATEGY-DISABLED (err u105)) +(define-constant ERR-MAX-STRATEGIES-REACHED (err u106)) +(define-constant ERR-SLIPPAGE-TOO-HIGH (err u107)) +(define-constant ERR-EMERGENCY-SHUTDOWN (err u108)) +(define-constant ERR-TOKEN-NOT-SET (err u109)) +(define-constant ERR-INVALID-TOKEN (err u110)) +(define-constant ERR-INVALID-NAME (err u111)) +(define-constant ERR-INVALID-PROTOCOL (err u112)) +(define-constant ERR-INVALID-DEPOSIT-RANGE (err u113)) +(define-constant ERR-INVALID-MIN-DEPOSIT (err u114)) +(define-constant ERR-INVALID-STRATEGY-ID (err u115)) +(define-constant ERR-NOT-CONTRACT (err u116)) +(define-constant ERR-REENTRANCY (err u117)) \ No newline at end of file diff --git a/tests/yield-aggregator_test.ts b/tests/yield-aggregator_test.ts new file mode 100644 index 0000000..9a18ae0 --- /dev/null +++ b/tests/yield-aggregator_test.ts @@ -0,0 +1,26 @@ + +import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v0.14.0/index.ts'; +import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts'; + +Clarinet.test({ + name: "Ensure that <...>", + async fn(chain: Chain, accounts: Map) { + let block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 3); + }, +}); From ab44ad65bbaa003767501f2d8f67762b1892124b Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:40:15 +0100 Subject: [PATCH 02/11] Add data variables and maps to Yield Aggregator contract - Defined data variables for contract owner, emergency shutdown, TVL, fees, max strategies, and token contract. - Added data maps for strategies, user deposits, and strategy allocations. --- contracts/yield-aggregator.clar | 43 ++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index 061e09e..74ab48b 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -33,4 +33,45 @@ (define-constant ERR-INVALID-MIN-DEPOSIT (err u114)) (define-constant ERR-INVALID-STRATEGY-ID (err u115)) (define-constant ERR-NOT-CONTRACT (err u116)) -(define-constant ERR-REENTRANCY (err u117)) \ No newline at end of file +(define-constant ERR-REENTRANCY (err u117)) + +;; Data Variables +(define-data-var contract-owner principal tx-sender) +(define-data-var emergency-shutdown bool false) +(define-data-var total-value-locked uint u0) +(define-data-var performance-fee uint u200) ;; 2% represented as basis points +(define-data-var management-fee uint u100) ;; 1% represented as basis points +(define-data-var max-strategies uint u10) +(define-data-var token-contract (optional principal) none) + +;; Data Maps +(define-map Strategies + { strategy-id: uint } + { + name: (string-utf8 64), + protocol: (string-utf8 64), + enabled: bool, + tvl: uint, + apy: uint, + risk-score: uint, + last-harvest: uint + } +) + +(define-map UserDeposits + { user: principal } + { + total-deposit: uint, + share-tokens: uint, + last-deposit-block: uint + } +) + +(define-map StrategyAllocations + { strategy-id: uint } + { + allocation-percentage: uint, + min-deposit: uint, + max-deposit: uint + } +) \ No newline at end of file From 48178bc9984eb59f7cc16537cfbd8919684a0d32 Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:41:25 +0100 Subject: [PATCH 03/11] Add strategy type and read-only functions to Yield Aggregator contract - Defined a data variable for strategy type with relevant fields. - Implemented read-only functions to get strategy list, strategy info, user info, total TVL, token contract, best strategy calculation, and active strategies. --- contracts/yield-aggregator.clar | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index 74ab48b..05f3eb1 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -74,4 +74,58 @@ min-deposit: uint, max-deposit: uint } +) + +;; Types +(define-data-var strategy-type + (tuple (strategy-id uint) (enabled bool) (tvl uint) (apy uint) (risk-score uint)) + (tuple + (strategy-id u0) + (enabled false) + (tvl u0) + (apy u0) + (risk-score u0) + ) +) + +;; Read-only Functions +(define-read-only (get-strategy-list) + (list u1 u2 u3 u4 u5 u6 u7 u8 u9 u10) +) + +(define-read-only (get-strategy-info (strategy-id uint)) + (map-get? Strategies { strategy-id: strategy-id }) +) + +(define-read-only (get-user-info (user principal)) + (map-get? UserDeposits { user: user }) +) + +(define-read-only (get-total-tvl) + (var-get total-value-locked) +) + +(define-read-only (get-token-contract) + (var-get token-contract) +) + +(define-read-only (calculate-best-strategy (amount uint)) + (let + ( + (strategies (get-active-strategies)) + (initial-acc (tuple + (best-apy u0) + (best-strategy u0) + )) + ) + (fold calculate-highest-apy strategies initial-acc) + ) +) + +(define-read-only (get-active-strategies) + (let + ((strategy-1 (convert-to-filtered-strategy (unwrap-strategy u1))) + (strategy-2 (convert-to-filtered-strategy (unwrap-strategy u2)))) + (filter is-strategy-active (list strategy-1 strategy-2)) + ) ) \ No newline at end of file From 6076d4f8ec3e3d235b44bcfa5b0a63d25ecea6dd Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:41:54 +0100 Subject: [PATCH 04/11] Implement SIP-010 Fungible Token Standard trait - Defined the `sip-010-token` trait with required functions for token transfer, total supply, balance, name, symbol, decimals, and token URI. --- contracts/sip-010-trait.clar | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 contracts/sip-010-trait.clar diff --git a/contracts/sip-010-trait.clar b/contracts/sip-010-trait.clar new file mode 100644 index 0000000..7d8ab4a --- /dev/null +++ b/contracts/sip-010-trait.clar @@ -0,0 +1,25 @@ +;; SIP-010 Fungible Token Standard +(define-trait sip-010-token + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; Returns the total number of tokens + (get-total-supply () (response uint uint)) + + ;; Returns the token balance of the specified principal + (get-balance (principal) (response uint uint)) + + ;; Returns the token name + (get-name () (response (string-ascii 32) uint)) + + ;; Returns the token symbol + (get-symbol () (response (string-ascii 32) uint)) + + ;; Returns the number of decimals used + (get-decimals () (response uint uint)) + + ;; Returns the URI containing token metadata + (get-token-uri () (response (optional (string-utf8 256)) uint)) + ) +) \ No newline at end of file From 1d3976072f3801085c30f68d2552f2cc11dde1ca Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:42:56 +0100 Subject: [PATCH 05/11] Add private functions for strategy management in Yield Aggregator contract - Implemented `convert-to-filtered-strategy` to filter strategy details. - Added `is-strategy-active` to check if a strategy is active based on its APY. - Created `calculate-highest-apy` to determine the strategy with the highest APY. --- contracts/yield-aggregator.clar | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index 05f3eb1..7a4580d 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -128,4 +128,50 @@ (strategy-2 (convert-to-filtered-strategy (unwrap-strategy u2)))) (filter is-strategy-active (list strategy-1 strategy-2)) ) +) + +;; Private Functions +(define-private (convert-to-filtered-strategy (strategy { + strategy-id: uint, + name: (string-utf8 64), + protocol: (string-utf8 64), + enabled: bool, + tvl: uint, + apy: uint, + risk-score: uint, + last-harvest: uint + })) + (tuple + (strategy-id (get strategy-id strategy)) + (enabled (get enabled strategy)) + (tvl (get tvl strategy)) + (apy (get apy strategy)) + (risk-score (get risk-score strategy)) + ) +) + +(define-private (is-strategy-active (strategy { + strategy-id: uint, + enabled: bool, + tvl: uint, + apy: uint, + risk-score: uint + })) + (and (get enabled strategy) (> (get apy strategy) u0)) +) + +(define-private (calculate-highest-apy + (strategy (tuple (strategy-id uint) (enabled bool) (tvl uint) (apy uint) (risk-score uint))) + (acc (tuple (best-apy uint) (best-strategy uint))) +) + (if (and + (get enabled strategy) + (> (get apy strategy) (get best-apy acc)) + ) + (tuple + (best-apy (get apy strategy)) + (best-strategy (get strategy-id strategy)) + ) + acc + ) ) \ No newline at end of file From 6bfdf1f9c5a8ce0a25a65d80a19f4bf28ecd09e9 Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:43:49 +0100 Subject: [PATCH 06/11] Add deposit and withdraw public functions to Yield Aggregator contract - Implemented `deposit` function to handle token deposits and share calculations. - Added `withdraw` function to manage token withdrawals and share deductions. - Included SIP-010 trait implementation for token transfer, name, symbol, decimals, balance, total supply, and token URI. --- contracts/yield-aggregator.clar | 106 ++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index 7a4580d..69dc796 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -174,4 +174,110 @@ ) acc ) +) + +;; Public Functions +(define-public (deposit (token ) (amount uint)) + (let + ( + (user tx-sender) + (current-deposit (default-to { total-deposit: u0, share-tokens: u0, last-deposit-block: u0 } + (map-get? UserDeposits { user: user }))) + ) + (asserts! (not (var-get emergency-shutdown)) ERR-EMERGENCY-SHUTDOWN) + (asserts! (> amount u0) ERR-INVALID-AMOUNT) + + ;; Transfer tokens to contract + (try! (contract-call? token transfer + amount + tx-sender + (as-contract tx-sender) + none)) + + (let + ( + (new-shares (calculate-shares amount)) + (new-total-deposit (+ (get total-deposit current-deposit) amount)) + ) + (map-set UserDeposits + { user: user } + { + total-deposit: new-total-deposit, + share-tokens: (+ (get share-tokens current-deposit) new-shares), + last-deposit-block: block-height + } + ) + + (var-set total-value-locked (+ (var-get total-value-locked) amount)) + + (try! (allocate-to-best-strategy amount)) + + (ok true) + ) + ) +) + +(define-public (withdraw (token ) (share-amount uint)) + (let + ( + (user tx-sender) + (user-deposit (unwrap! (map-get? UserDeposits { user: user }) ERR-INSUFFICIENT-BALANCE)) + ) + (asserts! (<= share-amount (get share-tokens user-deposit)) ERR-INSUFFICIENT-BALANCE) + + (let + ( + (withdrawal-amount (calculate-withdrawal-amount share-amount)) + (new-shares (- (get share-tokens user-deposit) share-amount)) + ) + (map-set UserDeposits + { user: user } + { + total-deposit: (- (get total-deposit user-deposit) withdrawal-amount), + share-tokens: new-shares, + last-deposit-block: (get last-deposit-block user-deposit) + } + ) + + (var-set total-value-locked (- (var-get total-value-locked) withdrawal-amount)) + + ;; Transfer tokens back to user + (try! (as-contract (contract-call? token transfer + withdrawal-amount + tx-sender + user + none))) + + (ok withdrawal-amount) + ) + ) +) + +;; SIP-010 Trait Implementation +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (ok true) +) + +(define-read-only (get-name) + (ok "Yield Aggregator Token") +) + +(define-read-only (get-symbol) + (ok "YAT") +) + +(define-read-only (get-decimals) + (ok u6) +) + +(define-read-only (get-balance (who principal)) + (ok u0) +) + +(define-read-only (get-total-supply) + (ok u0) +) + +(define-read-only (get-token-uri) + (ok none) ) \ No newline at end of file From 8e414dcba224c40274e32c6772f88e75f34308f9 Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:44:27 +0100 Subject: [PATCH 07/11] Add admin functions to Yield Aggregator contract - Implemented `add-strategy` to add new strategies with specified parameters. - Added `update-strategy-apy` to update the APY of a strategy. - Created `toggle-emergency-shutdown` to enable or disable emergency shutdown. - Included `set-token-contract` to set the token contract address. --- contracts/yield-aggregator.clar | 69 +++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index 69dc796..d16f04d 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -280,4 +280,73 @@ (define-read-only (get-token-uri) (ok none) +) + +;; Admin Functions +(define-public (add-strategy (name (string-utf8 64)) (protocol (string-utf8 64)) (min-deposit uint) (max-deposit uint)) + (let + ( + (strategy-count (len (get-strategy-list))) + ) + (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED) + (asserts! (< strategy-count (var-get max-strategies)) ERR-MAX-STRATEGIES-REACHED) + + (map-set Strategies + { strategy-id: (+ strategy-count u1) } + { + name: name, + protocol: protocol, + enabled: true, + tvl: u0, + apy: u0, + risk-score: u0, + last-harvest: block-height + } + ) + + (map-set StrategyAllocations + { strategy-id: (+ strategy-count u1) } + { + allocation-percentage: u0, + min-deposit: min-deposit, + max-deposit: max-deposit + } + ) + + (ok true) + ) +) + +(define-public (update-strategy-apy (strategy-id uint) (new-apy uint)) + (let + ((strategy (map-get? Strategies { strategy-id: strategy-id }))) + (begin + (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED) + ;; Check if strategy exists + (asserts! (is-some strategy) ERR-STRATEGY-NOT-FOUND) + + (map-set Strategies + { strategy-id: strategy-id } + (merge (unwrap! strategy ERR-STRATEGY-NOT-FOUND) + { apy: new-apy }) + ) + (ok true) + ) + ) +) + +(define-public (toggle-emergency-shutdown) + (begin + (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED) + (var-set emergency-shutdown (not (var-get emergency-shutdown))) + (ok true) + ) +) + +(define-public (set-token-contract (new-token principal)) + (begin + (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED) + (var-set token-contract (some new-token)) + (ok true) + ) ) \ No newline at end of file From 5263ee02c8b79f3f4bd65e47578728858b5dbc70 Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:45:25 +0100 Subject: [PATCH 08/11] Add helper functions to Yield Aggregator contract - Implemented `is-contract-owner` to check if the sender is the contract owner. - Added `calculate-shares` to calculate the number of shares for a given amount. - Created `calculate-withdrawal-amount` to determine the withdrawal amount based on shares. - Included `allocate-to-best-strategy` to allocate funds to the best strategy. - Added `reallocate-funds` to reallocate funds to a specific strategy. --- Clarinet.toml | 6 +++- contracts/yield-aggregator.clar | 61 +++++++++++++++++++++++++++++++++ tests/sip-010-trait_test.ts | 26 ++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/sip-010-trait_test.ts diff --git a/Clarinet.toml b/Clarinet.toml index 2ae4533..a2f6e23 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -4,9 +4,13 @@ authors = [] description = "" telemetry = true requirements = [] +[contracts.sip-010-trait] +path = "contracts/sip-010-trait.clar" +depends_on = [] + [contracts.yield-aggregator] path = "contracts/yield-aggregator.clar" -depends_on = [] +depends_on = ["sip-010-trait"] [repl] costs_version = 2 diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index d16f04d..bd2a2fe 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -347,6 +347,67 @@ (begin (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED) (var-set token-contract (some new-token)) + (ok true) + ) +) + +;; Helper Functions +(define-private (is-contract-owner) + (is-eq tx-sender (var-get contract-owner)) +) + +(define-private (calculate-shares (amount uint)) + (let + ( + (total-supply (var-get total-value-locked)) + ) + (if (is-eq total-supply u0) + amount + (/ (* amount u1000000) total-supply) + ) + ) +) + +(define-private (calculate-withdrawal-amount (share-amount uint)) + (let + ( + (total-shares (var-get total-value-locked)) + ) + (/ (* share-amount (var-get total-value-locked)) u1000000) + ) +) + +(define-private (allocate-to-best-strategy (amount uint)) + (let + ( + (best-strategy (calculate-best-strategy amount)) + ) + (if (is-eq (get best-strategy best-strategy) u0) + (ok true) ;; Return (response bool) when no strategy is found + (begin + (try! (reallocate-funds (get best-strategy best-strategy) amount)) + (ok true) ;; Return (response bool) after successful reallocation + ) + ) + ) +) + +(define-private (reallocate-funds (strategy-id uint) (amount uint)) + (let + ( + (strategy (unwrap! (map-get? Strategies { strategy-id: strategy-id }) ERR-STRATEGY-NOT-FOUND)) + (allocation (unwrap! (map-get? StrategyAllocations { strategy-id: strategy-id }) ERR-STRATEGY-NOT-FOUND)) + ) + (asserts! (get enabled strategy) ERR-STRATEGY-DISABLED) + (asserts! (>= amount (get min-deposit allocation)) ERR-INVALID-AMOUNT) + (asserts! (<= amount (get max-deposit allocation)) ERR-INVALID-AMOUNT) + + ;; Update strategy TVL + (map-set Strategies + { strategy-id: strategy-id } + (merge strategy { tvl: (+ (get tvl strategy) amount) }) + ) + (ok true) ) ) \ No newline at end of file diff --git a/tests/sip-010-trait_test.ts b/tests/sip-010-trait_test.ts new file mode 100644 index 0000000..9a18ae0 --- /dev/null +++ b/tests/sip-010-trait_test.ts @@ -0,0 +1,26 @@ + +import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v0.14.0/index.ts'; +import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts'; + +Clarinet.test({ + name: "Ensure that <...>", + async fn(chain: Chain, accounts: Map) { + let block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 3); + }, +}); From 0d79d02cc16a48caa93e232d7418e2dfc788bb8e Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:46:15 +0100 Subject: [PATCH 09/11] Add helper functions for strategy validation and token verification - Implemented `unwrap-strategy` to convert strategy ID to full strategy info. - Added `verify-token` to check if the token is allowed. - Created `validate-strategy-params` to validate strategy parameters. - Included `validate-strategy-id` to validate the strategy ID. --- contracts/yield-aggregator.clar | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/contracts/yield-aggregator.clar b/contracts/yield-aggregator.clar index bd2a2fe..9e131d4 100644 --- a/contracts/yield-aggregator.clar +++ b/contracts/yield-aggregator.clar @@ -410,4 +410,69 @@ (ok true) ) +) + +;; Convert strategy ID to full strategy info +(define-private (unwrap-strategy (strategy-id uint)) + (let + ( + (strategy (map-get? Strategies { strategy-id: strategy-id })) + ) + (if (is-some strategy) + (let + ( + (strategy-info (unwrap-panic strategy)) + ) + { + strategy-id: strategy-id, + name: (get name strategy-info), + protocol: (get protocol strategy-info), + enabled: (get enabled strategy-info), + tvl: (get tvl strategy-info), + apy: (get apy strategy-info), + risk-score: (get risk-score strategy-info), + last-harvest: (get last-harvest strategy-info) + } + ) + { + strategy-id: strategy-id, + name: u"default-name-utf8", + protocol: u"default-protocol-utf8", + enabled: false, + tvl: u0, + apy: u0, + risk-score: u0, + last-harvest: u0 + } + ) + ) +) + +(define-private (verify-token (token )) + (let ((allowed-token (unwrap! (var-get token-contract) ERR-TOKEN-NOT-SET))) + (asserts! (is-eq (contract-of token) allowed-token) ERR-INVALID-TOKEN) + (ok true) + ) +) + +(define-private (validate-strategy-params + (name (string-utf8 64)) + (protocol (string-utf8 64)) + (min-deposit uint) + (max-deposit uint)) + (begin + (asserts! (> (len name) u0) ERR-INVALID-NAME) + (asserts! (> (len protocol) u0) ERR-INVALID-PROTOCOL) + (asserts! (> max-deposit min-deposit) ERR-INVALID-DEPOSIT-RANGE) + (asserts! (> min-deposit u0) ERR-INVALID-MIN-DEPOSIT) + (ok true) + ) +) + +(define-private (validate-strategy-id (strategy-id uint)) + (begin + (asserts! (<= strategy-id (var-get max-strategies)) ERR-INVALID-STRATEGY-ID) + (asserts! (> strategy-id u0) ERR-INVALID-STRATEGY-ID) + (ok true) + ) ) \ No newline at end of file From c0679ce95065291923055f84dea47929b687d95e Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:48:45 +0100 Subject: [PATCH 10/11] Add contributing guidelines, license, and technical documentation - Added contributing guidelines to guide contributors on how to contribute to the project. - Included a license file with the MIT License. - Added technical documentation detailing the contract's key components, functions, and usage. --- CONTRIBUTING.md | 102 ++++++++++++++++++++++++++ LICENSE | 21 ++++++ READMEmd | 131 +++++++++++++++++++++++++++++++++ TECHNICAL.md | 191 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 445 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 READMEmd create mode 100644 TECHNICAL.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..779e743 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,102 @@ +# Contributing to Yield Aggregator + +We welcome contributions to the Yield Aggregator smart contract! This document provides guidelines and instructions for contributing. + +## Development Process + +1. **Fork the Repository** + - Create your own fork of the project + - Set up your local development environment + +2. **Create a Feature Branch** + - Branch naming convention: `feature/description` or `fix/description` + - Keep changes focused and atomic + +3. **Development Guidelines** + + - Follow Clarity best practices + - Maintain consistent code style + - Add comments for complex logic + - Update documentation as needed + +4. **Testing Requirements** + + - Add tests for new features + - Ensure all existing tests pass + - Test edge cases thoroughly + - Document test scenarios + +5. **Code Review Process** + + - Submit detailed pull requests + - Respond to review comments + - Make requested changes promptly + - Ensure CI checks pass + +## Code Style Guidelines + +### Clarity Conventions + +```clarity +;; Use clear, descriptive names +(define-constant MAX-STRATEGIES u10) + +;; Group related functions +;; Strategy management functions +(define-public (add-strategy ...) ...) +(define-public (update-strategy ...) ...) + +;; Add meaningful comments +;; Calculate user's share of the pool based on deposit amount +(define-private (calculate-shares (amount uint)) ...) +``` + +### Documentation Standards + +- Use clear, concise comments +- Document function parameters and return values +- Explain complex calculations +- Update README for significant changes + +## Testing Guidelines + +1. **Unit Tests** + - Test individual functions + - Cover edge cases + - Verify error conditions + +2. **Integration Tests** + - Test interaction between components + - Verify end-to-end workflows + - Test with realistic data + +3. **Security Tests** + - Test access controls + - Verify fund safety + - Check error handling + +## Submitting Changes + +1. **Pull Request Process** + - Create detailed PR description + - Reference related issues + - Include test results + - Update documentation + +2. **Review Process** + - Address review comments + - Make requested changes + - Maintain PR discussion + +3. **Merge Requirements** + - All tests passing + - Documentation updated + - Code review approved + - CI checks successful + +## Getting Help + +- Join our developer community +- Ask questions in issues +- Review existing documentation +- Contact maintainers \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9898aff --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Yield Aggregator + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/READMEmd b/READMEmd new file mode 100644 index 0000000..fd90b43 --- /dev/null +++ b/READMEmd @@ -0,0 +1,131 @@ +# Yield Aggregator Smart Contract + +A sophisticated yield optimization protocol built on Stacks blockchain that automatically allocates deposited tokens across multiple DeFi strategies to maximize returns while managing risk. + +## Features + +- **Automated Yield Optimization**: Dynamically allocates funds to the highest-yielding strategies +- **Multi-Strategy Support**: Supports up to 10 concurrent yield-generating strategies +- **SIP-010 Compliant**: Fully implements the SIP-010 fungible token standard +- **Risk Management**: Built-in risk scoring system and emergency shutdown mechanism +- **Fee Structure**: Configurable performance and management fees +- **User Shares**: Proportional share token system for tracking user deposits + +## Architecture + +### Core Components + +1. **Strategy Management** + + - Dynamic strategy registration + - APY tracking and updates + - Risk scoring system + - TVL monitoring + - Strategy allocation limits + +2. **User Management** + + - Deposit tracking + - Share token calculation + - Withdrawal processing + - Balance management + +3. **Security Features** + - Emergency shutdown + - Access controls + - Reentrancy protection + - Slippage checks + +### Key Data Structures + +- `Strategies`: Stores strategy information and performance metrics +- `UserDeposits`: Tracks user deposits and share tokens +- `StrategyAllocations`: Manages strategy allocation parameters + +## Usage + +### For Users + +```clarity +;; Deposit tokens +(contract-call? .yield-aggregator deposit token-trait amount) + +;; Withdraw tokens +(contract-call? .yield-aggregator withdraw token-trait share-amount) + +;; Query user info +(contract-call? .yield-aggregator get-user-info user-principal) +``` + +### For Administrators + +```clarity +;; Add new strategy +(contract-call? .yield-aggregator add-strategy name protocol min-deposit max-deposit) + +;; Update strategy APY +(contract-call? .yield-aggregator update-strategy-apy strategy-id new-apy) + +;; Toggle emergency shutdown +(contract-call? .yield-aggregator toggle-emergency-shutdown) +``` + +## Error Codes + +| Code | Description | +| ---- | -------------------------- | +| u100 | Not authorized | +| u101 | Invalid amount | +| u102 | Insufficient balance | +| u103 | Strategy already exists | +| u104 | Strategy not found | +| u105 | Strategy disabled | +| u106 | Maximum strategies reached | +| u107 | Slippage too high | +| u108 | Emergency shutdown active | + +## Security Considerations + +1. **Access Control** + + - Contract owner privileges + - Strategy management restrictions + - Emergency shutdown capability + +2. **Fund Safety** + + - Deposit/withdrawal validations + - Balance checks + - Share token calculations + +3. **Risk Management** + - Strategy risk scoring + - Allocation limits + - Emergency procedures + +## Testing + +Comprehensive test coverage is essential. Test cases should include: + +1. Basic functionality + + - Deposits + - Withdrawals + - Share calculations + +2. Strategy management + + - Adding strategies + - Updating APY + - Allocation logic + +3. Security features + + - Access controls + - Emergency shutdown + - Error conditions + +4. Edge cases + - Zero amounts + - Maximum values + - Invalid inputs diff --git a/TECHNICAL.md b/TECHNICAL.md new file mode 100644 index 0000000..d00c3dc --- /dev/null +++ b/TECHNICAL.md @@ -0,0 +1,191 @@ +# Technical Documentation + +## Contract Architecture + +### Core Components + +1. **Token Standard Implementation** + + - Implements SIP-010 fungible token standard + - Provides transfer and token info functions + - Manages token metadata + +2. **Strategy Management** + + ```clarity + (define-map Strategies + { strategy-id: uint } + { + name: (string-utf8 64), + protocol: (string-utf8 64), + enabled: bool, + tvl: uint, + apy: uint, + risk-score: uint, + last-harvest: uint + } + ) + ``` + +3. **User Deposit Tracking** + ```clarity + (define-map UserDeposits + { user: principal } + { + total-deposit: uint, + share-tokens: uint, + last-deposit-block: uint + } + ) + ``` + +### Key Functions + +1. **Deposit Processing** + + ```clarity + (define-public (deposit (token ) (amount uint)) + ``` + + - Validates deposit amount + - Transfers tokens to contract + - Calculates and issues share tokens + - Allocates funds to best strategy + +2. **Withdrawal Processing** + + ```clarity + (define-public (withdraw (token ) (share-amount uint)) + ``` + + - Validates withdrawal amount + - Calculates token amount + - Updates user shares + - Transfers tokens to user + +3. **Strategy Management** + ```clarity + (define-public (add-strategy (name (string-utf8 64)) (protocol (string-utf8 64)) (min-deposit uint) (max-deposit uint)) + ``` + - Adds new yield strategies + - Sets allocation parameters + - Updates strategy metrics + +## Implementation Details + +### Share Token Calculation + +The contract uses a proportional share system: + +```clarity +(define-private (calculate-shares (amount uint)) + (let + ( + (total-supply (var-get total-value-locked)) + ) + (if (is-eq total-supply u0) + amount + (/ (* amount u1000000) total-supply) + ) + ) +) +``` + +### Strategy Selection + +Best strategy selection process: + +```clarity +(define-private (calculate-highest-apy + (strategy (tuple (strategy-id uint) (enabled bool) (tvl uint) (apy uint) (risk-score uint))) + (acc (tuple (best-apy uint) (best-strategy uint))) +) +``` + +### Security Measures + +1. **Access Control** + + - Contract owner validation + - Strategy management restrictions + - Emergency shutdown capability + +2. **Fund Safety** + + - Balance verification + - Share calculation precision + - Withdrawal limits + +3. **Error Handling** + - Comprehensive error codes + - Input validation + - State checks + +## Integration Guide + +### Contract Deployment + +1. Deploy contract with initial parameters: + + - Set contract owner + - Configure fee structure + - Set maximum strategies + +2. Initialize first strategy: + ```clarity + (contract-call? .yield-aggregator add-strategy + "Strategy Name" + "Protocol Name" + min-deposit + max-deposit) + ``` + +### User Integration + +1. Token Approval: + + ```clarity + (contract-call? .token-contract approve + .yield-aggregator + amount) + ``` + +2. Deposit Tokens: + ```clarity + (contract-call? .yield-aggregator deposit + .token-contract + amount) + ``` + +### Administrative Functions + +1. Strategy Management: + + ```clarity + ;; Update APY + (contract-call? .yield-aggregator update-strategy-apy + strategy-id + new-apy) + + ;; Emergency Shutdown + (contract-call? .yield-aggregator toggle-emergency-shutdown) + ``` + +## Performance Considerations + +1. **Gas Optimization** + + - Efficient data structures + - Minimal state changes + - Optimized calculations + +2. **Scalability** + + - Strategy limit management + - TVL considerations + - Transaction batching + +3. **Memory Usage** + - Compact data storage + - Efficient mapping structure + - Minimal temporary storage From 1ae9f263a13eab6a96b32d92b4e353dd82b74005 Mon Sep 17 00:00:00 2001 From: philip-hue Date: Fri, 27 Dec 2024 17:54:09 +0100 Subject: [PATCH 11/11] renamed: TECHNICAL.md -> docs/TECHNICAL.md --- TECHNICAL.md => docs/TECHNICAL.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename TECHNICAL.md => docs/TECHNICAL.md (100%) diff --git a/TECHNICAL.md b/docs/TECHNICAL.md similarity index 100% rename from TECHNICAL.md rename to docs/TECHNICAL.md