Source Code


;; xyk-core-v-1-2

;; Use XYK pool trait and SIP 010 trait
(use-trait xyk-pool-trait .xyk-pool-trait-v-1-2.xyk-pool-trait)
(use-trait sip-010-trait .sip-010-trait-ft-standard-v-1-1.sip-010-trait)

;; Error constants
(define-constant ERR_NOT_AUTHORIZED (err u1001))
(define-constant ERR_INVALID_AMOUNT (err u1002))
(define-constant ERR_INVALID_PRINCIPAL (err u1003))
(define-constant ERR_ALREADY_ADMIN (err u1004))
(define-constant ERR_ADMIN_LIMIT_REACHED (err u1005))
(define-constant ERR_ADMIN_NOT_IN_LIST (err u1006))
(define-constant ERR_CANNOT_REMOVE_CONTRACT_DEPLOYER (err u1007))
(define-constant ERR_NO_POOL_DATA (err u1008))
(define-constant ERR_POOL_NOT_CREATED (err u1009))
(define-constant ERR_POOL_DISABLED (err u1010))
(define-constant ERR_POOL_ALREADY_CREATED (err u1011))
(define-constant ERR_INVALID_POOL (err u1012))
(define-constant ERR_INVALID_POOL_URI (err u1013))
(define-constant ERR_INVALID_POOL_SYMBOL (err u1014))
(define-constant ERR_INVALID_POOL_NAME (err u1015))
(define-constant ERR_MATCHING_TOKEN_CONTRACTS (err u1016))
(define-constant ERR_INVALID_X_TOKEN (err u1017))
(define-constant ERR_INVALID_Y_TOKEN (err u1018))
(define-constant ERR_MINIMUM_X_AMOUNT (err u1019))
(define-constant ERR_MINIMUM_Y_AMOUNT (err u1020))
(define-constant ERR_MINIMUM_LP_AMOUNT (err u1021))
(define-constant ERR_INVALID_FEE (err u1022))
(define-constant ERR_MINIMUM_BURN_AMOUNT (err u1023))
(define-constant ERR_INVALID_MIN_BURNT_SHARES (err u1024))

;; Contract deployer address
(define-constant CONTRACT_DEPLOYER tx-sender)

;; Maximum BPS
(define-constant BPS u10000)

;; Admins list and helper var used to remove admins
(define-data-var admins (list 5 principal) (list tx-sender))
(define-data-var admin-helper principal tx-sender)

;; ID of last created pool
(define-data-var last-pool-id uint u0)

;; Minimum shares required to mint when creating a pool
(define-data-var minimum-total-shares uint u10000)

;; Minimum shares required to burn when creating a pool
(define-data-var minimum-burnt-shares uint u1000)

;; Data var used to enable or disable pool creation by anyone
(define-data-var public-pool-creation bool false)

;; Define pools map
(define-map pools uint {
    id: uint,
    name: (string-ascii 32),
    symbol: (string-ascii 32),
    pool-contract: principal
})

;; Get admins list
(define-read-only (get-admins)
    (ok (var-get admins))
)

;; Get admin helper var
(define-read-only (get-admin-helper)
    (ok (var-get admin-helper))
)

;; Get ID of last created pool
(define-read-only (get-last-pool-id)
    (ok (var-get last-pool-id))
)

;; Get a pool by pool ID
(define-read-only (get-pool-by-id (id uint))
    (ok (map-get? pools id))
)

;; Get minimum shares required to mint when creating a pool
(define-read-only (get-minimum-total-shares)
    (ok (var-get minimum-total-shares))
)

;; Get minimum shares required to burn when creating a pool
(define-read-only (get-minimum-burnt-shares)
    (ok (var-get minimum-burnt-shares))
)

;; Get public pool creation status
(define-read-only (get-public-pool-creation)
    (ok (var-get public-pool-creation))
)

