Source Code

;; SPDX-License-Identifier: BUSL-1.1
;; treasury-grant-v4 - Contract for managing treasury grants and token distributions

(use-trait ft-trait 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.trait-sip-010.sip-010-trait)

;; Error codes for various failure scenarios
(define-constant ERR-NOT-AUTHORIZED (err u1000)) ;; When caller is not authorized
(define-constant ERR-TOKEN-MISMATCH (err u1001)) ;; When provided tokens don't match pool tokens
(define-constant ERR-ALREADY-CLAIMED (err u1002)) ;; When balance has already been claimed
(define-constant ERR-UPDATE-PRINCIPAL-MAP-FAILED (err u1003)) ;; When failed to update claim status
(define-constant ERR-INVALID-POOL-ID (err u1004)) ;; When provided pool ID is invalid

;; Constants for decimal handling and maximum values
(define-constant ONE_8 u100000000) ;; 1.0 with 8 decimal places
(define-constant MAX_UINT u340282366920938463463374607431768211455) ;; Maximum uint value

;; Snapshot block for balance calculations
;; __IF_MAINNET__				
(define-data-var snapshot-block uint u1509640)
;; (define-data-var snapshot-block uint u26356)
;; __ENDIF__

;; Exchange rates for different tokens (with 8 decimal places)
(define-data-var btc-rate uint u10273463917525) ;; 102,734.64 
(define-data-var stx-rate uint u67721865) ;; 0.67721865
(define-data-var usd-rate uint u100000000) ;; 1.00

;; Percentage of tokens to be lost/held (with 8 decimal places)
(define-data-var btc-pct uint u25000000) ;; 25%
(define-data-var stx-pct uint u100000000) ;; 100%
(define-data-var usd-pct uint u9000000) ;; 9%

;; Map to track which addresses have claimed their balances for specific pools
(define-map claimed { address: principal, pool-id: uint } bool)

;; Check if caller is DAO or extension
(define-read-only (is-dao-or-extension)
	(ok (asserts! (or (is-eq tx-sender 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.executor-dao) (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.executor-dao is-extension contract-caller)) ERR-NOT-AUTHORIZED)))

;; Getter functions for data variables
(define-read-only (get-snapshot-block)
	(var-get snapshot-block))

(define-read-only (get-btc-rate)
	(var-get btc-rate))

(define-read-only (get-stx-rate)
	(var-get stx-rate))

(define-read-only (get-usd-rate)
	(var-get usd-rate))

(define-read-only (get-btc-pct)
	(var-get btc-pct))

(define-read-only (get-stx-pct)
	(var-get stx-pct))

(define-read-only (get-usd-pct)
	(var-get usd-pct))

;; Setter functions for data variables (only callable by DAO or extension)
(define-public (set-snapshot-block (new-block uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set snapshot-block new-block))))

(define-public (set-btc-rate (new-rate uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set btc-rate new-rate))))

(define-public (set-stx-rate (new-rate uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set stx-rate new-rate))))

(define-public (set-usd-rate (new-rate uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set usd-rate new-rate))))

(define-public (set-btc-pct (new-pct uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set btc-pct new-pct))))

(define-public (set-stx-pct (new-pct uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set stx-pct new-pct))))

(define-public (set-usd-pct (new-pct uint))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set usd-pct new-pct))))

