diff --git a/.github/workflows/build-test-fmt.yml b/.github/workflows/build-test-fmt.yml index e36b588..17f9435 100644 --- a/.github/workflows/build-test-fmt.yml +++ b/.github/workflows/build-test-fmt.yml @@ -26,11 +26,11 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-cafc2606a2187a42b236df4aa65f4e8cdfcea970 + version: nightly-0079a1146b79a4aeda58b0258215bedb1f92700b - name: Run tests working-directory: packages/contracts - run: forge build + run: yarn build - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index fe42c3c..29740de 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -57,7 +57,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1.2.0 with: - version: nightly-cafc2606a2187a42b236df4aa65f4e8cdfcea970 + version: nightly-0079a1146b79a4aeda58b0258215bedb1f92700b - name: Run tests working-directory: packages/contracts diff --git a/.gitignore b/.gitignore index e07a445..0ee936e 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ book # For zksync zkout +.cache diff --git a/docs/getting-started.md b/docs/getting-started.md index 2d145a4..b1c1833 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -10,13 +10,6 @@ First, install foundry by running the following command: curl -L https://foundry.paradigm.xyz | bash ``` -Then, install the specific version of foundry by running the following command: -Note: The latest version of foundry fails some tests. - -```sh -foundryup -v nightly-cafc2606a2187a42b236df4aa65f4e8cdfcea970 -``` - ## Clone the repository ```sh diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 75ca880..406c600 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -32,7 +32,7 @@ Then, move `email_auth.zkey` and `email_auth.wasm` in the unzipped directory `pa Run each integration tests **one by one** as each test will consume lot of memory. ```bash -Eg: forge test --match-test 'testIntegration_Account_Recovery' -vvv --chain 8453 --ffi +Eg: contracts % forge test --skip '*ZKSync*' --match-contract "IntegrationTest" -vvv --chain 8453 --ffi ``` #### Deploy Common Contracts. You need to deploy common contracts, i.e., `ECDSAOwnedDKIMRegistry`, `Verifier`, and implementations of `EmailAuth` and `SimpleWallet`, only once before deploying each wallet. @@ -42,9 +42,9 @@ You need to deploy common contracts, i.e., `ECDSAOwnedDKIMRegistry`, `Verifier`, 4. `forge script script/DeployCommons.s.sol:Deploy --rpc-url $RPC_URL --chain-id $CHAIN_ID --etherscan-api-key $ETHERSCAN_API_KEY --broadcast --verify -vvvv` #### Deploy Each Wallet. -After deploying common contracts, you can deploy a proxy contract of `SimpleWallet`, which is an example contract supporting our email-based account recovery. +After deploying common contracts, you can deploy a proxy contract of `SimpleWallet`, which is an example contract supporting our email-based account recovery by `RecoveryController`. 1. Check that the env values of `DKIM`, `VERIFIER`, `EMAIL_AUTH_IMPL`, and `SIMPLE_WALLET_IMPL` are the same as those output by the `DeployCommons.s.sol` script. -2. `forge script script/DeploySimpleWallet.s.sol:Deploy --rpc-url $RPC_URL --chain-id $CHAIN_ID --broadcast -vvvv` +2. `forge script script/DeployRecoveryController.s.sol:Deploy --rpc-url $RPC_URL --chain-id $CHAIN_ID --broadcast -vvvv` ## Specification There are four main contracts that developers should understand: `IDKIMRegistry`, `Verifier`, `EmailAuth` and `EmailAccountRecovery`. @@ -251,62 +251,40 @@ Next, you should uncomment the following lines in `foundry.toml`. # via-ir = true ``` -And then you should uncomment the following lines in `src/EmailAccountRecovery.sol`. +Partial comment-out files can be found the following. Please uncomment them. +(Uncomment from `FOR_ZKSYNC:START` to `FOR_ZKSYNC:END`) -``` -// import {SystemContractsCaller} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; -// import {DEPLOYER_SYSTEM_CONTRACT} from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; -``` - -And lines 229 - 263 in the `handleAcceptance` function too. +- src/utils/ZKSyncCreate2Factory.sol +- test/helpers/DeploymentHelper.sol -At the first forge build, you got the following warning like the following. +At the first forge build, you need to detect the missing libraries. ``` -┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Warning: Your code or one of its dependencies uses the 'extcodesize' instruction, which is │ -│ usually needed in the following cases: │ -│ 1. To detect whether an address belongs to a smart contract. │ -│ 2. To detect whether the deploy code execution has finished. │ -│ zkSync Era comes with native account abstraction support (so accounts are smart contracts, │ -│ including private-key controlled EOAs), and you should avoid differentiating between contracts │ -│ and non-contract addresses. │ -└──────────────────────────────────────────────────────────────────────────────────────────────────┘ ---> ../../node_modules/forge-std/src/StdCheats.sol - -Failed to compile with zksolc: Missing libraries detected [ZkMissingLibrary { contract_name: "SubjectUtils", contract_path: "src/libraries/SubjectUtils.sol", missing_libraries: ["src/libraries/DecimalUtils.sol:DecimalUtils"] }, ZkMissingLibrary { contract_name: "DecimalUtils", contract_path: "src/libraries/DecimalUtils.sol", missing_libraries: [] }] +forge build --zksync --zk-detect-missing-libraries ``` -Please run the following command in order to deploy the missing libraries: +As you saw before, you need to deploy missing libraries. +You can deploy them by the following command for example. ``` -forge create --deploy-missing-libraries --private-key --rpc-url --chain --zksync -forge create --deploy-missing-libraries --private-key {YOUR_PRIVATE_KEY} --rpc-url https://sepolia.era.zksync.dev --chain 300 --zksync +$ forge build --zksync --zk-detect-missing-libraries +Missing libraries detected: src/libraries/SubjectUtils.sol:SubjectUtils, src/libraries/DecimalUtils.sol:DecimalUtils ``` -The above command output the following(for example): +Run the following command in order to deploy each missing library: ``` -[⠊] Compiling... -No files changed, compilation skipped -Deployer: 0xfB1CcCBDa2C41a77cDAC448641006Fc7fcf1f3b9 -Deployed to: 0x91cc0f0A227b8dD56794f9391E8Af48B40420A0b -Transaction hash: 0x4f94ab71443d01988105540c3abb09ed66f8af5d0bb6a88691e2dafa88b3583d -[⠢] Compiling... -[⠃] Compiling 68 files with 0.8.26 -[⠆] Solc 0.8.26 finished in 12.20s -Compiler run successful! -Deployer: 0xfB1CcCBDa2C41a77cDAC448641006Fc7fcf1f3b9 -Deployed to: 0x981E3Df952358A57753C7B85dE7949Da4aBCf54A -Transaction hash: 0xfdca7b9eb3ae933ca123111489572427ee95eb6be74978b24c73fe74cb4988d7 +forge create src/libraries/DecimalUtils.sol:DecimalUtils --private-key {YOUR_PRIVATE_KEY} --rpc-url https://sepolia.era.zksync.dev --chain 300 --zksync +forge create src/libraries/SubjectUtils.sol:SubjectUtils --private-key {YOUR_PRIVATE_KEY} --rpc-url https://sepolia.era.zksync.dev --chain 300 --zksync --libraries src/libraries/DecimalUtils.sol:DecimalUtils:{DECIMAL_UTILS_DEPLOYED_ADDRESS} ``` After that, you can see the following line in foundry.toml. Also, this line is needed only for foundry-zksync, if you use foundry, please remove this line. Otherwise, the test will fail. ``` -libraries = ["{PROJECT_DIR}/packages/contracts/src/libraries/DecimalUtils.sol:DecimalUtils:{DEPLOYED_ADDRESS}", "{PROJECT_DIR}/packages/contracts/src/libraries/SubjectUtils.sol:SubjectUtils:{DEPLOYED_ADDRESS}"] - +libraries = [ + "{PROJECT_DIR}/packages/contracts/src/libraries/DecimalUtils.sol:DecimalUtils:{DEPLOYED_ADDRESS}", + "{PROJECT_DIR}/packages/contracts/src/libraries/SubjectUtils.sol:SubjectUtils:{DEPLOYED_ADDRESS}"] ``` Incidentally, the above line already exists in `foundy.toml` with it commented out, if you uncomment it by replacing `{PROJECT_DIR}` with the appropriate path, it will also work. @@ -336,7 +314,7 @@ https://github.com/matter-labs/foundry-zksync/issues/382 Failing test cases are here. -EmailAuthWithUserOverrideableDkim.t.sol +DKIMRegistryUpgrade.t.sol - testAuthEmail() @@ -348,25 +326,47 @@ EmailAuth.t.sol - testExpectRevertAuthEmailInvalidSubject() - testExpectRevertAuthEmailInvalidTimestamp() -DeployCommons.t.sol +EmailAuthWithUserOverrideableDkim.t.sol + +- testAuthEmail() -- test_run() +# For integration testing -DeployRecoveryController.t.sol +To pass the instegration testing, you should use era-test-node. +See the following URL and install it. +https://github.com/matter-labs/era-test-node -- test_run() +Run the era-test-node -DeploySimpleWallet.t.sol +``` +era_test_node fork https://sepolia.era.zksync.dev +``` -- test_run() -- test_run_no_dkim() -- test_run_no_email_auth() -- test_run_no_simple_wallet() -- test_run_no_verifier() +You remove .zksolc-libraries-cache directory, and run the following command. -# For integration testing +``` +forge build --zksync --zk-detect-missing-libraries +``` + +As you saw before, you need to deploy missing libraries. +You can deploy them by the following command for example. + +``` +Missing libraries detected: src/libraries/SubjectUtils.sol:SubjectUtils, src/libraries/DecimalUtils.sol:DecimalUtils + +Run the following command in order to deploy each missing library: -forge test --match-test 'testIntegration_Account_Recovery' --zksync --chain 300 -vvv --ffi +forge create src/libraries/DecimalUtils.sol:DecimalUtils --private-key {YOUR_PRIVATE_KEY} --rpc-url http://127.0.0.1:8011 --chain 260 --zksync +forge create src/libraries/SubjectUtils.sol:SubjectUtils --private-key {YOUR_PRIVATE_KEY} --rpc-url http://127.0.0.1:8011 --chain 260 --zksync --libraries src/libraries/DecimalUtils.sol:DecimalUtils:{DECIMAL_UTILS_DEPLOYED_ADDRESS} +``` + +Set the libraries in foundry.toml using the above deployed address. + +And then, run the integration testing. + +``` +forge test --match-contract "IntegrationZkSyncTest" --system-mode=true --zksync --gas-limit 1000000000 --chain 300 -vvv --ffi +``` # For zkSync deployment (For test net) @@ -375,6 +375,6 @@ Second just run the following commands with `--zksync` ``` source .env -forge script script/DeployCommons.s.sol:Deploy --zksync --rpc-url $SEPOLIA_RPC_URL --broadcast -vvvv +forge script script/DeployRecoveryControllerZKSync.s.sol:Deploy --zksync --rpc-url $RPC_URL --broadcast --slow --via-ir --system-mode true -vvvv ``` diff --git a/packages/contracts/foundry.toml b/packages/contracts/foundry.toml index 5c54641..7ff2e1a 100644 --- a/packages/contracts/foundry.toml +++ b/packages/contracts/foundry.toml @@ -22,7 +22,10 @@ build_info = true extra_output = ["storageLayout"] # For missing libraries, please comment out this if you use foundry-zksync -#libraries = ["{PROJECT_DIR}/packages/contracts/src/libraries/DecimalUtils.sol:DecimalUtils:0x91cc0f0a227b8dd56794f9391e8af48b40420a0b", "{PROJECT_DIR}/packages/contracts/src/libraries/SubjectUtils.sol:SubjectUtils:0x981e3df952358a57753c7b85de7949da4abcf54a"] +#libraries = [ +# "{PROJECT_DIR}/packages/contracts/src/libraries/DecimalUtils.sol:DecimalUtils:0x91cc0f0a227b8dd56794f9391e8af48b40420a0b", +# "{PROJECT_DIR}/packages/contracts/src/libraries/SubjectUtils.sol:SubjectUtils:0x981e3df952358a57753c7b85de7949da4abcf54a" +#] [rpc_endpoints] localhost = "${LOCALHOST_RPC_URL}" diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 2234fd8..8893e58 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -3,10 +3,10 @@ "version": "1.0.0", "license": "MIT", "scripts": { - "build": "forge build", + "build": "forge build --skip '*ZKSync*'", "zkbuild": "forge build --zksync", - "test": "forge test --no-match-test \"testIntegration\"", - "zktest": "forge test --no-match-test \"testIntegration\" --zksync --chain 300", + "test": "forge test --no-match-test \"testIntegration\" --skip '*ZKSync*'", + "zktest": "forge test --no-match-test \"testIntegration\" --system-mode=true --zksync --gas-limit 1000000000 --chain 300", "lint": "solhint 'src/**/*.sol'" }, "dependencies": { diff --git a/packages/contracts/script/ChangeOwners.s.sol b/packages/contracts/script/ChangeOwners.s.sol index 2507fab..8f7bd0e 100644 --- a/packages/contracts/script/ChangeOwners.s.sol +++ b/packages/contracts/script/ChangeOwners.s.sol @@ -10,6 +10,7 @@ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Own contract ChangeOwners is Script { function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); if (deployerPrivateKey == 0) { console.log("PRIVATE_KEY env var not set"); diff --git a/packages/contracts/script/DeployEmailAuthWithCreate2ZKSync.s.sol b/packages/contracts/script/DeployEmailAuthWithCreate2ZKSync.s.sol new file mode 100644 index 0000000..5997291 --- /dev/null +++ b/packages/contracts/script/DeployEmailAuthWithCreate2ZKSync.s.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; + +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "../src/EmailAuth.sol"; +import {ZKSyncCreate2Factory} from "../src/utils/ZKSyncCreate2Factory.sol"; + +contract Deploy is Script { + using ECDSA for *; + + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + if (deployerPrivateKey == 0) { + console.log("PRIVATE_KEY env var not set"); + return; + } + + vm.startBroadcast(deployerPrivateKey); + + EmailAuth emailAuth = new EmailAuth(); + + address recoveredAccount = address(0x1); + bytes32 accountSalt = 0x0; + + ERC1967Proxy proxy = new ERC1967Proxy( + address(emailAuth), + abi.encodeCall(emailAuth.initialize, (recoveredAccount, accountSalt, address(this))) + ); + console.log("normal deployed proxyAddress %s", address(proxy)); + + ZKSyncCreate2Factory factory = new ZKSyncCreate2Factory(); + string memory artifact = vm.readFile("zkout/ERC1967Proxy.sol/ERC1967Proxy.json"); + bytes32 bytecodeHash = vm.parseJsonBytes32(artifact, ".hash"); + console.log("bytecodeHash"); + console.logBytes32(bytes32(bytecodeHash)); + + bytes memory emailAuthInit = abi.encode( + address(emailAuth), abi.encodeCall(EmailAuth.initialize, (recoveredAccount, accountSalt, address(this))) + ); + + address computedAddress = factory.computeAddress(accountSalt, bytecodeHash, emailAuthInit); + console.log("computedAddress", computedAddress); + + (bool success, bytes memory returnData) = factory.deploy(accountSalt, bytecodeHash, emailAuthInit); + + address payable proxyAddress = abi.decode(returnData, (address)); + console.log("proxyAddress %s", proxyAddress); + + EmailAuth emailAuthProxy = EmailAuth(proxyAddress); + console.log("emailAuthProxy.controller() %s", emailAuthProxy.controller()); + vm.stopBroadcast(); + } +} diff --git a/packages/contracts/script/DeployRecoveryControllerZKSync.s.sol b/packages/contracts/script/DeployRecoveryControllerZKSync.s.sol new file mode 100644 index 0000000..6767cc5 --- /dev/null +++ b/packages/contracts/script/DeployRecoveryControllerZKSync.s.sol @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; + +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "../test/helpers/SimpleWallet.sol"; +import "../test/helpers/RecoveryControllerZKSync.sol"; +import "../src/utils/Verifier.sol"; +import "../src/utils/ECDSAOwnedDKIMRegistry.sol"; +// import "../src/utils/ForwardDKIMRegistry.sol"; +import "../src/EmailAuth.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ZKSyncCreate2Factory} from "../src/utils/ZKSyncCreate2Factory.sol"; + +contract Deploy is Script { + using ECDSA for *; + + ECDSAOwnedDKIMRegistry dkim; + Verifier verifier; + EmailAuth emailAuthImpl; + SimpleWallet simpleWallet; + RecoveryControllerZKSync recoveryControllerZKSync; + ZKSyncCreate2Factory factoryImpl; + + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + if (deployerPrivateKey == 0) { + console.log("PRIVATE_KEY env var not set"); + return; + } + address signer = vm.envAddress("SIGNER"); + if (signer == address(0)) { + console.log("SIGNER env var not set"); + return; + } + + vm.startBroadcast(deployerPrivateKey); + address initialOwner = msg.sender; + + // Deploy ECDSAOwned DKIM registry + dkim = ECDSAOwnedDKIMRegistry(vm.envOr("ECDSA_DKIM", address(0))); + if (address(dkim) == address(0)) { + ECDSAOwnedDKIMRegistry ecdsaDkimImpl = new ECDSAOwnedDKIMRegistry(); + console.log( + "ECDSAOwnedDKIMRegistry implementation deployed at: %s", + address(ecdsaDkimImpl) + ); + ERC1967Proxy ecdsaDkimProxy = new ERC1967Proxy( + address(ecdsaDkimImpl), + abi.encodeCall(ecdsaDkimImpl.initialize, (initialOwner, signer)) + ); + dkim = ECDSAOwnedDKIMRegistry(address(ecdsaDkimProxy)); + console.log( + "ECDSAOwnedDKIMRegistry deployed at: %s", + address(dkim) + ); + vm.setEnv("ECDSA_DKIM", vm.toString(address(dkim))); + // dkimImpl = new ForwardDKIMRegistry(); + // console.log( + // "ForwardDKIMRegistry implementation deployed at: %s", + // address(dkimImpl) + // ); + // ERC1967Proxy dkimProxy = new ERC1967Proxy( + // address(dkimImpl), + // abi.encodeCall(dkimImpl.initialize, (initialOwner, signer)) + // ); + // dkim = ForwardDKIMRegistry(address(dkimProxy)); + // console.log("ForwardDKIMRegistry deployed at: %s", address(dkim)); + // vm.setEnv("DKIM", vm.toString(address(dkim))); + } + // Deploy Verifier + verifier = Verifier(vm.envOr("VERIFIER", address(0))); + if (address(verifier) == address(0)) { + Verifier verifierImpl = new Verifier(); + console.log( + "Verifier implementation deployed at: %s", + address(verifierImpl) + ); + ERC1967Proxy verifierProxy = new ERC1967Proxy( + address(verifierImpl), + abi.encodeCall(verifierImpl.initialize, (initialOwner)) + ); + verifier = Verifier(address(verifierProxy)); + console.log("Verifier deployed at: %s", address(verifier)); + vm.setEnv("VERIFIER", vm.toString(address(verifier))); + } + + // Deploy EmailAuth Implementation + emailAuthImpl = EmailAuth(vm.envOr("EMAIL_AUTH_IMPL", address(0))); + if (address(emailAuthImpl) == address(0)) { + emailAuthImpl = new EmailAuth(); + console.log( + "EmailAuth implementation deployed at: %s", + address(emailAuthImpl) + ); + vm.setEnv("EMAIL_AUTH_IMPL", vm.toString(address(emailAuthImpl))); + } + + // Deploy Factory + factoryImpl = ZKSyncCreate2Factory(vm.envOr("FACTORY", address(0))); + if (address(factoryImpl) == address(0)) { + factoryImpl = new ZKSyncCreate2Factory(); + console.log( + "Factory implementation deployed at: %s", + address(factoryImpl) + ); + vm.setEnv("FACTORY", vm.toString(address(factoryImpl))); + } + + // Create RecoveryControllerZKSync as EmailAccountRecovery implementation + { + RecoveryControllerZKSync recoveryControllerZKSyncImpl = new RecoveryControllerZKSync(); + ERC1967Proxy recoveryControllerZKSyncProxy = new ERC1967Proxy( + address(recoveryControllerZKSyncImpl), + abi.encodeCall( + recoveryControllerZKSyncImpl.initialize, + ( + signer, + address(verifier), + address(dkim), + address(emailAuthImpl), + address(factoryImpl) + ) + ) + ); + recoveryControllerZKSync = RecoveryControllerZKSync( + payable(address(recoveryControllerZKSyncProxy)) + ); + console.log( + "RecoveryControllerZKSync deployed at: %s", + address(recoveryControllerZKSync) + ); + vm.setEnv( + "RECOVERY_CONTROLLER_ZKSYNC", + vm.toString(address(recoveryControllerZKSync)) + ); + } + + // Deploy SimpleWallet Implementation + { + SimpleWallet simpleWalletImpl = new SimpleWallet(); + console.log( + "SimpleWallet implementation deployed at: %s", + address(simpleWalletImpl) + ); + vm.setEnv( + "SIMPLE_WALLET_IMPL", + vm.toString(address(simpleWalletImpl)) + ); + ERC1967Proxy simpleWalletProxy = new ERC1967Proxy( + address(simpleWalletImpl), + abi.encodeCall( + simpleWalletImpl.initialize, + (signer, address(recoveryControllerZKSync)) + ) + ); + simpleWallet = SimpleWallet(payable(address(simpleWalletProxy))); + console.log("SimpleWallet deployed at: %s", address(simpleWallet)); + vm.setEnv("SIMPLE_WALLET", vm.toString(address(simpleWallet))); + } + vm.stopBroadcast(); + } +} diff --git a/packages/contracts/src/EmailAccountRecovery.sol b/packages/contracts/src/EmailAccountRecovery.sol index 5de0591..c1a87ac 100644 --- a/packages/contracts/src/EmailAccountRecovery.sol +++ b/packages/contracts/src/EmailAccountRecovery.sol @@ -4,9 +4,6 @@ pragma solidity ^0.8.12; import "./EmailAuth.sol"; import "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {L2ContractHelper} from "@matterlabs/zksync-contracts/l2/contracts/L2ContractHelper.sol"; -// import {SystemContractsCaller} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; -// import {DEPLOYER_SYSTEM_CONTRACT} from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; /// @title Email Account Recovery Contract /// @notice Provides mechanisms for email-based account recovery, leveraging guardians and template-based email verification. @@ -16,7 +13,6 @@ abstract contract EmailAccountRecovery { address public verifierAddr; address public dkimAddr; address public emailAuthImplementationAddr; - bytes32 public proxyBytecodeHash = 0x0100008338d33e12c716a5b695c6f7f4e526cf162a9378c0713eea5386c09951; /// @notice Returns the address of the verifier contract. /// @dev This function is virtual and can be overridden by inheriting contracts. @@ -117,51 +113,46 @@ abstract contract EmailAccountRecovery { function computeEmailAuthAddress( address recoveredAccount, bytes32 accountSalt - ) public view returns (address) { - // If on zksync, we use L2ContractHelper.computeCreate2Address - if (block.chainid == 324 || block.chainid == 300) { - // The bytecodeHash is hardcoded here because type(ERC1967Proxy).creationCode doesn't work on eraVM currently - // If you failed some test cases, check the bytecodeHash by yourself - // see, test/ComputeCreate2Address.t.sol - return - L2ContractHelper.computeCreate2Address( - address(this), - accountSalt, - bytes32( - proxyBytecodeHash - ), - keccak256( + ) public view virtual returns (address) { + return + Create2.computeAddress( + accountSalt, + keccak256( + abi.encodePacked( + type(ERC1967Proxy).creationCode, abi.encode( emailAuthImplementation(), abi.encodeCall( EmailAuth.initialize, - (recoveredAccount, accountSalt, address(this)) - ) - ) - ) - ); - } else { - return - Create2.computeAddress( - accountSalt, - keccak256( - abi.encodePacked( - type(ERC1967Proxy).creationCode, - abi.encode( - emailAuthImplementation(), - abi.encodeCall( - EmailAuth.initialize, - ( - recoveredAccount, - accountSalt, - address(this) - ) + ( + recoveredAccount, + accountSalt, + address(this) ) ) ) ) - ); - } + ) + ); + } + + /// @notice Deploys a new proxy contract for email authentication. + /// @dev This function uses the CREATE2 opcode to deploy a new ERC1967Proxy contract with a deterministic address. + /// @param recoveredAccount The address of the account to be recovered. + /// @param accountSalt A bytes32 salt value used to ensure the uniqueness of the deployed proxy address. + /// @return address The address of the newly deployed proxy contract. + function deployEmailAuthProxy( + address recoveredAccount, + bytes32 accountSalt + ) internal virtual returns (address) { + ERC1967Proxy proxy = new ERC1967Proxy{salt: accountSalt}( + emailAuthImplementation(), + abi.encodeCall( + EmailAuth.initialize, + (recoveredAccount, accountSalt, address(this)) + ) + ); + return address(proxy); } /// @notice Calculates a unique subject template ID for an acceptance subject template using its index. @@ -226,51 +217,8 @@ abstract contract EmailAccountRecovery { EmailAuth guardianEmailAuth; if (guardian.code.length == 0) { - // // Deploy proxy of the guardian's EmailAuth contract - // if (block.chainid == 324 || block.chainid == 300) { - // (bool success, bytes memory returnData) = SystemContractsCaller - // .systemCallWithReturndata( - // uint32(gasleft()), - // address(DEPLOYER_SYSTEM_CONTRACT), - // uint128(0), - // abi.encodeCall( - // DEPLOYER_SYSTEM_CONTRACT.create2, - // ( - // emailAuthMsg.proof.accountSalt, - // proxyBytecodeHash, - // abi.encode( - // emailAuthImplementation(), - // abi.encodeCall( - // EmailAuth.initialize, - // ( - // recoveredAccount, - // emailAuthMsg.proof.accountSalt, - // address(this) - // ) - // ) - // ) - // ) - // ) - // ); - // address payable proxyAddress = abi.decode(returnData, (address)); - // ERC1967Proxy proxy = ERC1967Proxy(proxyAddress); - // guardianEmailAuth = EmailAuth(address(proxy)); - // guardianEmailAuth.initialize( - // recoveredAccount, - // emailAuthMsg.proof.accountSalt, - // address(this) - // ); - // } else { - // Deploy proxy of the guardian's EmailAuth contract - ERC1967Proxy proxy = new ERC1967Proxy{salt: emailAuthMsg.proof.accountSalt}( - emailAuthImplementation(), - abi.encodeCall( - EmailAuth.initialize, - (recoveredAccount, emailAuthMsg.proof.accountSalt, address(this)) - ) - ); - guardianEmailAuth = EmailAuth(address(proxy)); - // } + address proxyAddress = deployEmailAuthProxy(recoveredAccount, emailAuthMsg.proof.accountSalt); + guardianEmailAuth = EmailAuth(proxyAddress); guardianEmailAuth.initDKIMRegistry(dkim()); guardianEmailAuth.initVerifier(verifier()); for ( diff --git a/packages/contracts/src/EmailAccountRecoveryZKSync.sol b/packages/contracts/src/EmailAccountRecoveryZKSync.sol new file mode 100644 index 0000000..fbd14a6 --- /dev/null +++ b/packages/contracts/src/EmailAccountRecoveryZKSync.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {EmailAuth} from "./EmailAuth.sol"; +import {EmailAccountRecovery} from "./EmailAccountRecovery.sol"; +import {ZKSyncCreate2Factory} from "./utils/ZKSyncCreate2Factory.sol"; + +/// @title Email Account Recovery Contract +/// @notice Provides mechanisms for email-based account recovery, leveraging guardians and template-based email verification. +/// @dev This contract is abstract and requires implementation of several methods for configuring a new guardian and recovering an account contract. +abstract contract EmailAccountRecoveryZKSync is EmailAccountRecovery { + + // This is the address of the zkSync factory contract + address public factoryAddr; + // The bytecodeHash is hardcoded here because type(ERC1967Proxy).creationCode doesn't work on eraVM currently + // If you failed some test cases, check the bytecodeHash by yourself + // see, test/ComputeCreate2Address.t.sol + bytes32 public constant proxyBytecodeHash = 0x0100008338d33e12c716a5b695c6f7f4e526cf162a9378c0713eea5386c09951; + + /// @notice Returns the address of the zkSyncfactory contract. + /// @dev This function is virtual and can be overridden by inheriting contracts. + /// @return address The address of the zkSync factory contract. + function factory() public view virtual returns (address) { + return factoryAddr; + } + + /// @notice Computes the address for email auth contract using the CREATE2 opcode. + /// @dev This function utilizes the `ZKSyncCreate2Factory` to compute the address. The computation uses a provided account address to be recovered, account salt, + /// and the hash of the encoded ERC1967Proxy creation code concatenated with the encoded email auth contract implementation + /// address and the initialization call data. This ensures that the computed address is deterministic and unique per account salt. + /// @param recoveredAccount The address of the account to be recovered. + /// @param accountSalt A bytes32 salt value defined as a hash of the guardian's email address and an account code. This is assumed to be unique to a pair of the guardian's email address and the wallet address to be recovered. + /// @return address The computed address. + function computeEmailAuthAddress( + address recoveredAccount, + bytes32 accountSalt + ) public view override returns (address) { + // If on zksync, we use another logic to calculate create2 address. + return ZKSyncCreate2Factory(factory()).computeAddress( + accountSalt, + proxyBytecodeHash, + abi.encode( + emailAuthImplementation(), + abi.encodeCall( + EmailAuth.initialize, + (recoveredAccount, accountSalt, address(this)) + ) + ) + ); + } + + /// @notice Deploys a proxy contract for email authentication using the CREATE2 opcode. + /// @dev This function utilizes the `ZKSyncCreate2Factory` to deploy the proxy contract. The deployment uses a provided account address to be recovered, account salt, + /// and the hash of the encoded ERC1967Proxy creation code concatenated with the encoded email auth contract implementation + /// address and the initialization call data. This ensures that the deployed address is deterministic and unique per account salt. + /// @param recoveredAccount The address of the account to be recovered. + /// @param accountSalt A bytes32 salt value defined as a hash of the guardian's email address and an account code. This is assumed to be unique to a pair of the guardian's email address and the wallet address to be recovered. + /// @return address The address of the deployed proxy contract. + function deployEmailAuthProxy( + address recoveredAccount, + bytes32 accountSalt + ) internal override returns (address) { + (bool success, bytes memory returnData) = ZKSyncCreate2Factory(factory()).deploy( + accountSalt, + proxyBytecodeHash, + abi.encode( + emailAuthImplementation(), + abi.encodeCall( + EmailAuth.initialize, + ( + recoveredAccount, + accountSalt, + address(this) + ) + ) + ) + ); + address payable proxyAddress = abi.decode(returnData, (address)); + return proxyAddress; + } +} \ No newline at end of file diff --git a/packages/contracts/src/utils/ZKSyncCreate2Factory.sol b/packages/contracts/src/utils/ZKSyncCreate2Factory.sol new file mode 100644 index 0000000..ee3ba88 --- /dev/null +++ b/packages/contracts/src/utils/ZKSyncCreate2Factory.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {ZKSyncCreate2FactoryBase} from "./ZKSyncCreate2FactoryBase.sol"; +// // FOR_ZKSYNC:START +// import {L2ContractHelper} from "@matterlabs/zksync-contracts/l2/contracts/L2ContractHelper.sol"; +// import {SystemContractsCaller} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; +// import {DEPLOYER_SYSTEM_CONTRACT} from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; +// // FOR_ZKSYNC:END + +contract ZKSyncCreate2Factory is ZKSyncCreate2FactoryBase { + // // FOR_ZKSYNC:START + // function computeAddress(bytes32 salt, bytes32 bytecodeHash, bytes memory input) external view returns (address) { + // return L2ContractHelper.computeCreate2Address(address(this), salt, bytes32(bytecodeHash), keccak256(input)); + // } + + // function deploy(bytes32 salt, bytes32 bytecodeHash, bytes memory input) + // external + // returns (bool success, bytes memory returnData) + // { + // (success, returnData) = SystemContractsCaller.systemCallWithReturndata( + // uint32(gasleft()), + // address(DEPLOYER_SYSTEM_CONTRACT), + // uint128(0), + // abi.encodeCall(DEPLOYER_SYSTEM_CONTRACT.create2, (salt, bytecodeHash, input)) + // ); + // } + // // FOR_ZKSYNC:END +} diff --git a/packages/contracts/src/utils/ZKSyncCreate2FactoryBase.sol b/packages/contracts/src/utils/ZKSyncCreate2FactoryBase.sol new file mode 100644 index 0000000..f97447e --- /dev/null +++ b/packages/contracts/src/utils/ZKSyncCreate2FactoryBase.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +contract ZKSyncCreate2FactoryBase { + function computeAddress(bytes32 salt, bytes32 bytecodeHash, bytes memory input) external virtual view returns (address) { + return address(0); + } + + function deploy(bytes32 salt, bytes32 bytecodeHash, bytes memory input) + external + virtual + returns (bool success, bytes memory returnData) + { + return (false, ""); + } +} diff --git a/packages/contracts/test/ComputeCreate2Address.t.sol b/packages/contracts/test/ComputeCreate2Address.t.sol deleted file mode 100644 index abd0289..0000000 --- a/packages/contracts/test/ComputeCreate2Address.t.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import {L2ContractHelper} from "@matterlabs/zksync-contracts/l2/contracts/L2ContractHelper.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -// import {SystemContractsCaller} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; -// import {DEPLOYER_SYSTEM_CONTRACT} from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; - -import "../src/EmailAuth.sol"; -import "./helpers/StructHelper.sol"; - -contract ComputeCreate2AddressTest is StructHelper { - constructor() {} - - function testComputeCreate2Address() public { - // This test is not neccessary for non zkSync chains - if (block.chainid != 324 && block.chainid != 300) { - console.log("skip"); - return; - } - - address recoveredAccount = address(0x1); - bytes32 accountSalt = 0x0; - - // See the example code - // https://github.com/matter-labs/foundry-zksync/blob/13497a550e4a097c57bec7430435ab810a6d10fc/zk-tests/src/Contracts.t.sol#L195 - string memory artifact = vm.readFile( - "zkout/ERC1967Proxy.sol/ERC1967Proxy.json" - ); - bytes32 bytecodeHash = vm.parseJsonBytes32( - artifact, - '.hash' - ); - console.log("bytecodeHash"); - console.logBytes32(bytes32(bytecodeHash)); - address computedAddress = L2ContractHelper.computeCreate2Address( - address(this), - accountSalt, - bytes32(bytecodeHash), - keccak256( - abi.encode( - address(emailAuth), - abi.encodeCall( - EmailAuth.initialize, - (recoveredAccount, accountSalt, address(this)) - ) - ) - ) - ); - - console.log("computedAddress", computedAddress); - - // This is the previous way to deploy the proxy -> test has been passed but not working actually by clave side - // ERC1967Proxy proxy = new ERC1967Proxy{salt: accountSalt}( - // address(emailAuth), - // abi.encodeCall( - // EmailAuth.initialize, - // (recoveredAccount, accountSalt, address(this)) - // ) - // ); - // console.log("proxy", address(proxy)); - // assertEq(computedAddress, address(proxy)); - - // (bool success, bytes memory returnData) = SystemContractsCaller - // .systemCallWithReturndata( - // uint32(gasleft()), - // address(DEPLOYER_SYSTEM_CONTRACT), - // uint128(0), - // abi.encodeCall( - // DEPLOYER_SYSTEM_CONTRACT.create2, - // ( - // accountSalt, - // bytecodeHash, - // abi.encode( - // address(emailAuth), - // abi.encodeCall( - // EmailAuth.initialize, - // ( - // recoveredAccount, - // accountSalt, - // address(this) - // ) - // ) - // ) - // ) - // ) - // ); - // address payable proxyAddress = abi.decode(returnData, (address)); - } -} diff --git a/packages/contracts/test/ComputeCreate2AddressZKSync.t.sol b/packages/contracts/test/ComputeCreate2AddressZKSync.t.sol new file mode 100644 index 0000000..eb68cbc --- /dev/null +++ b/packages/contracts/test/ComputeCreate2AddressZKSync.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ZKSyncCreate2Factory} from "../src/utils/ZKSyncCreate2Factory.sol"; +import "../src/EmailAuth.sol"; +import "./helpers/StructHelper.sol"; + +contract ComputeCreate2AddressZKSyncTest is StructHelper { + constructor() {} + + function testComputeCreate2Address() public { + // This test is not neccessary for non zkSync chains + if (block.chainid != 324 && block.chainid != 300) { + console.log("skip"); + return; + } + + address recoveredAccount = address(0x1); + bytes32 accountSalt = 0x0; + ZKSyncCreate2Factory factory = new ZKSyncCreate2Factory(); + bytes memory emailAuthInit = abi.encode( + address(emailAuth), abi.encodeCall(EmailAuth.initialize, (recoveredAccount, accountSalt, address(this))) + ); + + // See the example code + // https://github.com/matter-labs/foundry-zksync/blob/13497a550e4a097c57bec7430435ab810a6d10fc/zk-tests/src/Contracts.t.sol#L195 + string memory artifact = vm.readFile("zkout/ERC1967Proxy.sol/ERC1967Proxy.json"); + bytes32 bytecodeHash = vm.parseJsonBytes32(artifact, ".hash"); + console.log("bytecodeHash"); + console.logBytes32(bytes32(bytecodeHash)); + + address computedAddress = factory.computeAddress(accountSalt, bytecodeHash, emailAuthInit); + console.log("computedAddress", computedAddress); + + (bool success, bytes memory returnData) = factory.deploy(accountSalt, bytecodeHash, emailAuthInit); + + address payable proxyAddress = abi.decode(returnData, (address)); + assertEq(proxyAddress, computedAddress); + } +} \ No newline at end of file diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_acceptanceSubjectTemplates.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_acceptanceSubjectTemplates.t.sol new file mode 100644 index 0000000..71ebc88 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_acceptanceSubjectTemplates.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_acceptanceSubjectTemplates is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testAcceptanceSubjectTemplates() public { + skipIfZkSync(); + + setUp(); + string[][] memory res = recoveryController.acceptanceSubjectTemplates(); + assertEq(res[0][0], "Accept"); + assertEq(res[0][1], "guardian"); + assertEq(res[0][2], "request"); + assertEq(res[0][3], "for"); + assertEq(res[0][4], "{ethAddr}"); + } +} diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_completeRecovery.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_completeRecovery.t.sol new file mode 100644 index 0000000..b76c15b --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_completeRecovery.t.sol @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_completeRecovery is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function requestGuardian() public { + setUp(); + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryController.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + } + + function handleAcceptance() public { + requestGuardian(); + + console.log("guardian", guardian); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + // acceptGuardian is internal, we call handleAcceptance, which calls acceptGuardian internally. + vm.startPrank(someRelayer); + recoveryController.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.ACCEPTED + ); + } + + function handleRecovery() public { + handleAcceptance(); + + assertEq(recoveryController.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryController.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryController.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + recoveryController.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + + assertEq(recoveryController.isRecovering(address(simpleWallet)), true); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryController.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + assertEq( + recoveryController.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryController.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + } + + function testCompleteRecovery() public { + skipIfZkSync(); + + handleRecovery(); + + assertEq(recoveryController.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryController.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryController.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryController.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.startPrank(someRelayer); + vm.warp(4 days); + recoveryController.completeRecovery( + address(simpleWallet), + new bytes(0) + ); + vm.stopPrank(); + + assertEq(recoveryController.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryController.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), newSigner); + assertEq( + recoveryController.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + } + + function testExpectRevertCompleteRecoveryRecoveryNotInProgress() public { + skipIfZkSync(); + + handleAcceptance(); + + assertEq(recoveryController.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryController.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryController.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + vm.startPrank(someRelayer); + vm.warp(4 days); + vm.expectRevert(bytes("recovery not in progress")); + bytes memory recoveryCalldata; + recoveryController.completeRecovery( + address(simpleWallet), + recoveryCalldata + ); + + vm.stopPrank(); + } + + function testExpectRevertCompleteRecovery() public { + skipIfZkSync(); + + vm.warp(block.timestamp + 3 days); + + handleRecovery(); + + assertEq(recoveryController.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryController.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryController.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryController.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.warp(0); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("timelock not expired")); + bytes memory recoveryCalldata; + recoveryController.completeRecovery( + address(simpleWallet), + recoveryCalldata + ); + + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_handleAcceptance.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_handleAcceptance.t.sol new file mode 100644 index 0000000..2bda392 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_handleAcceptance.t.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_handleAcceptance is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function requestGuardian() public { + skipIfZkSync(); + + setUp(); + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryController.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + } + + function testHandleAcceptance() public { + skipIfZkSync(); + + requestGuardian(); + + console.log("guardian", guardian); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + // acceptGuardian is internal, we call handleAcceptance, which calls acceptGuardian internally. + vm.startPrank(someRelayer); + recoveryController.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.ACCEPTED + ); + } + + // Can not test recovery in progress using handleAcceptance + // Can not test invalid guardian using handleAcceptance + + function testExpectRevertHandleAcceptanceGuardianStatusMustBeRequested() + public + { + skipIfZkSync(); + + requestGuardian(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + emailAuthMsg.proof.accountSalt = 0x0; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("guardian status must be REQUESTED")); + recoveryController.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleAcceptanceInvalidTemplateIndex() public { + skipIfZkSync(); + + requestGuardian(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + + uint templateIdx = 1; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid template index")); + recoveryController.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleAcceptanceInvalidSubjectParams() public { + skipIfZkSync(); + + requestGuardian(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](2); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + subjectParamsForAcceptance[1] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid subject params")); + recoveryController.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleAcceptanceInvalidWalletAddressInEmail() + public + { + skipIfZkSync(); + + requestGuardian(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryController.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(0x0)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryController.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid account in email")); + recoveryController.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecovery.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_handleRecovery.t.sol similarity index 54% rename from packages/contracts/test/EmailAccountRecovery.t.sol rename to packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_handleRecovery.t.sol index f567025..f497a36 100644 --- a/packages/contracts/test/EmailAccountRecovery.t.sol +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_handleRecovery.t.sol @@ -3,130 +3,20 @@ pragma solidity ^0.8.12; import "forge-std/Test.sol"; import "forge-std/console.sol"; -import {EmailAuthMsg} from "../src/EmailAuth.sol"; -import "./helpers/StructHelper.sol"; -import "./helpers/SimpleWallet.sol"; - +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -contract EmailAccountRecoveryTest is StructHelper { +contract EmailAccountRecoveryTest_handleRecovery is StructHelper { constructor() {} function setUp() public override { super.setUp(); } - function testTransfer() public { - setUp(); - - assertEq(address(simpleWallet).balance, 1 ether); - assertEq(receiver.balance, 0 ether); - - vm.startPrank(deployer); - simpleWallet.transfer(receiver, 1 ether); - vm.stopPrank(); - - assertEq(address(simpleWallet).balance, 0 ether); - assertEq(receiver.balance, 1 ether); - } - - function testExpectRevertTransferOnlyOwner() public { - setUp(); - - assertEq(address(simpleWallet).balance, 1 ether); - assertEq(receiver.balance, 0 ether); - - vm.startPrank(receiver); - vm.expectRevert( - abi.encodeWithSelector( - OwnableUpgradeable.OwnableUnauthorizedAccount.selector, - receiver - ) - ); - simpleWallet.transfer(receiver, 1 ether); - vm.stopPrank(); - } - - function testExpectRevertTransferOnlyOwnerInsufficientBalance() public { - setUp(); - - assertEq(address(simpleWallet).balance, 1 ether); - assertEq(receiver.balance, 0 ether); - - vm.startPrank(deployer); - assertEq(receiver.balance, 0 ether); - vm.expectRevert(bytes("insufficient balance")); - simpleWallet.transfer(receiver, 2 ether); - vm.stopPrank(); - } - - function testWithdraw() public { - setUp(); - - assertEq(address(simpleWallet).balance, 1 ether); - assertEq(deployer.balance, 0 ether); - - vm.startPrank(deployer); - simpleWallet.withdraw(1 ether); - vm.stopPrank(); - - assertEq(address(simpleWallet).balance, 0 ether); - assertEq(deployer.balance, 1 ether); - } - - function testExpectRevertWithdrawOnlyOwner() public { - setUp(); - - assertEq(address(simpleWallet).balance, 1 ether); - assertEq(deployer.balance, 0 ether); - - vm.startPrank(receiver); - vm.expectRevert( - abi.encodeWithSelector( - OwnableUpgradeable.OwnableUnauthorizedAccount.selector, - address(receiver) - ) - ); - simpleWallet.withdraw(1 ether); - vm.stopPrank(); - } - - function testExpectRevertWithdrawInsufficientBalance() public { - setUp(); - - assertEq(address(simpleWallet).balance, 1 ether); - assertEq(deployer.balance, 0 ether); - - vm.startPrank(deployer); - vm.expectRevert(bytes("insufficient balance")); - simpleWallet.withdraw(10 ether); - vm.stopPrank(); - } - - function testAcceptanceSubjectTemplates() public { - setUp(); - string[][] memory res = recoveryController.acceptanceSubjectTemplates(); - assertEq(res[0][0], "Accept"); - assertEq(res[0][1], "guardian"); - assertEq(res[0][2], "request"); - assertEq(res[0][3], "for"); - assertEq(res[0][4], "{ethAddr}"); - } - - function testRecoverySubjectTemplates() public { - setUp(); - string[][] memory res = recoveryController.recoverySubjectTemplates(); - assertEq(res[0][0], "Set"); - assertEq(res[0][1], "the"); - assertEq(res[0][2], "new"); - assertEq(res[0][3], "signer"); - assertEq(res[0][4], "of"); - assertEq(res[0][5], "{ethAddr}"); - assertEq(res[0][6], "to"); - assertEq(res[0][7], "{ethAddr}"); - } - - function testRequestGuardian() public { + function requestGuardian() public { setUp(); require( recoveryController.guardians(guardian) == @@ -143,55 +33,10 @@ contract EmailAccountRecoveryTest is StructHelper { ); } - // function testRequestGuardianNotOwner() public { - // setUp(); - - // require( - // recoveryController.guardians(guardian) == - // recoveryController.GuardianStatus.NONE - // ); - - // vm.startPrank(receiver); - // recoveryController.requestGuardian(guardian); - // vm.stopPrank(); - - // require( - // recoveryController.guardians(guardian) == - // recoveryController.GuardianStatus.NONE - // ); - // } - - function testExpectRevertRequestGuardianInvalidGuardian() public { - setUp(); + function handleAcceptance() public { + skipIfZkSync(); - require( - recoveryController.guardians(guardian) == - RecoveryController.GuardianStatus.NONE - ); - - vm.startPrank(deployer); - vm.expectRevert(bytes("invalid guardian")); - recoveryController.requestGuardian(address(0x0)); - vm.stopPrank(); - } - - function testExpectRevertRequestGuardianGuardianStatusMustBeNone() public { - setUp(); - - require( - recoveryController.guardians(guardian) == - RecoveryController.GuardianStatus.NONE - ); - - vm.startPrank(deployer); - recoveryController.requestGuardian(guardian); - vm.expectRevert(bytes("guardian status must be NONE")); - recoveryController.requestGuardian(guardian); - vm.stopPrank(); - } - - function testHandleAcceptance() public { - testRequestGuardian(); + requestGuardian(); console.log("guardian", guardian); @@ -228,141 +73,10 @@ contract EmailAccountRecoveryTest is StructHelper { ); } - // Can not test recovery in progress using handleAcceptance - // Can not test invalid guardian using handleAcceptance - - function testExpectRevertHandleAcceptanceGuardianStatusMustBeRequested() - public - { - testRequestGuardian(); - - require( - recoveryController.guardians(guardian) == - RecoveryController.GuardianStatus.REQUESTED - ); - - uint templateIdx = 0; - - EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); - uint templateId = recoveryController.computeAcceptanceTemplateId( - templateIdx - ); - emailAuthMsg.templateId = templateId; - bytes[] memory subjectParamsForAcceptance = new bytes[](1); - subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); - emailAuthMsg.subjectParams = subjectParamsForAcceptance; - emailAuthMsg.proof.accountSalt = 0x0; - - vm.mockCall( - address(recoveryController.emailAuthImplementationAddr()), - abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), - abi.encode(0x0) - ); - - vm.startPrank(someRelayer); - vm.expectRevert(bytes("guardian status must be REQUESTED")); - recoveryController.handleAcceptance(emailAuthMsg, templateIdx); - vm.stopPrank(); - } - - function testExpectRevertHandleAcceptanceInvalidTemplateIndex() public { - testRequestGuardian(); - - require( - recoveryController.guardians(guardian) == - RecoveryController.GuardianStatus.REQUESTED - ); - - uint templateIdx = 1; - - EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); - uint templateId = recoveryController.computeAcceptanceTemplateId( - templateIdx - ); - emailAuthMsg.templateId = templateId; - bytes[] memory subjectParamsForAcceptance = new bytes[](1); - subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); - emailAuthMsg.subjectParams = subjectParamsForAcceptance; - - vm.mockCall( - address(recoveryController.emailAuthImplementationAddr()), - abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), - abi.encode(0x0) - ); - - vm.startPrank(someRelayer); - vm.expectRevert(bytes("invalid template index")); - recoveryController.handleAcceptance(emailAuthMsg, templateIdx); - vm.stopPrank(); - } - - function testExpectRevertHandleAcceptanceInvalidSubjectParams() public { - testRequestGuardian(); - - require( - recoveryController.guardians(guardian) == - RecoveryController.GuardianStatus.REQUESTED - ); - - uint templateIdx = 0; - - EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); - uint templateId = recoveryController.computeAcceptanceTemplateId( - templateIdx - ); - emailAuthMsg.templateId = templateId; - bytes[] memory subjectParamsForAcceptance = new bytes[](2); - subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); - subjectParamsForAcceptance[1] = abi.encode(address(simpleWallet)); - emailAuthMsg.subjectParams = subjectParamsForAcceptance; - - vm.mockCall( - address(recoveryController.emailAuthImplementationAddr()), - abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), - abi.encode(0x0) - ); - - vm.startPrank(someRelayer); - vm.expectRevert(bytes("invalid subject params")); - recoveryController.handleAcceptance(emailAuthMsg, templateIdx); - vm.stopPrank(); - } - - function testExpectRevertHandleAcceptanceInvalidWalletAddressInEmail() - public - { - testRequestGuardian(); - - require( - recoveryController.guardians(guardian) == - RecoveryController.GuardianStatus.REQUESTED - ); - - uint templateIdx = 0; - - EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); - uint templateId = recoveryController.computeAcceptanceTemplateId( - templateIdx - ); - emailAuthMsg.templateId = templateId; - bytes[] memory subjectParamsForAcceptance = new bytes[](1); - subjectParamsForAcceptance[0] = abi.encode(address(0x0)); - emailAuthMsg.subjectParams = subjectParamsForAcceptance; - - vm.mockCall( - address(recoveryController.emailAuthImplementationAddr()), - abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), - abi.encode(0x0) - ); - - vm.startPrank(someRelayer); - vm.expectRevert(bytes("invalid account in email")); - recoveryController.handleAcceptance(emailAuthMsg, templateIdx); - vm.stopPrank(); - } - function testHandleRecovery() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -417,7 +131,9 @@ contract EmailAccountRecoveryTest is StructHelper { } function testExpectRevertHandleRecoveryGuardianIsNotDeployed() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -458,7 +174,9 @@ contract EmailAccountRecoveryTest is StructHelper { } function testExpectRevertHandleRecoveryInvalidTemplateId() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -499,7 +217,9 @@ contract EmailAccountRecoveryTest is StructHelper { function testExpectRevertHandleRecoveryGuardianStatusMustBeAccepted() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -553,7 +273,9 @@ contract EmailAccountRecoveryTest is StructHelper { } function testExpectRevertHandleRecoveryInvalidTemplateIndex() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -592,7 +314,9 @@ contract EmailAccountRecoveryTest is StructHelper { } function testExpectRevertHandleRecoveryInvalidSubjectParams() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -632,7 +356,7 @@ contract EmailAccountRecoveryTest is StructHelper { } // function testExpectRevertHandleRecoveryInvalidGuardianInEmail() public { - // testHandleAcceptance(); + // handleAcceptance(); // assertEq(recoveryController.isRecovering(address(simpleWallet)), false); // assertEq( @@ -667,7 +391,9 @@ contract EmailAccountRecoveryTest is StructHelper { // } function testExpectRevertHandleRecoveryInvalidNewSigner() public { - testHandleAcceptance(); + skipIfZkSync(); + + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); assertEq( @@ -704,70 +430,4 @@ contract EmailAccountRecoveryTest is StructHelper { recoveryController.handleRecovery(emailAuthMsg, templateIdx); vm.stopPrank(); } - - function testExpectRevertRejectRecoveryOwnableUnauthorizedAccount() public { - testHandleRecovery(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), true); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - block.timestamp + - recoveryController.timelockPeriodOfAccount( - address(simpleWallet) - ) - ); - assertEq(simpleWallet.owner(), deployer); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - newSigner - ); - - vm.startPrank(deployer); - vm.expectRevert("recovery not in progress"); - recoveryController.rejectRecovery(); - vm.stopPrank(); - } - - function testCompleteRecovery() public { - testHandleRecovery(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), true); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - block.timestamp + - recoveryController.timelockPeriodOfAccount( - address(simpleWallet) - ) - ); - assertEq(simpleWallet.owner(), deployer); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - newSigner - ); - - vm.startPrank(someRelayer); - vm.warp(4 days); - recoveryController.completeRecovery( - address(simpleWallet), - new bytes(0) - ); - vm.stopPrank(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), false); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - 0 - ); - assertEq(simpleWallet.owner(), newSigner); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - address(0x0) - ); - } } diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_recoverySubjectTemplates.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_recoverySubjectTemplates.t.sol new file mode 100644 index 0000000..50e720a --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_recoverySubjectTemplates.t.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_recoverySubjectTemplates is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testRecoverySubjectTemplates() public { + skipIfZkSync(); + + setUp(); + string[][] memory res = recoveryController.recoverySubjectTemplates(); + assertEq(res[0][0], "Set"); + assertEq(res[0][1], "the"); + assertEq(res[0][2], "new"); + assertEq(res[0][3], "signer"); + assertEq(res[0][4], "of"); + assertEq(res[0][5], "{ethAddr}"); + assertEq(res[0][6], "to"); + assertEq(res[0][7], "{ethAddr}"); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryForRejectRecovery.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_rejectRecovery.t.sol similarity index 63% rename from packages/contracts/test/EmailAccountRecoveryForRejectRecovery.t.sol rename to packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_rejectRecovery.t.sol index 7b346fc..2c55471 100644 --- a/packages/contracts/test/EmailAccountRecoveryForRejectRecovery.t.sol +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_rejectRecovery.t.sol @@ -3,13 +3,13 @@ pragma solidity ^0.8.12; import "forge-std/Test.sol"; import "forge-std/console.sol"; -import {EmailAuthMsg} from "../src/EmailAuth.sol"; -import "./helpers/StructHelper.sol"; -import "./helpers/SimpleWallet.sol"; - +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -contract EmailAccountRecoveryForRejectRecoveryTest is StructHelper { +contract EmailAccountRecoveryForRejectRecoveryTest_rejectRecovery is StructHelper { constructor() {} function setUp() public override { @@ -140,6 +140,8 @@ contract EmailAccountRecoveryForRejectRecoveryTest is StructHelper { } function testRejectRecovery() public { + skipIfZkSync(); + vm.warp(block.timestamp + 3 days); handleRecovery(); @@ -181,6 +183,8 @@ contract EmailAccountRecoveryForRejectRecoveryTest is StructHelper { } function testExpectRevertRejectRecoveryRecoveryNotInProgress() public { + skipIfZkSync(); + handleAcceptance(); assertEq(recoveryController.isRecovering(address(simpleWallet)), false); @@ -203,6 +207,8 @@ contract EmailAccountRecoveryForRejectRecoveryTest is StructHelper { } function testExpectRevertRejectRecovery() public { + skipIfZkSync(); + vm.warp(block.timestamp + 1 days); handleRecovery(); @@ -230,150 +236,9 @@ contract EmailAccountRecoveryForRejectRecoveryTest is StructHelper { vm.stopPrank(); } - function testCompleteRecovery() public { - handleRecovery(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), true); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - block.timestamp + - recoveryController.timelockPeriodOfAccount( - address(simpleWallet) - ) - ); - assertEq(simpleWallet.owner(), deployer); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - newSigner - ); - - vm.startPrank(someRelayer); - vm.warp(4 days); - bytes memory recoveryCalldata; - recoveryController.completeRecovery( - address(simpleWallet), - recoveryCalldata - ); - vm.stopPrank(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), false); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - 0 - ); - assertEq(simpleWallet.owner(), newSigner); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - address(0x0) - ); - } - - function testExpectRevertCompleteRecoveryRecoveryNotInProgress() public { - handleAcceptance(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), false); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - 0 - ); - assertEq(simpleWallet.owner(), deployer); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - address(0x0) - ); - - vm.startPrank(someRelayer); - vm.warp(4 days); - vm.expectRevert(bytes("recovery not in progress")); - bytes memory recoveryCalldata; - recoveryController.completeRecovery( - address(simpleWallet), - recoveryCalldata - ); - - vm.stopPrank(); - } - - function testExpectRevertCompleteRecovery() public { - vm.warp(block.timestamp + 3 days); - - handleRecovery(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), true); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - block.timestamp + - recoveryController.timelockPeriodOfAccount( - address(simpleWallet) - ) - ); - assertEq(simpleWallet.owner(), deployer); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - newSigner - ); - - vm.warp(0); - - vm.startPrank(someRelayer); - vm.expectRevert(bytes("timelock not expired")); - bytes memory recoveryCalldata; - recoveryController.completeRecovery( - address(simpleWallet), - recoveryCalldata - ); - - vm.stopPrank(); - } - - function testExpectRevertHandleRecoveryInvalidNewSigner() public { - handleAcceptance(); - - assertEq(recoveryController.isRecovering(address(simpleWallet)), false); - assertEq( - recoveryController.currentTimelockOfAccount(address(simpleWallet)), - 0 - ); - assertEq(simpleWallet.owner(), deployer); - assertEq( - recoveryController.newSignerCandidateOfAccount( - address(simpleWallet) - ), - address(0x0) - ); - uint templateIdx = 0; - - EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); - uint templateId = recoveryController.computeRecoveryTemplateId( - templateIdx - ); - emailAuthMsg.templateId = templateId; - bytes[] memory subjectParamsForRecovery = new bytes[](2); - subjectParamsForRecovery[0] = abi.encode(simpleWallet); - subjectParamsForRecovery[1] = abi.encode(address(0x0)); - emailAuthMsg.subjectParams = subjectParamsForRecovery; - - vm.mockCall( - address(recoveryController.emailAuthImplementationAddr()), - abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), - abi.encode(0x0) - ); - - vm.startPrank(someRelayer); - vm.expectRevert(bytes("invalid new signer")); - recoveryController.handleRecovery(emailAuthMsg, templateIdx); - vm.stopPrank(); - } - function testExpectRevertRejectRecoveryOwnableUnauthorizedAccount() public { + skipIfZkSync(); + handleRecovery(); assertEq(recoveryController.isRecovering(address(simpleWallet)), true); diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_requestGuardian.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_requestGuardian.t.sol new file mode 100644 index 0000000..e03bdaf --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_requestGuardian.t.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_requestGuardian is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testRequestGuardian() public { + skipIfZkSync(); + + setUp(); + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryController.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.REQUESTED + ); + } + + // function testRequestGuardianNotOwner() public { + // setUp(); + + // require( + // recoveryController.guardians(guardian) == + // recoveryController.GuardianStatus.NONE + // ); + + // vm.startPrank(receiver); + // recoveryController.requestGuardian(guardian); + // vm.stopPrank(); + + // require( + // recoveryController.guardians(guardian) == + // recoveryController.GuardianStatus.NONE + // ); + // } + + function testExpectRevertRequestGuardianInvalidGuardian() public { + skipIfZkSync(); + + setUp(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + vm.expectRevert(bytes("invalid guardian")); + recoveryController.requestGuardian(address(0x0)); + vm.stopPrank(); + } + + function testExpectRevertRequestGuardianGuardianStatusMustBeNone() public { + skipIfZkSync(); + + setUp(); + + require( + recoveryController.guardians(guardian) == + RecoveryController.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryController.requestGuardian(guardian); + vm.expectRevert(bytes("guardian status must be NONE")); + recoveryController.requestGuardian(guardian); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_transfer.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_transfer.t.sol new file mode 100644 index 0000000..460700a --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_transfer.t.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_transfer is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testTransfer() public { + skipIfZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(receiver.balance, 0 ether); + + vm.startPrank(deployer); + simpleWallet.transfer(receiver, 1 ether); + vm.stopPrank(); + + assertEq(address(simpleWallet).balance, 0 ether); + assertEq(receiver.balance, 1 ether); + } + + function testExpectRevertTransferOnlyOwner() public { + skipIfZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(receiver.balance, 0 ether); + + vm.startPrank(receiver); + vm.expectRevert( + abi.encodeWithSelector( + OwnableUpgradeable.OwnableUnauthorizedAccount.selector, + receiver + ) + ); + simpleWallet.transfer(receiver, 1 ether); + vm.stopPrank(); + } + + function testExpectRevertTransferOnlyOwnerInsufficientBalance() public { + skipIfZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(receiver.balance, 0 ether); + + vm.startPrank(deployer); + assertEq(receiver.balance, 0 ether); + vm.expectRevert(bytes("insufficient balance")); + simpleWallet.transfer(receiver, 2 ether); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_withdraw.t.sol b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_withdraw.t.sol new file mode 100644 index 0000000..4eefa10 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecovery/EmailAccountRecovery_withdraw.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryTest_withdraw is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testWithdraw() public { + skipIfZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(deployer.balance, 0 ether); + + vm.startPrank(deployer); + simpleWallet.withdraw(1 ether); + vm.stopPrank(); + + assertEq(address(simpleWallet).balance, 0 ether); + assertEq(deployer.balance, 1 ether); + } + + function testExpectRevertWithdrawOnlyOwner() public { + skipIfZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(deployer.balance, 0 ether); + + vm.startPrank(receiver); + vm.expectRevert( + abi.encodeWithSelector( + OwnableUpgradeable.OwnableUnauthorizedAccount.selector, + address(receiver) + ) + ); + simpleWallet.withdraw(1 ether); + vm.stopPrank(); + } + + function testExpectRevertWithdrawInsufficientBalance() public { + skipIfZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(deployer.balance, 0 ether); + + vm.startPrank(deployer); + vm.expectRevert(bytes("insufficient balance")); + simpleWallet.withdraw(10 ether); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_completeRecovery.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_completeRecovery.t.sol new file mode 100644 index 0000000..6713f75 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_completeRecovery.t.sol @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_completeRecovery is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function requestGuardian() public { + setUp(); + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryControllerZKSync.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + } + + function handleAcceptance() public { + requestGuardian(); + + console.log("guardian", guardian); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + // acceptGuardian is internal, we call handleAcceptance, which calls acceptGuardian internally. + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.ACCEPTED + ); + } + + function handleRecovery() public { + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + } + + function testCompleteRecovery() public { + skipIfNotZkSync(); + + handleRecovery(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.startPrank(someRelayer); + vm.warp(4 days); + recoveryControllerZKSync.completeRecovery( + address(simpleWallet), + new bytes(0) + ); + vm.stopPrank(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), newSigner); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + } + + function testExpectRevertCompleteRecoveryRecoveryNotInProgress() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + vm.startPrank(someRelayer); + vm.warp(4 days); + vm.expectRevert(bytes("recovery not in progress")); + bytes memory recoveryCalldata; + recoveryControllerZKSync.completeRecovery( + address(simpleWallet), + recoveryCalldata + ); + + vm.stopPrank(); + } + + function testExpectRevertCompleteRecovery() public { + vm.warp(block.timestamp + 3 days); + + handleRecovery(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.warp(0); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("timelock not expired")); + bytes memory recoveryCalldata; + recoveryControllerZKSync.completeRecovery( + address(simpleWallet), + recoveryCalldata + ); + + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_handleAcceptance.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_handleAcceptance.t.sol new file mode 100644 index 0000000..9db7889 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_handleAcceptance.t.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_handleAcceptance is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function requestGuardian() public { + skipIfNotZkSync(); + + setUp(); + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryControllerZKSync.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + } + + function testHandleAcceptance() public { + skipIfNotZkSync(); + + requestGuardian(); + + console.log("guardian", guardian); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + // acceptGuardian is internal, we call handleAcceptance, which calls acceptGuardian internally. + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.ACCEPTED + ); + } + + // Can not test recovery in progress using handleAcceptance + // Can not test invalid guardian using handleAcceptance + + function testExpectRevertHandleAcceptanceGuardianStatusMustBeRequested() + public + { + skipIfNotZkSync(); + + requestGuardian(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + emailAuthMsg.proof.accountSalt = 0x0; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("guardian status must be REQUESTED")); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleAcceptanceInvalidTemplateIndex() public { + skipIfNotZkSync(); + + requestGuardian(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 1; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid template index")); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleAcceptanceInvalidSubjectParams() public { + skipIfNotZkSync(); + + requestGuardian(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](2); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + subjectParamsForAcceptance[1] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid subject params")); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleAcceptanceInvalidWalletAddressInEmail() + public + { + skipIfNotZkSync(); + + requestGuardian(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(0x0)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid account in email")); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_handleRecovery.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_handleRecovery.t.sol new file mode 100644 index 0000000..3a55ec9 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_handleRecovery.t.sol @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_handleRecovery is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function requestGuardian() public { + setUp(); + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryControllerZKSync.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + } + + function handleAcceptance() public { + skipIfNotZkSync(); + + requestGuardian(); + + console.log("guardian", guardian); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + // acceptGuardian is internal, we call handleAcceptance, which calls acceptGuardian internally. + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.ACCEPTED + ); + } + + function testHandleRecovery() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + } + + function testExpectRevertHandleRecoveryGuardianIsNotDeployed() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + emailAuthMsg.proof.accountSalt = 0x0; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("guardian is not deployed")); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleRecoveryInvalidTemplateId() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid template id")); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + // Can not test recovery in progress using handleRecovery + // Can not test invalid guardian using handleRecovery + + function testExpectRevertHandleRecoveryGuardianStatusMustBeAccepted() + public + { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + emailAuthMsg.proof.accountSalt = 0x0; + + // vm.mockCall( + // address(simpleWallet.emailAuthImplementationAddr()), + // abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + // abi.encode(0x0) + // ); + + // // Deploy mock guardian, that status is NONE + // address mockCallAddress; + // if(block.chainid == 300) { + // mockCallAddress = address(0x889170C6bEe9053626f8460A9875d22Cf6DE0782); + // } else { + // mockCallAddress = address(0x2Cfb66029975B1c8881adaa3b79c5Caa4FEB84B5); + // } + // vm.mockCall( + // mockCallAddress, + // abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + // abi.encode(0x0) + // ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("guardian is not deployed")); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleRecoveryInvalidTemplateIndex() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + uint templateIdx = 1; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid template index")); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + function testExpectRevertHandleRecoveryInvalidSubjectParams() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](3); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + subjectParamsForRecovery[1] = abi.encode(address(0x0)); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid subject params")); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + } + + // function testExpectRevertHandleRecoveryInvalidGuardianInEmail() public { + // handleAcceptance(); + + // assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + // assertEq( + // recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + // 0 + // ); + // assertEq(simpleWallet.owner(), deployer); + // assertEq( + // recoveryControllerZKSync.newSignerCandidateOfAccount(address(simpleWallet)), + // address(0x0) + // ); + // uint templateIdx = 0; + + // EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + // uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId(templateIdx); + // emailAuthMsg.templateId = templateId; + // bytes[] memory subjectParamsForRecovery = new bytes[](2); + // subjectParamsForRecovery[0] = abi.encode(address(0x0)); + // subjectParamsForRecovery[1] = abi.encode(newSigner); + // emailAuthMsg.subjectParams = subjectParamsForRecovery; + + // vm.mockCall( + // address(recoveryControllerZKSync.emailAuthImplementationAddr()), + // abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + // abi.encode(0x0) + // ); + + // vm.startPrank(someRelayer); + // vm.expectRevert(bytes("invalid guardian in email")); + // recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + // vm.stopPrank(); + // } + + function testExpectRevertHandleRecoveryInvalidNewSigner() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(address(0x0)); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + vm.expectRevert(bytes("invalid new signer")); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_rejectRecovery.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_rejectRecovery.t.sol new file mode 100644 index 0000000..8293060 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_rejectRecovery.t.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_rejectRecovery is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + /** + * Set up functions + */ + function requestGuardian() public { + setUp(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryControllerZKSync.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + } + + function handleAcceptance() public { + requestGuardian(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + + console.log("guardian", guardian); + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + emailAuthMsg.subjectParams = subjectParamsForAcceptance; + address recoveredAccount = recoveryControllerZKSync + .extractRecoveredAccountFromAcceptanceSubject( + emailAuthMsg.subjectParams, + templateIdx + ); + address computedGuardian = recoveryControllerZKSync.computeEmailAuthAddress( + recoveredAccount, + emailAuthMsg.proof.accountSalt + ); + console.log("computed guardian", computedGuardian); + uint templateId = recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + // acceptGuardian is internal, we call handleAcceptance, which calls acceptGuardian internally. + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.ACCEPTED + ); + } + + function handleRecovery() public { + handleAcceptance(); + + assertEq(simpleWallet.owner(), deployer); + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + uint templateIdx = 0; + + EmailAuthMsg memory emailAuthMsg = buildEmailAuthMsg(); + uint templateId = recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ); + emailAuthMsg.templateId = templateId; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(simpleWallet); + subjectParamsForRecovery[1] = abi.encode(newSigner); + emailAuthMsg.subjectParams = subjectParamsForRecovery; + + vm.mockCall( + address(recoveryControllerZKSync.emailAuthImplementationAddr()), + abi.encodeWithSelector(EmailAuth.authEmail.selector, emailAuthMsg), + abi.encode(0x0) + ); + + vm.startPrank(someRelayer); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + vm.stopPrank(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + } + + function testRejectRecovery() public { + skipIfNotZkSync(); + + vm.warp(block.timestamp + 3 days); + + handleRecovery(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.warp(0); + + vm.startPrank(address(simpleWallet)); + recoveryControllerZKSync.rejectRecovery(); + vm.stopPrank(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + } + + function testExpectRevertRejectRecoveryRecoveryNotInProgress() public { + skipIfNotZkSync(); + + handleAcceptance(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), false); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + 0 + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + address(0x0) + ); + + vm.startPrank(deployer); + vm.expectRevert(bytes("recovery not in progress")); + recoveryControllerZKSync.rejectRecovery(); + vm.stopPrank(); + } + + function testExpectRevertRejectRecovery() public { + skipIfNotZkSync(); + + vm.warp(block.timestamp + 1 days); + + handleRecovery(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.startPrank(address(simpleWallet)); + vm.warp(block.timestamp + 4 days); + vm.expectRevert(bytes("timelock expired")); + recoveryControllerZKSync.rejectRecovery(); + vm.stopPrank(); + } + + function testExpectRevertRejectRecoveryOwnableUnauthorizedAccount() public { + skipIfNotZkSync(); + + handleRecovery(); + + assertEq(recoveryControllerZKSync.isRecovering(address(simpleWallet)), true); + assertEq( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)), + block.timestamp + + recoveryControllerZKSync.timelockPeriodOfAccount( + address(simpleWallet) + ) + ); + assertEq(simpleWallet.owner(), deployer); + assertEq( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ), + newSigner + ); + + vm.startPrank(deployer); + vm.expectRevert("recovery not in progress"); + recoveryControllerZKSync.rejectRecovery(); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_requestGuardian.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_requestGuardian.t.sol new file mode 100644 index 0000000..0341865 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_requestGuardian.t.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_requestGuardian is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testRequestGuardian() public { + skipIfNotZkSync(); + + setUp(); + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryControllerZKSync.requestGuardian(guardian); + vm.stopPrank(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED + ); + } + + // function testRequestGuardianNotOwner() public { + // setUp(); + + // require( + // recoveryControllerZKSync.guardians(guardian) == + // recoveryControllerZKSync.GuardianStatus.NONE + // ); + + // vm.startPrank(receiver); + // recoveryControllerZKSync.requestGuardian(guardian); + // vm.stopPrank(); + + // require( + // recoveryControllerZKSync.guardians(guardian) == + // recoveryControllerZKSync.GuardianStatus.NONE + // ); + // } + + function testExpectRevertRequestGuardianInvalidGuardian() public { + skipIfNotZkSync(); + + setUp(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + vm.expectRevert(bytes("invalid guardian")); + recoveryControllerZKSync.requestGuardian(address(0x0)); + vm.stopPrank(); + } + + function testExpectRevertRequestGuardianGuardianStatusMustBeNone() public { + skipIfNotZkSync(); + + setUp(); + + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.NONE + ); + + vm.startPrank(deployer); + recoveryControllerZKSync.requestGuardian(guardian); + vm.expectRevert(bytes("guardian status must be NONE")); + recoveryControllerZKSync.requestGuardian(guardian); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_transfer.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_transfer.t.sol new file mode 100644 index 0000000..b784847 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_transfer.t.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_transfer is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testTransfer() public { + skipIfNotZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(receiver.balance, 0 ether); + + vm.startPrank(deployer); + simpleWallet.transfer(receiver, 1 ether); + vm.stopPrank(); + + assertEq(address(simpleWallet).balance, 0 ether); + assertEq(receiver.balance, 1 ether); + } + + function testExpectRevertTransferOnlyOwner() public { + skipIfNotZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(receiver.balance, 0 ether); + + vm.startPrank(receiver); + vm.expectRevert( + abi.encodeWithSelector( + OwnableUpgradeable.OwnableUnauthorizedAccount.selector, + receiver + ) + ); + simpleWallet.transfer(receiver, 1 ether); + vm.stopPrank(); + } + + function testExpectRevertTransferOnlyOwnerInsufficientBalance() public { + skipIfNotZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(receiver.balance, 0 ether); + + vm.startPrank(deployer); + assertEq(receiver.balance, 0 ether); + vm.expectRevert(bytes("insufficient balance")); + simpleWallet.transfer(receiver, 2 ether); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_withdraw.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_withdraw.t.sol new file mode 100644 index 0000000..07d347d --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZKSync_withdraw.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_withdraw is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testWithdraw() public { + skipIfNotZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(deployer.balance, 0 ether); + + vm.startPrank(deployer); + simpleWallet.withdraw(1 ether); + vm.stopPrank(); + + assertEq(address(simpleWallet).balance, 0 ether); + assertEq(deployer.balance, 1 ether); + } + + function testExpectRevertWithdrawOnlyOwner() public { + skipIfNotZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(deployer.balance, 0 ether); + + vm.startPrank(receiver); + vm.expectRevert( + abi.encodeWithSelector( + OwnableUpgradeable.OwnableUnauthorizedAccount.selector, + address(receiver) + ) + ); + simpleWallet.withdraw(1 ether); + vm.stopPrank(); + } + + function testExpectRevertWithdrawInsufficientBalance() public { + skipIfNotZkSync(); + + setUp(); + + assertEq(address(simpleWallet).balance, 1 ether); + assertEq(deployer.balance, 0 ether); + + vm.startPrank(deployer); + vm.expectRevert(bytes("insufficient balance")); + simpleWallet.withdraw(10 ether); + vm.stopPrank(); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZkSync_acceptanceSubjectTemplates.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZkSync_acceptanceSubjectTemplates.t.sol new file mode 100644 index 0000000..9290a69 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZkSync_acceptanceSubjectTemplates.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryControllerZKSync} from "../helpers/RecoveryControllerZKSync.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_acceptanceSubjectTemplates is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testAcceptanceSubjectTemplates() public { + skipIfNotZkSync(); + + setUp(); + string[][] memory res = recoveryController.acceptanceSubjectTemplates(); + assertEq(res[0][0], "Accept"); + assertEq(res[0][1], "guardian"); + assertEq(res[0][2], "request"); + assertEq(res[0][3], "for"); + assertEq(res[0][4], "{ethAddr}"); + } +} diff --git a/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZkSync_recoverySubjectTemplates.t.sol b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZkSync_recoverySubjectTemplates.t.sol new file mode 100644 index 0000000..051c655 --- /dev/null +++ b/packages/contracts/test/EmailAccountRecoveryZkSync/EmailAccountRecoveryZkSync_recoverySubjectTemplates.t.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {RecoveryController} from "../helpers/RecoveryController.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; +import {SimpleWallet} from "../helpers/SimpleWallet.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract EmailAccountRecoveryZKSyncTest_recoverySubjectTemplates is StructHelper { + constructor() {} + + function setUp() public override { + super.setUp(); + } + + function testRecoverySubjectTemplates() public { + skipIfNotZkSync(); + + setUp(); + string[][] memory res = recoveryController.recoverySubjectTemplates(); + assertEq(res[0][0], "Set"); + assertEq(res[0][1], "the"); + assertEq(res[0][2], "new"); + assertEq(res[0][3], "signer"); + assertEq(res[0][4], "of"); + assertEq(res[0][5], "{ethAddr}"); + assertEq(res[0][6], "to"); + assertEq(res[0][7], "{ethAddr}"); + } +} diff --git a/packages/contracts/test/Integration.t.sol b/packages/contracts/test/Integration.t.sol index 1f8d7c7..b17fc0c 100644 --- a/packages/contracts/test/Integration.t.sol +++ b/packages/contracts/test/Integration.t.sol @@ -13,6 +13,7 @@ import "../src/utils/ECDSAOwnedDKIMRegistry.sol"; import "./helpers/SimpleWallet.sol"; import "./helpers/RecoveryController.sol"; import "forge-std/console.sol"; +import "../src/utils/ZKSyncCreate2Factory.sol"; contract IntegrationTest is Test { using Strings for *; @@ -37,16 +38,8 @@ contract IntegrationTest is Test { 0x0ea9c777dc7110e5a9e89b13f0cfc540e3845ba120b2b6dc24024d61488d4788; uint256 startTimestamp = 1723443691; // September 11, 2024, 17:34:51 UTC - bool isZksync = false; - function setUp() public { - if (block.chainid == 300) { - vm.createSelectFork("https://sepolia.era.zksync.dev"); - isZksync = true; - } else { - vm.createSelectFork("https://mainnet.base.org"); - isZksync = false; - } + vm.createSelectFork("https://mainnet.base.org"); vm.warp(startTimestamp); @@ -95,6 +88,11 @@ contract IntegrationTest is Test { console.log("emailAuthImpl"); console.logAddress(address(emailAuthImpl)); + // Create zkSync Factory + ZKSyncCreate2Factory factoryImpl = new ZKSyncCreate2Factory(); + console.log("factoryImpl"); + console.logAddress(address(factoryImpl)); + // Create RecoveryController as EmailAccountRecovery implementation RecoveryController recoveryControllerImpl = new RecoveryController(); ERC1967Proxy recoveryControllerProxy = new ERC1967Proxy( @@ -141,17 +139,10 @@ contract IntegrationTest is Test { vm.deal(address(relayer), 1 ether); console.log("SimpleWallet is at ", address(simpleWallet)); - if (isZksync) { - assertEq( - address(simpleWallet), - 0x05A78D3dB903a58B5FA373E07e5044B95B12aec4 - ); - } else { - assertEq( - address(simpleWallet), - 0x0C06688e61C06466E2a5C6fE4E15c359260a33f3 - ); - } + assertEq( + address(simpleWallet), + 0xeb8E21A363Dce22ff6057dEEF7c074062037F571 + ); address simpleWalletOwner = simpleWallet.owner(); // Verify the email proof for acceptance @@ -184,13 +175,8 @@ contract IntegrationTest is Test { emailProof.domainName = "gmail.com"; emailProof.publicKeyHash = bytes32(vm.parseUint(pubSignals[9])); emailProof.timestamp = vm.parseUint(pubSignals[11]); - if (isZksync) { - emailProof - .maskedSubject = "Accept guardian request for 0x05A78D3dB903a58B5FA373E07e5044B95B12aec4"; - } else { - emailProof - .maskedSubject = "Accept guardian request for 0x0C06688e61C06466E2a5C6fE4E15c359260a33f3"; - } + emailProof + .maskedSubject = "Accept guardian request for 0xeb8E21A363Dce22ff6057dEEF7c074062037F571"; emailProof.emailNullifier = bytes32(vm.parseUint(pubSignals[10])); emailProof.accountSalt = bytes32(vm.parseUint(pubSignals[32])); accountSalt = emailProof.accountSalt; @@ -270,13 +256,8 @@ contract IntegrationTest is Test { emailProof.timestamp = vm.parseUint(pubSignals[11]); // 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 is account 9 - if (isZksync) { - emailProof - .maskedSubject = "Set the new signer of 0x05A78D3dB903a58B5FA373E07e5044B95B12aec4 to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"; - } else { - emailProof - .maskedSubject = "Set the new signer of 0x0C06688e61C06466E2a5C6fE4E15c359260a33f3 to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"; - } + emailProof + .maskedSubject = "Set the new signer of 0xeb8E21A363Dce22ff6057dEEF7c074062037F571 to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"; emailProof.emailNullifier = bytes32(vm.parseUint(pubSignals[10])); emailProof.accountSalt = bytes32(vm.parseUint(pubSignals[32])); diff --git a/packages/contracts/test/IntegrationZKSync.t.sol b/packages/contracts/test/IntegrationZKSync.t.sol new file mode 100644 index 0000000..f30cc72 --- /dev/null +++ b/packages/contracts/test/IntegrationZKSync.t.sol @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@zk-email/contracts/DKIMRegistry.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "../src/EmailAuth.sol"; +import "../src/utils/Verifier.sol"; +import "../src/utils/ECDSAOwnedDKIMRegistry.sol"; +import "./helpers/SimpleWallet.sol"; +import "./helpers/RecoveryControllerZKSync.sol"; +import "forge-std/console.sol"; +import "../src/utils/ZKSyncCreate2Factory.sol"; + +contract IntegrationZKSyncTest is Test { + using Strings for *; + using console for *; + + EmailAuth emailAuth; + Verifier verifier; + ECDSAOwnedDKIMRegistry dkim; + + RecoveryControllerZKSync recoveryControllerZKSync; + SimpleWallet simpleWallet; + + address deployer = vm.addr(1); + address receiver = vm.addr(2); + address guardian = vm.addr(3); + address relayer = deployer; + + bytes32 accountSalt; + string selector = "12345"; + string domainName = "gmail.com"; + bytes32 publicKeyHash = + 0x0ea9c777dc7110e5a9e89b13f0cfc540e3845ba120b2b6dc24024d61488d4788; + uint256 startTimestamp = 1723443691; // September 11, 2024, 17:34:51 UTC + + function setUp() public { + vm.createSelectFork("http://127.0.0.1:8011"); + + vm.warp(startTimestamp); + + vm.startPrank(deployer); + address signer = deployer; + + // Create DKIM registry + { + ECDSAOwnedDKIMRegistry ecdsaDkimImpl = new ECDSAOwnedDKIMRegistry(); + ERC1967Proxy ecdsaDkimProxy = new ERC1967Proxy( + address(ecdsaDkimImpl), + abi.encodeCall(ecdsaDkimImpl.initialize, (msg.sender, signer)) + ); + dkim = ECDSAOwnedDKIMRegistry(address(ecdsaDkimProxy)); + } + string memory signedMsg = dkim.computeSignedMsg( + dkim.SET_PREFIX(), + selector, + domainName, + publicKeyHash + ); + bytes32 digest = MessageHashUtils.toEthSignedMessageHash( + bytes(signedMsg) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, digest); + bytes memory signature = abi.encodePacked(r, s, v); + dkim.setDKIMPublicKeyHash( + selector, + domainName, + publicKeyHash, + signature + ); + + // Create Verifier + { + Verifier verifierImpl = new Verifier(); + ERC1967Proxy verifierProxy = new ERC1967Proxy( + address(verifierImpl), + abi.encodeCall(verifierImpl.initialize, (msg.sender)) + ); + verifier = Verifier(address(verifierProxy)); + } + + // Create EmailAuth + EmailAuth emailAuthImpl = new EmailAuth(); + console.log("emailAuthImpl"); + console.logAddress(address(emailAuthImpl)); + + // Create zkSync Factory + ZKSyncCreate2Factory factoryImpl = new ZKSyncCreate2Factory(); + console.log("factoryImpl"); + console.logAddress(address(factoryImpl)); + + // Create RecoveryController as EmailAccountRecovery implementation + RecoveryControllerZKSync recoveryControllerZKSyncImpl = new RecoveryControllerZKSync(); + ERC1967Proxy recoveryControllerZKSyncProxy = new ERC1967Proxy( + address(recoveryControllerZKSyncImpl), + abi.encodeCall( + recoveryControllerZKSyncImpl.initialize, + ( + signer, + address(verifier), + address(dkim), + address(emailAuthImpl), + address(factoryImpl) + ) + ) + ); + recoveryControllerZKSync = RecoveryControllerZKSync( + payable(address(recoveryControllerZKSyncProxy)) + ); + + // Create SimpleWallet as EmailAccountRecovery implementation + SimpleWallet simpleWalletImpl = new SimpleWallet(); + ERC1967Proxy simpleWalletProxy = new ERC1967Proxy( + address(simpleWalletImpl), + abi.encodeCall( + simpleWalletImpl.initialize, + (signer, address(recoveryControllerZKSync)) + ) + ); + simpleWallet = SimpleWallet(payable(address(simpleWalletProxy))); + // console.log( + // "emailAuthImplementation", + // simpleWallet.emailAuthImplementation() + // ); + + vm.stopPrank(); + } + + function testIntegration_Account_Recovery_ZkSync() public { + console.log("testIntegration_Account_Recovery_ZKSync"); + + bytes32 accountCode = 0x1162ebff40918afe5305e68396f0283eb675901d0387f97d21928d423aaa0b54; + uint templateIdx = 0; + + vm.startPrank(relayer); + vm.deal(address(relayer), 1 ether); + + console.log("SimpleWallet is at ", address(simpleWallet)); + assertEq( + address(simpleWallet), + 0x7c5E4b26643682AF77A196781A851c9Fe769472d + ); + address simpleWalletOwner = simpleWallet.owner(); + + // Verify the email proof for acceptance + string[] memory inputGenerationInput = new string[](3); + inputGenerationInput[0] = string.concat( + vm.projectRoot(), + "/test/bin/accept.sh" + ); + inputGenerationInput[1] = string.concat( + vm.projectRoot(), + "/test/emails/", + block.chainid.toString(), + "/accept.eml" + ); + inputGenerationInput[2] = uint256(accountCode).toHexString(32); + vm.ffi(inputGenerationInput); + + string memory publicInputFile = vm.readFile( + string.concat( + vm.projectRoot(), + "/test/build_integration/email_auth_public.json" + ) + ); + string[] memory pubSignals = abi.decode( + vm.parseJson(publicInputFile), + (string[]) + ); + + EmailProof memory emailProof; + emailProof.domainName = "gmail.com"; + emailProof.publicKeyHash = bytes32(vm.parseUint(pubSignals[9])); + emailProof.timestamp = vm.parseUint(pubSignals[11]); + emailProof + .maskedSubject = "Accept guardian request for 0x7c5E4b26643682AF77A196781A851c9Fe769472d"; + emailProof.emailNullifier = bytes32(vm.parseUint(pubSignals[10])); + emailProof.accountSalt = bytes32(vm.parseUint(pubSignals[32])); + accountSalt = emailProof.accountSalt; + emailProof.isCodeExist = vm.parseUint(pubSignals[33]) == 1; + emailProof.proof = proofToBytes( + string.concat( + vm.projectRoot(), + "/test/build_integration/email_auth_proof.json" + ) + ); + + console.log("dkim public key hash: "); + console.logBytes32(bytes32(vm.parseUint(pubSignals[9]))); + console.log("email nullifier: "); + console.logBytes32(bytes32(vm.parseUint(pubSignals[10]))); + console.log("timestamp: ", vm.parseUint(pubSignals[11])); + console.log("account salt: "); + console.logBytes32(bytes32(vm.parseUint(pubSignals[32]))); + console.log("is code exist: ", vm.parseUint(pubSignals[33])); + + // Call Request guardian -> GuardianStatus.REQUESTED + guardian = recoveryControllerZKSync.computeEmailAuthAddress( + address(simpleWallet), + accountSalt + ); + recoveryControllerZKSync.requestGuardian(guardian); + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.REQUESTED, + "GuardianStatus should be REQUESTED" + ); + + // Call handleAcceptance -> GuardianStatus.ACCEPTED + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(address(simpleWallet)); + EmailAuthMsg memory emailAuthMsg = EmailAuthMsg({ + templateId: recoveryControllerZKSync.computeAcceptanceTemplateId( + templateIdx + ), + subjectParams: subjectParamsForAcceptance, + skipedSubjectPrefix: 0, + proof: emailProof + }); + recoveryControllerZKSync.handleAcceptance(emailAuthMsg, templateIdx); + require( + recoveryControllerZKSync.guardians(guardian) == + RecoveryControllerZKSync.GuardianStatus.ACCEPTED, + "GuardianStatus should be ACCEPTED" + ); + + // Verify the email proof for recovery + inputGenerationInput = new string[](3); + inputGenerationInput[0] = string.concat( + vm.projectRoot(), + "/test/bin/recovery.sh" + ); + inputGenerationInput[1] = string.concat( + vm.projectRoot(), + "/test/emails/", + block.chainid.toString(), + "/recovery.eml" + ); + inputGenerationInput[2] = uint256(accountCode).toHexString(32); + vm.ffi(inputGenerationInput); + + publicInputFile = vm.readFile( + string.concat( + vm.projectRoot(), + "/test/build_integration/email_auth_public.json" + ) + ); + pubSignals = abi.decode(vm.parseJson(publicInputFile), (string[])); + + // EmailProof memory emailProof; + emailProof.domainName = "gmail.com"; + emailProof.publicKeyHash = bytes32(vm.parseUint(pubSignals[9])); + emailProof.timestamp = vm.parseUint(pubSignals[11]); + + // 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 is account 9 + emailProof + .maskedSubject = "Set the new signer of 0x7c5E4b26643682AF77A196781A851c9Fe769472d to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720"; + + emailProof.emailNullifier = bytes32(vm.parseUint(pubSignals[10])); + emailProof.accountSalt = bytes32(vm.parseUint(pubSignals[32])); + require( + emailProof.accountSalt == accountSalt, + "accountSalt should be the same" + ); + emailProof.isCodeExist = vm.parseUint(pubSignals[33]) == 1; + emailProof.proof = proofToBytes( + string.concat( + vm.projectRoot(), + "/test/build_integration/email_auth_proof.json" + ) + ); + + console.log("dkim public key hash: "); + console.logBytes32(bytes32(vm.parseUint(pubSignals[9]))); + console.log("email nullifier: "); + console.logBytes32(bytes32(vm.parseUint(pubSignals[10]))); + console.log("timestamp: ", vm.parseUint(pubSignals[11])); + console.log("account salt: "); + console.logBytes32(bytes32(vm.parseUint(pubSignals[32]))); + console.log("is code exist: ", vm.parseUint(pubSignals[33])); + + // Call handleRecovery -> isRecovering = true; + bytes[] memory subjectParamsForRecovery = new bytes[](2); + subjectParamsForRecovery[0] = abi.encode(address(simpleWallet)); + subjectParamsForRecovery[1] = abi.encode( + address(0xa0Ee7A142d267C1f36714E4a8F75612F20a79720) + ); + emailAuthMsg = EmailAuthMsg({ + templateId: recoveryControllerZKSync.computeRecoveryTemplateId( + templateIdx + ), + subjectParams: subjectParamsForRecovery, + skipedSubjectPrefix: 0, + proof: emailProof + }); + recoveryControllerZKSync.handleRecovery(emailAuthMsg, templateIdx); + require( + recoveryControllerZKSync.isRecovering(address(simpleWallet)), + "isRecovering should be set" + ); + require( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ) == 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720, + "newSignerCandidate should be set" + ); + require( + recoveryControllerZKSync.currentTimelockOfAccount(address(simpleWallet)) > + 0, + "timelock should be set" + ); + require( + simpleWallet.owner() == simpleWalletOwner, + "simpleWallet owner should be old one" + ); + + // Call completeRecovery + // Warp at 3 days + 10 seconds later + vm.warp(startTimestamp + (3 * 24 * 60 * 60) + 10); + recoveryControllerZKSync.completeRecovery( + address(simpleWallet), + new bytes(0) + ); + console.log("simpleWallet owner: ", simpleWallet.owner()); + require( + !recoveryControllerZKSync.isRecovering(address(simpleWallet)), + "isRecovering should be reset" + ); + require( + recoveryControllerZKSync.newSignerCandidateOfAccount( + address(simpleWallet) + ) == address(0), + "newSignerCandidate should be reset" + ); + require( + recoveryControllerZKSync.currentTimelockOfAccount( + address(simpleWallet) + ) == 0, + "timelock should be reset" + ); + require( + simpleWallet.owner() == 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720, + "simpleWallet owner should be new one" + ); + vm.stopPrank(); + } + + function proofToBytes( + string memory proofPath + ) internal view returns (bytes memory) { + string memory proofFile = vm.readFile(proofPath); + string[] memory pi_a = abi.decode( + vm.parseJson(proofFile, ".pi_a"), + (string[]) + ); + uint256[2] memory pA = [vm.parseUint(pi_a[0]), vm.parseUint(pi_a[1])]; + string[][] memory pi_b = abi.decode( + vm.parseJson(proofFile, ".pi_b"), + (string[][]) + ); + uint256[2][2] memory pB = [ + [vm.parseUint(pi_b[0][1]), vm.parseUint(pi_b[0][0])], + [vm.parseUint(pi_b[1][1]), vm.parseUint(pi_b[1][0])] + ]; + string[] memory pi_c = abi.decode( + vm.parseJson(proofFile, ".pi_c"), + (string[]) + ); + uint256[2] memory pC = [vm.parseUint(pi_c[0]), vm.parseUint(pi_c[1])]; + bytes memory proof = abi.encode(pA, pB, pC); + return proof; + } +} diff --git a/packages/contracts/test/emails/300/accept.eml b/packages/contracts/test/emails/300/accept.eml index f7340ad..7e10ce7 100644 --- a/packages/contracts/test/emails/300/accept.eml +++ b/packages/contracts/test/emails/300/accept.eml @@ -1,90 +1,90 @@ Delivered-To: rrelayerbob@gmail.com -Received: by 2002:a50:45c6:0:b0:264:9270:cc66 with SMTP id c6csp1312972ecu; - Mon, 12 Aug 2024 07:26:02 -0700 (PDT) -X-Received: by 2002:a05:6214:4388:b0:6b0:90b4:1ca9 with SMTP id 6a1803df08f44-6bf4f66c992mr4233056d6.6.1723472762703; - Mon, 12 Aug 2024 07:26:02 -0700 (PDT) -ARC-Seal: i=1; a=rsa-sha256; t=1723472762; cv=none; +Received: by 2002:a05:6400:2670:b0:264:9270:cc66 with SMTP id jy48csp750570ecb; + Wed, 28 Aug 2024 00:53:22 -0700 (PDT) +X-Received: by 2002:a05:6214:5b0a:b0:6bf:8cb9:420 with SMTP id 6a1803df08f44-6c3362e69d4mr13631006d6.28.1724831602260; + Wed, 28 Aug 2024 00:53:22 -0700 (PDT) +ARC-Seal: i=1; a=rsa-sha256; t=1724831602; cv=none; d=google.com; s=arc-20160816; - b=vcvdTjMIx6jVaoyYdgwc/lQQLkGzwzJkejj++NHvdRMrTAUrmBcq4P3uRlETlg4QI/ - YZ5UTmDkqn+nsu0P1zNU9zoe8CiQk6bJQnaKsEqN2o6GAVf28p1K2b5HGxYYjbwPPjns - pVrwGO4oV3m91G7tDoKAcHTJ02K1yz4Ge7uOQd2LF3c8a/6m4PIxCD30+5gg1/qWjAfk - Obt40LmhxX4/yfqcuWf0F3SdXl3krrABX343FDLAewOw07JQwQHIQlBY+Y+2qolt0Grc - xuLA3sOJVUC5uNOaDJzA1G4TisX9DIL/tfjweqF/d9Q5va13JvruTptBqRowwilXqX5P - CLZw== + b=XHYkOsPNkoh6Z/JlWBpmbftiVMlvTrQleXhtrViXaWIdnKIvwTAPXdaICvrHd/Gx6P + 3oVxnHb8Cuhi3cJ+ctMJ40RfT+f2+DRWXG2Csihny9ayIWeJ4mhPEy1Y6ZXkCEr3Gud8 + mVeHCXLthekdgQly8uhWxC6vn3wXtCEvx49iJM0gyfcyAI4Nt1eYDS0hr2gH6XNY4F+Q + mEiHfFbZj6s95egRsp2ZipfGz7yojKoKDTUWupDDPF4YpM9TxHrKyqVhk7mgT5PHaAWm + xkgUr8fWf+2/a+ini06UovznocNQCwGEHbRyPcIQ8SssjV21Xh0P3vwUSdKQKZYJJvTD + wUIg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=to:subject:message-id:date:from:mime-version:dkim-signature; - bh=dAcGAR4My5Lv5E1hBYZpLjIn3d3hkexlA4mfbt+aoNg=; + bh=wfoUswYdacWo98C2NPNyTM5htDNHLSMk6+9pc8+wtuY=; fh=OYPr0JdXtRRlM3Htj/E6+khbD9huvtdqkRTmPoW0wko=; - b=id73VLGPEdCHON1/1BV42acaxe0SQj63bwZMLiqIPI/ull9ayrGcrO77bSO37r2v1t - XiP4Bnb+5+EmBOOno3mOJwwlE5F+MoD1OXJOtYnaRw/wsjz0x4O+OYyfFL9v7SPkfQDV - NhjnFbxOyrXs9rfsfi79misBR4LUmJQCdgiLs+9QWDUq0ckYtOrXJUs6fke3yijTZEkm - +oECBc4vh5apTkfJXlerRzjRD8UQUdPfHW5lO3/JHRJj4MIgumabeb97gcBoh++VcEZO - qTX8uOx0/j+0fl/Qz1WPnKEsHlN/OitzCbDxjZ4KiIYSGKM13Jm0rEwmQl3fkaP2zeZK - be9g==; + b=H8LtD87fAH1qc7wN2hssgsTYQ10genWfiNzNwL+379b1AP/AMubRu3Ekep9uRlkgle + K7AUiX3xkCtX0jmtFAm4EgRqByv9EgAaedHzXbrc/jOBWUq7g0zJM+uk6QffM7ETNNvt + aI263gbooi401SQK+epiPYQ+yF4nXX6cNidm/HyGR0LzxEKWio3NPblQUxihlUKbwN/T + 4MWYgM18G4/ebFrdkbe9707tKHDK6P/BuK9P4mWH6DJwHmGVK40Pyu7xKCtr0/L74/Zu + U04dV6Rho5On1vS0fiEjdhSJdQ5Co1UkyMhn2s8Oalxk9pm95yN6EIcMM+81l2l6dI4+ + GkbA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=AbjyPZA+; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=kOFwnIGQ; spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com Return-Path: Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) - by mx.google.com with SMTPS id 6a1803df08f44-6bd82e7d133sor35657466d6.10.2024.08.12.07.26.02 + by mx.google.com with SMTPS id 6a1803df08f44-6c162e8aa5dsor71159546d6.9.2024.08.28.00.53.22 for (Google Transport Security); - Mon, 12 Aug 2024 07:26:02 -0700 (PDT) + Wed, 28 Aug 2024 00:53:22 -0700 (PDT) Received-SPF: pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; Authentication-Results: mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=AbjyPZA+; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=kOFwnIGQ; spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=gmail.com; s=20230601; t=1723472762; x=1724077562; dara=google.com; + d=gmail.com; s=20230601; t=1724831601; x=1725436401; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; - bh=dAcGAR4My5Lv5E1hBYZpLjIn3d3hkexlA4mfbt+aoNg=; - b=AbjyPZA+Xu7/aQThoDilu0ukN07IBlywWk8F4S+yMXD6q+GOaXB90gTU2subhJts26 - H5SYRTNnvcXMqPOJtb88TzynpxUpej5DLR48ROGtpR7X3q1iQQyRRRZFEUOSS6Utxt7D - S1Jwx2kGOu/OSN3xamxMyuAWoi7ikWQJSjJ9g/b2IWmIly9v2FE+gl8doPPrZCumR3/F - 1WwDcqh6aL0MLgUPy5Uz+FVlU4aK3Md2SFKDRipW9whWoxun6dSjCHMLiZZSL0bV/ZHJ - loxolU3peA+0YhVd/Q7ZISaQ4DTg/qjNtVI6OmK09P475EhMNX39et6189GgbwVndGFq - hgCw== + bh=wfoUswYdacWo98C2NPNyTM5htDNHLSMk6+9pc8+wtuY=; + b=kOFwnIGQ8CiIR0+qd/kNcIr9AyL0k/UMnrM2nWMnTJPHgOVyYzJt1v9dGuP1MqNvrA + kHAigFUd6kPv6vYZna755EG9ciFpvWXvdb0IgigVfKH35jBxKA2kTdGfbgkfHlZA3KSj + fwsytkQrkSVFpp/5ZuOIZ7WWqRbLYjJ75ICYYjmzpCQwjAA6MDpMvSpYHOlxmXxBzE+c + 9VPDfEIhD8HsVMgTB3NSYRPkSX7M8Sl3aFOUWhNW6KU7W5yqgNXq28P2PiyiFGzsh2B3 + dTx6SR/HadkLihJGpkC5C8qNgeCoUf1crLTNjbGESClqzNHIyjJ5Q6wt3MuKlgu80olN + xJmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20230601; t=1723472762; x=1724077562; + d=1e100.net; s=20230601; t=1724831601; x=1725436401; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; - bh=dAcGAR4My5Lv5E1hBYZpLjIn3d3hkexlA4mfbt+aoNg=; - b=Q8hiZxgOauaTndhqxya7bEZJoH/1U8K7nrW/yizA+fUpTGrejdh3/SfmTb/OJNHpZQ - fTsGJqEHEDIhJds1PdK6/qbMD/C1OY88BVScJ/UauzGid9Gis9AbpmwzOARnhV8+HgaL - kWQBl36PHOhbXtaX8UxEQrk7jE83lgXqvDVjdcE618P505L92eT+EzTCM+jWB3ShAK7i - 92e57Qr7M4JhLIj/W0BUoRjEDl7GEpjv4uLeHoIPONI9UOt7rYlKTEZtBJuCCt8I5tuO - zMY2T2QYcOASwc2TCWJ3rijf8WZaoxpSK4cJFtq7Drz8xSFHPfdcIc+onRVF5wQbVEbm - eZGg== -X-Gm-Message-State: AOJu0Yw7LeKO/8TWPHdDWwfvkXjVBzc7O37fDC2MDvtMq/djeJ1X+zXy - t90nn2xwzgEV3VUxU4CsA3Dfz4sJgUtgwkhOseIQ5pWRGWJc2DEOHZaqosy+EKtcd74uRf9Z4O/ - oBDfsRhNOdyvhHyJqjnkmedzxZN/y -X-Google-Smtp-Source: AGHT+IFMoDCfSFZzXjpvIXfeTd4IrAJgBhwtjJEGUhl8i59puhKhpNeQizDSDGG/AOZxbcx06wG5xnYU/yO39Qn8VJ0= -X-Received: by 2002:a05:6214:5f0b:b0:6b7:980b:e0ac with SMTP id - 6a1803df08f44-6bf4f7e2315mr3150516d6.32.1723472761940; Mon, 12 Aug 2024 - 07:26:01 -0700 (PDT) + bh=wfoUswYdacWo98C2NPNyTM5htDNHLSMk6+9pc8+wtuY=; + b=P8nKrBqskVA91k6q3XbmEdSc5BVCGGg2Utz+Ire/PS7MN3jnT1sQ5HxVqnWq/HiiDS + tVz4/wRFtUDmkkCNEzQ4DzLr2674IcS+vQ23hRaovrYEKos9yu2TKyV9xtNI/+zP3NNU + BmtuwCazEpMzK1BmOZ4NDX+Jd3DAha1uodiTxyfmKNxpoVg1QTluPlKtWALVc56Rk2tT + sGZXH6rpDalEF32vsi4hcGj7sA9n7pZIexLtjd1BZSILtJ0c0ImA2yyZIJwotFV/7Yn4 + DvC0VgQj5dtdrSI707fhUYXPZ+z1tEaVtINPNKz7X9Cy7TmTZ8dOdj05DaTA4eCixgFM + mgPQ== +X-Gm-Message-State: AOJu0YwrGQCEe9+PJ6Zepa+H3q3Cq2LyydJZATE3K+17Co3Iom94DUhW + vD7YwjhQbaLqC1niaQEOlycvMfVNNnTluvbVzbuYqqE/Kk5SBwcM7zJBcW5OBmSMV81PM0fPTWK + mLt1/FTNquOh5CWZ8GAsjw2p44F0k +X-Google-Smtp-Source: AGHT+IF9+HGRRMj6SjPxYPjB7WJiyoxKQSEitAal6QSa9IbQzWwfeh3hSAdLj9EMUwnFSJLGcixB1gy28cyAeKaW51A= +X-Received: by 2002:a05:6214:4306:b0:6bf:7474:348f with SMTP id + 6a1803df08f44-6c3362d2fa8mr9836466d6.21.1724831601609; Wed, 28 Aug 2024 + 00:53:21 -0700 (PDT) MIME-Version: 1.0 From: "emailwallet.relayer.2" -Date: Mon, 12 Aug 2024 23:25:51 +0900 -Message-ID: -Subject: Accept guardian request for 0x05A78D3dB903a58B5FA373E07e5044B95B12aec4 +Date: Wed, 28 Aug 2024 16:53:10 +0900 +Message-ID: +Subject: Accept guardian request for 0x7c5E4b26643682AF77A196781A851c9Fe769472d Code 1162ebff40918afe5305e68396f0283eb675901d0387f97d21928d423aaa0b54 To: rrelayerbob@gmail.com -Content-Type: multipart/alternative; boundary="000000000000c1738a061f7d45eb" +Content-Type: multipart/alternative; boundary="000000000000e9559a0620b9a62f" ---000000000000c1738a061f7d45eb +--000000000000e9559a0620b9a62f Content-Type: text/plain; charset="UTF-8" ---000000000000c1738a061f7d45eb +--000000000000e9559a0620b9a62f Content-Type: text/html; charset="UTF-8"

