Source Code

;; title: escrow-service
;; version:
;; summary:
;; description:

;; Commodities Trading Contract
;; Implements secure trading of commodities with price feeds, escrow, and trading functionality

;; Error codes
(define-constant ERROR-UNAUTHORIZED-ACCESS (err u100))
(define-constant ERROR-INVALID-COMMODITY-PRICE (err u101))
(define-constant ERROR-INSUFFICIENT-ESCROW-BALANCE (err u102))
(define-constant ERROR-TRADING-DISABLED (err u103))
(define-constant ERROR-INVALID-TRADE-QUANTITY (err u104))
(define-constant ERROR-ESCROW-TRANSACTION-FAILED (err u105))
(define-constant ERROR-INVALID-INPUT (err u106))

;; Data Variables
(define-data-var contract-administrator principal tx-sender)
(define-data-var commodity-price-oracle principal tx-sender)
(define-data-var market-trading-status bool true)
(define-data-var minimum-trade-quantity uint u100)

;; Data Maps
(define-map available-commodities-inventory
  { commodity-identifier: uint }
  {
    available-quantity: uint,
    current-market-price: uint,
    commodity-owner: principal,
  }
)

(define-map active-trading-positions
  {
    trader-address: principal,
    trade-position-id: uint,
  }
  {
    commodity-identifier: uint,
    traded-quantity: uint,
    position-entry-price: uint,
    position-creation-timestamp: uint,
  }
)

(define-map trader-escrow-accounts
  { trader-address: principal }
  { escrow-balance: uint }
)

;; Read-only functions
(define-read-only (get-commodity-market-data (commodity-identifier uint))
  (match (map-get? available-commodities-inventory { commodity-identifier: commodity-identifier })
    commodity-market-data (ok commodity-market-data)
    (err u404)
  )
)

(define-read-only (get-trader-position-details
    (trader-address principal)
    (trade-position-id uint)
  )
  (match (map-get? active-trading-positions {
    trader-address: trader-address,
    trade-position-id: trade-position-id,
  })
    trader-position-data (ok trader-position-data)
    (err u404)
  )
)

(define-read-only (get-trader-escrow-balance (trader-address principal))
  (match (map-get? trader-escrow-accounts { trader-address: trader-address })
    escrow-account-data (ok (get escrow-balance escrow-account-data))
    (err u404)
  )
)

;; Private functions
(define-private (verify-administrator-access (caller-address principal))
  (if (is-eq caller-address (var-get contract-administrator))
    (ok true)
    ERROR-UNAUTHORIZED-ACCESS
  )
)

(define-private (validate-trade-parameters
    (trade-quantity uint)
    (trade-price uint)
  )
  (if (and
      (>= trade-quantity (var-get minimum-trade-quantity))
      (> trade-price u0)
    )
    (ok true)
    ERROR-INVALID-TRADE-QUANTITY
  )
)

(define-private (validate-uint (value uint))
  (> value u0)
)

;; Public functions
(define-public (update-price-oracle-address (new-oracle-address principal))
  (begin
    (try! (verify-administrator-access tx-sender))
    (asserts! (is-some (some new-oracle-address)) ERROR-INVALID-INPUT)
    (ok (var-set commodity-price-oracle new-oracle-address))
  )
)

(define-public (toggle-market-trading-status)
  (begin
    (try! (verify-administrator-access tx-sender))
    (ok (var-set market-trading-status (not (var-get market-trading-status))))
  )
)

(define-public (register-new-commodity
    (commodity-identifier uint)
    (initial-available-quantity uint)
    (initial-market-price uint)
  )
  (begin
    (try! (verify-administrator-access tx-sender))
    (asserts! (validate-uint commodity-identifier) ERROR-INVALID-INPUT)
    (asserts! (validate-uint initial-available-quantity) ERROR-INVALID-INPUT)
    (asserts! (validate-uint initial-market-price) ERROR-INVALID-INPUT)
    (try! (validate-trade-parameters initial-available-quantity initial-market-price))
    (map-set available-commodities-inventory { commodity-identifier: commodity-identifier } {
      available-quantity: initial-available-quantity,
      current-market-price: initial-market-price,
      commodity-owner: tx-sender,
    })
    (ok true)
  )
)

(define-public (execute-trade
    (commodity-identifier uint)
    (trade-quantity uint)
    (trade-position-id uint)
  )
  (let (
      (commodity-data (unwrap! (get-commodity-market-data commodity-identifier)
        ERROR-INVALID-COMMODITY-PRICE
      ))
      (current-market-price (get current-market-price commodity-data))
      (total-trade-cost (* trade-quantity current-market-price))
    )
    (begin
      (asserts! (var-get market-trading-status) ERROR-TRADING-DISABLED)
      (asserts! (validate-uint commodity-identifier) ERROR-INVALID-INPUT)
      (asserts! (validate-uint trade-quantity) ERROR-INVALID-INPUT)
      (asserts! (validate-uint trade-position-id) ERROR-INVALID-INPUT)
      (try! (validate-trade-parameters trade-quantity current-market-price))

      ;; Check escrow balance
      (match (map-get? trader-escrow-accounts { trader-address: tx-sender })
        escrow-account-data (if (>= (get escrow-balance escrow-account-data) total-trade-cost)
          (begin
            ;; Update escrow balance
            (map-set trader-escrow-accounts { trader-address: tx-sender } { escrow-balance: (- (get escrow-balance escrow-account-data) total-trade-cost) })

            ;; Record trading position
            (map-set active-trading-positions {
              trader-address: tx-sender,
              trade-position-id: trade-position-id,
            } {
              commodity-identifier: commodity-identifier,
              traded-quantity: trade-quantity,
              position-entry-price: current-market-price,
              position-creation-timestamp: stacks-block-height,
            })
            (ok true)
          )
          ERROR-INSUFFICIENT-ESCROW-BALANCE
        )
        ERROR-INSUFFICIENT-ESCROW-BALANCE
      )
    )
  )
)

(define-public (close-trading-position (trade-position-id uint))
  (let (
      (position-data (unwrap! (get-trader-position-details tx-sender trade-position-id)
        ERROR-INVALID-TRADE-QUANTITY
      ))
      (commodity-data (unwrap!
        (get-commodity-market-data (get commodity-identifier position-data))
        ERROR-INVALID-COMMODITY-PRICE
      ))
      (current-market-price (get current-market-price commodity-data))
      (position-settlement-amount (* (get traded-quantity position-data) current-market-price))
    )
    (begin
      (asserts! (validate-uint trade-position-id) ERROR-INVALID-INPUT)
      ;; Return funds to escrow account
      (match (map-get? trader-escrow-accounts { trader-address: tx-sender })
        escrow-account-data (map-set trader-escrow-accounts { trader-address: tx-sender } { escrow-balance: (+ (get escrow-balance escrow-account-data) position-settlement-amount) })
        (map-set trader-escrow-accounts { trader-address: tx-sender } { escrow-balance: position-settlement-amount })
      )

      ;; Delete the position
      (map-delete active-trading-positions {
        trader-address: tx-sender,
        trade-position-id: trade-position-id,
      })
      (ok true)
    )
  )
)

;; Contract initialization
(define-public (initialize-contract (administrator-address principal))
  (begin
    (asserts! (is-some (some administrator-address)) ERROR-INVALID-INPUT)
    (var-set contract-administrator administrator-address)
    (var-set commodity-price-oracle administrator-address)
    (ok true)
  )
)

Functions (8)

FunctionAccessArgs
get-commodity-market-dataread-onlycommodity-identifier: uint
get-trader-escrow-balanceread-onlytrader-address: principal
verify-administrator-accessprivatecaller-address: principal
validate-uintprivatevalue: uint
update-price-oracle-addresspublicnew-oracle-address: principal
toggle-market-trading-statuspublic
close-trading-positionpublictrade-position-id: uint
initialize-contractpublicadministrator-address: principal