Solidity Contract Invocation – Handling String Parameter in Raw Call

contract-developmentcontract-invocationsolidity

I'm trying to invoke another contract in solidity using raw calls but I'm encountering some problems.

instance.call.gas(gas).value(price)(bytes4(keccak256("method(string)")), param);

this instruction is inside my second contract and while I try to get the value of the param from my first contract function its empty but instead its value is passed into msg.data.

I added an event on my first contract to see how the parameters has been sent.

function method(string param) payable external {
 Debug(param, msg.sender, msg.value, msg.data);
}

basically param is always empty while the data inside the msg are always sent and in msg.data there is my parameter value (in bytes).

I saw this discussion already and I tried to cast my string to bytes32 or passing the value without storing it into a local variable but the result is the same.

Calling a method directly with a parameter eg. instance.method2(stringParam) will work, but I can't use it in my case since I'm trying to call a payable method and I need to sent eth.

Best Answer

You have to properly encode the parameter using the ABI encoding. Something like

instance.call.gas(gas).value(price)(
    bytes4(keccak256("method(string)")),
    u256(0x20),   // pointer to data
    param.length, // length of data
    param         // actual data
);

could work, but it would probably be better to either use inline assembly for that (because it does not include any magic) or wait a little more and use the function abi.encode which should hopefully be available soon.

In general, using low-level calls is discouraged and should only be used of there is no other way, for example if you do not want to propagate a revert condition.

You can do the same (apart from not propagating the revert) using

interface MethodStringInterface {
    function method(string);
}
contract MyContract {
    function myFunction(address _a) {
        MethodStringInterface(_a).method("abc");
    }
}

or even better:

interface MethodStringInterface {
    function method(string);
}
contract MyContract {
    function myFunction(MethodStringInterface _a) {
        _a.method("abc");
    }
}
Related Topic