---000000000000c1738a061f7d45eb-- +--000000000000e9559a0620b9a62f-- diff --git a/packages/contracts/test/emails/300/recovery.eml b/packages/contracts/test/emails/300/recovery.eml index 59d1362..5b79541 100644 --- a/packages/contracts/test/emails/300/recovery.eml +++ b/packages/contracts/test/emails/300/recovery.eml @@ -1,90 +1,90 @@ Delivered-To: rrelayerbob@gmail.com -Received: by 2002:a50:45c6:0:b0:264:9270:cc66 with SMTP id c6csp1314270ecu; - Mon, 12 Aug 2024 07:28:30 -0700 (PDT) -X-Received: by 2002:a05:6902:2082:b0:e0e:cb25:c40f with SMTP id 3f1490d57ef6-e113d27bb49mr644234276.37.1723472910560; - Mon, 12 Aug 2024 07:28:30 -0700 (PDT) -ARC-Seal: i=1; a=rsa-sha256; t=1723472910; cv=none; +Received: by 2002:a05:6400:2670:b0:264:9270:cc66 with SMTP id jy48csp750751ecb; + Wed, 28 Aug 2024 00:54:02 -0700 (PDT) +X-Received: by 2002:a05:6102:26c9:b0:492:a9f8:4a71 with SMTP id ada2fe7eead31-49a4ed30da9mr1309811137.8.1724831642136; + Wed, 28 Aug 2024 00:54:02 -0700 (PDT) +ARC-Seal: i=1; a=rsa-sha256; t=1724831642; cv=none; d=google.com; s=arc-20160816; - b=CatZtuL6lj1FU5iyQZyRHoqbpEJgvWil6eq/wWz0NfA0iY8NgqUqTOFdBHXF0phsI+ - a0uuFKHnOVJboPJbKEsQGFL1EIM2XNUP65EdUzHDqH4s/d4bl27bxRScMp1DExVM3SoG - tq+7XKb01CGjK4ydT90FhmIEWyHG7wEFDJaOqSUAjiG6V7wEkj5eXuiFPQqVWpQWrdlf - 0Pno03YAhrqPow5jhbTwG0hXl/F4OXtXU+jaO2QKIZJ2bB8eFFcHVsOHSsw5M7gjIZ13 - s2blLt12Fbzy8sX906g0Gr+zHMB67EwzM80GZoip05s8T5L1K+54e69iujlfjJGOcjVJ - P1nA== + b=cpTbX87GQyaBGaVXbNadJs8tRJ3H6JwDzUCyAR6zbLifi7OvRIHe/CqXAdOjD88MEN + S5Vri34IQ0pQX+/0KPKejM0sKlvuQXSca6+H87JzalG4kkXOChT58Wjm5zrWcY9Xslh5 + s275+Zh5T6gx7IBZZZtXLcOgtRgZgsHhpF2DkzgSiRh1FtJC35ewsspFC1VtqWZqUAZh + +41nK1p++/vFWBqpB3YZXSdWFDGsyJ7QtHFfi3vG/x8b/h6c4vHrvT0zx5lDwZB+YmV+ + xXROwXhRH2ND5375vtZz8OyfaQHhY43HVSP4dOfrHgeUbtVzcQiYW+SR3Q6FPHCUqStc + EycQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=to:subject:message-id:date:from:mime-version:dkim-signature; - bh=KcwTRtPiKk2j+vxpSLodKakyPE1kwRe/deHc2g8j5Bo=; + bh=IehaSA/JR4L/KX7NlPKK5B7zTFO/0C8UNbHTPhhIN84=; fh=OYPr0JdXtRRlM3Htj/E6+khbD9huvtdqkRTmPoW0wko=; - b=eRHm8dXx/CK2Dme025d2LDa51KZFZ5K5XhH/ffIUFez1PV5nYn/bNewv8L8ZiE5/xd - 7zh/wtjB95Wv4BXabZ+hFgMb9KDcBWoz5E17LN/FgvNuV+E7wHALYvCQm9lGYR5qfw/G - GU4QyoX5m/a7OVw2eiG+9+kF1x6mVGZsY208Yc272/RSQrfau0qQCQCACAM/EEMCwdvL - tXPSF80JlkyznrF7HDkl0zdy7D0+oYXYlluZIadDSd0S2GiqoYkD91be+xSh7GRl1c/9 - nbsJV8ifeOlXLIMuYg9rWO1Ii0fmfp6OGrLEPvkTyBIReWs552JuX1ALXXAG2qz8qVxG - kAGw==; + b=OUQ5TcZbiTNRl396nTBZH0uojnYZfc5tChwa5yntVLuQouy9qXfZXnKWHKNwRb/MiT + P2sVT7nG4DDbzSMdsOi7fG06vsBJfq4Oetaco5s1zyL7T7dCC0L4PnwJ2sRQbpSoh/Zb + YAknwfYkp1F0bjeYw2HBdfqlT93kzDLe7sI6zk2uADL1gp9oUMzKpotSMIrbzBIX6nGv + 26ASehd6ceNdxqCKCFCagi8WOuQQBNjN8E+G2m+7Zx1YjGif1LTgmzh6RxtpYT/M02gW + 4eidLLTEv6bQzFwKo9rwJq3iD1A9H9HESrEJGGWjAd5dTDxOLRQXge3igywzFjOnwPq0 + 9huw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=kDV3WaYT; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=XTkwK+MJ; spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com Return-Path: Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) - by mx.google.com with SMTPS id 3f1490d57ef6-e0ec8ba44efsor3812256276.2.2024.08.12.07.28.30 + by mx.google.com with SMTPS id ada2fe7eead31-498e47e8b90sor1864165137.5.2024.08.28.00.54.02 for (Google Transport Security); - Mon, 12 Aug 2024 07:28:30 -0700 (PDT) + Wed, 28 Aug 2024 00:54:02 -0700 (PDT) Received-SPF: pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; Authentication-Results: mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=kDV3WaYT; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=XTkwK+MJ; spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=gmail.com; s=20230601; t=1723472910; x=1724077710; dara=google.com; + d=gmail.com; s=20230601; t=1724831641; x=1725436441; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; - bh=KcwTRtPiKk2j+vxpSLodKakyPE1kwRe/deHc2g8j5Bo=; - b=kDV3WaYTvmK4uve2kN1AAt12vmS7jERDYJMUCWnA0AUhwYCw3UZ1POGrTyH4w3kw8c - g9+zBmjo+5eAGncdODLkuU7yDtW6AbjHUa+vJUxYXiDyh0wYZMt908iS7WJ0lJ9PDwEN - 1aF6Uu+vdtbWRA/kVrU9hup/DP8Le1d6h0mKyge1pbPA6swJqUr0XRrJ5F5Hp0xf/y5M - PKXHCKDvvxf2tKKyD2TKmXm6R9yE4aWkGvMrNCumpUfk+w//uB3J+jtldBBH8fqhPl0Q - pmMePv4xfOgPyhV94TxmsMa2gyVLyKeN4QyKMcbDFFTT7IizvwHlNk3wTzWFFu1Gh0Iz - dWLA== + bh=IehaSA/JR4L/KX7NlPKK5B7zTFO/0C8UNbHTPhhIN84=; + b=XTkwK+MJbzJP7Sy/I5oNWMkuJ7jrQZc5ovy3+4meyBwreJFQ1+F5UZOtUKd1svPC+u + v3TNvLvzlUTeXHW7TujG7U2WOxNOqqyYF18mgBIRlw5FXtgjz9j+bbALp0fo61e7gKhS + PHiRPocLWJpLgsMFy5nHod39aqcLWv0uMkCzwveMnDzlZYQFVxEKotyumIHUKLPIj2DI + BhOM5LhSml1qRzpB3eKpXGzEaLnyesPrOQNPwKB4ZfiSaP3xPYNfeAZhwohVEBTigi0h + ZnrH0DwfaJ6P1UdTTfG1YPfY1qm8BpEi4qFrYUUeZL1fK341NNq0y08egz0R6Q/CPJKL + l0KA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20230601; t=1723472910; x=1724077710; + d=1e100.net; s=20230601; t=1724831641; x=1725436441; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; - bh=KcwTRtPiKk2j+vxpSLodKakyPE1kwRe/deHc2g8j5Bo=; - b=oOYZjv7O3cgWFRDTHsdDksv1YCNyim0fd1MAlCEfA3szcL/ZJ7mz0R/5Yn5nw1BaSw - 6Z3GArs0uRjMTaqt0e0N1ndFQszK9kyHMZcQl1yC87A0GLcNJsd3Qwdnj765sQ/eVZmZ - sYeKKN2gUXvUMNxi0nq+a46izWdgYuVGD1GsIrWG9Z4C7/5R9qitls5IdZISTqW5rgwS - 4uovtFP78jD5w48SO5pusBYRFdG1LDCXJCSChM+OVQQIlgAsBEFWHPMI6Qe2IDSWp4zo - r3Hz8qi7WKtqF7W+2u+HgMDncdTbebFChirFYY3mNz1GNJ1qf/ks1d6Mv9D0KX4AB0jG - XMeQ== -X-Gm-Message-State: AOJu0YzzxHkXhnzAC1IM5u8jXOGWk1Xzrjka0M17SqnYJOY/UBZyo1bS - PQeHJKUplBNXFONu83fwYnmugr6SmRpJATslK/OcLy/tpiTd7GleyRS4wD3i9aMIRWU4TkgIAbp - 2q75XCN63+yFL/igOfdzsk6oihLjG -X-Google-Smtp-Source: AGHT+IFAWN5D9YSAVPoZbZaChqpvELVXAJFi1kcX+rzDehga6v9dV16xiu4JOgws6BCv8zr0cZOa75ffs1puG6Llm3w= -X-Received: by 2002:a05:6902:188c:b0:e08:6ce9:6e8e with SMTP id - 3f1490d57ef6-e113cec32bcmr556650276.23.1723472909924; Mon, 12 Aug 2024 - 07:28:29 -0700 (PDT) + bh=IehaSA/JR4L/KX7NlPKK5B7zTFO/0C8UNbHTPhhIN84=; + b=tlCjTjy8XCtY9U/SDWIKjLwWqYynJdQR9tV9VMWBcf37cDm9UpUVw5Ey9aScTxPLq+ + gadkjTGfA6ljpL8UsTbM1A2/b296DjfVZ2bYiCzZ58z/Httf8lSd9nInC1Tmd0iRIm3O + 8L/X9hWHkcbPGm8EhXNRiUKNBAFxshxpr24+jE2gWoPa7FVUmD1uY/nlYKMRGvvARqkq + RGQ9qpCa8LnWEPyRsE8AaV/g16kksprSEOoZocT3bwL+r/4O+s45ZNEHn9X0jEFPmb4U + 18/92ludL29pZ/PYgmz39iKbTEucY/7lZz3oKq2iNmWZPVKSjEf5kfYKMzIycQauxeN8 + lBDw== +X-Gm-Message-State: AOJu0YwP8OnzV4z4jcMcybAydVxa6xFnitsmWGjgRtqw9ENgSBo5ki6u + RGO9v2uhgEZvUHgR0fuGEYNoi7DCCDKFWePFH0ZVWNRGylHSvGShhFUZfztJ/KnOeUFE3KO+JyG + 8/1vz6i+Tu9quIYwBMxTsxItL1LJD +X-Google-Smtp-Source: AGHT+IFGI8kUAMWkx4LUP4o9Lv0V2QzLQJZlC8zeOQeO/P2VdPkPSYJrFwDo+q0Ez3VMetqJIH7d4YaBQI3E5y+sAWA= +X-Received: by 2002:a05:6102:c0b:b0:498:e21c:cc66 with SMTP id + ada2fe7eead31-49a4ecd79bemr1392977137.6.1724831641483; Wed, 28 Aug 2024 + 00:54:01 -0700 (PDT) MIME-Version: 1.0 From: "emailwallet.relayer.2" -Date: Mon, 12 Aug 2024 23:28:18 +0900 -Message-ID: -Subject: Set the new signer of 0x05A78D3dB903a58B5FA373E07e5044B95B12aec4 to +Date: Wed, 28 Aug 2024 16:53:50 +0900 +Message-ID: +Subject: Set the new signer of 0x7c5E4b26643682AF77A196781A851c9Fe769472d to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 Code 1162ebff40918afe5305e68396f0283eb675901d0387f97d21928d423aaa0b54 To: rrelayerbob@gmail.com -Content-Type: multipart/alternative; boundary="00000000000093925d061f7d4e6d" +Content-Type: multipart/alternative; boundary="00000000000049c5260620b9a9a6" ---00000000000093925d061f7d4e6d +--00000000000049c5260620b9a9a6 Content-Type: text/plain; charset="UTF-8" ---00000000000093925d061f7d4e6d +--00000000000049c5260620b9a9a6 Content-Type: text/html; charset="UTF-8"