;; Get DY
(define-public (get-dy
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (x-amount uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))
    (protocol-fee (get x-protocol-fee pool-data))
    (provider-fee (get x-provider-fee pool-data))
    
    ;; Calculate fees and perform AMM calculations
    (x-amount-fees-protocol (/ (* x-amount protocol-fee) BPS))
    (x-amount-fees-provider (/ (* x-amount provider-fee) BPS))
    (x-amount-fees-total (+ x-amount-fees-protocol x-amount-fees-provider))
    (dx (- x-amount x-amount-fees-total))
    (updated-x-balance (+ x-balance dx))
    (dy (/ (* y-balance dx) updated-x-balance))
    )
    (begin
        ;; Assert that pool-status is true and correct token traits are used
        (asserts! (is-eq (get pool-status pool-data) true) ERR_POOL_DISABLED)
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)
        
        ;; Assert that x-amount is greater than 0
        (asserts! (> x-amount u0) ERR_INVALID_AMOUNT)

        ;; Return number of y tokens the caller would receive
        (ok dy)
    )
    )
)

;; Get DX
(define-public (get-dx
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (y-amount uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))
    (protocol-fee (get x-protocol-fee pool-data))
    (provider-fee (get x-provider-fee pool-data))

    ;; Calculate fees and perform AMM calculations
    (y-amount-fees-protocol (/ (* y-amount protocol-fee) BPS))
    (y-amount-fees-provider (/ (* y-amount provider-fee) BPS))
    (y-amount-fees-total (+ y-amount-fees-protocol y-amount-fees-provider))
    (dy (- y-amount y-amount-fees-total))
    (updated-y-balance (+ y-balance dy))
    (dx (/ (* x-balance dy) updated-y-balance))
    )
    (begin
        ;; Assert that pool-status is true and correct token traits are used
        (asserts! (is-eq (get pool-status pool-data) true) ERR_POOL_DISABLED)
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)

        ;; Assert that y-amount is greater than 0
        (asserts! (> y-amount u0) ERR_INVALID_AMOUNT)

        ;; Return number of x tokens the caller would receive
        (ok dx)
    )
    )
)

;; Get DLP
(define-public (get-dlp
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (x-amount uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (pool-contract (contract-of pool-trait))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (total-shares (get total-shares pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))

    ;; Calculate y-amount, pool balances, and dlp
    (y-amount (/ (* x-amount y-balance) x-balance))
    (updated-x-balance (+ x-balance x-amount))
    (updated-y-balance (+ y-balance y-amount))
    (dlp (/ (* x-amount total-shares) x-balance))
    )
    (begin
        ;; Assert that pool-status is true and correct token traits are used
        (asserts! (is-eq (get pool-status pool-data) true) ERR_POOL_DISABLED)
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)

        ;; Assert that x-amount and y-amount are greater than 0
        (asserts! (> x-amount u0) ERR_INVALID_AMOUNT)
        (asserts! (> y-amount u0) ERR_MINIMUM_Y_AMOUNT)

        ;; Return number of LP tokens caller would receive and y-amount they would transfer
        (ok {dlp: dlp, y-amount: y-amount})
    )
    )
)

;; Set minimum shares required to mint and burn when creating a pool
(define-public (set-minimum-shares (min-total uint) (min-burnt uint))
    (let (
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin and amounts are greater than 0
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)
        (asserts! (and (> min-total u0) (> min-burnt u0)) ERR_INVALID_AMOUNT)
        
        ;; Assert that min-total is greater than min-burnt
        (asserts! (> min-total min-burnt) ERR_INVALID_MIN_BURNT_SHARES)

        ;; Update minimum-total-shares and minimum-burnt-shares
        (var-set minimum-total-shares min-total)
        (var-set minimum-burnt-shares min-burnt)

        ;; Print function data and return true
        (print {
        action: "set-minimum-shares",
        caller: caller,
        data: {
            min-total: min-total,
            min-burnt: min-burnt
        }
        })
        (ok true)
    )
    )
)

;; Enable or disable public pool creation
(define-public (set-public-pool-creation (status bool))
    (let (
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)

        ;; Set public-pool-creation to status
        (var-set public-pool-creation status)
        
        ;; Print function data and return true
        (print {action: "set-public-pool-creation", caller: caller, data: {status: status}})
        (ok true)
    )
    )
)

;; Set pool uri for a pool
(define-public (set-pool-uri (pool-trait <xyk-pool-trait>) (uri (string-utf8 256)))
    (let (
    ;; Gather all pool data
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin and pool is created and valid
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)
        (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL)
        (asserts! (is-eq (get pool-created pool-data) true) ERR_POOL_NOT_CREATED)
        
        ;; Assert that uri length is greater than 0
        (asserts! (> (len uri) u0) ERR_INVALID_POOL_URI)
        
        ;; Set pool uri for pool
        (try! (contract-call? pool-trait set-pool-uri uri))
        
        ;; Print function data and return true
        (print {
        action: "set-pool-uri",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: (contract-of pool-trait),
            uri: uri
        }
        })
        (ok true)
    )
    )
)

;; Set pool status for a pool
(define-public (set-pool-status (pool-trait <xyk-pool-trait>) (status bool))
    (let (
    ;; Gather all pool data
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin and pool is created and valid
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)
        (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL)
        (asserts! (is-eq (get pool-created pool-data) true) ERR_POOL_NOT_CREATED)
        
        ;; Set pool status for pool
        (try! (contract-call? pool-trait set-pool-status status))
        
        ;; Print function data and return true
        (print {
        action: "set-pool-status",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: (contract-of pool-trait),
            status: status
        }
        })
        (ok true)
    )
    )
)

;; Set fee address for a pool
(define-public (set-fee-address (pool-trait <xyk-pool-trait>) (address principal))
    (let (
    ;; Gather all pool data
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin and pool is created and valid
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)
        (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL)
        (asserts! (is-eq (get pool-created pool-data) true) ERR_POOL_NOT_CREATED)
        
        ;; Assert that address is standard principal
        (asserts! (is-standard address) ERR_INVALID_PRINCIPAL)
        
        ;; Set fee address for pool
        (try! (contract-call? pool-trait set-fee-address address))
        
        ;; Print function data and return true
        (print {
        action: "set-fee-address",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: (contract-of pool-trait),
            address: address
        }
        })
        (ok true)
    )
    )
)

;; Set x fees for a pool
(define-public (set-x-fees (pool-trait <xyk-pool-trait>) (protocol-fee uint) (provider-fee uint))
    (let (
    ;; Gather all pool data
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin and pool is created and valid
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)
        (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL)
        (asserts! (is-eq (get pool-created pool-data) true) ERR_POOL_NOT_CREATED)
        
        ;; Assert protocol-fee and provider-fee is less than maximum BPS
        (asserts! (< (+ protocol-fee provider-fee) BPS) ERR_INVALID_FEE)
        
        ;; Set x fees for pool
        (try! (contract-call? pool-trait set-x-fees protocol-fee provider-fee))
        
        ;; Print function data and return true
        (print {
        action: "set-x-fees",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: (contract-of pool-trait),
            protocol-fee: protocol-fee,
            provider-fee: provider-fee
        }
        })
        (ok true)
    )
    )
)

;; Set y fees for a pool
(define-public (set-y-fees (pool-trait <xyk-pool-trait>) (protocol-fee uint) (provider-fee uint))
    (let (
    ;; Gather all pool data
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (caller tx-sender)
    )
    (begin
        ;; Assert caller is an admin and pool is created and valid
        (asserts! (is-some (index-of (var-get admins) caller)) ERR_NOT_AUTHORIZED)
        (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL)
        (asserts! (is-eq (get pool-created pool-data) true) ERR_POOL_NOT_CREATED)
        
        ;; Assert protocol-fee and provider-fee is less than maximum BPS
        (asserts! (< (+ protocol-fee provider-fee) BPS) ERR_INVALID_FEE)
        
        ;; Set y fees for pool
        (try! (contract-call? pool-trait set-y-fees protocol-fee provider-fee))
        
        ;; Print function data and return true
        (print {
        action: "set-y-fees",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: (contract-of pool-trait),
            protocol-fee: protocol-fee,
            provider-fee: provider-fee
        }
        })
        (ok true)
    )
    )
)

;; Create a new pool
(define-public (create-pool 
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (x-amount uint) (y-amount uint)
    (burn-amount uint)
    (x-protocol-fee uint) (x-provider-fee uint)
    (y-protocol-fee uint) (y-provider-fee uint)
    (fee-address principal) (uri (string-utf8 256)) (status bool)
    )
    (let (
    ;; Gather all pool data and pool contract
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-contract (contract-of pool-trait))
    
    ;; Get pool ID and create pool symbol and name 
    (new-pool-id (+ (var-get last-pool-id) u1))
    (symbol (unwrap! (create-symbol x-token-trait y-token-trait) ERR_INVALID_POOL_SYMBOL))
    (name (concat symbol "-LP"))
    (x-token-contract (contract-of x-token-trait))
    (y-token-contract (contract-of y-token-trait))

    ;; Calculate total shares
    (total-shares (sqrti (* x-amount y-amount)))
    (min-burnt-shares (var-get minimum-burnt-shares))
    (caller tx-sender)
    )
    (begin
        ;; Assert that caller is an admin or public-pool-creation is true
        (asserts! (or (is-some (index-of (var-get admins) caller)) (var-get public-pool-creation)) ERR_NOT_AUTHORIZED)
        
        ;; Assert that pool is not created
        (asserts! (is-eq (get pool-created pool-data) false) ERR_POOL_ALREADY_CREATED)

        ;; Assert that x-token-contract and y-token-contract are not matching
        (asserts! (not (is-eq x-token-contract y-token-contract)) ERR_MATCHING_TOKEN_CONTRACTS)

        ;; Assert that addresses are standard principals
        (asserts! (is-standard x-token-contract) ERR_INVALID_PRINCIPAL)
        (asserts! (is-standard y-token-contract) ERR_INVALID_PRINCIPAL)
        (asserts! (is-standard fee-address) ERR_INVALID_PRINCIPAL)

        ;; Assert that x and y amount is greater than 0
        (asserts! (and (> x-amount u0) (> y-amount u0)) ERR_INVALID_AMOUNT)

        ;; Assert that total shares minted meets minimum total shares required
        (asserts! (>= total-shares (var-get minimum-total-shares)) ERR_MINIMUM_LP_AMOUNT)

        ;; Assert that burn amount meets minimum shares required to burn
        (asserts! (>= burn-amount min-burnt-shares) ERR_MINIMUM_BURN_AMOUNT)

        ;; Assert that total shares is greater than or equal to 0 after subtracting burn amount
        (asserts! (>= (- total-shares burn-amount) u0) ERR_MINIMUM_LP_AMOUNT)

        ;; Assert that length of pool uri, symbol, and name is greater than 0
        (asserts! (> (len uri) u0) ERR_INVALID_POOL_URI)
        (asserts! (> (len symbol) u0) ERR_INVALID_POOL_SYMBOL)
        (asserts! (> (len name) u0) ERR_INVALID_POOL_NAME)

        ;; Assert that fees are less than maximum BPS
        (asserts! (< (+ x-protocol-fee x-provider-fee) BPS) ERR_INVALID_FEE)
        (asserts! (< (+ y-protocol-fee y-provider-fee) BPS) ERR_INVALID_FEE)

        ;; Create pool and set fees
        (try! (contract-call? pool-trait create-pool x-token-contract y-token-contract fee-address caller new-pool-id name symbol uri status))
        (try! (contract-call? pool-trait set-x-fees x-protocol-fee x-provider-fee))
        (try! (contract-call? pool-trait set-y-fees y-protocol-fee y-provider-fee))

        ;; Update ID of last created pool and add pool to pools map
        (var-set last-pool-id new-pool-id)
        (map-set pools new-pool-id {id: new-pool-id, name: name, symbol: symbol, pool-contract: pool-contract})
        
        ;; Transfer x-amount x tokens and y-amount y tokens from caller to pool-contract
        (try! (contract-call? x-token-trait transfer x-amount caller pool-contract none))
        (try! (contract-call? y-token-trait transfer y-amount caller pool-contract none))

        ;; Update pool balances
        (try! (contract-call? pool-trait update-pool-balances x-amount y-amount))

        ;; Mint LP tokens to caller 
        (try! (contract-call? pool-trait pool-mint (- total-shares burn-amount) caller))

        ;; Mint burn amount LP tokens to pool-contract
        (try! (contract-call? pool-trait pool-mint burn-amount pool-contract))

        ;; Print create pool data and return true
        (print {
        action: "create-pool",
        caller: caller,
        data: {
            pool-id: new-pool-id,
            pool-name: name,
            pool-contract: pool-contract,
            x-token: x-token-contract,
            y-token: y-token-contract,
            x-protocol-fee: x-protocol-fee,
            x-provider-fee: x-provider-fee,
            y-protocol-fee: y-protocol-fee,
            y-provider-fee: y-provider-fee,
            x-amount: x-amount,
            y-amount: y-amount,
            burn-amount: burn-amount,
            total-shares: total-shares,
            pool-symbol: symbol,
            pool-uri: uri,
            pool-status: status,
            creation-height: burn-block-height,
            fee-address: fee-address
        }
        })
        (ok true)
    )
    )
)

