Source Code

;; title: intent-filler-impl
;; version: 1.0.0
;; summary: Intent Filler Implementation
;; description: Implements a cross-chain intent filler by facilitating token transfers between chains.

;; Traits:
;;  - ft-trait: SIP-010 fungible token interface for token operations.
;;  - connection-contract: connection interface for cross-chain message verification.
(use-trait ft-trait .sip-010-trait.sip-010-trait)
(use-trait connection-contract .connection-trait.connection-trait)

;; Constants:
;;  - Defined error codes for clear revert reasons.
(define-constant err-not-admin (err u3100))
(define-constant err-not-connection-contract (err u3101))
(define-constant err-amount-invalid (err u3102))
(define-constant err-already-initialized (err u3103))
(define-constant err-signature-verification-failed (err u3104))
(define-constant err-token-mismatch (err u3105))
(define-constant err-withdraw-failed (err u3106))
(define-constant err-not-sent-from-hub (err u3107))
(define-constant err-not-hub-intent (err u3108))
(define-constant err-fill-already-exists (err u3109))
(define-constant err-receiver-address-invalid (err u3110))
(define-constant err-fill-not-found (err u3111))
(define-constant err-not-filler (err u3112))
(define-constant err-invalid-chain-id (err u3113))


;;  - NATIVE_TOKEN constant representing the native token principal.
(define-constant NATIVE_TOKEN 'ST000000000000000000002AMW42H.nativetoken)

;; Public Functions:
;;  - init: Initialize contract with connection, hub chain ID, and hub intent contract.
;;  - set-intent-filler-impl: Admin function to update intent filler implementation address.
;;  - set-connection-impl: Admin function to update connection implementation address.
;;  - set-admin: Admin function to update admin principal.
;;  - fill-intent: Create and store a fill intent; deposits tokens.
;;  - cancel-fill: Cancel an existing fill and withdraw tokens.
;;  - recv-message: Receive and verify cross-chain messages; execute fills.
;;  - get-remaining: Read-only function to decode payload and retrieve remaining params for recv-message.
;;  - is-admin: Read-only function that asserts the caller is admin.
;;  - is-not-initialized: Read-only function that asserts contract is not initialized.

;; Private Functions:
;;  - are-token-matched: Validate the token trait passed matches the expected token.
;;  - decode-fill: Decode a fill message from raw bytes.
;;  - encode-fill-response: Encode a fill response message for sending.

;; --- Initialization ---
;; Initializes the contract with the connection contract, hub chain ID, and hub intent contract.
;; Only callable by admin and if contract not already initialized.
(define-public (init (connection principal) (hub-chain-id uint) (hub-intent (buff 256)))
  (begin
    (try! (is-admin))
    (try! (is-not-initialized))
    (asserts! (> hub-chain-id u0) err-invalid-chain-id)
    (try!  (contract-call? .intent-filler-state set-connection-impl connection))
    (try!  (contract-call? .intent-filler-state set-hub-chain-id hub-chain-id))
    (try! (contract-call? .intent-filler-state set-hub-intent hub-intent))
    (ok true)
  )
)

;; --- Admin Setter Functions ---
;; All require caller to be admin.
;;
;; Updates the intent filler implementation contract principal.
(define-public (set-intent-filler-impl (who principal))
  (begin
    (try! (is-admin))
    (contract-call? .intent-filler-state set-intent-filler-impl who)
  )
)

;; Updates the connection implementation contract principal.
(define-public (set-connection-impl (who principal))
  (begin
    (try! (is-admin))
    (contract-call? .intent-filler-state set-connection-impl who)
  )
)

;; --- Fill Management ---

;; Creates a fill intent by storing its hash and depositing the specified amount of tokens.
;; Requires the fill not to already exist and valid token matching.
;; Emits "FillCreated" event on success.
(define-public (fill-intent 
  (intent-data 
      { fill-id: uint,
        intent-hash: (buff 32),
        solver: (buff 100),
        receiver: (buff 100),
        token: (buff 100),
        amount: uint})
  (token (optional <ft-trait>))
)
    (let (
      (amount (get amount intent-data))
      (receiver (get receiver intent-data))    
      (fill-hash (keccak256 (unwrap-panic (to-consensus-buff? intent-data))))
      (filled (contract-call? .intent-filler-state is-hash-filled fill-hash))  
    )

    (try! (are-token-matched token (get token intent-data)))
    (asserts! (> amount u0) err-amount-invalid)
    (asserts! (> (len receiver) u0) err-receiver-address-invalid)
    (asserts! (not filled) err-fill-already-exists)
    (try! (contract-call? .intent-filler-state deposit token amount))
    (try! (contract-call? .intent-filler-state set-fill fill-hash true))
    (try! (contract-call? .intent-filler-state set-filler fill-hash contract-caller))
    (ok (print {
      event: "FillCreated",
      creator: contract-caller,
      fill-hash: fill-hash,
      amount: amount
    }))
  )
  )

;; Cancels a previously created fill if the caller is the filler.
;; Removes stored fill and filler records, withdraws tokens to caller.
;; Emits "FillCancelled" event on success.
(define-public (cancel-fill 
  (intent-data 
      { fill-id: uint,
        intent-hash: (buff 32),
        solver: (buff 100),
        receiver: (buff 100),
        token: (buff 100),
        amount: uint})
  (token (optional <ft-trait>))
)
    (let (
      (amount (get amount intent-data))
      (fill-hash (keccak256 (unwrap-panic (to-consensus-buff? intent-data))))
      (filled (contract-call? .intent-filler-state is-hash-filled fill-hash)) 
      (filler (unwrap! (contract-call? .intent-filler-state get-filler fill-hash) err-not-filler)) 
    )
    (try! (are-token-matched token (get token intent-data)))
    (asserts! filled err-fill-not-found)
    (asserts! (is-eq contract-caller filler) err-not-filler)

    (try! (contract-call? .intent-filler-state delete-fill fill-hash))
    (try! (contract-call? .intent-filler-state delete-filler fill-hash))
    
    (try! (contract-call? .intent-filler-state withdraw token amount contract-caller))
    (ok (print {
      event: "FillCancelled",
      canceller: contract-caller,
      fill-hash: fill-hash,
      amount: amount
    }))
  )
  )

;; --- Cross-Chain Message Receiver ---

;; Verifies and processes cross-chain fill messages.
;; Checks that the connection contract matches the stored approved contract.
;; Verifies message signatures via connection contract.
;; Confirms message is from authorized hub chain and hub intent contract.
;; Decodes fill message and, if a fill exists, executes withdrawal to the receiver and deletes fill record.
;; Sends a fill-response message back to the hub.
(define-public (recv-message 
  (src-chain-id uint)
  (src-address (buff 256))
  (conn-sn uint)
  (payload (buff 4096))
  (signatures (list 50 (buff 65)))
  (connection <connection-contract>)
  (token (optional <ft-trait>))
)
  (let (
    (current-connection-contract (contract-call? .intent-filler-state get-connection-impl))
  )
    ;; Ensure the connection contract is correct
    (asserts! 
      (is-eq (contract-of connection) current-connection-contract)
      err-not-connection-contract
    )

    ;; Ensure the message is sent from the hub chain and the hub intent contract
    (asserts! 
      (is-eq src-chain-id (contract-call? .intent-filler-state get-hub-chain-id))
      err-not-sent-from-hub
    )

    (asserts! 
      (is-eq src-address (unwrap-panic (contract-call? .intent-filler-state get-hub-intent)))
      err-not-hub-intent
    )

    ;; Verify message signature
    (try! 
      (contract-call? .intent-filler-state verify-message src-chain-id src-address conn-sn payload signatures connection)
    )

    (let (
      (message (unwrap-panic (decode-fill payload)))
      (fill-id (get fill-id message))
      (intent-hash (get intent-hash message))
      (solver (get solver message))
      (receiver (unwrap-panic (from-consensus-buff? principal (get receiver message))))
      (amount (get amount message))
      (fill-hash (keccak256 (unwrap-panic (to-consensus-buff? message))))
      (has-fill (contract-call? .intent-filler-state is-hash-filled fill-hash))
      (fill-response (encode-fill-response fill-id (if has-fill u1 u0)))
    )

      ;; Ensure token matches the one expected
      (try! (are-token-matched token (get token message)))
      ;; Execute fill if it exists 
        (if has-fill
          (begin 
            (try! (contract-call? .intent-filler-state delete-fill fill-hash))
            (try! (contract-call? .intent-filler-state delete-filler fill-hash))
            (try! (contract-call? .intent-filler-state withdraw token amount receiver))
            (print {
              event: "FillExecuted",
              fill-hash: fill-hash,
              receiver: receiver,
              amount: amount
            })
            true
          )
          true
        )

      ;; Send the fill-response back to the hub
      (ok (try!
        (contract-call? .intent-filler-state send-message src-chain-id src-address fill-response connection)
      ))
      ;; (ok true)
    )
  )
)

;; --- Utility Functions ---

;; Checks that the token matches the one expected
(define-private (are-token-matched (token (optional <ft-trait>)) (token-buff (buff 4096)))
(let (
  (token-addr (unwrap-panic (from-consensus-buff? principal token-buff)))
  (token-trait (match token ft-token (contract-of ft-token) NATIVE_TOKEN))
)
  (ok (asserts! (is-eq token-trait token-addr) err-token-mismatch))
)
)

;; Encode and Decode
(define-private (decode-fill (raw (buff 4096)))
  (let (
    (rlp-list (contract-call? .rlp-decode rlp-to-list raw))
    (fill-id (contract-call? .rlp-decode rlp-decode-uint rlp-list u0))
    (intent-hash (unwrap-panic (element-at? rlp-list u1)))
    (solver (unwrap-panic (element-at? rlp-list u2)))
    (receiver (unwrap-panic (element-at? rlp-list u3)))
    (token (unwrap-panic (element-at? rlp-list u4)))
    (amount (contract-call? .rlp-decode rlp-decode-uint rlp-list u5))
  )
    (ok {
      fill-id: fill-id,
      intent-hash: intent-hash,
      solver: solver,
      receiver: receiver,
      token: token,
      amount: amount
    })
  )
)

;; Encodes a fill response message (fill ID and filled flag) into RLP bytes.
(define-private (encode-fill-response (fill-id uint) (filled uint))
    (contract-call? .rlp-encode encode-arr
      (list
        (contract-call? .rlp-encode encode-uint fill-id)
        (contract-call? .rlp-encode encode-uint filled)
      )
    )
)

;; read only functions
(define-read-only (get-remaining (payload (buff 4096)))
  (let (
          (message (unwrap-panic (decode-fill payload)))
          (token-addr (unwrap-panic (from-consensus-buff? principal (get token message))))
          (token-trait (if (is-eq token-addr NATIVE_TOKEN) none (some token-addr)))
        )
        {token: token-trait}
))

;; guard read only functions
(define-read-only (is-admin)
    (ok (asserts! (is-eq contract-caller (contract-call? .intent-filler-state get-admin)) err-not-admin))
)

(define-read-only (is-not-initialized)
  (ok (asserts! (is-eq (contract-call? .intent-filler-state get-hub-chain-id) u0) err-already-initialized))
)

Functions (10)

FunctionAccessArgs
initpublicconnection: principal, hub-chain-id: uint, hub-intent: (buff 256
set-intent-filler-implpublicwho: principal
set-connection-implpublicwho: principal
recv-messagepublicsrc-chain-id: uint, src-address: (buff 256
are-token-matchedprivatetoken: (optional <ft-trait>
decode-fillprivateraw: (buff 4096
encode-fill-responseprivatefill-id: uint, filled: uint
get-remainingread-onlypayload: (buff 4096
is-adminread-only
is-not-initializedread-only