Skip to content

Commit fb34bb2

Browse files
authored
Merge pull request #44 from delegatable/passthrough-errors
Passthrough errors
2 parents 938879a + 3d4470d commit fb34bb2

File tree

5 files changed

+10059
-10660
lines changed

5 files changed

+10059
-10660
lines changed

contracts/DelegatableCore.sol

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,35 @@ abstract contract DelegatableCore is EIP712Decoder {
4242
address sender
4343
) internal returns (bool success) {
4444
bytes memory full = abi.encodePacked(data, sender);
45+
bytes memory errorMessage;
46+
(success, errorMessage) = address(to).call{gas: gasLimit}(full);
47+
48+
if (!success) {
49+
if (errorMessage.length > 0) {
50+
string memory reason = extractRevertReason(errorMessage);
51+
revert(reason);
52+
} else {
53+
revert("DelegatableCore::execution-failed");
54+
}
55+
}
56+
}
57+
58+
function extractRevertReason(bytes memory revertData)
59+
internal
60+
pure
61+
returns (string memory reason)
62+
{
63+
uint256 l = revertData.length;
64+
if (l < 68) return "";
65+
uint256 t;
66+
assembly {
67+
revertData := add(revertData, 4)
68+
t := mload(revertData) // Save the content of the length slot
69+
mstore(revertData, sub(l, 4)) // Set proper length
70+
}
71+
reason = abi.decode(revertData, (string));
4572
assembly {
46-
success := call(gasLimit, to, 0, add(full, 0x20), mload(full), 0, 0)
73+
mstore(revertData, t) // Restore the content of the length slot
4774
}
4875
}
4976

contracts/DelegatableRelayCore.sol

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,43 @@ abstract contract DelegatableRelayCore is EIP712Decoder {
3535
multiNonce[intendedSender][queue] = nonce;
3636
}
3737

38+
3839
function _execute(
3940
address to,
4041
bytes memory data,
4142
uint256 gasLimit,
4243
address sender
4344
) internal returns (bool success) {
4445
bytes memory full = abi.encodePacked(data, sender);
46+
bytes memory errorMessage;
47+
(success, errorMessage) = address(to).call{gas: gasLimit}(full);
48+
49+
if (!success) {
50+
if (errorMessage.length > 0) {
51+
string memory reason = extractRevertReason(errorMessage);
52+
revert(reason);
53+
} else {
54+
revert("DelegatableCore::execution-failed");
55+
}
56+
}
57+
}
58+
59+
function extractRevertReason(bytes memory revertData)
60+
internal
61+
pure
62+
returns (string memory reason)
63+
{
64+
uint256 l = revertData.length;
65+
if (l < 68) return "";
66+
uint256 t;
67+
assembly {
68+
revertData := add(revertData, 4)
69+
t := mload(revertData) // Save the content of the length slot
70+
mstore(revertData, sub(l, 4)) // Set proper length
71+
}
72+
reason = abi.decode(revertData, (string));
4573
assembly {
46-
success := call(gasLimit, to, 0, add(full, 0x20), mload(full), 0, 0)
74+
mstore(revertData, t) // Restore the content of the length slot
4775
}
4876
}
4977

contracts/mock/MockDelegatable.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ contract MockDelegatable is Delegatable, Ownable {
1313
purpose = purpose_;
1414
}
1515

16+
function alwaysFail() public pure {
17+
revert("I always fail");
18+
}
19+
1620
function _msgSender()
1721
internal
1822
view

test/Delegatable.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,35 @@ describe("Delegatable", () => {
9595
"0xf6e94ae88b8b72d51444924d7cf26f28b4eaf7d5d274dffbdc83cb92cb4eeac5"
9696
);
9797
});
98+
99+
it("INVOKE alwaysFail() bubbles out error", async () => {
100+
const INVOCATION_MESSAGE = {
101+
replayProtection: {
102+
nonce: "0x01",
103+
queue: "0x00",
104+
},
105+
batch: [
106+
{
107+
authority: [],
108+
transaction: {
109+
to: Delegatable.address,
110+
gasLimit: "21000000000000",
111+
data: (await Delegatable.populateTransaction.alwaysFail()).data,
112+
},
113+
},
114+
],
115+
};
116+
const invocation = delegatableUtils.signInvocation(INVOCATION_MESSAGE, pk0);
117+
await expect(
118+
Delegatable.invoke([
119+
{
120+
signature: invocation.signature,
121+
invocations: invocation.invocations,
122+
},
123+
])
124+
).to.be.revertedWith("I always fail");
125+
});
126+
98127
it("READ getEIP712DomainHash(string,string,uint256,address)", async () => {});
99128
it("READ verifyDelegationSignature(SignedDelegation memory signedDelegation)`", async () => {
100129
const _delegation = generateDelegation(

0 commit comments

Comments
 (0)