Source Code

(use-trait ft-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)
(use-trait pool-trait .pool-trait.pool-trait)
(use-trait gas-oracle-trait .gas-oracle-trait.gas-oracle-trait)
(use-trait messenger-trait .messenger-trait.messenger-trait)

(define-constant err-unauthorized (err u1000))
(define-constant err-deposit-amount-too-small (err u1001))
(define-constant err-token-not-supported (err u1002))
(define-constant err-wrong-token-for-pool (err u1003))
(define-constant err-already-initialized (err u1004))
(define-constant err-already-sent (err u1005))
(define-constant err-not-initialized (err u1006))
(define-constant err-not-enough-fee (err u1007))
(define-constant err-gas-usage-not-set (err u1008))
(define-constant err-bridge-source-not-registered (err u1009))
(define-constant err-already-processed (err u1010))
(define-constant err-no-message (err u1011))
(define-constant err-swap-prohibited (err u1012))
(define-constant err-wrong-gas-oracle (err u1013))
(define-constant err-messenger-not-registered (err u1014))
(define-constant err-wrong-messenger (err u1015))
(define-constant err-wrong-destination-chain (err u1016))
(define-constant err-unknown-chain-or-token (err u1017))
(define-constant err-wrong-buffer-length (err u1018))
(define-constant err-empty-buffer (err u1019))
(define-constant err-wrong-msg-length (err u1020))

(define-constant chain-precision u6)
(define-constant oracle-precision u18)

(define-constant zero-bytes-12 0x000000000000000000000000)
(define-constant zero-bytes-16 0x00000000000000000000000000000000)
(define-constant zero-bytes-32 0x0000000000000000000000000000000000000000000000000000000000000000)

(define-data-var this-chain-id (optional uint) none)
(define-data-var gas-oracle-principal (optional principal) none)
(define-data-var owner principal contract-caller)
(define-data-var stop-authority principal contract-caller)
(define-data-var rebalancer principal contract-caller)
(define-data-var can-swap bool false)

(define-map token-infos
  (buff 32) ;; Token principal hash (keccak256(hash-bytes + name))
  {
    token: principal,
    pool: principal,
    from-gas-oracle-scaling-factor: uint,
    bridging-fee-conversion-scaling-factor: uint,
  }
)

(define-map processed-messages
  (buff 32)
  bool
)

(define-map messengers
  uint
  principal
)

(define-map sent-messages
  (buff 32)
  bool
)

(define-map other-bridges
  uint
  (buff 32)
)

(define-map other-bridge-tokens
  {
    chain-id: uint,
    token-address: (buff 32),
  }
  bool
)

(define-map gas-usage-map
  uint
  uint
)

(define-public (init
    (this-chain-id_ uint)
    (gas-oracle principal)
  )
  (begin
    (asserts! (is-none (var-get this-chain-id)) err-already-initialized)
    (var-set this-chain-id (some this-chain-id_))
    (var-set gas-oracle-principal (some gas-oracle))
    (only-owner)
  )
)

