meta-peg-out-endpoint-v2-02

SP673Z4BPB4R73359K9HE55F2X91V5BJTN5SXZ5T

Source Code

(use-trait ft-trait 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.trait-sip-010.sip-010-trait)

(define-constant err-unauthorised (err u1000))
(define-constant err-paused (err u1001))
(define-constant err-peg-in-address-not-found (err u1002))
(define-constant err-invalid-amount (err u1003))
(define-constant err-token-mismatch (err u1004))
(define-constant err-invalid-tx (err u1005))
(define-constant err-already-sent (err u1006))
(define-constant err-address-mismatch (err u1007))
(define-constant err-request-already-revoked (err u1008))
(define-constant err-request-already-finalized (err u1009))
(define-constant err-revoke-grace-period (err u1010))
(define-constant err-request-already-claimed (err u1011))
(define-constant err-invalid-input (err u1012))
(define-constant err-tx-mined-before-request (err u1013))
(define-constant err-commit-tx-mismatch (err u1014))
(define-constant err-invalid-burn-height (err u1003))
(define-constant err-tx-mined-before-start (err u1015))

(define-constant MAX_UINT u340282366920938463463374607431768211455)
(define-constant ONE_8 u100000000)

(define-constant burn-height-start burn-block-height)

(define-data-var paused bool true)
(define-data-var fee-to-address principal tx-sender)

;; read-only functions

(define-read-only (is-dao-or-extension)
	(ok (asserts! (or (is-eq tx-sender 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.executor-dao) (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.executor-dao is-extension contract-caller)) err-unauthorised)))

(define-read-only (is-paused)
	(var-get paused))

(define-read-only (get-fee-to-address)
  (var-get fee-to-address))

(define-read-only (get-pair-details (pair { token: principal, chain-id: uint }))
  (match (contract-call? .meta-bridge-registry-v2-02 get-pair-details-or-fail pair)
    ok-value (some ok-value)
    err-value none))

(define-read-only (get-pair-details-many (pairs (list 200 { token: principal, chain-id: uint })))
  (map get-pair-details pairs))

(define-read-only (get-request (request-id uint))
  (match (contract-call? .meta-bridge-registry-v2-02 get-request-or-fail request-id)
    ok-value (some ok-value)
    err-value none))

(define-read-only (get-request-many (request-ids (list 200 uint)))
  (map get-request request-ids))

(define-read-only (get-request-revoke-grace-period)
  (contract-call? .meta-bridge-registry-v2-02 get-request-revoke-grace-period))

(define-read-only (get-request-claim-grace-period)
  (contract-call? .meta-bridge-registry-v2-02 get-request-claim-grace-period))

(define-read-only (is-peg-in-address-approved (address (buff 128)))
  (contract-call? .meta-bridge-registry-v2-02 is-peg-in-address-approved address))

(define-read-only (get-pair-details-or-fail (pair { token: principal, chain-id: uint }))
  (contract-call? .meta-bridge-registry-v2-02 get-pair-details-or-fail pair))

(define-read-only (get-tick-to-pair-or-fail (tick (string-utf8 256)))
	(contract-call? .meta-bridge-registry-v2-02 get-tick-to-pair-or-fail tick))

(define-read-only (get-peg-in-sent-or-default (bitcoin-tx (buff 32768)) (output uint) (offset uint))
  (contract-call? .meta-bridge-registry-v2-02 get-peg-in-sent-or-default bitcoin-tx output offset))

(define-read-only (get-request-or-fail (request-id uint))
  (contract-call? .meta-bridge-registry-v2-02 get-request-or-fail request-id))

(define-read-only (validate-peg-out (amount uint) (pair { token: principal, chain-id: uint }))
  (let (
      (token-details (try! (get-pair-details-or-fail pair)))
      (fee (mul-down amount (get peg-out-fee token-details))))
		(asserts! (> amount fee) err-invalid-amount)
    (asserts! (not (get peg-out-paused token-details)) err-paused)    
    (ok { token-details: token-details, fee: fee })))

;; governance functions

(define-public (pause (new-paused bool))
	(begin
		(try! (is-dao-or-extension))
		(ok (var-set paused new-paused))))

(define-public (set-fee-to-address (new-fee-to-address principal))
  (begin
    (try! (is-dao-or-extension))
    (ok (var-set fee-to-address new-fee-to-address))))

(define-public (transfer-all-to (new-owner principal) (token-trait <ft-trait>))
  (begin 
    (try! (is-dao-or-extension))
    (as-contract (contract-call? token-trait transfer-fixed (unwrap-panic (contract-call? token-trait get-balance-fixed tx-sender)) tx-sender new-owner none))))

(define-public (transfer-all-to-many (new-owner principal) (token-traits (list 10 <ft-trait>)))
  (ok (map transfer-all-to (list new-owner new-owner new-owner new-owner new-owner new-owner new-owner new-owner new-owner new-owner) token-traits)))

;; public functions

;; request peg-out of `tick` of `amount` (net of fee) to `peg-out-address`
;; request escrows the relevant pegged-in token and gas-fee token to the contract until the request is either finalized or revoked.
;;
;; token-trait => the trait of pegged-in token
(define-public (request-peg-out (amount uint) (peg-out-address (buff 128)) (token-trait <ft-trait>) (the-chain-id uint))
  (let (
      (token (contract-of token-trait))
			(validation-data (try! (validate-peg-out amount { token: token, chain-id: the-chain-id })))
      (token-details (get token-details validation-data))
      (fee (get fee validation-data))
      (amount-net (- amount fee))
      (gas-fee (get peg-out-gas-fee token-details))
      (request-details { requested-by: tx-sender, peg-out-address: peg-out-address, tick: (get tick token-details), token: token, amount-net: amount-net, fee: fee, gas-fee: gas-fee, claimed: u0, claimed-by: tx-sender, fulfilled-by: 0x, revoked: false, finalized: false, requested-at: block-height, requested-at-burn-height: burn-block-height })
      (request-id (as-contract (try! (contract-call? .meta-bridge-registry-v2-02 set-request u0 request-details)))))
    (try! (contract-call? token-trait transfer-fixed amount tx-sender (as-contract tx-sender) none))
    (and (> gas-fee u0) (try! (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc transfer-fixed gas-fee tx-sender (as-contract tx-sender) none)))
    (print (merge request-details { type: "request-peg-out", request-id: request-id }))
    (ok true)))

;; claim peg-out request, so that the claimer can safely process the peg-out (within the grace period)
;;
(define-public (claim-peg-out (request-id uint) (fulfilled-by (buff 128)))
  (let (
      (claimer tx-sender)
      (request-details (try! (get-request-or-fail request-id)))
      (token-details (try! (get-pair-details-or-fail (try! (get-tick-to-pair-or-fail (get tick request-details)))))))
    (asserts! (not (get peg-out-paused token-details)) err-paused)
    (asserts! (< (get claimed request-details) block-height) err-request-already-claimed)
    (asserts! (not (get revoked request-details)) err-request-already-revoked)
    (asserts! (not (get finalized request-details)) err-request-already-finalized)

    (as-contract (try! (contract-call? .meta-bridge-registry-v2-02 set-request request-id (merge request-details { claimed: (+ block-height (get-request-claim-grace-period)), claimed-by: claimer, fulfilled-by: fulfilled-by }))))

    (print (merge request-details { type: "claim-peg-out", request-id: request-id, claimed: (+ block-height (get-request-claim-grace-period)), claimed-by: claimer, fulfilled-by: fulfilled-by }))
    (ok true)
  )
)

;; finalize peg-out request
;; finalize `request-id` with `tx`
;; pays the fee to `fee-to-address` and burn the relevant pegged-in tokens.
;;
;; peg-out finalization can be done by either a peg-in address or a non-peg-in (i.e. 3rd party) address
;; if the latter, then the overall peg-in balance does not change.
;; the claimer sends non-pegged-in BRC20 tokens to the peg-out requester and receives the pegged-in BRC20 tokens (along with gas-fee)
;; if the former, then the overall peg-in balance decreases.
;; the relevant BRC20 tokens are burnt (with fees paid to `fee-to-address`)
(define-public (finalize-peg-out-on-index (request-id uint)
  (tx { bitcoin-tx: (buff 32768), output: uint, offset: uint, tick: (string-utf8 256), amt: uint, from: (buff 128), to: (buff 128), from-bal: uint, to-bal: uint, decimals: uint })
  (block { header: (buff 80), height: uint })
  (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
  (signature-packs (list 10 { signer: principal, tx-hash: (buff 32), signature: (buff 65) }))
  (token-trait <ft-trait>))
  (begin 
    (try! (index-tx tx block proof signature-packs))
    (finalize-peg-out request-id (get bitcoin-tx tx) (get output tx) (get offset tx) token-trait)))

(define-public (finalize-peg-out (request-id uint) (tx (buff 32768)) (output-idx uint) (offset-idx uint) (token-trait <ft-trait>))
  (let (
      (token (contract-of token-trait))
      (request-details (try! (get-request-or-fail request-id)))
      (token-details (try! (get-pair-details-or-fail (try! (get-tick-to-pair-or-fail (get tick request-details))))))
      (tx-idxed (try! (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.oracle-v2-01 get-bitcoin-tx-indexed-or-fail tx output-idx offset-idx)))
      (tx-mined-height (try! (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.oracle-v2-01 get-bitcoin-tx-mined-or-fail tx)))
      (amount-in-fixed (decimals-to-fixed (get amt tx-idxed) (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.oracle-v2-01 get-tick-decimals-or-default (get tick tx-idxed))))
      (fulfilled-by (get from tx-idxed))
      (is-fulfilled-by-peg-in (is-peg-in-address-approved fulfilled-by))
      )
    (asserts! (not (get peg-out-paused token-details)) err-paused)
		(asserts! (< burn-height-start tx-mined-height) err-tx-mined-before-start)
    (asserts! (is-eq (get tick request-details) (get tick tx-idxed)) err-token-mismatch)
    (asserts! (is-eq (get amount-net request-details) amount-in-fixed) err-invalid-amount)
    (asserts! (is-eq (get peg-out-address request-details) (get to tx-idxed)) err-address-mismatch)
    (asserts! (is-eq (get fulfilled-by request-details) fulfilled-by) err-address-mismatch)
    (asserts! (< (get requested-at-burn-height request-details) tx-mined-height) err-tx-mined-before-request)
    (asserts! (not (get-peg-in-sent-or-default tx output-idx offset-idx)) err-already-sent)
    (asserts! (not (get revoked request-details)) err-request-already-revoked)
    (asserts! (not (get finalized request-details)) err-request-already-finalized)

    (as-contract (try! (contract-call? .meta-bridge-registry-v2-02 set-peg-in-sent { tx: tx, output: output-idx, offset: offset-idx } true)))
    (as-contract (try! (contract-call? .meta-bridge-registry-v2-02 set-request request-id (merge request-details { finalized: true }))))

    (and (> (get fee request-details) u0) (as-contract (try! (contract-call? token-trait transfer-fixed (get fee request-details) tx-sender (var-get fee-to-address) none))))
    (and (> (get gas-fee request-details) u0) (as-contract (try! (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc transfer-fixed (get gas-fee request-details) tx-sender (if is-fulfilled-by-peg-in (var-get fee-to-address) (get claimed-by request-details)) none))))

    (if is-fulfilled-by-peg-in
      (and (not (get no-burn token-details)) (as-contract (try! (contract-call? token-trait burn-fixed (get amount-net request-details) tx-sender))))
      (as-contract (try! (contract-call? token-trait transfer-fixed (get amount-net request-details) tx-sender (get claimed-by request-details) none)))
    )

    (print { type: "finalize-peg-out", request-id: request-id, tx: tx })
    (ok true)))

;; revoke peg-out request
;; only after `request-revoke-grace-period` passed
;; returns fee and pegged-in tokens to the requester.
(define-public (revoke-peg-out (request-id uint) (token-trait <ft-trait>))
  (let (
      (token (contract-of token-trait))
      (request-details (try! (get-request-or-fail request-id)))
      (token-details (try! (get-pair-details-or-fail (try! (get-tick-to-pair-or-fail (get tick request-details)))))))
    (asserts! (> block-height (+ (get requested-at request-details) (get-request-revoke-grace-period))) err-revoke-grace-period)
    (asserts! (< (get claimed request-details) block-height) err-request-already-claimed)
    (asserts! (not (get revoked request-details)) err-request-already-revoked)
    (asserts! (not (get finalized request-details)) err-request-already-finalized)

    (as-contract (try! (contract-call? .meta-bridge-registry-v2-02 set-request request-id (merge request-details { revoked: true }))))

    (and (> (get fee request-details) u0) (as-contract (try! (contract-call? token-trait transfer-fixed (get fee request-details) tx-sender (get requested-by request-details) none))))
    (and (> (get gas-fee request-details) u0) (as-contract (try! (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.token-abtc transfer-fixed (get gas-fee request-details) tx-sender (get requested-by request-details) none))))
    (as-contract (try! (contract-call? token-trait transfer-fixed (get amount-net request-details) tx-sender (get requested-by request-details) none)))

    (print { type: "revoke-peg-out", request-id: request-id })
    (ok true)))

;; internal functions

(define-private (index-tx
  (tx { bitcoin-tx: (buff 32768), output: uint, offset: uint, tick: (string-utf8 256), amt: uint, from: (buff 128), to: (buff 128), from-bal: uint, to-bal: uint, decimals: uint })
  (block { header: (buff 80), height: uint })
  (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
  (signature-packs (list 10 { signer: principal, tx-hash: (buff 32), signature: (buff 65) })))
  (begin 
    (and 
      (not (is-ok (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.oracle-v2-01 get-bitcoin-tx-indexed-or-fail (get bitcoin-tx tx) (get output tx) (get offset tx))))
      (as-contract (try! (contract-call? 'SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1NK.oracle-v2-01 index-tx-many (list { tx: tx, block: block, proof: proof, signature-packs: signature-packs })))))
    (print { type: "indexed-tx", tx: tx, block: block, proof: proof, signature-packs: signature-packs })
    (ok true)))

(define-private (min (a uint) (b uint))
  (if (< a b) a b))

(define-private (mul-down (a uint) (b uint))
  (/ (* a b) ONE_8))

(define-private (div-down (a uint) (b uint))
  (if (is-eq a u0) u0 (/ (* a ONE_8) b)))

(define-private (decimals-to-fixed (amount uint) (decimals uint))
  (/ (* amount ONE_8) (pow u10 decimals)))

Functions (27)

FunctionAccessArgs
is-dao-or-extensionread-only
is-pausedread-only
get-fee-to-addressread-only
get-pair-detailsread-onlypair: { token: principal, chain-id: uint }
get-pair-details-manyread-onlypairs: (list 200 { token: principal, chain-id: uint }
get-requestread-onlyrequest-id: uint
get-request-manyread-onlyrequest-ids: (list 200 uint
get-request-revoke-grace-periodread-only
get-request-claim-grace-periodread-only
is-peg-in-address-approvedread-onlyaddress: (buff 128
get-pair-details-or-failread-onlypair: { token: principal, chain-id: uint }
get-tick-to-pair-or-failread-onlytick: (string-utf8 256
get-peg-in-sent-or-defaultread-onlybitcoin-tx: (buff 32768
get-request-or-failread-onlyrequest-id: uint
validate-peg-outread-onlyamount: uint, pair: { token: principal, chain-id: uint }
pausepublicnew-paused: bool
set-fee-to-addresspublicnew-fee-to-address: principal
transfer-all-topublicnew-owner: principal, token-trait: <ft-trait>
transfer-all-to-manypublicnew-owner: principal, token-traits: (list 10 <ft-trait>
request-peg-outpublicamount: uint, peg-out-address: (buff 128
claim-peg-outpublicrequest-id: uint, fulfilled-by: (buff 128
finalize-peg-outpublicrequest-id: uint, tx: (buff 32768
revoke-peg-outpublicrequest-id: uint, token-trait: <ft-trait>
minprivatea: uint, b: uint
mul-downprivatea: uint, b: uint
div-downprivatea: uint, b: uint
decimals-to-fixedprivateamount: uint, decimals: uint