0

I have been struggling to figure out this for a while and maybe someone can help? It is a bit lengthy to look through but if you have a good understanding of smart contracts and hardhat it may be easy for you. It may also be interesting if you are up for a challenge? Caveat... I don't want to use Remix to do this but rather hardhat, ethers.js, and deploy scripts.

Here is a contract (HoeToken) i already deployed and verified on etherscan. When i deployed it, i minted all the tokens to the deployer of the contract. So the tokens aren't held in the contract itself but rather the deployers address.... https://goerli.etherscan.io/address/0xA56AfB65Df413F36f9c753c2e8E56249a44AB812#code

Now here is this contract ("Bucket") that I want to interact with that was deployed by someone else. The ultimate goal is to call this "drop" function in the contract to win the challenge. But i have to transfer my ERC20 tokens on the contract above (HoeToken contact)to win the challenge... https://goerli.etherscan.io/address/0x873289a1aD6Cf024B927bd13bd183B264d274c68#code

So in order to call the "drop" function I created this contract....

pragma solidity 0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

interface Bucket {
    function drop(address erc20, uint256 amount) external;
}

contract EmitWinner {

    constructor() payable {}

    address hoetokenAddress = 0xA56AfB65Df413F36f9c753c2e8E56249a44AB812;
    address bucketAddress = 0x873289a1aD6Cf024B927bd13bd183B264d274c68;

    ERC20 erc20 = ERC20(hoetokenAddress);

    mapping(address => uint) tokenOwnerBalance;
    mapping (address => mapping (address => uint256)) allowed;

    //Approve Emit Winner to spend hoetoken (Do this on etherscan "write" tab)
    function approve(address spender, uint256 _amount) public {
        erc20.approve(spender, _amount);
    }


     function drop(address tokenOwner, uint256 _amount) external {
        bool success = ERC20(erc20).transferFrom(tokenOwner, bucketAddress, _amount);
        require(success, "Transaction Failed");
        Bucket(bucketAddress).drop(tokenOwner, _amount);
    }

    receive() external payable {}
}

So first thing I did was write deploy scripts to deploy this contract (EmitWinner) and verify it on etherscan which was done successfully.

Next i went on etherscan to the contract I just deployed above (Emit Winner), went to contract tab, then the write tab, then using metamask with the deployer address that holds the HoeTokens (initial contract at top), I called the approve function putting in the Emit Winner address and the "amount" of some HoeTokens. Works fine so far.

Now that those tokens have been approved all i need to do is call the drop function on the bucket address account to win this challenge right? And i figure i can do this inside the EmitWinner contract by writing a second script in my deploy folder. If i run this second script using hardhat (I say 2nd script because remember my first deploy script deployed the contract) it should get an instance of the Emitwinner Contract using getContractAt. Then if I use the same private key I initially deployed the contract with as the signer it should give me access to call the drop function right? So here is the second deploy scripts i am running in my terminal with hardhat....

const hre = require("hardhat");

async function main() {

const provider = await ethers.provider;
const privKey = process.env.PRIVATE_KEY;
const signer = await new ethers.Wallet(privKey, provider);

const emitwinnerInstance = await hre.ethers.getContractAt("EmitWinner", 0xcB1200d8Fe06cE2F77fb6567af2f57BB6053502D);

console.log("Invoking transferFrom and Drop Function via Drop...");
tx3 = await emitwinnerInstance.drop(signer, "1");
await tx3.wait();
console.log("Transaction:",tx3.hash);
console.log("Completed");
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
  });

This is where it fails... and says...

reason: 'invalid address or ENS name',
  code: 'INVALID_ARGUMENT',
  argument: 'name',
  value: 1.1593266137294635e+48

Any idea what i am doing wrong and why this fails? My two guesses are... 1.)The way the deploy script is set up is not correct. Or even though the "signer" is the same address and private key as the "deployer", since i am running it off a 2nd script it won't work?

2.)Or maybe the actual drop function in my smart contract is written wrong? Is it possible it doesn't make sense the way i call transfeFrom and then.... Bucket(bucketAddress).drop(tokenOwner, _amount); ? Can i call this by putting in the "tokenOwner" as the address after I called transferFrom?

Those are my guesses or maybe it something else? If you made this far thank you and would be eager to hear what you think the issue is? Thanks in advance for the help?

1 Answer 1

0

When using javascript, you need to write the address inside "...", like this:

const emitwinnerInstance = await hre.ethers.getContractAt("EmitWinner", "0xcB1200d8Fe06cE2F77fb6567af2f57BB6053502D");

This should fix the error. I'm not sure if the rest of the process is correct; let me know if you get another error.


Edit.

Another bug is using signer as argument for address. signer is a object with multiple keys, we should use signer.address.

tx3 = await emitwinnerInstance.drop(signer.address, "1");

It's also possible that ethers wants to be sure we're signing using the correct signer. If this raises another error, try:

tx3 = await emitwinnerInstance.connect(signer).drop(signer.address, "1");

Edit pt2.

If you have complete freedom on how to write the Emit Winner, this is the simplest you can do:

contract EmitWinner {
    address bucketAddress = 0x873289a1aD6Cf024B927bd13bd183B264d274c68;

    constructor() {}

    // This is called by Bucket
    // We just need to return true.
    function transferFrom(address from, address to, uint256 amount) public {
        return true;
    }

    function drop() external {
        address erc20 = address(this);
        uint256 amount = 1;
        Bucket(bucketAddress).drop(erc20, amount);
    }
}

In this case you don't have to have a token, you just trick Bucket into calling the function of this contract. So you just need to call drop() in ethers:

tx3 = await emitwinnerInstance.drop();

If you want to use another token, instead do this:

contract EmitWinner {
    address hoetokenAddress = 0xA56AfB65Df413F36f9c753c2e8E56249a44AB812;
    address bucketAddress = 0x873289a1aD6Cf024B927bd13bd183B264d274c68;
    ERC20 erc20 = ERC20(hoetokenAddress);

    constructor() {}

    function drop(address tokenOwner, uint256 _amount) external {
        // Get tokens from owner
        bool success = erc20.transferFrom(tokenOwner, address(this), _amount);
        require(success, "Transaction Failed");

        // Approve Bucket to spend these tokens
        erc20.approve(bucketAddress, _amount);

        // We are ready to call drop (notice the arguments here)
        Bucket(bucketAddress).drop(hoetokenAddress, _amount);
    }
}

In this case, the token owner (you) needs to approve EmitWinner as spender (otherwise the first transferFrom fails). So you need to make a total of two transactions:

let tx1 = await hoetokenInstance.approve(emitwinnerInstance.address, "1");
await tx1.wait()
let tx2 = await emitwinnerInstance.drop(signer.address, "1");
await tx2.wait()
8
  • Good catch. I put the address in quotes but it still failed with this message... ``` Invoking transferFrom and Drop Function via Drop... Error: invalid address or ENS name (argument="name", value={"_isSigner":true,"address":"0xfDdFf72abDa81C5b6804660e9146E85aC9bB8583","provider":"<WrappedHardhatProvider>"}, code=INVALID_ARGUMENT, version=contracts/5.7.0) ```
    – Blockpain
    Feb 25 at 12:26
  • I think the issue may have something to do with how i am defining the provider but not sure?
    – Blockpain
    Feb 25 at 12:33
  • @Blockpain Ok, it's cause signer is an object not an address. I edited the answer with the fix.
    – 0xSanson
    Feb 25 at 22:23
  • Thanks for the response and good catch. You are right it should be signer.address. I tried with both the first one and the one with "connect" and unfortunately still failing. Error message.... reason: 'cannot estimate gas; transaction may fail or may require manual gas limit', code:' Think the issue might be the way the drop function is written in the smart contract but not sure. Can i call call transferFrom and send tokens to bucketAddress, then call drop with the tokenOwner address? Does tokenOwner still own the tokens or did i give them to bucketAdress in TransferFrom?
    – Blockpain
    Feb 26 at 11:00
  • 1
    Finally got it! Thanks so much for the persistance and help, Learned a lot and finally see what i was doing wrong now. Needed to not only approve the EmitWinner address but also approve the bucketAddress too. Then can finally call drop on the bucket address and the thing i kept thinking i had to put the tokenOwner as the address here..... Bucket(bucketAddress).drop(hoetokenAddress, _amount); Now i see you have to put the hoetokenAddress and it will take it from the signers account at hoeTokenAddress! That confuses me a bit, but all makes sense now. Thanks again. Really appreciate it.
    – Blockpain
    Mar 1 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.