Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trait for minting NFT's with any FT and STX using unified interface #60

Closed
LNow opened this issue Feb 23, 2022 · 16 comments
Closed

Trait for minting NFT's with any FT and STX using unified interface #60

LNow opened this issue Feb 23, 2022 · 16 comments

Comments

@LNow
Copy link

LNow commented Feb 23, 2022

As the ecosystem grows we see more and more NFT contracts that can be minted not only with plain STX but also with different FT (ie. CityCoins). Each have a slightly different function name and signature. I think we can do better.

There should be a trait that defines unified interface which can be used to mint new NFT with STX and any SIP-010 compliant FT. Such trait could look like this:

(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)

(define-trait (
    ;; mints 1 NFT using supplied FT as tender
    (mint-with (<sip-010-trait>) (response bool uint))

    ;; mints X NFTs using supplied FT as tender
    (mint-many-with (uint <sip-010-trait>) (response bool uint))

    ;; returns minting price in supplied FT
    (get-mint-price-in (<sip-010-trait>) (response uint uint))
))

And to make it work also for STX we could define global wrapped STX SIP-010 compliant FT that is not issuing new token, but uses plain STX:

(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)

(define-read-only (get-balance (owner principal))
    (ok (stx-get-balance owner))
)

(define-read-only (get-decimals)
    (ok u6)
)

(define-read-only (get-name)
    (ok "Wrapped STX")
)

(define-read-only (get-symbol)
    (ok "WSTX")
)

(define-read-only (get-token-uri)
    (ok (some u"https://www.stacks.co"))
)

(define-read-only (get-total-supply)
    (ok stx-liquid-supply)
)

(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
    (begin
        (try! (stx-transfer? amount sender recipient))
        (match memo to-print (print to-print) 0x)
        (ok true)
    )
)

Very similar approach can be used by NFT marketplaces to define unified interface for listing/buying NFTs for STX and any SIP-010 compliant FT.

@Jamil @friedger @dantrevino @MarvinJanssen @radicleart

@friedger
Copy link
Contributor

@LNow Yes, please! That is a good proposal. We should also add standard error codes for

  • unsupported token: u500
  • insufficient balance: u103
  • not authorized to mint: u403

I would like to move from sip-10 to sip-transferable (#52)

For wrapped stx

I suggest to not include stx minting here but define
mint-with-ustx, etc.

@radicleart
Copy link
Contributor

Can this approach work with Arkadikos or ALEX's wrapped bitcoin as in SP3DX3H4FEYZJZ586MFBS25ZW3HZDMEW92260R2PR.Wrapped-Bitcoin SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wbtc ?

@radicleart
Copy link
Contributor

Does this suggestion also impact sip-0013 ?

@LNow
Copy link
Author

LNow commented Feb 23, 2022

@friedger

The problem with stx as a sip-10 is that it can't be operated. Implementations might need to implement two cases anyway.

What do you mean when you say it can't be operated and why you might need to implement two cases?

Also the UI for post-conditions would need to handle two different cases.
Yes, UI will have to generate two different types of post-conditions. One when someone choose wstx and another one when other FT is used.

With regards to wSTX name, I'm not attached to it. We can change it to STX-FT or anything else.

I suggest to not include stx minting here but define
mint-with-ustx, etc.

The key concept of this idea is to not have separate function for ustx or anything else. Instead of implementing different logic for STX, and different for FT you'll have just one. Example: https://github.com/LNow/clarity-projects/blob/2163db81b9286f0c30b00dd7728fb0e93dfc6243/contracts/tokens/mintable-nft-token.clar#L42-L59

One function to rule them all

@LNow
Copy link
Author

LNow commented Feb 23, 2022

@radicleart that's the thing - it will work with any fungible token that is compliant with SIP-010. Citycoins, USDA, DIKO, Nothing, wbtc, xbtc, wrapped-bitcoin, ANY.

Does this suggestion also impact sip-0013 ?

Sure, why not.
I want this to be a separate trait that can be used in any contract that you think it can be used in.

@radicleart
Copy link
Contributor

@LNow i think most people (me for sure, stxnft i believe) are following this for the commission since crash punks

(define-trait commission
    ((pay (uint uint) (response bool uint)))
)

which could be generalised to...

(use-trait sip-010-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)

(define-trait commission
    ((pay (<sip-010-trait> uint uint) (response bool uint)))
)

and the listing and buying methods also need to be generalised...

(define-public (list-in-ustx (id uint) (price uint) (comm <commission-trait>))

to

(define-public (list-in-token (id uint) (price uint) (comm <commission-trait>) (token <sip-010-trait>))...

likewise for the buy-in-ustx ?

@LNow
Copy link
Author

LNow commented Feb 23, 2022

@radicleart Yes, generalized version of #51 would look like you described it.

@radicleart
Copy link
Contributor

radicleart commented Feb 24, 2022

@LNow so is the answer to @friedger on this point?

The key concept of this idea is to not have separate function for ustx or anything else. Instead of implementing different logic for STX, and different for FT you'll have just one. Example: https://github.com/LNow/clarity-projects/blob/2163db81b9286f0c30b00dd7728fb0e93dfc6243/contracts/tokens/mintable-nft-token.clar#L42-L59

that at this line

    (and (> price u0) (try! (contract-call? tender transfer price tx-sender artistAddress none)))
    (and (> commission u0) (try! (contract-call? tender transfer commission tx-sender commissionAddress none)))

the tender contract passed in is an ft trait impl that just implements transfer as stx-transfer? as opposed to ft-transfer ?

Hence only one function needed for both transfer types in the main contract?

@LNow
Copy link
Author

LNow commented Feb 24, 2022

the tender contract passed in is an ft trait impl that just implements transfer as stx-transfer? as opposed to ft-transfer ?
Hence only one function needed for both transfer types in the main contract?

Yes

@friedger
Copy link
Contributor

How do you build post conditions for these txs if they can transfer either STXs or FTs?

@LNow
Copy link
Author

LNow commented Feb 24, 2022

The same way Alex/Arkadiko/Stackswaps are doing it. Based on token address user picked on the UI. With one additional if statement.
If user choose wrapped stx -> STX post-condition, otherwise -> FT post-condition.

@MarvinJanssen
Copy link
Collaborator

Great idea. Is there a way to allow for flexibility in payment schedule? When the Jellyboo NFT project was launched, it featured a pay-what-you-want model to mint (with a set minimum). The standard would be a lot more flexibility if you can set a price per NFT in mint-with and mint-with-many. Then it is also possible to introduce discounts in cases. For projects with a fixed price you just specify whatever get-mint-price-in returns.

radicleart added a commit to radicleart/clarity-market that referenced this issue Feb 28, 2022
…r dao and create and swap fungible tokens. Ultimate goal is to address the unified interface for NFT stacksgov/sips#60
@LNow
Copy link
Author

LNow commented Mar 12, 2022

@MarvinJanssen something like this?

(define-public (mint-with (tender <sip-010-trait>) (customPrice (optional uint)))
  (let
    (
      (pricing (unwrap! (map-get? TenderPricing (contract-of tender)) ERR_UNKNOWN_TENDER))
      (price (match customPrice price (max price (get price pricing)) (get price pricing)))
      ;; remaining local variables
    )
    ;; minting operations
    (ok true)
  )
)

(define-private (max (a uint) (b uint))
  (if (> a b) a b)
)

or

(define-public (mint-with (tender <sip-010-trait>) (customPrice uint))
  (let
    (
      (pricing (unwrap! (map-get? TenderPricing (contract-of tender)) ERR_UNKNOWN_TENDER))
      (price (max customPrice (get price pricing)))
      ;; remaining local variables
    )
    ;; minting operations
    (ok true)
  )
)

(define-private (max (a uint) (b uint))
  (if (> a b) a b)
)

with extra function that returns price if it has been set:

(define-read-only (get-mint-price-in (tender <sip-010-trait>))
  (get price (map-get? TenderPricing (contract-of tender)))
)

@unclemantis
Copy link

I LOVE IT!

@friedger
Copy link
Contributor

We could even extend it to tradables which would include nfts

@radicleart
Copy link
Contributor

Started on this here @friedger - see list-in-token and buy-in-token ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants