Source Code

;; title: market-marker
;; version:
;; summary:
;; description:

;; traits
;; Cryptoeconomic Primitive Smart Contract
;; This contract implements various cryptoeconomic primitives for DeFi applications

;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-INSUFFICIENT-BALANCE (err u101))
(define-constant ERR-INVALID-PARAMETER (err u102))
(define-constant ERR-POOL-DEPLETED (err u103))
(define-constant ERR-OWNER-ONLY (err u104))
(define-constant ERR-NOT-ACTIVE (err u105))
(define-constant ERR-ALREADY-INITIALIZED (err u106))
(define-constant ERR-EXPIRED (err u107))
(define-constant ERR-INVALID-PRINCIPAL (err u108))
(define-constant ERR-INVALID-TOKEN-ID (err u109))
(define-constant ERR-INVALID-AMOUNT (err u110))
(define-constant ERR-INVALID-WEIGHT (err u111))
(define-constant ERR-INVALID-CURVE-TYPE (err u112))
(define-constant ERR-INVALID-CURVE-PARAMS (err u113))

;; Contract variables
(define-data-var contract-owner principal tx-sender)
(define-data-var protocol-fee-percent uint u5) ;; 0.5% default fee
(define-data-var is-active bool true)
(define-data-var total-liquidity uint u0)
(define-data-var last-price uint u0)
(define-data-var contract-initialized bool false)

;; Asset maps
(define-map user-balances
  {
    user: principal,
    token-id: uint,
  }
  { amount: uint }
)
(define-map liquidity-providers
  principal
  {
    amount: uint,
    last-deposit-block: uint,
  }
)
(define-map token-pools
  uint
  {
    reserve: uint,
    weight: uint,
  }
)
(define-map bonding-curves
  uint
  {
    type: (string-ascii 20),
    params: (list 5 uint),
  }
)
(define-map staking-positions
  {
    user: principal,
    pool-id: uint,
  }
  {
    amount: uint,
    rewards: uint,
    start-block: uint,
    end-block: uint,
  }
)

;; Constants
(define-constant PRECISION_FACTOR u1000000) ;; 6 decimal places of precision
(define-constant MIN_LIQUIDITY u1000) ;; Minimum liquidity to start
(define-constant MAX_WEIGHT u1000000) ;; Maximum weight for a token (100%)
(define-constant BLOCKS_PER_YEAR u52560) ;; ~365 days with 10-minute blocks
(define-constant MAX_TOKEN_ID u1000000) ;; Maximum token ID allowed
(define-constant MAX_AMOUNT u1000000000000) ;; Maximum amount allowed (1 trillion)

;; Helper functions

;; Validate principal is not null
(define-private (validate-principal (address principal))
  (if (is-eq address tx-sender)
    true
    (if (is-eq address (var-get contract-owner))
      true
      (if (is-some (map-get? liquidity-providers address))
        true
        false
      )
    )
  )
)

;; Validate token ID is within range
(define-private (validate-token-id (token-id uint))
  (< token-id MAX_TOKEN_ID)
)

;; Validate amount is within range
(define-private (validate-amount (amount uint))
  (and (> amount u0) (< amount MAX_AMOUNT))
)

;; Validate weight is within range
(define-private (validate-weight (weight uint))
  (<= weight MAX_WEIGHT)
)

;; Validate curve type
(define-private (validate-curve-type (curve-type (string-ascii 20)))
  (or
    (is-eq curve-type "linear")
    (is-eq curve-type "exponential")
    (is-eq curve-type "constant")
  )
)

;; Validate curve params
(define-private (validate-curve-params (params (list 5 uint)))
  (and (>= (len params) u1) (<= (len params) u5))
)