;; Swap x token for y token via a pool
(define-public (swap-x-for-y
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (x-amount uint) (min-dy uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (pool-contract (contract-of pool-trait))
    (fee-address (get fee-address pool-data))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))
    (protocol-fee (get x-protocol-fee pool-data))
    (provider-fee (get x-provider-fee pool-data))
    
    ;; Calculate fees and perform AMM calculations
    (x-amount-fees-protocol (/ (* x-amount protocol-fee) BPS))
    (x-amount-fees-provider (/ (* x-amount provider-fee) BPS))
    (x-amount-fees-total (+ x-amount-fees-protocol x-amount-fees-provider))
    (dx (- x-amount x-amount-fees-total))
    (updated-x-balance (+ x-balance dx))
    (dy (/ (* y-balance dx) updated-x-balance))
    (updated-y-balance (- y-balance dy))
    (caller tx-sender)
    )
    (begin
        ;; Assert that pool-status is true and correct token traits are used
        (asserts! (is-eq (get pool-status pool-data) true) ERR_POOL_DISABLED)
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)
        
        ;; Assert that x-amount is greater than 0
        (asserts! (> x-amount u0) ERR_INVALID_AMOUNT)

        ;; Assert that min-dy is greater than 0 and dy is greater than or equal to min-dy
        (asserts! (> min-dy u0) ERR_INVALID_AMOUNT)
        (asserts! (>= dy min-dy) ERR_MINIMUM_Y_AMOUNT)
        
        ;; Transfer dx + x-amount-fees-provider x tokens from caller to pool-contract
        (try! (contract-call? x-token-trait transfer (+ dx x-amount-fees-provider) caller pool-contract none))
        
        ;; Transfer dy y tokens from pool-contract to caller
        (try! (contract-call? pool-trait pool-transfer y-token-trait dy caller))
        
        ;; Transfer x-amount-fees-protocol x tokens from caller to fee-address
        (if (> x-amount-fees-protocol u0)
        (try! (contract-call? x-token-trait transfer x-amount-fees-protocol caller fee-address none))
        false
        )

        ;; Update pool balances
        (try! (contract-call? pool-trait update-pool-balances (+ updated-x-balance x-amount-fees-provider) updated-y-balance))
        
        ;; Print swap data and return number of y tokens the caller received
        (print {
        action: "swap-x-for-y",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: pool-contract,
            x-token: x-token,
            y-token: y-token,
            x-amount: x-amount,
            x-amount-fees-protocol: x-amount-fees-protocol,
            x-amount-fees-provider: x-amount-fees-provider,
            dy: dy,
            min-dy: min-dy
        }
        })
        (ok dy)
    )
    )
)

;; Swap y token for x token via a pool
(define-public (swap-y-for-x
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (y-amount uint) (min-dx uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (pool-contract (contract-of pool-trait))
    (fee-address (get fee-address pool-data))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))
    (protocol-fee (get y-protocol-fee pool-data))
    (provider-fee (get y-provider-fee pool-data))

    ;; Calculate fees and perform AMM calculations
    (y-amount-fees-protocol (/ (* y-amount protocol-fee) BPS))
    (y-amount-fees-provider (/ (* y-amount provider-fee) BPS))
    (y-amount-fees-total (+ y-amount-fees-protocol y-amount-fees-provider))
    (dy (- y-amount y-amount-fees-total))
    (updated-y-balance (+ y-balance dy))
    (dx (/ (* x-balance dy) updated-y-balance))
    (updated-x-balance (- x-balance dx))
    (caller tx-sender)
    )
    (begin
        ;; Assert that pool-status is true and correct token traits are used
        (asserts! (is-eq (get pool-status pool-data) true) ERR_POOL_DISABLED)
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)

        ;; Assert that y-amount is greater than 0
        (asserts! (> y-amount u0) ERR_INVALID_AMOUNT)

        ;; Assert that min-dx is greater than 0 and dx is greater than or equal to min-dx
        (asserts! (> min-dx u0) ERR_INVALID_AMOUNT)
        (asserts! (>= dx min-dx) ERR_MINIMUM_X_AMOUNT)

        ;; Transfer dy + y-amount-fees-provider y tokens from caller to pool-contract
        (try! (contract-call? y-token-trait transfer (+ dy y-amount-fees-provider) caller pool-contract none))
        
        ;; Transfer dx x tokens from pool-contract to caller 
        (try! (contract-call? pool-trait pool-transfer x-token-trait dx caller))
        
        ;; Transfer y-amount-fees-protocol y tokens from caller to fee-address
        (if (> y-amount-fees-protocol u0)
        (try! (contract-call? y-token-trait transfer y-amount-fees-protocol caller fee-address none))
        false
        )

        ;; Update pool balances
        (try! (contract-call? pool-trait update-pool-balances updated-x-balance (+ updated-y-balance y-amount-fees-provider)))
        
        ;; Print swap data and return number of x tokens the caller received
        (print {
        action: "swap-y-for-x",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: pool-contract,
            x-token: x-token,
            y-token: y-token,
            y-amount: y-amount,
            y-amount-fees-protocol: y-amount-fees-protocol,
            y-amount-fees-provider: y-amount-fees-provider,
            dx: dx,
            min-dx: min-dx
        }
        })
        (ok dx)
    )
    )
)