(define-public (swap-and-bridge
    (pool-ref <pool-trait>)
    (ft-ref <ft-trait>)
    (messenger-ref <messenger-trait>)
    (gas-oracle-ref <gas-oracle-trait>)
    (amount uint)
    (recipient (buff 32))
    (destination-chain-id uint)
    (receive-token (buff 32))
    (nonce (buff 32))
    (messenger-id uint)
    (fee-native-amount uint)
    (fee-token-amount uint)
  )
  (begin
    (try! (assert-can-swap))
    (try! (assert-gas-oracle gas-oracle-ref))
    (try! (assert-messenger messenger-ref messenger-id))
    (try! (assert-not-empty-buff-32 recipient))
    (try! (assert-not-empty-buff-32 receive-token))
    (try! (assert-not-empty-buff-32 nonce))
    (asserts! (> amount fee-token-amount) err-deposit-amount-too-small)
    (let (
        (fee-tokens-in-native (try! (convert-bridging-fee-in-tokens-to-native ft-ref gas-oracle-ref
          fee-token-amount
        )))
        (bridging-fee (+ fee-tokens-in-native fee-native-amount))
        (amount-after-fee (- amount fee-token-amount))
        (vusd-amount (try! (send-and-swap-to-vusd pool-ref ft-ref contract-caller amount-after-fee)))
      )
      (if (> fee-token-amount u0)
        (try! (contract-call? ft-ref transfer fee-token-amount contract-caller
          (as-contract contract-caller) none
        ))
        true
      )
      (if (> fee-native-amount u0)
        (try! (stx-transfer? fee-native-amount contract-caller
          (as-contract contract-caller)
        ))
        true
      )
      (print {
        event: "TokensSent",
        token: (contract-of ft-ref),
        sender: contract-caller,
        amount: vusd-amount,
        recipient: recipient,
        destinationChainId: destination-chain-id,
        receiveToken: receive-token,
        nonce: nonce,
        messenger: messenger-id,
      })
      (let (
          (send-fee (try! (send-tokens messenger-ref gas-oracle-ref vusd-amount recipient
            destination-chain-id receive-token nonce messenger-id bridging-fee
          )))
          (bridge-tx-cost (get bridge-tx-cost send-fee))
          (message-tx-cost (get message-tx-cost send-fee))
        )
        (print {
          event: "ReceiveFee",
          bridgeTransactionCost: bridge-tx-cost,
          messageTransactionCost: message-tx-cost,
          userPayNative: fee-native-amount,
          userPayTokens: fee-token-amount,
          totalFeeNative: bridging-fee,
          feeTokenInNative: fee-tokens-in-native,
        })
        (ok true)
      )
    )
  )
)

(define-public (receive-tokens
    (messenger-ref <messenger-trait>)
    (amount uint)
    (recipient principal)
    (source-chain-id uint)
    (pool-ref <pool-trait>)
    (ft-ref <ft-trait>)
    (nonce (buff 32))
    (messenger-id uint)
    (receive-amount-min uint)
    (extra-gas uint)
  )
  (begin
    (try! (assert-can-swap))
    (try! (assert-messenger messenger-ref messenger-id))
    (let (
        (another-bridge (unwrap! (map-get? other-bridges source-chain-id)
          err-bridge-source-not-registered
        ))
        (token-address (contract-of ft-ref))
        (token-principal-hash (principal-hash token-address))
        (recipient-buff32 (principal-to-buff32 recipient))
        (this-chain-id_ (unwrap! (var-get this-chain-id) err-not-initialized))
        (message-hash (try! (hash-message amount recipient-buff32 source-chain-id this-chain-id_
          token-principal-hash nonce messenger-id
        )))
        (message-hash-with-sender (try! (contract-call? messenger-ref hash-with-sender-address message-hash
          another-bridge
        )))
        (has-messenger-receive (unwrap-panic (contract-call? messenger-ref is-message-received
          message-hash-with-sender
        )))
        (receive-amount (try! (receive-and-swap-from-vusd pool-ref ft-ref recipient amount
          receive-amount-min
        )))
      )
      (asserts! (is-none (map-get? processed-messages message-hash-with-sender))
        err-already-processed
      )
      (map-set processed-messages message-hash-with-sender true)
      (asserts! has-messenger-receive err-no-message)
      (print {
        event: "TokensReceived",
        token: (contract-of ft-ref),
        extraGasAmount: extra-gas,
        amount: receive-amount,
        recipient: recipient,
        nonce: nonce,
        messenger: messenger-id,
        message: message-hash-with-sender,
      })
      (if (> extra-gas u0)
        (match (stx-transfer? extra-gas contract-caller recipient)
          value (ok true)
          err-value (ok true)
        )
        (ok true)
      )
    )
  )
)

(define-public (swap
    (amount uint)
    (send-pool-ref <pool-trait>)
    (send-ft-ref <ft-trait>)
    (receive-pool-ref <pool-trait>)
    (receive-ft-ref <ft-trait>)
    (recipient principal)
    (receive-amount-min uint)
  )
  (let (
      (vusd-amount (try! (send-and-swap-to-vusd send-pool-ref send-ft-ref contract-caller amount)))
      (receive-amount (try! (receive-and-swap-from-vusd receive-pool-ref receive-ft-ref recipient
        vusd-amount receive-amount-min
      )))
    )
    (print {
      event: "Swapped",
      sendToken: (contract-of send-ft-ref),
      receiveToken: (contract-of receive-ft-ref),
      sender: contract-caller,
      recipient: recipient,
      sendAmount: amount,
      receiveAmount: receive-amount,
    })
    (assert-can-swap)
  )
)

(define-private (convert-bridging-fee-in-tokens-to-native
    (ft-ref <ft-trait>)
    (gas-oracle-ref <gas-oracle-trait>)
    (fee-token-amount uint)
  )
  (let (
      (token-address (contract-of ft-ref))
      (this-chain-id_ (unwrap! (var-get this-chain-id) err-not-initialized))
      (price (try! (contract-call? gas-oracle-ref get-price this-chain-id_)))
      (token-principal-hash (principal-hash token-address))
      (token-info (unwrap! (map-get? token-infos token-principal-hash)
        err-token-not-supported
      ))
      (bridging-fee-conversion-scaling-factor (get bridging-fee-conversion-scaling-factor token-info))
      (fee (if (is-eq fee-token-amount u0)
        u0
        (/ (* fee-token-amount bridging-fee-conversion-scaling-factor) price)
      ))
    )
    (try! (assert-gas-oracle gas-oracle-ref))
    (print {
      event: "BridgingFeeFromTokens",
      gas: fee,
    })
    (ok fee)
  )
)

(define-private (receive-and-swap-from-vusd
    (pool-ref <pool-trait>)
    (ft-ref <ft-trait>)
    (recipient principal)
    (vusd-amount uint)
    (receive-amount-min uint)
  )
  (let (
      (token-address (contract-of ft-ref))
      (token-principal-hash (principal-hash token-address))
      (token-info (unwrap! (map-get? token-infos token-principal-hash)
        err-token-not-supported
      ))
      (pool-address (contract-of pool-ref))
    )
    (asserts! (is-eq pool-address (get pool token-info)) err-wrong-token-for-pool)
    (contract-call? pool-ref swap-from-vusd ft-ref recipient vusd-amount
      receive-amount-min (is-eq recipient (var-get rebalancer))
    )
  )
)

(define-private (send-and-swap-to-vusd
    (pool-ref <pool-trait>)
    (ft-ref <ft-trait>)
    (user principal)
    (amount uint)
  )
  (let (
      (token-address (contract-of ft-ref))
      (token-principal-hash (principal-hash token-address))
      (token-info (unwrap! (map-get? token-infos token-principal-hash)
        err-token-not-supported
      ))
      (pool-address (contract-of pool-ref))
    )
    (asserts! (is-eq pool-address (get pool token-info)) err-wrong-token-for-pool)
    (contract-call? pool-ref swap-to-vusd ft-ref user amount
      (is-eq user (var-get rebalancer))
    )
  )
)

(define-private (send-tokens
    (messenger-ref <messenger-trait>)
    (gas-oracle-ref <gas-oracle-trait>) ;; validated in get-transaction-cost
    (amount uint)
    (recipient (buff 32))
    (destination-chain-id uint)
    (receive-token (buff 32))
    (nonce (buff 32))
    (messenger-id uint)
    (bridging-fee uint)
  )
  (let (
      (this-chain-id_ (unwrap! (var-get this-chain-id) err-not-initialized))
      (message (try! (hash-message amount recipient this-chain-id_ destination-chain-id
        receive-token nonce messenger-id
      )))
      (bridge-tx-cost (try! (get-transaction-cost gas-oracle-ref destination-chain-id)))
      (message-tx-cost (try! (as-contract (contract-call? messenger-ref send-message gas-oracle-ref message))))
    )
    (asserts! (not (is-eq destination-chain-id this-chain-id_))
      err-wrong-destination-chain
    )
    (asserts!
      (is-eq
        (map-get? other-bridge-tokens {
          chain-id: destination-chain-id,
          token-address: receive-token,
        })
        (some true)
      )
      err-unknown-chain-or-token
    )
    (asserts! (is-none (map-get? sent-messages message)) err-already-sent)
    (asserts! (>= bridging-fee (+ bridge-tx-cost message-tx-cost))
      err-not-enough-fee
    )
    (map-set sent-messages message true)
    (ok {
      bridge-tx-cost: bridge-tx-cost,
      message-tx-cost: message-tx-cost,
    })
  )
)