---00000000000093925d061f7d4e6d-- +--00000000000049c5260620b9a9a6-- diff --git a/packages/contracts/test/emails/8453/accept.eml b/packages/contracts/test/emails/8453/accept.eml index df46893..1cc21c9 100644 --- a/packages/contracts/test/emails/8453/accept.eml +++ b/packages/contracts/test/emails/8453/accept.eml @@ -1,90 +1,90 @@ -Delivered-To: emaiwallet.alice@gmail.com -Received: by 2002:a05:6f02:726:b0:74:e105:693f with SMTP id 38csp825465rck; - Thu, 5 Sep 2024 06:02:50 -0700 (PDT) -X-Received: by 2002:a05:690c:6711:b0:62f:60db:326 with SMTP id 00721157ae682-6db2603b6b3mr49363407b3.20.1725541370615; - Thu, 05 Sep 2024 06:02:50 -0700 (PDT) -ARC-Seal: i=1; a=rsa-sha256; t=1725541370; cv=none; +Delivered-To: rrelayerbob@gmail.com +Received: by 2002:a05:6400:28a:b0:271:da57:79 with SMTP id hs10csp865237ecb; + Sun, 8 Sep 2024 17:38:53 -0700 (PDT) +X-Received: by 2002:a05:6358:9143:b0:1b8:226a:2622 with SMTP id e5c5f4694b2df-1b84beaea04mr864194555d.21.1725842333641; + Sun, 08 Sep 2024 17:38:53 -0700 (PDT) +ARC-Seal: i=1; a=rsa-sha256; t=1725842333; cv=none; d=google.com; s=arc-20160816; - b=SPNTugt1irLrj4rA8/gIiA4JZlSwcYoR6UrO6uIGxmTJpDngkXcBgM4ghQ/fxNjFAo - Yx5mhg5w3/5rOnxDiuA36J2ArzpyzriRXGaHgN/DTSQyloMIbPC51g9xTOs0ks2s52sH - ThuPMTcUaV4b8mVLgr8rX5dJK6ujrTfKEt1Pntxlgdw7qa3+/q2WyvpX/1G2TY3PLaZS - CGjZ9dyUcIB088a8INtwlXEnpDJ7GTCsloMNJKd69j2c0xAPp4adcbFPuOtqvgODkKmi - VcvDSHjylwGqgHWsbORyETw77aBwKCnAEKN7HKySutijVwgPwpW5vMwKh7F5oOwaHbkr - y7sQ== + b=Eo8FQDuq26qqB1YlOaTQ5KkpTWjMuduMUW/YTGvZXhX88eYKZsBZHO1Qgucz7eT4GP + lxiIeLbUjVkJU/kbWNOLusUabrsuMYm3UnOTVeiTZBX8LtHpy8/kWi72IiL0/wpszOB6 + M+CKFud0FgParMBL/FeowgITRN2lfSXXcrEFE7OJ46OUeZTkZH6o5klmAxRaP/WWnK7Q + Lv72Pleqdm7epcpoduR2K5V2t4WD/6hEhqFxkJQf3LfxOAIRYXX2brBpCKGNN7AmnMGJ + 1j4yTPYwX3FjSYBB0dFgRQpO7JqUQuoIWQWuE2IsFf8MowINFSOfzG2HGLCptV7GBdu8 + nR5g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=to:subject:message-id:date:from:mime-version:dkim-signature; - bh=hwfeT+eXDMKMbSAtqbg1CNGGYc5xGkm1k4wPnXXOvAs=; - fh=mGvgZSvjBMYp45+5ZRabA1Bu+M/iTPP6wzPoGZ2mmaM=; - b=Jz1deQH/eGSLh8tjVbWxeVue3QtFqFhrFT07GRfaUHD6MXxYIesea8Ze6SMLHYdjjn - TfZoaWfB8nXaJOpL6yspR8vkO22m1RFJGesoQ2Zw1r4jXAjh4J2sWDvGFkalMkF6wcgW - x+LalJUgcv0UVRn+PWfqYEHIE5vqrQyxoukcspkwcFK7dNqfHnLQoJbGZKjrmKxsS/sW - 8vAdykVieYcVE7+ubbdzR1KMZz6ivQjmiW5WcTTqTkLFeP/Dp80n+bPbjP9HlMLCSK3k - UBUOlaFnsL/2tYkxzG5bzdu2HcgT1QqAMgPlhrW/sgxdRujbnuSMuSpjCRPDcFTtlt09 - ntaw==; + bh=u3BMBmOH3oo7W5+AST9Mudcc8sUjkyZ9Ee118Aw1hRA=; + fh=OYPr0JdXtRRlM3Htj/E6+khbD9huvtdqkRTmPoW0wko=; + b=S7CO2YA0iDfPdwyRITTdtNCwpPJQ8OA27yWCiXjsYME+l2C6U73hWlxoUbTO8XqTOL + sQEE4O8zwppLIWznAgRsdXQ5tvcJjn8J+hRO/sDeiGAfeMVyKOXBFWfGwUpbUHHBZ1wS + 6rS2zhzDcrjm6qmnqUY+AeptvSkeb46ECH+QIutZ3BU8iXIe6xYhf7XL/5AkoSrrrTf6 + 4gmti0p2vUJcV+Npyb7QHzcW+AjCFpLPtvkDXRJMHeFxKlMFiaiiugn7vOk1yjxCSaVn + ahfeCUgcBSwkAp03icM2nTpYCVr6Ou0/ZOS74+yal0VsHbQkKAk6Xr6qP4AwCowX0Sc/ + //0w==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=Hzybui+n; - spf=pass (google.com: domain of suegamisora@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=suegamisora@gmail.com; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=RwyfZUyf; + spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com -Return-Path: +Return-Path: Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) - by mx.google.com with SMTPS id 00721157ae682-6d6ad2b42f9sor35604077b3.8.2024.09.05.06.02.50 - for + by mx.google.com with SMTPS id af79cd13be357-7a9a7a73740sor176146885a.19.2024.09.08.17.38.53 + for (Google Transport Security); - Thu, 05 Sep 2024 06:02:50 -0700 (PDT) -Received-SPF: pass (google.com: domain of suegamisora@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; + Sun, 08 Sep 2024 17:38:53 -0700 (PDT) +Received-SPF: pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; Authentication-Results: mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=Hzybui+n; - spf=pass (google.com: domain of suegamisora@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=suegamisora@gmail.com; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=RwyfZUyf; + spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=gmail.com; s=20230601; t=1725541370; x=1726146170; dara=google.com; + d=gmail.com; s=20230601; t=1725842333; x=1726447133; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; - bh=hwfeT+eXDMKMbSAtqbg1CNGGYc5xGkm1k4wPnXXOvAs=; - b=Hzybui+nhhOVD6I2QhZlxFI83wpacTX63HF3MyMplwN82g6DVQw542cNFAWEqEslGE - E1Mlb+gZkwOX9yYPQiIB0SawG2na5Dx6kjLFIXsUwcv1J0NWVSRB/WpLhX6HfJl0VMt7 - vhJLtYgYG+eMz0Mw1BIUPQGohGXI/D9ZwV9JShD5KrQTDl2iBMuGYQH9Mqb1C7BPtkTr - 8UvamiLaAYmv869rdp/QeAxEyHcPCCtDV8kYQSNTbLi/6KpsNRJ3Q/IxwYFoy/q9btPp - mPTXXT/przsCmBarCmNy7A/6bjryTJcWCU2TRptE27QB7M9nqNLgBJcgAy53b7rTk0CQ - DDCw== + bh=u3BMBmOH3oo7W5+AST9Mudcc8sUjkyZ9Ee118Aw1hRA=; + b=RwyfZUyfwOg5nNyFlHnCyClaJ27wgLGNekZls/hqRQSxYrc25H+aqdjBkpubBKuvpj + E0COKCv052KffeYVWD5/xcmIxNXpX7ZVv923i65P8ObFCVtt02qpyTarw2GYO9QXPtIa + roA2+VV0W7gEoQl1iSeQckJrRtt/9+nZwdZY/0/uAROHnOeKlvWjA4bikh74EZtjJg2E + jetr3A8eh2+yJ2EeXknJCZSX+QUpwJEjxnOju+tSiUH3ojAxjD9gnx86ZWVGSsaeUjP1 + +PlSjabZVDmkrOSq1HMj5fGpWCKDGQsvKa9MKLrwwzQBrEp0DE4TDhoR6TWN7znLdKgv + AXxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20230601; t=1725541370; x=1726146170; + d=1e100.net; s=20230601; t=1725842333; x=1726447133; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; - bh=hwfeT+eXDMKMbSAtqbg1CNGGYc5xGkm1k4wPnXXOvAs=; - b=kwldqm9cXwvuymaYr46gu97zPJn0wwwXu5RcKljxv379B1qQIMbHtdAkSz+ek4R1h8 - sfvXdk5LPZknjeQnZuuV41bfd26IgsUhI4q/ybRUz+G/GcQ05YWZJEFPTfGp0OT5mHOM - Pt50d0Q7NCEU1wGslZhLYkPBxTNEDJC0uadF2MACIad4sO32/u09wf9F7YphuoNcPY5d - 0J/1dfpzsfvUFTNbOTW9maUgJjBLz/vygroYvHTrBhrM/vbe4u6UTV1Bb/8Fsdh5JJg/ - YM64uCcMAIbesjOpameaz4I+K2tn+fiKqMj/j3TKemWbZjD48vMo1vwtA4gJmr0nWFcI - +8NQ== -X-Gm-Message-State: AOJu0YxvZnnsz1CAGYNjlcB4zfNBDYktlbs9mToQ1rgP3mDyM6OKHLjB - Xg+B5iaEMeXO5W25G1XZeMA/VNbVBOgHw45k//HNdj2fbNR/Y8Wd2xCE5W23d7nrD9qYj/ut4GN - fEEXfsRAsGT8mO9+fehRZh0saWFwWjQ== -X-Google-Smtp-Source: AGHT+IFZQtF1GDCDe0ITr7SZgu2pNEwcOnQaWvLAxxSHpSldnG/GJbb0DX/V+bGZwjPZeowlIKkeQhPoyQOvZX4JFL0= -X-Received: by 2002:a05:690c:10d:b0:64b:1eb2:3dd4 with SMTP id - 00721157ae682-6db25f8d9d4mr51110687b3.8.1725541369642; Thu, 05 Sep 2024 - 06:02:49 -0700 (PDT) + bh=u3BMBmOH3oo7W5+AST9Mudcc8sUjkyZ9Ee118Aw1hRA=; + b=OEx1J3HwB0T1PRnrPhqrVXxq2fd4HehGlOGD8qyfS8tuRl6sdyKsrF+wgTVNyWcwCM + uY9ZRVCFTb5/2/Z9n6O8dRV9JWsnK7hb7NYSmD/s2zaPFtfTIYBafm5TW+3uaPjQ25Gf + /5sGy64lE5uOegFmf12PkWKIQwgs1ytMLQ/PN24tjIS142p6Fj2Ns1f1CvV6ySttUO7i + btVq9zrBUNrsB88II+0srbEnYVrMIspUp4bMXgLiRuRjU53tiIXoZe9QqZrY3ZT1SXvq + cURQ3X5zoMg2FIJT7qLyRG9xa8bbq+uaszbjE6n/AmxowT6VzPJebE12Z7wvvGKs0+qs + RfZw== +X-Gm-Message-State: AOJu0Yyf0PsSEn60KLTqdIQcs8o4LO7Pa3spSzQjW1mUOwM8e/WTWsW0 + Zqu6dlktj82yEUBmOe30+KU4qyJ/WS4/FFRyayQDHrEK7IZEZIsaGDdPQpcmIOx+d+ZhxJLePKe + xsurjVlEiLeE9Va+J0s6JwZm7dG3X +X-Google-Smtp-Source: AGHT+IElTLWDEBfxtyVnOba2cERBK6YgvSGie3f/wnb+tcjM9hAUxYGKZci+WQB/O7imoT0PysrYKS2H8UaDLXML0Ew= +X-Received: by 2002:a05:620a:1910:b0:79f:12fb:ed1 with SMTP id + af79cd13be357-7a9a38a8187mr646569985a.16.1725842332968; Sun, 08 Sep 2024 + 17:38:52 -0700 (PDT) MIME-Version: 1.0 -From: Sora Suegami -Date: Thu, 5 Sep 2024 22:02:38 +0900 -Message-ID: -Subject: Accept guardian request for 0x0C06688e61C06466E2a5C6fE4E15c359260a33f3 +From: "emailwallet.relayer.2" +Date: Mon, 9 Sep 2024 09:38:42 +0900 +Message-ID: +Subject: Accept guardian request for 0xeb8E21A363Dce22ff6057dEEF7c074062037F571 Code 1162ebff40918afe5305e68396f0283eb675901d0387f97d21928d423aaa0b54 -To: emaiwallet.alice@gmail.com -Content-Type: multipart/alternative; boundary="00000000000062053206215ee808" +To: rrelayerbob@gmail.com +Content-Type: multipart/alternative; boundary="00000000000031f01c0621a4fbb6" ---00000000000062053206215ee808 +--00000000000031f01c0621a4fbb6 Content-Type: text/plain; charset="UTF-8" ---00000000000062053206215ee808 +--00000000000031f01c0621a4fbb6 Content-Type: text/html; charset="UTF-8" -

