initial commit

This commit is contained in:
fiftyeightandeight
2024-01-23 16:35:44 +08:00
commit f4fc46fe32
5 changed files with 1251 additions and 0 deletions

98
LICENSE Normal file
View File

@@ -0,0 +1,98 @@
Business Source License 1.1
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
"Business Source License" is a trademark of MariaDB Corporation Ab.
-----------------------------------------------------------------------------
Parameters
Licensor: ALEXGO Pte. Ltd.
Licensed Work: bitcoin-bridge
The Licensed Work is (c) 2024 ALEXGO Pte. Ltd.
Additional Use Grant: None
Change Date: 2028-01-01
Change License: MIT License
-----------------------------------------------------------------------------
Terms
The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited
production use.
Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.
If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.
MariaDB hereby grants you permission to use this Licenses text to license
your works, and to refer to it using the trademark "Business Source License",
as long as you comply with the Covenants of Licensor below.
-----------------------------------------------------------------------------
Covenants of Licensor
In consideration of the right to use this Licenses text and the "Business
Source License" name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:
1. To specify as the Change License the GPL Version 2.0 or any later version,
or a license that is compatible with GPL Version 2.0 or a later version,
where "compatible" means that software provided under the Change License can
be included in a program with software provided under GPL Version 2.0 or a
later version. Licensor may specify additional Change Licenses without
limitation.
2. To either: (a) specify an additional grant of rights to use that does not
impose any additional restriction on the right granted in this License, as
the Additional Use Grant; or (b) insert the text "None".
3. To specify a Change Date.
4. Not to modify this License in any other way.
-----------------------------------------------------------------------------
Notice
The Business Source License (this document, or the "License") is not an Open
Source license. However, the Licensed Work will eventually be made available
under an Open Source License, as stated in this License.

View File

@@ -0,0 +1,318 @@
(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-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 MAX_UINT u340282366920938463463374607431768211455)
(define-constant ONE_8 u100000000)
(define-data-var contract-owner principal tx-sender)
(define-data-var fee-address principal tx-sender)
;; governance functions
(define-public (set-contract-owner (new-contract-owner principal))
(begin
(try! (is-contract-owner))
(ok (var-set contract-owner new-contract-owner))))
(define-public (set-fee-address (new-fee-address principal))
(begin
(try! (is-contract-owner))
(ok (var-set fee-address new-fee-address))))
;; read-only functions
(define-read-only (get-request-revoke-grace-period)
(contract-call? .brc20-bridge-registry-v1-02 get-request-revoke-grace-period))
(define-read-only (get-request-claim-grace-period)
(contract-call? .brc20-bridge-registry-v1-02 get-request-claim-grace-period))
(define-read-only (is-peg-in-address-approved (address (buff 128)))
(contract-call? .brc20-bridge-registry-v1-02 is-peg-in-address-approved address))
(define-read-only (get-token-to-tick-or-fail (token principal))
(contract-call? .brc20-bridge-registry-v1-02 get-token-to-tick-or-fail token))
(define-read-only (get-token-details-or-fail (tick (string-utf8 4)))
(contract-call? .brc20-bridge-registry-v1-02 get-token-details-or-fail tick))
(define-read-only (get-token-details-or-fail-by-address (token principal))
(contract-call? .brc20-bridge-registry-v1-02 get-token-details-or-fail-by-address token))
(define-read-only (is-approved-token (tick (string-utf8 4)))
(contract-call? .brc20-bridge-registry-v1-02 is-approved-token tick))
(define-read-only (get-request-or-fail (request-id uint))
(contract-call? .brc20-bridge-registry-v1-02 get-request-or-fail request-id))
(define-read-only (create-order-or-fail (order { user: principal, dest: uint }))
(ok (unwrap! (to-consensus-buff? order) err-invalid-input)))
(define-read-only (decode-order-or-fail (order-script (buff 128)))
(ok (unwrap! (from-consensus-buff? { user: principal, dest: uint } (unwrap-panic (slice? order-script u2 (len order-script)))) err-invalid-input)))
(define-read-only (get-peg-in-sent-or-default (bitcoin-tx (buff 8192)) (output uint) (offset uint))
(contract-call? .brc20-bridge-registry-v1-02 get-peg-in-sent-or-default bitcoin-tx output offset))
(define-read-only (get-fee-address)
(var-get fee-address))
(define-read-only (extract-tx-ins-outs (tx (buff 8192)))
(if (try! (contract-call? .clarity-bitcoin-v1-05 is-segwit-tx tx))
(let
(
(parsed-tx (unwrap! (contract-call? .clarity-bitcoin-v1-05 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-05 parse-tx tx) err-invalid-tx))
)
(ok { ins: (get ins parsed-tx), outs: (get outs parsed-tx) })
)
)
)
;; validate-tx
;;
;; given the inputs,
;; (1) confirm the tx is indexed (by Bitcoin Oracle),
;; (2) the tx has not yet been processed,
;; (3) the peg-in address in the tx is one of the approved / correct addresses to receive BRC20 tokens
(define-read-only (validate-tx (tx (buff 8192)) (output-idx uint) (offset-idx uint) (order-idx uint) (token principal))
(let (
(tx-idxed (try! (contract-call? .oracle-v1-06 get-bitcoin-tx-indexed-or-fail tx output-idx offset-idx)))
(parsed-tx (try! (extract-tx-ins-outs tx)))
(order-script (get scriptPubKey (unwrap-panic (element-at? (get outs parsed-tx) order-idx))))
(order-details (try! (decode-order-or-fail order-script)))
(token-details (try! (get-token-details-or-fail (get tick tx-idxed))))
(amt-in-fixed (decimals-to-fixed (get amt tx-idxed) (contract-call? .oracle-v1-06 get-tick-decimals-or-default (get tick tx-idxed))))
(fee (mul-down amt-in-fixed (get peg-in-fee token-details)))
(amt-net (- amt-in-fixed fee)))
(asserts! (is-eq token (get token token-details)) err-token-mismatch)
(asserts! (not (get-peg-in-sent-or-default tx output-idx offset-idx)) err-already-sent)
(asserts! (is-peg-in-address-approved (get to tx-idxed)) err-peg-in-address-not-found)
(ok { order-details: order-details, fee: fee, amt-net: amt-net, tx-idxed: tx-idxed, token-details: token-details })
)
)
;; public functions
;; pegs in `tick` of `amt` (net of fee) from Bitcoin to Stacks.
;; the relevant tx must (1) have been indexed and (2) include OP_RETURN with Stacks address (see `create-order-or-fail`)
;;
;; tx => bitcoin tx hash of peg-in transfer
;; order-idx => index of OP_RETURN
;; token-trait => trait of pegged-in token
(define-public (finalize-peg-in-on-index
(tx { bitcoin-tx: (buff 8192), output: uint, offset: uint, tick: (string-utf8 4), 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) }))
(order-idx uint) (token-trait <sip010-trait>))
(begin
(try! (index-tx tx block proof signature-packs))
(finalize-peg-in (get bitcoin-tx tx) (get output tx) (get offset tx) order-idx token-trait)))
(define-public (finalize-peg-in (tx (buff 8192)) (output-idx uint) (offset-idx uint) (order-idx uint) (token-trait <sip010-trait>))
(let (
(token (contract-of token-trait))
(validation-data (try! (validate-tx tx output-idx offset-idx order-idx token)))
(tx-idxed (get tx-idxed validation-data))
(order-details (get order-details validation-data))
(order-address (get user order-details))
(dest (get dest order-details))
(token-details (get token-details validation-data))
(fee (get fee validation-data))
(amt-net (get amt-net validation-data)))
(asserts! (not (get peg-in-paused token-details)) err-paused)
(as-contract (try! (contract-call? .brc20-bridge-registry-v1-02 set-peg-in-sent { tx: tx, output: output-idx, offset: offset-idx } true)))
(and (> fee u0) (as-contract (try! (contract-call? token-trait mint-fixed fee (var-get fee-address)))))
;; map cannot hold traits, so the below have to be hard-coded.
;; mint to order-address if either dest == 0 or order-address is not registered with b20, or token is not registered with b20
(if (or (is-eq dest u0) (is-err (contract-call? .stxdx-registry get-user-id-or-fail order-address)) (is-none (contract-call? .stxdx-registry get-asset-id token)))
(begin
(and (> amt-net u0) (as-contract (try! (contract-call? token-trait mint-fixed amt-net order-address))))
(print (merge tx-idxed { type: "peg-in", order-address: order-address, fee: fee, amt-net: amt-net, dest: u0, bitcoin-tx: tx, output-idx: output-idx, offset-idx: offset-idx, order-idx: order-idx }))
)
(begin
(and (> amt-net u0) (as-contract (try! (contract-call? token-trait mint-fixed amt-net tx-sender))))
(and (> amt-net u0) (as-contract (try! (contract-call? .stxdx-wallet-zero transfer-in
amt-net
(try! (contract-call? .stxdx-registry get-user-id-or-fail order-address)) ;; user-id
(unwrap-panic (contract-call? .stxdx-registry get-asset-id token)) ;; asset-id
token-trait))))
(print (merge tx-idxed { type: "peg-in", order-address: order-address, fee: fee, amt-net: amt-net, dest: u1, bitcoin-tx: tx, output-idx: output-idx, offset-idx: offset-idx, order-idx: order-idx }))
)
)
(ok true)))
;; 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 (tick (string-utf8 4)) (amount uint) (peg-out-address (buff 128)) (token-trait <sip010-trait>))
(let (
(token (contract-of token-trait))
(token-details (try! (get-token-details-or-fail tick)))
(fee (mul-down amount (get peg-out-fee token-details)))
(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: tick, 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? .brc20-bridge-registry-v1-02 set-request u0 request-details)))))
(asserts! (not (get peg-out-paused token-details)) err-paused)
(asserts! (is-eq token (get token token-details)) err-token-mismatch)
(asserts! (> amount u0) err-invalid-amount)
(try! (contract-call? token-trait transfer-fixed amount tx-sender (as-contract tx-sender) none))
(and (> gas-fee u0) (try! (contract-call? .token-susdt 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-token-details-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? .brc20-bridge-registry-v1-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-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-address`)
(define-public (finalize-peg-out-on-index (request-id uint)
(tx { bitcoin-tx: (buff 8192), output: uint, offset: uint, tick: (string-utf8 4), 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 <sip010-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 8192)) (output-idx uint) (offset-idx uint) (token-trait <sip010-trait>))
(let (
(token (contract-of token-trait))
(request-details (try! (get-request-or-fail request-id)))
(token-details (try! (get-token-details-or-fail (get tick request-details))))
(tx-idxed (try! (contract-call? .oracle-v1-06 get-bitcoin-tx-indexed-or-fail tx output-idx offset-idx)))
(tx-mined-height (try! (contract-call? .oracle-v1-06 get-bitcoin-tx-mined-or-fail tx)))
(amount-in-fixed (decimals-to-fixed (get amt tx-idxed) (contract-call? .oracle-v1-06 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! (is-eq token (get token token-details)) err-token-mismatch)
(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? .brc20-bridge-registry-v1-02 set-peg-in-sent { tx: tx, output: output-idx, offset: offset-idx } true)))
(as-contract (try! (contract-call? .brc20-bridge-registry-v1-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-address) none))))
(and (> (get gas-fee request-details) u0) (as-contract (try! (contract-call? .token-susdt transfer-fixed (get gas-fee request-details) tx-sender (if is-fulfilled-by-peg-in (var-get fee-address) (get claimed-by request-details)) none))))
(if is-fulfilled-by-peg-in
(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 <sip010-trait>))
(let (
(token (contract-of token-trait))
(request-details (try! (get-request-or-fail request-id)))
(token-details (try! (get-token-details-or-fail (get tick request-details)))))
(asserts! (is-eq token (get token token-details)) err-token-mismatch)
(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? .brc20-bridge-registry-v1-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? .token-susdt 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 8192), output: uint, offset: uint, tick: (string-utf8 4), 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? .oracle-v1-06 get-bitcoin-tx-indexed-or-fail (get bitcoin-tx tx) (get output tx) (get offset tx))))
(as-contract (try! (contract-call? .oracle-v1-06 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 (is-contract-owner)
(ok (asserts! (is-eq (var-get contract-owner) tx-sender) err-unauthorised)))
(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)))

View File

@@ -0,0 +1,129 @@
(use-trait sip010-trait .trait-sip-010.sip-010-trait)
(define-constant err-unauthorised (err u1000))
(define-constant err-token-not-found (err u1001))
(define-constant err-request-not-found (err u1002))
(define-data-var contract-owner principal tx-sender)
(define-map peg-in-sent { tx: (buff 8192), output: uint, offset: uint } bool)
;; governance functions
(define-public (set-contract-owner-legacy (new-owner principal))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-contract-owner new-owner))))
(define-public (approve-operator (operator principal) (approved bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 approve-operator operator approved))))
(define-public (set-request-revoke-grace-period (grace-period uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-request-revoke-grace-period grace-period))))
(define-public (set-request-claim-grace-period (grace-period uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-request-claim-grace-period grace-period))))
(define-public (pause-peg-in (tick (string-utf8 4)) (paused bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 pause-peg-in tick paused))))
(define-public (pause-peg-out (tick (string-utf8 4)) (paused bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 pause-peg-out tick paused))))
(define-public (set-peg-in-fee (tick (string-utf8 4)) (new-peg-in-fee uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-peg-in-fee tick new-peg-in-fee))))
(define-public (set-peg-out-fee (tick (string-utf8 4)) (new-peg-out-fee uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-peg-out-fee tick new-peg-out-fee))))
(define-public (set-peg-out-gas-fee (tick (string-utf8 4)) (new-peg-out-gas-fee uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-peg-out-gas-fee tick new-peg-out-gas-fee))))
(define-public (approve-token (tick (string-utf8 4)) (token principal) (approved bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 approve-token tick token approved))))
(define-public (approve-peg-in-address (address (buff 128)) (approved bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 approve-peg-in-address address approved))))
(define-public (set-contract-owner (new-contract-owner principal))
(begin
(try! (is-contract-owner))
(ok (var-set contract-owner new-contract-owner))))
;; read-only functions
(define-read-only (get-request-revoke-grace-period)
(contract-call? .brc20-bridge-registry-v1-01 get-request-revoke-grace-period))
(define-read-only (get-request-claim-grace-period)
(contract-call? .brc20-bridge-registry-v1-01 get-request-claim-grace-period))
(define-read-only (is-peg-in-address-approved (address (buff 128)))
(contract-call? .brc20-bridge-registry-v1-01 is-peg-in-address-approved address))
(define-read-only (get-token-to-tick-or-fail (token principal))
(contract-call? .brc20-bridge-registry-v1-01 get-token-to-tick-or-fail token))
(define-read-only (get-token-details-or-fail (tick (string-utf8 4)))
(contract-call? .brc20-bridge-registry-v1-01 get-token-details-or-fail tick))
(define-read-only (get-token-details-or-fail-by-address (token principal))
(contract-call? .brc20-bridge-registry-v1-01 get-token-details-or-fail-by-address token))
(define-read-only (is-approved-token (tick (string-utf8 4)))
(contract-call? .brc20-bridge-registry-v1-01 is-approved-token tick))
(define-read-only (get-request-or-fail (request-id uint))
(contract-call? .brc20-bridge-registry-v1-01 get-request-or-fail request-id))
(define-read-only (get-approved-operator-or-default (operator principal))
(contract-call? .brc20-bridge-registry-v1-01 get-approved-operator-or-default operator))
(define-read-only (get-peg-in-sent-or-default (bitcoin-tx (buff 8192)) (output uint) (offset uint))
(match (as-max-len? bitcoin-tx u4096)
some-value
(or
(contract-call? .brc20-bridge-registry-v1-01 get-peg-in-sent-or-default some-value output offset)
(default-to false (map-get? peg-in-sent { tx: bitcoin-tx, output: output, offset: offset })))
(default-to false (map-get? peg-in-sent { tx: bitcoin-tx, output: output, offset: offset }))))
(define-read-only (is-approved-operator)
(contract-call? .brc20-bridge-registry-v1-01 is-approved-operator))
;; priviledged functions
(define-public (set-peg-in-sent (peg-in-tx { tx: (buff 8192), output: uint, offset: uint }) (sent bool))
(begin
(try! (is-approved-operator))
(ok (map-set peg-in-sent peg-in-tx sent))))
(define-public (set-request (request-id uint) (details { requested-by: principal, peg-out-address: (buff 128), tick: (string-utf8 4), amount-net: uint, fee: uint, gas-fee: uint, claimed: uint, claimed-by: principal, fulfilled-by: (buff 128), revoked: bool, finalized: bool, requested-at: uint, requested-at-burn-height: uint}))
(begin
(try! (is-approved-operator))
(as-contract (contract-call? .brc20-bridge-registry-v1-01 set-request request-id details))))
;; internal functions
(define-private (is-contract-owner)
(ok (asserts! (is-eq (var-get contract-owner) tx-sender) err-unauthorised)))

View File

@@ -0,0 +1,619 @@
(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-address-mismatch (err u1006))
(define-constant err-request-already-revoked (err u1007))
(define-constant err-request-already-finalized (err u1008))
(define-constant err-revoke-grace-period (err u1009))
(define-constant err-request-already-claimed (err u1010))
(define-constant err-bitcoin-tx-not-mined (err u1011))
(define-constant err-invalid-input (err u1012))
(define-constant err-tx-mined-before-request (err u1013))
(define-constant err-dest-mismatch (err u1014))
(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 contract-owner principal tx-sender)
(define-data-var fee-address principal tx-sender)
(define-data-var peg-in-paused bool true)
(define-data-var peg-out-paused bool true)
(define-data-var peg-in-fee uint u0)
(define-data-var peg-in-min-fee uint u0)
(define-data-var peg-out-fee uint u0)
(define-data-var peg-out-min-fee uint u0)
(define-map use-whitelist uint bool)
(define-map whitelisted {launch-id: uint, owner: (buff 128)} bool)
;; governance functions
(define-public (set-use-whitelist (launch-id uint) (new-whitelisted bool))
(begin
(try! (is-contract-owner))
(ok (map-set use-whitelist launch-id new-whitelisted))))
(define-public (set-whitelisted (launch-id uint) (whitelisted-users (list 200 {owner: (buff 128), whitelisted: bool})))
(begin
(try! (is-contract-owner))
(fold set-whitelisted-iter whitelisted-users launch-id)
(ok true)))
(define-public (set-contract-owner (new-contract-owner principal))
(begin
(try! (is-contract-owner))
(ok (var-set contract-owner new-contract-owner))))
(define-public (set-fee-address (new-fee-address principal))
(begin
(try! (is-contract-owner))
(ok (var-set fee-address new-fee-address))))
(define-public (pause-peg-in (paused bool))
(begin
(try! (is-contract-owner))
(ok (var-set peg-in-paused paused))))
(define-public (pause-peg-out (paused bool))
(begin
(try! (is-contract-owner))
(ok (var-set peg-out-paused paused))))
(define-public (set-peg-in-fee (fee uint))
(begin
(try! (is-contract-owner))
(ok (var-set peg-in-fee fee))))
(define-public (set-peg-in-min-fee (fee uint))
(begin
(try! (is-contract-owner))
(ok (var-set peg-in-min-fee fee))))
(define-public (set-peg-out-fee (fee uint))
(begin
(try! (is-contract-owner))
(ok (var-set peg-out-fee fee))))
(define-public (set-peg-out-min-fee (fee uint))
(begin
(try! (is-contract-owner))
(ok (var-set peg-out-min-fee fee))))
;; read-only functions
(define-read-only (is-peg-in-paused)
(var-get peg-in-paused))
(define-read-only (is-peg-out-paused)
(var-get peg-out-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 (get-peg-out-fee)
(var-get peg-out-fee))
(define-read-only (get-peg-out-min-fee)
(var-get peg-out-min-fee))
(define-read-only (get-request-revoke-grace-period)
(contract-call? .btc-bridge-registry-v1-03 get-request-revoke-grace-period))
(define-read-only (get-request-claim-grace-period)
(contract-call? .btc-bridge-registry-v1-03 get-request-claim-grace-period))
(define-read-only (is-peg-in-address-approved (address (buff 128)))
(contract-call? .btc-bridge-registry-v1-03 is-peg-in-address-approved address))
(define-read-only (get-request-or-fail (request-id uint))
(contract-call? .btc-bridge-registry-v1-03 get-request-or-fail request-id))
(define-read-only (get-peg-in-sent-or-default (tx (buff 16384)) (output uint))
(contract-call? .btc-bridge-registry-v1-03 get-peg-in-sent-or-default tx output))
(define-read-only (get-fee-address)
(var-get fee-address))
(define-read-only (extract-tx-ins-outs (tx (buff 16384)))
(if (try! (contract-call? .clarity-bitcoin-v1-06 is-segwit-tx tx))
(let (
(parsed-tx (unwrap! (contract-call? .clarity-bitcoin-v1-06 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-06 parse-tx tx) err-invalid-tx)))
(ok { ins: (get ins parsed-tx), outs: (get outs parsed-tx) }))
))
(define-read-only (get-txid (tx (buff 16384)))
(if (try! (contract-call? .clarity-bitcoin-v1-06 is-segwit-tx tx))
(ok (contract-call? .clarity-bitcoin-v1-06 get-segwit-txid tx))
(ok (contract-call? .clarity-bitcoin-v1-06 get-txid tx))
))
(define-read-only (get-use-whitelist-or-default (launch-id uint))
(default-to false (map-get? use-whitelist launch-id)))
(define-read-only (get-whitelisted-or-default (launch-id uint) (owner (buff 128)))
(if (get-use-whitelist-or-default launch-id)
(default-to false (map-get? whitelisted {launch-id: launch-id, owner: owner}))
true))
;; verify-mined
;;
;; it takes Bitcoin tx and confirms if the tx is mined on Bitcoin L1
(define-read-only (verify-mined (tx (buff 16384)) (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-06 is-segwit-tx tx))
(contract-call? .clarity-bitcoin-v1-06 was-segwit-tx-mined? block tx proof)
(contract-call? .clarity-bitcoin-v1-06 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 16384)) (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-1-or-fail (order { user: principal, pool-id: uint, min-dy: uint }))
(ok (unwrap! (to-consensus-buff? { u: (get user order), p: (int-to-ascii (get pool-id order)), y: (int-to-ascii (get min-dy order)) }) err-invalid-input)))
(define-read-only (decode-order-1-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? { u: principal, p: (string-ascii 40), y: (string-ascii 40) } (unwrap-panic (slice? order-script offset (len order-script)))) err-invalid-input))
(pool-id (unwrap! (string-to-uint? (get p raw-order)) err-invalid-input))
(min-dy (unwrap! (string-to-uint? (get y raw-order)) err-invalid-input)))
(ok { user: (get u raw-order), pool-id: pool-id, min-dy: min-dy })))
(define-read-only (validate-tx-1 (tx (buff 16384)) (output-idx uint) (order-idx uint) (token principal))
(validate-tx-1-extra (try! (validate-tx-1-base tx output-idx order-idx)) token))
(define-read-only (create-order-2-or-fail (order { user: principal, pool1-id: uint, pool2-id: uint, min-dz: uint }))
(ok (unwrap! (to-consensus-buff? { u: (get user order), p1: (int-to-ascii (get pool1-id order)), p2: (int-to-ascii (get pool2-id order)), z: (int-to-ascii (get min-dz order)) }) err-invalid-input)))
(define-read-only (decode-order-2-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? { u: principal, p1: (string-ascii 40), p2: (string-ascii 40), z: (string-ascii 40) } (unwrap-panic (slice? order-script offset (len order-script)))) err-invalid-input))
(pool1-id (unwrap! (string-to-uint? (get p1 raw-order)) err-invalid-input))
(pool2-id (unwrap! (string-to-uint? (get p2 raw-order)) err-invalid-input))
(min-dz (unwrap! (string-to-uint? (get z raw-order)) err-invalid-input)))
(ok { user: (get u raw-order), pool1-id: pool1-id, pool2-id: pool2-id, min-dz: min-dz })))
(define-read-only (validate-tx-2 (tx (buff 16384)) (output-idx uint) (order-idx uint) (token1 principal) (token2 principal))
(validate-tx-2-extra (try! (validate-tx-2-base tx output-idx order-idx)) token1 token2))
(define-read-only (create-order-3-or-fail (order { user: principal, pool1-id: uint, pool2-id: uint, pool3-id: uint, min-dw: uint }))
(ok (unwrap! (to-consensus-buff? { u: (get user order), p1: (int-to-ascii (get pool1-id order)), p2: (int-to-ascii (get pool2-id order)), p3: (int-to-ascii (get pool3-id order)), w: (int-to-ascii (get min-dw order)) }) err-invalid-input)))
(define-read-only (decode-order-3-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? { u: principal, p1: (string-ascii 40), p2: (string-ascii 40), p3: (string-ascii 40), w: (string-ascii 40) } (unwrap-panic (slice? order-script offset (len order-script)))) err-invalid-input))
(pool1-id (unwrap! (string-to-uint? (get p1 raw-order)) err-invalid-input))
(pool2-id (unwrap! (string-to-uint? (get p2 raw-order)) err-invalid-input))
(pool3-id (unwrap! (string-to-uint? (get p3 raw-order)) err-invalid-input))
(min-dw (unwrap! (string-to-uint? (get w raw-order)) err-invalid-input)))
(ok { user: (get u raw-order), pool1-id: pool1-id, pool2-id: pool2-id, pool3-id: pool3-id, min-dw: min-dw })))
(define-read-only (validate-tx-3 (tx (buff 16384)) (output-idx uint) (order-idx uint) (token1 principal) (token2 principal) (token3 principal))
(validate-tx-3-extra (try! (validate-tx-3-base tx output-idx order-idx)) token1 token2 token3))
(define-read-only (create-order-launchpad-or-fail (order { user: principal, launch-id: uint }))
(ok (unwrap! (to-consensus-buff? { u: (get user order), l: (int-to-ascii (get launch-id order)) }) err-invalid-input)))
(define-read-only (decode-order-launchpad-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? { u: principal, l: (string-ascii 40) } (unwrap-panic (slice? order-script offset (len order-script)))) err-invalid-input))
(launch-id (unwrap! (string-to-uint? (get l raw-order)) err-invalid-input)))
(ok { user: (get u raw-order), launch-id: launch-id })))
(define-read-only (validate-tx-launchpad (tx (buff 16384)) (output-idx uint) (order-idx uint) (owner-idx uint))
(validate-tx-launchpad-extra (try! (validate-tx-launchpad-base tx output-idx order-idx owner-idx))))
;; public functions
;; pegs in BTC (net of fee) from Bitcoin to Stacks.
;; the relevant tx include OP_RETURN with Stacks address (see `create-order-or-fail`)
;;
;; tx => bitcoin tx hash of peg-in transfer
;; order-idx => index of OP_RETURN
(define-public (finalize-peg-in-0
(tx (buff 16384))
(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))
(amount-net (get amount-net validation-data))
(fee (get fee validation-data)))
(as-contract (try! (contract-call? .btc-bridge-registry-v1-03 set-peg-in-sent tx output-idx true)))
(and (> fee u0) (as-contract (try! (contract-call? .token-abtc mint-fixed fee (var-get fee-address)))))
(as-contract (try! (contract-call? .token-abtc mint-fixed amount-net order-details)))
(print { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: fee, amount-net: amount-net })
(ok true)))
(define-public (finalize-peg-in-1
(tx (buff 16384))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint)
(token-trait <sip010-trait>))
(let (
(common-check (try! (finalize-peg-in-common tx block proof output-idx order-idx)))
(validation-data (try! (validate-tx-1-base tx output-idx order-idx)))
(order-details (get order-details validation-data))
(amount-net (get amount-net validation-data))
(fee (get fee validation-data))
(minted (as-contract (try! (contract-call? .token-abtc mint-fixed amount-net tx-sender)))))
(as-contract (try! (contract-call? .btc-bridge-registry-v1-03 set-peg-in-sent tx output-idx true)))
(and (> fee u0) (as-contract (try! (contract-call? .token-abtc mint-fixed fee (var-get fee-address)))))
(print { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: fee, amount-net: amount-net })
(match (validate-tx-1-extra validation-data (contract-of token-trait))
extra-details
(let (
(swapped (as-contract (try! (contract-call? .amm-swap-pool-v1-1 swap-helper .token-abtc token-trait (get factor extra-details) amount-net (some (get min-dy order-details)))))))
(as-contract (try! (contract-call? token-trait transfer-fixed swapped tx-sender (get user order-details) none)))
(ok true)
)
err-value
(begin
(as-contract (try! (contract-call? .token-abtc transfer-fixed amount-net tx-sender (get user order-details) none)))
(ok false)
)
)))
(define-public (finalize-peg-in-2
(tx (buff 16384))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint)
(token1-trait <sip010-trait>) (token2-trait <sip010-trait>))
(let (
(common-check (try! (finalize-peg-in-common tx block proof output-idx order-idx)))
(validation-data (try! (validate-tx-2-base tx output-idx order-idx)))
(order-details (get order-details validation-data))
(amount-net (get amount-net validation-data))
(fee (get fee validation-data))
(minted (as-contract (try! (contract-call? .token-abtc mint-fixed amount-net tx-sender)))))
(as-contract (try! (contract-call? .btc-bridge-registry-v1-03 set-peg-in-sent tx output-idx true)))
(and (> fee u0) (as-contract (try! (contract-call? .token-abtc mint-fixed fee (var-get fee-address)))))
(print { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: fee, amount-net: amount-net })
(match (validate-tx-2-extra validation-data (contract-of token1-trait) (contract-of token2-trait))
extra-details
(let (
(swapped (as-contract (try! (contract-call? .amm-swap-pool-v1-1 swap-helper-a .token-abtc token1-trait token2-trait (get factor1 extra-details) (get factor2 extra-details) amount-net (some (get min-dz order-details)))))))
(as-contract (try! (contract-call? token2-trait transfer-fixed swapped tx-sender (get user order-details) none)))
(ok true)
)
err-value
(begin
(as-contract (try! (contract-call? .token-abtc transfer-fixed amount-net tx-sender (get user order-details) none)))
(ok false)
)
)))
(define-public (finalize-peg-in-3
(tx (buff 16384))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint)
(token1-trait <sip010-trait>) (token2-trait <sip010-trait>) (token3-trait <sip010-trait>))
(let (
(common-check (try! (finalize-peg-in-common tx block proof output-idx order-idx)))
(validation-data (try! (validate-tx-3-base tx output-idx order-idx)))
(order-details (get order-details validation-data))
(amount-net (get amount-net validation-data))
(fee (get fee validation-data))
(minted (as-contract (try! (contract-call? .token-abtc mint-fixed amount-net tx-sender)))))
(as-contract (try! (contract-call? .btc-bridge-registry-v1-03 set-peg-in-sent tx output-idx true)))
(and (> fee u0) (as-contract (try! (contract-call? .token-abtc mint-fixed fee (var-get fee-address)))))
(print { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: fee, amount-net: amount-net })
(match (validate-tx-3-extra validation-data (contract-of token1-trait) (contract-of token2-trait) (contract-of token3-trait))
extra-details
(let (
(swapped (as-contract (try! (contract-call? .amm-swap-pool-v1-1 swap-helper-b .token-abtc token1-trait token2-trait token3-trait (get factor1 extra-details) (get factor2 extra-details) (get factor3 extra-details) amount-net (some (get min-dw order-details)))))))
(as-contract (try! (contract-call? token3-trait transfer-fixed swapped tx-sender (get user order-details) none)))
(ok true)
)
err-value
(begin
(as-contract (try! (contract-call? .token-abtc transfer-fixed amount-net tx-sender (get user order-details) none)))
(ok false)
)
)))
(define-public (finalize-peg-in-launchpad
(tx (buff 16384))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (order-idx uint) (owner-idx uint))
(let (
(common-check (try! (finalize-peg-in-common tx block proof output-idx order-idx)))
(validation-data (try! (validate-tx-launchpad-base tx output-idx order-idx owner-idx)))
(order-details (get order-details validation-data))
(amount-net (get amount-net validation-data))
(fee (get fee validation-data)))
(as-contract (try! (contract-call? .btc-bridge-registry-v1-03 set-peg-in-sent tx output-idx true)))
(and (> fee u0) (as-contract (try! (contract-call? .token-abtc mint-fixed fee (var-get fee-address)))))
(as-contract (try! (contract-call? .token-abtc mint-fixed amount-net tx-sender)))
(print { type: "peg-in", tx-id: (try! (get-txid tx)), output: output-idx, order-details: order-details, fee: fee, amount-net: amount-net })
(if (is-ok (validate-tx-launchpad-extra validation-data))
(let (
(bounds (as-contract (try! (contract-call? .alex-launchpad-v1-7 register-on-behalf (get user order-details) (get launch-id order-details) amount-net .token-abtc)))))
(map-set whitelisted { launch-id: (get launch-id order-details), owner: (get owner-script validation-data) } false)
(ok bounds)
)
(begin
(as-contract (try! (contract-call? .token-abtc transfer-fixed amount-net tx-sender (get user order-details) none)))
(ok { start: u0, end: u0 })
)
)))
;; request peg-out of aBTC (net of fee) to `peg-out-address`
;; request escrows the relevant aBTC and gas-fee token to the contract until the request is either finalized or revoked.
;;
(define-public (request-peg-out-0 (peg-out-address (buff 128)) (amount uint))
(let (
(gas-fee (var-get peg-out-min-fee))
(fee (- (max (mul-down amount (var-get peg-out-fee)) gas-fee) gas-fee))
(check-amount (asserts! (> amount (+ fee gas-fee)) err-invalid-amount))
(amount-net (- amount fee gas-fee))
(request-details { requested-by: tx-sender, peg-out-address: peg-out-address, 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? .btc-bridge-registry-v1-03 set-request u0 request-details)))))
(asserts! (not (var-get peg-out-paused)) err-paused)
(try! (contract-call? .token-abtc transfer-fixed amount tx-sender (as-contract tx-sender) none))
(print (merge request-details { type: "request-peg-out", request-id: request-id }))
(ok request-id)))
(define-public (request-peg-out-1 (peg-out-address (buff 128)) (token-trait <sip010-trait>) (factor uint) (dx uint) (min-dy (optional uint)))
(let (
(swapped (try! (contract-call? .amm-swap-pool-v1-1 swap-helper token-trait .token-abtc factor dx min-dy))))
(request-peg-out-0 peg-out-address swapped)))
(define-public (request-peg-out-2 (peg-out-address (buff 128)) (token1-trait <sip010-trait>) (token2-trait <sip010-trait>) (factor1 uint) (factor2 uint) (dx uint) (min-dz (optional uint)))
(let (
(swapped (try! (contract-call? .amm-swap-pool-v1-1 swap-helper-a token1-trait token2-trait .token-abtc factor1 factor2 dx min-dz))))
(request-peg-out-0 peg-out-address swapped)))
(define-public (request-peg-out-3 (peg-out-address (buff 128)) (token1-trait <sip010-trait>) (token2-trait <sip010-trait>) (token3-trait <sip010-trait>) (factor1 uint) (factor2 uint) (factor3 uint) (dx uint) (min-dw (optional uint)))
(let (
(swapped (try! (contract-call? .amm-swap-pool-v1-1 swap-helper-b token1-trait token2-trait token3-trait .token-abtc factor1 factor2 factor3 dx min-dw))))
(request-peg-out-0 peg-out-address swapped)))
(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))))
(asserts! (not (var-get peg-out-paused)) 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? .btc-bridge-registry-v1-03 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-address` and burn the relevant pegged-in tokens.
;;
(define-public (finalize-peg-out
(request-id uint)
(tx (buff 16384))
(block { header: (buff 80), height: uint })
(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint })
(output-idx uint) (fulfilled-by-idx uint))
(let (
(request-details (try! (get-request-or-fail request-id)))
(was-mined (try! (verify-mined tx block proof)))
(parsed-tx (try! (extract-tx-ins-outs tx)))
(output (unwrap! (element-at (get outs parsed-tx) output-idx) err-invalid-tx))
(fulfilled-by (get scriptPubKey (unwrap! (element-at (get outs parsed-tx) fulfilled-by-idx) err-invalid-tx)))
(amount (get value output))
(peg-out-address (get scriptPubKey output))
(is-fulfilled-by-peg-in (is-peg-in-address-approved fulfilled-by))
)
(asserts! (not (var-get peg-out-paused)) err-paused)
(asserts! (is-eq amount (get amount-net request-details)) err-invalid-amount)
(asserts! (is-eq (get peg-out-address request-details) peg-out-address) err-address-mismatch)
(asserts! (is-eq (get fulfilled-by request-details) fulfilled-by) err-address-mismatch)
(asserts! (< (get requested-at-burn-height request-details) (get height block)) err-tx-mined-before-request)
;; (asserts! (<= block-height (get claimed request-details)) err-request-claim-expired) ;; allow fulfilled if not claimed again
(asserts! (not (get-peg-in-sent-or-default tx output-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? .btc-bridge-registry-v1-03 set-peg-in-sent tx output-idx true)))
(as-contract (try! (contract-call? .btc-bridge-registry-v1-03 set-request request-id (merge request-details { finalized: true }))))
(and (> (get fee request-details) u0) (as-contract (try! (contract-call? .token-abtc transfer-fixed (get fee request-details) tx-sender (var-get fee-address) none))))
(and (> (get gas-fee request-details) u0) (as-contract (try! (contract-call? .token-abtc transfer-fixed (get gas-fee request-details) tx-sender (if is-fulfilled-by-peg-in (var-get fee-address) (get claimed-by request-details)) none))))
(if is-fulfilled-by-peg-in
(as-contract (try! (contract-call? .token-abtc burn-fixed (get amount-net request-details) tx-sender)))
(as-contract (try! (contract-call? .token-abtc 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))
(let (
(request-details (try! (get-request-or-fail request-id))))
(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? .btc-bridge-registry-v1-03 set-request request-id (merge request-details { revoked: true }))))
(and (> (get fee request-details) u0) (as-contract (try! (contract-call? .token-abtc 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? .token-abtc transfer-fixed (get gas-fee request-details) tx-sender (get requested-by request-details) none))))
(as-contract (try! (contract-call? .token-abtc 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 (validate-tx-1-base (tx (buff 16384)) (output-idx uint) (order-idx uint))
(let (
(validation-data (try! (validate-tx-common tx output-idx order-idx))))
(ok { order-details: (try! (decode-order-1-or-fail (get order-script validation-data))), fee: (get fee validation-data), amount-net: (get amount-net validation-data) })))
(define-private (validate-tx-1-extra (validation-data { order-details: { user: principal, pool-id: uint, min-dy: uint }, fee: uint, amount-net: uint }) (token principal))
(let (
(order-details (get order-details validation-data))
(pool-details (try! (contract-call? .amm-swap-pool-v1-1 get-pool-details-by-id (get pool-id order-details))))
(token-y (if (is-eq (get token-x pool-details) .token-abtc) (get token-y pool-details) (get token-x pool-details)))
(factor (get factor pool-details))
(dy (try! (contract-call? .amm-swap-pool-v1-1 get-helper .token-abtc token-y factor (get amount-net validation-data)))))
(asserts! (>= dy (get min-dy order-details)) err-slippage)
(asserts! (is-eq token-y token) err-token-mismatch)
(ok { validation-data: validation-data, token-y: token-y, factor: factor })))
(define-private (validate-tx-2-base (tx (buff 16384)) (output-idx uint) (order-idx uint))
(let (
(validation-data (try! (validate-tx-common tx output-idx order-idx))))
(ok { order-details: (try! (decode-order-2-or-fail (get order-script validation-data))), fee: (get fee validation-data), amount-net: (get amount-net validation-data) })))
(define-private (validate-tx-2-extra (validation-data { order-details: { user: principal, pool1-id: uint, pool2-id: uint, min-dz: uint }, fee: uint, amount-net: uint }) (token1 principal) (token2 principal))
(let (
(order-details (get order-details validation-data))
(pool1-details (try! (contract-call? .amm-swap-pool-v1-1 get-pool-details-by-id (get pool1-id order-details))))
(pool2-details (try! (contract-call? .amm-swap-pool-v1-1 get-pool-details-by-id (get pool2-id order-details))))
(token1-y (if (is-eq (get token-x pool1-details) .token-abtc) (get token-y pool1-details) (get token-x pool1-details)))
(token2-y (if (is-eq (get token-x pool2-details) token1-y) (get token-y pool2-details) (get token-x pool2-details)))
(factor1 (get factor pool1-details))
(factor2 (get factor pool2-details))
(dz (try! (contract-call? .amm-swap-pool-v1-1 get-helper-a .token-abtc token1-y token2-y factor1 factor2 (get amount-net validation-data)))))
(asserts! (is-eq token1-y token1) err-token-mismatch)
(asserts! (is-eq token2-y token2) err-token-mismatch)
(asserts! (>= dz (get min-dz order-details)) err-slippage)
(ok { validation-data: validation-data, token1-y: token1-y, token2-y: token2-y, factor1: factor1, factor2: factor2 })))
(define-private (validate-tx-3-base (tx (buff 16384)) (output-idx uint) (order-idx uint))
(let (
(validation-data (try! (validate-tx-common tx output-idx order-idx))))
(ok { order-details: (try! (decode-order-3-or-fail (get order-script validation-data))), fee: (get fee validation-data), amount-net: (get amount-net validation-data) })))
(define-private (validate-tx-3-extra (validation-data { order-details: { user: principal, pool1-id: uint, pool2-id: uint, pool3-id: uint, min-dw: uint }, fee: uint, amount-net: uint }) (token1 principal) (token2 principal) (token3 principal))
(let (
(order-details (get order-details validation-data))
(pool1-details (try! (contract-call? .amm-swap-pool-v1-1 get-pool-details-by-id (get pool1-id order-details))))
(pool2-details (try! (contract-call? .amm-swap-pool-v1-1 get-pool-details-by-id (get pool2-id order-details))))
(pool3-details (try! (contract-call? .amm-swap-pool-v1-1 get-pool-details-by-id (get pool3-id order-details))))
(token1-y (if (is-eq (get token-x pool1-details) .token-abtc) (get token-y pool1-details) (get token-x pool1-details)))
(token2-y (if (is-eq (get token-x pool2-details) token1-y) (get token-y pool2-details) (get token-x pool2-details)))
(token3-y (if (is-eq (get token-x pool3-details) token2-y) (get token-y pool3-details) (get token-x pool3-details)))
(factor1 (get factor pool1-details))
(factor2 (get factor pool2-details))
(factor3 (get factor pool3-details))
(dw (try! (contract-call? .amm-swap-pool-v1-1 get-helper-b .token-abtc token1-y token2-y token3-y factor1 factor2 factor3 (get amount-net validation-data)))))
(asserts! (is-eq token1-y token1) err-token-mismatch)
(asserts! (is-eq token2-y token2) err-token-mismatch)
(asserts! (is-eq token3-y token3) err-token-mismatch)
(asserts! (>= dw (get min-dw order-details)) err-slippage)
(ok { validation-data: validation-data, token1-y: token1-y, token2-y: token2-y, token3-y: token3-y, factor1: factor1, factor2: factor2, factor3: factor3 })))
(define-private (validate-tx-launchpad-base (tx (buff 16384)) (output-idx uint) (order-idx uint) (owner-idx uint))
(let (
(validation-data (try! (validate-tx-common tx output-idx order-idx)))
(owner-script (get scriptPubKey (unwrap-panic (element-at? (get outs (get parsed-tx validation-data)) owner-idx)))))
(ok { owner-script: owner-script, order-details: (try! (decode-order-launchpad-or-fail (get order-script validation-data))), fee: (get fee validation-data), amount-net: (get amount-net validation-data) })))
(define-private (validate-tx-launchpad-extra (validation-data { owner-script: (buff 128), order-details: { user: principal, launch-id: uint }, fee: uint, amount-net: uint }))
(let (
(order-details (get order-details validation-data)))
(asserts! (get-whitelisted-or-default (get launch-id order-details) (get owner-script validation-data)) err-not-in-whitelist)
(try! (contract-call? .alex-launchpad-v1-7 validate-register (get user order-details) (get launch-id order-details) (get amount-net validation-data) .token-abtc))
(ok validation-data)))
(define-private (validate-tx-common (tx (buff 16384)) (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 16384))
(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 (set-whitelisted-iter (e {owner: (buff 128), whitelisted: bool}) (launch-id uint))
(begin
(map-set whitelisted {launch-id: launch-id, owner: (get owner e)} (get whitelisted e))
launch-id))
(define-private (is-contract-owner)
(ok (asserts! (is-eq (var-get contract-owner) tx-sender) err-unauthorised)))
(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)))

View File

@@ -0,0 +1,87 @@
(use-trait sip010-trait .trait-sip-010.sip-010-trait)
(define-constant err-unauthorised (err u1000))
(define-constant err-request-not-found (err u1002))
(define-constant err-invalid-input (err u1003))
(define-data-var contract-owner principal tx-sender)
(define-map peg-in-sent { tx: (buff 16384), output: uint } bool)
;; governance functions
(define-public (set-contract-owner (new-contract-owner principal))
(begin
(try! (is-contract-owner))
(ok (var-set contract-owner new-contract-owner))))
(define-public (set-contract-owner-legacy (new-owner principal))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .btc-bridge-registry-v1-02 set-contract-owner new-owner))))
(define-public (approve-operator (operator principal) (approved bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .btc-bridge-registry-v1-02 approve-operator operator approved))))
(define-public (set-request-revoke-grace-period (grace-period uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .btc-bridge-registry-v1-02 set-request-revoke-grace-period grace-period))))
(define-public (set-request-claim-grace-period (grace-period uint))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .btc-bridge-registry-v1-02 set-request-claim-grace-period grace-period))))
(define-public (approve-peg-in-address (address (buff 128)) (approved bool))
(begin
(try! (is-contract-owner))
(as-contract (contract-call? .btc-bridge-registry-v1-02 approve-peg-in-address address approved))))
;; read-only functions
(define-read-only (get-request-revoke-grace-period)
(contract-call? .btc-bridge-registry-v1-02 get-request-revoke-grace-period))
(define-read-only (get-request-claim-grace-period)
(contract-call? .btc-bridge-registry-v1-02 get-request-claim-grace-period))
(define-read-only (is-peg-in-address-approved (address (buff 128)))
(contract-call? .btc-bridge-registry-v1-02 is-peg-in-address-approved address))
(define-read-only (get-request-or-fail (request-id uint))
(contract-call? .btc-bridge-registry-v1-02 get-request-or-fail request-id))
(define-read-only (get-approved-operator-or-default (operator principal))
(contract-call? .btc-bridge-registry-v1-02 get-approved-operator-or-default operator))
(define-read-only (is-approved-operator)
(contract-call? .btc-bridge-registry-v1-02 is-approved-operator))
(define-read-only (get-peg-in-sent-or-default (tx (buff 16384)) (output uint))
(match (as-max-len? tx u4096)
some-value
(or
(contract-call? .btc-bridge-registry-v1-02 get-peg-in-sent-or-default some-value output)
(default-to false (map-get? peg-in-sent { tx: tx, output: output })))
(default-to false (map-get? peg-in-sent { tx: tx, output: output }))))
;; priviledged functions
(define-public (set-peg-in-sent (tx (buff 16384)) (output uint) (sent bool))
(begin
(try! (is-approved-operator))
(ok (map-set peg-in-sent { tx: tx, output: output } sent))))
(define-public (set-request (request-id uint) (details { requested-by: principal, peg-out-address: (buff 128), amount-net: uint, fee: uint, gas-fee: uint, claimed: uint, claimed-by: principal, fulfilled-by: (buff 128), revoked: bool, finalized: bool, requested-at: uint, requested-at-burn-height: uint}))
(begin
(try! (is-approved-operator))
(contract-call? .btc-bridge-registry-v1-02 set-request request-id details)))
;; internal functions
(define-private (is-contract-owner)
(ok (asserts! (is-eq (var-get contract-owner) tx-sender) err-unauthorised)))