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

Is it possible to call a ERC20 function inside a ERC721 contract?

我想要实现的是从 ERC721 合约中的 ERC20 合约调用 transferFrom,如下所示:

我的 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;
    }

}

我的 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;
    }
}

从 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

这是我的 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();
});

为了与 ERC20 代币交互,您必须从所需的合约中创建它的实例。您需要将 ERC20 导入您的 nfts 合约,然后创建一个指向您的代币的 ERC20 代币实例。应该是这样的:

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

然后您将能够与该令牌进行交互:

token.transferFrom("args");

希望此信息对您有用:)

如果您不想要合约的全部功能,您可以使用该界面。

openzeppelin IERC20

您在项目中复制了完整代码。也许创建一个接口目录。然后将其导入您的合约:

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

为了调用合同方法,您始终需要 ABI。由于您将文件导入到合约中,IERC721 将在您的合约中全局可用。为了调用 transferFrom 你需要传递 3 个参数。 “发件人地址”、“收件人地址”和金额。如何使用它的一个例子是这样的

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);
 }