-
-
Save romeroadrian/535a969c96e0a6f78781287bd0931b6a to your computer and use it in GitHub Desktop.
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
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import "../src/libs/Bytes.sol"; | |
import "../src/libs/SignatureValidator.sol"; | |
import "../src/AmbireAccountFactory.sol"; | |
import "../src/AmbireAccount.sol"; | |
import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | |
interface ACoolWalletFeature { | |
function doSomethingGreatInWallet(uint256) external; | |
} | |
contract TestTryCatch { | |
uint256[20] public foo; | |
function test() external { | |
// simulate expensive operation | |
for (uint256 index = 0; index < 20; index++) { | |
foo[index] = index + 1; | |
} | |
} | |
} | |
contract AuditTest is Test { | |
event LogErr(address indexed to, uint256 value, bytes data, bytes returnData); | |
function setUp() public { | |
} | |
function test_AmbireAccountFactory_EmptyBytecode() public { | |
AmbireAccountFactory factory = new AmbireAccountFactory(address(0)); | |
bytes memory code = hex"00"; | |
uint256 salt = 42; | |
address expectedAddr = address( | |
uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(factory), salt, keccak256(code))))) | |
); | |
factory.deploy(code, salt); | |
// deploy succeeded but account has no code | |
assertEq(expectedAddr.code.length, 0); | |
// this means that a new call to deploy will fail, as the account already exists | |
// but deploySafe will think otherwise because codesize is zero | |
vm.expectRevert(); | |
factory.deploy(code, salt); | |
} | |
function test_AmbireAccount_FallbackDoesntRevert() public { | |
address user = makeAddr("user"); | |
address[] memory addrs = new address[](1); | |
addrs[0] = user; | |
AmbireAccount account = new AmbireAccount(addrs); | |
// wallet doesn't have fallback | |
assertEq(account.privileges(address(0x6969)), bytes32(0)); | |
// the following doesnt fail | |
ACoolWalletFeature(address(account)).doSomethingGreatInWallet(42); | |
} | |
function test_AmbireAccount_LowLevelCallsToAccountsWithNoCode() public { | |
address user = makeAddr("user"); | |
address[] memory addrs = new address[](1); | |
addrs[0] = user; | |
AmbireAccount account = new AmbireAccount(addrs); | |
AmbireAccount.Transaction[] memory txns = new AmbireAccount.Transaction[](1); | |
txns[0].to = address(0x42); | |
txns[0].value = 0; | |
txns[0].data = abi.encodeWithSignature("someFunction(address,uint256)", user, 42); | |
// destination doesnt have any code | |
assertEq(txns[0].to.code.length, 0); | |
// transaction succeeds | |
vm.prank(user); | |
account.executeBySender(txns); | |
} | |
function test_AmbireAccount_ForceFailTryCatch() public { | |
address user = makeAddr("user"); | |
address[] memory addrs = new address[](1); | |
addrs[0] = user; | |
AmbireAccount account = new AmbireAccount(addrs); | |
TestTryCatch testTryCatch = new TestTryCatch(); | |
AmbireAccount.Transaction[] memory txns = new AmbireAccount.Transaction[](1); | |
txns[0].to = address(account); | |
txns[0].value = 0; | |
txns[0].data = abi.encodeWithSelector( | |
AmbireAccount.tryCatch.selector, | |
address(testTryCatch), | |
uint256(0), | |
abi.encodeWithSelector(TestTryCatch.test.selector) | |
); | |
// This should actually be a call to "execute", we simplify the case using "executeBySender" | |
// to avoid the complexity of providing a signature. Core issue remains the same. | |
vm.expectEmit(true, false, false, false); | |
emit LogErr(address(testTryCatch), 0, "", ""); | |
vm.prank(user); | |
account.executeBySender{gas: 450_000}(txns); | |
// assert call to TestTryCatch failed | |
assertEq(testTryCatch.foo(0), 0); | |
} | |
// NOTE: moved this test to a separate file in order to properly test selfdestruct | |
// function test_AmbireAccount_DestroyImplementation() public { | |
// // Master account implementation can be destroyed by any of the configured privileges | |
// address deployer = makeAddr("deployer"); | |
// address user = makeAddr("user"); | |
// // Lets say deployer creates reference implementation | |
// address[] memory addrsImpl = new address[](1); | |
// addrsImpl[0] = deployer; | |
// AmbireAccount implementation = new AmbireAccount(addrsImpl); | |
// // User deploys wallet | |
// address[] memory addrsWallet = new address[](1); | |
// addrsWallet[0] = user; | |
// AmbireAccount wallet = AmbireAccount(payable( | |
// new AccountProxy(addrsWallet, address(implementation)) | |
// )); | |
// // Test the wallet is working ok | |
// assertTrue(wallet.supportsInterface(0x4e2312e0)); | |
// // Now privilege sets fallback | |
// Destroyer destroyer = new Destroyer(); | |
// AmbireAccount.Transaction[] memory txns = new AmbireAccount.Transaction[](1); | |
// txns[0].to = address(implementation); | |
// txns[0].value = 0; | |
// txns[0].data = abi.encodeWithSelector( | |
// AmbireAccount.setAddrPrivilege.selector, | |
// address(0x6969), | |
// bytes32(uint256(uint160(address(destroyer)))) | |
// ); | |
// vm.prank(deployer); | |
// implementation.executeBySender(txns); | |
// // and destroys master implementation | |
// Destroyer(address(implementation)).destruct(); | |
// console.log(address(implementation).code.length); | |
// // Now every wallet (proxy) that points to this master implementation will be bricked | |
// assertTrue(wallet.supportsInterface(0x4e2312e0)); | |
// } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment