(impl-trait .extension-trait.extension-trait)
(use-trait sip010-trait .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-invalid-tx (err u1004))
(define-constant err-already-sent (err u1005))
(define-constant err-bitcoin-tx-not-mined (err u1011))
(define-constant err-invalid-input (err u1012))
(define-constant err-token-mismatch (err u1015))
(define-constant err-slippage (err u1016))
(define-constant err-not-in-whitelist (err u1017))
(define-constant MAX_UINT u340282366920938463463374607431768211455)
(define-constant ONE_8 u100000000)
(define-data-var fee-to-address principal .executor-dao)
(define-data-var peg-in-paused bool true)
(define-data-var peg-in-fee uint u0)
(define-data-var peg-in-min-fee uint u0)
(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 (pause-peg-in (paused bool))
(begin
(try! (is-dao-or-extension))
(ok (var-set peg-in-paused paused))))
(define-public (set-peg-in-fee (fee uint))
(begin
(try! (is-dao-or-extension))
(ok (var-set peg-in-fee fee))))
(define-public (set-peg-in-min-fee (fee uint))
(begin
(try! (is-dao-or-extension))
(ok (var-set peg-in-min-fee fee))))
(define-read-only (is-dao-or-extension)
(ok (asserts! (or (is-eq tx-sender .executor-dao) (contract-call? .executor-dao is-extension contract-caller)) err-unauthorised)))
(define-read-only (is-peg-in-paused)
(var-get peg-in-paused))
(define-read-only (get-peg-in-fee)
(var-get peg-in-fee))
(define-read-only (get-peg-in-min-fee)
(var-get peg-in-min-fee))
(define-read-only (is-peg-in-address-approved (address (buff 128)))
(contract-call? .btc-bridge-registry-v2-01 is-peg-in-address-approved address))
(define-read-only (get-peg-in-sent-or-default (tx (buff 32768)) (output uint))
(contract-call? .btc-bridge-registry-v2-01 get-peg-in-sent-or-default tx output))
(define-read-only (get-fee-to-address)
(var-get fee-to-address))
(define-read-only (extract-tx-ins-outs (tx (buff 32768)))
(if (try! (contract-call? .clarity-bitcoin-v1-07 is-segwit-tx tx))
(let (
(parsed-tx (unwrap! (contract-call? .clarity-bitcoin-v1-07 parse-wtx tx) err-invalid-tx)))
(ok { ins: (get ins parsed-tx), outs: (get outs parsed-tx) }))
(let (
(parsed-tx (unwrap! (contract-call? .clarity-bitcoin-v1-07 parse-tx tx) err-invalid-tx)))
(ok { ins: (get ins parsed-tx), outs: (get outs parsed-tx) }))))
(define-read-only (get-txid (tx (buff 32768)))
(if (try! (contract-call? .clarity-bitcoin-v1-07 is-segwit-tx tx))
(ok (contract-call? .clarity-bitcoin-v1-07 get-segwit-txid tx))
(ok (contract-call? .clarity-bitcoin-v1-07 get-txid tx))))
(define-read-only (verify-mined (tx (buff 32768)) (block { header: (buff 80), height: uint }) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint }))
(if (is-eq chain-id u1)
(let (
(response (if (try! (contract-call? .clarity-bitcoin-v1-07 is-segwit-tx tx)) (contract-call? .clarity-bitcoin-v1-07 was-segwit-tx-mined? block tx proof) (contract-call? .clarity-bitcoin-v1-07 was-tx-mined? block tx proof))))
(if (or (is-err response) (not (unwrap-panic response))) err-bitcoin-tx-not-mined (ok true)))
(ok true))) ;; if not mainnet, assume verified
(define-read-only (create-order-0-or-fail (order principal))
(ok (unwrap! (to-consensus-buff? order) err-invalid-input)))
(define-read-only (decode-order-0-or-fail (order-script (buff 128)))
(let (
(op-code (unwrap-panic (slice? order-script u1 u2))))
(ok (unwrap! (from-consensus-buff? principal (unwrap-panic (slice? order-script (if (< op-code 0x4c) u2 u3) (len order-script)))) err-invalid-input))))
(define-read-only (validate-tx-0 (tx (buff 32768)) (output-idx uint) (order-idx uint))
(let (
(validation-data (try! (validate-tx-common tx output-idx order-idx))))
(ok { order-details: (try! (decode-order-0-or-fail (get order-script validation-data))), fee: (get fee validation-data), amount-net: (get amount-net validation-data) })))
(define-read-only (create-order-cross-or-fail (order { recipient: (buff 256), chain-id: uint }))
(ok (unwrap! (to-consensus-buff? { r: (get recipient order), c: (int-to-ascii (get chain-id order)) }) err-invalid-input)))
(define-read-only (decode-order-cross-or-fail (order-script (buff 128)))
(let (
(op-code (unwrap-panic (slice? order-script u1 u2)))
(offset (if (< op-code 0x4c) u2 u3))
(raw-order (unwrap! (from-consensus-buff? { r: (buff 256), c: (string-ascii 40) } (unwrap-panic (slice? order-script offset (len order-script)))) err-invalid-input)))
(ok { recipient: (get r raw-order), chain-id: (unwrap! (string-to-uint? (get c raw-order)) err-invalid-input) })))
(define-read-only (validate-tx-cross (tx (buff 32768)) (output-idx uint) (order-idx uint))
(validate-tx-cross-extra (try! (validate-tx-cross-base tx output-idx order-idx))))
(define-public (finalize-peg-in-0
(tx (buff 32768))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint))
(let (
(common-check (try! (finalize-peg-in-common tx block proof output-idx order-idx)))
(validation-data (try! (validate-tx-0 tx output-idx order-idx)))
(order-details (get order-details validation-data)))
(as-contract (try! (contract-call? .btc-bridge-registry-v2-01 set-peg-in-sent tx output-idx true)))
(and (> (get fee validation-data) u0) (as-contract (try! (contract-call? .token-abtc mint-fixed (get fee validation-data) (var-get fee-to-address)))))
(as-contract (try! (contract-call? .token-abtc mint-fixed (get amount-net validation-data) order-details)))
(print { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: (get fee validation-data), amount-net: (get amount-net validation-data) })
(ok true)))
(define-public (finalize-peg-in-cross
(tx (buff 32768))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint))
(let (
(common-check (try! (finalize-peg-in-common tx block proof output-idx order-idx)))
(validation-data (try! (validate-tx-cross-base tx output-idx order-idx)))
(order-details (get order-details validation-data))
(print-msg { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: (get fee validation-data), amount-net: (get amount-net validation-data) }))
(as-contract (try! (contract-call? .token-abtc mint-fixed (get amount-net validation-data) tx-sender)))
(as-contract (try! (contract-call? .btc-bridge-registry-v2-01 set-peg-in-sent tx output-idx true)))
(and (> (get fee validation-data) u0) (as-contract (try! (contract-call? .token-abtc mint-fixed (get fee validation-data) (var-get fee-to-address)))))
(match (validate-tx-cross-extra validation-data)
ok-value
(begin
(as-contract (try! (contract-call? .cross-peg-out-endpoint-v2-01 transfer-to-unwrap .token-abtc (get amount-net validation-data) (get chain-id order-details) (get recipient order-details))))
(print (merge print-msg { success: true }))
(ok true))
err-value
(begin
(print (merge print-msg { success: false, err-value: err-value }))
(ok false)))))
(define-private (validate-tx-cross-base (tx (buff 32768)) (output-idx uint) (order-idx uint))
(let (
(validation-data (try! (validate-tx-common tx output-idx order-idx))))
(ok { order-details: (try! (decode-order-cross-or-fail (get order-script validation-data))), fee: (get fee validation-data), amount-net: (get amount-net validation-data) })))
(define-private (validate-tx-cross-extra (validation-data { order-details: { recipient: (buff 256), chain-id: uint }, fee: uint, amount-net: uint }))
(begin
(try! (contract-call? .cross-peg-out-endpoint-v2-01 validate-transfer-to-unwrap tx-sender .token-abtc (get amount-net validation-data) (get chain-id (get order-details validation-data))))
(ok validation-data)))
(define-private (validate-tx-common (tx (buff 32768)) (output-idx uint) (order-idx uint))
(let (
(parsed-tx (try! (extract-tx-ins-outs tx)))
(output (unwrap! (element-at (get outs parsed-tx) output-idx) err-invalid-tx))
(amount (get value output))
(peg-in-address (get scriptPubKey output))
(order-script (get scriptPubKey (unwrap-panic (element-at? (get outs parsed-tx) order-idx))))
(fee (max (mul-down amount (var-get peg-in-fee)) (var-get peg-in-min-fee)))
(amount-net (- amount fee))
)
(asserts! (not (get-peg-in-sent-or-default tx output-idx)) err-already-sent)
(asserts! (is-peg-in-address-approved peg-in-address) err-peg-in-address-not-found)
(asserts! (> amount-net u0) err-invalid-amount)
(ok { parsed-tx: parsed-tx, order-script: order-script, fee: fee, amount-net: amount-net })))
(define-private (finalize-peg-in-common
(tx (buff 32768))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint))
(begin
(asserts! (not (var-get peg-in-paused)) err-paused)
(verify-mined tx block proof)))
(define-private (max (a uint) (b uint))
(if (< a b) b a))
(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-public (callback (sender principal) (payload (buff 2048)))
(ok true))