0

I get an EVM exception, when trying to call the external method of a payable function.

This is the called function

contract ContractA {
receive() external payable {}
function bookTicket() external payable{}
}

This is the calling function

contract ContractB {
function test_bookTicket() public{
ContractA newcontract = new ContractA();
newcontract.bookTicket {value:1} ();
}
}

When I try executing test_bookTicket(), EVM throws an exception. As I am new to solidity, can someone help /point out what the issue is? Thanks a ton in advance.

2
  • Can you please share the exception you're seeing?
    – red-swan
    Dec 17, 2023 at 6:36
  • from: 0x5B3...eddC4to: payer1.test_bookTicket() 0xd8b...33fa8value: 0 weidata: 0x2cb...72e9elogs: 0hash: 0x060...1dc03 status 0x0 Transaction mined but execution failed transaction hash 0x060dc9865e5e00759960ec4de216490ab06089d1024688b2f838cb82a871dc03 block hash 0xa3e2403a7e28b56e5330835e681080cc665c3f67ae5e8cf7bd0e7a25f815f38b block number 3 from 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 to payer1.test_bookTicket() 0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8 gas gas transaction cost 30742 gas execution cost 9678 gas input 0x2cb...72e9e decoded input {} decoded output{} logs [] Dec 17, 2023 at 9:40

3 Answers 3

0

So contracts on the chain have addresses. They are instantiated objects. When you write new ContractA(), that wants to deploy a new contract to the chain. So with the code you've shared, my guess would be that ContractA doesn't have a constructor and so will fail when being constructed. Though, you would need to share more explicit details for us to really understand your problem and be able to help.

Generally, solidity files call into other on-chain contracts using interfaces. So, if you have an address of a ContractA contract on chain, then you would call it with something like this:

interface ContractA {
    function bookTicket() external payable;
}

contract ContractB {
    function test_bookTicket(uint256 v) public{
        address targetContractA = address(0x1337);
        ContractA newcontract = ContractA(targetContractA);
        newcontract.bookTicket{value:v}();
    }
}
4
  • Thanks so much for your inputs. Based on the inputs, I added the constructor. I also modified the code as suggested i.e., getting the deployed address of ContractA and the using that address in ContractB. Even after doing both the actions, the EVM throws an exception. One thing that I observed is, if the "VALUE" field in the Remix drop-down is set to 1, then it works. However, my requirement is to set the msg.value field programmatically. Basically the JSON {value:1} in the above code seems not to take effect. Any pointers/help will be much appreciated. Thanks a ton in advance. Dec 17, 2023 at 8:04
  • As mentioned earlier, when I fill the "VALUE" drop down in GUI of Remix with ethers, it works. I want it to be done programmatically. Just wanted to clarify. Thanks Dec 17, 2023 at 12:50
  • Your first question was how to call Contract A from Contract B. Now you have a separate question. Understand, though, that value is part of the transaction that is signed by the user. The user will have to construct the call to Contract B including the value they want to pass through the test_bookTicket method. I.e This method only has access to the ETH that the user sends it or that are already deposited on the contract itself. I've editted the method above to reflect the programmatic value input.
    – red-swan
    Dec 17, 2023 at 18:59
  • 1
    Thanks red-swan. I was able to use the interface idea suggested and could successfully transfer ethers between contracts/addresses. I earlier did not realize that it had something to do with the value and hence did not mention that upfront - sorry about that. Dec 18, 2023 at 13:09
0

First, we have to develop both our contracts, then set the address of contract B in Contract A, then call the book ticket method of Contract A, in which contract A month ethers will be transferred to Contract B, then automatic Ethers amount will be calculated. This will add a token to the user's wallet.

contract ContractA {
    address payable public contractB;

    receive() external payable {
       
    }

    function bookTicket() external payable {
        require(contractB != address(this), "Contract Address not Set!");

        ContractB(contractB).test_bookTicket{value: address(this).balance}(
            msg.sender
        );
    }

    // set book ticket contract
    function setContractB(address newContractB) external {
        contractB = payable(newContractB);
    }
}

// This is the calling function contract, that can add ticket into users wallet
contract ContractB {
    mapping(address => uint256) public userTickets;

    function test_bookTicket(address user) external payable {
        userTickets[user] += address(this).balance;
    }
}
2
  • Thanks Prashant for the input. Both the original code and the code that you provided, works ONLY if we attempt transferring 0 ETH. (In your code as your are using this.balance, it was inherently zero, giving me the clue). So, the problem seems to be getting ETH into the contract (currently there are 0 ETH as shown in Remix). Though the Remix VM Shanghai account has huge number of ethers, how do I get some of it into the current contract on Remix? Kindly let know. Thanks a ton in advance. Dec 17, 2023 at 12:47
  • I added Another Answer You can also check that, I hope this answer fullfil your need. Dec 18, 2023 at 14:02
0

How to use: As soon as we deploy Contract-B, we see some methods in Contract-B.

Then we call test_bookTicket() of Contract-B to send some ether to it. At the same time Contract-A Instance is created and its address is passed in the variable "newcontract".

Instance: Contract-A is basically an instance which is created at run time through the method of Contract-B, the address of Instance Contract-A remains only in deploy condition, we have to verify it ourselves.


// This is Instance Contract
contract ContractA {
    receive() external payable {}
    
    bool public working = false;
    uint public balance = 0;

    function bookTicket() external payable {
        working = true;
        balance += msg.value; 
    }
}

// This is the Main Contract  
contract ContractB {
    // set Instance of Contract-A Address 
    ContractA public newcontract;

    function test_bookTicket() public payable {
        // create instance
        newcontract = new ContractA();
        // transfer this contract ether to Contract-A
        newcontract.bookTicket{value: msg.value}();
    }

    // when test_bookTicket() call: In Contract-A bookTicket() method automatic call and update "working variable status"
    // check contract A "Working Method"
    function checkContractA() external view returns (bool) {
        return newcontract.working();
    }
    
     // when test_bookTicket() call: In Contract-A bookTicket() method automatic call and update "balance variable value"
    // check contract A "Working Method"
    function checkContractA_Balance() external view returns (uint) {
        return newcontract.balance();
    }
    
    // you can put Contract-A Instance contract addess and check the total balance in Contract-A, 
    // check your Contract A Instance Balance 
    function contractBalance(address newcontract_)
        external
        view
        returns (uint256)
    {
        return address(newcontract_).balance;
    }
}

Your Answer

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

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