+

---00000000000062053206215ee808-- +--00000000000031f01c0621a4fbb6-- diff --git a/packages/contracts/test/emails/8453/recovery.eml b/packages/contracts/test/emails/8453/recovery.eml index 6a1ba65..0be721c 100644 --- a/packages/contracts/test/emails/8453/recovery.eml +++ b/packages/contracts/test/emails/8453/recovery.eml @@ -1,89 +1,89 @@ -Delivered-To: emaiwallet.alice@gmail.com -Received: by 2002:a05:6f02:726:b0:74:e105:693f with SMTP id 38csp826612rck; - Thu, 5 Sep 2024 06:04:03 -0700 (PDT) -X-Received: by 2002:a05:690c:10d:b0:64b:1eb2:3dd4 with SMTP id 00721157ae682-6db25f8d9d4mr51191717b3.8.1725541442826; - Thu, 05 Sep 2024 06:04:02 -0700 (PDT) -ARC-Seal: i=1; a=rsa-sha256; t=1725541442; cv=none; +Delivered-To: rrelayerbob@gmail.com +Received: by 2002:a05:6400:28a:b0:271:da57:79 with SMTP id hs10csp865517ecb; + Sun, 8 Sep 2024 17:39:56 -0700 (PDT) +X-Received: by 2002:a05:6102:e0f:b0:49c:1bc:1eff with SMTP id ada2fe7eead31-49c01bc22femr2263104137.28.1725842396118; + Sun, 08 Sep 2024 17:39:56 -0700 (PDT) +ARC-Seal: i=1; a=rsa-sha256; t=1725842396; cv=none; d=google.com; s=arc-20160816; - b=dgN9rb86tcKbl314vh/lV9xEGr3TsVHL1FMhbqdaICkT4bRKhlqrWrhEmErTP0KqQF - SFh8tOL3B/Vm3dlYxO0Xu1+KOJIXfv0Bo0aWxLTGvqKVbFx+okXu8mwa+aIf6bv85qtR - oWX8VN6KXffW6XvrYDkC3+Aiu18l2HDPeyOjBpTS10kBAppWhD37VSH5F3QPD545XXDT - vVmyz1dy5r/h0QapkUymr+FxgyQWAMUdhdVFlTMxH6sb87v4EAwfdjqGluhdnR3VKWeg - nqAbg2kRdzdzkyiEwQpGPMJKV133VpS1VaAqOSAdWqXvOUjIjFy1eMzL6GJeP8uvjJk0 - NZyg== + b=D41hDnFU3G2f/9wRCrVHNx7sasI4u/B06uRAaZgq+tOf6E/zpCTbGcMG2Qk83mFEIM + L00rZK/B4/akwHdjQW8Iel1GwfkdJNHEM+XGiEW6641RId75OwQC2kMG/m+fHakpBsLU + Xpcq4RjCaKJ1WqwkhYjrHxpH/ScVA1vp5sLVbHaymmFVYtC3ztYrVhyYnuKrUjgnAqdR + +PelC5UCIbuORBiLbdTc+cCzQIJV4OlI+u641PQJwrs67H2UrgZnyl6cXMcPNJrzXZiw + oKoD/mTHXGmPY+k6vO23OFy6fU63scpYc6i29ZIuseE2X6+CBodBAtPiYFmeK+hUxTDP + R1SQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=to:subject:message-id:date:from:mime-version:dkim-signature; - bh=6MM6Pl5GNQLdstU+WdNRpoeU1wDZibKScQ1LQdrk3eg=; - fh=mGvgZSvjBMYp45+5ZRabA1Bu+M/iTPP6wzPoGZ2mmaM=; - b=ci4fHCOfEpifWZHpYFon66bE8Zg1j3QaglQUNefhyIV9Gvhxf4ITh040xO5fKylMdD - /KkJYxgf+LPaIUln8ZiBTenIjSDpmmxReU612Xd5hSdpbAk8IDRkQTmbiSr2cwbKJMuc - GBfiu/GVAMhWIT9tN22Fy6wxDKRs9L74dYKLozcSnG71XlvdfTJvHkFo28Nz27hsV6Vo - YLDHQ+vxGJS0vkZxgmnPwtDZNf7axqYG6BzE8xQLhp6V7pW9c3sakfXxPV5TPWGu4r5k - af0cQbf1S0tcwaztLO1HmGFP12BWTZr8W6+HTBHp1jL5u2nPoFPcloKPrktlH6XDxEec - 0+MQ==; + bh=mvDapCNNa9oKBO12XFAqF92pzaTTjGLTiE2clMtdRzM=; + fh=OYPr0JdXtRRlM3Htj/E6+khbD9huvtdqkRTmPoW0wko=; + b=a9iG6t8RdHTSDJrDnbsG10Kdnq2Es9QDHumVktkIoOi0UF2vf5YprDs0nj7OFSPoCc + nAZAZvIwB8fL4f+cqM+ZIWzFjplOY1tfspreWsfZkxDUrhKNUhhiuyVI3JkdY/BMeCJ2 + mim96jnyH3ckvBPgeJdiCkZWknstuQrce1CQyfp64rUB0WQ4MgdgyPYBLSrbc2ZfQo3r + jahYqae1lJTY37m29wLIMh/vix2SJutq47+/St2rIk59SpgTz3xIJSPKI76ovOBR3sbU + /PjYxt8wFw2eP0g/etqbGS1sxVdu4UMWDqXpCrEQ63bTlfgTpwrYMh9ugZNfG4kHxD96 + 3uAA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=Q5n+i86m; - spf=pass (google.com: domain of suegamisora@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=suegamisora@gmail.com; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=RfgErx0i; + spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com -Return-Path: +Return-Path: Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) - by mx.google.com with SMTPS id 00721157ae682-6d2cdc3039esor68648397b3.6.2024.09.05.06.04.02 - for + by mx.google.com with SMTPS id af79cd13be357-7a9a7a296c5sor198986385a.15.2024.09.08.17.39.55 + for (Google Transport Security); - Thu, 05 Sep 2024 06:04:02 -0700 (PDT) -Received-SPF: pass (google.com: domain of suegamisora@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; + Sun, 08 Sep 2024 17:39:56 -0700 (PDT) +Received-SPF: pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; Authentication-Results: mx.google.com; - dkim=pass header.i=@gmail.com header.s=20230601 header.b=Q5n+i86m; - spf=pass (google.com: domain of suegamisora@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=suegamisora@gmail.com; + dkim=pass header.i=@gmail.com header.s=20230601 header.b=RfgErx0i; + spf=pass (google.com: domain of emailwalletrelayer987@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=emailwalletrelayer987@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com; dara=pass header.i=@gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=gmail.com; s=20230601; t=1725541442; x=1726146242; dara=google.com; + d=gmail.com; s=20230601; t=1725842395; x=1726447195; dara=google.com; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; - bh=6MM6Pl5GNQLdstU+WdNRpoeU1wDZibKScQ1LQdrk3eg=; - b=Q5n+i86mZfOdkMTNlN15YVu0tk49g4URHzjCOmJE4EQT0DR+HyDNBjhInKG+r4VOWk - 38Phl8iOirJCGFpvMbjm9ltpCN0/9T32a61HFO+c3ghy2VIZAGRfhmmOs23pSjEZ6S/A - IeiD2Lulba2/TWY/pHRdIdDqaOfR+6S1p5Xa+jHuJ1dP9BqiRojF/kEQV2eLvSHWbr5V - lVjxPG9klVwCjeYiw1HuY4ujhcpKEz/bBZeZE2dUAbr9xmb5bwMyybf3qbuyfDWo6Gkn - CD2LkmMwY1fjl89uLi7TIf5p7V/N7fZ0KmQKOstX5Uv00tqzQPAil/LLmK8UeXe0SjmG - Cjfw== + bh=mvDapCNNa9oKBO12XFAqF92pzaTTjGLTiE2clMtdRzM=; + b=RfgErx0ifG4YFDsIv9nxEu9TStTl0mBznYQZMMwT6Dpn3diDq+uhHBN2bRDMAT0dYv + 5w88/E+d8oLyhT6/TBlkps9ahwTKkfOZUCi4UUmBx2n/p+5uYTAVLP9Fv29PtgQFHnDe + qySfOEb7NKj8LR22QXYRUxRLN/jhmskaIpg9fs+HmC7jU4+fpoFmpoIgWb8rGI/uzip8 + pZYYEvVICaXHH3E4aRMps28DY2VYHBycwlC+a4+O0Ui7Ctpay0eALDUq20YrqimKBa+K + 72gp//f9/UMHuG581IRQDiqE3pPz+Cx8KYc0z7DYbEF1YknhJG7gwdOwPJzGjv2LVeps + o3xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20230601; t=1725541442; x=1726146242; + d=1e100.net; s=20230601; t=1725842395; x=1726447195; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; - bh=6MM6Pl5GNQLdstU+WdNRpoeU1wDZibKScQ1LQdrk3eg=; - b=iMG18iXwzfOoSQrn0LHwBxtrYlO1rftVf0uOjTpqxsxnGF2GCLVkWHllEgTP7JYvVv - 9wIexH3EDZP1Yzg+1t5o6QhhMUahxN0tENE5HPBPN/ssDVfMyIxe71/GDjRrziFa05pO - 7A/SnVgSEYw1loZE7s6t0jVioZKwH166K+Nv6Vp63euWUNDy5cqlDJxGvIZ7uq7LrKL1 - 1zypW6RO8ErksCd5jeLkX+S0clUKRhFty717ARMExlBTj8COIfdr0dH2J29TmHYut0um - znsNqV8lnvPGWUS0PYZQNelXB/pWWn67ReCJPzzyTp1QQXqHR8RE6Db5mwS3b/lns4G+ - 85GA== -X-Gm-Message-State: AOJu0YyBYMZ3PLz65C9cjQOEP0c3TuvrKm5l13t/nM9BxP4lljzHXgBl - RzrJLi93EvdrFsKb9w49o4lIfuueSV3/dlBjumVzHHpax5R5op+nwVDB76v1Lpej45Z4tDkmx02 - uLiv/t8NwuH8xDsweoaVGPiqU5pIYJw== -X-Google-Smtp-Source: AGHT+IGqY86xMnFOVbwQtNcCB0TeRXVjm/VBJ8dui1WDsotviQlm04Q/WqfNNFCxtJ09hl5EaFu07qxaL3R1mgULrr0= -X-Received: by 2002:a05:690c:90a:b0:65f:96e9:42f4 with SMTP id - 00721157ae682-6db25fad0f0mr54339607b3.15.1725541441967; Thu, 05 Sep 2024 - 06:04:01 -0700 (PDT) + bh=mvDapCNNa9oKBO12XFAqF92pzaTTjGLTiE2clMtdRzM=; + b=GWRnggEPlVvXAqrwoXCKq4ZOgguVgZiZb4jQpoFHV+/oc9UYFBSsN9b5B+OUaj2F0N + G6nv2u7c346WsoIfb3kCvyqwQbZBP1Kzcb/n3DUW7CjlE2VSz6BOOrPQxLl5nshDU1Du + QGGoGjE4+dzEZBdYjJVFSq2L1DJilntkNAFS1ff9DaGqOCEEftx0Y/MfFumclnwZ5VYR + C2+o9qH45iwtw88mBsfJ7Yl7OoI9w10NuuraYQcoeu72zwmb30E2uHfUe5osGldslgXJ + RcGV8JsFGwAJJUbmr/4HZFQDt3EMeydn7KkieTgi41fExIj5ZC3VhEt1rw4PTdqcTbSG + mEdQ== +X-Gm-Message-State: AOJu0YzZrLLg8bVVKkxvRm1k5x4qHXit4w1BuMonhJq6LxOP+DHivono + +8JABSGbjSpXofT9yUe7vRgJ5G3KTm5sIL9UP2ZOxlI4c3j37jmENGKx2sb/T9IdgYq1A3890fY + 2SoTWndVkyH23ZCxWm/36uVvQOyum +X-Google-Smtp-Source: AGHT+IGhU+pRzJcpq3/XnnchlgpJSMnc3PvuTm0gfTpuyITwadr4gnoO1o/F4dGPxxKl9Epjas8+m4UXLqVKM8yWsCo= +X-Received: by 2002:a05:620a:2901:b0:7a2:1bc:fc1e with SMTP id + af79cd13be357-7a99739b7bbmr1356449485a.61.1725842395467; Sun, 08 Sep 2024 + 17:39:55 -0700 (PDT) MIME-Version: 1.0 -From: Sora Suegami -Date: Thu, 5 Sep 2024 22:03:50 +0900 -Message-ID: -Subject: Set the new signer of 0x0C06688e61C06466E2a5C6fE4E15c359260a33f3 to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 -To: emaiwallet.alice@gmail.com -Content-Type: multipart/alternative; boundary="000000000000b19c7a06215eecc9" +From: "emailwallet.relayer.2" +Date: Mon, 9 Sep 2024 09:39:44 +0900 +Message-ID: +Subject: Set the new signer of 0xeb8E21A363Dce22ff6057dEEF7c074062037F571 to 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 +To: rrelayerbob@gmail.com +Content-Type: multipart/alternative; boundary="000000000000eb98600621a4fedf" ---000000000000b19c7a06215eecc9 +--000000000000eb98600621a4fedf Content-Type: text/plain; charset="UTF-8" ---000000000000b19c7a06215eecc9 +--000000000000eb98600621a4fedf Content-Type: text/html; charset="UTF-8" -

+

---000000000000b19c7a06215eecc9-- +--000000000000eb98600621a4fedf-- diff --git a/packages/contracts/test/helpers/DeploymentHelper.sol b/packages/contracts/test/helpers/DeploymentHelper.sol index 3acad10..dc34550 100644 --- a/packages/contracts/test/helpers/DeploymentHelper.sol +++ b/packages/contracts/test/helpers/DeploymentHelper.sol @@ -5,16 +5,21 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "../../src/EmailAuth.sol"; -import "../../src/utils/Verifier.sol"; -import "../../src/utils/ECDSAOwnedDKIMRegistry.sol"; -// import "../../src/utils/ForwardDKIMRegistry.sol"; +import {EmailAuth, EmailAuthMsg} from "../../src/EmailAuth.sol"; +import {Verifier, EmailProof} from "../../src/utils/Verifier.sol"; +import {ECDSAOwnedDKIMRegistry} from "../../src/utils/ECDSAOwnedDKIMRegistry.sol"; import {UserOverrideableDKIMRegistry} from "@zk-email/contracts/UserOverrideableDKIMRegistry.sol"; -import "./SimpleWallet.sol"; -import "./RecoveryController.sol"; +import {SimpleWallet} from "./SimpleWallet.sol"; +import {RecoveryController, EmailAccountRecovery} from "./RecoveryController.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +// // FOR_ZKSYNC:START +// import {ZKSyncCreate2Factory} from "../../src/utils/ZKSyncCreate2Factory.sol"; +// import "../../src/utils/ForwardDKIMRegistry.sol"; +// import {RecoveryControllerZKSync, EmailAccountRecoveryZKSync} from "./RecoveryControllerZKSync.sol"; +// // FOR_ZKSYNC:END + contract DeploymentHelper is Test { using ECDSA for *; @@ -26,6 +31,10 @@ contract DeploymentHelper is Test { SimpleWallet simpleWalletImpl; SimpleWallet simpleWallet; + // // FOR_ZKSYNC:START + // RecoveryControllerZKSync recoveryControllerZKSync; + // // FOR_ZKSYNC:END + address deployer = vm.addr(1); address receiver = vm.addr(2); address guardian; @@ -127,13 +136,42 @@ contract DeploymentHelper is Test { payable(address(recoveryControllerProxy)) ); + // Create SimpleWallet simpleWalletImpl = new SimpleWallet(); + address recoveryControllerAddress = address(recoveryController); + + // // FOR_ZKSYNC:START + // // Create zkSync Factory implementation + // if (isZkSync()) { + // ZKSyncCreate2Factory factoryImpl = new ZKSyncCreate2Factory(); + // // Create RecoveryControllerZKSync as EmailAccountRecovery implementation + // RecoveryControllerZKSync recoveryControllerZKSyncImpl = new RecoveryControllerZKSync(); + // ERC1967Proxy recoveryControllerZKSyncProxy = new ERC1967Proxy( + // address(recoveryControllerZKSyncImpl), + // abi.encodeCall( + // recoveryControllerZKSyncImpl.initialize, + // ( + // signer, + // address(verifier), + // address(dkim), + // address(emailAuthImpl), + // address(factoryImpl) + // ) + // ) + // ); + // recoveryControllerZKSync = RecoveryControllerZKSync( + // payable(address(recoveryControllerZKSyncProxy)) + // ); + // recoveryControllerAddress = address(recoveryControllerZKSync); + // } + // // FOR_ZKSYNC:END + ERC1967Proxy simpleWalletProxy = new ERC1967Proxy( address(simpleWalletImpl), abi.encodeCall( simpleWalletImpl.initialize, - (signer, address(recoveryController)) + (signer, recoveryControllerAddress) ) ); simpleWallet = SimpleWallet(payable(address(simpleWalletProxy))); @@ -142,6 +180,33 @@ contract DeploymentHelper is Test { // Set guardian address guardian = EmailAccountRecovery(address(recoveryController)) .computeEmailAuthAddress(address(simpleWallet), accountSalt); + // // FOR_ZKSYNC:START + // if (isZkSync()) { + // guardian = EmailAccountRecoveryZKSync(address(recoveryControllerZKSync)) + // .computeEmailAuthAddress(address(simpleWallet), accountSalt); + // } + // // FOR_ZKSYNC:END + vm.stopPrank(); } + + function isZkSync() public view returns (bool) { + return block.chainid == 324 || block.chainid == 300; + } + + function skipIfZkSync() public { + if (isZkSync()) { + vm.skip(true); + } else { + vm.skip(false); + } + } + + function skipIfNotZkSync() public { + if (!isZkSync()) { + vm.skip(true); + } else { + vm.skip(false); + } + } } diff --git a/packages/contracts/test/helpers/RecoveryControllerZKSync.sol b/packages/contracts/test/helpers/RecoveryControllerZKSync.sol new file mode 100644 index 0000000..f9a7a67 --- /dev/null +++ b/packages/contracts/test/helpers/RecoveryControllerZKSync.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {EmailAccountRecoveryZKSync} from "../../src/EmailAccountRecoveryZKSync.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {SimpleWallet} from "./SimpleWallet.sol"; + +contract RecoveryControllerZKSync is OwnableUpgradeable, EmailAccountRecoveryZKSync { + enum GuardianStatus { + NONE, + REQUESTED, + ACCEPTED + } + uint public constant DEFAULT_TIMELOCK_PERIOD = 3 days; + + mapping(address => bool) public isActivatedOfAccount; + mapping(address => bool) public isRecovering; + mapping(address => address) public newSignerCandidateOfAccount; + mapping(address => GuardianStatus) public guardians; + mapping(address => uint) public timelockPeriodOfAccount; + mapping(address => uint) public currentTimelockOfAccount; + + // modifier onlyNotRecoveringOwner() { + // require(msg.sender == owner(), "only owner"); + // require(!isRecovering, "recovery in progress"); + // _; + // } + + constructor() {} + + function initialize( + address _initialOwner, + address _verifier, + address _dkim, + address _emailAuthImplementation, + address _factory + ) public initializer { + __Ownable_init(_initialOwner); + verifierAddr = _verifier; + dkimAddr = _dkim; + emailAuthImplementationAddr = _emailAuthImplementation; + factoryAddr = _factory; + } + + function isActivated( + address recoveredAccount + ) public view override returns (bool) { + return isActivatedOfAccount[recoveredAccount]; + } + + function acceptanceSubjectTemplates() + public + pure + override + returns (string[][] memory) + { + string[][] memory templates = new string[][](1); + templates[0] = new string[](5); + templates[0][0] = "Accept"; + templates[0][1] = "guardian"; + templates[0][2] = "request"; + templates[0][3] = "for"; + templates[0][4] = "{ethAddr}"; + return templates; + } + + function recoverySubjectTemplates() + public + pure + override + returns (string[][] memory) + { + string[][] memory templates = new string[][](1); + templates[0] = new string[](8); + templates[0][0] = "Set"; + templates[0][1] = "the"; + templates[0][2] = "new"; + templates[0][3] = "signer"; + templates[0][4] = "of"; + templates[0][5] = "{ethAddr}"; + templates[0][6] = "to"; + templates[0][7] = "{ethAddr}"; + return templates; + } + + function extractRecoveredAccountFromAcceptanceSubject( + bytes[] memory subjectParams, + uint templateIdx + ) public pure override returns (address) { + require(templateIdx == 0, "invalid template index"); + require(subjectParams.length == 1, "invalid subject params"); + return abi.decode(subjectParams[0], (address)); + } + + function extractRecoveredAccountFromRecoverySubject( + bytes[] memory subjectParams, + uint templateIdx + ) public pure override returns (address) { + require(templateIdx == 0, "invalid template index"); + require(subjectParams.length == 2, "invalid subject params"); + return abi.decode(subjectParams[0], (address)); + } + + function requestGuardian(address guardian) public { + address account = msg.sender; + require(!isRecovering[account], "recovery in progress"); + require(guardian != address(0), "invalid guardian"); + require( + guardians[guardian] == GuardianStatus.NONE, + "guardian status must be NONE" + ); + if (!isActivatedOfAccount[account]) { + isActivatedOfAccount[account] = true; + } + guardians[guardian] = GuardianStatus.REQUESTED; + } + + function configureTimelockPeriod(uint period) public { + timelockPeriodOfAccount[msg.sender] = period; + } + + function acceptGuardian( + address guardian, + uint templateIdx, + bytes[] memory subjectParams, + bytes32 + ) internal override { + address account = abi.decode(subjectParams[0], (address)); + require(!isRecovering[account], "recovery in progress"); + require(guardian != address(0), "invalid guardian"); + + require( + guardians[guardian] == GuardianStatus.REQUESTED, + "guardian status must be REQUESTED" + ); + require(templateIdx == 0, "invalid template index"); + require(subjectParams.length == 1, "invalid subject params"); + guardians[guardian] = GuardianStatus.ACCEPTED; + } + + function processRecovery( + address guardian, + uint templateIdx, + bytes[] memory subjectParams, + bytes32 + ) internal override { + address account = abi.decode(subjectParams[0], (address)); + require(!isRecovering[account], "recovery in progress"); + require(guardian != address(0), "invalid guardian"); + require( + guardians[guardian] == GuardianStatus.ACCEPTED, + "guardian status must be ACCEPTED" + ); + require(templateIdx == 0, "invalid template index"); + require(subjectParams.length == 2, "invalid subject params"); + address newSignerInEmail = abi.decode(subjectParams[1], (address)); + require(newSignerInEmail != address(0), "invalid new signer"); + isRecovering[account] = true; + newSignerCandidateOfAccount[account] = newSignerInEmail; + currentTimelockOfAccount[account] = + block.timestamp + + timelockPeriodOfAccount[account]; + } + + function rejectRecovery() public { + address account = msg.sender; + require(isRecovering[account], "recovery not in progress"); + require( + currentTimelockOfAccount[account] > block.timestamp, + "timelock expired" + ); + isRecovering[account] = false; + newSignerCandidateOfAccount[account] = address(0); + currentTimelockOfAccount[account] = 0; + } + + function completeRecovery(address account, bytes memory) public override { + require(account != address(0), "invalid account"); + require(isRecovering[account], "recovery not in progress"); + require( + currentTimelockOfAccount[account] <= block.timestamp, + "timelock not expired" + ); + address newSigner = newSignerCandidateOfAccount[account]; + isRecovering[account] = false; + currentTimelockOfAccount[account] = 0; + newSignerCandidateOfAccount[account] = address(0); + SimpleWallet(payable(account)).changeOwner(newSigner); + } +} diff --git a/packages/contracts/test/script/ChangeOwners.t.sol b/packages/contracts/test/script/ChangeOwners.t.sol index f9c7b68..dcbe183 100644 --- a/packages/contracts/test/script/ChangeOwners.t.sol +++ b/packages/contracts/test/script/ChangeOwners.t.sol @@ -8,9 +8,10 @@ import {Deploy} from "../../script/DeployCommons.s.sol"; import {Deploy as Deploy2} from "../../script/DeployForwardDKIMRegistry.s.sol"; import {ChangeOwners} from "../../script/ChangeOwners.s.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; -contract ChangeOwnersTest is Test { - function setUp() public { +contract ChangeOwnersTest is StructHelper { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -20,6 +21,8 @@ contract ChangeOwnersTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); vm.setEnv("SOURCE_DKIM", vm.toString(vm.envAddress("ECDSA_DKIM"))); diff --git a/packages/contracts/test/script/ChangeSignerInECDSAOwnedDKIMRegistry.t.sol b/packages/contracts/test/script/ChangeSignerInECDSAOwnedDKIMRegistry.t.sol index 9020849..26bc879 100644 --- a/packages/contracts/test/script/ChangeSignerInECDSAOwnedDKIMRegistry.t.sol +++ b/packages/contracts/test/script/ChangeSignerInECDSAOwnedDKIMRegistry.t.sol @@ -7,9 +7,10 @@ import "forge-std/console.sol"; import {Deploy} from "../../script/DeployCommons.s.sol"; import {ChangeSigner} from "../../script/ChangeSignerInECDSAOwnedDKIMRegistry.s.sol"; import {ECDSAOwnedDKIMRegistry} from "../../src/utils/ECDSAOwnedDKIMRegistry.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; -contract ChangeSignerInECDSAOwnedDKIMRegistryTest is Test { - function setUp() public { +contract ChangeSignerInECDSAOwnedDKIMRegistryTest is StructHelper { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -19,6 +20,8 @@ contract ChangeSignerInECDSAOwnedDKIMRegistryTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); ChangeSigner changeSigner = new ChangeSigner(); diff --git a/packages/contracts/test/script/ChangeSourceInForwardDKIMRegistry.t.sol b/packages/contracts/test/script/ChangeSourceInForwardDKIMRegistry.t.sol index 96b72f0..0c90097 100644 --- a/packages/contracts/test/script/ChangeSourceInForwardDKIMRegistry.t.sol +++ b/packages/contracts/test/script/ChangeSourceInForwardDKIMRegistry.t.sol @@ -8,9 +8,10 @@ import {Deploy} from "../../script/DeployCommons.s.sol"; import {Deploy as Deploy2} from "../../script/DeployForwardDKIMRegistry.s.sol"; import {ChangeSource} from "../../script/ChangeSourceInForwardDKIMRegistry.s.sol"; import {ForwardDKIMRegistry} from "../../src/utils/ForwardDKIMRegistry.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; -contract ChangeSourceInForwardDKIMRegistryTest is Test { - function setUp() public { +contract ChangeSourceInForwardDKIMRegistryTest is StructHelper { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -20,6 +21,8 @@ contract ChangeSourceInForwardDKIMRegistryTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); vm.setEnv("SOURCE_DKIM", vm.toString(vm.envAddress("ECDSA_DKIM"))); diff --git a/packages/contracts/test/script/DeployCommons.t.sol b/packages/contracts/test/script/DeployCommons.t.sol index c158388..a9ff89b 100644 --- a/packages/contracts/test/script/DeployCommons.t.sol +++ b/packages/contracts/test/script/DeployCommons.t.sol @@ -5,9 +5,10 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import { Deploy } from "../../script/DeployCommons.s.sol"; +import { StructHelper } from "../helpers/StructHelper.sol"; -contract DeployCommonsTest is Test { - function setUp() public { +contract DeployCommonsTest is StructHelper { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -19,6 +20,8 @@ contract DeployCommonsTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); } diff --git a/packages/contracts/test/script/DeployRecoveryController.t.sol b/packages/contracts/test/script/DeployRecoveryController.t.sol index 770f563..6a66b7f 100644 --- a/packages/contracts/test/script/DeployRecoveryController.t.sol +++ b/packages/contracts/test/script/DeployRecoveryController.t.sol @@ -5,10 +5,10 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import {Deploy} from "../../script/DeployRecoveryController.s.sol"; -import "../helpers/StructHelper.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; -contract DeployRecoveryControllerTest is Test { - function setUp() public { +contract DeployRecoveryControllerTest is StructHelper { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -17,6 +17,8 @@ contract DeployRecoveryControllerTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); require( diff --git a/packages/contracts/test/script/DeploySimpleWallet.t.sol b/packages/contracts/test/script/DeploySimpleWallet.t.sol index 6bf3b05..a0bbf16 100644 --- a/packages/contracts/test/script/DeploySimpleWallet.t.sol +++ b/packages/contracts/test/script/DeploySimpleWallet.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import { Deploy } from "../../script/DeployCommons.s.sol"; -import "../helpers/StructHelper.sol"; +import { StructHelper } from "../helpers/StructHelper.sol"; contract DeploySimpleWalletTest is StructHelper { function setUp() public override { @@ -38,29 +38,39 @@ contract DeploySimpleWalletTest is StructHelper { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); } function test_run_no_dkim() public { + skipIfZkSync(); + vm.setEnv("DKIM", vm.toString(address(0))); Deploy deploy = new Deploy(); deploy.run(); } function test_run_no_verifier() public { + skipIfZkSync(); + vm.setEnv("VERIFIER", vm.toString(address(0))); Deploy deploy = new Deploy(); deploy.run(); } function test_run_no_email_auth() public { + skipIfZkSync(); + vm.setEnv("EMAIL_AUTH_IMPL", vm.toString(address(0))); Deploy deploy = new Deploy(); deploy.run(); } function test_run_no_simple_wallet() public { + skipIfZkSync(); + vm.setEnv("SIMPLE_WALLET_IMPL", vm.toString(address(0))); Deploy deploy = new Deploy(); deploy.run(); diff --git a/packages/contracts/test/script/RenounceOwners.t.sol b/packages/contracts/test/script/RenounceOwners.t.sol index 732169b..cafb661 100644 --- a/packages/contracts/test/script/RenounceOwners.t.sol +++ b/packages/contracts/test/script/RenounceOwners.t.sol @@ -8,9 +8,10 @@ import {Deploy} from "../../script/DeployCommons.s.sol"; import {Deploy as Deploy2} from "../../script/DeployForwardDKIMRegistry.s.sol"; import {RenounceOwners} from "../../script/RenounceOwners.s.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; -contract RenounceOwnersTest is Test { - function setUp() public { +contract RenounceOwnersTest is StructHelper { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -19,6 +20,8 @@ contract RenounceOwnersTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); vm.setEnv("SOURCE_DKIM", vm.toString(vm.envAddress("ECDSA_DKIM"))); diff --git a/packages/contracts/test/script/Upgrades.t.sol b/packages/contracts/test/script/Upgrades.t.sol index fe74bfb..862eba3 100644 --- a/packages/contracts/test/script/Upgrades.t.sol +++ b/packages/contracts/test/script/Upgrades.t.sol @@ -9,12 +9,13 @@ import {Deploy as Deploy2} from "../../script/DeployForwardDKIMRegistry.s.sol"; import {Upgrades} from "../../script/Upgrades.s.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {StructHelper} from "../helpers/StructHelper.sol"; -contract UpgradesTest is Test { +contract UpgradesTest is StructHelper { uint256 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - function setUp() public { + function setUp() public override { vm.setEnv( "PRIVATE_KEY", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" @@ -23,6 +24,8 @@ contract UpgradesTest is Test { } function test_run() public { + skipIfZkSync(); + Deploy deploy = new Deploy(); deploy.run(); vm.setEnv("SOURCE_DKIM", vm.toString(vm.envAddress("ECDSA_DKIM")));