;; ============================ ADMIN ============================

(define-public (add-pool
    (pool-ref <pool-trait>)
    (ft-ref <ft-trait>)
  )
  (let (
      (toke-decimals (unwrap-panic (contract-call? ft-ref get-decimals)))
      (token-address (contract-of ft-ref))
      (token-address-from-pool (try! (contract-call? pool-ref get-token-address)))
      (pool-address (contract-of pool-ref))
      (bridging-fee-conversion-scaling-factor (pow u10 (+ (- oracle-precision toke-decimals) chain-precision)))
      (from-gas-oracle-scaling-factor (pow u10 (- oracle-precision toke-decimals)))
      (token-principal-hash (principal-hash token-address))
    )
    (asserts! (is-eq token-address-from-pool token-address)
      err-wrong-token-for-pool
    )
    (map-set token-infos token-principal-hash {
      pool: pool-address,
      token: token-address,
      from-gas-oracle-scaling-factor: from-gas-oracle-scaling-factor,
      bridging-fee-conversion-scaling-factor: bridging-fee-conversion-scaling-factor,
    })
    (only-owner)
  )
)

(define-public (register-bridge
    (chain-id_ uint)
    (bridge-address (buff 32))
  )
  (begin
    (map-set other-bridges chain-id_ bridge-address)
    (only-owner)
  )
)

(define-public (remove-bridge (chain-id_ uint))
  (begin
    (map-delete other-bridges chain-id_)
    (only-owner)
  )
)

(define-public (add-bridge-token
    (chain-id_ uint)
    (token-address (buff 32))
  )
  (begin
    (map-set other-bridge-tokens {
      chain-id: chain-id_,
      token-address: token-address,
    }
      true
    )
    (only-owner)
  )
)

(define-public (remove-bridge-token
    (chain-id_ uint)
    (token-address (buff 32))
  )
  (begin
    (map-delete other-bridge-tokens {
      chain-id: chain-id_,
      token-address: token-address,
    })
    (only-owner)
  )
)

(define-public (withdraw-gas-tokens (amount uint))
  (let ((caller contract-caller))
    (try! (as-contract (stx-transfer? amount tx-sender caller)))
    (only-owner)
  )
)

(define-public (withdraw-bridging-fee-in-tokens
    (ft-ref <ft-trait>)
    (amount uint)
  )
  (let ((caller contract-caller))
    (try! (as-contract (contract-call? ft-ref transfer amount tx-sender caller none)))
    (only-owner)
  )
)

(define-public (start-swap)
  (begin
    (var-set can-swap true)
    (only-owner)
  )
)

(define-public (stop-swap)
  (begin
    (var-set can-swap false)
    (only-stop-authority)
  )
)

(define-public (set-stop-authority (new-authority principal))
  (begin
    (var-set stop-authority new-authority)
    (only-owner)
  )
)

(define-public (set-rebalancer (new-rebalancer principal))
  (begin
    (var-set rebalancer new-rebalancer)
    (only-owner)
  )
)

(define-public (set-messenger
    (messenger-id uint)
    (new-messenger principal)
  )
  (begin
    (map-set messengers messenger-id new-messenger)
    (only-owner)
  )
)

(define-public (remove-messenger (messenger-id uint))
  (begin
    (map-delete messengers messenger-id)
    (only-owner)
  )
)

;; ============================ VIEW =============================

(define-read-only (get-this-chain-id)
  (unwrap-panic (var-get this-chain-id))
)

(define-read-only (get-gas-oracle-address)
  (unwrap-panic (var-get gas-oracle-principal))
)

(define-read-only (get-owner)
  (var-get owner)
)

(define-read-only (get-stop-authority)
  (var-get stop-authority)
)

(define-read-only (get-rebalancer)
  (var-get rebalancer)
)

(define-read-only (is-swap-enabled)
  (var-get can-swap)
)

