3

I have an interface with a public function foo that consumes a single address parameter and returns a Point struct:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IFoo {
    struct Point {
        uint x;
        uint y;
    }
    function foo (address) external view returns (Point memory);
}

In my implementation I want to override foo with a mapping:

contract Foo is IFoo {
    mapping (address => Point) public override foo;
}

But I get the compiler error

TypeError: Overriding public state variable return types differ.

I want to understand how exactly these two types differ. Both consume an address and return a Point, so what's the problem here? I know this problem is unique to structs because when I try it with a map that returns a value type the compiler doesn't complain:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IFoo {
    function foo (address) external view returns (uint);
}

contract Foo is IFoo {
    mapping (address => uint) public override foo;
}

2 Answers 2

4
+50

Your issue is explained by the documentation.

Basically, the getter for :

mapping (address => Point) public override foo;

is :

// return 2 DISTINCT parameters : uint and uint
function foo(address) public returns (uint, uint) { /**/ }

Whereas a struct is really encoded as a tuple such that :

function foo(address) public returns (Point) { /**/ }

Is represented in the abi as :

// Return 1 parameter : tuple of 2 uint
// This code is invalid in solidity, but this is how the abi sees it
function foo(address) public returns ((uint, uint)) { /**/ }

So you get TypeError: Overriding public state variable return types differ.

I see two ways to get around it, they might not be the best so maybe someone will propose a better solution :

  1. Make your interface function foo return two distinct uint
  2. Don't make your mapping public, and write your own getter.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IFoo {
    struct Point {
        uint x;
        uint y;
    }

    // Method 1 : interface returns distinct parameters
    function foo (address) external view returns(uint, uint);

    // Method 2 : interface is left unchanged
    function bar (address) external view returns (Point memory);

}

contract Foo is IFoo{

    // Method 1 : implementation is left unchanged
    mapping (address => IFoo.Point) public override foo;

    // Method 2 : mapping is not public and not named `bar`
    mapping (address => IFoo.Point) _bar;

    // Method 2 : Write your own getter than returns Point
    function bar(address key) public view override returns (Point memory) {
        return _bar[key];
    }
}

I hope that answers your question.

1

I was facing the same issue a while back. This issue cannot be resolved directly. For example, in your code, you are creating a function in the interface for your function foo.

Interface:

interface IFoo {
   struct Point {
      uint x;
      uint y;
   }
   function foo (address) external view returns (Point memory);
}

Contract:

mapping (address => Point) public override foo;

When discussing interface, your function returns the Point struct from a memory location. On the other hand, in the contract, your mapping returns the Point struct from the storage location. This doesn't happen in uint256 or address types, that's why TypeError: Overriding public state variable return types differ. error.

If you still want the struct to be returned, create another function that returns the mapping variable.

Contract:

mapping (address => Point) private _foo;

function foo(address add) public view override returns (Point memory) {
     return _foo[add];
}

In this case, the interface and contract will have Point memory as the return type, hence no error.

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.