Home > database >  Vault that receives NFTs and send tokens in exchange, vice-versa
Vault that receives NFTs and send tokens in exchange, vice-versa

Time:09-08

I'm trying to build a contract that creates a parity 1:1 in between a token (e.g. PunkToken) and an NFT collection (e.g. CryptoPunk).

The idea is to deploy both the token and the NFT collection contracts. Then deploy a vault that stores all the created NFTs. Users can then exchange in both directions PunkToken for CryptoPunk vice-versa at a fixed 1:1 parity.

'Require' asides, my idea was to do something like this:

    // create two functions for doing the swap both ways

    function tokenToNft() public payable {
        _transfer(address(this), msg.sender, 1);   // _transfer function from ERC721
    }

    function nftToToken() public payable {
        _transfer(address(this), msg.sender, 1);   // _transfer function from ERC20
    }

To do so, I tried importing both ERC20 and ERC721 but quickly ran into clashes due to the identical functions names. I then tried to change the names of the ERC20 functions. Not working since the token became unrecognized by the blockchain (I believe).

CodePudding user response:

You can make use of the ERC721 function onERC721Received() that the collection is supposed to invoke on your contract as the recipient when a safe transfer to your contract occurs.

ERC20 does not implement any hook, so you'll need to use the approve() transferFrom() approach to pull the ERC20 tokens from their wallet.

Your contract will act as an intermediary, so it needs to hold these specific tokens.

pragma solidity ^0.8.16;
 
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
 
contract MyVault is IERC721Receiver {
    IERC20 immutable public erc20Token;
    IERC721 immutable public erc721Collection;

    uint256 constant public AMOUNT_OF_ERC20_PER_ERC721 = 1 * 1e18; // 1 token, 18 decimals

    constructor(IERC20 _erc20Token, IERC721 _erc721Collection) {
        erc20Token = _erc20Token;
        erc721Collection = _erc721Collection;
    }

    // ERC721 to ERC20
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4) {
        // TODO validate that you're receiving from a whitelisted collection
        // TODO store which token ID you have received from whom, so that you can return it later

        // Transfer 1 ERC20 token in exchange for the NFT
        bool success = erc20Token.transfer(_operator, AMOUNT_OF_ERC20_PER_ERC721);
        require(success);

        // required by the ERC721 standard
        return this.onERC721Received.selector;
    }

    function erc20toErc721() external {
        // Pull 1 ERC20 token from the user's address to your contract.
        // The user needs to `approve()` your contract to spend their tokens first
        // directly on the ERC20 address, without your contract as the intermediary,
        // otherwise this `transferFrom()` fails
        bool success = erc20Token.transferFrom(msg.sender, address(this), AMOUNT_OF_ERC20_PER_ERC721);
        require(success);

        // Hardcoded for simplification. TODO retrieve which token ID they are eligigle to claim
        uint256 tokenId = 1;

        // send the NFT from your Vault contract to the user
        erc721Collection.safeTransferFrom(address(this), msg.sender, tokenId);
    }
}
  • Related