;; Get the balance of a specific pool for an address at the snapshot block
(define-read-only (get-pool-balance (pool-id uint) (address principal))
	(let (
			(snapshot-block-id (unwrap-panic (get-stacks-block-info? id-header-hash (var-get snapshot-block))))
			(btc-rate-val (var-get btc-rate))
			(stx-rate-val (var-get stx-rate))
			(usd-rate-val (var-get usd-rate))
			(btc-pct-val (var-get btc-pct))
			(stx-pct-val (var-get stx-pct))
			(usd-pct-val (var-get usd-pct))
			(snapshot-data (at-block snapshot-block-id
				(let (
						(pool-tokens (unwrap-panic (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-registry-v2-01 get-pool-details-by-id pool-id)))
						(pool-details (unwrap-panic (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 get-pool-details (get token-x pool-tokens) (get token-y pool-tokens) (get factor pool-tokens))))
						(total-supply (unwrap-panic (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 get-total-supply-fixed pool-id)))
						(user-farm-details (match (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.alex-farming get-user-id 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 pool-id address)
							some-value (let ((reward-cycle (unwrap-panic (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.alex-farming get-reward-cycle 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 pool-id tenure-height))))
								(contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.alex-farming get-staker-at-cycle-or-default 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 pool-id reward-cycle some-value))
							{ amount-staked: u0, to-return: u0 }))
						(user-lp-balance (unwrap-panic (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 get-balance-fixed pool-id address)))
						(user-surge-balance-1 (contract-call? 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.farming-campaign-v2-01 get-campaign-staker-or-default u1 pool-id address))
						(user-surge-balance-2 (contract-call? 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.farming-campaign-v2-02 get-campaign-staker-or-default u2 pool-id address))
						(user-surge-balance-3 (contract-call? 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.farming-campaign-v2-03 get-campaign-staker-or-default u3 pool-id address))
						(user-surge-balance-4 (contract-call? 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.farming-campaign-v2-03 get-campaign-staker-or-default u4 pool-id address))
						(total-lp-balance (+ user-lp-balance (get amount-staked user-farm-details) (get to-return user-farm-details) (if (get claimed user-surge-balance-1) u0 (get amount user-surge-balance-1)) (if (get claimed user-surge-balance-2) u0 (get amount user-surge-balance-2)) (if (get claimed user-surge-balance-3) u0 (get amount user-surge-balance-3)) (if (get claimed user-surge-balance-4) u0 (get amount user-surge-balance-4))))
						(token-x-bal (if (is-eq total-supply u0) u0 (/ (* (/ (* total-lp-balance ONE_8) total-supply) (get balance-x pool-details)) ONE_8)))
						(token-y-bal (if (is-eq total-supply u0) u0 (/ (* (/ (* total-lp-balance ONE_8) total-supply) (get balance-y pool-details)) ONE_8)))
						(lost-x-bal (if (or (is-eq (get token-x pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc) (is-eq (get token-x pool-tokens) 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc)) (mul-down token-x-bal btc-pct-val) (if (is-eq (get token-x pool-tokens) 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-wstx-v2) (mul-down token-x-bal stx-pct-val) (if (is-eq (get token-x pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-susdt) (mul-down token-x-bal usd-pct-val) u0))))
						(lost-y-bal (if (or (is-eq (get token-y pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc) (is-eq (get token-y pool-tokens) 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc)) (mul-down token-y-bal btc-pct-val) (if (is-eq (get token-y pool-tokens) 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-wstx-v2) (mul-down token-y-bal stx-pct-val) (if (is-eq (get token-y pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-susdt) (mul-down token-y-bal usd-pct-val) u0))))
						(native-x-bal (- token-x-bal lost-x-bal))
						(native-y-bal (- token-y-bal lost-y-bal))
						(usdc-x-bal (if (or (is-eq (get token-x pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc) (is-eq (get token-x pool-tokens) 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc)) (mul-down lost-x-bal btc-rate-val) (if (is-eq (get token-x pool-tokens) 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-wstx-v2) (mul-down lost-x-bal stx-rate-val) (if (is-eq (get token-x pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-susdt) (mul-down lost-x-bal usd-rate-val) u0))))
						(usdc-y-bal (if (or (is-eq (get token-y pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc) (is-eq (get token-y pool-tokens) 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc)) (mul-down lost-y-bal btc-rate-val) (if (is-eq (get token-y pool-tokens) 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-wstx-v2) (mul-down lost-y-bal stx-rate-val) (if (is-eq (get token-y pool-tokens) 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-susdt) (mul-down lost-y-bal usd-rate-val) u0)))))
					{ pool-id: pool-id, total-lp-balance: total-lp-balance, token-x-bal: token-x-bal, token-y-bal: token-y-bal, native-x-bal: native-x-bal, native-y-bal: native-y-bal, usdc-x-bal: usdc-x-bal, usdc-y-bal: usdc-y-bal, usdc-total-bal: (+ usdc-x-bal usdc-y-bal) }))))
		snapshot-data))

;; Helper function to iterate through pool balances
(define-private (get-pool-balance-iter (pool-id uint) (prior { address: principal, balances: (list 200 { pool-id: uint, total-lp-balance: uint, token-x-bal: uint, token-y-bal: uint, native-x-bal: uint, native-y-bal: uint, usdc-x-bal: uint, usdc-y-bal: uint, usdc-total-bal: uint }) }))
	(let (
			(pool-balance (get-pool-balance pool-id (get address prior)))
			(updated-balances (unwrap-panic (as-max-len? (append (get balances prior) pool-balance) u200)))
		)
		{ address: (get address prior), balances: updated-balances }))

;; Get balances for multiple pools for an address
(define-read-only (get-pool-balances (address principal) (pool-ids (list 200 uint)))
	(fold get-pool-balance-iter pool-ids { address: address, balances: (list) }))

;; Check if a balance has been claimed
(define-read-only (get-claimed-or-default (address principal) (pool-id uint))
	(default-to false (map-get? claimed { address: address, pool-id: pool-id })))

;; Claim balance from a specific pool
(define-public (claim-balance (details { dest: (buff 256), pool-id: uint, token-x-trait: <ft-trait>, token-y-trait: <ft-trait> }))
	(let (
			(pool-id (get pool-id details))
			(pool-tokens (try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-registry-v2-01 get-pool-details-by-id pool-id)))
			(pool-balance (get-pool-balance pool-id tx-sender))
			(token-x-trait (get token-x-trait details))
			(token-y-trait (get token-y-trait details))
		)
		;; Verify token traits match pool tokens
		(asserts! (is-eq (get token-x pool-tokens) (contract-of token-x-trait)) ERR-TOKEN-MISMATCH)
		(asserts! (is-eq (get token-y pool-tokens) (contract-of token-y-trait)) ERR-TOKEN-MISMATCH)
		(asserts! (not (is-eq pool-id u170)) ERR-INVALID-POOL-ID)
		;; Check if already claimed
		(asserts! (not (get-claimed-or-default tx-sender pool-id)) ERR-ALREADY-CLAIMED)
		;; Handle sBTC to aBTC conversion for token X
		(and
			(> (get native-x-bal pool-balance) u0)
			(if (is-eq (contract-of token-x-trait) 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc)
				(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01 transfer-ft 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc (get native-x-bal pool-balance) tx-sender))
				(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01 transfer-ft token-x-trait (get native-x-bal pool-balance) tx-sender))
			)
		)
		;; Handle sBTC to aBTC conversion for token Y
		(and
			(> (get native-y-bal pool-balance) u0)
			(if (is-eq (contract-of token-y-trait) 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc)
				(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01 transfer-ft 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc (get native-y-bal pool-balance) tx-sender))
				(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01 transfer-ft token-y-trait (get native-y-bal pool-balance) tx-sender))
			)
		)
		;; Mark as claimed
		(asserts! (map-set claimed { address: tx-sender, pool-id: pool-id } true) ERR-UPDATE-PRINCIPAL-MAP-FAILED)
		;; Print claim details
		(print (merge pool-balance { address: tx-sender, dest: (get dest details), claimed: true }))
		(ok true)))

;; Helper function to iterate through multiple claims
(define-private (claim-balance-iter (details { dest: (buff 256), pool-id: uint, token-x-trait: <ft-trait>, token-y-trait: <ft-trait> }) (prior (response bool uint)))
	(match prior ok-value (claim-balance details) err-value prior))

;; Claim balances from multiple pools
(define-public (claim-balance-many (details (list 200 { dest: (buff 256), pool-id: uint, token-x-trait: <ft-trait>, token-y-trait: <ft-trait> })))
	(fold claim-balance-iter details (ok true)))

;; Helper functions for mathematical operations

;; Multiply two numbers and divide by ONE_8 to handle decimals
(define-private (mul-down (a uint) (b uint))
	(/ (* a b) ONE_8))

;; Divide two numbers with proper decimal handling
(define-private (div-down (a uint) (b uint))
	(if (is-eq a u0) u0 (/ (* a ONE_8) b)))

;; Get minimum of two numbers
(define-private (min (a uint) (b uint))
	(if (<= a b) a b))

;; Get maximum of two numbers
(define-private (max (a uint) (b uint))
	(if (>= a b) a b))

Functions (22)

FunctionAccessArgs
is-dao-or-extensionread-only
get-snapshot-blockread-only
get-btc-rateread-only
get-stx-rateread-only
get-usd-rateread-only
get-btc-pctread-only
get-stx-pctread-only
get-usd-pctread-only
set-snapshot-blockpublicnew-block: uint
set-btc-ratepublicnew-rate: uint
set-stx-ratepublicnew-rate: uint
set-usd-ratepublicnew-rate: uint
set-btc-pctpublicnew-pct: uint
set-stx-pctpublicnew-pct: uint
set-usd-pctpublicnew-pct: uint
get-pool-balanceread-onlypool-id: uint, address: principal
get-pool-balancesread-onlyaddress: principal, pool-ids: (list 200 uint
get-claimed-or-defaultread-onlyaddress: principal, pool-id: uint
mul-downprivatea: uint, b: uint
div-downprivatea: uint, b: uint
minprivatea: uint, b: uint
maxprivatea: uint, b: uint