I’m building a smart contract ERC20 token with multisig capabilities for Mint and Burn. I do this by having two methods, one to start the tx proposal, and one that actually has the logic.
/// @notice Mints new ERC20 tokens
/// @dev Proposes a transaction to be confirmed using multisig to mint `amount` of tokens.
/// @param to the recipient of the tokens
/// @param amount the amount of tokens
function mint(address to, uint256 amount)
public
multisig(ACTION_MINT, abi.encodeWithSignature('_multisigMint(address,uint256)', to, amount))
{}
/// @notice The function that will be executed on completion of the `mint` multisg requirements
/// @dev Restricted to the multisig contract only
/// @param to the recipient of the tokens
/// @param amount the amount of tokens
function _multisigMint(address to, uint256 amount) public onlyMultisig {
_mint(to, amount);
}
Executing the transaction was coded as follows
function executeTransaction(uint256 transactionId) public {
require(
transactionId <= _transactionId.current(),
'TransactionId does not exist in executeTransaction on MakerCheckerControlled'
);
if (isApproved(transactionId)) {
Transaction storage transaction = _transactions[transactionId];
address(this).functionCall(transaction.data, 'Transaction execution failed!');
}
}
This works perfectly in Hardhat Truffle tests, and when migrating to a Ganache.
But, when migrating a consortium Besu network, it burns through all gas (whatever number I set)
Transaction: 0xf73dc9806fd8cdf12632f105aac6074d286f2ab5e184f63247e6d26845478fe2 exited with an error (status 0) after consuming all gas.
Please check that the transaction:
- satisfies all conditions set by Solidity `assert` statements.
- has enough gas to execute the full transaction.
- does not trigger an invalid opcode by other means (ex: accessing an array out of bounds).
Using truffle debug I think it goes bad “inside” the revert(errorMessage) in _verifyCallResult in Address.sol on line 185. But let’s call this a guess.
Now when I use regular call, it works (verified by total supply and events)
function executeTransaction(uint256 transactionId) public {
require(
transactionId <= _transactionId.current(),
'TransactionId does not exist in executeTransaction on MakerCheckerControlled'
);
if (isApproved(transactionId)) {
Transaction storage transaction = _transactions[transactionId];
address(this).call(transaction.data);
}
}
I have no clue why there would be a difference or what it is…
Environment
- OpenZeppelin Contracts v4.1.0
- Truffle v5.3.6
- Hardhat v2.3.0
- Node v16.1.0
- Ganache CLI v6.12.2 (in Docker)
- Hyperledger Besu 21.1.4
- solc v0.8.4 optimizer enabled with 200 runs, evmVersion istanbul