简体   繁体   English

是否可以在 ERC721 合约中调用 ERC20 function?

[英]Is is possible to call a ERC20 function inside a ERC721 contract?

What I am trying to achieve is calling a transferFrom from ERC20 contract inside a ERC721 contract like this:我想要实现的是在 ERC721 合约中从 ERC20 合约调用transferFrom ,如下所示:

My ERC20 contract:我的 ERC20 合约:

pragma solidity ^0.7.0;
import "../openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "../openzeppelin-contracts/contracts/math/SafeMath.sol";

contract ERC20Token is IERC20 {
  using SafeMath for uint256;
  bytes32[] public candidateList;
   
  uint public totalTokens;
  uint public balanceTokens;
  uint public tokenPrice;
   
  // what is the voter address?
  // total tokens purchased
  // tokens voted per candidate 
   
  struct voter {
    address voterAddress;
    uint tokensBought;
    uint256[] tokensUsedPerCandidate;
  }
   
  mapping(address => voter) public voterInfo;
   
  mapping(bytes32 => uint256) public votesReceived;

  string public symbol;
  string public  name;
  uint8 public decimals;

  mapping(address => uint256) balances;
  mapping(address => mapping(address => uint256)) allowed;
  
  constructor(uint256 _totalTokens, uint256 _tokenPrice, bytes32[] memory _candidateNames)  {
    symbol = "NCToken";
    name = "NCSOFT TOKEN";
    decimals = 0;
    totalTokens = _totalTokens;
    balanceTokens = _totalTokens;
    tokenPrice = _tokenPrice;
    candidateList = _candidateNames;
    emit Transfer(address(0), msg.sender, totalTokens);


   function transferFrom(address from, address to, uint256 tokens) public override returns (bool) { //This is the function I am  trying to call from ERC721 contract
        balances[from] = SafeMath.sub(balances[from], tokens);
        allowed[from][msg.sender] = SafeMath.sub(allowed[from][msg.sender], tokens);
        balances[to] = SafeMath.add(balances[to], tokens);
        emit Transfer(from, to, tokens);
        return true;
    }

}

My ERC721 contract:我的 ERC721 合约:

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

import "../openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import "../openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
import "../openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol";
import "../openzeppelin-contracts/contracts/math/SafeMath.sol";
import "../openzeppelin-contracts/contracts/utils/Address.sol";
import "../openzeppelin-contracts/contracts/utils/Counters.sol";
import "./ERC20Token.sol";

contract NFTtoken is ERC721 {
    using SafeMath for uint256;
    using Address for address;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
   
    bytes32[] candidates = [bytes32('Rama'), bytes32('Nick'), bytes32('Jose')];
    ERC20Token ERC20TokenContract = new ERC20Token(1000, 1, candidates); //instantiating an ERC20 contract
    

    address payable public owner;
    mapping(bytes4 => bool) supportedInterfaces;

    mapping(uint256 => address) tokenOwners; //a mapping from NFT ID to the address that owns it
    mapping(address => uint256) balances; //a mapping from NFT ID to the address that owns it 
    mapping(uint256 => address) allowance; //a mapping from NFT ID to approved address
    mapping(address => mapping(address => bool)) operators; //Mapping from owner address to mapping of operator addresses.
   // mapping (uint256 => string) idToUri;
    uint8 public decimals;

    uint256[] public allValidTokenIds;
    mapping(uint256 => uint256) private allValidTokenIndex;
    string[] public allNFTNames;

    struct NFT {
        //uint NFTID;
        string name;
        address creator;
    }

    mapping(address => NFT) public nftInfo;


    constructor() ERC721("NC NFT example", "NCNFT") {
        owner = msg.sender;
        decimals = 0;
    }


    function mint(string calldata nftName) external payable {
        uint256 newItemId = _tokenIds.current();
        _mint(msg.sender, newItemId);

        nftInfo[msg.sender].name = nftName;
        nftInfo[msg.sender].creator = msg.sender;

        allValidTokenIndex[newItemId] = allValidTokenIds.length;
        allValidTokenIds.push(newItemId);
        _tokenIds.increment();
    }

    function transferNFT(address from, address to, uint256 tokenId)  public returns (bool){
        transferFrom(from, to, tokenId);
        ERC20TokenContract.transferFrom(to, nftInfo[from].creator, 10); 
//<-----***********This is throwing an error! I am trying to call ERC20Token.transferFrom. 
    }

    function allNFTs() public view returns (uint256[] memory) {
        return allValidTokenIds;
    }
}

Error Message when transferNFT from ERC721 is called.调用来自 ERC721 的transferNFT时的错误消息。

MetaMask - RPC Error: [ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert ERC721: operator query for nonexistent token","code":-32000,"data":{"0x0b5d04087c39a8caa2f730815e42f619d33c9d0c3b8682c8c01d3f1ecf0e7d0f":{"error":"revert","program_counter":7889,"return":"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c4552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e0000000000000000000000000000000000000000","reason":"ERC721: operator query for nonexistent token"},"stack":"RuntimeError: VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\n    at Function.RuntimeError.fromResults (C:\\Program Files\\WindowsApps\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\app\\resources\\static\\node\\node_modules\\ganache-core\\lib\\utils\\runtimeerror.js:94:13)\n    at BlockchainDouble.processBlock (C:\\Program Files\\WindowsApps\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\app\\resources\\static\\node\\node_modules\\ganache-core\\lib\\blockchain_double.js:627:24)\n    at runMicrotasks (<anonymous>)\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)","name":"RuntimeError"}}}}' 
{code: -32603, message: `[ethjs-query] while formatting outputs from RPC '{…/task_queues.js:93:5)","name":"RuntimeError"}}}}'`}
code: -32603
message: "[ethjs-query] while formatting outputs from RPC '{\"value\":{\"code\":-32603,\"data\":{\"message\":\"VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\",\"code\":-32000,\"data\":{\"0x0b5d04087c39a8caa2f730815e42f619d33c9d0c3b8682c8c01d3f1ecf0e7d0f\":{\"error\":\"revert\",\"program_counter\":7889,\"return\":\"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c4552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e0000000000000000000000000000000000000000\",\"reason\":\"ERC721: operator query for nonexistent token\"},\"stack\":\"RuntimeError: VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\\n    at Function.RuntimeError.fromResults (C:\\\\Program Files\\\\WindowsApps\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\app\\\\resources\\\\static\\\\node\\\\node_modules\\\\ganache-core\\\\lib\\\\utils\\\\runtimeerror.js:94:13)\\n    at BlockchainDouble.processBlock (C:\\\\Program Files\\\\WindowsApps\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\app\\\\resources\\\\static\\\\node\\\\node_modules\\\\ganache-core\\\\lib\\\\blockchain_double.js:627:24)\\n    at runMicrotasks (<anonymous>)\\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)\",\"name\":\"RuntimeError\"}}}}'"
[[Prototype]]: Object
localhost/:1 Uncaught (in promise) 
{code: -32603, message: `[ethjs-query] while formatting outputs from RPC '{…/task_queues.js:93:5)","name":"RuntimeError"}}}}'`, stack: '{\n  "code": -32603,\n  "message": "[ethjs-query] wh…gaeaoehlefnkodbefgpgknn/background-0.js:1:216902)'}
code: -32603
message: "[ethjs-query] while formatting outputs from RPC '{\"value\":{\"code\":-32603,\"data\":{\"message\":\"VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\",\"code\":-32000,\"data\":{\"0x0b5d04087c39a8caa2f730815e42f619d33c9d0c3b8682c8c01d3f1ecf0e7d0f\":{\"error\":\"revert\",\"program_counter\":7889,\"return\":\"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c4552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e0000000000000000000000000000000000000000\",\"reason\":\"ERC721: operator query for nonexistent token\"},\"stack\":\"RuntimeError: VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\\n    at Function.RuntimeError.fromResults (C:\\\\Program Files\\\\WindowsApps\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\app\\\\resources\\\\static\\\\node\\\\node_modules\\\\ganache-core\\\\lib\\\\utils\\\\runtimeerror.js:94:13)\\n    at BlockchainDouble.processBlock (C:\\\\Program Files\\\\WindowsApps\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\app\\\\resources\\\\static\\\\node\\\\node_modules\\\\ganache-core\\\\lib\\\\blockchain_double.js:627:24)\\n    at runMicrotasks (<anonymous>)\\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)\",\"name\":\"RuntimeError\"}}}}'"
stack: "{\n  \"code\": -32603,\n  \"message\": \"[ethjs-query] while formatting outputs from RPC '{\\\"value\\\":{\\\"code\\\":-32603,\\\"data\\\":{\\\"message\\\":\\\"VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\\\",\\\"code\\\":-32000,\\\"data\\\":{\\\"0x0b5d04087c39a8caa2f730815e42f619d33c9d0c3b8682c8c01d3f1ecf0e7d0f\\\":{\\\"error\\\":\\\"revert\\\",\\\"program_counter\\\":7889,\\\"return\\\":\\\"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c4552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e0000000000000000000000000000000000000000\\\",\\\"reason\\\":\\\"ERC721: operator query for nonexistent token\\\"},\\\"stack\\\":\\\"RuntimeError: VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\\\\n    at Function.RuntimeError.fromResults (C:\\\\\\\\Program Files\\\\\\\\WindowsApps\\\\\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\\\\\app\\\\\\\\resources\\\\\\\\static\\\\\\\\node\\\\\\\\node_modules\\\\\\\\ganache-core\\\\\\\\lib\\\\\\\\utils\\\\\\\\runtimeerror.js:94:13)\\\\n    at BlockchainDouble.processBlock (C:\\\\\\\\Program Files\\\\\\\\WindowsApps\\\\\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\\\\\app\\\\\\\\resources\\\\\\\\static\\\\\\\\node\\\\\\\\node_modules\\\\\\\\ganache-core\\\\\\\\lib\\\\\\\\blockchain_double.js:627:24)\\\\n    at runMicrotasks (<anonymous>)\\\\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)\\\",\\\"name\\\":\\\"RuntimeError\\\"}}}}'\",\n  \"stack\": \"Error: [ethjs-query] while formatting outputs from RPC '{\\\"value\\\":{\\\"code\\\":-32603,\\\"data\\\":{\\\"message\\\":\\\"VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\\\",\\\"code\\\":-32000,\\\"data\\\":{\\\"0x0b5d04087c39a8caa2f730815e42f619d33c9d0c3b8682c8c01d3f1ecf0e7d0f\\\":{\\\"error\\\":\\\"revert\\\",\\\"program_counter\\\":7889,\\\"return\\\":\\\"0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c4552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e0000000000000000000000000000000000000000\\\",\\\"reason\\\":\\\"ERC721: operator query for nonexistent token\\\"},\\\"stack\\\":\\\"RuntimeError: VM Exception while processing transaction: revert ERC721: operator query for nonexistent token\\\\n    at Function.RuntimeError.fromResults (C:\\\\\\\\Program Files\\\\\\\\WindowsApps\\\\\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\\\\\app\\\\\\\\resources\\\\\\\\static\\\\\\\\node\\\\\\\\node_modules\\\\\\\\ganache-core\\\\\\\\lib\\\\\\\\utils\\\\\\\\runtimeerror.js:94:13)\\\\n    at BlockchainDouble.processBlock (C:\\\\\\\\Program Files\\\\\\\\WindowsApps\\\\\\\\GanacheUI_2.5.4.0_x64__5dg5pnz03psnj\\\\\\\\app\\\\\\\\resources\\\\\\\\static\\\\\\\\node\\\\\\\\node_modules\\\\\\\\ganache-core\\\\\\\\lib\\\\\\\\blockchain_double.js:627:24)\\\\n    at runMicrotasks (<anonymous>)\\\\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)\\\",\\\"name\\\":\\\"RuntimeError\\\"}}}}'\\n    at new i (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:148782)\\n    at s (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:146325)\\n    at Object.internal (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:146935)\\n    at y.<anonymous> (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:210928)\\n    at Object.h (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:35204)\\n    at u (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:1:117610)\\n    at y.a.emit (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:1:118146)\\n    at y._setTransactionStatus (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-1.js:1:46740)\\n    at y.setTxStatusFailed (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-1.js:1:45972)\\n    at B._failTransaction (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:225990)\\n    at B.approveTransaction (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:217659)\\n    at async B.updateAndApproveTransaction (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:216902)\"\n}\n  at new i (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:148782)\n  at s (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:146325)\n  at Object.internal (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:146935)\n  at y.<anonymous> (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:210928)\n  at Object.h (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:18:35204)\n  at u (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:1:117610)\n  at y.a.emit (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/common-0.js:1:118146)\n  at y._setTransactionStatus (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-1.js:1:46740)\n  at y.setTxStatusFailed (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-1.js:1:45972)\n  at B._failTransaction (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:225990)\n  at B.approveTransaction (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:217659)\n  at async B.updateAndApproveTransaction (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background-0.js:1:216902)"
[[Prototype]]: Object
setTimeout (async)      
(anonymous) @   inpage.js:1
write   @   inpage.js:1

This is my javascript file calling the functions from both contracts.这是我的 javascript 文件,它从两个合约中调用函数。

// Import libraries we need.
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract'

import voting_artifacts from '../../build/contracts/Voting.json'
import voting_artifacts2 from '../../build/contracts/DeedToken.json'

window.App = {
 start: function() {
  var self = this;
  self.transferNFT();
 },
transferNFT: function() {
  NFTContract.deployed().then(function(contractInstance) {
    let toAddress = $("#to-address").val();
    let NFTid_temp = $("#nft-id").val();
    let NFTid = NFTid_temp.substring(7);
    contractInstance.transferFrom(web3.currentProvider.selectedAddress, toAddress, NFTid, {gas: 140000, from: web3.eth.accounts[0]});
  })
}
}

window.addEventListener('load', async function() {
 if (window.ethereum) {
   await window.ethereum.send('eth_requestAccounts');
   window.web3 = new Web3(window.ethereum);
 }
 else {
  console.warn("No web3 detected.");
  window.web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
 }

 App.start();
});

You could use the interface if you do not want the full functionality of the contract.如果您不想要合约的全部功能,您可以使用该界面。

openzeppelin IERC20 openzeppelin IERC20

You copy full code in your project.您在项目中复制完整代码。 Maybe create an interfaces directory.也许创建一个接口目录。 Then import it to your contract:然后将其导入您的合同:

import "../interfaces/IERC20.sol";

in order to call a contract method, you always need ABI.为了调用一个合约方法,你总是需要 ABI。 Since you imported the file into the contract, IERC721 will be globally available in your contract.由于您将文件导入到合同中, IERC721将在您的合同中全局可用。 In order to call transferFrom you need to pass 3 arguments.为了调用transferFrom ,您需要传递 3 个 arguments。 "from address", "to address", and amount. “从地址”、“到地址”和金额。 An example of how to use it will be like this如何使用它的一个例子是这样的

function stakeTokens(uint256 _amount,address _token) public{
    // add require staments
    // IERC20(_token) this will initialize the contract
    IERC20(_token).transferFrom(msg.sender,address(this),_amount);
 }

In order to interact with an ERC20 token, you have to create an instance of it from the desired contract.为了与 ERC20 代币进行交互,您必须从所需的合约中创建它的一个实例。 You would need to import ERC20 to your nfts contracts, and then create an ERC20 token instance pointing to your token.您需要将 ERC20 导入您的 nfts 合约,然后创建一个指向您的代币的 ERC20 代币实例。 It would be something like this:它会是这样的:

// Inside the nfts contract
ERC20 token = ERC20("your token address here");

And then you will be able to interact with that token as:然后您将能够与该令牌进行交互:

token.transferFrom("args");

Hope you find this information useful:)希望您发现此信息有用:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM