Source Code

(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)
   )
)

Functions (45)

FunctionAccessArgs
is-memo-usedread-onlymemo: (string-ascii 256
memo-to-request-idread-onlymemo: (string-ascii 256
get-token-details-manyread-onlytokens: (list 100 principal
get-token-detailsprivatetoken: principal
get-token-details-or-failread-onlytoken: principal
get-pending-requestsread-only
get-requests-processing-progressread-only
pause-peg-inpublictoken: principal, paused: bool
pause-peg-outpublictoken: principal, paused: bool
set-peg-in-feepublictoken: principal, new-peg-in-fee: uint
set-peg-out-feepublictoken: principal, new-peg-out-fee: uint
set-peg-out-gas-feepublictoken: principal, new-peg-out-gas-fee: uint
set-contract-ownerpublicnew-contract-owner: principal
set-fee-addresspublicnew-fee-address: principal
get-fee-addressread-only
authorise-userpublicuser: principal, authorised: bool
approve-tokenpublictoken: principal, approved: bool
is-contract-ownerprivate
is-authorised-userprivate
is-approved-tokenread-onlytoken: principal
get-user-peg-in-address-or-failread-onlyuser: principal
get-user-details-or-failread-onlyuser: principal
get-request-by-tx-sender-manyread-onlyrequests-id: (list 1000 uint
get-request-by-user-manyread-onlyuser: (list 1000 principal
get-request-by-userread-onlyuser: principal, request-id: uint
get-request-by-tx-senderread-onlyrequest-id: uint
get-request-or-failread-onlyrequest-id: uint
get-requestread-onlyrequest-id: uint
get-requests-manyread-onlyrequests-id: (list 1000 uint
get-pending-requestread-onlyrequest-id: uint
get-pending-requests-manyread-onlyrequests-id: (list 1000 uint
register-user-on-behalfpublicuser: principal
register-userprivateuser: principal
register-stxdx-user-by-tx-senderpublicpub-key: (buff 33
register-user-by-tx-senderpublic
request-peg-inpublictoken: principal, amount: uint, memo: (string-ascii 256
request-peg-outpublictoken-trait: <sip010-trait>, amount: uint, peg-out-address: (string-ascii 62
mark-sentpublicrequest-id: uint, memo: (string-ascii 256
mark-expiredpublicrequest-id: uint, memo: (string-ascii 256
add-peg-in-addresspublicaddress: (string-ascii 62
add-peg-in-address-manypublicaddresses: (list 1000 (string-ascii 62
add-peg-in-address-iterprivateaddress: (string-ascii 62
minprivatea: uint, b: uint
mul-downread-onlya: uint, b: uint
div-downread-onlya: uint, b: uint