(define-read-only (get-sent-message-status (message-hash (buff 32)))
  (ok (is-eq (map-get? sent-messages message-hash) (some true)))
)

(define-read-only (get-processed-message-status (message-hash (buff 32)))
  (ok (is-eq (map-get? processed-messages message-hash) (some true)))
)

(define-read-only (get-messenger (messenger-id uint))
  (ok (unwrap! (map-get? messengers messenger-id) err-messenger-not-registered))
)

(define-read-only (get-other-bridge (chain-id_ uint))
  (ok (unwrap! (map-get? other-bridges chain-id_) err-bridge-source-not-registered))
)

(define-read-only (is-bridge-token-supported
    (chain-id_ uint)
    (token-address (buff 32))
  )
  (ok (is-eq
    (map-get? other-bridge-tokens {
      chain-id: chain-id_,
      token-address: token-address,
    })
    (some true)
  ))
)

(define-read-only (get-token-info (token-principal-hash (buff 32)))
  (ok (unwrap! (map-get? token-infos token-principal-hash) err-token-not-supported))
)

;; ========================== GAS USAGE ==========================

(define-public (set-gas-usage
    (chain-id_ uint)
    (gas uint)
  )
  (begin
    (map-set gas-usage-map chain-id_ gas)
    (only-owner)
  )
)

(define-read-only (get-gas-usage (chain-id_ uint))
  (match (map-get? gas-usage-map chain-id_)
    gas-usage (ok gas-usage)
    err-gas-usage-not-set
  )
)

(define-public (get-transaction-cost
    (gas-oracle-ref <gas-oracle-trait>)
    (chain-id_ uint)
  )
  (begin
    (try! (assert-gas-oracle gas-oracle-ref))
    (let (
        (gas-usage (try! (get-gas-usage chain-id_)))
        (tx-const (try! (contract-call? gas-oracle-ref get-transaction-gas-cost-in-native-token
          chain-id_ gas-usage
        )))
      )
      (ok (if (is-eq tx-const u0)
        u1
        tx-const
      ))
    )
  )
)

;; ========================= OWNABLE ==========================

(define-public (set-owner (new-owner principal))
  (begin
    (try! (only-owner))
    (var-set owner new-owner)
    (ok true)
  )
)

(define-private (only-owner)
  (if (is-eq contract-caller (var-get owner))
    (ok true)
    err-unauthorized
  )
)

(define-private (only-stop-authority)
  (if (is-eq contract-caller (var-get stop-authority))
    (ok true)
    err-unauthorized
  )
)

;; ========================= ASSERTS ==========================

(define-private (assert-can-swap)
  (if (var-get can-swap)
    (ok true)
    err-swap-prohibited
  )
)

(define-private (assert-gas-oracle (gas-oracle-ref <gas-oracle-trait>))
  (let (
      (registered-gas-oracle (unwrap! (var-get gas-oracle-principal) err-not-initialized))
      (gas-oracle-address (contract-of gas-oracle-ref))
    )
    (asserts! (is-eq registered-gas-oracle gas-oracle-address)
      err-wrong-gas-oracle
    )
    (ok true)
  )
)

(define-private (assert-messenger
    (messenger-ref <messenger-trait>)
    (messenger-id uint)
  )
  (let (
      (registered-messenger (unwrap! (map-get? messengers messenger-id) err-messenger-not-registered))
      (messenger-address (contract-of messenger-ref))
    )
    (asserts! (is-eq registered-messenger messenger-address) err-wrong-messenger)
    (ok true)
  )
)

(define-private (assert-not-empty-buff-32 (b (buff 32)))
  (begin
    (asserts! (is-eq (len b) u32) err-wrong-buffer-length)
    (asserts! (not (is-eq b zero-bytes-32)) err-empty-buffer)
    (ok true)
  )
)

;; ========================= HELPERS ==========================

(define-read-only (hash-message
    (amount uint)
    (recipient (buff 32))
    (source-chain-id uint)
    (destination-chain-id uint)
    (receive-token (buff 32))
    (nonce (buff 32))
    (messenger-id uint)
  )
  (let (
      (msg1 (uint-to-buff-32 amount))
      (msg2 (concat msg1 recipient))
      (msg3 (concat msg2 (uint-to-buff-32 source-chain-id)))
      (msg4 (concat msg3 receive-token))
      (msg5 (concat msg4 nonce))
      (msg (concat msg5 (uint-to-buff-1 messenger-id)))
      (msg-hash (keccak256 msg))
      (last-30-bytes (unwrap! (slice? msg-hash u2 u32) err-wrong-buffer-length))
      (first-2-bytes (concat (uint-to-buff-1 source-chain-id)
        (uint-to-buff-1 destination-chain-id)
      ))
    )
    (ok (unwrap! (as-max-len? (concat first-2-bytes last-30-bytes) u32)
      err-wrong-msg-length
    ))
  )
)

;; ========================= PRIVATE HELPERS ==========================

(define-private (principal-to-buff32 (value principal))
  (concat zero-bytes-12
    (get hash-bytes (unwrap-panic (principal-destruct? value)))
  )
)

(define-private (uint-to-buff-32 (value uint))
  (concat zero-bytes-16
    (unwrap-panic (slice? (unwrap-panic (to-consensus-buff? value)) u1 u17))
  )
)

(define-private (uint-to-buff-1 (value uint))
  (unwrap-panic (slice? (unwrap-panic (to-consensus-buff? value)) u16 u17))
)

(define-private (principal-hash (value principal))
  (let (
      (dest (unwrap-panic (principal-destruct? value)))
      (hash-bytes (get hash-bytes dest))
      (name (get name dest))
      (name-bytes (match name
        v (string-to-buff v)
        0x
      ))
    )
    (keccak256 (concat hash-bytes name-bytes))
  )
)

(define-private (string-to-buff (str (string-ascii 40)))
  (let (
      (str-bytes (unwrap-panic (to-consensus-buff? str)))
      (length (len str-bytes))
    )
    (unwrap-panic (slice? str-bytes u5 length))
  )
)

Functions (37)

FunctionAccessArgs
swap-and-bridgepublicpool-ref: <pool-trait>, ft-ref: <ft-trait>, messenger-ref: <messenger-trait>, gas-oracle-ref: <gas-oracle-trait>, amount: uint, recipient: (buff 32
receive-tokenspublicmessenger-ref: <messenger-trait>, amount: uint, recipient: principal, source-chain-id: uint, pool-ref: <pool-trait>, ft-ref: <ft-trait>, nonce: (buff 32
register-bridgepublicchain-id_: uint, bridge-address: (buff 32
remove-bridgepublicchain-id_: uint
add-bridge-tokenpublicchain-id_: uint, token-address: (buff 32
remove-bridge-tokenpublicchain-id_: uint, token-address: (buff 32
withdraw-gas-tokenspublicamount: uint
start-swappublic
stop-swappublic
set-stop-authoritypublicnew-authority: principal
set-rebalancerpublicnew-rebalancer: principal
remove-messengerpublicmessenger-id: uint
get-this-chain-idread-only
get-gas-oracle-addressread-only
get-ownerread-only
get-stop-authorityread-only
get-rebalancerread-only
is-swap-enabledread-only
get-sent-message-statusread-onlymessage-hash: (buff 32
get-processed-message-statusread-onlymessage-hash: (buff 32
get-messengerread-onlymessenger-id: uint
get-other-bridgeread-onlychain-id_: uint
is-bridge-token-supportedread-onlychain-id_: uint, token-address: (buff 32
get-token-inforead-onlytoken-principal-hash: (buff 32
get-gas-usageread-onlychain-id_: uint
set-ownerpublicnew-owner: principal
only-ownerprivate
only-stop-authorityprivate
assert-can-swapprivate
assert-gas-oracleprivategas-oracle-ref: <gas-oracle-trait>
assert-not-empty-buff-32privateb: (buff 32
hash-messageread-onlyamount: uint, recipient: (buff 32
principal-to-buff32privatevalue: principal
uint-to-buff-32privatevalue: uint
uint-to-buff-1privatevalue: uint
principal-hashprivatevalue: principal
string-to-buffprivatestr: (string-ascii 40