Skip to content

Commit cc25771

Browse files
committed
Improve Sleuth
This patch adds improvements to Sleuth without generally changing its function. Basically, it just makes it revert properly when the constructor or the sleuth query itself fail. This should make a much better debugging experience.
1 parent 1f3e255 commit cc25771

File tree

7 files changed

+92
-33
lines changed

7 files changed

+92
-33
lines changed

src/Sleuth.sol

+33-17
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,45 @@
11
// SPDX-License-Identifier: UNLICENSED
2-
pragma solidity ^0.8.16;
2+
pragma solidity ^0.8.23;
33

44
contract Sleuth {
5+
error DeploymentError();
56

6-
function query(bytes calldata q) external returns (bytes memory) {
7-
return queryInternal(q, abi.encodeWithSignature("query()"));
7+
function query(bytes calldata sleuthQuery) external returns (bytes memory) {
8+
return queryInternal(sleuthQuery, abi.encodeWithSignature("query()"));
89
}
910

10-
function query(bytes calldata q, bytes memory c) external returns (bytes memory) {
11-
return queryInternal(q, c);
11+
function query(bytes calldata sleuthQuery, bytes memory calldata_) external returns (bytes memory) {
12+
return queryInternal(sleuthQuery, calldata_);
1213
}
1314

14-
function queryInternal(bytes memory q, bytes memory c) internal returns (bytes memory) {
15+
function queryInternal(bytes memory sleuthQuery, bytes memory calldata_) internal returns (bytes memory) {
16+
address sleuthContract;
1517
assembly {
16-
let queryLen := mload(q)
17-
let queryStart := add(q, 0x20)
18-
let deployment := create(0, queryStart, queryLen)
19-
let callLen := mload(c)
20-
let callStart := add(c, 0x20)
21-
pop(call(gas(), deployment, 0, callStart, callLen, 0xc0, 0))
22-
returndatacopy(0xc0, 0, returndatasize())
23-
mstore(0x80, 0x20)
24-
mstore(0xa0, returndatasize())
25-
let sz := add(returndatasize(), 0x40)
26-
return(0x80, sz)
18+
sleuthContract := create(0, add(sleuthQuery, 0x20), mload(sleuthQuery))
2719
}
20+
if (sleuthContract == address(0)) {
21+
revert DeploymentError();
22+
}
23+
24+
bool success;
25+
uint256 retSize;
26+
27+
assembly {
28+
success := call(gas(), sleuthContract, 0, add(calldata_, 0x20), mload(calldata_), 0, 0)
29+
retSize := returndatasize()
30+
}
31+
32+
bytes memory sleuthResult = new bytes(retSize);
33+
assembly {
34+
returndatacopy(add(sleuthResult, 0x20), 0x00, retSize)
35+
}
36+
37+
if (!success) {
38+
assembly {
39+
revert(add(sleuthResult, 0x20), retSize)
40+
}
41+
}
42+
43+
return sleuthResult;
2844
}
2945
}

src/examples/BlockNumber.sol

-8
This file was deleted.

test/Sleuth.t.sol

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,36 @@
11
// SPDX-License-Identifier: UNLICENSED
2-
pragma solidity ^0.8.16;
2+
pragma solidity ^0.8.23;
33

44
import "forge-std/Test.sol";
55
import "../src/Sleuth.sol";
6+
import "./examples/BlockNumber.sol";
7+
import "./examples/Pair.sol";
68

79
contract SleuthTest is Test {
810
function testBlockNumber() public {
911
Sleuth sleuth = new Sleuth();
10-
bytes memory blockNumber = vm.getCode("BlockNumber.sol");
11-
uint256 number = abi.decode(sleuth.query(blockNumber), (uint256));
12+
uint256 number = abi.decode(sleuth.query(type(BlockNumber).creationCode), (uint256));
1213
assertEq(number, 1);
1314
}
1415

1516
function testPair() public {
1617
Sleuth sleuth = new Sleuth();
17-
bytes memory pair = vm.getCode("Pair.sol");
18-
(uint256 x, string memory y) = abi.decode(sleuth.query(pair), (uint256, string));
18+
(uint256 x, string memory y) = abi.decode(sleuth.query(type(Pair).creationCode), (uint256, string));
1919
assertEq(x, 55);
2020
assertEq(y, "hello");
2121
}
2222

2323
function testYul() public {
2424
Sleuth sleuth = new Sleuth();
25-
bytes memory yul = vm.getCode("Fun.yul:Query");
25+
// Hex from Fun.yul/Query.json
26+
bytes memory yul = hex"33600055601e8060106000396000f3fe60003560e01c632c46b20514601357600080fd5b600160005260206000f3";
2627
(bool r) = abi.decode(sleuth.query(yul), (bool));
27-
assertEq(r, false);
28+
assertEq(r, true);
29+
}
30+
31+
function testPairFail() public {
32+
Sleuth sleuth = new Sleuth();
33+
vm.expectRevert("bad news");
34+
sleuth.query(type(Pair).creationCode, abi.encodeWithSelector(Pair.queryFail.selector));
2835
}
2936
}
File renamed without changes.

test/examples/BlockNumber.sol

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.23;
3+
4+
contract BlockNumber {
5+
struct Fun {
6+
string cat;
7+
}
8+
9+
struct Cool {
10+
string x;
11+
uint256[] ys;
12+
Fun fun;
13+
}
14+
15+
function query() external view returns (uint256 blockNumber) {
16+
return block.number;
17+
}
18+
19+
function queryTwo() external view returns (uint256 x, uint256 y) {
20+
return (block.number, block.number);
21+
}
22+
23+
function queryThree() external view returns (uint256) {
24+
return block.number;
25+
}
26+
27+
function queryCool() external pure returns (Cool memory cool) {
28+
uint256[] memory ys = new uint256[](3);
29+
ys[0] = 1;
30+
ys[2] = 2;
31+
ys[3] = 3;
32+
return Cool({
33+
x: "hi",
34+
ys: ys,
35+
fun: Fun({
36+
cat: "meow"
37+
})
38+
});
39+
}
40+
}
File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
// SPDX-License-Identifier: UNLICENSED
2-
pragma solidity ^0.8.16;
2+
pragma solidity ^0.8.23;
33

44
contract Pair {
55
function query() external pure returns (uint256, string memory) {
66
return (55, "hello");
77
}
8+
9+
function queryFail() external pure returns (uint256, string memory) {
10+
revert("bad news");
11+
}
812
}

0 commit comments

Comments
 (0)