;; Non-recursive power function (supports limited exponents)
(define-private (pow-uint
    (base uint)
    (exp uint)
  )
  (if (> exp u10)
    u0 ;; Return 0 for exponents that are too large
    (if (is-eq exp u0)
      u1
      (if (is-eq exp u1)
        base
        (if (is-eq exp u2)
          (* base base)
          (if (is-eq exp u3)
            (* base (* base base))
            (if (is-eq exp u4)
              (* base (* base (* base base)))
              (if (is-eq exp u5)
                (* base (* base (* base (* base base))))
                (if (is-eq exp u6)
                  (* base (* base (* base (* base (* base base)))))
                  (if (is-eq exp u7)
                    (* base (* base (* base (* base (* base (* base base))))))
                    (if (is-eq exp u8)
                      (* base
                        (* base (* base (* base (* base (* base (* base base))))))
                      )
                      (if (is-eq exp u9)
                        (* base
                          (* base
                            (* base
                              (* base (* base (* base (* base (* base base)))))
                            ))
                        )
                        (* base
                          (* base
                            (* base
                              (* base
                                (* base (* base (* base (* base (* base base)))))
                              ))
                          ))
                      )
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )
)

;; Read-only functions

;; Get balance for a specific token
(define-read-only (get-balance
    (user principal)
    (token-id uint)
  )
  (default-to u0
    (get amount
      (map-get? user-balances {
        user: user,
        token-id: token-id,
      })
    ))
)

;; Check if the caller is the contract owner
(define-read-only (is-owner)
  (is-eq tx-sender (var-get contract-owner))
)

;; Get token pool information
(define-read-only (get-pool-info (token-id uint))
  (map-get? token-pools token-id)
)

;; Get current protocol fee
(define-read-only (get-protocol-fee)
  (var-get protocol-fee-percent)
)

;; Get contract status
(define-read-only (get-contract-status)
  (var-get is-active)
)

;; Get total liquidity in the contract
(define-read-only (get-total-liquidity)
  (var-get total-liquidity)
)

;; Get liquidity provider information
(define-read-only (get-provider-info (provider principal))
  (map-get? liquidity-providers provider)
)

;; Get bonding curve for a token
(define-read-only (get-bonding-curve (token-id uint))
  (map-get? bonding-curves token-id)
)

;; Get staking position for a user
(define-read-only (get-staking-position
    (user principal)
    (pool-id uint)
  )
  (map-get? staking-positions {
    user: user,
    pool-id: pool-id,
  })
)

;; Calculate price using the bonding curve
(define-read-only (calculate-price
    (token-id uint)
    (amount uint)
  )
  (match (map-get? bonding-curves token-id)
    curve
    (let (
        (curve-type (get type curve))
        (curve-params (get params curve))
        (current-supply (default-to u0 (get reserve (map-get? token-pools token-id))))
      )
      (if (is-eq curve-type "linear")
        ;; Linear: price = m * supply + b
        ;; params[0] = m (slope), params[1] = b (y-intercept)
        (+ (* (default-to u0 (element-at? curve-params u0)) current-supply)
          (default-to u0 (element-at? curve-params u1))
        )

        (if (is-eq curve-type "exponential")
          ;; Exponential: price = a * (b ^ supply)
          ;; params[0] = a, params[1] = b (scaled by PRECISION_FACTOR)
          (let (
              (a (default-to u0 (element-at? curve-params u0)))
              (b (default-to u0 (element-at? curve-params u1)))
              (scaled-exp (/ (* b current-supply) PRECISION_FACTOR))
            )
            (* a (pow-uint u2 scaled-exp))
          )
          ;; Using base 2 with scaled exponent for simplicity

          ;; Constant: price is fixed
          ;; params[0] = fixed price
          (default-to u0 (element-at? curve-params u0))
        )
      )
    )
    ;; Return default value if bonding curve not found
    u0
  )
)

;; Calculate rewards for staking
(define-read-only (calculate-staking-rewards
    (user principal)
    (pool-id uint)
  )
  (match (map-get? staking-positions {
    user: user,
    pool-id: pool-id,
  })
    position
    (let (
        (staked-amount (get amount position))
        (start-block (get start-block position))
        (current-rewards (get rewards position))
        (blocks-staked (- stacks-block-height start-block))
        (reward-rate (/ (* staked-amount blocks-staked) BLOCKS_PER_YEAR))
      )
      (+ current-rewards reward-rate)
    )
    ;; Return 0 if no staking position found
    u0
  )
)

;; Calculate weighted price for a swap
(define-read-only (calculate-swap-price
    (token-in uint)
    (token-out uint)
    (amount-in uint)
  )
  (match (map-get? token-pools token-in)
    pool-in
    (match (map-get? token-pools token-out)
      pool-out
      (let (
          (reserve-in (get reserve pool-in))
          (reserve-out (get reserve pool-out))
          (weight-in (get weight pool-in))
          (weight-out (get weight pool-out))
          (fee (var-get protocol-fee-percent))
          (fee-amount (/ (* amount-in fee) u1000)) ;; fee is in 0.1% units
          (amount-in-after-fee (- amount-in fee-amount))
          (price-ratio (/ (* reserve-out weight-in) (* reserve-in weight-out)))
        )
        (/ (* amount-in-after-fee price-ratio) PRECISION_FACTOR)
      )
      ;; Return 0 if output pool not found
      u0
    )
    ;; Return 0 if input pool not found
    u0
  )
)

;; Public functions

;; Initialize the contract
(define-public (initialize (owner principal))
  (begin
    (asserts! (not (var-get contract-initialized)) ERR-ALREADY-INITIALIZED)
    ;; Validate owner principal
    (asserts! (validate-principal owner) ERR-INVALID-PRINCIPAL)
    ;; Set owner after validation
    (var-set contract-owner owner)
    (var-set contract-initialized true)
    (ok true)
  )
)

;; Update contract owner (owner only)
(define-public (set-owner (new-owner principal))
  (begin
    (asserts! (is-owner) ERR-OWNER-ONLY)
    ;; Validate new owner principal
    (asserts! (validate-principal new-owner) ERR-INVALID-PRINCIPAL)
    ;; Set new owner after validation
    (var-set contract-owner new-owner)
    (ok true)
  )
)

;; Update protocol fee (owner only)
(define-public (set-protocol-fee (new-fee uint))
  (begin
    (asserts! (is-owner) ERR-OWNER-ONLY)
    ;; Fee is in 0.1% units, max 5% (50)
    (asserts! (<= new-fee u50) ERR-INVALID-PARAMETER)
    (var-set protocol-fee-percent new-fee)
    (ok true)
  )
)

;; Activate/deactivate contract (owner only)
(define-public (set-contract-status (active bool))
  (begin
    (asserts! (is-owner) ERR-OWNER-ONLY)
    (var-set is-active active)
    (ok true)
  )
)

;; Create a new token pool
(define-public (create-pool
    (token-id uint)
    (initial-reserve uint)
    (weight uint)
  )
  (begin
    (asserts! (is-owner) ERR-OWNER-ONLY)
    (asserts! (var-get is-active) ERR-NOT-ACTIVE)

    ;; Validate inputs
    (asserts! (validate-token-id token-id) ERR-INVALID-TOKEN-ID)
    (asserts! (validate-amount initial-reserve) ERR-INVALID-AMOUNT)
    (asserts! (validate-weight weight) ERR-INVALID-WEIGHT)
    (asserts! (is-none (map-get? token-pools token-id)) ERR-INVALID-PARAMETER)

    ;; Create pool with validated inputs
    (map-set token-pools token-id {
      reserve: initial-reserve,
      weight: weight,
    })

    ;; Update total liquidity
    (var-set total-liquidity (+ (var-get total-liquidity) initial-reserve))

    (ok true)
  )
)

Functions (17)

FunctionAccessArgs
validate-principalprivateaddress: principal
validate-token-idprivatetoken-id: uint
validate-amountprivateamount: uint
validate-weightprivateweight: uint
validate-curve-typeprivatecurve-type: (string-ascii 20
validate-curve-paramsprivateparams: (list 5 uint
is-ownerread-only
get-pool-inforead-onlytoken-id: uint
get-protocol-feeread-only
get-contract-statusread-only
get-total-liquidityread-only
get-provider-inforead-onlyprovider: principal
get-bonding-curveread-onlytoken-id: uint
initializepublicowner: principal
set-ownerpublicnew-owner: principal
set-protocol-feepublicnew-fee: uint
set-contract-statuspublicactive: bool