;; Add liquidity to a pool
(define-public (add-liquidity
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (x-amount uint) (min-dlp uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (pool-contract (contract-of pool-trait))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (total-shares (get total-shares pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))

    ;; Calculate y-amount, pool balances, and dlp
    (y-amount (/ (* x-amount y-balance) x-balance))
    (updated-x-balance (+ x-balance x-amount))
    (updated-y-balance (+ y-balance y-amount))
    (dlp (/ (* x-amount total-shares) x-balance))
    (caller tx-sender)
    )
    (begin
        ;; Assert that pool-status is true and correct token traits are used
        (asserts! (is-eq (get pool-status pool-data) true) ERR_POOL_DISABLED)
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)

        ;; Assert that x-amount and y-amount are greater than 0
        (asserts! (> x-amount u0) ERR_INVALID_AMOUNT)
        (asserts! (> y-amount u0) ERR_MINIMUM_Y_AMOUNT)

        ;; Assert that min-dlp is greater than 0 and dlp is greater than or equal to min-dlp
        (asserts! (> min-dlp u0) ERR_INVALID_AMOUNT)
        (asserts! (>= dlp min-dlp) ERR_MINIMUM_LP_AMOUNT)

        ;; Transfer x-amount x tokens from caller to pool-contract
        (try! (contract-call? x-token-trait transfer x-amount caller pool-contract none))
        
        ;; Transfer y-amount y tokens from caller to pool-contract
        (try! (contract-call? y-token-trait transfer y-amount caller pool-contract none))
        
        ;; Update pool balances
        (try! (contract-call? pool-trait update-pool-balances updated-x-balance updated-y-balance))
        
        ;; Mint LP tokens to caller
        (try! (contract-call? pool-trait pool-mint dlp caller))

        ;; Print add liquidity data and return number of LP tokens caller received
        (print {
        action: "add-liquidity",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: pool-contract,
            x-token: x-token,
            y-token: y-token,
            x-amount: x-amount,
            y-amount: y-amount,
            dlp: dlp,
            min-dlp: min-dlp
        }
        })
        (ok dlp)
    )
    )
)

;; Withdraw liquidity from a pool
(define-public (withdraw-liquidity
    (pool-trait <xyk-pool-trait>)
    (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>)
    (amount uint) (min-x-amount uint) (min-y-amount uint)
    )
    (let (
    ;; Gather all pool data and check if pool is valid
    (pool-data (unwrap! (contract-call? pool-trait get-pool) ERR_NO_POOL_DATA))
    (pool-validity-check (asserts! (is-valid-pool (get pool-id pool-data) (contract-of pool-trait)) ERR_INVALID_POOL))
    (x-token (get x-token pool-data))
    (y-token (get y-token pool-data))
    (x-balance (get x-balance pool-data))
    (y-balance (get y-balance pool-data))
    (total-shares (get total-shares pool-data))

    ;; Calculate x-amount and y-amount to transfer and updated balances
    (x-amount (/ (* amount x-balance) total-shares))
    (y-amount (/ (* amount y-balance) total-shares))
    (updated-x-balance (- x-balance x-amount))
    (updated-y-balance (- y-balance y-amount))
    (caller tx-sender)
    )
    (begin
        ;; Assert that correct token traits are used
        (asserts! (is-eq (contract-of x-token-trait) x-token) ERR_INVALID_X_TOKEN)
        (asserts! (is-eq (contract-of y-token-trait) y-token) ERR_INVALID_Y_TOKEN)

        ;; Assert that amount is greater than 0
        (asserts! (> amount u0) ERR_INVALID_AMOUNT)

        ;; Assert that x-amount + y-amount is greater than 0
        (asserts! (> (+ x-amount y-amount) u0) ERR_INVALID_AMOUNT)

        ;; Assert that x-amount is greater than or equal to min-x-amount
        (asserts! (>= x-amount min-x-amount) ERR_MINIMUM_X_AMOUNT)

        ;; Assert that y-amount is greater than or equal to min-y-amount
        (asserts! (>= y-amount min-y-amount) ERR_MINIMUM_Y_AMOUNT)

        ;; Transfer x-amount x tokens from pool-contract to caller
        (if (> x-amount u0)
        (try! (contract-call? pool-trait pool-transfer x-token-trait x-amount caller))
        false
        )

        ;; Transfer y-amount y tokens from pool-contract to caller
        (if (> y-amount u0)
        (try! (contract-call? pool-trait pool-transfer y-token-trait y-amount caller))
        false
        )

        ;; Update pool balances
        (try! (contract-call? pool-trait update-pool-balances updated-x-balance updated-y-balance))

        ;; Burn LP tokens from caller
        (try! (contract-call? pool-trait pool-burn amount caller))

        ;; Print withdraw liquidity data and return number of x and y tokens caller received
        (print {
        action: "withdraw-liquidity",
        caller: caller,
        data: {
            pool-id: (get pool-id pool-data),
            pool-name: (get pool-name pool-data),
            pool-contract: (contract-of pool-trait),
            x-token: x-token,
            y-token: y-token,
            amount: amount,
            x-amount: x-amount,
            y-amount: y-amount,
            min-x-amount: min-x-amount,
            min-y-amount: min-y-amount
        }
        })
        (ok {x-amount: x-amount, y-amount: y-amount})
    )
    )
)

;; Add an admin to the admins list
(define-public (add-admin (admin principal))
    (let (
    (admins-list (var-get admins))
    (caller tx-sender)
    )
    ;; Assert caller is an existing admin and new admin is not in admins-list
    (asserts! (is-some (index-of admins-list caller)) ERR_NOT_AUTHORIZED)
    (asserts! (is-none (index-of admins-list admin)) ERR_ALREADY_ADMIN)
    
    ;; Add admin to list with max length of 5
    (var-set admins (unwrap! (as-max-len? (append admins-list admin) u5) ERR_ADMIN_LIMIT_REACHED))
    
    ;; Print add admin data and return true
    (print {action: "add-admin", caller: caller, data: {admin: admin}})
    (ok true)
    )
)

;; Remove an admin from the admins list
(define-public (remove-admin (admin principal))
    (let (
    (admins-list (var-get admins))
    (caller tx-sender)
    )
    ;; Assert caller is an existing admin and admin to remove is in admins-list
    (asserts! (is-some (index-of admins-list caller)) ERR_NOT_AUTHORIZED)
    (asserts! (is-some (index-of admins-list admin)) ERR_ADMIN_NOT_IN_LIST)

    ;; Assert contract deployer cannot be removed
    (asserts! (not (is-eq admin CONTRACT_DEPLOYER)) ERR_CANNOT_REMOVE_CONTRACT_DEPLOYER)

    ;; Set admin-helper to admin to remove and filter admins-list to remove admin
    (var-set admin-helper admin)
    (var-set admins (filter admin-not-removable admins-list))

    ;; Print remove admin data and return true
    (print {action: "remove-admin", caller: caller, data: {admin: admin}})
    (ok true)
    )
)

;; Set pool uri for multiple pools
(define-public (set-pool-uri-multi
    (pool-traits (list 120 <xyk-pool-trait>))
    (uris (list 120 (string-utf8 256)))
    )
    (ok (map set-pool-uri pool-traits uris))
)

