3

I am trying to pass a struct as an argument while calling the function of another contract:

pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;

library Test { 
  struct TStruct {
    int x;
    int y;
  }   
}

contract A {

  function SetStruct (Test.TStruct sin) public returns (int) {
    data = 10;
    Test.TStruct memory s;
    s.x = sin.x;
    s.y = sin.y;
    return s.x;
  }
}

contract B {
  address public BAddr;

  function TalkToA () public {
    BAddr = new A();
    Test.TStruct memory sin = Test.TStruct(10, 5);
    BAddr.call(bytes4(keccak256("SetStruct((int256,int256))")), sin);
  }
}

Invoking SetStruct directly from Remix causes it to correctly execute. However, when I deploy contract B, the call to SetStruct does not go through. What is the correct way of passing a struct as a parameter in a call invocation?

6

By using abi.encodeWithSignature, your struct successfully to the low-level call.

The code below is modified based on what you provided.

pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;

library Test { 
  struct TStruct {
    int x;
    int y;
  }   
}

contract A {

  function SetStruct (Test.TStruct sin) public returns (int) {
    Test.TStruct memory s;
    s.x = sin.x;
    s.y = sin.y;
    return s.x;
  }
}

contract Bnew {
  address public BAddr;
  bool public success;

  function TalkToA () public returns (bool) {
    BAddr = new A();
    Test.TStruct memory sin = Test.TStruct(10, 5);
    bytes memory data = abi.encodeWithSignature("SetStruct((int256,int256))", sin); 
    success = address(BAddr).call(data);
    return success;
  }
}

The bool public success is introduced to capture the returned value of the call. When testing in Remix, this variable changes from false to true, after calling the TalkToA function. It means that the .call was successful.

The key of the solution is the line: bytes memory data = abi.encodeWithSignature("SetStruct((int256,int256))", sin);

1

As you did, to pass/return a struct you need the line pragma experimental ABIEncoderV2;. Remix itself warns you with

Warning: Experimental features are turned on. Do not use experimental features on live deployments.

What I suggest to you is to unwrap your struct when you need it as argument/return type, and use it only as internal storage.

For example, you could write your function in this way:

function SetStruct (int _x, int _y) public returns (int) {
  data = 10;
  Test.TStruct memory s;
  s.x = _x;
  s.y = _y;
  return s.x;
}

Doing so you can also remove pragma experimental ABIEncoderV2; line.

  • My application logic would benefit greatly if I could pass structs: I need to pass a lot of parameters. Unwrapping would lead to pretty ugly code and structs provide a clean solution to resolve this. – Suvam Mukherjee Jan 23 at 10:35
  • Obviously, like in most of all programming languages. But keep in mind that Solidity is still under development and maybe one day struct, or even functions, could be passed as argument / returned without any problems. But not being part of the community I don't know when it will be available. – 0Alic Jan 23 at 11:04

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.