0

I have a crowdsale contract which inherits the following contracts from openzepplin:

 contract MyCrowdsale is Crowdsale, TimedCrowdsale, AllowanceCrowdsale, PostDeliveryCrowdsale, IncreasingPriceCrowdsale {
     constructor(
        uint256 openingTime,
        uint256 closingTime,
        uint256 initialRate,
        uint256 finalRate,
        address tokenWallet,
        address payable wallet,
        IERC20 token
    )
    public
    Crowdsale(initialRate, wallet, token)
    TimedCrowdsale(openingTime, closingTime)
    IncreasingPriceCrowdsale(initialRate, finalRate)
    AllowanceCrowdsale(tokenWallet)
    PostDeliveryCrowdsale()
    {

    }
} 

And everything works fine when I deploy it with the wallet being a Metamask wallet. The wallet is where the collected ETH from the ICO should go.

  await MyCrowdsale.deploy(
     openingTime,
     closingTime,
     initialRate,
     finalRate,
     tokenWallet,  //tokenWallet - address holding MyToken to sell (granted allowance)
     myMetamaskVault, //wallet - where to send ETH from crowdsale. This parameter cannot be a Gnosis Safe???
     tokenProxyAdd
);

When I deploy the crowdsale with myMetamaskVault as the vault, (approve crowdsale to spend from tokenWallet), everything works great.

However, if I simply replace the vault where to send ETH with a Gnosis multi-sig safe, I can no longer call buyTokens() on the crowdsale. It returns Out of gas, as show in my Tenderly stacktrace:

enter image description hereenter image description here

I understand that Gnosis wallets cannot be used to sign transactions, but the approve() is the one that needs signing. Why can't my crowdsale simply transfer received ETH to Gnosis?

Is there a specific limitation in openzepplin Crowdsale that prevents using a multisig safe as the vault?

I suppose I can do this manually, transfering from Metamask to Gnosis as the funds are coming in, but this is obviously not ideal.

You can test on Rinkeby via Etherscan by calling buyTokens():

Working with a metamask address as wallet: https://rinkeby.etherscan.io/address/0x24aBb9Ce02B0349a8170b653400757dE5cFB4E02#writeContract

Same contract, Not working with Gnosis safe as wallet: https://rinkeby.etherscan.io/address/0xAA3F9E7DA337276A9fa5839Fb0FebA31F760469e#writeContract

8
  • What version of Gnosis Safe are you using?
    – Ismael
    Jul 22 at 0:48
  • @Ismael Im using a safe created through OpenZepplin Defender UI and then added to the Gnosis web app as an existing safe. All done recently so must be the latest version.
    – parliament
    Jul 22 at 9:45
  • From the stack trace the fallback code looks non trivial. The last crowdsale from OZ uses transfer to forward the ether so the wallet is subject to the 2300 gas stipend limit.
    – Ismael
    Jul 22 at 20:29
  • @Ismael Im not calling the fallback function. Im calling buyTokens directly through etherscan and metamask. Same process works as long as its not a gnosis safe
    – parliament
    Jul 22 at 21:41
  • OZ's contract does call the wallet fallback when it forwards the ether deposited. Without the contracts is difficult to say anything.
    – Ismael
    Jul 22 at 23:54
0

The crowdsale uses a small amount of gas in the transfer of ETH to the destination wallet, and it is evidently not enough to process a receive in a Gnosis Safe.

You can override the function _forwardFunds to use .call instead of .transfer but you need to make sure to check the return value of .call and revert if it's false.

function _forwardFunds() internal {
    (bool success,) = wallet().call.value(msg.value)('');
    require(success, 'Failed to forward funds');
}

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.