;; Set pool status for multiple pools
(define-public (set-pool-status-multi
    (pool-traits (list 120 <xyk-pool-trait>))
    (statuses (list 120 bool))
    )
    (ok (map set-pool-status pool-traits statuses))
)

;; Set fee address for multiple pools
(define-public (set-fee-address-multi
    (pool-traits (list 120 <xyk-pool-trait>))
    (addresses (list 120 principal))
    )
    (ok (map set-fee-address pool-traits addresses))
)

;; Set x fees for multiple pools
(define-public (set-x-fees-multi
    (pool-traits (list 120 <xyk-pool-trait>))
    (protocol-fees (list 120 uint)) (provider-fees (list 120 uint))
    )
    (ok (map set-x-fees pool-traits protocol-fees provider-fees))
)

;; Set y fees for multiple pools
(define-public (set-y-fees-multi
    (pool-traits (list 120 <xyk-pool-trait>))
    (protocol-fees (list 120 uint)) (provider-fees (list 120 uint))
    )
    (ok (map set-y-fees pool-traits protocol-fees provider-fees))
)

;; Helper function for removing an admin
(define-private (admin-not-removable (admin principal))
    (not (is-eq admin (var-get admin-helper)))
)

;; Create pool symbol using x token and y token symbols
(define-private (create-symbol (x-token-trait <sip-010-trait>) (y-token-trait <sip-010-trait>))
    (let (
    ;; Get x token and y token symbols
    (x-symbol (unwrap-panic (contract-call? x-token-trait get-symbol)))
    (y-symbol (unwrap-panic (contract-call? y-token-trait get-symbol)))
    
    ;; Truncate symbols if length exceeds 14
    (x-truncated 
        (if (> (len x-symbol) u14)
        (unwrap-panic (slice? x-symbol u0 u14))
        x-symbol
        )
    )
    (y-truncated
        (if (> (len y-symbol) u14)
        (unwrap-panic (slice? y-symbol u0 u14))
        y-symbol
        )
    )
    )
    ;; Return pool symbol with max length of 29
    (as-max-len? (concat x-truncated (concat "-" y-truncated)) u29)
    )
)

;; Check if a pool is valid
(define-private (is-valid-pool (id uint) (contract principal))
    (let (
    (pool-data (unwrap! (map-get? pools id) false))
    )
    (is-eq contract (get pool-contract pool-data))
    )
)

Functions (25)

FunctionAccessArgs
get-adminsread-only
get-admin-helperread-only
get-last-pool-idread-only
get-pool-by-idread-onlyid: uint
get-minimum-total-sharesread-only
get-minimum-burnt-sharesread-only
get-public-pool-creationread-only
set-minimum-sharespublicmin-total: uint, min-burnt: uint
set-public-pool-creationpublicstatus: bool
set-pool-uripublicpool-trait: <xyk-pool-trait>, uri: (string-utf8 256
set-pool-statuspublicpool-trait: <xyk-pool-trait>, status: bool
set-fee-addresspublicpool-trait: <xyk-pool-trait>, address: principal
set-x-feespublicpool-trait: <xyk-pool-trait>, protocol-fee: uint, provider-fee: uint
set-y-feespublicpool-trait: <xyk-pool-trait>, protocol-fee: uint, provider-fee: uint
create-poolpublicpool-trait: <xyk-pool-trait>, x-token-trait: <sip-010-trait>, y-token-trait: <sip-010-trait>, x-amount: uint, y-amount: uint, burn-amount: uint, x-protocol-fee: uint, x-provider-fee: uint, y-protocol-fee: uint, y-provider-fee: uint, fee-address: principal, uri: (string-utf8 256
add-adminpublicadmin: principal
remove-adminpublicadmin: principal
set-pool-uri-multipublicpool-traits: (list 120 <xyk-pool-trait>
set-pool-status-multipublicpool-traits: (list 120 <xyk-pool-trait>
set-fee-address-multipublicpool-traits: (list 120 <xyk-pool-trait>
set-x-fees-multipublicpool-traits: (list 120 <xyk-pool-trait>
set-y-fees-multipublicpool-traits: (list 120 <xyk-pool-trait>
admin-not-removableprivateadmin: principal
create-symbolprivatex-token-trait: <sip-010-trait>, y-token-trait: <sip-010-trait>
is-valid-poolprivateid: uint, contract: principal