Git Product home page Git Product logo

erc721r's Introduction

ERC721R

Learn more on our Notion site.

How to Install

npm install && npm run prepare

About The Project

The goal of ERC721R is to add refund functionality to the ERC721 and ERC1155 standards. This repo contains community provided examples you can use in your own NFT smart contract.

Beta

This code is still in beta and undergoing reviews. Use at your own risk.

Motivation

The NFT space needs greater accountability. The space faces too many rugpulls and for the health of the NFT ecosystem as a whole we need better mechanisms to prevent these from happening.

Offering refunds provides greater protection for buyers and more legitimacy for creators.

The Azuki ERC721A provided gas improvements to the original ERC721 standard. ERC721R provides trustless refunds.

How it works in practice

When you mint an NFT in an ERC721R collection the funds are held by the smart contract in escrow. The creators are unable to withdraw the funds till the waiting period has been completed. During this waiting period the buyer is able to return their NFT to the smart contract and receive their ETH back.

If the creators decide to rug, buyers will request their funds back before the waiting period has completed only losing gas costs for the transactions.

Usage

See the example smart contracts for usage.

Benefits

For buyers:

  • Low risk purchase (worst case scenario you get your money back minus gas costs)
  • Protects against rug pulls
  • Forces greater accountability from creators to deliver

For sellers:

  • Builds trust with buyers

A benefit to both:

  • The project floor price is unlikely to drop below the mint price while refunds are open.
  • Short term flippers leave the project early leaving a high quality core intact.

Another thread on the benefits of refunds can be found in Daniel Tenner's Twitter thread: https://twitter.com/swombat/status/1492484783036936192

How long should the refund period be?

There's no one answer to this question, but some things to consider:

A longer refund period means:

  • More time for the team to deliver before the refund period runs out.
  • A longer delay till the team can access the funds.

What some other projects have done:

  • Exodia offered a 14 day refund period.
  • Curious Addy’s Trading Club offered a 100 day refund period.
  • CryptoFighters is offering a 45 day refund period.

Supporting ERC721R projects

We've created a list of high-quality buyers that have committed to purchase from the next 10 NFT projects to implement refunds. If you'd like to be added to the list you can fill in this form.

As long as the project has a mint price of 0.2ETH or less with at least a 14-day trustless refund period, everyone in the list commits to mint. Of course, there's no guarantee minters won't execute a refund.

Projects using ERC721R

Roadmap

In the future we may see more complex implementations of ERC721R that include:

  • Vesting over a period of time. For example, the creator is able to release 25% of the funds in the smart contract each month.
  • Cliffs. For example, 10% of funds are immediately available for release, and the rest is released at a later date (allowing buyers to receive a 90% refund upon purchase). The benefit is that the creators have access to some of the funds raised while still mostly protecting buyers.

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".

Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Collaborators

License

Distributed under the MIT License. See LICENSE.txt for more information.

Disclaimer

Exodia Labs is not liable for any outcomes as a result of using ERC721R. DYOR.

Contact

erc721r's People

Contributors

amirh24 avatar davidberiro avatar elie222 avatar iamsidar07 avatar lawweiliang avatar madeinfree avatar pandapip1 avatar royosherove avatar skyonedot avatar tina1998612 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

erc721r's Issues

Revoke refund functionality

Allow someone to send a transaction that they no longer wish to receive a refund.

Pros:

  • Allows a team to more creative things in the refund period. For example, an airdrop but not wanting to reward holders that will later refund the original.

Cons:

  • Would be annoying if you bought on secondary hoping to refund at mint price and then can't (although this can also happen if an NFT is refunded and owner then sells it again, but less likely to happen in this scenario)

Refund event and IERC721R interface

Wonder if we add a Refund event. Probably helpful to have.

Also thinking about the interface as a whole. Perhaps something like this with Refund event:

interface IERC721R {
    function refund(uint256[] calldata tokenIds) external;

    function getRefundPrice(uint256 tokenId) external view returns (uint256);

    function getRefundGuaranteeEndTime() external view returns (uint256);

    function isRefundGuaranteeActive() external view returns (bool);
}

Anything else we may want here?

Sample code in README outdated

We updated the codebase with a fix for owner withdrawing all funds but didn't update the README sample code to match this

Use EIP-5507 function names

EIP-5507 is based off of this, but uses different function names. Can we update this to use those function names?

Question: Why do we need notContract() modifier?

Hi!

Could you please help me understand why in this contract we need to check that publicSale and preSale are not called by the contracts? Is it to prevent some kind of attack?

Also, after a bit research, I found out that the isContract function from the OZ package does not guarantee that the address is not a contract and shouldn't be used for that purpose.

Related links:

Problem: Withdraw Function

Currently, most of the project once they finished mint. They will run away with all the money and say bye bye to the community.
This is very bad for NFT space.

I thing we should do something for this.

In Venture Capitalist market, when a project raise a certain amount of money. The project owner would not be able to get the total amount directly. They will get it in a timeline/vesting period.

My suggestion is adding a vesting period inside the withdraw function.
For eg, if the NFT project raise USD100,000. Initially, the project owner can only take 30%, after a year, it can take another 40% and a year and a half, get the remaining.

In between the period, if the community are not happy, they can halt the project by voting. If 50% of the NFT token holder say no to the project. The smart contract will halt the project by returning all the remaining fund back to the token holder.

Calculation:
NFT: 10000
Raise USD 100,000
Initially, project take 30% -> 30,000
Then, if the community found out that, the owner take the money and do nothing for the community. They will vote and halt the project. If voting are more than 50% which is 5000 nft holder say "stop it".

Then, remaining USD70,000 will refund back to all the NFT owner equally (USD70,000/10000 = USD7000) and the NFT tokens will send it back to owner.
The project certified dead. End story.

Benefit of implement this,

  • Reduce the risk of NFT investor
  • Create more real NFT project rather than scam project
  • We can educate NFT investor and I believe eventually this will become a standard for NFT project.

Question Arises Would Be.

What if one whale are not happy with the owner and he bought up 5000 nft token and shutting down the project?
Answered:
That why usually we will set a minimum cap for each owner address. Let say 5 nft token for each owner.

What if they can create 1000 accounts with 5 nft token each, then shut down the project. If he/she really did that, I am speechless and nothing I can do.

Free NFTs for contributors

Hey,

If you've contributed to this project we're happy to send you some free NFTs from the CryptoFighters Alliance collection. You can reach out to me on Twitter @elie2222 if interested. My DMs are open.

Token address will be updated first after refunded

testing case "Freely minted NFTs cannot be refunded" and "NFT cannot be refunded twice" have err.

Freely minted NFTs cannot be refunded
The first one is because the tokenId should be 2, which is the third token minted in the case. [0, 1] in the previous public tests so the owner minted here should be 2 instead of 1.
NFT cannot be refunded twice
The refunded token owner will be updated to the refundAddress, so the second time we call refund, it should be reverted with not token owner first instead of Already refunded.

I'm not sure if only I have encountered the problem.

Problem: Everyone can refund without any condition

Question

Will a businessman refund stuff without setting any condition? Does it make sense?

For our contract, we could observed that as long as user bought an NFT. Within a period of time, they may refund without any further condition.

Real World Analogy

I am a grocery shop owner. Whenever a customer bought something from me, they can return the product and get refund after a period of time. In my country, Malaysia, this does not make sense.


Go in-dept

Why customer want to refund in the first place?

  1. Customer not happy with the picture (picture is different from the marketing site)
  2. Customer not happy with the picture (it is not rare enough so refund and mint a new picture)
  3. Customer not happy with the price (The market price is lower than the minting price).

You may observe that none of this are favorable to the project owner. I don't think they are going to implement this. Any clues for this @elie222 @0xsublime @Amirh24 ?

Suggestion

