Skip to content

Commit 49a573c

Browse files
AA benchmark tests (#577)
* add yarn aabenchmark * lint/prettier * fix foundry.toml * format * remove libs incompatible with forge coverage * prettier ignore file w bytecode * rename import --------- Co-authored-by: WhiteOakKong <aadam.debevec@outlook.com> Co-authored-by: WhiteOakKong <92236155+WhiteOakKong@users.noreply.github.com>
1 parent 8da4090 commit 49a573c

File tree

9 files changed

+570
-2
lines changed

9 files changed

+570
-2
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ node_modules/
99
typechain/
1010

1111
# files
12+
src/test/smart-wallet/utils/AABenchmarkArtifacts.sol
1213
coverage.json

contracts/lib/TWStrings.sol

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,132 @@ library TWStrings {
6464
require(value == 0, "Strings: hex length insufficient");
6565
return string(buffer);
6666
}
67+
68+
/// @dev Returns the hexadecimal representation of `value`.
69+
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
70+
/// and the alphabets are capitalized conditionally according to
71+
/// https://eips.ethereum.org/EIPS/eip-55
72+
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
73+
str = toHexString(value);
74+
/// @solidity memory-safe-assembly
75+
assembly {
76+
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
77+
let o := add(str, 0x22)
78+
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
79+
let t := shl(240, 136) // `0b10001000 << 240`
80+
for {
81+
let i := 0
82+
} 1 {
83+
84+
} {
85+
mstore(add(i, i), mul(t, byte(i, hashed)))
86+
i := add(i, 1)
87+
if eq(i, 20) {
88+
break
89+
}
90+
}
91+
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
92+
o := add(o, 0x20)
93+
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
94+
}
95+
}
96+
97+
/// @dev Returns the hexadecimal representation of `value`.
98+
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
99+
function toHexString(address value) internal pure returns (string memory str) {
100+
str = toHexStringNoPrefix(value);
101+
/// @solidity memory-safe-assembly
102+
assembly {
103+
let strLength := add(mload(str), 2) // Compute the length.
104+
mstore(str, 0x3078) // Write the "0x" prefix.
105+
str := sub(str, 2) // Move the pointer.
106+
mstore(str, strLength) // Write the length.
107+
}
108+
}
109+
110+
/// @dev Returns the hexadecimal representation of `value`.
111+
/// The output is encoded using 2 hexadecimal digits per byte.
112+
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
113+
/// @solidity memory-safe-assembly
114+
assembly {
115+
str := mload(0x40)
116+
117+
// Allocate the memory.
118+
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
119+
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
120+
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
121+
mstore(0x40, add(str, 0x80))
122+
123+
// Store "0123456789abcdef" in scratch space.
124+
mstore(0x0f, 0x30313233343536373839616263646566)
125+
126+
str := add(str, 2)
127+
mstore(str, 40)
128+
129+
let o := add(str, 0x20)
130+
mstore(add(o, 40), 0)
131+
132+
value := shl(96, value)
133+
134+
// We write the string from rightmost digit to leftmost digit.
135+
// The following is essentially a do-while loop that also handles the zero case.
136+
for {
137+
let i := 0
138+
} 1 {
139+
140+
} {
141+
let p := add(o, add(i, i))
142+
let temp := byte(i, value)
143+
mstore8(add(p, 1), mload(and(temp, 15)))
144+
mstore8(p, mload(shr(4, temp)))
145+
i := add(i, 1)
146+
if eq(i, 20) {
147+
break
148+
}
149+
}
150+
}
151+
}
152+
153+
/// @dev Returns the hex encoded string from the raw bytes.
154+
/// The output is encoded using 2 hexadecimal digits per byte.
155+
function toHexString(bytes memory raw) internal pure returns (string memory str) {
156+
str = toHexStringNoPrefix(raw);
157+
/// @solidity memory-safe-assembly
158+
assembly {
159+
let strLength := add(mload(str), 2) // Compute the length.
160+
mstore(str, 0x3078) // Write the "0x" prefix.
161+
str := sub(str, 2) // Move the pointer.
162+
mstore(str, strLength) // Write the length.
163+
}
164+
}
165+
166+
/// @dev Returns the hex encoded string from the raw bytes.
167+
/// The output is encoded using 2 hexadecimal digits per byte.
168+
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
169+
/// @solidity memory-safe-assembly
170+
assembly {
171+
let length := mload(raw)
172+
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
173+
mstore(str, add(length, length)) // Store the length of the output.
174+
175+
// Store "0123456789abcdef" in scratch space.
176+
mstore(0x0f, 0x30313233343536373839616263646566)
177+
178+
let o := add(str, 0x20)
179+
let end := add(raw, length)
180+
181+
for {
182+
183+
} iszero(eq(raw, end)) {
184+
185+
} {
186+
raw := add(raw, 1)
187+
mstore8(add(o, 1), mload(and(mload(raw), 15)))
188+
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
189+
o := add(o, 2)
190+
}
191+
mstore(o, 0) // Zeroize the slot after the string.
192+
mstore(0x40, add(o, 0x20)) // Allocate the memory.
193+
}
194+
}
67195
}

