0

If we deploy the contract B, and pass the contract B address to the contract A. When call the contract B function from the contract A, inside the contract B function, the msg.sender is the "address of contract A", not external account original calls from the contract A.

Here is the example:

pragma solidity ^0.4.18;

contract metaCoin {
    mapping (address => uint) public balances;
    address[] public addresses;

    function metaCoin() {
        balances[msg.sender] = 10000;
        addresses.push( msg.sender );
    }
    function getBalance(address _addr) public view returns (uint) {
        return balances[_addr];
    }
    function sendToken(address receiver, uint amount) returns(bool successful, address _caller){
        if (balances[msg.sender] < amount) return (false, msg.sender);
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        addresses.push( receiver );
        return (false, msg.sender);
    }
}

contract coinCaller{

    function sendCoin(address coinContractAddress, address receiver, uint amount) public returns (address) {
        metaCoin m = metaCoin(coinContractAddress);
        var (status, addr) = m.sendToken(receiver, amount);
        return addr;
    }
}

Step 1. Using the account, 0x1328500533b017449698300A868fA24eaC1D7486 to deploy metaCoin contract at the address, 0x9628a8814b1b83c67a974f23af8dfd16fea49ae9

Step 2. Use the same account to deploy coinCaller contract at the address 0xd8d5b5f968f119531a941a05682bbbb154d367c0, then call,

coinCaller.sendCoin("0x9628a8814b1b83c67a974f23af8dfd16fea49ae9", "_any_account_addr_", 88)

This function should have transferred the balance from 0x1328500533b017449698300A868fA24eaC1D7486 to _any_account_addr_ with 88. But, it doesn't perform like that. The issue is that in metaCoin.sendToken() the msg.sender is coinCaller contract address, 0xd8d5b5f968f119531a941a05682bbbb154d367c0.

This is documented in the Solidity documentation as the "sender of the message (current call)", which is the coinCaller contract in this call.

Is there a way to work around this to have the external account address of coinCaller.sendCoin()?

  • Unfortunately, you wouldn't be able to safely use an interface like that. It could be done, with some code edits, but it would make the coinCaller an exploitable. – ReyHaynes Feb 12 '18 at 19:05
0

It's not possible or even desirable.

You're trying to achieve an arrangement where contract coinCaller can spend ETH that belongs to other accounts.

You should be treating coinCaller more like an escrow account or an ATM. It has it's own balance.

Not possible: coinCaller instructs Alice's wallet to send ETH to Bob.

Is Possible: Alice sends Alice's ETH to coinCaller with instructions to forward or hold for Bob.

It might help to think of the contract as a sort of programmable vending machine.

Hope it helps.

  • Thanks, Rob. It's true that the current implementation has the reason to be like that. But, sendCoin() function can enforce Alice is the owner of coinCaller, and metaCoin contract can store the "approve" status for the balance transfer from Alice to Bob. Thinking the ERC 20 token, which has the "approve()" Alice to spend X number of token, and inside Alice's contract (as owner), she wants to call "transferFrom( _originalOwner, _toBob, X)" since Alice was approved. With this restriction, it's not possible to implement so. – R.Grove Feb 12 '18 at 19:31
0

I don't go deeply on what you want to achieve with your smart contracts, but to answer your question you can use tx.origin to know the externally owned account sending the original transaction. msg.sender is always the last caller, EOA or Contract Account.

See also this answer for more details.

  • Isn't usage of tx.origin a rather bad idea? I think there are a lot of risks in using tx.origin you have no idea if the original sender is a contract which could be doing something malicious. – TovarishFin Oct 25 '18 at 7:56
  • Not at all. As always when you are developing smart contract, you need know what you are doing, but there is nothing bad in the use of tx.origin per se: that value is always the signer of the transaction, and contract does not sign transaction, so tx.origin is always a EOA. – Giuseppe Bertone Oct 26 '18 at 8:29
0

If here you are moving tokens, the safer way could be:

  • to pass explicitly the msg sender as parameter in the call after a local check of consistency or local reading of msg.sender value before the call;

  • AND to verify that parameter vs tx.origin at the remote end;

  • AND to check that the calling contract be trusted (by some whitelist?)

(I.e. If the passed parameter does coincide with the local tx.origin AND the calling contract is absolutely known and trusted, you can safely move tokens from tx.origin account on behalf of the user. If not you should revert).

First and last checks can be enough when your whitelisting is strong and unbreakable as well.

(Hoping to have truly understood your aim... I’m not sure of it!)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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