1

I have this second-layer delegatecall (delegatecall on a contract that already did delegatecall) that continues to fail even after checking all the main reasons why it could fail (storage layout, uint alias, contract, etc.).

And when I switch from delegatecall to call, it runs perfectly (but I need delegatecall).

I simplified the troubling function to a simple getHi()/getHello() (no args) with a console.log in the body, but it still fails. This is a brief summary of the contracts involved in order to show the basic setup:

(I wouldn't bother too much with the first contract. It's just a proxy that redirect calls)

contract Diamond { AppStorage s; fallback() external payable { LibDiamond.DiamondStorage storage ds; bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } address facet = ds.facets[msg.sig]; require(facet != address(0), "Diamond: Function does not exist"); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } } 
contract FacetX { AppStorage s; using SafeERC20 for IERC20; function getHi() public { console.log(s.facetY); //-----> logs the address of FacetY (bool success, ) = s.facetY.delegatecall( //-----> with `call`, runs smoothly abi.encodeWithSignature('getHello()') ); require(success, 'failed'); //-----> fails with this error } } 
contract FacetY { AppStorage s; using SafeERC20 for IERC20; function getHello() public view { console.log('hello world'); //-----> never gets logged } } 

AppStorage is just a struct with all the state vars in a separate file that I import on each contract. Something like:

struct AppStorage { address facetY; address usdt; ... } 

They're defined elsewhere without issues. That's why s.facetY logs the address of FacetY.

1 Answer 1

1

SOLUTION:

delegatecall forwards msg.value (besides msg.sender).

I was calling the proxy while sending Ether and even though my functions don't handle Ether per se, I still needed to label them as payable (both functions in the second and third contract). Otherwise, the tx will fail.

So by adding payable to getHi() and getHello(), the problem was fixed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.