foundry.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ remappings = [
3838
'erc721a-upgradeable/=lib/ERC721A-Upgradeable/',
3939
'erc721a/=lib/ERC721A/',
4040
'@thirdweb-dev/dynamic-contracts/=lib/dynamic-contracts/',
41-
'lib/sstore2=lib/dynamic-contracts/lib/sstore2/'
41+
'lib/sstore2=lib/dynamic-contracts/lib/sstore2/',
4242
]
43+
fs_permissions = [{ access = "read-write", path = "./src/test/smart-wallet/utils"}]
4344
src = 'contracts'
4445
test = 'src/test'
4546
verbosity = 0

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
"forge:build": "forge build",
6060
"forge:test": "forge test",
6161
"gas": "forge test --mc Benchmark --gas-report > gasreport.txt",
62-
"forge:snapshot": "forge snapshot --check"
62+
"forge:snapshot": "forge snapshot --check",
63+
"aabenchmark": "forge test --mc AABenchmarkPrepare && forge test --mc ProfileThirdwebAccount -vvv"
6364
},
6465
"dependencies": {}
6566
}

src/test/smart-wallet/utils/AABenchmarkArtifacts.sol

Lines changed: 14 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
// Test utils
5+
import "../../utils/BaseTest.sol";
6+
7+
// Account Abstraction setup for smart wallets.
8+
import { IEntryPoint } from "contracts/prebuilts/account/utils/Entrypoint.sol";
9+
10+
import { AccountFactory } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.sol";
11+
import "contracts/lib/TWStrings.sol";
12+
13+
import "forge-std/Test.sol";
14+
15+
contract AABenchmarkPrepare is BaseTest {
16+
AccountFactory private accountFactory;
17+
18+
function setUp() public override {
19+
super.setUp();
20+
accountFactory = new AccountFactory(IEntryPoint(payable(address(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789))));
21+
}
22+
23+
function test_prepareBenchmarkFile() public {
24+
address accountFactoryAddress = address(accountFactory);
25+
bytes memory accountFactoryBytecode = accountFactoryAddress.code;
26+
27+
address accountImplAddress = accountFactory.accountImplementation();
28+
bytes memory accountImplBytecode = accountImplAddress.code;
29+
30+
string memory accountFactoryAddressString = string.concat(
31+
"address constant THIRDWEB_ACCOUNT_FACTORY_ADDRESS = ",
32+
TWStrings.toHexStringChecksummed(accountFactoryAddress),
33+
";"
34+
);
35+
string memory accountFactoryBytecodeString = string.concat(
36+
'bytes constant THIRDWEB_ACCOUNT_FACTORY_BYTECODE = hex"',
37+
TWStrings.toHexStringNoPrefix(accountFactoryBytecode),
38+
'"',
39+
";"
40+
);
41+
42+
string memory accountImplAddressString = string.concat(
43+
"address constant THIRDWEB_ACCOUNT_IMPL_ADDRESS = ",
44+
TWStrings.toHexStringChecksummed(accountImplAddress),
45+
";"
46+
);
47+
string memory accountImplBytecodeString = string.concat(
48+
'bytes constant THIRDWEB_ACCOUNT_IMPL_BYTECODE = hex"',
49+
TWStrings.toHexStringNoPrefix(accountImplBytecode),
50+
'"',
51+
";"
52+
);
53+
54+
string memory path = "src/test/smart-wallet/utils/AABenchmarkArtifacts.sol";
55+
56+
vm.removeFile(path);
57+
58+
vm.writeLine(path, "");
59+
vm.writeLine(path, "pragma solidity ^0.8.0;");
60+
vm.writeLine(path, "interface ThirdwebAccountFactory {");
61+
vm.writeLine(
62+
path,
63+
" function createAccount(address _admin, bytes calldata _data) external returns (address);"
64+
);
65+
vm.writeLine(
66+
path,
67+
" function getAddress(address _adminSigner, bytes calldata _data) external view returns (address);"
68+
);
69+
vm.writeLine(path, "}");
70+
71+
vm.writeLine(path, "interface ThirdwebAccount {");
72+
vm.writeLine(path, " function execute(address _target, uint256 _value, bytes calldata _calldata) external;");
73+
vm.writeLine(path, "}");
74+
vm.writeLine(path, accountFactoryAddressString);
75+
vm.writeLine(path, accountImplAddressString);
76+
vm.writeLine(path, accountFactoryBytecodeString);
77+
vm.writeLine(path, accountImplBytecodeString);
78+
79+
vm.writeLine(path, "");
80+
}
81+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "./AATestBase.sol";
5+
import { ThirdwebAccountFactory, ThirdwebAccount, THIRDWEB_ACCOUNT_FACTORY_ADDRESS, THIRDWEB_ACCOUNT_IMPL_ADDRESS, THIRDWEB_ACCOUNT_FACTORY_BYTECODE, THIRDWEB_ACCOUNT_IMPL_BYTECODE } from "./AABenchmarkArtifacts.sol";
6+
7+
contract ProfileThirdwebAccount is AAGasProfileBase {
8+
ThirdwebAccountFactory factory;
9+
10+
function setUp() external {
11+
initializeTest("thirdwebAccount");
12+
factory = ThirdwebAccountFactory(THIRDWEB_ACCOUNT_FACTORY_ADDRESS);
13+
vm.etch(address(factory), THIRDWEB_ACCOUNT_FACTORY_BYTECODE);
14+
vm.etch(THIRDWEB_ACCOUNT_IMPL_ADDRESS, THIRDWEB_ACCOUNT_IMPL_BYTECODE);
15+
setAccount();
16+
}
17+
18+
function fillData(
19+
address _to,
20+
uint256 _value,
21+
bytes memory _data
22+
) internal view override returns (bytes memory) {
23+
return abi.encodeWithSelector(ThirdwebAccount.execute.selector, _to, _value, _data);
24+
}
25+
26+
function getSignature(UserOperation memory _op) internal view override returns (bytes memory) {
27+
return signUserOpHash(key, _op);
28+
}
29+
30+
function createAccount(address _owner) internal override {
31+
// if (address(account).code.length == 0) {
32+
factory.createAccount(_owner, "");
33+
// }
34+
}
35+
36+
function getAccountAddr(address _owner) internal view override returns (IAccount) {
37+
return IAccount(factory.getAddress(_owner, ""));
38+
}
39+
40+
function getInitCode(address _owner) internal view override returns (bytes memory) {
41+
return abi.encodePacked(address(factory), abi.encodeWithSelector(factory.createAccount.selector, _owner, ""));
42+
}
43+
44+
function getDummySig(UserOperation memory _op) internal pure override returns (bytes memory) {
45+
return
46+
hex"fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";
47+
}
48+
}

src/test/smart-wallet/utils/AATestArtifacts.sol

Lines changed: 9 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)