-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix error handling in multicall and move fn to own contract
- Loading branch information
Showing
8 changed files
with
172 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity ^0.8.0; | ||
|
||
import "../utils/MultiCallable.sol"; | ||
|
||
contract MulticallMock is MultiCallable { | ||
error EmptyCustomError(); | ||
error UintCustomError(uint errCode); | ||
uint zero = 0; | ||
|
||
function panicError() public view { | ||
// use storage to trick compiler into thinking this makes sense | ||
uint(100) / zero; | ||
} | ||
|
||
function emptyRevert() public pure { | ||
revert(); | ||
} | ||
|
||
function emptyRequire() public pure { | ||
require(false); | ||
} | ||
|
||
function emptyCustomError() public pure { | ||
revert EmptyCustomError(); | ||
} | ||
|
||
function uintCustomError(uint errCode) public pure { | ||
revert UintCustomError(errCode); | ||
} | ||
|
||
function stringRevert32() public pure { | ||
require(false, "String revert"); | ||
} | ||
|
||
function stringRevert64() public pure { | ||
require(false, "012345678901234567890123456789012345678901234567890123456789001234567890"); | ||
} | ||
|
||
function success() public pure returns (bool) { | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity ^0.8.0; | ||
|
||
contract MultiCallable { | ||
error RevertedWithoutReason(uint index); | ||
|
||
// WARNING: Do not set this function as payable | ||
function multicall(bytes[] calldata data) external returns (bytes[] memory results) { | ||
|
||
uint callCount = data.length; | ||
results = new bytes[](callCount); | ||
|
||
for (uint i = 0; i < callCount; i++) { | ||
(bool ok, bytes memory result) = address(this).delegatecall(data[i]); | ||
|
||
uint length = result.length; | ||
|
||
if (!ok) { | ||
|
||
// 0 length returned from empty revert() / require(false) | ||
if (length == 0) { | ||
revert RevertedWithoutReason(i); | ||
} | ||
|
||
assembly { | ||
result := add(result, 0x20) | ||
revert(result, add(result, length)) | ||
} | ||
} | ||
|
||
results[i] = result; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
const { ethers } = require('hardhat'); | ||
const { expect } = require('chai'); | ||
const { parseEther } = ethers.utils; | ||
const { DIVIDE_BY_ZERO } = require('../utils').errors; | ||
|
||
describe('Multicall unit tests', function () { | ||
it('should bubble up empty custom error signatures', async function () { | ||
const { multicall } = this; | ||
const calldata = multicall.interface.encodeFunctionData('emptyCustomError'); | ||
await expect(multicall.multicall([calldata])).to.be.revertedWithCustomError(multicall, 'EmptyCustomError'); | ||
}); | ||
|
||
it('should bubble up custom error with 32 byte uint value', async function () { | ||
const { multicall } = this; | ||
const calldata = multicall.interface.encodeFunctionData('uintCustomError', [parseEther('3459802')]); | ||
await expect(multicall.multicall([calldata])).to.be.revertedWithCustomError(multicall, 'UintCustomError', [ | ||
calldata, | ||
]); | ||
}); | ||
|
||
it('should bubble up 32 byte string revert messages', async function () { | ||
const { multicall } = this; | ||
const calldata = multicall.interface.encodeFunctionData('stringRevert32'); | ||
await expect(multicall.multicall([calldata])).to.be.revertedWith('String revert'); | ||
}); | ||
|
||
it('should bubble up 64 byte string revert messages', async function () { | ||
const { multicall } = this; | ||
const calldata = multicall.interface.encodeFunctionData('stringRevert64'); | ||
await expect(multicall.multicall([calldata])).to.be.revertedWith( | ||
'012345678901234567890123456789012345678901234567890123456789001234567890', | ||
); | ||
}); | ||
|
||
it('should bubble up panic error codes', async function () { | ||
const { multicall } = this; | ||
const calldata = multicall.interface.encodeFunctionData('panicError'); | ||
await expect(multicall.multicall([calldata])).to.be.revertedWithPanic(DIVIDE_BY_ZERO); | ||
}); | ||
|
||
it('should bubble up first empty revert messages', async function () { | ||
const { multicall } = this; | ||
const calldata = [ | ||
multicall.interface.encodeFunctionData('emptyRevert'), | ||
multicall.interface.encodeFunctionData('success'), | ||
multicall.interface.encodeFunctionData('emptyRevert'), | ||
]; | ||
await expect(multicall.multicall(calldata)).to.be.revertedWithCustomError(multicall, 'RevertedWithoutReason', 0); | ||
}); | ||
|
||
it('should bubble up empty require messages with correct index', async function () { | ||
const { multicall } = this; | ||
const calldata = [ | ||
multicall.interface.encodeFunctionData('success'), // 0 | ||
multicall.interface.encodeFunctionData('success'), // 1 | ||
multicall.interface.encodeFunctionData('emptyRequire'), // 2 | ||
]; | ||
await expect(multicall.multicall(calldata)).to.be.revertedWithCustomError(multicall, 'RevertedWithoutReason', 2); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters