0

I am trying to call the buyAssetsForEth function in the OpenSeaMarketV2 library using delegatecall as follows:

pragma solidity 0.8.4;

contract Owned {
    address public owner;

    constructor(address _owner) public {
        owner = _owner;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
}

contract Router is Owned {
    struct OpenSeaBuy {
        address a;
    }

    address public openSeaMarketV2 = 0xd9145CCE52D386f254917e481eB44e9943F39138;

    constructor() Owned(msg.sender) public {
    }

    function buyAsset(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public payable {

        (bool success, bytes memory result)=openSeaMarketV2.delegatecall(abi.encodeWithSignature("buyAssetsForEth((address),bool)",openSeaBuys,revertIfTrxFails));
        require(success, 'Delegate Call failed');

    }

    receive() payable external {}

    function contractAddress()
    external view
    returns (address)
    {
        return address(this);
    }

}

The OpenSeaMarketV2 library is deployed separately to 0xd9145CCE52D386f254917e481eB44e9943F39138

library OpenSeaMarketV2 {

    struct OpenSeaBuy {
        address a;
    }

    function buyAssetsForEth(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public {
        uint256 x = 3;
    }
}

But the execution of buyAsset fails with the message: "Delegate Call failed"

Any ideas on why it is failing?

1 Answer 1

2

You just encountered one of the caveats of library ABI encoding. You can read about it here. Essentially, this line related to libraries function selectors is the explanation :

Non-storage structs are referred to by their fully qualified name, i.e. C.S for contract C { struct S { ... } }.

Meaning that the function selector for OpenSeaMarketV2.buyAssetsForEth is not computed from :

  • keccak256(buyAssetsForEth((address),bool)) : 0x443e309e

but rather from :

  • keccak256(buyAssetsForEth(OpenSeaMarketV2.OpenSeaBuy,bool)) : 0x2baccc78

This is a library specific encoding scheme. To solve your issue, there are two main ways. Either change your signature string to explicitly use the one that the library was compiled with :

function buyAsset(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public payable {

    (bool success, bytes memory result)=openSeaMarketV2.delegatecall(abi.encodeWithSignature("buyAssetsForEth(OpenSeaMarketV2.OpenSeaBuy,bool)",openSeaBuys,revertIfTrxFails));
    require(success, 'Delegate Call failed');
}

Or refer directly to the library function selector for a slightly more concise syntax but definitely less error prone :

function buyAsset(OpenSeaBuy memory openSeaBuys, bool revertIfTrxFails) public payable {

    (bool success, bytes memory result)= openSeaMarketV2.delegatecall(abi.encodeWithSelector(OpenSeaMarketV2.buyAssetsForEth.selector,openSeaBuys,revertIfTrxFails));
    require(success, 'Delegate Call failed');
}

I hope that answers your question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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