diff --git a/clarity/Clarinet.toml b/clarity/Clarinet.toml index bdb25592..ed3a6838 100644 --- a/clarity/Clarinet.toml +++ b/clarity/Clarinet.toml @@ -1434,6 +1434,11 @@ path = "contracts/bridge/stx20-bridge-endpoint-v1-01.clar" clarity_version = 2 epoch = 2.4 +[contracts.stx20-bridge-endpoint-v1-02] +path = "contracts/bridge/stx20-bridge-endpoint-v1-02.clar" +clarity_version = 2 +epoch = 2.4 + [contracts.stx20-bridge-registry-v1-01] path = "contracts/bridge/stx20-bridge-registry-v1-01.clar" clarity_version = 2 diff --git a/clarity/contracts/bridge/stx20-bridge-endpoint-v1-02.clar b/clarity/contracts/bridge/stx20-bridge-endpoint-v1-02.clar new file mode 100644 index 00000000..d4fc9a4a --- /dev/null +++ b/clarity/contracts/bridge/stx20-bridge-endpoint-v1-02.clar @@ -0,0 +1,70 @@ +(use-trait ft-trait .trait-sip-010.sip-010-trait) + +(define-constant err-not-authorised (err u1000)) +(define-constant err-invalid-token-or-ticker (err u1001)) +(define-constant err-invalid-peg-in-address (err u1002)) +(define-constant err-invalid-tx (err u1003)) +(define-constant err-amount-exceeds-max-len (err u1004)) + +(define-constant ONE_8 u100000000) + +(define-data-var contract-owner principal tx-sender) + +(define-data-var fee uint u1000000) + +(define-read-only (get-approved-operator-or-default (operator principal)) + (contract-call? .stx20-bridge-registry-v1-01 get-approved-operator-or-default operator)) + +(define-read-only (get-ticker-to-token-or-fail (ticker (string-ascii 8))) + (contract-call? .stx20-bridge-registry-v1-01 get-ticker-to-token-or-fail ticker)) + +(define-read-only (get-token-to-ticker-or-fail (token principal)) + (contract-call? .stx20-bridge-registry-v1-01 get-token-to-ticker-or-fail token)) + +(define-read-only (get-tx-sent-or-default (tx { txid: (buff 32), from: principal, to: principal, ticker: (string-ascii 8), amount: uint })) + (contract-call? .stx20-bridge-registry-v1-01 get-tx-sent-or-default tx)) + +;; governance calls + +(define-public (set-contract-owner (owner principal)) + (begin + (try! (check-is-owner)) + (ok (var-set contract-owner owner)))) + +(define-public (set-fee (new-fee uint)) + (begin + (try! (check-is-owner)) + (ok (var-set fee new-fee)))) + +;; privileged calls + +(define-public (finalize-peg-in (tx { txid: (buff 32), from: principal, to: principal, ticker: (string-ascii 8), amount: uint }) (token-trait )) + (begin + (asserts! (get-approved-operator-or-default tx-sender) err-not-authorised) ;; only oracle can call this. + (asserts! (is-eq (get to tx) .stx20-bridge-registry-v1-01) err-invalid-peg-in-address) ;; recipient must be this contract. + (asserts! (not (get-tx-sent-or-default tx)) err-invalid-tx) ;; it should not have been sent before. + (asserts! (is-eq (contract-of token-trait) (try! (get-ticker-to-token-or-fail (get ticker tx)))) err-invalid-token-or-ticker) ;; token-trait must be the token for this ticker. + (as-contract (try! (contract-call? .stx20-bridge-registry-v1-01 set-tx-sent tx true))) + (as-contract (try! (contract-call? token-trait mint-fixed (* (get amount tx) ONE_8) (get from tx)))) + (print { action: "finalize-peg-in", payload: { tx: tx, token: (contract-of token-trait) } }) + (ok true))) + +;; public calls + +(define-public (finalize-peg-out (token-trait ) (amount uint)) + (let ( + (sender tx-sender) + (ticker (try! (get-token-to-ticker-or-fail (contract-of token-trait)))) + (memo (concat "t" (concat ticker (unwrap! (as-max-len? (int-to-ascii (/ amount ONE_8)) u20) err-amount-exceeds-max-len)))) + (memo-buff (unwrap-panic (to-consensus-buff? memo))) + (sliced-memo-buff (unwrap-panic (slice? memo-buff u5 (len memo-buff))))) + (as-contract (try! (contract-call? token-trait burn-fixed amount sender))) + (try! (stx-transfer? (var-get fee) tx-sender .stx20-bridge-registry-v1-01)) + (as-contract (try! (contract-call? .stx20-bridge-registry-v1-01 transfer-stx u1 sender sliced-memo-buff))) + (print { action: "finalize-peg-out", payload: { token: (contract-of token-trait), amount: amount, memo: memo, memo-buff: sliced-memo-buff} }) + (ok true))) + +;; internal calls + +(define-private (check-is-owner) + (ok (asserts! (is-eq tx-sender (var-get contract-owner)) err-not-authorised))) \ No newline at end of file diff --git a/clarity/tests/stx20-bridge_test.ts b/clarity/tests/stx20-bridge_test.ts index 631d4c51..e5f79c3a 100644 --- a/clarity/tests/stx20-bridge_test.ts +++ b/clarity/tests/stx20-bridge_test.ts @@ -10,7 +10,7 @@ import { } from 'https://deno.land/x/clarinet@v1.2.0/index.ts'; const contractNames = { - endpoint: 'stx20-bridge-endpoint-v1-01', + endpoint: 'stx20-bridge-endpoint-v1-02', registry: 'stx20-bridge-registry-v1-01', stxs: 'stx20-stxs', wstx: 'token-wstx', @@ -183,7 +183,7 @@ Clarinet.test({ responses.receipts[4].result.expectErr(1003); responses.receipts[5].result.expectErr(1); responses.receipts[6].result.expectErr(1001); - responses.receipts[7].result.expectOk("tSTXS1"); + responses.receipts[7].result.expectOk(); console.log(responses.receipts[3].events); console.log(responses.receipts[7].events); },