diff --git a/.gas-snapshot b/.gas-snapshot new file mode 100644 index 0000000..0316ac9 --- /dev/null +++ b/.gas-snapshot @@ -0,0 +1 @@ +SnapshotTest:test_snapshot((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes)) (runs: 256, μ: 409655, ~: 410110) \ No newline at end of file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..8f7fbf5 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,15 @@ +name: test +on: + push: + branches: [main] + pull_request: + workflow_dispatch: +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - uses: foundry-rs/foundry-toolchain@v1 + - run: for i in {1..16}; do forge snapshot --force | grep test_snapshot; done diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0473c65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# foundry +cache/ +out/ +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ +/broadcast/**/run-latest.json +docs/ + +.env +.vscode/ +.DS_Store +*.log diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6a86d3e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/webauthn-sol"] + path = lib/webauthn-sol + url = https://github.com/base-org/webauthn-sol diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..c08d9cf --- /dev/null +++ b/foundry.toml @@ -0,0 +1,8 @@ +[profile.default] +isolate = true + +[fmt] +tab_width = 2 +sort_imports = true +bracket_spacing = true +number_underscore = "thousands" diff --git a/lib/webauthn-sol b/lib/webauthn-sol new file mode 160000 index 0000000..619f20a --- /dev/null +++ b/lib/webauthn-sol @@ -0,0 +1 @@ +Subproject commit 619f20ab0f074fef41066ee4ab24849a913263b2 diff --git a/test/Snapshot.t.sol b/test/Snapshot.t.sol new file mode 100644 index 0000000..37aab54 --- /dev/null +++ b/test/Snapshot.t.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; + +import { Utils, WebAuthnInfo } from "webauthn-sol/../test/Utils.sol"; +import { WebAuthn } from "webauthn-sol/WebAuthn.sol"; + +contract SnapshotTest is Test { + using Utils for uint256; + using Utils for bytes32; + + uint256 internal constant PRIVATE_KEY = uint256(0x03d99692017473e2d631945a812607b23269d85721e0f370b8d3e7d29a874fd2); + PublicKey public PUBLIC_KEY = abi.decode( + hex"1c05286fe694493eae33312f2d2e0d0abeda8db76238b7a204be1fb87f54ce4228fef61ef4ac300f631657635c28e59bfb2fe71bce1634c81c65642042f6dc4d", + (PublicKey) + ); + + function test(UserOperation calldata userOp) external { + bytes32 userOpHash = keccak256(abi.encode(userOp)); + WebAuthnInfo memory webauthn = userOpHash.getWebAuthnStruct(); + (bytes32 r, bytes32 s) = vm.signP256(PRIVATE_KEY, webauthn.messageHash); + assertTrue( + WebAuthn.verify({ + challenge: abi.encode(userOpHash), + requireUV: false, + webAuthnAuth: WebAuthn.WebAuthnAuth({ + authenticatorData: webauthn.authenticatorData, + clientDataJSON: webauthn.clientDataJSON, + typeIndex: 1, + challengeIndex: 23, + r: uint256(r), + s: uint256(s).normalizeS() + }), + x: PUBLIC_KEY.x, + y: PUBLIC_KEY.y + }) + ); + } +} + +struct PublicKey { + uint256 x; + uint256 y; +} + +struct UserOperation { + bytes field0; + bytes field1; + uint256 field2; + uint256 field3; + uint256 field4; +}