replace refundAddress to address(this)

I know it is convenient to let an EOA to do something with refunded NFT. However, it may caught some bad things.

  1. Presale and public sale is the primary market. Refund should place NFT back into primary market and user can choose to mint NFT or choose to buy NFT which refunded by other.
    function buyRefunded(uint256 amount, uint256[] memory ids) external payable {
        ...
    }
  2. For the issue of NFT remaining in contract, contract owner can claim NFT after presale, public sale and refund end for future use.
    function claim(uint256[] memory ids) external onlyOwner {
        require(TIMELOCK);
    }

Problem: Owner Mint Function

Problem
Now, owner mint function without timeline. What the community worry about is, what if the project turn out super famous and the owner mint most of the NFT. This is totally unfair for the community.

Suggestion,
I think we should add a timeline for this function.
For eg, if use sell 10,000 picture. After 1 years, if owner could not sell off everything. Owner will mint the remaining and sell it over opensea or other NFT marketplace. I think this make more sense.

Use Local Memory Type Variable Instead of Global Storage Type Variable in Event to Save Gas

Hi, we recently have conducted a systematic study about Solidity event usage, evolution, and impact, and we are attempting to build a tool to improve the practice of Solidity event use based on our findings. We have tried our prototype tool on some of the most popular GitHub Solidity repositories, and for your repository, we find a potential optimization of gas consumption arisen from event use.

The point is that when we use emit operation to store the value of a certain variable, local memory type variable would be preferable to global storage type (state) variable if they hold the same value. The reason is that an extra SLOAD operation would be needed to access the variable if it is storage type, and the SLOAD operation costs 800 gas.

For your repository, we find that the following event use can be improved:

  • CryptoFightersPotion.sol
    function name:constructor
    event name:  SetBaseURI
    variable:    baseURI->_baseURI
    constructor(string memory _baseURI, address _cryptoFightersV1)
        ERC1155(_baseURI)
    {
        baseURI = _baseURI;
        cryptoFightersV1 = _cryptoFightersV1;
        emit SetBaseURI(baseURI);
    }

  function name:updateBaseUri
  event name:  SetBaseURI
  variable:    baseURI->_baseURI

    function updateBaseUri(string memory _baseURI) external onlyOwner {
        baseURI = _baseURI;
        emit SetBaseURI(baseURI);
    }

Do you find our results useful? Your reply and invaluable suggestions would be greatly appreciated, and are vital for improving our tool. Thanks a lot for your time!

Do we need to have two different mintPrice?

Presale nft price should be lower than publicSale nft price? Right?

Business logic

  • Presale - if you get in, you get discounted price
  • PublicSale - Market Price, usually higher than the presale price

Benefits

  • Good for marketing (people will keep on retweet to get into the preSale list)

Problem with this implementation

  • Refund is getting more complex because it need to keep track of two different price.

Any clue on this?

image

Bad practices in example contract

The example contract (which we should expect will be forked by plenty of developers) uses some bad practices.

  1. Disallowing contracts to mint
  2. Limiting per-address mints

Both of these lend a veneer of extra security but offer absolutely no protection against sophisticated users and bots, while limiting casual and cautious users.

Blocking contracts from minting:
Holding NFTs in an EOA is not necessarily the best practice. Smart contracts wallets and multisigs are sensible options for most users but they suffer because many contracts restrict them arbitrarily. It also confers no security benefits and doesn't stop bots, who can just as well programatically submit many transactions, or transaction bundles.

Limiting per-address mint:
Any sophisticated user or bot can easily set up several addresses and bypass the minting limit as much as they want. This restriction may inflate the number of listed holders and make it look like engagement is being driven, or fairness is implemented, but that is clearly not the case. It may make sense for a whitelist mint, but not for a public mint.

A third problem:
3. Allowing users to send more than quantity * mintPrice to public mint.

This adds no value to users, and can really only cause unsophisticated users to overpay. The UI should set the exact value to be sent in the transaction.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.