(use-trait sip010-trait .trait-sip-010.sip-010-trait)
(define-constant err-unauthorised (err u1000))
(define-constant err-user-not-found (err u1001))
(define-constant err-invalid-peg-in-id (err u1002))
(define-constant err-request-not-found (err u1003))
(define-constant err-user-already-registered (err u1004))
(define-constant err-invalid-amount (err u1005))
(define-constant err-already-sent (err u1006))
(define-constant err-already-expired (err u1007))
(define-constant err-address-already-registered (err u1008))
(define-constant err-peg-in-addresses-exhausted (err u1009))
(define-constant err-paused (err u1010))
(define-constant err-token-mismatch (err u1011))
(define-constant err-memo-mismatch (err u1012))
(define-constant err-memo-already-used (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)
(define-map authorised-users principal bool)
(define-map approved-tokens principal { approved: bool, peg-in-paused: bool, peg-out-paused: bool, peg-in-fee: uint, peg-out-fee: uint, peg-out-gas-fee: uint })
(define-data-var peg-in-id-nonce uint u0)
(define-map peg-in-id-to-address uint (string-ascii 62))
(define-map peg-in-address-to-id (string-ascii 62) uint)
(define-map users principal { peg-in-id: uint, peg-out-address: (string-ascii 62) })
(define-data-var peg-in-id-assigned uint u0)
(define-data-var request-nonce uint u0)
(define-map requests uint
{
user: principal,
peg-in: bool,
token: principal,
amount: uint,
requested-at: uint,
expired: bool,
sent: bool,
memo: (string-ascii 256)
}
)
(define-map memo-used (string-ascii 256) uint)
(define-data-var requests-processed uint u0)
(define-read-only (is-memo-used (memo (string-ascii 256)))
(is-some (map-get? memo-used memo))
)
(define-read-only (memo-to-request-id (memo (string-ascii 256)))
(default-to u0 (map-get? memo-used memo))
)
(define-read-only (get-token-details-many (tokens (list 100 principal)))
(map get-token-details tokens)
)
(define-private (get-token-details (token principal))
(map-get? approved-tokens token)
)
(define-read-only (get-token-details-or-fail (token principal))
(ok (unwrap! (map-get? approved-tokens token) err-unauthorised))
)
(define-read-only (get-pending-requests)
(- (var-get request-nonce) (var-get requests-processed))
)
(define-read-only (get-requests-processing-progress)
{
requests-applied: (var-get request-nonce),
requests-processed: (var-get requests-processed),
}
)
(define-public (pause-peg-in (token principal) (paused bool))
(let
(
(token-details (try! (get-token-details-or-fail token)))
)
(try! (is-contract-owner))
(ok (map-set approved-tokens token (merge token-details { peg-in-paused: paused })))
)
)
(define-public (pause-peg-out (token principal) (paused bool))
(let
(
(token-details (try! (get-token-details-or-fail token)))
)
(try! (is-contract-owner))
(ok (map-set approved-tokens token (merge token-details { peg-out-paused: paused })))
)
)
(define-public (set-peg-in-fee (token principal) (new-peg-in-fee uint))
(let
(
(token-details (try! (get-token-details-or-fail token)))
)
(try! (is-contract-owner))
(ok (map-set approved-tokens token (merge token-details { peg-in-fee: new-peg-in-fee })))
)
)
(define-public (set-peg-out-fee (token principal) (new-peg-out-fee uint))
(let
(
(token-details (try! (get-token-details-or-fail token)))
)
(try! (is-contract-owner))
(ok (map-set approved-tokens token (merge token-details { peg-out-fee: new-peg-out-fee })))
)
)
(define-public (set-peg-out-gas-fee (token principal) (new-peg-out-gas-fee uint))
(let
(
(token-details (try! (get-token-details-or-fail token)))
)
(try! (is-contract-owner))
(ok (map-set approved-tokens token (merge token-details { peg-out-gas-fee: new-peg-out-gas-fee })))
)
)
(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-read-only (get-fee-address)
(var-get fee-address)
)
(define-public (authorise-user (user principal) (authorised bool))
(begin
(try! (is-contract-owner))
(ok (map-set authorised-users user authorised))
)
)
(define-public (approve-token (token principal) (approved bool))
(begin
(try! (is-contract-owner))
(ok
(match (map-get? approved-tokens token)
token-details
(map-set approved-tokens token (merge token-details { approved: approved }))
(map-set approved-tokens token { approved: approved, peg-in-paused: true, peg-out-paused: true, peg-in-fee: u1000000, peg-out-fee: u1000000, peg-out-gas-fee: u3000000000 })
)
)
)
)
(define-private (is-contract-owner)
(ok (asserts! (is-eq (var-get contract-owner) tx-sender) err-unauthorised))
)
(define-private (is-authorised-user)
(ok (asserts! (default-to false (map-get? authorised-users tx-sender)) err-unauthorised))
)
(define-read-only (is-approved-token (token principal))
(match (get-token-details-or-fail token)
ok-value
(get approved ok-value)
err-value
false
)
)
(define-read-only (get-user-peg-in-address-or-fail (user principal))
(let
(
(user-details (try! (get-user-details-or-fail user)))
)
(ok (unwrap! (map-get? peg-in-id-to-address (get peg-in-id user-details)) err-invalid-peg-in-id))
)
)
(define-read-only (get-user-details-or-fail (user principal))
(ok (unwrap! (map-get? users user) err-user-not-found))
)
(define-read-only (get-request-by-tx-sender-many (requests-id (list 1000 uint)))
(map get-request-by-tx-sender requests-id)
)
(define-read-only (get-request-by-user-many (user (list 1000 principal)) (requests-id (list 1000 uint)))
(map get-request-by-user user requests-id)
)
(define-read-only (get-request-by-user (user principal) (request-id uint))
(match (map-get? requests request-id)
request
(if (is-eq (get user request) user)
(some request)
none
)
none
)
)
(define-read-only (get-request-by-tx-sender (request-id uint))
(get-request-by-user tx-sender request-id)
)
(define-read-only (get-request-or-fail (request-id uint))
(ok (unwrap! (map-get? requests request-id) err-request-not-found))
)
(define-read-only (get-request (request-id uint))
(map-get? requests request-id)
)
(define-read-only (get-requests-many (requests-id (list 1000 uint)))
(map get-request requests-id)
)
(define-read-only (get-pending-request (request-id uint))
(match (map-get? requests request-id)
request
(if (and (not (get sent request)) (not (get expired request)))
(some request)
none
)
none
)
)
(define-read-only (get-pending-requests-many (requests-id (list 1000 uint)))
(map get-pending-request requests-id)
)
(define-public (register-user-on-behalf (user principal))
(begin
(try! (is-contract-owner))
(register-user user)
)
)
(define-private (register-user (user principal))
(let
(
(peg-in-id (+ (mod (var-get peg-in-id-assigned) (var-get peg-in-id-nonce)) u1))
)
(asserts! (is-none (map-get? users user)) err-user-already-registered)
(asserts! (>= (var-get peg-in-id-nonce) peg-in-id) err-peg-in-addresses-exhausted)
(map-set users user { peg-in-id: peg-in-id, peg-out-address: "" })
(var-set peg-in-id-assigned (+ (var-get peg-in-id-assigned) u1))
(ok { peg-in-id: peg-in-id, peg-out-address: "" })
)
)
(define-public (register-stxdx-user-by-tx-sender (pub-key (buff 33)))
(begin
(try! (contract-call? .stxdx-registry register-user pub-key))
(register-user tx-sender)
)
)
(define-public (register-user-by-tx-sender)
(register-user tx-sender)
)
(define-public (request-peg-in (token principal) (amount uint) (memo (string-ascii 256)))
(let
(
(user (try! (get-user-details-or-fail tx-sender)))
(token-details (try! (get-token-details-or-fail token)))
(request-id (+ (var-get request-nonce) u1))
)
(asserts! (not (get peg-in-paused token-details)) err-paused)
(asserts! (is-approved-token token) err-unauthorised)
(asserts! (> amount u0) err-invalid-amount)
(asserts! (not (is-memo-used memo)) err-memo-already-used)
(map-set memo-used memo request-id)
(map-set requests request-id { user: tx-sender, peg-in: true, token: token, amount: amount, requested-at: block-height, expired: false, sent: false, memo: memo })
(ok (var-set request-nonce request-id))
)
)
(define-public (request-peg-out (token-trait <sip010-trait>) (amount uint) (peg-out-address (string-ascii 62)))
(let
(
(sender tx-sender)
(token (contract-of token-trait))
(user (try! (get-user-details-or-fail sender)))
(token-details (try! (get-token-details-or-fail token)))
(request-id (+ (var-get request-nonce) u1))
(fee (mul-down amount (get peg-out-fee token-details)))
(amount-net (- amount fee))
)
(asserts! (not (get peg-out-paused token-details)) err-paused)
(asserts! (is-approved-token token) err-unauthorised)
(asserts! (> amount u0) err-invalid-amount)
(and (> fee u0) (try! (contract-call? token-trait transfer-fixed fee sender (var-get fee-address) none)))
(try! (contract-call? .token-susdt transfer-fixed (get peg-out-gas-fee token-details) sender (var-get fee-address) none))
(as-contract (try! (contract-call? token-trait burn-fixed amount-net sender)))
(map-set users sender (merge user { peg-out-address: peg-out-address }))
(map-set requests request-id { user: sender, peg-in: false, token: token, amount: amount-net, requested-at: block-height, expired: false, sent: false, memo: "" })
(ok (var-set request-nonce request-id))
)
)
(define-public (mark-sent (request-id uint) (memo (string-ascii 256)) (token-trait <sip010-trait>))
(let
(
(token (contract-of token-trait))
(request (try! (get-request-or-fail request-id)))
(token-details (try! (get-token-details-or-fail token)))
)
(asserts! (or (is-ok (is-contract-owner)) (is-ok (is-authorised-user))) err-unauthorised)
(asserts! (not (get sent request)) err-already-sent)
(asserts! (not (get expired request)) err-already-expired)
(asserts! (is-approved-token token) err-unauthorised)
(asserts! (is-eq (get token request) token) err-token-mismatch)
(if (get peg-in request)
(let
(
(fee (mul-down (get amount request) (get peg-in-fee token-details)))
(amount-net (- (get amount request) fee))
)
(asserts! (is-eq (get memo request) memo) err-memo-mismatch)
(and (> fee u0) (as-contract (try! (contract-call? token-trait mint-fixed fee (var-get fee-address)))))
(as-contract (try! (contract-call? token-trait mint-fixed amount-net tx-sender)))
(as-contract (try! (contract-call? .stxdx-wallet-zero transfer-in
amount-net
(try! (contract-call? .stxdx-registry get-user-id-or-fail (get user request)))
(unwrap! (contract-call? .stxdx-registry get-asset-id token) err-token-mismatch)
token-trait
)))
)
(map-set memo-used memo request-id)
)
(map-set requests request-id (merge request { sent: true, memo: memo }))
(ok (var-set requests-processed (+ (var-get requests-processed) u1)))
)
)
(define-public (mark-expired (request-id uint) (memo (string-ascii 256)))
(let
(
(request (try! (get-request-or-fail request-id)))
)
(asserts! (or (is-ok (is-contract-owner)) (is-ok (is-authorised-user))) err-unauthorised)
(asserts! (not (get sent request)) err-already-sent)
(asserts! (not (get expired request)) err-already-expired)
(map-delete memo-used (get memo request))
(map-set requests request-id (merge request { expired: true, memo: memo }))
(ok (var-set requests-processed (+ (var-get requests-processed) u1)))
)
)
(define-public (add-peg-in-address (address (string-ascii 62)))
(let
(
(peg-in-id (+ (var-get peg-in-id-nonce) u1))
)
(asserts! (or (is-ok (is-contract-owner)) (is-ok (is-authorised-user))) err-unauthorised)
(asserts! (is-none (map-get? peg-in-address-to-id address)) err-address-already-registered)
(map-set peg-in-id-to-address peg-in-id address)
(map-set peg-in-address-to-id address peg-in-id)
(ok (var-set peg-in-id-nonce peg-in-id))
)
)
(define-public (mark-sent-many (sent (list 1000 { request-id: uint, memo: (string-ascii 256), token: <sip010-trait> })))
(fold mark-sent-iter sent (ok true))
)
(define-public (mark-expired-many (marked (list 1000 { request-id: uint, memo: (string-ascii 256) })))
(fold mark-expired-iter marked (ok true))
)
(define-public (add-peg-in-address-many (addresses (list 1000 (string-ascii 62))))
(fold add-peg-in-address-iter addresses (ok true))
)
(define-private (mark-sent-iter (sent { request-id: uint, memo: (string-ascii 256), token: <sip010-trait> }) (previous-response (response bool uint)))
(match previous-response prev-ok (mark-sent (get request-id sent) (get memo sent) (get token sent)) prev-err previous-response)
)
(define-private (mark-expired-iter (marked { request-id: uint, memo: (string-ascii 256) }) (previous-response (response bool uint)))
(match previous-response prev-ok (mark-expired (get request-id marked) (get memo marked)) prev-err previous-response)
)
(define-private (add-peg-in-address-iter (address (string-ascii 62)) (previous-response (response bool uint)))
(match previous-response prev-ok (add-peg-in-address address) prev-err previous-response)
)
(define-private (min (a uint) (b uint))
(if (< a b) a b)
)
(define-read-only (mul-down (a uint) (b uint))
(/ (* a b) ONE_8)
)
(define-read-only (div-down (a uint) (b uint))
(if (is-eq a u0)
u0
(/ (* a ONE_8) b)
)
)