From 04bed7c8299314dbe763144eb942f97777a731d9 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:57:18 -0500 Subject: [PATCH 01/45] feat: add ma v2 scaffolding with basic uo test --- .vitest/src/instances.ts | 11 + .../src/ma-v2/abis/accountFactoryAbi.ts | 638 ++++++++ .../src/ma-v2/abis/modularAccountAbi.ts | 1241 ++++++++++++++++ .../abis/semiModularAccountBytecodeAbi.ts | 1288 ++++++++++++++++ .../abis/semiModularAccountStorageAbi.ts | 1301 +++++++++++++++++ .../src/ma-v2/account/account.ts | 158 ++ .../src/ma-v2/client/client.test.ts | 64 + .../src/ma-v2/client/client.ts | 73 + .../smart-contracts/src/ma-v2/index.ts | 0 .../allowlist-module/abis/allowlistModule.ts | 715 +++++++++ .../abis/nativeTokenLimitModule.ts | 403 +++++ .../abis/paymasterGuardModule.ts | 241 +++ .../abis/singleSignerValidationModule.ts | 330 +++++ .../single-signer-validation/module.ts | 49 + .../single-signer-validation/signer.ts | 74 + .../time-range-module/abis/timeRangeModule.ts | 295 ++++ .../abis/webauthnValidation.ts | 373 +++++ .../smart-contracts/src/ma-v2/utils.ts | 42 + 18 files changed, 7296 insertions(+) create mode 100644 account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/account/account.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/client/client.test.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/client/client.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/index.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModule.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModule.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModule.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModule.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModule.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidation.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/utils.ts diff --git a/.vitest/src/instances.ts b/.vitest/src/instances.ts index a8b1a76c40..08431e186f 100644 --- a/.vitest/src/instances.ts +++ b/.vitest/src/instances.ts @@ -10,6 +10,17 @@ import { split } from "../../aa-sdk/core/src/transport/split"; import { poolId, rundlerBinaryPath } from "./constants"; import { paymasterTransport } from "./paymaster/transport"; +export const local070InstanceOptSep = defineInstance({ + chain: localhost, + forkBlockNumber: 21153995, + forkUrl: + process.env.VITEST_OPT_SEPOLIA_FORK_URL ?? + "https://opt-sepolia-rpc.publicnode.com", + entryPointVersion: "0.7.0", + anvilPort: 8345, + bundlerPort: 8445, +}); + export const local060Instance = defineInstance({ chain: localhost, forkBlockNumber: 6381303, diff --git a/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts new file mode 100644 index 0000000000..cb3ca8194b --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts @@ -0,0 +1,638 @@ +export const accountFactoryAbi = [ + { + type: "constructor", + inputs: [ + { + name: "_entryPoint", + type: "address", + internalType: "contract IEntryPoint", + }, + { + name: "_accountImpl", + type: "address", + internalType: "contract ModularAccount", + }, + { + name: "_semiModularImpl", + type: "address", + internalType: "contract SemiModularAccountBytecode", + }, + { + name: "_singleSignerValidationModule", + type: "address", + internalType: "address", + }, + { + name: "_webAuthnValidationModule", + type: "address", + internalType: "address", + }, + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "ACCOUNT_IMPL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract ModularAccount", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "ENTRY_POINT", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IEntryPoint", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "SEMI_MODULAR_ACCOUNT_IMPL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract SemiModularAccountBytecode", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "SINGLE_SIGNER_VALIDATION_MODULE", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "WEBAUTHN_VALIDATION_MODULE", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "acceptOwnership", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "addStake", + inputs: [ + { + name: "unstakeDelay", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "createAccount", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "contract ModularAccount", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "createSemiModularAccount", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "contract SemiModularAccountBytecode", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "createWebAuthnAccount", + inputs: [ + { + name: "ownerX", + type: "uint256", + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "contract ModularAccount", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getAddress", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getAddressSemiModular", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getAddressWebAuthn", + inputs: [ + { + name: "ownerX", + type: "uint256", + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getSalt", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "getSaltWebAuthn", + inputs: [ + { + name: "ownerX", + type: "uint256", + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "pendingOwner", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "renounceOwnership", + inputs: [], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "transferOwnership", + inputs: [ + { + name: "newOwner", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "unlockStake", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdraw", + inputs: [ + { + name: "to", + type: "address", + internalType: "address payable", + }, + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdrawStake", + inputs: [ + { + name: "withdrawAddress", + type: "address", + internalType: "address payable", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "ModularAccountDeployed", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "owner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "salt", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OwnershipTransferStarted", + inputs: [ + { + name: "previousOwner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newOwner", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OwnershipTransferred", + inputs: [ + { + name: "previousOwner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newOwner", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "SemiModularAccountDeployed", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "owner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "salt", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "WebAuthnModularAccountDeployed", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "ownerX", + type: "uint256", + indexed: true, + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + indexed: true, + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "AddressEmptyCode", + inputs: [ + { + name: "target", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "AddressInsufficientBalance", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "FailedInnerCall", + inputs: [], + }, + { + type: "error", + name: "InvalidAction", + inputs: [], + }, + { + type: "error", + name: "OwnableInvalidOwner", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "OwnableUnauthorizedAccount", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "TransferFailed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts new file mode 100644 index 0000000000..7919fe6bb3 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts @@ -0,0 +1,1241 @@ +export const modularAccountAbi = [ + { + type: "constructor", + inputs: [ + { + name: "entryPoint", + type: "address", + internalType: "contract IEntryPoint", + }, + { + name: "executionInstallDelegate", + type: "address", + internalType: "contract ExecutionInstallDelegate", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "fallback", + stateMutability: "payable", + }, + { + type: "receive", + stateMutability: "payable", + }, + { + type: "function", + name: "accountId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "entryPoint", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IEntryPoint", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "execute", + inputs: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "result", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "executeBatch", + inputs: [ + { + name: "calls", + type: "tuple[]", + internalType: "struct Call[]", + components: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [ + { + name: "results", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "executeUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "executeWithRuntimeValidation", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + { + name: "authorization", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "getExecutionData", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "data", + type: "tuple", + internalType: "struct ExecutionDataView", + components: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + { + name: "executionHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getValidationData", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + outputs: [ + { + name: "data", + type: "tuple", + internalType: "struct ValidationDataView", + components: [ + { + name: "validationFlags", + type: "uint8", + internalType: "ValidationFlags", + }, + { + name: "validationHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + { + name: "executionHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initializeWithValidation", + inputs: [ + { + name: "validationConfig", + type: "bytes25", + internalType: "ValidationConfig", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + { + name: "installData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hooks", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "installExecution", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + { + name: "moduleInstallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "installValidation", + inputs: [ + { + name: "validationConfig", + type: "bytes25", + internalType: "ValidationConfig", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + { + name: "installData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hooks", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + name: "hash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "onERC1155BatchReceived", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onERC1155Received", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onERC721Received", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "performCreate", + inputs: [ + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "isCreate2", + type: "bool", + internalType: "bool", + }, + { + name: "salt", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "createdAddr", + type: "address", + internalType: "address", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "proxiableUUID", + inputs: [], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "uninstallExecution", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + { + name: "moduleUninstallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "uninstallValidation", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + { + name: "uninstallData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hookUninstallData", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "upgradeToAndCall", + inputs: [ + { + name: "newImplementation", + type: "address", + internalType: "address", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "userOpHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "missingAccountFunds", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "validationData", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "ExecutionInstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + indexed: false, + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ExecutionUninstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "onUninstallSucceeded", + type: "bool", + indexed: false, + internalType: "bool", + }, + { + name: "manifest", + type: "tuple", + indexed: false, + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Initialized", + inputs: [ + { + name: "version", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Upgraded", + inputs: [ + { + name: "implementation", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ValidationInstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ValidationUninstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "onUninstallSucceeded", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ArrayLengthMismatch", + inputs: [], + }, + { + type: "error", + name: "CreateFailed", + inputs: [], + }, + { + type: "error", + name: "DeferredActionSignatureInvalid", + inputs: [], + }, + { + type: "error", + name: "DeferredValidationHasValidationHooks", + inputs: [], + }, + { + type: "error", + name: "ExecutionHookAlreadySet", + inputs: [ + { + name: "hookConfig", + type: "bytes25", + internalType: "HookConfig", + }, + ], + }, + { + type: "error", + name: "InterfaceNotSupported", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "InvalidInitialization", + inputs: [], + }, + { + type: "error", + name: "ModuleInstallCallbackFailed", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "revertReason", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + type: "error", + name: "NonCanonicalEncoding", + inputs: [], + }, + { + type: "error", + name: "NotEntryPoint", + inputs: [], + }, + { + type: "error", + name: "PreValidationHookDuplicate", + inputs: [], + }, + { + type: "error", + name: "RequireUserOperationContext", + inputs: [], + }, + { + type: "error", + name: "SegmentOutOfOrder", + inputs: [], + }, + { + type: "error", + name: "SelfCallRecursionDepthExceeded", + inputs: [], + }, + { + type: "error", + name: "SignatureValidationInvalid", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "UnauthorizedCallContext", + inputs: [], + }, + { + type: "error", + name: "UnexpectedAggregator", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + { + name: "aggregator", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "UnrecognizedFunction", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "UpgradeFailed", + inputs: [], + }, + { + type: "error", + name: "UserOpValidationInvalid", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "ValidationAlreadySet", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "ValidationAssocHookLimitExceeded", + inputs: [], + }, + { + type: "error", + name: "ValidationEntityIdInUse", + inputs: [], + }, + { + type: "error", + name: "ValidationFunctionMissing", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "ValidationSignatureSegmentMissing", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts new file mode 100644 index 0000000000..8f2c3dd7cd --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts @@ -0,0 +1,1288 @@ +export const semiModularAccountBytecodeAbi = [ + { + type: "constructor", + inputs: [ + { + name: "entryPoint", + type: "address", + internalType: "contract IEntryPoint", + }, + { + name: "executionInstallDelegate", + type: "address", + internalType: "contract ExecutionInstallDelegate", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "fallback", + stateMutability: "payable", + }, + { + type: "receive", + stateMutability: "payable", + }, + { + type: "function", + name: "accountId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "entryPoint", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IEntryPoint", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "execute", + inputs: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "result", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "executeBatch", + inputs: [ + { + name: "calls", + type: "tuple[]", + internalType: "struct Call[]", + components: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [ + { + name: "results", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "executeUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "executeWithRuntimeValidation", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + { + name: "authorization", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "getExecutionData", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "data", + type: "tuple", + internalType: "struct ExecutionDataView", + components: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + { + name: "executionHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getFallbackSignerData", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getValidationData", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + outputs: [ + { + name: "data", + type: "tuple", + internalType: "struct ValidationDataView", + components: [ + { + name: "validationFlags", + type: "uint8", + internalType: "ValidationFlags", + }, + { + name: "validationHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + { + name: "executionHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "installExecution", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + { + name: "moduleInstallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "installValidation", + inputs: [ + { + name: "validationConfig", + type: "bytes25", + internalType: "ValidationConfig", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + { + name: "installData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hooks", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + name: "hash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "onERC1155BatchReceived", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onERC1155Received", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onERC721Received", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "performCreate", + inputs: [ + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "isCreate2", + type: "bool", + internalType: "bool", + }, + { + name: "salt", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "createdAddr", + type: "address", + internalType: "address", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "proxiableUUID", + inputs: [], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "uninstallExecution", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + { + name: "moduleUninstallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "uninstallValidation", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + { + name: "uninstallData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hookUninstallData", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "updateFallbackSignerData", + inputs: [ + { + name: "fallbackSigner", + type: "address", + internalType: "address", + }, + { + name: "isDisabled", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "upgradeToAndCall", + inputs: [ + { + name: "newImplementation", + type: "address", + internalType: "address", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "userOpHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "missingAccountFunds", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "validationData", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "ExecutionInstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + indexed: false, + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ExecutionUninstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "onUninstallSucceeded", + type: "bool", + indexed: false, + internalType: "bool", + }, + { + name: "manifest", + type: "tuple", + indexed: false, + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "FallbackSignerUpdated", + inputs: [ + { + name: "newFallbackSigner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "isDisabled", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Initialized", + inputs: [ + { + name: "version", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Upgraded", + inputs: [ + { + name: "implementation", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ValidationInstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ValidationUninstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "onUninstallSucceeded", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ArrayLengthMismatch", + inputs: [], + }, + { + type: "error", + name: "CreateFailed", + inputs: [], + }, + { + type: "error", + name: "DeferredActionSignatureInvalid", + inputs: [], + }, + { + type: "error", + name: "DeferredValidationHasValidationHooks", + inputs: [], + }, + { + type: "error", + name: "ExecutionHookAlreadySet", + inputs: [ + { + name: "hookConfig", + type: "bytes25", + internalType: "HookConfig", + }, + ], + }, + { + type: "error", + name: "FallbackSignerDisabled", + inputs: [], + }, + { + type: "error", + name: "FallbackSignerMismatch", + inputs: [], + }, + { + type: "error", + name: "FallbackValidationInstallationNotAllowed", + inputs: [], + }, + { + type: "error", + name: "InterfaceNotSupported", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "InvalidInitialization", + inputs: [], + }, + { + type: "error", + name: "InvalidSignatureType", + inputs: [], + }, + { + type: "error", + name: "ModuleInstallCallbackFailed", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "revertReason", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + type: "error", + name: "NonCanonicalEncoding", + inputs: [], + }, + { + type: "error", + name: "NotEntryPoint", + inputs: [], + }, + { + type: "error", + name: "PreValidationHookDuplicate", + inputs: [], + }, + { + type: "error", + name: "RequireUserOperationContext", + inputs: [], + }, + { + type: "error", + name: "SegmentOutOfOrder", + inputs: [], + }, + { + type: "error", + name: "SelfCallRecursionDepthExceeded", + inputs: [], + }, + { + type: "error", + name: "SignatureValidationInvalid", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "UnauthorizedCallContext", + inputs: [], + }, + { + type: "error", + name: "UnexpectedAggregator", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + { + name: "aggregator", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "UnrecognizedFunction", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "UpgradeFailed", + inputs: [], + }, + { + type: "error", + name: "UserOpValidationInvalid", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "ValidationAlreadySet", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "ValidationAssocHookLimitExceeded", + inputs: [], + }, + { + type: "error", + name: "ValidationEntityIdInUse", + inputs: [], + }, + { + type: "error", + name: "ValidationFunctionMissing", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "ValidationSignatureSegmentMissing", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts new file mode 100644 index 0000000000..aa320f38ab --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts @@ -0,0 +1,1301 @@ +export const semiModularAccountStorageAbi = [ + { + type: "constructor", + inputs: [ + { + name: "entryPoint", + type: "address", + internalType: "contract IEntryPoint", + }, + { + name: "executionInstallDelegate", + type: "address", + internalType: "contract ExecutionInstallDelegate", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "fallback", + stateMutability: "payable", + }, + { + type: "receive", + stateMutability: "payable", + }, + { + type: "function", + name: "accountId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "entryPoint", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IEntryPoint", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "execute", + inputs: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "result", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "executeBatch", + inputs: [ + { + name: "calls", + type: "tuple[]", + internalType: "struct Call[]", + components: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [ + { + name: "results", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "executeUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "executeWithRuntimeValidation", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + { + name: "authorization", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "getExecutionData", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "data", + type: "tuple", + internalType: "struct ExecutionDataView", + components: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + { + name: "executionHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getFallbackSignerData", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getValidationData", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + outputs: [ + { + name: "data", + type: "tuple", + internalType: "struct ValidationDataView", + components: [ + { + name: "validationFlags", + type: "uint8", + internalType: "ValidationFlags", + }, + { + name: "validationHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + { + name: "executionHooks", + type: "bytes25[]", + internalType: "HookConfig[]", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initialize", + inputs: [ + { + name: "initialSigner", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "installExecution", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + { + name: "moduleInstallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "installValidation", + inputs: [ + { + name: "validationConfig", + type: "bytes25", + internalType: "ValidationConfig", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + { + name: "installData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hooks", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + name: "hash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "onERC1155BatchReceived", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onERC1155Received", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onERC721Received", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "performCreate", + inputs: [ + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "isCreate2", + type: "bool", + internalType: "bool", + }, + { + name: "salt", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "createdAddr", + type: "address", + internalType: "address", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "proxiableUUID", + inputs: [], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "uninstallExecution", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + { + name: "moduleUninstallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "uninstallValidation", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + { + name: "uninstallData", + type: "bytes", + internalType: "bytes", + }, + { + name: "hookUninstallData", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "updateFallbackSignerData", + inputs: [ + { + name: "fallbackSigner", + type: "address", + internalType: "address", + }, + { + name: "isDisabled", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "upgradeToAndCall", + inputs: [ + { + name: "newImplementation", + type: "address", + internalType: "address", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "userOpHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "missingAccountFunds", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "validationData", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "ExecutionInstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "manifest", + type: "tuple", + indexed: false, + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ExecutionUninstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "onUninstallSucceeded", + type: "bool", + indexed: false, + internalType: "bool", + }, + { + name: "manifest", + type: "tuple", + indexed: false, + internalType: "struct ExecutionManifest", + components: [ + { + name: "executionFunctions", + type: "tuple[]", + internalType: "struct ManifestExecutionFunction[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "skipRuntimeValidation", + type: "bool", + internalType: "bool", + }, + { + name: "allowGlobalValidation", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "executionHooks", + type: "tuple[]", + internalType: "struct ManifestExecutionHook[]", + components: [ + { + name: "executionSelector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "isPreHook", + type: "bool", + internalType: "bool", + }, + { + name: "isPostHook", + type: "bool", + internalType: "bool", + }, + ], + }, + { + name: "interfaceIds", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "FallbackSignerUpdated", + inputs: [ + { + name: "newFallbackSigner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "isDisabled", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Initialized", + inputs: [ + { + name: "version", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Upgraded", + inputs: [ + { + name: "implementation", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ValidationInstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ValidationUninstalled", + inputs: [ + { + name: "module", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "onUninstallSucceeded", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ArrayLengthMismatch", + inputs: [], + }, + { + type: "error", + name: "CreateFailed", + inputs: [], + }, + { + type: "error", + name: "DeferredActionSignatureInvalid", + inputs: [], + }, + { + type: "error", + name: "DeferredValidationHasValidationHooks", + inputs: [], + }, + { + type: "error", + name: "ExecutionHookAlreadySet", + inputs: [ + { + name: "hookConfig", + type: "bytes25", + internalType: "HookConfig", + }, + ], + }, + { + type: "error", + name: "FallbackSignerDisabled", + inputs: [], + }, + { + type: "error", + name: "FallbackSignerMismatch", + inputs: [], + }, + { + type: "error", + name: "FallbackValidationInstallationNotAllowed", + inputs: [], + }, + { + type: "error", + name: "InterfaceNotSupported", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "InvalidInitialization", + inputs: [], + }, + { + type: "error", + name: "InvalidSignatureType", + inputs: [], + }, + { + type: "error", + name: "ModuleInstallCallbackFailed", + inputs: [ + { + name: "module", + type: "address", + internalType: "address", + }, + { + name: "revertReason", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + type: "error", + name: "NonCanonicalEncoding", + inputs: [], + }, + { + type: "error", + name: "NotEntryPoint", + inputs: [], + }, + { + type: "error", + name: "PreValidationHookDuplicate", + inputs: [], + }, + { + type: "error", + name: "RequireUserOperationContext", + inputs: [], + }, + { + type: "error", + name: "SegmentOutOfOrder", + inputs: [], + }, + { + type: "error", + name: "SelfCallRecursionDepthExceeded", + inputs: [], + }, + { + type: "error", + name: "SignatureValidationInvalid", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "UnauthorizedCallContext", + inputs: [], + }, + { + type: "error", + name: "UnexpectedAggregator", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + { + name: "aggregator", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "UnrecognizedFunction", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "UpgradeFailed", + inputs: [], + }, + { + type: "error", + name: "UserOpValidationInvalid", + inputs: [ + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "ValidationAlreadySet", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "validationFunction", + type: "bytes24", + internalType: "ModuleEntity", + }, + ], + }, + { + type: "error", + name: "ValidationAssocHookLimitExceeded", + inputs: [], + }, + { + type: "error", + name: "ValidationEntityIdInUse", + inputs: [], + }, + { + type: "error", + name: "ValidationFunctionMissing", + inputs: [ + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "ValidationSignatureSegmentMissing", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts new file mode 100644 index 0000000000..9e1290779b --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -0,0 +1,158 @@ +import type { + EntryPointDef, + SmartAccountSigner, + SmartContractAccountWithSigner, + ToSmartContractAccountParams, +} from "@aa-sdk/core"; +import { + createBundlerClient, + getAccountAddress, + getEntryPoint, + toSmartContractAccount, +} from "@aa-sdk/core"; +import { + concatHex, + encodeFunctionData, + getContract, + type Address, + type Chain, + type Hex, + type Transport, +} from "viem"; +import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; +import { addresses } from "../utils.js"; +import { standardExecutor } from "../../msca/account/standardExecutor.js"; +import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; + +export const DEFAULT_OWNER_ENTITY_ID = 0; + +export type SMAV2Account< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0">; + +export type CreateSMAV2AccountParams< + TTransport extends Transport = Transport, + TSigner extends SmartAccountSigner = SmartAccountSigner, + TEntryPointVersion extends "0.7.0" = "0.7.0" +> = Pick< + ToSmartContractAccountParams< + "SMAV2Account", + TTransport, + Chain, + TEntryPointVersion + >, + "transport" | "chain" +> & { + signer: TSigner; + salt?: bigint; + factoryAddress?: Address; + initCode?: Hex; + initialOwner?: Address; + accountAddress?: Address; + entryPoint?: EntryPointDef; + isGlobalValidation?: boolean; + entityId?: bigint; +}; + +export async function createSMAV2Account< + TTransport extends Transport = Transport, + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + config: CreateSMAV2AccountParams +): Promise>; + +export async function createSMAV2Account( + config: CreateSMAV2AccountParams +): Promise { + const { + transport, + chain, + signer, + salt = 0n, + factoryAddress = addresses.accountFactory, + initCode, + initialOwner, + accountAddress, + entryPoint = getEntryPoint(chain, { version: "0.7.0" }), + isGlobalValidation = true, + entityId = 0n, + } = config; + + if (entityId >= 2n ** 32n) { + throw new Error("Entity ID must be less than uint32.max"); + } + + const client = createBundlerClient({ + transport, + chain, + }); + + const getAccountInitCode = async () => { + if (initCode) { + return initCode; + } + + // If an initial owner is not provided, use the signer's address + const ownerAddress = initialOwner || (await signer.getAddress()); + + return concatHex([ + factoryAddress, + encodeFunctionData({ + abi: accountFactoryAbi, + functionName: "createAccount", + args: [ownerAddress, salt, DEFAULT_OWNER_ENTITY_ID], + }), + ]); + }; + + const _accountAddress = await getAccountAddress({ + client, + entryPoint, + accountAddress, + getAccountInitCode, + }); + + const baseAccount = await toSmartContractAccount({ + transport, + chain, + entryPoint, + accountAddress: _accountAddress, + source: `SMAV2Account`, + getAccountInitCode, + ...standardExecutor, + ...singleSignerMessageSigner(signer), + }); + + // TODO: add deferred action flag + const getAccountNonce = async (nonceKey?: bigint): Promise => { + // uint32 entityId + (bytes1 options) makes a uint40 + const nonceKeySuffix: bigint = + entityId * 256n + (isGlobalValidation ? 1n : 0n); + + if (nonceKey) { + // comparing the end 40 bytes to suffix + if (nonceKey % 2n ** 40n !== nonceKeySuffix) { + throw new Error("Invalid nonceKey"); + } + } else { + nonceKey = nonceKeySuffix; + } + + const entryPointContract = getContract({ + address: entryPoint.address, + abi: entryPoint.abi, + client, + }); + + return entryPointContract.read.getNonce([ + _accountAddress, + nonceKey, + ]) as Promise; + }; + + return { + ...baseAccount, + getAccountNonce, + getSigner: () => signer, + }; +} diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts new file mode 100644 index 0000000000..b78fb4a679 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -0,0 +1,64 @@ +import { custom, parseEther, publicActions } from "viem"; + +import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; + +import { createSMAV2AccountClient } from "./client.js"; + +import { local070InstanceOptSep } from "~test/instances.js"; +import { setBalance } from "viem/actions"; +import { accounts } from "~test/constants.js"; + +describe("MA v2 Tests", async () => { + const instance = local070InstanceOptSep; + const client = instance.getClient().extend(publicActions); + + const signer: SmartAccountSigner = new LocalAccountSigner( + accounts.fundedAccountOwner + ); + + it("sends a simple UO", async () => { + const provider = await givenConnectedProvider({ signer }); + + await setBalance(instance.getClient(), { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); + + const startingAddressBalance = await client.getBalance({ + address: target, + }); + + const result = await provider.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + const txnHash1 = provider.waitForUserOperationTransaction(result); + await expect(txnHash1).resolves.not.toThrowError(); + + const newAddressBalance = await client.getBalance({ + address: target, + }); + + await expect(newAddressBalance).toEqual( + startingAddressBalance + sendAmount + ); + }); + + const givenConnectedProvider = async ({ + signer, + }: { + signer: SmartAccountSigner; + }) => + createSMAV2AccountClient({ + chain: instance.chain, + signer, + transport: custom(instance.getClient()), + }); +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts new file mode 100644 index 0000000000..701ff5e55d --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -0,0 +1,73 @@ +import { + createSmartAccountClient, + type SmartAccountClient, + type SmartAccountSigner, +} from "@aa-sdk/core"; +import { type Chain, type CustomTransport, type Transport } from "viem"; + +import type { SmartAccountClientConfig } from "@aa-sdk/core"; + +import { + createSMAV2Account, + type CreateSMAV2AccountParams, + type SMAV2Account, +} from "../account/account.js"; + +export type CreateSMAV2AccountClientParams< + TTransport extends Transport = Transport, + TChain extends Chain = Chain, + TSigner extends SmartAccountSigner = SmartAccountSigner +> = CreateSMAV2AccountParams & + Omit< + SmartAccountClientConfig, + "transport" | "account" | "chain" + >; + +export function createSMAV2AccountClient< + TChain extends Chain = Chain, + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + args: CreateSMAV2AccountClientParams +): Promise>>; + +/** + * Creates a MAv2 account client using the provided configuration parameters. + * + * @example + * ```ts + * import { http } from "viem"; + * import { createSMAV2AccountClient } from "@account-kit/smart-contracts"; + * import { LocalAccountSigner } from "@aa-sdk/core"; + * import { sepolia } from "@account-kit/infra"; + * + * const MNEMONIC = "..."; + * const RPC_URL = "..."; + * + * const signer = LocalAccountSigner.mnemonicToAccountSigner(MNEMONIC); + * + * const chain = sepolia; + * + * const transport = http(RPC_URL); + * + * const SMAV2SignerAccountClient = await createSMAV2AccountClient({ + * chain, + * signer, + * transport, + * }); + * ``` + * + * @param {CreateSMAV2AccountClientParams} config The configuration parameters required to create the MAv2 account client + * @returns {Promise} A promise that resolves to a `SmartAccountClient` instance + */ +export async function createSMAV2AccountClient({ + ...config +}: CreateSMAV2AccountClientParams): Promise { + const maV2Account = await createSMAV2Account({ + ...config, + }); + + return createSmartAccountClient({ + ...config, + account: maV2Account, + }); +} diff --git a/account-kit/smart-contracts/src/ma-v2/index.ts b/account-kit/smart-contracts/src/ma-v2/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModule.ts new file mode 100644 index 0000000000..270e36dc13 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModule.ts @@ -0,0 +1,715 @@ +export const allowlistModuleAbi = [ + { + type: "function", + name: "addressAllowlist", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "allowed", + type: "bool", + internalType: "bool", + }, + { + name: "hasSelectorAllowlist", + type: "bool", + internalType: "bool", + }, + { + name: "hasERC20SpendLimit", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "checkAllowlistCalldata", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "deleteAllowlist", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "inputs", + type: "tuple[]", + internalType: "struct AllowlistModule.AllowlistInput[]", + components: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "hasSelectorAllowlist", + type: "bool", + internalType: "bool", + }, + { + name: "hasERC20SpendLimit", + type: "bool", + internalType: "bool", + }, + { + name: "erc20SpendLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "erc20SpendLimits", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "moduleId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onInstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onUninstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "postExecutionHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preExecutionHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "preRuntimeValidationHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "preSignatureValidationHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preUserOpValidationHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "selectorAllowlist", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "setAddressAllowlist", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "allowed", + type: "bool", + internalType: "bool", + }, + { + name: "hasSelectorAllowlist", + type: "bool", + internalType: "bool", + }, + { + name: "hasERC20SpendLimit", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setSelectorAllowlist", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "selector", + type: "bytes4", + internalType: "bytes4", + }, + { + name: "allowed", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "updateAllowlist", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "inputs", + type: "tuple[]", + internalType: "struct AllowlistModule.AllowlistInput[]", + components: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "hasSelectorAllowlist", + type: "bool", + internalType: "bool", + }, + { + name: "hasERC20SpendLimit", + type: "bool", + internalType: "bool", + }, + { + name: "erc20SpendLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "selectors", + type: "bytes4[]", + internalType: "bytes4[]", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "updateLimits", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "hasERC20SpendLimit", + type: "bool", + internalType: "bool", + }, + { + name: "newLimit", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "AddressAllowlistUpdated", + inputs: [ + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "target", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entry", + type: "tuple", + indexed: false, + internalType: "struct AllowlistModule.AddressAllowlistEntry", + components: [ + { + name: "allowed", + type: "bool", + internalType: "bool", + }, + { + name: "hasSelectorAllowlist", + type: "bool", + internalType: "bool", + }, + { + name: "hasERC20SpendLimit", + type: "bool", + internalType: "bool", + }, + ], + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ERC20SpendLimitUpdated", + inputs: [ + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "token", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newLimit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "SelectorAllowlistUpdated", + inputs: [ + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "targetAndSelector", + type: "bytes24", + indexed: true, + internalType: "bytes24", + }, + { + name: "allowed", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "AddressNotAllowed", + inputs: [], + }, + { + type: "error", + name: "ERC20NotAllowed", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ExceededTokenLimit", + inputs: [], + }, + { + type: "error", + name: "InvalidCalldataLength", + inputs: [], + }, + { + type: "error", + name: "NoSelectorSpecified", + inputs: [], + }, + { + type: "error", + name: "NotImplemented", + inputs: [], + }, + { + type: "error", + name: "SelectorNotAllowed", + inputs: [], + }, + { + type: "error", + name: "SpendingRequestNotAllowed", + inputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + }, + { + type: "error", + name: "UnexpectedDataPassed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModule.ts new file mode 100644 index 0000000000..de0f1d2aa1 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModule.ts @@ -0,0 +1,403 @@ +export const nativeTokenLimitModuleAbi = [ + { + type: "function", + name: "limits", + inputs: [ + { + name: "entityId", + type: "uint256", + internalType: "uint256", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "limit", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "moduleId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onInstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onUninstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "postExecutionHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preExecutionHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "preRuntimeValidationHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preSignatureValidationHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preUserOpValidationHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "specialPaymasters", + inputs: [ + { + name: "paymaster", + type: "address", + internalType: "address", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "allowed", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "updateLimits", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "newLimit", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "updateSpecialPaymaster", + inputs: [ + { + name: "paymaster", + type: "address", + internalType: "address", + }, + { + name: "allowed", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "NativeTokenSpendLimitUpdated", + inputs: [ + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newLimit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "SpecialPaymasterUpdated", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "paymaster", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "allowed", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ExceededNativeTokenLimit", + inputs: [], + }, + { + type: "error", + name: "InvalidPaymaster", + inputs: [], + }, + { + type: "error", + name: "NotImplemented", + inputs: [], + }, + { + type: "error", + name: "UnexpectedDataPassed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModule.ts new file mode 100644 index 0000000000..275480aab0 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModule.ts @@ -0,0 +1,241 @@ +export const paymasterGuardModuleAbi = [ + { + type: "function", + name: "moduleId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onInstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onUninstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "paymasters", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "paymaster", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "preRuntimeValidationHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "preSignatureValidationHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preUserOpValidationHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "error", + name: "BadPaymasterSpecified", + inputs: [], + }, + { + type: "error", + name: "InvalidPaymaster", + inputs: [], + }, + { + type: "error", + name: "NotImplemented", + inputs: [], + }, + { + type: "error", + name: "UnexpectedDataPassed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModule.ts new file mode 100644 index 0000000000..d18325cd52 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModule.ts @@ -0,0 +1,330 @@ +export const singleSignerValidationModuleAbi = [ + { + type: "function", + name: "moduleId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onInstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onUninstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "replaySafeHash", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + { + name: "hash", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "signers", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "transferSigner", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "newSigner", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "validateRuntime", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "validateSignature", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "digest", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "userOpHash", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "event", + name: "SignerTransferred", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "newSigner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "previousSigner", + type: "address", + indexed: false, + internalType: "address", + }, + ], + anonymous: true, + }, + { + type: "error", + name: "InvalidSignatureType", + inputs: [], + }, + { + type: "error", + name: "NotAuthorized", + inputs: [], + }, + { + type: "error", + name: "NotImplemented", + inputs: [], + }, + { + type: "error", + name: "UnexpectedDataPassed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts new file mode 100644 index 0000000000..599f29ba9b --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts @@ -0,0 +1,49 @@ +import { encodeAbiParameters, type Address, type Hex } from "viem"; + +import { singleSignerValidationModuleAbi } from "./abis/singleSignerValidationModule.js"; + +const addresses = { + default: "0xEa3a0b544d517f6Ed3Dc2186C74D869c702C376e", +} as Record; + +const meta = { + name: "SingleSignerValidation", + version: "alpha.1", + addresses, +}; + +// Todo: some unified type for ERC-6900 v0.8 modules. I couldn't figure out how to parameterize the class itself over the abi type parameters for onInstall and onUninstall. +export const SingleSignerValidationModule = { + meta, + abi: singleSignerValidationModuleAbi, + encodeOnInstallData: (args: { entityId: number; signer: Address }): Hex => { + const { entityId, signer } = args; + + return encodeAbiParameters( + [ + { + type: "uint32", + value: entityId, + }, + { + type: "address", + value: signer, + }, + ], + [entityId, signer] + ); + }, + encodeOnUninstallData: (args: { entityId: number }): Hex => { + const { entityId } = args; + + return encodeAbiParameters( + [ + { + type: "uint32", + value: entityId, + }, + ], + [entityId] + ); + }, +}; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts new file mode 100644 index 0000000000..fd1dcd8a33 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts @@ -0,0 +1,74 @@ +import type { SmartAccountSigner } from "@aa-sdk/core"; +import { + hashMessage, + hashTypedData, + type Hex, + type SignableMessage, + type TypedData, + type TypedDataDefinition, +} from "viem"; + +import { packSignature } from "../../utils.js"; +/** + * Creates an object with methods for generating a dummy signature, signing user operation hashes, signing messages, and signing typed data. + * + * @example + * ```ts + * import { singleSignerMessageSigner } from "@account-kit/smart-contracts"; + * import { LocalAccountSigner } from "@aa-sdk/core"; + * + * const MNEMONIC = "...": + * + * const signer = LocalAccountSigner.mnemonicToAccountSigner(MNEMONIC); + * + * const messageSigner = singleSignerMessageSigner(signer, chain); + * ``` + * + * @param {TSigner} signer the signer to use for signing operations + * @returns {object} an object with methods for signing operations and managing signatures + */ +export const singleSignerMessageSigner = ( + signer: TSigner +) => { + return { + getDummySignature: (): Hex => { + const dummyEcdsaSignature = + "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; + + return packSignature({ + // orderedHookData: [], + validationSignature: dummyEcdsaSignature, + }); + }, + + signUserOperationHash: (uoHash: `0x${string}`): Promise<`0x${string}`> => { + return signer.signMessage({ raw: uoHash }).then((signature: Hex) => + packSignature({ + // orderedHookData: [], + validationSignature: signature, + }) + ); + }, + + // TODO: we can't implement these methods yet, because the RI at `alpha.0` doesn't have a wrapping type, + // and viem doesn't support raw signing, only via EIP-191 or EIP-712. + // When we do implement this, we need to prefix the data with the validation module address & entityId. + + signMessage({ + message, + }: { + message: SignableMessage; + }): Promise<`0x${string}`> { + return signer.signMessage({ raw: hashMessage(message) }); + }, + + signTypedData: < + const typedData extends TypedData | Record, + primaryType extends keyof typedData | "EIP712Domain" = keyof typedData + >( + typedDataDefinition: TypedDataDefinition + ): Promise => { + return signer.signMessage({ raw: hashTypedData(typedDataDefinition) }); + }, + }; +}; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModule.ts new file mode 100644 index 0000000000..e4fe309ca3 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModule.ts @@ -0,0 +1,295 @@ +export const timeRangeModuleAbi = [ + { + type: "function", + name: "moduleId", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "onInstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onUninstall", + inputs: [ + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "preRuntimeValidationHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "preSignatureValidationHook", + inputs: [ + { + name: "", + type: "uint32", + internalType: "uint32", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "pure", + }, + { + type: "function", + name: "preUserOpValidationHook", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "setTimeRange", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "validUntil", + type: "uint48", + internalType: "uint48", + }, + { + name: "validAfter", + type: "uint48", + internalType: "uint48", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "timeRanges", + inputs: [ + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "validUntil", + type: "uint48", + internalType: "uint48", + }, + { + name: "validAfter", + type: "uint48", + internalType: "uint48", + }, + ], + stateMutability: "view", + }, + { + type: "event", + name: "TimeRangeSet", + inputs: [ + { + name: "entityId", + type: "uint32", + indexed: true, + internalType: "uint32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "validUntil", + type: "uint48", + indexed: false, + internalType: "uint48", + }, + { + name: "validAfter", + type: "uint48", + indexed: false, + internalType: "uint48", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "NotImplemented", + inputs: [], + }, + { + type: "error", + name: "TimeRangeNotValid", + inputs: [], + }, + { + type: "error", + name: "UnexpectedDataPassed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidation.ts b/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidation.ts new file mode 100644 index 0000000000..ac35abfc5d --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidation.ts @@ -0,0 +1,373 @@ +export const webauthnValidationModuleAbi = [ + { + type: "constructor", + inputs: [ + { + name: "_entryPoint", + type: "address", + internalType: "contract IEntryPoint", + }, + { + name: "_accountImpl", + type: "address", + internalType: "contract ModularAccount", + }, + { + name: "_webauthnValidationModule", + type: "address", + internalType: "address", + }, + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "ACCOUNT_IMPL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract ModularAccount", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "ENTRY_POINT", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IEntryPoint", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "WEBAUTHN_VALIDATION_MODULE", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "addStake", + inputs: [ + { + name: "unstakeDelay", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "createAccount", + inputs: [ + { + name: "ownerX", + type: "uint256", + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "contract ModularAccount", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getAddress", + inputs: [ + { + name: "ownerX", + type: "uint256", + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getSalt", + inputs: [ + { + name: "ownerX", + type: "uint256", + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + internalType: "uint256", + }, + { + name: "entityId", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "renounceOwnership", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "transferOwnership", + inputs: [ + { + name: "newOwner", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "unlockStake", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdraw", + inputs: [ + { + name: "to", + type: "address", + internalType: "address payable", + }, + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdrawStake", + inputs: [ + { + name: "withdrawAddress", + type: "address", + internalType: "address payable", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "ModularAccountDeployed", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "ownerX", + type: "uint256", + indexed: true, + internalType: "uint256", + }, + { + name: "ownerY", + type: "uint256", + indexed: true, + internalType: "uint256", + }, + { + name: "salt", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OwnershipTransferred", + inputs: [ + { + name: "previousOwner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newOwner", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "AddressEmptyCode", + inputs: [ + { + name: "target", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "AddressInsufficientBalance", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "FailedInnerCall", + inputs: [], + }, + { + type: "error", + name: "OwnableInvalidOwner", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "OwnableUnauthorizedAccount", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "TransferFailed", + inputs: [], + }, +]; diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts new file mode 100644 index 0000000000..f480b5561f --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -0,0 +1,42 @@ +import { type Hex, concat } from "viem"; + +export type PackSignatureParams = { + // orderedHookData: HookData[]; + validationSignature: Hex; +}; + +// Signature packing utility +export const packSignature = ({ + // orderedHookData, TO DO: integrate in next iteration of MAv2 sdk + validationSignature, +}: PackSignatureParams): Hex => { + return concat(["0xFF", "0x00", validationSignature]); +}; + +export const addresses = { + allowlistModule: + "0xE46ca4a98c485caEE2Abb6ef5116292B8c78a868" as `0x${string}`, + nativeTokenLimitModule: + "0xEa6a05306315196f2A7CA2ec7eEA45290bae00A0" as `0x${string}`, + paymasterGuardModule: + "0x976D01aF75D128cae526B5328AC268ac83D607f4" as `0x${string}`, + singleSignerValidationModule: + "0xF56716aE104545BdAf012e14e4640beb52727479" as `0x${string}`, + timeRangeModule: + "0x6FD0a9765a86788126a55aD5a483029a484F996C" as `0x${string}`, + webauthnValidationModule: + "0xCf3423F8EB9EE215560802D03a48cDD44f85bD28" as `0x${string}`, + executionInstallDelegate: + "0x8Bf909fEb66EBcC4725f04E70F319791Ec9d9628" as `0x${string}`, + modularAccountImpl: + "0x99090abd2700E24Cc70E3c486A89F7af876fFA33" as `0x${string}`, + semiModularAccountBytecodeImpl: + "0xDCBb5d4639428B18703801f7Cd7230add729E4b0" as `0x${string}`, + semiModularAccountStorageOnlyImpl: + "0x563ea43Ba0CD9150466B054Ce47FB0Fb45B234E2" as `0x${string}`, + accountFactory: "0xA5FC7aDc6d2c838443dF5bb826152c58b918f2c8" as `0x${string}`, + accountFactoryOwner: + "0x3BD786ac7Dec96eF9d06ebbCFa4e0E0947A2c303" as `0x${string}`, +}; + +export const tempChainId = 11155420; From b11a94c93a86eb6353c6f2cf882460e8a69dd9be Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:35:40 -0500 Subject: [PATCH 02/45] fix: review fixes --- aa-sdk/core/src/errors/client.ts | 35 +++++++++++++++++++ aa-sdk/core/src/index.ts | 2 ++ .../src/ma-v2/account/account.ts | 27 +++++++------- .../InvalidEntityIdError/constructor.mdx | 25 +++++++++++++ .../InvalidNonceKeyError/constructor.mdx | 30 ++++++++++++++++ 5 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx create mode 100644 site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index 9e102637b2..5c42a1e252 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -53,3 +53,38 @@ export class ChainNotFoundError extends BaseError { super("No chain supplied to the client"); } } + +/** + * Error class denoting that the provided entity id is invalid because it's too large. + */ +export class InvalidEntityIdError extends BaseError { + override name = "InvalidEntityIdError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the entity id is invalid because it's too large. + * + * @param {bigint} entityId the invalid entityId used + */ + constructor(entityId: bigint) { + super(`Entity ID used is ${entityId}, but must be less than uint32.max`); + } +} + +/** + * Error class denoting that the provided entity id is invalid because it's too large. + */ +export class InvalidNonceKeyError extends BaseError { + override name = "InvalidNonceKeyError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. + * + * @param {bigint} nonceKey the invalid nonceKey used + * @param {bigint} nonceKeySuffix the expected nonceKeySuffix that must be contained in nonceKey + */ + constructor(nonceKey: bigint, nonceKeySuffix: bigint) { + super( + `Nonce key used is ${nonceKey}, but must contain the nonce key suffix of: ${nonceKeySuffix}` + ); + } +} diff --git a/aa-sdk/core/src/index.ts b/aa-sdk/core/src/index.ts index 3a30e59763..c5d33dea24 100644 --- a/aa-sdk/core/src/index.ts +++ b/aa-sdk/core/src/index.ts @@ -73,6 +73,8 @@ export { ChainNotFoundError, IncompatibleClientError, InvalidRpcUrlError, + InvalidEntityIdError, + InvalidNonceKeyError, } from "./errors/client.js"; export { EntryPointNotFoundError, diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts index 9e1290779b..4211cfe639 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -14,15 +14,19 @@ import { concatHex, encodeFunctionData, getContract, + maxUint32, + toHex, type Address, type Chain, type Hex, type Transport, + hexToBigInt, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; import { addresses } from "../utils.js"; import { standardExecutor } from "../../msca/account/standardExecutor.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; +import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; export const DEFAULT_OWNER_ENTITY_ID = 0; @@ -78,8 +82,8 @@ export async function createSMAV2Account( entityId = 0n, } = config; - if (entityId >= 2n ** 32n) { - throw new Error("Entity ID must be less than uint32.max"); + if (entityId >= maxUint32) { + throw new InvalidEntityIdError(entityId); } const client = createBundlerClient({ @@ -125,17 +129,12 @@ export async function createSMAV2Account( // TODO: add deferred action flag const getAccountNonce = async (nonceKey?: bigint): Promise => { - // uint32 entityId + (bytes1 options) makes a uint40 - const nonceKeySuffix: bigint = - entityId * 256n + (isGlobalValidation ? 1n : 0n); - - if (nonceKey) { - // comparing the end 40 bytes to suffix - if (nonceKey % 2n ** 40n !== nonceKeySuffix) { - throw new Error("Invalid nonceKey"); - } - } else { - nonceKey = nonceKeySuffix; + const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ + isGlobalValidation ? "01" : "00" + }`; + + if (nonceKey && toHex(nonceKey, { size: 5 }) !== nonceKeySuffix) { + throw new InvalidNonceKeyError(nonceKey, hexToBigInt(nonceKeySuffix)); } const entryPointContract = getContract({ @@ -146,7 +145,7 @@ export async function createSMAV2Account( return entryPointContract.read.getNonce([ _accountAddress, - nonceKey, + nonceKey ?? hexToBigInt(nonceKeySuffix), ]) as Promise; }; diff --git a/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx new file mode 100644 index 0000000000..3755d0a81f --- /dev/null +++ b/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx @@ -0,0 +1,25 @@ +--- +# This file is autogenerated +title: InvalidEntityIdError +description: Overview of the InvalidEntityIdError method +--- + +# InvalidEntityIdError + +Initializes a new instance of the error message with a default message indicating that the entity id is invalid because it's too large. +:::note +`InvalidEntityIdError` extends `BaseError`, see the docs for BaseError for all supported methods. +::: + +## Import + +```ts +import { InvalidEntityIdError } from "@aa-sdk/core"; +``` + +## Parameters + +### entityId + +`bigint` +the invalid entityId used diff --git a/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx new file mode 100644 index 0000000000..6919c0032c --- /dev/null +++ b/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx @@ -0,0 +1,30 @@ +--- +# This file is autogenerated +title: InvalidNonceKeyError +description: Overview of the InvalidNonceKeyError method +--- + +# InvalidNonceKeyError + +Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. +:::note +`InvalidNonceKeyError` extends `BaseError`, see the docs for BaseError for all supported methods. +::: + +## Import + +```ts +import { InvalidNonceKeyError } from "@aa-sdk/core"; +``` + +## Parameters + +### nonceKey + +`bigint` +the invalid nonceKey used + +### nonceKeySuffix + +`bigint` +the expected nonceKeySuffix that must be contained in nonceKey From 3f1b3effa643fc6e080f0d67931adb0339377488 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:16:53 -0500 Subject: [PATCH 03/45] fix: some review fixes --- .../smart-contracts/src/ma-v2/account/account.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts index 4211cfe639..26ad7312ef 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -97,7 +97,7 @@ export async function createSMAV2Account( } // If an initial owner is not provided, use the signer's address - const ownerAddress = initialOwner || (await signer.getAddress()); + const ownerAddress = initialOwner ?? (await signer.getAddress()); return concatHex([ factoryAddress, @@ -109,18 +109,11 @@ export async function createSMAV2Account( ]); }; - const _accountAddress = await getAccountAddress({ - client, - entryPoint, - accountAddress, - getAccountInitCode, - }); - const baseAccount = await toSmartContractAccount({ transport, chain, entryPoint, - accountAddress: _accountAddress, + accountAddress, source: `SMAV2Account`, getAccountInitCode, ...standardExecutor, @@ -144,7 +137,7 @@ export async function createSMAV2Account( }); return entryPointContract.read.getNonce([ - _accountAddress, + baseAccount.address, nonceKey ?? hexToBigInt(nonceKeySuffix), ]) as Promise; }; From 76052dc3ae4fd33c3e825528328d5088087eb75f Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:23:58 -0500 Subject: [PATCH 04/45] fix: more review fixes --- .../smart-contracts/src/ma-v2/account/account.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts index 26ad7312ef..ff62e1d8c9 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -6,7 +6,6 @@ import type { } from "@aa-sdk/core"; import { createBundlerClient, - getAccountAddress, getEntryPoint, toSmartContractAccount, } from "@aa-sdk/core"; @@ -36,24 +35,17 @@ export type SMAV2Account< export type CreateSMAV2AccountParams< TTransport extends Transport = Transport, - TSigner extends SmartAccountSigner = SmartAccountSigner, - TEntryPointVersion extends "0.7.0" = "0.7.0" + TSigner extends SmartAccountSigner = SmartAccountSigner > = Pick< - ToSmartContractAccountParams< - "SMAV2Account", - TTransport, - Chain, - TEntryPointVersion - >, - "transport" | "chain" + ToSmartContractAccountParams<"SMAV2Account", TTransport, Chain, "0.7.0">, + "transport" | "chain" | "accountAddress" > & { signer: TSigner; salt?: bigint; factoryAddress?: Address; initCode?: Hex; initialOwner?: Address; - accountAddress?: Address; - entryPoint?: EntryPointDef; + entryPoint?: EntryPointDef<"0.7.0", Chain>; isGlobalValidation?: boolean; entityId?: bigint; }; From 49d67b6d0e70181d6682ac2beb2025177e53d159 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:38:44 -0500 Subject: [PATCH 05/45] chore: rename abi files for clarity --- account-kit/smart-contracts/src/ma-v2/client/client.ts | 3 +-- .../abis/{allowlistModule.ts => allowlistModuleAbi.ts} | 0 ...{nativeTokenLimitModule.ts => nativeTokenLimitModuleAbi.ts} | 0 .../{paymasterGuardModule.ts => paymasterGuardModuleAbi.ts} | 0 ...rValidationModule.ts => singleSignerValidationModuleAbi.ts} | 0 .../src/ma-v2/modules/single-signer-validation/module.ts | 2 +- .../abis/{timeRangeModule.ts => timeRangeModuleAbi.ts} | 0 .../abis/{webauthnValidation.ts => webauthnValidationAbi.ts} | 0 8 files changed, 2 insertions(+), 3 deletions(-) rename account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/{allowlistModule.ts => allowlistModuleAbi.ts} (100%) rename account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/{nativeTokenLimitModule.ts => nativeTokenLimitModuleAbi.ts} (100%) rename account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/{paymasterGuardModule.ts => paymasterGuardModuleAbi.ts} (100%) rename account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/{singleSignerValidationModule.ts => singleSignerValidationModuleAbi.ts} (100%) rename account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/{timeRangeModule.ts => timeRangeModuleAbi.ts} (100%) rename account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/{webauthnValidation.ts => webauthnValidationAbi.ts} (100%) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index 701ff5e55d..f2e3b36335 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -2,11 +2,10 @@ import { createSmartAccountClient, type SmartAccountClient, type SmartAccountSigner, + type SmartAccountClientConfig, } from "@aa-sdk/core"; import { type Chain, type CustomTransport, type Transport } from "viem"; -import type { SmartAccountClientConfig } from "@aa-sdk/core"; - import { createSMAV2Account, type CreateSMAV2AccountParams, diff --git a/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts similarity index 100% rename from account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModule.ts rename to account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts diff --git a/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts similarity index 100% rename from account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModule.ts rename to account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts diff --git a/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModuleAbi.ts similarity index 100% rename from account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModule.ts rename to account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModuleAbi.ts diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModuleAbi.ts similarity index 100% rename from account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModule.ts rename to account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/abis/singleSignerValidationModuleAbi.ts diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts index 599f29ba9b..c865569eaf 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts @@ -1,6 +1,6 @@ import { encodeAbiParameters, type Address, type Hex } from "viem"; -import { singleSignerValidationModuleAbi } from "./abis/singleSignerValidationModule.js"; +import { singleSignerValidationModuleAbi } from "./abis/singleSignerValidationModuleAbi.js"; const addresses = { default: "0xEa3a0b544d517f6Ed3Dc2186C74D869c702C376e", diff --git a/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModule.ts b/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts similarity index 100% rename from account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModule.ts rename to account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts diff --git a/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidation.ts b/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidationAbi.ts similarity index 100% rename from account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidation.ts rename to account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidationAbi.ts From 28926df43869984e43b0c1f3bcf97be13af56c5c Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:27:53 -0500 Subject: [PATCH 06/45] chore: more fixes --- account-kit/smart-contracts/src/ma-v2/client/client.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index b78fb4a679..825403a2b4 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -10,7 +10,11 @@ import { accounts } from "~test/constants.js"; describe("MA v2 Tests", async () => { const instance = local070InstanceOptSep; - const client = instance.getClient().extend(publicActions); + let client: ReturnType; + + beforeAll(async () => { + client = instance.getClient().extend(publicActions); + }); const signer: SmartAccountSigner = new LocalAccountSigner( accounts.fundedAccountOwner From f7ce23631e0efd8b353ae075bf910382a8175ca9 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 13:23:46 -0500 Subject: [PATCH 07/45] chore: more fixes --- .../src/ma-v2/account/account.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts index ff62e1d8c9..fc75fbb622 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -46,9 +46,16 @@ export type CreateSMAV2AccountParams< initCode?: Hex; initialOwner?: Address; entryPoint?: EntryPointDef<"0.7.0", Chain>; - isGlobalValidation?: boolean; - entityId?: bigint; -}; +} & ( + | { + isGlobalValidation: boolean; + entityId: bigint; + } + | { + isGlobalValidation: never; + entityId: never; + } + ); export async function createSMAV2Account< TTransport extends Transport = Transport, @@ -95,8 +102,8 @@ export async function createSMAV2Account( factoryAddress, encodeFunctionData({ abi: accountFactoryAbi, - functionName: "createAccount", - args: [ownerAddress, salt, DEFAULT_OWNER_ENTITY_ID], + functionName: "createSemiModularAccount", + args: [ownerAddress, salt], }), ]); }; From 5bd7bcd284efbd8b65d2dede261283c974910dc0 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:17:46 -0500 Subject: [PATCH 08/45] feat: add install validation, add tests for session key --- aa-sdk/core/src/errors/client.ts | 16 ++ aa-sdk/core/src/index.ts | 1 + .../src/ma-v2/actions/common/types.ts | 37 ++++ .../src/ma-v2/actions/common/utils.ts | 30 ++++ .../install-validation/installValidation.ts | 158 ++++++++++++++++++ .../src/ma-v2/client/client.test.ts | 76 ++++++++- .../EntityIdOverrideError/constructor.mdx | 18 ++ 7 files changed, 327 insertions(+), 9 deletions(-) create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/common/types.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts create mode 100644 site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index 5c42a1e252..165e6373fc 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -88,3 +88,19 @@ export class InvalidNonceKeyError extends BaseError { ); } } + +/** + * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. + */ +export class EntityIdOverrideError extends BaseError { + override name = "InvalidNonceKeyError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. + */ + constructor() { + super( + `Installing entityId of 0 overrides the owner's entity id in the account` + ); + } +} diff --git a/aa-sdk/core/src/index.ts b/aa-sdk/core/src/index.ts index c5d33dea24..5f66aae5cc 100644 --- a/aa-sdk/core/src/index.ts +++ b/aa-sdk/core/src/index.ts @@ -75,6 +75,7 @@ export { InvalidRpcUrlError, InvalidEntityIdError, InvalidNonceKeyError, + EntityIdOverrideError, } from "./errors/client.js"; export { EntryPointNotFoundError, diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts new file mode 100644 index 0000000000..1a133ae0bc --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts @@ -0,0 +1,37 @@ +import type { Address, Hex } from "viem"; + +export type ModuleEntity = { + address: Address; + entityId: number; +}; + +export type ValidationConfig = { + address: Address; + entityId: number; // uint32 + isGlobal: boolean; + isSignatureValidation: boolean; + isUserOpValidation: boolean; +}; + +export enum HookType { + EXECUTION = "0x00", + VALIDATION = "0x01", +} + +export type HookConfig = { + address: Address; + entityId: number; // uint32 + hookType: HookType; + hasPreHooks: boolean; + hasPostHooks: boolean; +}; + +// maps to type ValidationStorage in MAv2 implementation +export type ValidationData = { + isGlobal: boolean; // validation flag + isSignatureValidation: boolean; // validation flag + isUserOpValidation: boolean; + validationHooks: HookConfig[]; + executionHooks: Hex[]; + selectors: Hex[]; +}; diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts new file mode 100644 index 0000000000..2d79719bce --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -0,0 +1,30 @@ +import { type Hex, toHex, concatHex } from "viem"; +import type { ValidationConfig, HookConfig, ModuleEntity } from "./types"; + +export function serializeValidationConfig(config: ValidationConfig): Hex { + const isUserOpValidationBit = config.isUserOpValidation ? 1 : 0; + const isSignatureValidationBit = config.isSignatureValidation ? 2 : 0; + const isGlobalBit = config.isGlobal ? 4 : 0; + return concatHex([ + serializeModuleEntity(config), + toHex(isUserOpValidationBit + isSignatureValidationBit + isGlobalBit, { + size: 1, + }), + ]); +} + +export function serializeHookConfig(config: HookConfig): Hex { + return concatHex([ + config.address, + toHex(config.entityId, { size: 4 }), + config.hookType, + // hasPostHook is stored in the first bit, hasPreHook in the second bit + toHex((config.hasPostHooks ? 1 : 0) + (config.hasPreHooks ? 2 : 0), { + size: 1, + }), + ]); +} + +export function serializeModuleEntity(config: ModuleEntity): Hex { + return concatHex([config.address, toHex(config.entityId, { size: 4 })]); +} diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts new file mode 100644 index 0000000000..6f2e50cb20 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -0,0 +1,158 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + EntityIdOverrideError, + type GetAccountParameter, + type GetEntryPointFromAccount, + type SendUserOperationResult, + type SmartContractAccount, + type SmartAccountClient, + type UserOperationOverridesParameter, +} from "@aa-sdk/core"; +import { + type Address, + type Hex, + type Chain, + type Transport, + encodeFunctionData, + concatHex, +} from "viem"; + +import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; +import type { HookConfig, ValidationConfig } from "../common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, +} from "../common/utils.js"; + +export type InstallValidationParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type UninstallValidationParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type InstallValidationActions< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export const installValidationActions: < + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends SmartContractAccount = SmartContractAccount +>( + client: SmartAccountClient +) => InstallValidationActions = (client) => ({ + installValidation: async ({ + validationConfig, + selectors, + installData, + hooks, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "installValidation", + client + ); + } + + if (validationConfig.entityId === 0) { + throw new EntityIdOverrideError(); + } + + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, + + uninstallValidation: async ({ + // moduleAddress, entityId, uninstallData, hookUninstallDatas, + account = client.account, + overrides, + }) => { + // if (!account) { + // throw new AccountNotFoundError(); + // } + + // if (!isSmartAccountClient(client)) { + // throw new IncompatibleClientError( + // "SmartAccountClient", + // "uninstallValidation", + // client + // ); + // } + + // const { moduleAddress, entityId, uninstallData, hookUninstallDatas } = args; + + // const callData = encodeFunctionData({ + // abi: semiModularAccountBytecodeAbi, + // functionName: "uninstallValidation", + // args: [ + // serializeModuleEntity({ + // moduleAddress, + // entityId, + // }), + // uninstallData, + // hookUninstallDatas, + // ], + // }); + + return client.sendUserOperation({ + uo: "0x", + account, + overrides, + }); + }, +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 825403a2b4..50be586aa8 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -7,6 +7,9 @@ import { createSMAV2AccountClient } from "./client.js"; import { local070InstanceOptSep } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; +import { addresses } from "../utils.js"; +import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; describe("MA v2 Tests", async () => { const instance = local070InstanceOptSep; @@ -20,6 +23,19 @@ describe("MA v2 Tests", async () => { accounts.fundedAccountOwner ); + const sessionKey: SmartAccountSigner = new LocalAccountSigner( + accounts.unfundedAccountOwner + ); + + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); + + const getTargetBalance = async (): Promise => { + return client.getBalance({ + address: target, + }); + }; + it("sends a simple UO", async () => { const provider = await givenConnectedProvider({ signer }); @@ -28,12 +44,7 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const target = "0x000000000000000000000000000000000000dEaD"; - const sendAmount = parseEther("1"); - - const startingAddressBalance = await client.getBalance({ - address: target, - }); + const startingAddressBalance = await getTargetBalance(); const result = await provider.sendUserOperation({ uo: { @@ -46,11 +57,58 @@ describe("MA v2 Tests", async () => { const txnHash1 = provider.waitForUserOperationTransaction(result); await expect(txnHash1).resolves.not.toThrowError(); - const newAddressBalance = await client.getBalance({ - address: target, + await expect(await getTargetBalance()).toEqual( + startingAddressBalance + sendAmount + ); + }); + + it("adds a session key with no permissions", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const result = await provider.installValidation({ + validationConfig: { + address: addresses.singleSignerValidationModule, + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + const startingAddressBalance = await getTargetBalance(); + + // connect session key + provider.signer = sessionKey; + provider.entityId = 1n; + + const result2 = await provider.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, }); - await expect(newAddressBalance).toEqual( + txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount ); }); diff --git a/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx new file mode 100644 index 0000000000..ef8e82cc95 --- /dev/null +++ b/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx @@ -0,0 +1,18 @@ +--- +# This file is autogenerated +title: EntityIdOverrideError +description: Overview of the EntityIdOverrideError method +--- + +# EntityIdOverrideError + +Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. +:::note +`EntityIdOverrideError` extends `BaseError`, see the docs for BaseError for all supported methods. +::: + +## Import + +```ts +import { EntityIdOverrideError } from "@aa-sdk/core"; +``` From 9e5c594d925546e58cf6467292b5eed05d558896 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:34:51 -0500 Subject: [PATCH 09/45] feat: add uninstall validation, tests --- .../src/ma-v2/actions/common/types.ts | 4 +- .../src/ma-v2/actions/common/utils.ts | 2 +- .../install-validation/installValidation.ts | 54 ++++++------ .../src/ma-v2/client/client.test.ts | 86 +++++++++++++++++-- 4 files changed, 109 insertions(+), 37 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts index 1a133ae0bc..14f5739e6e 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts @@ -1,12 +1,12 @@ import type { Address, Hex } from "viem"; export type ModuleEntity = { - address: Address; + moduleAddress: Address; entityId: number; }; export type ValidationConfig = { - address: Address; + moduleAddress: Address; entityId: number; // uint32 isGlobal: boolean; isSignatureValidation: boolean; diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts index 2d79719bce..7af59140a2 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -26,5 +26,5 @@ export function serializeHookConfig(config: HookConfig): Hex { } export function serializeModuleEntity(config: ModuleEntity): Hex { - return concatHex([config.address, toHex(config.entityId, { size: 4 })]); + return concatHex([config.moduleAddress, toHex(config.entityId, { size: 4 })]); } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts index 6f2e50cb20..f2d77d386c 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -24,6 +24,7 @@ import type { HookConfig, ValidationConfig } from "../common/types.js"; import { serializeValidationConfig, serializeHookConfig, + serializeModuleEntity, } from "../common/utils.js"; export type InstallValidationParams< @@ -118,39 +119,40 @@ export const installValidationActions: < }, uninstallValidation: async ({ - // moduleAddress, entityId, uninstallData, hookUninstallDatas, + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, account = client.account, overrides, }) => { - // if (!account) { - // throw new AccountNotFoundError(); - // } - - // if (!isSmartAccountClient(client)) { - // throw new IncompatibleClientError( - // "SmartAccountClient", - // "uninstallValidation", - // client - // ); - // } + if (!account) { + throw new AccountNotFoundError(); + } - // const { moduleAddress, entityId, uninstallData, hookUninstallDatas } = args; + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "uninstallValidation", + client + ); + } - // const callData = encodeFunctionData({ - // abi: semiModularAccountBytecodeAbi, - // functionName: "uninstallValidation", - // args: [ - // serializeModuleEntity({ - // moduleAddress, - // entityId, - // }), - // uninstallData, - // hookUninstallDatas, - // ], - // }); + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }); return client.sendUserOperation({ - uo: "0x", + uo: callData, account, overrides, }); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 50be586aa8..effed682df 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -72,9 +72,9 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const result = await provider.installValidation({ + let result = await provider.installValidation({ validationConfig: { - address: addresses.singleSignerValidationModule, + moduleAddress: addresses.singleSignerValidationModule, entityId: 1, isGlobal: true, isSignatureValidation: true, @@ -93,11 +93,17 @@ describe("MA v2 Tests", async () => { const startingAddressBalance = await getTargetBalance(); - // connect session key - provider.signer = sessionKey; - provider.entityId = 1n; + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1n, + isGlobalValidation: true, + }); - const result2 = await provider.sendUserOperation({ + result = await sessionKeyClient.sendUserOperation({ uo: { target: target, value: sendAmount, @@ -105,22 +111,86 @@ describe("MA v2 Tests", async () => { }, }); - txnHash = provider.waitForUserOperationTransaction(result); + txnHash = sessionKeyClient.waitForUserOperationTransaction(result); await expect(txnHash).resolves.not.toThrowError(); - await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount ); }); + it("uninstalls a session key", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: addresses.singleSignerValidationModule, + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + result = await provider.uninstallValidation({ + moduleAddress: addresses.singleSignerValidationModule, + entityId: 1, + uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ + entityId: 1, + }), + hookUninstallDatas: [], + }); + + txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1n, + isGlobalValidation: true, + }); + + result = sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + await expect(result).rejects.toThrowError(); + }); + const givenConnectedProvider = async ({ signer, + accountAddress, }: { signer: SmartAccountSigner; + accountAddress?: `0x${string}`; }) => createSMAV2AccountClient({ chain: instance.chain, signer, + accountAddress, transport: custom(instance.getClient()), }); }); From d010f10d8ab07aecb082098b7c52458c350a8987 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:06:16 -0500 Subject: [PATCH 10/45] chore: update abi types --- account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts | 2 +- account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts | 2 +- .../src/ma-v2/abis/semiModularAccountBytecodeAbi.ts | 2 +- .../src/ma-v2/abis/semiModularAccountStorageAbi.ts | 2 +- .../ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts | 2 +- .../native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts | 2 +- .../paymaster-guard-module/abis/paymasterGuardModuleAbi.ts | 2 +- .../ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts | 2 +- .../modules/webauthn-validation/abis/webauthnValidationAbi.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts index cb3ca8194b..33637670c0 100644 --- a/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/abis/accountFactoryAbi.ts @@ -635,4 +635,4 @@ export const accountFactoryAbi = [ name: "TransferFailed", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts index 7919fe6bb3..b5102f1e32 100644 --- a/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/abis/modularAccountAbi.ts @@ -1238,4 +1238,4 @@ export const modularAccountAbi = [ name: "ValidationSignatureSegmentMissing", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts index 8f2c3dd7cd..eef9d88165 100644 --- a/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountBytecodeAbi.ts @@ -1285,4 +1285,4 @@ export const semiModularAccountBytecodeAbi = [ name: "ValidationSignatureSegmentMissing", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts index aa320f38ab..d24a236c79 100644 --- a/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/abis/semiModularAccountStorageAbi.ts @@ -1298,4 +1298,4 @@ export const semiModularAccountStorageAbi = [ name: "ValidationSignatureSegmentMissing", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts b/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts index 270e36dc13..8e7893acac 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/allowlist-module/abis/allowlistModuleAbi.ts @@ -712,4 +712,4 @@ export const allowlistModuleAbi = [ name: "UnexpectedDataPassed", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts b/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts index de0f1d2aa1..a534830c0a 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/native-token-limit-module/abis/nativeTokenLimitModuleAbi.ts @@ -400,4 +400,4 @@ export const nativeTokenLimitModuleAbi = [ name: "UnexpectedDataPassed", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModuleAbi.ts b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModuleAbi.ts index 275480aab0..523a5e2623 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModuleAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/abis/paymasterGuardModuleAbi.ts @@ -238,4 +238,4 @@ export const paymasterGuardModuleAbi = [ name: "UnexpectedDataPassed", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts b/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts index e4fe309ca3..faecde3c0b 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/time-range-module/abis/timeRangeModuleAbi.ts @@ -292,4 +292,4 @@ export const timeRangeModuleAbi = [ name: "UnexpectedDataPassed", inputs: [], }, -]; +] as const; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidationAbi.ts b/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidationAbi.ts index ac35abfc5d..6b07b403db 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidationAbi.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/webauthn-validation/abis/webauthnValidationAbi.ts @@ -370,4 +370,4 @@ export const webauthnValidationModuleAbi = [ name: "TransferFailed", inputs: [], }, -]; +] as const; From 392e2e41d8e5c9771d6dbb0c37790a0160b2251e Mon Sep 17 00:00:00 2001 From: linna Date: Mon, 16 Dec 2024 19:30:28 -0500 Subject: [PATCH 11/45] feat: add initial paymaster guard module impl --- .../modules/paymaster-guard-module/module.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts diff --git a/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts new file mode 100644 index 0000000000..9076cc0e03 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/paymaster-guard-module/module.ts @@ -0,0 +1,49 @@ +import { encodeAbiParameters, type Address, type Hex } from "viem"; +import { paymasterGuardModuleAbi } from "./abis/paymasterGuardModuleAbi.js"; + +const addresses = { + default: "0x00", //dummy address, replace with deployed module address? +} as Record; + +const meta = { + name: "PaymasterGuardModule", + version: "alpha.1", + addresses, +}; + +export const PaymasterGuardModule = { + meta, + abi: paymasterGuardModuleAbi, + encodeOnInstallData: (args: { + entityId: number; + paymaster: Address; + }): Hex => { + const { entityId, paymaster } = args; + return encodeAbiParameters( + [ + { + type: "uint32", + value: entityId, + }, + { + type: "address", + value: paymaster, + }, + ], + [entityId, paymaster] + ); + }, + encodeOnUninstallData: (args: { entityId: number }): Hex => { + const { entityId } = args; + + return encodeAbiParameters( + [ + { + type: "uint32", + value: entityId, + }, + ], + [entityId] + ); + }, +}; From f46ac29519a9f0373ed35c663bd8d5111cb83472 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 12/45] chore: removing arbsepo instance --- .vitest/src/instances.ts | 13 +--------- .../src/ma-v2/account/account.ts | 10 +++++++- .../src/ma-v2/client/client.test.ts | 4 ++-- .../smart-contracts/src/ma-v2/utils.ts | 24 +++++++++---------- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/.vitest/src/instances.ts b/.vitest/src/instances.ts index 08431e186f..489c8b4350 100644 --- a/.vitest/src/instances.ts +++ b/.vitest/src/instances.ts @@ -10,17 +10,6 @@ import { split } from "../../aa-sdk/core/src/transport/split"; import { poolId, rundlerBinaryPath } from "./constants"; import { paymasterTransport } from "./paymaster/transport"; -export const local070InstanceOptSep = defineInstance({ - chain: localhost, - forkBlockNumber: 21153995, - forkUrl: - process.env.VITEST_OPT_SEPOLIA_FORK_URL ?? - "https://opt-sepolia-rpc.publicnode.com", - entryPointVersion: "0.7.0", - anvilPort: 8345, - bundlerPort: 8445, -}); - export const local060Instance = defineInstance({ chain: localhost, forkBlockNumber: 6381303, @@ -34,7 +23,7 @@ export const local060Instance = defineInstance({ export const local070Instance = defineInstance({ chain: localhost, - forkBlockNumber: 6381303, + forkBlockNumber: 7299096, forkUrl: process.env.VITEST_SEPOLIA_FORK_URL ?? "https://ethereum-sepolia-rpc.publicnode.com", diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts index fc75fbb622..1f9cce1c1b 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -8,6 +8,7 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, + getAccountAddress, } from "@aa-sdk/core"; import { concatHex, @@ -119,6 +120,13 @@ export async function createSMAV2Account( ...singleSignerMessageSigner(signer), }); + const accountAddress_ = await getAccountAddress({ + client, + entryPoint, + accountAddress, + getAccountInitCode, + }); + // TODO: add deferred action flag const getAccountNonce = async (nonceKey?: bigint): Promise => { const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ @@ -136,7 +144,7 @@ export async function createSMAV2Account( }); return entryPointContract.read.getNonce([ - baseAccount.address, + accountAddress_, nonceKey ?? hexToBigInt(nonceKeySuffix), ]) as Promise; }; diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 825403a2b4..55a14f331f 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -4,12 +4,12 @@ import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; import { createSMAV2AccountClient } from "./client.js"; -import { local070InstanceOptSep } from "~test/instances.js"; +import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; describe("MA v2 Tests", async () => { - const instance = local070InstanceOptSep; + const instance = local070Instance; let client: ReturnType; beforeAll(async () => { diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index f480b5561f..4766fc033b 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -15,28 +15,26 @@ export const packSignature = ({ export const addresses = { allowlistModule: - "0xE46ca4a98c485caEE2Abb6ef5116292B8c78a868" as `0x${string}`, + "0xe768abEe3ad779Be1318388fC5D6A6fCf658011B" as `0x${string}`, nativeTokenLimitModule: - "0xEa6a05306315196f2A7CA2ec7eEA45290bae00A0" as `0x${string}`, + "0xbFD077C53ec21f91699cF2DBab5324ea608b4eF0" as `0x${string}`, paymasterGuardModule: - "0x976D01aF75D128cae526B5328AC268ac83D607f4" as `0x${string}`, + "0x97018B224C969A1992076293f15482FB9982A271" as `0x${string}`, singleSignerValidationModule: - "0xF56716aE104545BdAf012e14e4640beb52727479" as `0x${string}`, + "0x2a42a36ee6DC9E8f75d2f8B5ef621EE8F2bD3156" as `0x${string}`, timeRangeModule: - "0x6FD0a9765a86788126a55aD5a483029a484F996C" as `0x${string}`, + "0x335a66470B5052DD9F540bD64ca6b7dE205F6f0B" as `0x${string}`, webauthnValidationModule: - "0xCf3423F8EB9EE215560802D03a48cDD44f85bD28" as `0x${string}`, + "0x52bB58A05659F2fF4bDb57E602824859De11119A" as `0x${string}`, executionInstallDelegate: "0x8Bf909fEb66EBcC4725f04E70F319791Ec9d9628" as `0x${string}`, modularAccountImpl: - "0x99090abd2700E24Cc70E3c486A89F7af876fFA33" as `0x${string}`, + "0x7219030794F2937ff4A322B7CE9a58C070aF08C5" as `0x${string}`, semiModularAccountBytecodeImpl: - "0xDCBb5d4639428B18703801f7Cd7230add729E4b0" as `0x${string}`, + "0xc6176BeF7E32224238ef3A2Ee3F9BaCfA801Cc95" as `0x${string}`, semiModularAccountStorageOnlyImpl: - "0x563ea43Ba0CD9150466B054Ce47FB0Fb45B234E2" as `0x${string}`, - accountFactory: "0xA5FC7aDc6d2c838443dF5bb826152c58b918f2c8" as `0x${string}`, + "0x8bD01f353058513fE5968C80585dc0792f56961b" as `0x${string}`, + accountFactory: "0x52fd2B39bd2a2c411371514114f9a1b3F9Ba3a64" as `0x${string}`, accountFactoryOwner: - "0x3BD786ac7Dec96eF9d06ebbCFa4e0E0947A2c303" as `0x${string}`, + "0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C" as `0x${string}`, }; - -export const tempChainId = 11155420; From d481ee9a676f981243b7e7f2e990383732cb3863 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 13/45] chore: removing arbsepo instance --- .vitest/src/instances.ts | 13 +--------- aa-sdk/core/src/errors/client.ts | 4 ++-- .../src/ma-v2/account/account.ts | 20 +++++++++++----- .../src/ma-v2/client/client.test.ts | 7 +++--- .../smart-contracts/src/ma-v2/utils.ts | 24 +++++++++---------- .../InvalidEntityIdError/constructor.mdx | 2 +- 6 files changed, 33 insertions(+), 37 deletions(-) diff --git a/.vitest/src/instances.ts b/.vitest/src/instances.ts index 08431e186f..489c8b4350 100644 --- a/.vitest/src/instances.ts +++ b/.vitest/src/instances.ts @@ -10,17 +10,6 @@ import { split } from "../../aa-sdk/core/src/transport/split"; import { poolId, rundlerBinaryPath } from "./constants"; import { paymasterTransport } from "./paymaster/transport"; -export const local070InstanceOptSep = defineInstance({ - chain: localhost, - forkBlockNumber: 21153995, - forkUrl: - process.env.VITEST_OPT_SEPOLIA_FORK_URL ?? - "https://opt-sepolia-rpc.publicnode.com", - entryPointVersion: "0.7.0", - anvilPort: 8345, - bundlerPort: 8445, -}); - export const local060Instance = defineInstance({ chain: localhost, forkBlockNumber: 6381303, @@ -34,7 +23,7 @@ export const local060Instance = defineInstance({ export const local070Instance = defineInstance({ chain: localhost, - forkBlockNumber: 6381303, + forkBlockNumber: 7299096, forkUrl: process.env.VITEST_SEPOLIA_FORK_URL ?? "https://ethereum-sepolia-rpc.publicnode.com", diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index 5c42a1e252..b3040fe785 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -63,9 +63,9 @@ export class InvalidEntityIdError extends BaseError { /** * Initializes a new instance of the error message with a default message indicating that the entity id is invalid because it's too large. * - * @param {bigint} entityId the invalid entityId used + * @param {number} entityId the invalid entityId used */ - constructor(entityId: bigint) { + constructor(entityId: number) { super(`Entity ID used is ${entityId}, but must be less than uint32.max`); } } diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts index fc75fbb622..a5ab0e932f 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -8,6 +8,7 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, + getAccountAddress, } from "@aa-sdk/core"; import { concatHex, @@ -49,11 +50,11 @@ export type CreateSMAV2AccountParams< } & ( | { isGlobalValidation: boolean; - entityId: bigint; + entityId: number; } | { - isGlobalValidation: never; - entityId: never; + isGlobalValidation?: never; + entityId?: never; } ); @@ -78,10 +79,10 @@ export async function createSMAV2Account( accountAddress, entryPoint = getEntryPoint(chain, { version: "0.7.0" }), isGlobalValidation = true, - entityId = 0n, + entityId = 0, } = config; - if (entityId >= maxUint32) { + if (entityId >= Number(maxUint32)) { throw new InvalidEntityIdError(entityId); } @@ -119,6 +120,13 @@ export async function createSMAV2Account( ...singleSignerMessageSigner(signer), }); + const accountAddress_ = await getAccountAddress({ + client, + entryPoint, + accountAddress, + getAccountInitCode, + }); + // TODO: add deferred action flag const getAccountNonce = async (nonceKey?: bigint): Promise => { const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ @@ -136,7 +144,7 @@ export async function createSMAV2Account( }); return entryPointContract.read.getNonce([ - baseAccount.address, + accountAddress_, nonceKey ?? hexToBigInt(nonceKeySuffix), ]) as Promise; }; diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 825403a2b4..8ead56e5fa 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -4,13 +4,14 @@ import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; import { createSMAV2AccountClient } from "./client.js"; -import { local070InstanceOptSep } from "~test/instances.js"; +import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; describe("MA v2 Tests", async () => { - const instance = local070InstanceOptSep; - let client: ReturnType; + const instance = local070Instance; + let client: ReturnType & + ReturnType; beforeAll(async () => { client = instance.getClient().extend(publicActions); diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index f480b5561f..4766fc033b 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -15,28 +15,26 @@ export const packSignature = ({ export const addresses = { allowlistModule: - "0xE46ca4a98c485caEE2Abb6ef5116292B8c78a868" as `0x${string}`, + "0xe768abEe3ad779Be1318388fC5D6A6fCf658011B" as `0x${string}`, nativeTokenLimitModule: - "0xEa6a05306315196f2A7CA2ec7eEA45290bae00A0" as `0x${string}`, + "0xbFD077C53ec21f91699cF2DBab5324ea608b4eF0" as `0x${string}`, paymasterGuardModule: - "0x976D01aF75D128cae526B5328AC268ac83D607f4" as `0x${string}`, + "0x97018B224C969A1992076293f15482FB9982A271" as `0x${string}`, singleSignerValidationModule: - "0xF56716aE104545BdAf012e14e4640beb52727479" as `0x${string}`, + "0x2a42a36ee6DC9E8f75d2f8B5ef621EE8F2bD3156" as `0x${string}`, timeRangeModule: - "0x6FD0a9765a86788126a55aD5a483029a484F996C" as `0x${string}`, + "0x335a66470B5052DD9F540bD64ca6b7dE205F6f0B" as `0x${string}`, webauthnValidationModule: - "0xCf3423F8EB9EE215560802D03a48cDD44f85bD28" as `0x${string}`, + "0x52bB58A05659F2fF4bDb57E602824859De11119A" as `0x${string}`, executionInstallDelegate: "0x8Bf909fEb66EBcC4725f04E70F319791Ec9d9628" as `0x${string}`, modularAccountImpl: - "0x99090abd2700E24Cc70E3c486A89F7af876fFA33" as `0x${string}`, + "0x7219030794F2937ff4A322B7CE9a58C070aF08C5" as `0x${string}`, semiModularAccountBytecodeImpl: - "0xDCBb5d4639428B18703801f7Cd7230add729E4b0" as `0x${string}`, + "0xc6176BeF7E32224238ef3A2Ee3F9BaCfA801Cc95" as `0x${string}`, semiModularAccountStorageOnlyImpl: - "0x563ea43Ba0CD9150466B054Ce47FB0Fb45B234E2" as `0x${string}`, - accountFactory: "0xA5FC7aDc6d2c838443dF5bb826152c58b918f2c8" as `0x${string}`, + "0x8bD01f353058513fE5968C80585dc0792f56961b" as `0x${string}`, + accountFactory: "0x52fd2B39bd2a2c411371514114f9a1b3F9Ba3a64" as `0x${string}`, accountFactoryOwner: - "0x3BD786ac7Dec96eF9d06ebbCFa4e0E0947A2c303" as `0x${string}`, + "0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C" as `0x${string}`, }; - -export const tempChainId = 11155420; diff --git a/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx index 3755d0a81f..34ebabe704 100644 --- a/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx +++ b/site/pages/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor.mdx @@ -21,5 +21,5 @@ import { InvalidEntityIdError } from "@aa-sdk/core"; ### entityId -`bigint` +`number` the invalid entityId used From 47a7d710ea6bf4749c6056758223a0bfe3d527ff Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Wed, 18 Dec 2024 02:34:53 -0500 Subject: [PATCH 14/45] fix: more review fixes --- .vitest/src/instances.ts | 2 +- .../{account.ts => semiModularAccountV2.ts} | 14 +- .../src/ma-v2/client/client.ts | 2 +- .../single-signer-validation/module.ts | 11 -- .../src/ma-v2/modules/utils.ts | 129 ++++++++++++++++++ .../smart-contracts/src/ma-v2/utils.ts | 126 +++++++++++++---- 6 files changed, 235 insertions(+), 49 deletions(-) rename account-kit/smart-contracts/src/ma-v2/account/{account.ts => semiModularAccountV2.ts} (93%) create mode 100644 account-kit/smart-contracts/src/ma-v2/modules/utils.ts diff --git a/.vitest/src/instances.ts b/.vitest/src/instances.ts index 489c8b4350..0332b7e6e5 100644 --- a/.vitest/src/instances.ts +++ b/.vitest/src/instances.ts @@ -23,7 +23,7 @@ export const local060Instance = defineInstance({ export const local070Instance = defineInstance({ chain: localhost, - forkBlockNumber: 7299096, + forkBlockNumber: 7303015, forkUrl: process.env.VITEST_SEPOLIA_FORK_URL ?? "https://ethereum-sepolia-rpc.publicnode.com", diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts similarity index 93% rename from account-kit/smart-contracts/src/ma-v2/account/account.ts rename to account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index a5ab0e932f..b8977309cf 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -8,7 +8,6 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, - getAccountAddress, } from "@aa-sdk/core"; import { concatHex, @@ -23,7 +22,7 @@ import { hexToBigInt, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; -import { addresses } from "../utils.js"; +import { getDefaultMAV2FactoryAddress } from "../utils.js"; import { standardExecutor } from "../../msca/account/standardExecutor.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; @@ -73,7 +72,7 @@ export async function createSMAV2Account( chain, signer, salt = 0n, - factoryAddress = addresses.accountFactory, + factoryAddress = getDefaultMAV2FactoryAddress(chain), initCode, initialOwner, accountAddress, @@ -120,13 +119,6 @@ export async function createSMAV2Account( ...singleSignerMessageSigner(signer), }); - const accountAddress_ = await getAccountAddress({ - client, - entryPoint, - accountAddress, - getAccountInitCode, - }); - // TODO: add deferred action flag const getAccountNonce = async (nonceKey?: bigint): Promise => { const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ @@ -144,7 +136,7 @@ export async function createSMAV2Account( }); return entryPointContract.read.getNonce([ - accountAddress_, + baseAccount.address, nonceKey ?? hexToBigInt(nonceKeySuffix), ]) as Promise; }; diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index f2e3b36335..946061daa9 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -10,7 +10,7 @@ import { createSMAV2Account, type CreateSMAV2AccountParams, type SMAV2Account, -} from "../account/account.js"; +} from "../account/semiModularAccountV2.js"; export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts index c865569eaf..180582d19f 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts @@ -2,19 +2,8 @@ import { encodeAbiParameters, type Address, type Hex } from "viem"; import { singleSignerValidationModuleAbi } from "./abis/singleSignerValidationModuleAbi.js"; -const addresses = { - default: "0xEa3a0b544d517f6Ed3Dc2186C74D869c702C376e", -} as Record; - -const meta = { - name: "SingleSignerValidation", - version: "alpha.1", - addresses, -}; - // Todo: some unified type for ERC-6900 v0.8 modules. I couldn't figure out how to parameterize the class itself over the abi type parameters for onInstall and onUninstall. export const SingleSignerValidationModule = { - meta, abi: singleSignerValidationModuleAbi, encodeOnInstallData: (args: { entityId: number; signer: Address }): Hex => { const { entityId, signer } = args; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/utils.ts b/account-kit/smart-contracts/src/ma-v2/modules/utils.ts new file mode 100644 index 0000000000..8da6ba6028 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/modules/utils.ts @@ -0,0 +1,129 @@ +import type { Chain, Address } from "viem"; +import { + arbitrum, + arbitrumSepolia, + base, + baseSepolia, + mainnet, + optimism, + optimismSepolia, + polygon, + polygonAmoy, + sepolia, +} from "@account-kit/infra"; + +export const getDefaultWebauthnValidationModuleAddress = ( + chain: Chain +): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x0000000000001D9d34E07D9834274dF9ae575217"; + } +}; + +export const getDefaultTimeRangeModuleAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000082B8e2012be914dFA4f62A0573eA"; + } +}; + +export const getDefaultSingleSignerValidationModuleAddress = ( + chain: Chain +): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000099DE0BF6fA90dEB851E2A2df7d83"; + } +}; + +export const getDefaultPaymasterGuardModuleAddress = ( + chain: Chain +): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x0000000000001aA7A7F7E29abe0be06c72FD42A1"; + } +}; + +export const getDefaultNativeTokenLimitModuleAddress = ( + chain: Chain +): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000001e541f0D090868FBe24b59Fbe06"; + } +}; + +export const getDefaultAllowlistModuleAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x0000000000002311EEE9A2B887af1F144dbb4F6e"; + } +}; diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index 4766fc033b..241f89885b 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -1,4 +1,16 @@ -import { type Hex, concat } from "viem"; +import { concat, type Hex, type Chain, type Address } from "viem"; +import { + arbitrum, + arbitrumSepolia, + base, + baseSepolia, + mainnet, + optimism, + optimismSepolia, + polygon, + polygonAmoy, + sepolia, +} from "@account-kit/infra"; export type PackSignatureParams = { // orderedHookData: HookData[]; @@ -13,28 +25,92 @@ export const packSignature = ({ return concat(["0xFF", "0x00", validationSignature]); }; -export const addresses = { - allowlistModule: - "0xe768abEe3ad779Be1318388fC5D6A6fCf658011B" as `0x${string}`, - nativeTokenLimitModule: - "0xbFD077C53ec21f91699cF2DBab5324ea608b4eF0" as `0x${string}`, - paymasterGuardModule: - "0x97018B224C969A1992076293f15482FB9982A271" as `0x${string}`, - singleSignerValidationModule: - "0x2a42a36ee6DC9E8f75d2f8B5ef621EE8F2bD3156" as `0x${string}`, - timeRangeModule: - "0x335a66470B5052DD9F540bD64ca6b7dE205F6f0B" as `0x${string}`, - webauthnValidationModule: - "0x52bB58A05659F2fF4bDb57E602824859De11119A" as `0x${string}`, - executionInstallDelegate: - "0x8Bf909fEb66EBcC4725f04E70F319791Ec9d9628" as `0x${string}`, - modularAccountImpl: - "0x7219030794F2937ff4A322B7CE9a58C070aF08C5" as `0x${string}`, - semiModularAccountBytecodeImpl: - "0xc6176BeF7E32224238ef3A2Ee3F9BaCfA801Cc95" as `0x${string}`, - semiModularAccountStorageOnlyImpl: - "0x8bD01f353058513fE5968C80585dc0792f56961b" as `0x${string}`, - accountFactory: "0x52fd2B39bd2a2c411371514114f9a1b3F9Ba3a64" as `0x${string}`, - accountFactoryOwner: - "0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C" as `0x${string}`, +export const getDefaultMAV2FactoryAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000017c61b5bEe81050EC8eFc9c6fecd"; + } +}; + +export const getDefaultSMAV2BytecodeAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x000000000000c5A9089039570Dd36455b5C07383"; + } +}; + +export const getDefaultSMAV2StorageAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x0000000000006E2f9d80CaEc0Da6500f005EB25A"; + } +}; + +export const getDefaultSMAV27702Address = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x69007702764179f14F51cdce752f4f775d74E139"; + } +}; + +export const getDefaultMAV2Address = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000002377B26b1EdA7b0BC371C60DD4f"; + } }; From be95010d8835216db0b9f03a3d0dafad611f105d Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 15/45] feat: add install validation, add tests for session key --- aa-sdk/core/src/errors/client.ts | 16 ++ aa-sdk/core/src/index.ts | 1 + .../src/ma-v2/account/semiModularAccountV2.ts | 1 + .../src/ma-v2/actions/common/types.ts | 37 ++++ .../src/ma-v2/actions/common/utils.ts | 32 ++++ .../install-validation/installValidation.ts | 160 ++++++++++++++++++ .../src/ma-v2/client/client.test.ts | 151 ++++++++++++++++- .../EntityIdOverrideError/constructor.mdx | 18 ++ 8 files changed, 407 insertions(+), 9 deletions(-) create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/common/types.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts create mode 100644 site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index b3040fe785..f90784ca6d 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -88,3 +88,19 @@ export class InvalidNonceKeyError extends BaseError { ); } } + +/** + * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. + */ +export class EntityIdOverrideError extends BaseError { + override name = "InvalidNonceKeyError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. + */ + constructor() { + super( + `Installing entityId of 0 overrides the owner's entity id in the account` + ); + } +} diff --git a/aa-sdk/core/src/index.ts b/aa-sdk/core/src/index.ts index c5d33dea24..5f66aae5cc 100644 --- a/aa-sdk/core/src/index.ts +++ b/aa-sdk/core/src/index.ts @@ -75,6 +75,7 @@ export { InvalidRpcUrlError, InvalidEntityIdError, InvalidNonceKeyError, + EntityIdOverrideError, } from "./errors/client.js"; export { EntryPointNotFoundError, diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index b8977309cf..1937d2e958 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -8,6 +8,7 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, + getAccountAddress, } from "@aa-sdk/core"; import { concatHex, diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts new file mode 100644 index 0000000000..14f5739e6e --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts @@ -0,0 +1,37 @@ +import type { Address, Hex } from "viem"; + +export type ModuleEntity = { + moduleAddress: Address; + entityId: number; +}; + +export type ValidationConfig = { + moduleAddress: Address; + entityId: number; // uint32 + isGlobal: boolean; + isSignatureValidation: boolean; + isUserOpValidation: boolean; +}; + +export enum HookType { + EXECUTION = "0x00", + VALIDATION = "0x01", +} + +export type HookConfig = { + address: Address; + entityId: number; // uint32 + hookType: HookType; + hasPreHooks: boolean; + hasPostHooks: boolean; +}; + +// maps to type ValidationStorage in MAv2 implementation +export type ValidationData = { + isGlobal: boolean; // validation flag + isSignatureValidation: boolean; // validation flag + isUserOpValidation: boolean; + validationHooks: HookConfig[]; + executionHooks: Hex[]; + selectors: Hex[]; +}; diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts new file mode 100644 index 0000000000..8bd366ab76 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -0,0 +1,32 @@ +import { type Hex, toHex, concatHex } from "viem"; +import type { ValidationConfig, HookConfig, ModuleEntity } from "./types"; +import { HookType } from "./types.js"; + +export function serializeValidationConfig(config: ValidationConfig): Hex { + const isUserOpValidationBit = config.isUserOpValidation ? 1 : 0; + const isSignatureValidationBit = config.isSignatureValidation ? 2 : 0; + const isGlobalBit = config.isGlobal ? 4 : 0; + return concatHex([ + serializeModuleEntity(config), + toHex(isUserOpValidationBit + isSignatureValidationBit + isGlobalBit, { + size: 1, + }), + ]); +} + +export function serializeHookConfig(config: HookConfig): Hex { + const hookTypeBit = config.hookType === HookType.VALIDATION ? 1 : 0; + const hasPostHooksBit = config.hasPostHooks ? 2 : 0; + const hasPreHooksBit = config.hasPreHooks ? 4 : 0; + return concatHex([ + config.address, + toHex(config.entityId, { size: 4 }), + toHex(hookTypeBit + hasPostHooksBit + hasPreHooksBit, { + size: 1, + }), + ]); +} + +export function serializeModuleEntity(config: ModuleEntity): Hex { + return concatHex([config.moduleAddress, toHex(config.entityId, { size: 4 })]); +} diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts new file mode 100644 index 0000000000..f2d77d386c --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -0,0 +1,160 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + EntityIdOverrideError, + type GetAccountParameter, + type GetEntryPointFromAccount, + type SendUserOperationResult, + type SmartContractAccount, + type SmartAccountClient, + type UserOperationOverridesParameter, +} from "@aa-sdk/core"; +import { + type Address, + type Hex, + type Chain, + type Transport, + encodeFunctionData, + concatHex, +} from "viem"; + +import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; +import type { HookConfig, ValidationConfig } from "../common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, + serializeModuleEntity, +} from "../common/utils.js"; + +export type InstallValidationParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type UninstallValidationParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type InstallValidationActions< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export const installValidationActions: < + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends SmartContractAccount = SmartContractAccount +>( + client: SmartAccountClient +) => InstallValidationActions = (client) => ({ + installValidation: async ({ + validationConfig, + selectors, + installData, + hooks, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "installValidation", + client + ); + } + + if (validationConfig.entityId === 0) { + throw new EntityIdOverrideError(); + } + + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, + + uninstallValidation: async ({ + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "uninstallValidation", + client + ); + } + + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 8ead56e5fa..ee6af7f58c 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -7,6 +7,9 @@ import { createSMAV2AccountClient } from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; +import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; +import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -21,6 +24,18 @@ describe("MA v2 Tests", async () => { accounts.fundedAccountOwner ); + const sessionKey: SmartAccountSigner = new LocalAccountSigner( + accounts.unfundedAccountOwner + ); + + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); + + const getTargetBalance = async (): Promise => + client.getBalance({ + address: target, + }); + it("sends a simple UO", async () => { const provider = await givenConnectedProvider({ signer }); @@ -29,12 +44,7 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const target = "0x000000000000000000000000000000000000dEaD"; - const sendAmount = parseEther("1"); - - const startingAddressBalance = await client.getBalance({ - address: target, - }); + const startingAddressBalance = await getTargetBalance(); const result = await provider.sendUserOperation({ uo: { @@ -47,23 +57,146 @@ describe("MA v2 Tests", async () => { const txnHash1 = provider.waitForUserOperationTransaction(result); await expect(txnHash1).resolves.not.toThrowError(); - const newAddressBalance = await client.getBalance({ - address: target, + await expect(await getTargetBalance()).toEqual( + startingAddressBalance + sendAmount + ); + }); + + it("adds a session key with no permissions", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + const startingAddressBalance = await getTargetBalance(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1, + isGlobalValidation: true, }); - await expect(newAddressBalance).toEqual( + result = await sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + txnHash = sessionKeyClient.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount ); }); + it("uninstalls a session key", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + result = await provider.uninstallValidation({ + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ + entityId: 1, + }), + hookUninstallDatas: [], + }); + + txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1, + isGlobalValidation: true, + }); + + await expect( + sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }) + ).rejects.toThrowError(); + }); + const givenConnectedProvider = async ({ signer, + accountAddress, }: { signer: SmartAccountSigner; + accountAddress?: `0x${string}`; }) => createSMAV2AccountClient({ chain: instance.chain, signer, + accountAddress, transport: custom(instance.getClient()), }); }); diff --git a/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx new file mode 100644 index 0000000000..ef8e82cc95 --- /dev/null +++ b/site/pages/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor.mdx @@ -0,0 +1,18 @@ +--- +# This file is autogenerated +title: EntityIdOverrideError +description: Overview of the EntityIdOverrideError method +--- + +# EntityIdOverrideError + +Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. +:::note +`EntityIdOverrideError` extends `BaseError`, see the docs for BaseError for all supported methods. +::: + +## Import + +```ts +import { EntityIdOverrideError } from "@aa-sdk/core"; +``` From d9c40edd0d95ea54432800adffd2d18d3dbcde46 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Wed, 18 Dec 2024 02:52:44 -0500 Subject: [PATCH 16/45] fix: lint --- .../smart-contracts/src/ma-v2/account/semiModularAccountV2.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index 1937d2e958..b8977309cf 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -8,7 +8,6 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, - getAccountAddress, } from "@aa-sdk/core"; import { concatHex, From 555fdc955020f7da6a173837f06e71b65b1ba691 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 17/45] feat: add view functions and user op calldata encodings --- .../src/ma-v2/account/semiModularAccountV2.ts | 120 +++++++++++++++++- .../install-validation/installValidation.ts | 61 +++++---- .../src/ma-v2/client/client.test.ts | 9 +- .../src/ma-v2/client/client.ts | 19 ++- 4 files changed, 173 insertions(+), 36 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index b8977309cf..a3409d5746 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -1,6 +1,7 @@ import type { EntryPointDef, SmartAccountSigner, + AccountOp, SmartContractAccountWithSigner, ToSmartContractAccountParams, } from "@aa-sdk/core"; @@ -20,18 +21,53 @@ import { type Hex, type Transport, hexToBigInt, + zeroAddress, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; import { getDefaultMAV2FactoryAddress } from "../utils.js"; import { standardExecutor } from "../../msca/account/standardExecutor.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; +import { modularAccountAbi } from "../abis/modularAccountAbi.js"; +import { serializeModuleEntity } from "../actions/common/utils.js"; + +const executeUserOpSelector: Hex = "0x8DD7712F"; export const DEFAULT_OWNER_ENTITY_ID = 0; +export type CalldataEncoder = { + encodeCallData: (callData: Hex) => Promise; +}; + +export type ExecutionDataView = { + module: Address; + skipRuntimeValidation: boolean; + allowGlobalValidation: boolean; + executionHooks: readonly Hex[]; +}; + +export type ValidationDataView = { + validationHooks: readonly Hex[]; + executionHooks: readonly Hex[]; + selectors: readonly Hex[]; + validationFlags: number; +}; + +export type ValidationDataParams = { + validationModuleAddress?: Address; + entityId?: number; +}; + export type SMAV2Account< - TSigner extends SmartAccountSigner = SmartAccountSigner -> = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0">; + TSigner extends SmartAccountSigner = SmartAccountSigner, + TCalldataEncoder extends CalldataEncoder = CalldataEncoder +> = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & + TCalldataEncoder & { + getExecutionData: (selector: Hex) => Promise; + getValidationData: ( + args: ValidationDataParams + ) => Promise; + }; export type CreateSMAV2AccountParams< TTransport extends Transport = Transport, @@ -62,11 +98,11 @@ export async function createSMAV2Account< TSigner extends SmartAccountSigner = SmartAccountSigner >( config: CreateSMAV2AccountParams -): Promise>; +): Promise & CalldataEncoder>; export async function createSMAV2Account( config: CreateSMAV2AccountParams -): Promise { +): Promise { const { transport, chain, @@ -141,9 +177,85 @@ export async function createSMAV2Account( ]) as Promise; }; + const accountContract = getContract({ + address: baseAccount.address, + abi: modularAccountAbi, + client, + }); + + const getExecutionData = async (selector: Hex) => { + if (!(await baseAccount.isAccountDeployed())) { + return {} as ExecutionDataView; + } + + return await accountContract.read.getExecutionData([selector]); + }; + + const getValidationData = async (args: ValidationDataParams) => { + if (!(await baseAccount.isAccountDeployed())) { + return {} as ValidationDataView; + } + + const { validationModuleAddress, entityId } = args; + return await accountContract.read.getValidationData([ + serializeModuleEntity({ + moduleAddress: validationModuleAddress ?? zeroAddress, + entityId: entityId ?? Number(maxUint32), + }), + ]); + }; + + const encodeCallData = async (callData: Hex): Promise => { + const validationData = await getValidationData({ + entityId: Number(entityId), + }); + + const numHooks = validationData?.executionHooks?.length ?? 0; + + return numHooks ? concatHex([executeUserOpSelector, callData]) : callData; + }; + + const encodeExecute: (tx: AccountOp) => Promise = async ({ + target, + data, + value, + }) => { + let callData = encodeFunctionData({ + abi: modularAccountAbi, + functionName: "execute", + args: [target, value ?? 0n, data], + }); + + callData = (await baseAccount.isAccountDeployed()) + ? await encodeCallData(callData) + : callData; + + return callData; + }; + + const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => + encodeCallData( + encodeFunctionData({ + abi: modularAccountAbi, + functionName: "executeBatch", + args: [ + txs.map((tx) => ({ + target: tx.target, + data: tx.data, + value: tx.value ?? 0n, + })), + ], + }) + ); + return { ...baseAccount, getAccountNonce, getSigner: () => signer, + getExecutionData, + getValidationData, + encodeExecute, + encodeBatchExecute, + encodeCallData, }; } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts index f2d77d386c..51d5576d32 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -6,9 +6,9 @@ import { type GetAccountParameter, type GetEntryPointFromAccount, type SendUserOperationResult, - type SmartContractAccount, type SmartAccountClient, type UserOperationOverridesParameter, + type SmartContractAccount, } from "@aa-sdk/core"; import { type Address, @@ -26,6 +26,7 @@ import { serializeHookConfig, serializeModuleEntity, } from "../common/utils.js"; +import type { CalldataEncoder } from "../../account/semiModularAccountV2.js"; export type InstallValidationParams< TAccount extends SmartContractAccount | undefined = @@ -72,7 +73,7 @@ export const installValidationActions: < TChain extends Chain | undefined = Chain | undefined, TAccount extends SmartContractAccount = SmartContractAccount >( - client: SmartAccountClient + client: SmartAccountClient & CalldataEncoder ) => InstallValidationActions = (client) => ({ installValidation: async ({ validationConfig, @@ -98,18 +99,22 @@ export const installValidationActions: < throw new EntityIdOverrideError(); } - const callData = encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }); + const { encodeCallData } = client; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); return client.sendUserOperation({ uo: callData, @@ -138,18 +143,22 @@ export const installValidationActions: < ); } - const callData = encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }); + const { encodeCallData } = client; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); return client.sendUserOperation({ uo: callData, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index ee6af7f58c..304e287309 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,6 +1,10 @@ import { custom, parseEther, publicActions } from "viem"; -import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; +import { + LocalAccountSigner, + type SmartAccountSigner, + type SmartAccountClient, +} from "@aa-sdk/core"; import { createSMAV2AccountClient } from "./client.js"; @@ -10,6 +14,7 @@ import { accounts } from "~test/constants.js"; import { installValidationActions } from "../actions/install-validation/installValidation.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { type CalldataEncoder } from "../account/semiModularAccountV2.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -192,7 +197,7 @@ describe("MA v2 Tests", async () => { }: { signer: SmartAccountSigner; accountAddress?: `0x${string}`; - }) => + }): Promise => createSMAV2AccountClient({ chain: instance.chain, signer, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index 946061daa9..946d55f88e 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -8,6 +8,7 @@ import { type Chain, type CustomTransport, type Transport } from "viem"; import { createSMAV2Account, + type CalldataEncoder, type CreateSMAV2AccountParams, type SMAV2Account, } from "../account/semiModularAccountV2.js"; @@ -24,10 +25,13 @@ export type CreateSMAV2AccountClientParams< export function createSMAV2AccountClient< TChain extends Chain = Chain, - TSigner extends SmartAccountSigner = SmartAccountSigner + TSigner extends SmartAccountSigner = SmartAccountSigner, + TCalldataEncoder extends CalldataEncoder = CalldataEncoder >( args: CreateSMAV2AccountClientParams -): Promise>>; +): Promise< + SmartAccountClient & TCalldataEncoder +>; /** * Creates a MAv2 account client using the provided configuration parameters. @@ -60,13 +64,20 @@ export function createSMAV2AccountClient< */ export async function createSMAV2AccountClient({ ...config -}: CreateSMAV2AccountClientParams): Promise { +}: CreateSMAV2AccountClientParams): Promise< + SmartAccountClient & CalldataEncoder +> { const maV2Account = await createSMAV2Account({ ...config, }); - return createSmartAccountClient({ + const client = createSmartAccountClient({ ...config, account: maV2Account, }); + + return { + ...client, + encodeCallData: maV2Account.encodeCallData, + }; } From 2b4b01a306fc91b2a82f4a27de6bd62c1da89899 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:48:19 -0500 Subject: [PATCH 18/45] fix: move install validation to the client --- .../install-validation/installValidation.ts | 169 ------------------ .../src/ma-v2/client/client.test.ts | 17 +- .../src/ma-v2/client/client.ts | 118 +++++++++++- 3 files changed, 121 insertions(+), 183 deletions(-) delete mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts deleted file mode 100644 index 51d5576d32..0000000000 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { - AccountNotFoundError, - IncompatibleClientError, - isSmartAccountClient, - EntityIdOverrideError, - type GetAccountParameter, - type GetEntryPointFromAccount, - type SendUserOperationResult, - type SmartAccountClient, - type UserOperationOverridesParameter, - type SmartContractAccount, -} from "@aa-sdk/core"; -import { - type Address, - type Hex, - type Chain, - type Transport, - encodeFunctionData, - concatHex, -} from "viem"; - -import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; -import type { HookConfig, ValidationConfig } from "../common/types.js"; -import { - serializeValidationConfig, - serializeHookConfig, - serializeModuleEntity, -} from "../common/utils.js"; -import type { CalldataEncoder } from "../../account/semiModularAccountV2.js"; - -export type InstallValidationParams< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined -> = { - validationConfig: ValidationConfig; - selectors: Hex[]; - installData: Hex; - hooks: { - hookConfig: HookConfig; - initData: Hex; - }[]; -} & UserOperationOverridesParameter> & - GetAccountParameter; - -export type UninstallValidationParams< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined -> = { - moduleAddress: Address; - entityId: number; - uninstallData: Hex; - hookUninstallDatas: Hex[]; -} & UserOperationOverridesParameter> & - GetAccountParameter; - -export type InstallValidationActions< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined -> = { - installValidation: ( - args: InstallValidationParams - ) => Promise; - uninstallValidation: ( - args: UninstallValidationParams - ) => Promise; -}; - -export const installValidationActions: < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends SmartContractAccount = SmartContractAccount ->( - client: SmartAccountClient & CalldataEncoder -) => InstallValidationActions = (client) => ({ - installValidation: async ({ - validationConfig, - selectors, - installData, - hooks, - account = client.account, - overrides, - }) => { - if (!account) { - throw new AccountNotFoundError(); - } - - if (!isSmartAccountClient(client)) { - throw new IncompatibleClientError( - "SmartAccountClient", - "installValidation", - client - ); - } - - if (validationConfig.entityId === 0) { - throw new EntityIdOverrideError(); - } - - const { encodeCallData } = client; - - const callData = await encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - account, - overrides, - }); - }, - - uninstallValidation: async ({ - moduleAddress, - entityId, - uninstallData, - hookUninstallDatas, - account = client.account, - overrides, - }) => { - if (!account) { - throw new AccountNotFoundError(); - } - - if (!isSmartAccountClient(client)) { - throw new IncompatibleClientError( - "SmartAccountClient", - "uninstallValidation", - client - ); - } - - const { encodeCallData } = client; - - const callData = await encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - account, - overrides, - }); - }, -}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 304e287309..9bfce280f5 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -6,15 +6,16 @@ import { type SmartAccountClient, } from "@aa-sdk/core"; -import { createSMAV2AccountClient } from "./client.js"; +import { + createSMAV2AccountClient, + type InstallValidationActions, +} from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { installValidationActions } from "../actions/install-validation/installValidation.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; -import { type CalldataEncoder } from "../account/semiModularAccountV2.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -68,9 +69,7 @@ describe("MA v2 Tests", async () => { }); it("adds a session key with no permissions", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); + let provider = await givenConnectedProvider({ signer }); await setBalance(client, { address: provider.getAddress(), @@ -126,9 +125,7 @@ describe("MA v2 Tests", async () => { }); it("uninstalls a session key", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); + let provider = await givenConnectedProvider({ signer }); await setBalance(client, { address: provider.getAddress(), @@ -197,7 +194,7 @@ describe("MA v2 Tests", async () => { }: { signer: SmartAccountSigner; accountAddress?: `0x${string}`; - }): Promise => + }): Promise => createSMAV2AccountClient({ chain: instance.chain, signer, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index 946d55f88e..3d023d36a1 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -1,10 +1,21 @@ import { createSmartAccountClient, + EntityIdOverrideError, type SmartAccountClient, type SmartAccountSigner, type SmartAccountClientConfig, + type UserOperationOverridesParameter, + type GetEntryPointFromAccount, + type SendUserOperationResult, } from "@aa-sdk/core"; -import { type Chain, type CustomTransport, type Transport } from "viem"; +import { + type Chain, + type CustomTransport, + type Transport, + type Hex, + type Address, +} from "viem"; +import { concatHex, encodeFunctionData } from "viem"; import { createSMAV2Account, @@ -13,6 +24,15 @@ import { type SMAV2Account, } from "../account/semiModularAccountV2.js"; +import type { HookConfig, ValidationConfig } from "../actions/common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, + serializeModuleEntity, +} from "../actions/common/utils.js"; + +import { semiModularAccountBytecodeAbi } from "../abis/semiModularAccountBytecodeAbi.js"; + export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, TChain extends Chain = Chain, @@ -23,6 +43,32 @@ export type CreateSMAV2AccountClientParams< "transport" | "account" | "chain" >; +export type InstallValidationActions = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export type InstallValidationParams = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; +} & UserOperationOverridesParameter>; + +export type UninstallValidationParams = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; +} & UserOperationOverridesParameter>; + export function createSMAV2AccountClient< TChain extends Chain = Chain, TSigner extends SmartAccountSigner = SmartAccountSigner, @@ -30,7 +76,8 @@ export function createSMAV2AccountClient< >( args: CreateSMAV2AccountClientParams ): Promise< - SmartAccountClient & TCalldataEncoder + SmartAccountClient & + InstallValidationActions >; /** @@ -65,7 +112,7 @@ export function createSMAV2AccountClient< export async function createSMAV2AccountClient({ ...config }: CreateSMAV2AccountClientParams): Promise< - SmartAccountClient & CalldataEncoder + SmartAccountClient & InstallValidationActions > { const maV2Account = await createSMAV2Account({ ...config, @@ -76,8 +123,71 @@ export async function createSMAV2AccountClient({ account: maV2Account, }); + const installValidation = async ({ + validationConfig, + selectors, + installData, + hooks, + overrides, + }: InstallValidationParams) => { + if (validationConfig.entityId === 0) { + throw new EntityIdOverrideError(); + } + + const callData = await maV2Account.encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + ...maV2Account, + overrides, + }); + }; + + const uninstallValidation = async ({ + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, + overrides, + }: UninstallValidationParams) => { + const callData = await maV2Account.encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + ...maV2Account, + overrides, + }); + }; + return { ...client, - encodeCallData: maV2Account.encodeCallData, + installValidation, + uninstallValidation, }; } From f808b02e80f24f6388afecc18d44ba9e675d6495 Mon Sep 17 00:00:00 2001 From: linna Date: Wed, 18 Dec 2024 19:12:41 -0500 Subject: [PATCH 19/45] feat: adds install and uninstall test for paymaster guard module --- .../src/ma-v2/client/client.test.ts | 77 ++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 9bfce280f5..f4125dfc8d 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,4 +1,4 @@ -import { custom, parseEther, publicActions } from "viem"; +import { custom, parseEther, publicActions, zeroAddress } from "viem"; import { LocalAccountSigner, @@ -14,8 +14,16 @@ import { import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; +import { + getDefaultPaymasterGuardModuleAddress, + getDefaultSingleSignerValidationModuleAddress, +} from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { PaymasterGuardModule } from "../modules/paymaster-guard-module/module.js"; +import { paymaster070 } from "~test/paymaster/paymaster070.js"; +import { addresses } from "../../../dist/esm/src/ma-v2/utils.js"; +import { HookType } from "../actions/common/types.js"; +import { installValidationActions } from "../../../dist/esm/src/ma-v2/actions/install-validation/installValidation.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -188,6 +196,71 @@ describe("MA v2 Tests", async () => { ).rejects.toThrowError(); }); + it.only("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const paymaster = paymaster070.getPaymasterStubData(); + + const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ + entityId: 1, + paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE + }); + + const installResult = await provider.installValidation({ + validationConfig: { + moduleAddress: zeroAddress, + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: "0x", + hooks: [ + { + hookConfig: { + address: getDefaultPaymasterGuardModuleAddress(provider.chain), + entityId: 0, // uint32 + hookType: HookType.VALIDATION, + hasPreHooks: true, + hasPostHooks: true, + }, + initData: hookInstallData, + }, + ], + }); + + // verify hook installtion succeeded + await expect( + provider.waitForUserOperationTransaction(installResult) + ).resolves.not.toThrowError(); + + // TO DO: verify if paymaster is valid + + const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ + entityId: 1, + }); + + const uninstallResult = await provider.uninstallValidation({ + moduleAddress: zeroAddress, + entityId: 1, + uninstallData: "0x", + hookUninstallDatas: [hookUninstallData], + }); + + // verify uninstall + await expect( + provider.waitForUserOperationTransaction(uninstallResult) + ).resolves.not.toThrowError(); + }); + const givenConnectedProvider = async ({ signer, accountAddress, From b1befe28fa1f7a0b9c215a1f64f733a1ac4293fd Mon Sep 17 00:00:00 2001 From: linna Date: Wed, 18 Dec 2024 19:16:15 -0500 Subject: [PATCH 20/45] fix: does not skip tests --- account-kit/smart-contracts/src/ma-v2/client/client.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index f4125dfc8d..85b6ae717a 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -196,7 +196,7 @@ describe("MA v2 Tests", async () => { ).rejects.toThrowError(); }); - it.only("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { + it("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { let provider = (await givenConnectedProvider({ signer })).extend( installValidationActions ); From 3fb73874bb35ab008c52fe62a2415f587e6ce211 Mon Sep 17 00:00:00 2001 From: linna Date: Wed, 18 Dec 2024 19:56:11 -0500 Subject: [PATCH 21/45] fix: removes import for installValidationActions --- account-kit/smart-contracts/src/ma-v2/client/client.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 85b6ae717a..ed48be6bc0 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -21,9 +21,7 @@ import { import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; import { PaymasterGuardModule } from "../modules/paymaster-guard-module/module.js"; import { paymaster070 } from "~test/paymaster/paymaster070.js"; -import { addresses } from "../../../dist/esm/src/ma-v2/utils.js"; import { HookType } from "../actions/common/types.js"; -import { installValidationActions } from "../../../dist/esm/src/ma-v2/actions/install-validation/installValidation.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -197,9 +195,7 @@ describe("MA v2 Tests", async () => { }); it("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); + let provider = await givenConnectedProvider({ signer }); await setBalance(client, { address: provider.getAddress(), From 4713fd688c5dda772da4f509c249438f1c428f76 Mon Sep 17 00:00:00 2001 From: linna Date: Thu, 19 Dec 2024 17:55:08 -0500 Subject: [PATCH 22/45] feat: adds test for successful install, functionality, and uninstall of paymaster guard module --- .../src/ma-v2/client/client.test.ts | 27 ++++++++++++++----- .../src/ma-v2/client/client.ts | 6 ++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index ed48be6bc0..07a3f76ee6 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -4,6 +4,7 @@ import { LocalAccountSigner, type SmartAccountSigner, type SmartAccountClient, + erc7677Middleware, } from "@aa-sdk/core"; import { @@ -195,7 +196,7 @@ describe("MA v2 Tests", async () => { }); it("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = await givenConnectedProvider({ signer, usePaymaster: true }); await setBalance(client, { address: provider.getAddress(), @@ -205,14 +206,14 @@ describe("MA v2 Tests", async () => { const paymaster = paymaster070.getPaymasterStubData(); const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ - entityId: 1, + entityId: 0, paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE }); const installResult = await provider.installValidation({ validationConfig: { moduleAddress: zeroAddress, - entityId: 1, + entityId: 0, isGlobal: true, isSignatureValidation: true, isUserOpValidation: true, @@ -238,15 +239,26 @@ describe("MA v2 Tests", async () => { provider.waitForUserOperationTransaction(installResult) ).resolves.not.toThrowError(); - // TO DO: verify if paymaster is valid + // happy path: with correct paymaster + const result = await provider.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + // verify if correct paymaster is used + const txnHash1 = provider.waitForUserOperationTransaction(result); + await expect(txnHash1).resolves.not.toThrowError(); const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ - entityId: 1, + entityId: 0, }); const uninstallResult = await provider.uninstallValidation({ moduleAddress: zeroAddress, - entityId: 1, + entityId: 0, uninstallData: "0x", hookUninstallDatas: [hookUninstallData], }); @@ -260,14 +272,17 @@ describe("MA v2 Tests", async () => { const givenConnectedProvider = async ({ signer, accountAddress, + usePaymaster = false, }: { signer: SmartAccountSigner; accountAddress?: `0x${string}`; + usePaymaster?: boolean; }): Promise => createSMAV2AccountClient({ chain: instance.chain, signer, accountAddress, transport: custom(instance.getClient()), + ...(usePaymaster ? erc7677Middleware() : {}), }); }); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index 3d023d36a1..ac7eac885c 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -14,6 +14,7 @@ import { type Transport, type Hex, type Address, + zeroAddress, } from "viem"; import { concatHex, encodeFunctionData } from "viem"; @@ -130,7 +131,10 @@ export async function createSMAV2AccountClient({ hooks, overrides, }: InstallValidationParams) => { - if (validationConfig.entityId === 0) { + if ( + validationConfig.entityId === 0 && + validationConfig.moduleAddress !== zeroAddress + ) { throw new EntityIdOverrideError(); } From 5cf06341805882a22ffe0d022e7b9d46a32ed05a Mon Sep 17 00:00:00 2001 From: linna Date: Thu, 19 Dec 2024 18:12:10 -0500 Subject: [PATCH 23/45] feat: adds tests that checks that validation fails with no paymaster --- .../src/ma-v2/client/client.test.ts | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 07a3f76ee6..dc0b4d5894 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -269,6 +269,85 @@ describe("MA v2 Tests", async () => { ).resolves.not.toThrowError(); }); + it("installs paymaster guard module, verifies use of invalid paymaster, then uninstalls module", async () => { + let provider = await givenConnectedProvider({ signer, usePaymaster: true }); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const paymaster = paymaster070.getPaymasterStubData(); + + const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ + entityId: 0, + paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE + }); + + const installResult = await provider.installValidation({ + validationConfig: { + moduleAddress: zeroAddress, + entityId: 0, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: "0x", + hooks: [ + { + hookConfig: { + address: getDefaultPaymasterGuardModuleAddress(provider.chain), + entityId: 0, // uint32 + hookType: HookType.VALIDATION, + hasPreHooks: true, + hasPostHooks: true, + }, + initData: hookInstallData, + }, + ], + }); + + // verify hook installtion succeeded + await expect( + provider.waitForUserOperationTransaction(installResult) + ).resolves.not.toThrowError(); + + // sad path: with wrong paymaster + let providerNoPaymaster = await givenConnectedProvider({ + signer, + usePaymaster: false, + }); + + // TO DO: verify if correct paymaster is used + await expect( + providerNoPaymaster.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }) + ).rejects.toThrowError(); + + const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ + entityId: 0, + }); + + console.log("paymaster" in paymaster ? paymaster.paymaster : "0x0"); + const uninstallResult = await provider.uninstallValidation({ + moduleAddress: zeroAddress, + entityId: 0, + uninstallData: "0x", + hookUninstallDatas: [hookUninstallData], + }); + + // verify uninstall + await expect( + provider.waitForUserOperationTransaction(uninstallResult) + ).resolves.not.toThrowError(); + }); + const givenConnectedProvider = async ({ signer, accountAddress, From 9c744bb14b2fd30a7b4c00036abf263136e5d739 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Mon, 16 Dec 2024 17:18:35 -0500 Subject: [PATCH 24/45] feat: add email OTP support (#1163) * feat: otp control before cleanup * feat: storybook testing * feat: update styles to match figma * fix: otp-loading * feat: pulling relevant code from draft PR 1163 * fix: update yarn.lock * feat: verifying otp state * feat: otp sends from otp completing * feat: scaffolding for react native OTP * feat: add email OTP support * feat: add otp widget and error handling * docs: add OTP email docs --------- Co-authored-by: rob chang Co-authored-by: Rob <30500864+RobChangCA@users.noreply.github.com> --- account-kit/core/src/store/store.ts | 14 +- account-kit/react/package.json | 3 +- .../auth/card/footer/email-not-reveived.tsx | 3 +- .../react/src/components/auth/card/index.tsx | 3 + .../components/auth/card/loading/email.tsx | 8 +- .../components/auth/card/loading/oauth.tsx | 7 +- .../auth/card/loading/otp.stories.tsx | 27 +++ .../src/components/auth/card/loading/otp.tsx | 124 +++++++++++ .../react/src/components/auth/card/steps.tsx | 5 + .../react/src/components/auth/context.ts | 10 +- .../components/auth/hooks/usePasskeyVerify.ts | 2 +- .../components/auth/sections/EmailAuth.tsx | 10 +- .../src/components/auth/sections/Footer.tsx | 2 + .../src/components/auth/sections/OAuth.tsx | 9 +- .../react/src/components/auth/types.ts | 1 + .../otp-input/otp-input.stories.tsx | 167 ++++++++++++++ .../src/components/otp-input/otp-input.tsx | 172 +++++++++++++++ account-kit/react/src/strings.ts | 15 ++ account-kit/react/src/utils.ts | 4 +- .../rn-signer/example/src/screens/Home.tsx | 3 +- account-kit/rn-signer/src/client.ts | 10 + account-kit/signer/src/base.ts | 120 +++++++--- account-kit/signer/src/client/base.ts | 7 +- account-kit/signer/src/client/index.ts | 49 ++++- account-kit/signer/src/client/types.ts | 22 +- account-kit/signer/src/metrics.ts | 7 +- account-kit/signer/src/session/manager.ts | 91 +++++--- account-kit/signer/src/session/types.ts | 2 +- account-kit/signer/src/signer.ts | 11 +- account-kit/signer/src/types.ts | 1 + examples/ui-demo/src/app/config.tsx | 2 +- examples/ui-demo/src/app/sections.ts | 2 +- site/pages/core/add-passkey.mdx | 2 +- site/pages/core/authenticate-users.mdx | 2 +- .../pages/react-native/signer/setup-guide.mdx | 2 +- site/pages/react/add-passkey.mdx | 2 +- site/pages/react/react-hooks.mdx | 2 +- site/pages/react/ui-components.mdx | 4 +- .../AlchemySignerWebClient/submitOtpCode.mdx | 49 +++++ .../authentication/email-magic-link.mdx | 9 +- .../pages/signer/authentication/email-otp.mdx | 39 ++++ .../signer/authentication/passkey-signup.mdx | 2 +- .../signer/authentication/social-login.mdx | 2 +- site/shared/get-api-key.mdx | 2 +- site/sidebar/signer.ts | 4 + yarn.lock | 208 +++++++----------- 46 files changed, 1008 insertions(+), 234 deletions(-) create mode 100644 account-kit/react/src/components/auth/card/loading/otp.stories.tsx create mode 100644 account-kit/react/src/components/auth/card/loading/otp.tsx create mode 100644 account-kit/react/src/components/otp-input/otp-input.stories.tsx create mode 100644 account-kit/react/src/components/otp-input/otp-input.tsx create mode 100644 site/pages/reference/account-kit/signer/classes/AlchemySignerWebClient/submitOtpCode.mdx create mode 100644 site/pages/signer/authentication/email-otp.mdx diff --git a/account-kit/core/src/store/store.ts b/account-kit/core/src/store/store.ts index ae03d51433..cb421afbba 100644 --- a/account-kit/core/src/store/store.ts +++ b/account-kit/core/src/store/store.ts @@ -209,6 +209,14 @@ export const createSigner = (params: ClientStoreConfig) => { return signer; }; +const AUTHENTICATING_STATUSES: AlchemySignerStatus[] = [ + AlchemySignerStatus.AUTHENTICATING_EMAIL, + AlchemySignerStatus.AUTHENTICATING_OAUTH, + AlchemySignerStatus.AUTHENTICATING_PASSKEY, + AlchemySignerStatus.AWAITING_EMAIL_AUTH, + AlchemySignerStatus.AWAITING_OTP_AUTH, +]; + /** * Converts the AlchemySigner's status to a more readable object * @@ -223,11 +231,7 @@ export const convertSignerStatusToState = ( status: alchemySignerStatus, error, isInitializing: alchemySignerStatus === AlchemySignerStatus.INITIALIZING, - isAuthenticating: - alchemySignerStatus === AlchemySignerStatus.AUTHENTICATING_EMAIL || - alchemySignerStatus === AlchemySignerStatus.AUTHENTICATING_OAUTH || - alchemySignerStatus === AlchemySignerStatus.AUTHENTICATING_PASSKEY || - alchemySignerStatus === AlchemySignerStatus.AWAITING_EMAIL_AUTH, + isAuthenticating: AUTHENTICATING_STATUSES.includes(alchemySignerStatus), isConnected: alchemySignerStatus === AlchemySignerStatus.CONNECTED, isDisconnected: alchemySignerStatus === AlchemySignerStatus.DISCONNECTED, }); diff --git a/account-kit/react/package.json b/account-kit/react/package.json index b39e456c3a..afa2b60743 100644 --- a/account-kit/react/package.json +++ b/account-kit/react/package.json @@ -47,10 +47,11 @@ }, "devDependencies": { "@storybook/addon-essentials": "^8.2.8", - "@storybook/addon-interactions": "^8.2.8", + "@storybook/addon-interactions": "^8.4.4", "@storybook/core-server": "^8.2.8", "@storybook/jest": "^0.2.3", "@storybook/react-vite": "^8.2.8", + "@storybook/test": "^8.4.4", "@storybook/test-runner": "^0.13.0", "@storybook/testing-library": "^0.2.2", "@tanstack/react-query": "^5.28.9", diff --git a/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx b/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx index 304392a255..18d62fa577 100644 --- a/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx +++ b/account-kit/react/src/components/auth/card/footer/email-not-reveived.tsx @@ -5,7 +5,7 @@ import { useAuthContext, type AuthStep } from "../../context.js"; import { Button } from "../../../button.js"; type EmailNotReceivedDisclaimerProps = { - authStep: Extract; + authStep: Extract; }; export const EmailNotReceivedDisclaimer = ({ authStep, @@ -40,6 +40,7 @@ export const EmailNotReceivedDisclaimer = ({ authenticate({ type: "email", email: authStep.email, + emailMode: authStep.type === "email_verify" ? "magicLink" : "otp", }); setEmailResent(true); }} diff --git a/account-kit/react/src/components/auth/card/index.tsx b/account-kit/react/src/components/auth/card/index.tsx index 5711e8ac08..260259ad86 100644 --- a/account-kit/react/src/components/auth/card/index.tsx +++ b/account-kit/react/src/components/auth/card/index.tsx @@ -73,6 +73,7 @@ export const AuthCardContent = ({ const canGoBack = useMemo(() => { return [ "email_verify", + "otp_verify", "passkey_verify", "passkey_create", "pick_eoa", @@ -85,9 +86,11 @@ export const AuthCardContent = ({ const onBack = useCallback(() => { switch (authStep.type) { case "email_verify": + case "otp_verify": case "passkey_verify": case "passkey_create": case "oauth_completing": + case "otp_completing": disconnect(config); // Terminate any inflight authentication didGoBack.current = true; setAuthStep({ type: "initial" }); diff --git a/account-kit/react/src/components/auth/card/loading/email.tsx b/account-kit/react/src/components/auth/card/loading/email.tsx index 9a593dfb7f..f0226f3ff2 100644 --- a/account-kit/react/src/components/auth/card/loading/email.tsx +++ b/account-kit/react/src/components/auth/card/loading/email.tsx @@ -39,15 +39,13 @@ export const LoadingEmail = () => { // eslint-disable-next-line jsdoc/require-jsdoc export const CompletingEmailAuth = () => { const { isConnected } = useSignerStatus(); - const { setAuthStep, authStep } = useAuthContext("email_completing"); + const { setAuthStep } = useAuthContext("email_completing"); useEffect(() => { - if (isConnected && authStep.createPasskeyAfter) { - setAuthStep({ type: "passkey_create" }); - } else if (isConnected) { + if (isConnected) { setAuthStep({ type: "complete" }); } - }, [authStep.createPasskeyAfter, isConnected, setAuthStep]); + }, [isConnected, setAuthStep]); return (
diff --git a/account-kit/react/src/components/auth/card/loading/oauth.tsx b/account-kit/react/src/components/auth/card/loading/oauth.tsx index a61068d210..689901d7e5 100644 --- a/account-kit/react/src/components/auth/card/loading/oauth.tsx +++ b/account-kit/react/src/components/auth/card/loading/oauth.tsx @@ -23,12 +23,7 @@ export const CompletingOAuth = () => { } else if (oauthWasCancelled) { setAuthStep({ type: "initial" }); } - }, [ - authStep.createPasskeyAfter, - isConnected, - oauthWasCancelled, - setAuthStep, - ]); + }, [isConnected, oauthWasCancelled, setAuthStep]); if (authStep.error && !oauthWasCancelled) { return ( diff --git a/account-kit/react/src/components/auth/card/loading/otp.stories.tsx b/account-kit/react/src/components/auth/card/loading/otp.stories.tsx new file mode 100644 index 0000000000..5265359e41 --- /dev/null +++ b/account-kit/react/src/components/auth/card/loading/otp.stories.tsx @@ -0,0 +1,27 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { useEffect } from "react"; +import { useAuthContext } from "../../context.js"; + +import { LoadingOtp } from "./otp.js"; +import type { Meta, StoryObj } from "@storybook/react"; + +const meta: Meta = { + title: "OTP/LoadingOTP", + component: LoadingOtp, + parameters: {}, + render: () => { + const { authStep, setAuthStep } = useAuthContext(); + useEffect(() => { + setAuthStep({ type: "otp_verify", email: "test@alchemy.com" }); + }, [setAuthStep]); + if (authStep.type === "otp_verify") { + return ; + } + + return

initializing story

; + }, +}; +export default meta; + +type Story = StoryObj; +export const Default: Story = {}; diff --git a/account-kit/react/src/components/auth/card/loading/otp.tsx b/account-kit/react/src/components/auth/card/loading/otp.tsx new file mode 100644 index 0000000000..542da7c2db --- /dev/null +++ b/account-kit/react/src/components/auth/card/loading/otp.tsx @@ -0,0 +1,124 @@ +import { useEffect, useState } from "react"; +import { EmailIllustration } from "../../../../icons/illustrations/email.js"; +import { ls } from "../../../../strings.js"; +import { + OTPInput, + type OTPCodeType, + initialOTPValue, + isOTPCodeType, +} from "../../../otp-input/otp-input.js"; +import { Spinner } from "../../../../icons/spinner.js"; +import { useAuthContext } from "../../context.js"; +import { useAuthenticate } from "../../../../hooks/useAuthenticate.js"; +import { useSignerStatus } from "../../../../hooks/useSignerStatus.js"; + +export const LoadingOtp = () => { + const { isConnected } = useSignerStatus(); + const { authStep, setAuthStep } = useAuthContext("otp_verify"); + const [otpCode, setOtpCode] = useState(initialOTPValue); + const [errorText, setErrorText] = useState( + getUserErrorMessage(authStep.error) + ); + const resetOTP = () => { + setOtpCode(initialOTPValue); + setErrorText(""); + }; + const { authenticate } = useAuthenticate({ + onError: (error: any) => { + console.error(error); + const { email } = authStep; + setAuthStep({ type: "otp_verify", email, error }); + }, + onSuccess: () => { + if (isConnected) { + setAuthStep({ type: "complete" }); + } + }, + }); + + useEffect(() => { + if (isOTPCodeType(otpCode)) { + setAuthStep({ + type: "otp_completing", + email: authStep.email, + otp: otpCode.join(""), + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [otpCode]); + + const setValue = (otpCode: OTPCodeType) => { + setOtpCode(otpCode); + if (isOTPCodeType(otpCode)) { + const otp = otpCode.join(""); + setAuthStep({ + type: "otp_completing", + email: authStep.email, + otp, + }); + authenticate({ type: "otp", otpCode: otp }); + } + }; + + return ( +
+
+ + +
+

+ {ls.loadingOtp.title} +

+

+ {ls.loadingOtp.body} +

+

+ {authStep.email} +

+ +
+ ); +}; + +export const CompletingOtp = () => { + return ( +
+
+ +
+

+ {ls.completingOtp.title} +

+

+ {ls.completingOtp.body} +

+
+ ); +}; + +function getUserErrorMessage(error: Error | undefined): string { + if (!error) { + return ""; + } + // Errors from Alchemy have a JSON message. + try { + const message = JSON.parse(error.message).error; + if (message === "invalid OTP code") { + return ls.error.otp.invalid; + } + return message; + } catch (e) { + // Ignore + } + return error.message; +} diff --git a/account-kit/react/src/components/auth/card/steps.tsx b/account-kit/react/src/components/auth/card/steps.tsx index 3a25ea1ce8..a6a9eae177 100644 --- a/account-kit/react/src/components/auth/card/steps.tsx +++ b/account-kit/react/src/components/auth/card/steps.tsx @@ -6,18 +6,23 @@ import { CompletingOAuth } from "./loading/oauth.js"; import { LoadingPasskeyAuth } from "./loading/passkey.js"; import { MainAuthContent } from "./main.js"; import { PasskeyAdded } from "./passkey-added.js"; +import { CompletingOtp, LoadingOtp } from "./loading/otp.js"; export const Step = () => { const { authStep } = useAuthContext(); switch (authStep.type) { case "email_verify": return ; + case "otp_verify": + return ; case "passkey_verify": return ; case "email_completing": return ; case "oauth_completing": return ; + case "otp_completing": + return ; case "passkey_create": return ; case "passkey_create_success": diff --git a/account-kit/react/src/components/auth/context.ts b/account-kit/react/src/components/auth/context.ts index d889eb4ad1..61827a9712 100644 --- a/account-kit/react/src/components/auth/context.ts +++ b/account-kit/react/src/components/auth/context.ts @@ -6,14 +6,20 @@ import type { AuthType } from "./types"; export type AuthStep = | { type: "email_verify"; email: string } + | { type: "otp_verify"; email: string; error?: Error } | { type: "passkey_verify"; error?: Error } | { type: "passkey_create"; error?: Error } | { type: "passkey_create_success" } - | { type: "email_completing"; createPasskeyAfter?: boolean } + | { type: "email_completing" } | { type: "oauth_completing"; config: Extract; - createPasskeyAfter?: boolean; + error?: Error; + } + | { + type: "otp_completing"; + email: string; + otp: string; error?: Error; } | { type: "initial"; error?: Error } diff --git a/account-kit/react/src/components/auth/hooks/usePasskeyVerify.ts b/account-kit/react/src/components/auth/hooks/usePasskeyVerify.ts index 48d0c39bd9..ab626254fa 100644 --- a/account-kit/react/src/components/auth/hooks/usePasskeyVerify.ts +++ b/account-kit/react/src/components/auth/hooks/usePasskeyVerify.ts @@ -10,7 +10,7 @@ export type UsePasskeyVerifyReturnType = { /** * Internal UI component hook used to complete passkey verification. * - * This is used to login with a passkey, not create a new passkey + * This is used to log in with a passkey, not create a new passkey * * @returns {UsePasskeyVerifyReturnType} an authenticate function to do passkey verification and a boolean indicating if the operation is pending */ diff --git a/account-kit/react/src/components/auth/sections/EmailAuth.tsx b/account-kit/react/src/components/auth/sections/EmailAuth.tsx index 367f67b92f..8b335bb084 100644 --- a/account-kit/react/src/components/auth/sections/EmailAuth.tsx +++ b/account-kit/react/src/components/auth/sections/EmailAuth.tsx @@ -19,6 +19,7 @@ type EmailAuthProps = Extract; // eslint-disable-next-line jsdoc/require-jsdoc export const EmailAuth = memo( ({ + emailMode = "otp", hideButton = false, buttonLabel = ls.login.email.button, placeholder = ls.login.email.placeholder, @@ -27,8 +28,12 @@ export const EmailAuth = memo( const signer = useSigner(); const { authenticateAsync, isPending } = useAuthenticate({ onMutate: async (params) => { - if ("email" in params) { - setAuthStep({ type: "email_verify", email: params.email }); + if (params.type === "email" && "email" in params) { + if (params.emailMode === "magicLink") { + setAuthStep({ type: "email_verify", email: params.email }); + } else { + setAuthStep({ type: "otp_verify", email: params.email }); + } } }, onSuccess: () => { @@ -56,6 +61,7 @@ export const EmailAuth = memo( await authenticateAsync({ type: "email", email, + emailMode, redirectParams, }); } catch (e) { diff --git a/account-kit/react/src/components/auth/sections/Footer.tsx b/account-kit/react/src/components/auth/sections/Footer.tsx index 679a3d7e39..40ae4781b3 100644 --- a/account-kit/react/src/components/auth/sections/Footer.tsx +++ b/account-kit/react/src/components/auth/sections/Footer.tsx @@ -14,6 +14,7 @@ const RenderFooterText = ({ authStep }: FooterProps) => { case "initial": return ; case "email_verify": + case "otp_verify": return ; case "passkey_create": case "wallet_connect": @@ -22,6 +23,7 @@ const RenderFooterText = ({ authStep }: FooterProps) => { case "oauth_completing": return ; case "email_completing": + case "otp_completing": case "passkey_create_success": case "eoa_connect": case "pick_eoa": diff --git a/account-kit/react/src/components/auth/sections/OAuth.tsx b/account-kit/react/src/components/auth/sections/OAuth.tsx index 4dcc9c00d3..2e2b2e5e3b 100644 --- a/account-kit/react/src/components/auth/sections/OAuth.tsx +++ b/account-kit/react/src/components/auth/sections/OAuth.tsx @@ -40,8 +40,11 @@ export const OAuth = memo(({ ...config }: Props) => { > ); default: - assertNever("unhandled authProviderId passed into auth sections"); + assertNever( + config, + `unhandled authProviderId ${ + (config as any).authProviderId + } passed into auth sections` + ); } - - throw Error("unhandled authProviderId passed into auth sections"); }); diff --git a/account-kit/react/src/components/auth/types.ts b/account-kit/react/src/components/auth/types.ts index f31b4a96b7..df92307c23 100644 --- a/account-kit/react/src/components/auth/types.ts +++ b/account-kit/react/src/components/auth/types.ts @@ -8,6 +8,7 @@ export type AuthType = | { // TODO: this should support setting redirectParams which will be added to the email redirect type: "email"; + emailMode?: "magicLink" | "otp"; hideButton?: boolean; buttonLabel?: string; placeholder?: string; diff --git a/account-kit/react/src/components/otp-input/otp-input.stories.tsx b/account-kit/react/src/components/otp-input/otp-input.stories.tsx new file mode 100644 index 0000000000..a7ccbd5a59 --- /dev/null +++ b/account-kit/react/src/components/otp-input/otp-input.stories.tsx @@ -0,0 +1,167 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import type { Meta, StoryObj } from "@storybook/react"; +import { initialOTPValue, OTPInput, type OTPCodeType } from "./otp-input.jsx"; +import { userEvent, within, expect, waitFor } from "@storybook/test"; +import { useState } from "react"; + +const meta: Meta = { + component: OTPInput, + title: "OTP/OTPInput", + render: (args) => { + const [value, setValue] = useState(initialOTPValue); + const [errorText, setErrorText] = useState(""); + const handleReset = () => { + setErrorText(""); + setValue(initialOTPValue); + }; + return ( + + ); + }, +}; +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const TestTyping: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const inputs = canvas.getAllByRole("textbox"); + + // Test typing individual numbers + await userEvent.type(inputs[0], "1"); + await userEvent.type(inputs[1], "2"); + await userEvent.type(inputs[2], "3"); + await userEvent.type(inputs[3], "4"); + await userEvent.type(inputs[4], "5"); + await userEvent.type(inputs[5], "6"); + + // Verify values + expect(inputs[0]).toHaveValue("1"); + expect(inputs[1]).toHaveValue("2"); + expect(inputs[2]).toHaveValue("3"); + expect(inputs[3]).toHaveValue("4"); + expect(inputs[4]).toHaveValue("5"); + expect(inputs[5]).toHaveValue("6"); + }, +}; + +export const TestPasting: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const inputs = canvas.getAllByRole("textbox"); + + // Simulate paste event + const clipboardData = new DataTransfer(); + clipboardData.setData("text/plain", "123456"); + + const pasteEvent = new ClipboardEvent("paste", { + clipboardData, + bubbles: true, + cancelable: true, + }); + + inputs[0].dispatchEvent(pasteEvent); + + await waitFor(() => { + expect(inputs[0]).toHaveValue("1"); + expect(inputs[1]).toHaveValue("2"); + expect(inputs[2]).toHaveValue("3"); + expect(inputs[3]).toHaveValue("4"); + expect(inputs[4]).toHaveValue("5"); + expect(inputs[5]).toHaveValue("6"); + }); + }, +}; + +export const TestKeyboardNavigation: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const inputs = canvas.getAllByRole("textbox"); + + // Focus first input + await userEvent.click(inputs[0]); + expect(document.activeElement).toBe(inputs[0]); + + // Test right arrow + await userEvent.keyboard("{ArrowRight}"); + expect(document.activeElement).toBe(inputs[1]); + + // Test left arrow + await userEvent.keyboard("{ArrowLeft}"); + expect(document.activeElement).toBe(inputs[0]); + + // Test backspace + await userEvent.type(inputs[0], "1"); + await userEvent.keyboard("{Backspace}"); + await userEvent.keyboard("{Backspace}"); + expect(inputs[0]).toHaveValue(""); + expect(document.activeElement).toBe(inputs[0]); + await userEvent.type(inputs[0], "1"); + await userEvent.keyboard("23456"); + }, +}; + +export const TestAutoFocus: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const inputs = canvas.getAllByRole("textbox"); + + // Check if first input is focused on mount + expect(document.activeElement).toBe(inputs[0]); + }, +}; + +export const TestErrorState: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + // Test invalid paste + const inputs = canvas.getAllByRole("textbox"); + const clipboardData = new DataTransfer(); + clipboardData.setData("text/plain", "123"); // Invalid length + + const pasteEvent = new ClipboardEvent("paste", { + clipboardData, + bubbles: true, + cancelable: true, + }); + + inputs[0].dispatchEvent(pasteEvent); + + // Check for error message + await waitFor(() => { + const errorMessage = canvas.getByText( + /The code you entered is incorrect/i + ); + expect(errorMessage).toBeInTheDocument(); + }); + }, +}; + +export const TestDisabledState: Story = { + args: { + disabled: true, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const inputs = canvas.getAllByRole("textbox"); + + // Verify all inputs are disabled + inputs.forEach((input) => { + expect(input).toBeDisabled(); + }); + + // Try to type (should not work) + await userEvent.type(inputs[0], "1"); + expect(inputs[0]).toHaveValue(""); + }, +}; diff --git a/account-kit/react/src/components/otp-input/otp-input.tsx b/account-kit/react/src/components/otp-input/otp-input.tsx new file mode 100644 index 0000000000..b1297af972 --- /dev/null +++ b/account-kit/react/src/components/otp-input/otp-input.tsx @@ -0,0 +1,172 @@ +import React, { useEffect, useRef, useState } from "react"; +import { ls } from "../../strings.js"; + +export type OTPCodeType = [string, string, string, string, string, string]; +export const initialOTPValue: OTPCodeType = ["", "", "", "", "", ""]; +const OTP_LENGTH = 6; +type OTPInputProps = { + errorText?: string; + value: OTPCodeType; + setValue: (otp: OTPCodeType) => void; + setErrorText: (error: string) => void; + disabled?: boolean; + handleReset: () => void; + className?: string; +}; + +export const isOTPCodeType = (arg: string[]): arg is OTPCodeType => { + return ( + Array.isArray(arg) && + arg.every((item: string) => typeof item === "string" && item !== "") && + arg.length === OTP_LENGTH + ); +}; + +export const OTPInput: React.FC = ({ + value, + setValue, + errorText, + disabled, + setErrorText, + handleReset, + className, +}) => { + const [autoComplete, setAutoComplete] = useState(""); + const [activeElement, setActiveElement] = useState(0); + + const refs = useRef>([]); + // Initialize refs + useEffect(() => { + refs.current = refs.current.slice(0, OTP_LENGTH); + refs.current[0]?.focus(); + }, []); + // Select active element when active element value changes + useEffect(() => { + if (activeElement !== null && refs.current[activeElement]) { + refs.current[activeElement]?.select(); + refs.current[activeElement]?.focus(); + } + }, [activeElement]); + + useEffect(() => { + const newValue = autoComplete.split(""); + if (isOTPCodeType(newValue)) { + setValue(newValue); + } + }, [autoComplete, setValue]); + + const handleChange = (e: React.ChangeEvent, i: number) => { + //Fix for ios chrome autocomplete + if (e.target.value.length === OTP_LENGTH) { + const chromeIOSAutocomplete = e.target.value.split(""); + if (isOTPCodeType(chromeIOSAutocomplete)) { + setValue(chromeIOSAutocomplete); + setActiveElement(null); + return; + } + } + const newValue = [...value] as OTPCodeType; + newValue.splice(i, 1, e.target.value); + setErrorText(""); + setValue(newValue); + focusNextElement(); + }; + + const handleClick = (i: number) => { + refs.current[i]?.select(); + setActiveElement(i); + setErrorText(""); + }; + + const focusNextElement = () => { + const nextElement = activeElement ? activeElement + 1 : 1; + setActiveElement(nextElement); + }; + + const focusPreviousElement = () => { + const previousElement = activeElement ? activeElement - 1 : 0; + setActiveElement(previousElement); + }; + + const handlePaste = (e: React.ClipboardEvent) => { + e.preventDefault(); + const pasteData = e.clipboardData + .getData("text/plain") + .split("") + .slice(0, OTP_LENGTH); + if (isOTPCodeType(pasteData)) { + setValue(pasteData); + } else { + setErrorText(ls.error.otp.invalid); + } + }; + + const handleKeydown = (e: React.KeyboardEvent) => { + if (activeElement === null) return; + switch (e.key) { + case "Backspace": + e.preventDefault(); + const newValue = [...value] as OTPCodeType; + newValue.splice(activeElement, 1, ""); + setValue(newValue); + focusPreviousElement(); + break; + case "ArrowLeft": + e.preventDefault(); + focusPreviousElement(); + break; + case "ArrowRight": + e.preventDefault(); + if (activeElement < OTP_LENGTH - 1) focusNextElement(); + break; + case "Spacebar": { + break; + } + } + }; + + return ( +
+ {/* Input for autocomplete, visibility hidden */} + setAutoComplete(e.target.value)} + onClick={handleReset} + /> +
+ {initialOTPValue.map((_, i) => ( + (refs.current[i] = el)} + tabIndex={i + 1} + type="text" + aria-label={`One time password input for the ${i + 1} digit`} + inputMode="numeric" + pattern="[0-9]*" + //Fix for ios chrome autocomplete + maxLength={i === 0 ? OTP_LENGTH : 1} + onFocus={() => setActiveElement(i)} + onPaste={handlePaste} + onChange={(e) => handleChange(e, i)} + onClick={() => handleClick(i)} + onInput={focusNextElement} + onKeyDown={handleKeydown} + key={i} + disabled={disabled} + value={value[i]} + aria-invalid={!!errorText} + /> + ))} +
+ {errorText && ( +

{errorText}

+ )} +
+ ); +}; diff --git a/account-kit/react/src/strings.ts b/account-kit/react/src/strings.ts index 584269415c..a5680833bf 100644 --- a/account-kit/react/src/strings.ts +++ b/account-kit/react/src/strings.ts @@ -30,9 +30,19 @@ const STRINGS = { resend: "Resend", resent: "Done!", }, + loadingOtp: { + title: "Enter verification code", + body: "We sent a verification code to", + notReceived: "Didn't receive code?", + resend: "Resend", + }, completingEmail: { body: "Completing login. Please wait a few seconds for this to screen to update.", }, + completingOtp: { + title: "Verifying...", + body: "It may take a moment to complete authentication.", + }, loadingPasskey: { title: "Continue with passkey", body: "Follow the prompt to verify your passkey.", @@ -53,6 +63,8 @@ const STRINGS = { "Passkey request timed out or canceled by the agent. You may have to use another method to register a passkey for your account.", oauthTitle: "Couldn't connect to ", oauthBody: "The connection failed or canceled", + otpTitle: "Connection failed", + otpBody: "The code could not be verified", walletTitle: "Couldn't connect to ", walletBody: "The wallet’s connection failed or canceled", timedOutTitle: "Connection timed out", @@ -84,6 +96,9 @@ const STRINGS = { }, }, }, + otp: { + invalid: "The code you entered is incorrect", + }, }, oauthContactSupport: { title: "Need help?", diff --git a/account-kit/react/src/utils.ts b/account-kit/react/src/utils.ts index 3fe233996a..8f9a11f773 100644 --- a/account-kit/react/src/utils.ts +++ b/account-kit/react/src/utils.ts @@ -7,8 +7,8 @@ export function capitalize(str: string) { .join(" "); } -export function assertNever(msg: string) { - throw new Error(msg); +export function assertNever(_: never, message: string): never { + throw new Error(message); } export function getWalletConnectParams( diff --git a/account-kit/rn-signer/example/src/screens/Home.tsx b/account-kit/rn-signer/example/src/screens/Home.tsx index bcd1d56110..ccf8ef0a88 100644 --- a/account-kit/rn-signer/example/src/screens/Home.tsx +++ b/account-kit/rn-signer/example/src/screens/Home.tsx @@ -73,11 +73,12 @@ export default function HomeScreen() { onChangeText={setEmail} value={email} /> + {/* TODO: implement OTP */} { signer - .authenticate({ email, type: "email" }) + .authenticate({ email, type: "email", emailMode: "magicLink" }) .catch(console.error); }} > diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 350a2195e3..2ba464ec6d 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -10,6 +10,7 @@ import { type GetWebAuthnAttestationResult, type OauthConfig, type OauthParams, + type OtpParams, type SignupResponse, type User, } from "@account-kit/signer"; @@ -35,6 +36,13 @@ export class RNSignerClient extends BaseSignerClient { connection, }); } + // TODO: implement OTP + override async submitOtpCode( + args: Omit + ): Promise<{ bundle: string }> { + console.log("submitOtpCode", args); + throw new Error("Method not implemented."); + } override async createAccount( params: CreateAccountParams @@ -49,6 +57,7 @@ export class RNSignerClient extends BaseSignerClient { const response = await this.request("/v1/signup", { email, + emailMode: "magicLink", targetPublicKey: publicKey, expirationSeconds, redirectParams: params.redirectParams?.toString(), @@ -65,6 +74,7 @@ export class RNSignerClient extends BaseSignerClient { return this.request("/v1/auth", { email: params.email, + emailMode: "magicLink", targetPublicKey, }); } diff --git a/account-kit/signer/src/base.ts b/account-kit/signer/src/base.ts index b07cbc107b..352f85145b 100644 --- a/account-kit/signer/src/base.ts +++ b/account-kit/signer/src/base.ts @@ -35,6 +35,7 @@ import { type ErrorInfo, } from "./types.js"; import { assertNever } from "./utils/typeAssertions.js"; +import type { SessionManagerEvents } from "./session/types"; export interface BaseAlchemySignerParams { client: TClient; @@ -46,6 +47,7 @@ type AlchemySignerStore = { user: User | null; status: AlchemySignerStatus; error: ErrorInfo | null; + otpId?: string; isNewUser?: boolean; }; @@ -238,6 +240,8 @@ export abstract class BaseAlchemySigner return this.authenticateWithOauth(params); case "oauthReturn": return this.handleOauthReturn(params); + case "otp": + return this.authenticateWithOtp(params); default: assertNever(type, `Unknown auth type: ${type}`); } @@ -296,6 +300,12 @@ export abstract class BaseAlchemySigner break; case "oauthReturn": break; + case "otp": + SignerLogger.trackEvent({ + name: "signer_authnticate", + data: { authType: "otp" }, + }); + break; default: assertNever(type, `Unknown auth type: ${type}`); } @@ -673,26 +683,30 @@ export abstract class BaseAlchemySigner ): Promise => { if ("email" in params) { const existingUser = await this.getUser(params.email); - const expirationSeconds = Math.floor( - this.sessionManager.expirationTimeMs / 1000 - ); + const expirationSeconds = this.getExpirationSeconds(); - const { orgId } = existingUser + const { orgId, otpId } = existingUser ? await this.inner.initEmailAuth({ email: params.email, + emailMode: params.emailMode, expirationSeconds, redirectParams: params.redirectParams, }) : await this.inner.createAccount({ type: "email", email: params.email, + emailMode: params.emailMode, expirationSeconds, redirectParams: params.redirectParams, }); - this.sessionManager.setTemporarySession({ orgId }); + this.sessionManager.setTemporarySession({ + orgId, + isNewUser: !existingUser, + }); this.store.setState({ status: AlchemySignerStatus.AWAITING_EMAIL_AUTH, + otpId, error: null, }); @@ -774,9 +788,7 @@ export abstract class BaseAlchemySigner ): Promise => { const params: OauthParams = { ...args, - expirationSeconds: Math.floor( - this.sessionManager.expirationTimeMs / 1000 - ), + expirationSeconds: this.getExpirationSeconds(), }; if (params.mode === "redirect") { return this.inner.oauthWithRedirect(params); @@ -785,6 +797,42 @@ export abstract class BaseAlchemySigner } }; + private authenticateWithOtp = async ( + args: Extract + ): Promise => { + const tempSession = this.sessionManager.getTemporarySession(); + const { orgId, isNewUser } = tempSession ?? {}; + const { otpId } = this.store.getState(); + if (!orgId) { + throw new Error("orgId not found in session"); + } + if (!otpId) { + throw new Error("otpId not found in session"); + } + const { bundle } = await this.inner.submitOtpCode({ + orgId, + otpId, + otpCode: args.otpCode, + expirationSeconds: this.getExpirationSeconds(), + }); + const user = await this.inner.completeAuthWithBundle({ + bundle, + orgId, + connectedEventName: "connectedOtp", + authenticatingType: "otp", + }); + + this.emitNewUserEvent(isNewUser); + if (tempSession) { + this.sessionManager.setTemporarySession({ + ...tempSession, + isNewUser: false, + }); + } + + return user; + }; + private handleOauthReturn = ({ bundle, orgId, @@ -804,30 +852,39 @@ export abstract class BaseAlchemySigner return user; }; - private registerListeners = () => { - this.sessionManager.on("connected", (session) => { - this.store.setState({ - user: session.user, - status: AlchemySignerStatus.CONNECTED, - error: null, - }); - }); + private getExpirationSeconds = () => + Math.floor(this.sessionManager.expirationTimeMs / 1000); - this.sessionManager.on("disconnected", () => { - this.store.setState({ - user: null, - status: AlchemySignerStatus.DISCONNECTED, - }); - }); + private registerListeners = () => { + // Declare listeners in an object to typecheck that every event type is + // handled. + const listeners: SessionManagerEvents = { + connected: (session) => { + this.store.setState({ + user: session.user, + status: AlchemySignerStatus.CONNECTED, + error: null, + }); + }, + disconnected: () => { + this.store.setState({ + user: null, + status: AlchemySignerStatus.DISCONNECTED, + }); + }, + initialized: () => { + this.store.setState((state) => ({ + status: state.user + ? AlchemySignerStatus.CONNECTED + : AlchemySignerStatus.DISCONNECTED, + ...(state.user ? { error: null } : undefined), + })); + }, + }; - this.sessionManager.on("initialized", () => { - this.store.setState((state) => ({ - status: state.user - ? AlchemySignerStatus.CONNECTED - : AlchemySignerStatus.DISCONNECTED, - ...(state.user ? { error: null } : undefined), - })); - }); + for (const [event, listener] of Object.entries(listeners)) { + this.sessionManager.on(event as keyof SessionManagerEvents, listener); + } this.inner.on("authenticating", ({ type }) => { const status = (() => { @@ -838,6 +895,9 @@ export abstract class BaseAlchemySigner return AlchemySignerStatus.AUTHENTICATING_PASSKEY; case "oauth": return AlchemySignerStatus.AUTHENTICATING_OAUTH; + case "otp": + case "otpVerify": + return AlchemySignerStatus.AWAITING_OTP_AUTH; default: assertNever(type, "unhandled authenticating type"); } diff --git a/account-kit/signer/src/client/base.ts b/account-kit/signer/src/client/base.ts index 169b7aba3e..dc734d8124 100644 --- a/account-kit/signer/src/client/base.ts +++ b/account-kit/signer/src/client/base.ts @@ -15,6 +15,7 @@ import type { GetWebAuthnAttestationResult, OauthConfig, OauthParams, + OtpParams, SignerBody, SignerResponse, SignerRoutes, @@ -126,7 +127,7 @@ export abstract class BaseSignerClient { public abstract initEmailAuth( params: Omit - ): Promise<{ orgId: string }>; + ): Promise<{ orgId: string; otpId?: string }>; public abstract completeAuthWithBundle(params: { bundle: string; @@ -144,6 +145,10 @@ export abstract class BaseSignerClient { args: Extract ): Promise; + public abstract submitOtpCode( + args: Omit + ): Promise<{ bundle: string }>; + public abstract disconnect(): Promise; public abstract exportWallet(params: TExportWalletParams): Promise; diff --git a/account-kit/signer/src/client/index.ts b/account-kit/signer/src/client/index.ts index 87fccdc50f..6169a663c1 100644 --- a/account-kit/signer/src/client/index.ts +++ b/account-kit/signer/src/client/index.ts @@ -18,6 +18,7 @@ import type { ExportWalletParams, OauthConfig, OauthParams, + OtpParams, User, } from "./types.js"; @@ -138,12 +139,13 @@ export class AlchemySignerWebClient extends BaseSignerClient */ public override createAccount = async (params: CreateAccountParams) => { if (params.type === "email") { - this.eventEmitter.emit("authenticating", { type: "email" }); - const { email, expirationSeconds } = params; + this.eventEmitter.emit("authenticating", { type: "otp" }); + const { email, emailMode, expirationSeconds } = params; const publicKey = await this.initIframeStamper(); const response = await this.request("/v1/signup", { email, + emailMode, targetPublicKey: publicKey, expirationSeconds, redirectParams: params.redirectParams?.toString(), @@ -205,18 +207,57 @@ export class AlchemySignerWebClient extends BaseSignerClient public override initEmailAuth = async ( params: Omit ) => { - this.eventEmitter.emit("authenticating", { type: "email" }); - const { email, expirationSeconds } = params; + this.eventEmitter.emit("authenticating", { type: "otp" }); + const { email, emailMode, expirationSeconds } = params; const publicKey = await this.initIframeStamper(); return this.request("/v1/auth", { email, + emailMode, targetPublicKey: publicKey, expirationSeconds, redirectParams: params.redirectParams?.toString(), }); }; + /** + * Authenticates using an OTP code which was previously received via email. + * + * @example + * ```ts + * import { AlchemySignerWebClient } from "@account-kit/signer"; + * + * const client = new AlchemySignerWebClient({ + * connection: { + * apiKey: "your-api-key", + * }, + * iframeConfig: { + * iframeContainerId: "signer-iframe-container", + * }, + * }); + * + * const account = await client.submitOtpCode({ + * orgId: "user-org-id", + * otpId: "opt-returned-from-initEmailAuth", + * otpCode: "otp-code-from-email", + * }); + * ``` + * + * @param {Omit} args The parameters for the OTP request, excluding the target public key. + * @returns {Promise<{ bundle: string }>} A promise that resolves to an object containing the credential bundle. + */ + public override async submitOtpCode( + args: Omit + ): Promise<{ bundle: string }> { + this.eventEmitter.emit("authenticating", { type: "otpVerify" }); + const targetPublicKey = await this.initIframeStamper(); + const { credentialBundle } = await this.request("/v1/otp", { + ...args, + targetPublicKey, + }); + return { bundle: credentialBundle }; + } + /** * Completes auth for the user by injecting a credential bundle and retrieving * the user information based on the provided organization ID. Emits events diff --git a/account-kit/signer/src/client/types.ts b/account-kit/signer/src/client/types.ts index 152c611f98..5da1d2e968 100644 --- a/account-kit/signer/src/client/types.ts +++ b/account-kit/signer/src/client/types.ts @@ -28,6 +28,7 @@ export type CreateAccountParams = | { type: "email"; email: string; + emailMode?: EmailType; expirationSeconds?: number; redirectParams?: URLSearchParams; } @@ -42,8 +43,11 @@ export type CreateAccountParams = creationOpts?: CredentialCreationOptionOverrides; }; +export type EmailType = "magicLink" | "otp"; + export type EmailAuthParams = { email: string; + emailMode?: EmailType; expirationSeconds?: number; targetPublicKey: string; redirectParams?: URLSearchParams; @@ -53,10 +57,19 @@ export type OauthParams = Extract & { expirationSeconds?: number; }; +export type OtpParams = { + orgId: string; + otpId: string; + otpCode: string; + targetPublicKey: string; + expirationSeconds?: number; +}; + export type SignupResponse = { orgId: string; userId?: string; address?: Address; + otpId?: string; }; export type OauthConfig = { @@ -107,6 +120,7 @@ export type SignerEndpoints = [ Body: Omit & { redirectParams?: string }; Response: { orgId: string; + otpId?: string; }; }, { @@ -133,11 +147,16 @@ export type SignerEndpoints = [ nonce: string; }; Response: OauthConfig; + }, + { + Route: "/v1/otp"; + Body: OtpParams; + Response: { credentialBundle: string }; } ]; export type AuthenticatingEventMetadata = { - type: "email" | "passkey" | "oauth"; + type: "email" | "passkey" | "oauth" | "otp" | "otpVerify"; }; export type AlchemySignerClientEvents = { @@ -147,6 +166,7 @@ export type AlchemySignerClientEvents = { connectedEmail(user: User, bundle: string): void; connectedPasskey(user: User): void; connectedOauth(user: User, bundle: string): void; + connectedOtp(user: User, bundle: string): void; disconnected(): void; }; diff --git a/account-kit/signer/src/metrics.ts b/account-kit/signer/src/metrics.ts index 09c8d05ed5..1ca747a37b 100644 --- a/account-kit/signer/src/metrics.ts +++ b/account-kit/signer/src/metrics.ts @@ -6,7 +6,12 @@ export type SignerEventsSchema = [ EventName: "signer_authnticate"; EventData: | { - authType: "email" | "passkey_anon" | "passkey_email" | "oauthReturn"; + authType: + | "email" + | "passkey_anon" + | "passkey_email" + | "otp" + | "oauthReturn"; provider?: never; } | { authType: "oauth"; provider: string }; diff --git a/account-kit/signer/src/session/manager.ts b/account-kit/signer/src/session/manager.ts index a44745f485..a61481d1f8 100644 --- a/account-kit/signer/src/session/manager.ts +++ b/account-kit/signer/src/session/manager.ts @@ -7,7 +7,11 @@ import { } from "zustand/middleware"; import { createStore, type Mutate, type StoreApi } from "zustand/vanilla"; import type { BaseSignerClient } from "../client/base"; -import type { User } from "../client/types"; +import type { + AlchemySignerClientEvent, + AlchemySignerClientEvents, + User, +} from "../client/types"; import { assertNever } from "../utils/typeAssertions.js"; import type { Session, SessionManagerEvents } from "./types"; @@ -39,6 +43,8 @@ type Store = Mutate< [["zustand/subscribeWithSelector", never], ["zustand/persist", SessionState]] >; +type TemporarySession = { orgId: string; isNewUser?: boolean }; + export class SessionManager { private sessionKey: string; private client: BaseSignerClient; @@ -85,11 +91,18 @@ export class SessionManager { switch (existingSession.type) { case "email": - case "oauth": { - const connectedEventName = - existingSession.type === "email" - ? "connectedEmail" - : "connectedOauth"; + case "oauth": + case "otp": { + const connectedEventName = (() => { + switch (existingSession.type) { + case "email": + return "connectedEmail"; + case "oauth": + return "connectedOauth"; + case "otp": + return "connectedOtp"; + } + })(); const result = await this.client .completeAuthWithBundle({ bundle: existingSession.bundle, @@ -133,7 +146,7 @@ export class SessionManager { } }; - public setTemporarySession = (session: { orgId: string }) => { + public setTemporarySession = (session: TemporarySession) => { // temporary session must be placed in localStorage so that it can be accessed across tabs localStorage.setItem( `${this.sessionKey}:temporary`, @@ -141,7 +154,7 @@ export class SessionManager { ); }; - public getTemporarySession = (): { orgId: string } | null => { + public getTemporarySession = (): TemporarySession | null => { // temporary session must be placed in localStorage so that it can be accessed across tabs const sessionStr = localStorage.getItem(`${this.sessionKey}:temporary`); @@ -187,7 +200,10 @@ export class SessionManager { private setSession = ( session_: - | Omit, "expirationDateMs"> + | Omit< + Extract, + "expirationDateMs" + > | Omit, "expirationDateMs"> ) => { const session = { @@ -230,28 +246,45 @@ export class SessionManager { } ); - this.client.on("disconnected", () => this.clearSession()); - - this.client.on("connectedEmail", (user, bundle) => - this.setSessionWithUserAndBundle({ type: "email", user, bundle }) - ); + // Helper type to ensure that a listener is either defined or explicitly + // omitted for every event type. + type Listeners = { + [K in keyof AlchemySignerClientEvents]: + | AlchemySignerClientEvents[K] + | undefined; + }; - this.client.on("connectedPasskey", (user) => { - const existingSession = this.getSession(); - if ( - existingSession != null && - existingSession.type === "passkey" && - existingSession.user.userId === user.userId - ) { - return; - } + const listeners: Listeners = { + connected: undefined, + newUserSignup: undefined, + authenticating: undefined, + connectedEmail: (user, bundle) => + this.setSessionWithUserAndBundle({ type: "email", user, bundle }), + connectedPasskey: (user) => { + const existingSession = this.getSession(); + if ( + existingSession != null && + existingSession.type === "passkey" && + existingSession.user.userId === user.userId + ) { + return; + } - this.setSession({ type: "passkey", user }); - }); + this.setSession({ type: "passkey", user }); + }, + connectedOauth: (user, bundle) => + this.setSessionWithUserAndBundle({ type: "oauth", user, bundle }), + connectedOtp: (user, bundle) => { + this.setSessionWithUserAndBundle({ type: "otp", user, bundle }); + }, + disconnected: () => this.clearSession(), + }; - this.client.on("connectedOauth", (user, bundle) => - this.setSessionWithUserAndBundle({ type: "oauth", user, bundle }) - ); + for (const [event, listener] of Object.entries(listeners)) { + if (listener) { + this.client.on(event as AlchemySignerClientEvent, listener); + } + } // sync local state if persisted state has changed from another tab // only do this in the browser @@ -295,7 +328,7 @@ export class SessionManager { user, bundle, }: { - type: "email" | "oauth"; + type: "email" | "oauth" | "otp"; user: User; bundle: string; }) => { diff --git a/account-kit/signer/src/session/types.ts b/account-kit/signer/src/session/types.ts index eda73c70fe..52cc9b4c41 100644 --- a/account-kit/signer/src/session/types.ts +++ b/account-kit/signer/src/session/types.ts @@ -2,7 +2,7 @@ import type { User } from "../client/types"; export type Session = | { - type: "email" | "oauth"; + type: "email" | "oauth" | "otp"; bundle: string; expirationDateMs: number; user: User; diff --git a/account-kit/signer/src/signer.ts b/account-kit/signer/src/signer.ts index 64585f152b..97aa36289d 100644 --- a/account-kit/signer/src/signer.ts +++ b/account-kit/signer/src/signer.ts @@ -8,7 +8,12 @@ import type { CredentialCreationOptionOverrides } from "./client/types.js"; import { SessionManagerParamsSchema } from "./session/manager.js"; export type AuthParams = - | { type: "email"; email: string; redirectParams?: URLSearchParams } + | { + type: "email"; + email: string; + emailMode?: "magicLink" | "otp"; + redirectParams?: URLSearchParams; + } | { type: "email"; bundle: string; orgId?: string; isNewUser?: boolean } | { type: "passkey"; @@ -37,6 +42,10 @@ export type AuthParams = orgId: string; idToken: string; isNewUser?: boolean; + } + | { + type: "otp"; + otpCode: string; }; export type OauthProviderConfig = diff --git a/account-kit/signer/src/types.ts b/account-kit/signer/src/types.ts index 9e17a05329..66f7cb931c 100644 --- a/account-kit/signer/src/types.ts +++ b/account-kit/signer/src/types.ts @@ -18,6 +18,7 @@ export enum AlchemySignerStatus { AUTHENTICATING_EMAIL = "AUTHENTICATING_EMAIL", AUTHENTICATING_OAUTH = "AUTHENTICATING_OAUTH", AWAITING_EMAIL_AUTH = "AWAITING_EMAIL_AUTH", + AWAITING_OTP_AUTH = "AWAITING_OTP_AUTH", } export interface ErrorInfo { diff --git a/examples/ui-demo/src/app/config.tsx b/examples/ui-demo/src/app/config.tsx index 6a7a36c2ec..d827762cf8 100644 --- a/examples/ui-demo/src/app/config.tsx +++ b/examples/ui-demo/src/app/config.tsx @@ -86,7 +86,7 @@ export const alchemyConfig = () => illustrationStyle: DEFAULT_CONFIG.ui.illustrationStyle, auth: { sections: [ - [{ type: "email" }], + [{ type: "email", emailMode: "otp" }], [ { type: "passkey" }, { type: "social", authProviderId: "google", mode: "popup" }, diff --git a/examples/ui-demo/src/app/sections.ts b/examples/ui-demo/src/app/sections.ts index 34251b5776..c8a05f197a 100644 --- a/examples/ui-demo/src/app/sections.ts +++ b/examples/ui-demo/src/app/sections.ts @@ -16,7 +16,7 @@ export function getSectionsForConfig( const sections: AuthType[][] = []; const midSection: AuthType[] = []; if (showEmail) { - sections.push([{ type: "email" }]); + sections.push([{ type: "email", emailMode: "otp" }]); } if (showPasskey) { midSection.push({ type: "passkey" }); diff --git a/site/pages/core/add-passkey.mdx b/site/pages/core/add-passkey.mdx index 9df6dd749a..f2362fe9fd 100644 --- a/site/pages/core/add-passkey.mdx +++ b/site/pages/core/add-passkey.mdx @@ -5,7 +5,7 @@ description: Learn how to add a passkey to your users' accounts with Account Kit # Add passkey -You may have noticed in the [Authenticate users](/core/authenticate-users) guide, that it's possible to authenticate users with a passkey they've already created. If you don't want users to have to type in an email to login with a passkey, this guide will cover how to add a passkey to a user's account once they have already logged in. +You may have noticed in the [Authenticate users](/core/authenticate-users) guide, that it's possible to authenticate users with a passkey they've already created. If you don't want users to have to type in an email to log in with a passkey, this guide will cover how to add a passkey to a user's account once they have already logged in. ## Authenticate the user diff --git a/site/pages/core/authenticate-users.mdx b/site/pages/core/authenticate-users.mdx index 3e2bf31d89..de75fcccee 100644 --- a/site/pages/core/authenticate-users.mdx +++ b/site/pages/core/authenticate-users.mdx @@ -93,7 +93,7 @@ await signer.authenticate({ ## Passkey auth with email backup -This approach will allow you to login or signup users using a passkey as the primary auth mechanism and register an email as a backup. +This approach will allow you to log in or sign up users using a passkey as the primary auth mechanism and register an email as a backup. :::danger It's important that you verify this email to be correct first! Otherwise, if a user loses their passkey, they will not be able to recover their account. diff --git a/site/pages/react-native/signer/setup-guide.mdx b/site/pages/react-native/signer/setup-guide.mdx index d80ffc95d4..7b21e29782 100644 --- a/site/pages/react-native/signer/setup-guide.mdx +++ b/site/pages/react-native/signer/setup-guide.mdx @@ -44,7 +44,7 @@ Only the Email Auth method is supported for the React Native Signer at this time - For testing, simply setting up a simple server on [http://localhost:3000](http://localhost:3000/) or any other server you control which redirects an incoming request to your app scheme is the easiest way to get started. a sample implementation of this can be found the `example` folder of the `React Native Signer` repo. - The http or https url you set up in the previous step would need to be added as the **Redirect URL** in the Account Kit Dashboard. - - This is the url that the user will be redirected to when they click the magic link in the email. + - This is the url that the user will be redirected to if you use the magic link email flow to log in your users. - Optionally stylize ✨ the email with your brand color and logo!
diff --git a/site/pages/react/add-passkey.mdx b/site/pages/react/add-passkey.mdx index 35014aa98b..c853ac79da 100644 --- a/site/pages/react/add-passkey.mdx +++ b/site/pages/react/add-passkey.mdx @@ -5,7 +5,7 @@ description: Learn how to add a passkey to your users' accounts with Account Kit # Add passkey -You may have noticed in the [Demo App](https://demo.alchemy.com/) that you can allow a user to login with an existing passkey, but there's no way to sign-up with a passkey. This guide will outline +You may have noticed in the [Demo App](https://demo.alchemy.com/) that you can allow a user to log in with an existing passkey, but there's no way to sign-up with a passkey. This guide will outline how to add a passkey to a user's account after they've signed up. If you're not sure how to authenticate your users, see [this guide](/react/getting-started). diff --git a/site/pages/react/react-hooks.mdx b/site/pages/react/react-hooks.mdx index b366383bef..1ad9099067 100644 --- a/site/pages/react/react-hooks.mdx +++ b/site/pages/react/react-hooks.mdx @@ -90,7 +90,7 @@ To use your own auth provider, follow the instructions on how to do so with [Aut ### Passkey auth with email backup -This approach will allow you to login or signup users using a passkey as the primary auth mechanism and register an email as a backup. +This approach will allow you to log in or sign up users using a passkey as the primary auth mechanism and register an email as a backup. :::danger It's important that you verify this email to be correct first! Otherwise, if a user loses their passkey, they will not be able to recover their account. diff --git a/site/pages/react/ui-components.mdx b/site/pages/react/ui-components.mdx index 055356545a..59d13f9c7a 100644 --- a/site/pages/react/ui-components.mdx +++ b/site/pages/react/ui-components.mdx @@ -102,7 +102,7 @@ type EmailAuthType = { ### Passkey -This adds a passkey login method to a section. Passkey login allows users to login with a passkey they've created else where in your app after logging in, or right after sign-up if you've enabled that setting. +This adds a passkey login method to a section. Passkey login allows users to log in with a passkey they've created else where in your app after logging in, or right after sign-up if you've enabled that setting. The shape of this auth type is: ```ts @@ -126,7 +126,7 @@ type ExternalWalletsAuthType = { ### Social -This adds social authentication methods to a section. Social login authentication allows you to login and signup users using an OAuth provider, such as Google Sign-In or Facebook Login. You can also configure custom providers through Auth0. The shape of this auth type is: +This adds social authentication methods to a section. Social login authentication allows you to log in and sign up users using an OAuth provider, such as Google Sign-In or Facebook Login. You can also configure custom providers through Auth0. The shape of this auth type is: ```ts type SocialAuthType = { diff --git a/site/pages/reference/account-kit/signer/classes/AlchemySignerWebClient/submitOtpCode.mdx b/site/pages/reference/account-kit/signer/classes/AlchemySignerWebClient/submitOtpCode.mdx new file mode 100644 index 0000000000..554d238c6e --- /dev/null +++ b/site/pages/reference/account-kit/signer/classes/AlchemySignerWebClient/submitOtpCode.mdx @@ -0,0 +1,49 @@ +--- +# This file is autogenerated + +title: submitOtpCode +description: Overview of the submitOtpCode method +--- + +# submitOtpCode + +Authenticates using an OTP code which was previously received via email. + +## Import + +```ts +import { AlchemySignerWebClient } from "@account-kit/signer"; +``` + +## Usage + +```ts +import { AlchemySignerWebClient } from "@account-kit/signer"; + +const client = new AlchemySignerWebClient({ + connection: { + apiKey: "your-api-key", + }, + iframeConfig: { + iframeContainerId: "signer-iframe-container", + }, +}); + +const account = await client.submitOtpCode({ + orgId: "user-org-id", + otpId: "opt-returned-from-initEmailAuth", + otpCode: "otp-code-from-email", +}); +``` + +## Parameters + +### args + +`Omit` +The parameters for the OTP request, excluding the target public key. + +## Returns + +`Promise<{ bundle: string }>` +A promise that resolves to an object containing the credential bundle. diff --git a/site/pages/signer/authentication/email-magic-link.mdx b/site/pages/signer/authentication/email-magic-link.mdx index 567cc86d8e..edcb48c56b 100644 --- a/site/pages/signer/authentication/email-magic-link.mdx +++ b/site/pages/signer/authentication/email-magic-link.mdx @@ -5,7 +5,13 @@ description: Authenticate a user using an email magic link # Email Magic Link Authentication -Email magic link authentication allows you to login and signup users using an email address. Your users will receive a link in their inbox which will redirect them to your site (configured in the dashboard) to complete login. +Email magic link authentication allows you to log in and sign up users using an email address. Your users will receive a link in their inbox which will redirect them to your site (configured in the dashboard) to complete login. + +:::tip +We recommend using the OTP email flow instead, as it is more reliable across different browser environments. OTP flows have also been shown to have up to a 3x higher conversion rate and a 10-second faster flow compared to magic link. + +For setting up the OTP flow, see [Email OTP Authentication](/signer/authentication/email-otp). +::: :::tip For setting up an account config, see the [Signer Quickstart](/signer/quickstart). @@ -21,6 +27,7 @@ import { signer } from "./signer"; // send the email await signer.authenticate({ type: "email", + emailMode: "magicLink", email: "user@mail.com", }); diff --git a/site/pages/signer/authentication/email-otp.mdx b/site/pages/signer/authentication/email-otp.mdx new file mode 100644 index 0000000000..ae7f016ac0 --- /dev/null +++ b/site/pages/signer/authentication/email-otp.mdx @@ -0,0 +1,39 @@ +--- +title: Email OTP Authentication +description: Authenticate a user using an email OTP code +--- + +# Email OTP Authentication + +Email magic link authentication allows you to log in and sign up users using an email address. Your users will receive six-digit code in their inbox which they can enter in your site to complete login. + +:::tip +For setting up an account config, see the [Signer Quickstart](/signer/quickstart). +::: + +## Authenticate a user + +:::code-group + +```ts twoslash [example.ts] +import { signer } from "./signer"; + +// send the email +await signer.authenticate({ + type: "email", + emailMode: "otp", + email: "user@mail.com", +}); + +// later once the user has entered the code from their email +await signer.authenticate({ + type: "otp", + otpCode: "123456", +}); +``` + +```ts twoslash [signer] filename="signer.ts" +// [!include ~/shared/signer/signer.ts] +``` + +::: diff --git a/site/pages/signer/authentication/passkey-signup.mdx b/site/pages/signer/authentication/passkey-signup.mdx index 3583ebc119..d37175a1d2 100644 --- a/site/pages/signer/authentication/passkey-signup.mdx +++ b/site/pages/signer/authentication/passkey-signup.mdx @@ -5,7 +5,7 @@ description: Authenticate a new user using a passkey # Passkey Signup -It is possible to create wallets for users using just a passkey. This is useful for creating wallets for users if you don't want to go through the magic link flow. +It is possible to create wallets for users using just a passkey. This is useful for creating wallets for users if you don't want to go through the email OTP or magic link flow. :::warning If you create a passkey without an email associated with the user, you risk your users losing access to their wallets if they lose their device. diff --git a/site/pages/signer/authentication/social-login.mdx b/site/pages/signer/authentication/social-login.mdx index d5ed48bfb7..f0b22bb0e0 100644 --- a/site/pages/signer/authentication/social-login.mdx +++ b/site/pages/signer/authentication/social-login.mdx @@ -5,7 +5,7 @@ description: Authenticate a user using OAuth # Social Login Authentication -Social login authentication allows you to login and signup users using an OAuth provider, such as Google Sign-In or Facebook Login. You can also configure custom providers through Auth0. +Social login authentication allows you to log in and sign up users using an OAuth provider, such as Google Sign-In or Facebook Login. You can also configure custom providers through Auth0. :::tip To set up configurations to enable social login, see the [Signer Quickstart](/signer/quickstart) to enable a social policy in the dashboard. diff --git a/site/shared/get-api-key.mdx b/site/shared/get-api-key.mdx index f002ba8b57..30971c0300 100644 --- a/site/shared/get-api-key.mdx +++ b/site/shared/get-api-key.mdx @@ -21,7 +21,7 @@ - For testing, use [http://localhost:3000](http://localhost:3000/) as the **Redirect URL** (Note **http** not https) - - The user will be redirected to the **Redirect URL** when they click the magic link in the email + - The user will be redirected to the **Redirect URL** if you use the magic link email flow - Optionally stylize ✨ the email with your brand color and logo! Date: Mon, 16 Dec 2024 22:47:50 +0000 Subject: [PATCH 25/45] chore(release): publish v4.7.0 [skip-ci] --- CHANGELOG.md | 11 +++++++++++ aa-sdk/core/CHANGELOG.md | 4 ++++ aa-sdk/core/package.json | 2 +- aa-sdk/core/src/version.ts | 2 +- aa-sdk/ethers/CHANGELOG.md | 4 ++++ aa-sdk/ethers/package.json | 6 +++--- account-kit/core/CHANGELOG.md | 6 ++++++ account-kit/core/package.json | 10 +++++----- account-kit/core/src/version.ts | 2 +- account-kit/infra/CHANGELOG.md | 4 ++++ account-kit/infra/package.json | 6 +++--- account-kit/infra/src/version.ts | 2 +- account-kit/logging/CHANGELOG.md | 4 ++++ account-kit/logging/package.json | 2 +- account-kit/logging/src/version.ts | 2 +- account-kit/plugingen/CHANGELOG.md | 4 ++++ account-kit/plugingen/package.json | 4 ++-- account-kit/plugingen/src/version.ts | 2 +- account-kit/react/CHANGELOG.md | 6 ++++++ account-kit/react/package.json | 10 +++++----- account-kit/react/src/version.ts | 2 +- account-kit/rn-signer/CHANGELOG.md | 6 ++++++ account-kit/rn-signer/package.json | 6 +++--- account-kit/signer/CHANGELOG.md | 6 ++++++ account-kit/signer/package.json | 6 +++--- account-kit/signer/src/version.ts | 2 +- account-kit/smart-contracts/CHANGELOG.md | 4 ++++ account-kit/smart-contracts/package.json | 8 ++++---- lerna.json | 2 +- 29 files changed, 97 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faa009167d..fe8c56d910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +### Bug Fixes + +- updates facebook icon used in ui component ([#1196](https://github.com/alchemyplatform/aa-sdk/issues/1196)) ([309b792](https://github.com/alchemyplatform/aa-sdk/commit/309b792d68566e452087bc4cb8b48d6565467a94)) + +### Features + +- add email OTP support ([#1163](https://github.com/alchemyplatform/aa-sdk/issues/1163)) ([76720d9](https://github.com/alchemyplatform/aa-sdk/commit/76720d9b647d5e65a78726935520c41d94c1f6ff)) +- add react-native-bare implementation ([#1202](https://github.com/alchemyplatform/aa-sdk/issues/1202)) ([987df11](https://github.com/alchemyplatform/aa-sdk/commit/987df1184c4a0bcca6db0d422c23df6a9d3dd3bd)) + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) ### Bug Fixes diff --git a/aa-sdk/core/CHANGELOG.md b/aa-sdk/core/CHANGELOG.md index df89017b11..9bb700d380 100644 --- a/aa-sdk/core/CHANGELOG.md +++ b/aa-sdk/core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +**Note:** Version bump only for package @aa-sdk/core + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @aa-sdk/core diff --git a/aa-sdk/core/package.json b/aa-sdk/core/package.json index 77a21c88da..cb2334c52e 100644 --- a/aa-sdk/core/package.json +++ b/aa-sdk/core/package.json @@ -1,7 +1,7 @@ { "name": "@aa-sdk/core", "license": "MIT", - "version": "4.6.1", + "version": "4.7.0", "description": "viem based SDK that enables interactions with ERC-4337 Smart Accounts. ABIs are based off the definitions generated in @account-abstraction/contracts", "author": "Alchemy", "type": "module", diff --git a/aa-sdk/core/src/version.ts b/aa-sdk/core/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/aa-sdk/core/src/version.ts +++ b/aa-sdk/core/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/aa-sdk/ethers/CHANGELOG.md b/aa-sdk/ethers/CHANGELOG.md index 168dadac15..46c2f242eb 100644 --- a/aa-sdk/ethers/CHANGELOG.md +++ b/aa-sdk/ethers/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +**Note:** Version bump only for package @aa-sdk/ethers + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @aa-sdk/ethers diff --git a/aa-sdk/ethers/package.json b/aa-sdk/ethers/package.json index d6963acb8c..c31329ea67 100644 --- a/aa-sdk/ethers/package.json +++ b/aa-sdk/ethers/package.json @@ -1,7 +1,7 @@ { "name": "@aa-sdk/ethers", "license": "MIT", - "version": "4.6.1", + "version": "4.7.0", "description": "Ethers.js wrapper for @aa-sdk/core", "author": "Alchemy", "type": "module", @@ -41,7 +41,7 @@ "test:run": "vitest run" }, "devDependencies": { - "@account-kit/smart-contracts": "^4.6.1", + "@account-kit/smart-contracts": "^4.7.0", "alchemy-sdk": "^3.0.0", "dotenv": "^16.0.3", "typescript": "^5.0.4", @@ -49,7 +49,7 @@ "vitest": "^2.0.4" }, "dependencies": { - "@aa-sdk/core": "^4.6.1", + "@aa-sdk/core": "^4.7.0", "@ethersproject/abi": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/bytes": "^5.7.0", diff --git a/account-kit/core/CHANGELOG.md b/account-kit/core/CHANGELOG.md index 12d12e0582..471aa7d17c 100644 --- a/account-kit/core/CHANGELOG.md +++ b/account-kit/core/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +### Features + +- add email OTP support ([#1163](https://github.com/alchemyplatform/aa-sdk/issues/1163)) ([76720d9](https://github.com/alchemyplatform/aa-sdk/commit/76720d9b647d5e65a78726935520c41d94c1f6ff)) + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @account-kit/core diff --git a/account-kit/core/package.json b/account-kit/core/package.json index 0a8798efc6..3a3c113d8f 100644 --- a/account-kit/core/package.json +++ b/account-kit/core/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/core", - "version": "4.6.1", + "version": "4.7.0", "description": "Core library for account kit that provides state management and framework indepednent abstractions across infra, Alchemy Signer, and Smart Contracts", "author": "Alchemy", "license": "MIT", @@ -45,10 +45,10 @@ "vitest": "^2.0.4" }, "dependencies": { - "@account-kit/infra": "^4.6.1", - "@account-kit/logging": "^4.6.1", - "@account-kit/signer": "^4.6.1", - "@account-kit/smart-contracts": "^4.6.1", + "@account-kit/infra": "^4.7.0", + "@account-kit/logging": "^4.7.0", + "@account-kit/signer": "^4.7.0", + "@account-kit/smart-contracts": "^4.7.0", "js-cookie": "^3.0.5", "zod": "^3.22.4", "zustand": "^5.0.0-rc.2" diff --git a/account-kit/core/src/version.ts b/account-kit/core/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/account-kit/core/src/version.ts +++ b/account-kit/core/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/account-kit/infra/CHANGELOG.md b/account-kit/infra/CHANGELOG.md index ab6d270f29..bdce241f0a 100644 --- a/account-kit/infra/CHANGELOG.md +++ b/account-kit/infra/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +**Note:** Version bump only for package @account-kit/infra + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @account-kit/infra diff --git a/account-kit/infra/package.json b/account-kit/infra/package.json index 330947c56a..488f71a639 100644 --- a/account-kit/infra/package.json +++ b/account-kit/infra/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/infra", - "version": "4.6.1", + "version": "4.7.0", "description": "adapters for @aa-sdk/core for interacting with alchemy services", "author": "Alchemy", "license": "MIT", @@ -44,8 +44,8 @@ "vitest": "^2.0.4" }, "dependencies": { - "@aa-sdk/core": "^4.6.1", - "@account-kit/logging": "^4.6.1", + "@aa-sdk/core": "^4.7.0", + "@account-kit/logging": "^4.7.0", "eventemitter3": "^5.0.1", "zod": "^3.22.4" }, diff --git a/account-kit/infra/src/version.ts b/account-kit/infra/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/account-kit/infra/src/version.ts +++ b/account-kit/infra/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/account-kit/logging/CHANGELOG.md b/account-kit/logging/CHANGELOG.md index 37f5254897..be59254ad7 100644 --- a/account-kit/logging/CHANGELOG.md +++ b/account-kit/logging/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +**Note:** Version bump only for package @account-kit/logging + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @account-kit/logging diff --git a/account-kit/logging/package.json b/account-kit/logging/package.json index ad2f042f79..a5b5114a5d 100644 --- a/account-kit/logging/package.json +++ b/account-kit/logging/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/logging", - "version": "4.6.1", + "version": "4.7.0", "description": "Core logging library for Account Kit packages", "author": "Alchemy", "license": "MIT", diff --git a/account-kit/logging/src/version.ts b/account-kit/logging/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/account-kit/logging/src/version.ts +++ b/account-kit/logging/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/account-kit/plugingen/CHANGELOG.md b/account-kit/plugingen/CHANGELOG.md index 081ddf3abe..7561daeb2f 100644 --- a/account-kit/plugingen/CHANGELOG.md +++ b/account-kit/plugingen/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +**Note:** Version bump only for package @account-kit/plugingen + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @account-kit/plugingen diff --git a/account-kit/plugingen/package.json b/account-kit/plugingen/package.json index 9c04e5d688..9ab34ff9ef 100644 --- a/account-kit/plugingen/package.json +++ b/account-kit/plugingen/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/plugingen", - "version": "4.6.1", + "version": "4.7.0", "description": "A CLI tool that enables you to generate TS code for your ERC-6900 plugins", "author": "Alchemy", "license": "MIT", @@ -60,7 +60,7 @@ }, "homepage": "https://github.com/alchemyplatform/aa-sdk#readme", "dependencies": { - "@aa-sdk/core": "^4.6.1", + "@aa-sdk/core": "^4.7.0", "bundle-require": "^4.0.2", "cac": "^6.7.14", "change-case": "^5.4.3", diff --git a/account-kit/plugingen/src/version.ts b/account-kit/plugingen/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/account-kit/plugingen/src/version.ts +++ b/account-kit/plugingen/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/account-kit/react/CHANGELOG.md b/account-kit/react/CHANGELOG.md index 067095002c..f63366bbc7 100644 --- a/account-kit/react/CHANGELOG.md +++ b/account-kit/react/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +### Features + +- add email OTP support ([#1163](https://github.com/alchemyplatform/aa-sdk/issues/1163)) ([76720d9](https://github.com/alchemyplatform/aa-sdk/commit/76720d9b647d5e65a78726935520c41d94c1f6ff)) + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) ### Bug Fixes diff --git a/account-kit/react/package.json b/account-kit/react/package.json index afa2b60743..76ce505f70 100644 --- a/account-kit/react/package.json +++ b/account-kit/react/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/react", - "version": "4.6.1", + "version": "4.7.0", "description": "React components and hooks for using Account Kit", "author": "Alchemy", "license": "MIT", @@ -67,10 +67,10 @@ "vitest": "^2.0.4" }, "dependencies": { - "@account-kit/core": "^4.6.1", - "@account-kit/infra": "^4.6.1", - "@account-kit/logging": "^4.6.1", - "@account-kit/signer": "^4.6.1", + "@account-kit/core": "^4.7.0", + "@account-kit/infra": "^4.7.0", + "@account-kit/logging": "^4.7.0", + "@account-kit/signer": "^4.7.0", "@tanstack/react-form": "^0.33.0", "@tanstack/zod-form-adapter": "^0.33.0", "@wagmi/connectors": "^5.1.15", diff --git a/account-kit/react/src/version.ts b/account-kit/react/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/account-kit/react/src/version.ts +++ b/account-kit/react/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/account-kit/rn-signer/CHANGELOG.md b/account-kit/rn-signer/CHANGELOG.md index 73066801cd..b504ab4279 100644 --- a/account-kit/rn-signer/CHANGELOG.md +++ b/account-kit/rn-signer/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +### Features + +- add email OTP support ([#1163](https://github.com/alchemyplatform/aa-sdk/issues/1163)) ([76720d9](https://github.com/alchemyplatform/aa-sdk/commit/76720d9b647d5e65a78726935520c41d94c1f6ff)) + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) ### Bug Fixes diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index a1ab088b61..5dd5bff2db 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/react-native-signer", - "version": "4.6.1", + "version": "4.7.0", "description": "React Native compatible Account Kit signer", "source": "./src/index.tsx", "main": "./lib/commonjs/index.js", @@ -146,8 +146,8 @@ "version": "0.42.2" }, "dependencies": { - "@aa-sdk/core": "^4.6.1", - "@account-kit/signer": "^4.6.1", + "@aa-sdk/core": "^4.7.0", + "@account-kit/signer": "^4.7.0", "viem": "^2.21.40" } } diff --git a/account-kit/signer/CHANGELOG.md b/account-kit/signer/CHANGELOG.md index d878cd7985..579a37d105 100644 --- a/account-kit/signer/CHANGELOG.md +++ b/account-kit/signer/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +### Features + +- add email OTP support ([#1163](https://github.com/alchemyplatform/aa-sdk/issues/1163)) ([76720d9](https://github.com/alchemyplatform/aa-sdk/commit/76720d9b647d5e65a78726935520c41d94c1f6ff)) + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @account-kit/signer diff --git a/account-kit/signer/package.json b/account-kit/signer/package.json index 1d71e68560..c980d074b5 100644 --- a/account-kit/signer/package.json +++ b/account-kit/signer/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/signer", - "version": "4.6.1", + "version": "4.7.0", "description": "Core interfaces and clients for interfacing with the Alchemy Signer API", "author": "Alchemy", "license": "MIT", @@ -48,8 +48,8 @@ "vitest": "^2.0.4" }, "dependencies": { - "@aa-sdk/core": "^4.6.1", - "@account-kit/logging": "^4.6.1", + "@aa-sdk/core": "^4.7.0", + "@account-kit/logging": "^4.7.0", "@turnkey/http": "^2.6.2", "@turnkey/iframe-stamper": "^1.0.0", "@turnkey/viem": "^0.4.8", diff --git a/account-kit/signer/src/version.ts b/account-kit/signer/src/version.ts index 49a5b63ce1..a6e3e3bf6d 100644 --- a/account-kit/signer/src/version.ts +++ b/account-kit/signer/src/version.ts @@ -1,3 +1,3 @@ // This file is autogenerated by inject-version.ts. Any changes will be // overwritten on commit! -export const VERSION = "4.6.1"; +export const VERSION = "4.7.0"; diff --git a/account-kit/smart-contracts/CHANGELOG.md b/account-kit/smart-contracts/CHANGELOG.md index 420fbd9bfe..4c15dc775e 100644 --- a/account-kit/smart-contracts/CHANGELOG.md +++ b/account-kit/smart-contracts/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.7.0](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.1...v4.7.0) (2024-12-16) + +**Note:** Version bump only for package @account-kit/smart-contracts + ## [4.6.1](https://github.com/alchemyplatform/aa-sdk/compare/v4.6.0...v4.6.1) (2024-12-10) **Note:** Version bump only for package @account-kit/smart-contracts diff --git a/account-kit/smart-contracts/package.json b/account-kit/smart-contracts/package.json index 917382a39c..aa2a31a311 100644 --- a/account-kit/smart-contracts/package.json +++ b/account-kit/smart-contracts/package.json @@ -1,6 +1,6 @@ { "name": "@account-kit/smart-contracts", - "version": "4.6.1", + "version": "4.7.0", "description": "aa-sdk compatible interfaces for Alchemy Smart Accounts", "author": "Alchemy", "license": "MIT", @@ -46,7 +46,7 @@ "test:run": "vitest run" }, "devDependencies": { - "@account-kit/plugingen": "^4.6.1", + "@account-kit/plugingen": "^4.7.0", "change-case": "^5.1.2", "dedent": "^1.5.1", "dotenv": "^16.3.1", @@ -68,8 +68,8 @@ "homepage": "https://github.com/alchemyplatform/aa-sdk#readme", "gitHead": "ee46e8bb857de3b631044fa70714ea706d9e317d", "dependencies": { - "@aa-sdk/core": "^4.6.1", - "@account-kit/infra": "^4.6.1" + "@aa-sdk/core": "^4.7.0", + "@account-kit/infra": "^4.7.0" }, "peerDependencies": { "viem": "^2.20.0" diff --git a/lerna.json b/lerna.json index 5d5a7c4286..66ec571370 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "4.6.1", + "version": "4.7.0", "npmClient": "yarn", "conventionalCommits": true, "changelog": true, From ddb1b3c09861987a99a4919e7a4ee78eb8a4fa60 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Mon, 16 Dec 2024 18:57:51 -0500 Subject: [PATCH 26/45] docs: fix typo on OTP page (#1224) --- site/pages/signer/authentication/email-otp.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/pages/signer/authentication/email-otp.mdx b/site/pages/signer/authentication/email-otp.mdx index ae7f016ac0..dacc419ad4 100644 --- a/site/pages/signer/authentication/email-otp.mdx +++ b/site/pages/signer/authentication/email-otp.mdx @@ -5,7 +5,7 @@ description: Authenticate a user using an email OTP code # Email OTP Authentication -Email magic link authentication allows you to log in and sign up users using an email address. Your users will receive six-digit code in their inbox which they can enter in your site to complete login. +Email OTP authentication allows you to log in and sign up users using an email address. Your users will receive six-digit code in their inbox which they can enter in your site to complete login. :::tip For setting up an account config, see the [Signer Quickstart](/signer/quickstart). From 4147576ddd35abece2dba746c9a1711d34ecee5f Mon Sep 17 00:00:00 2001 From: JP <36560907+0xfourzerofour@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:37:20 -0500 Subject: [PATCH 27/45] feat(chains): add ink and soneium mainnet (#1215) * feat(networks): add new networks * feat(chains): add ink and soneium to aa-sdk --------- Co-authored-by: Andy --- account-kit/infra/src/chains.ts | 36 +++++++++++++++++++++++++++++++++ account-kit/infra/src/index.ts | 2 ++ 2 files changed, 38 insertions(+) diff --git a/account-kit/infra/src/chains.ts b/account-kit/infra/src/chains.ts index 63b42ffc90..603f1153bb 100644 --- a/account-kit/infra/src/chains.ts +++ b/account-kit/infra/src/chains.ts @@ -364,6 +364,24 @@ export const soneiumMinato: Chain = defineChain({ }, }); +export const soneiumMainnet: Chain = defineChain({ + id: 1868, + name: "Soneium Mainnet", + network: "Soneium Mainnet", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: { + default: { + http: ["https://soneium-mainnet.g.alchemy.com/v2"], + }, + public: { + http: ["https://soneium-mainnet.g.alchemy.com/v2"], + }, + alchemy: { + http: ["https://soneium-mainnet.g.alchemy.com/v2"], + }, + }, +}); + export const opbnbTestnet: Chain = defineChain({ id: 5611, name: "OPBNB Testnet", @@ -418,6 +436,24 @@ export const beraChainBartio: Chain = defineChain({ }, }); +export const inkSepolia: Chain = defineChain({ + id: 763373, + name: "Ink Sepolia", + network: "Ink Sepolia", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: { + default: { + http: ["https://ink-sepolia.g.alchemy.com/v2"], + }, + public: { + http: ["https://ink-sepolia.g.alchemy.com/v2"], + }, + alchemy: { + http: ["https://ink-sepolia.g.alchemy.com/v2"], + }, + }, +}); + export const arbitrumNova: Chain = { ...vabn, rpcUrls: { diff --git a/account-kit/infra/src/index.ts b/account-kit/infra/src/index.ts index 0d897cc4d8..31d29ec9fb 100644 --- a/account-kit/infra/src/index.ts +++ b/account-kit/infra/src/index.ts @@ -34,8 +34,10 @@ export { opbnbMainnet, opbnbTestnet, soneiumMinato, + soneiumMainnet, unichainMainnet, unichainSepolia, + inkSepolia, } from "./chains.js"; export type * from "./client/decorators/alchemyEnhancedApis.js"; export { alchemyEnhancedApiActions } from "./client/decorators/alchemyEnhancedApis.js"; From 51166c3ef5882b8505c395cd1dee7ed3158c0ebd Mon Sep 17 00:00:00 2001 From: Alex <36385732+alex-miao@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:30:37 -0800 Subject: [PATCH 28/45] feat: add ink mainnet (#1225) --- account-kit/infra/src/chains.ts | 18 ++++++++++++++++++ account-kit/infra/src/index.ts | 1 + 2 files changed, 19 insertions(+) diff --git a/account-kit/infra/src/chains.ts b/account-kit/infra/src/chains.ts index 603f1153bb..0b1b24d4f2 100644 --- a/account-kit/infra/src/chains.ts +++ b/account-kit/infra/src/chains.ts @@ -436,6 +436,24 @@ export const beraChainBartio: Chain = defineChain({ }, }); +export const inkMainnet: Chain = defineChain({ + id: 57073, + name: "Ink Mainnet", + network: "Ink Mainnet", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: { + default: { + http: ["https://ink-mainnet.g.alchemy.com/v2"], + }, + public: { + http: ["https://ink-mainnet.g.alchemy.com/v2"], + }, + alchemy: { + http: ["https://ink-mainnet.g.alchemy.com/v2"], + }, + }, +}); + export const inkSepolia: Chain = defineChain({ id: 763373, name: "Ink Sepolia", diff --git a/account-kit/infra/src/index.ts b/account-kit/infra/src/index.ts index 31d29ec9fb..814451230d 100644 --- a/account-kit/infra/src/index.ts +++ b/account-kit/infra/src/index.ts @@ -37,6 +37,7 @@ export { soneiumMainnet, unichainMainnet, unichainSepolia, + inkMainnet, inkSepolia, } from "./chains.js"; export type * from "./client/decorators/alchemyEnhancedApis.js"; From f1642a7cd88e80de486f58609638e975914b2b57 Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Wed, 18 Dec 2024 11:17:51 -0500 Subject: [PATCH 29/45] feat: add OTP authentication capabilities to RN Signer (#1231) * feat: add OTP authentication capabilities to RN Signer * fix: update redirect route path * fix: fix linting --- account-kit/rn-signer/example/package.json | 1 + .../example/redirect-server/index.ts | 4 +- account-kit/rn-signer/example/src/App.tsx | 28 +++- .../screens/{Home.tsx => magic-link-auth.tsx} | 20 ++- .../example/src/screens/otp-auth.tsx | 150 ++++++++++++++++++ account-kit/rn-signer/src/client.ts | 26 ++- account-kit/rn-signer/src/signer.ts | 23 ++- yarn.lock | 15 ++ 8 files changed, 243 insertions(+), 24 deletions(-) rename account-kit/rn-signer/example/src/screens/{Home.tsx => magic-link-auth.tsx} (93%) create mode 100644 account-kit/rn-signer/example/src/screens/otp-auth.tsx diff --git a/account-kit/rn-signer/example/package.json b/account-kit/rn-signer/example/package.json index e5afa19f37..8bb4829484 100644 --- a/account-kit/rn-signer/example/package.json +++ b/account-kit/rn-signer/example/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@account-kit/signer": "^4.3.0", + "@react-navigation/bottom-tabs": "^7.2.0", "@react-navigation/native": "7.0.3", "@react-navigation/native-stack": "7.1.0", "dotenv": "^16.4.5", diff --git a/account-kit/rn-signer/example/redirect-server/index.ts b/account-kit/rn-signer/example/redirect-server/index.ts index b8c6756c80..7c7f2021a9 100644 --- a/account-kit/rn-signer/example/redirect-server/index.ts +++ b/account-kit/rn-signer/example/redirect-server/index.ts @@ -11,7 +11,9 @@ app.get("/", (req, res) => { const bundle = req.query.bundle; const orgId = req.query.orgId; - res.redirect(`${appScheme}://home?bundle=${bundle}&orgId=${orgId}`); + res.redirect( + `${appScheme}://magic-link-auth?bundle=${bundle}&orgId=${orgId}` + ); }); app.listen(port, () => { diff --git a/account-kit/rn-signer/example/src/App.tsx b/account-kit/rn-signer/example/src/App.tsx index ae41b10abe..4316a9b890 100644 --- a/account-kit/rn-signer/example/src/App.tsx +++ b/account-kit/rn-signer/example/src/App.tsx @@ -1,21 +1,35 @@ /* eslint-disable import/extensions */ import { createStaticNavigation } from "@react-navigation/native"; -import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import { Text } from "react-native"; +import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import { SafeAreaProvider } from "react-native-safe-area-context"; -import HomeScreen from "./screens/Home"; +import OtpAuthScreen from "./screens/otp-auth"; +import MagicLinkAuthScreen from "./screens/magic-link-auth"; const linking = { enabled: "auto" as const /* Automatically generate paths for all screens */, prefixes: ["rn-signer-demo://"], }; -const RootStack = createNativeStackNavigator({ - initialRouteName: "Home", +const RootStack = createBottomTabNavigator({ + initialRouteName: "MagicLinkAuth", screens: { - Home: { - screen: HomeScreen, - linking: { path: "home" }, + MagicLinkAuth: { + screen: MagicLinkAuthScreen, + linking: { path: "magic-link-auth" }, + options: { + tabBarLabel: "Magic Link", + tabBarIcon: () => 🪄, + }, + }, + OtpAuth: { + screen: OtpAuthScreen, + linking: { path: "otp-auth" }, + options: { + tabBarLabel: "OTP", + tabBarIcon: () => 🔑, + }, }, }, }); diff --git a/account-kit/rn-signer/example/src/screens/Home.tsx b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx similarity index 93% rename from account-kit/rn-signer/example/src/screens/Home.tsx rename to account-kit/rn-signer/example/src/screens/magic-link-auth.tsx index ccf8ef0a88..0821472b1f 100644 --- a/account-kit/rn-signer/example/src/screens/Home.tsx +++ b/account-kit/rn-signer/example/src/screens/magic-link-auth.tsx @@ -1,5 +1,4 @@ /* eslint-disable import/extensions */ -import { RNAlchemySigner } from "@account-kit/react-native-signer"; import type { User } from "@account-kit/signer"; import { useEffect, useState } from "react"; import { @@ -10,15 +9,18 @@ import { Linking, TouchableOpacity, } from "react-native"; + import Config from "react-native-config"; +import { RNAlchemySigner } from "@account-kit/react-native-signer"; -export default function HomeScreen() { - const [email, setEmail] = useState(""); - const [user, setUser] = useState(null); - const signer = new RNAlchemySigner({ +export default function MagicLinkAuthScreen() { + const signer = RNAlchemySigner({ client: { connection: { apiKey: Config.API_KEY! } }, }); + const [email, setEmail] = useState(""); + const [user, setUser] = useState(null); + const handleUserAuth = ({ bundle }: { bundle: string }) => { signer .authenticate({ @@ -73,12 +75,16 @@ export default function HomeScreen() { onChangeText={setEmail} value={email} /> - {/* TODO: implement OTP */} + { signer - .authenticate({ email, type: "email", emailMode: "magicLink" }) + .authenticate({ + email, + type: "email", + emailMode: "magicLink", + }) .catch(console.error); }} > diff --git a/account-kit/rn-signer/example/src/screens/otp-auth.tsx b/account-kit/rn-signer/example/src/screens/otp-auth.tsx new file mode 100644 index 0000000000..426abc9571 --- /dev/null +++ b/account-kit/rn-signer/example/src/screens/otp-auth.tsx @@ -0,0 +1,150 @@ +/* eslint-disable import/extensions */ +import type { User } from "@account-kit/signer"; +import { useEffect, useState } from "react"; +import { + View, + Text, + TextInput, + StyleSheet, + TouchableOpacity, +} from "react-native"; + +import Config from "react-native-config"; +import { RNAlchemySigner } from "@account-kit/react-native-signer"; +export default function MagicLinkAuthScreen() { + const [email, setEmail] = useState(""); + const [user, setUser] = useState(null); + + const signer = RNAlchemySigner({ + client: { connection: { apiKey: Config.API_KEY! } }, + }); + + const [awaitingOtp, setAwaitingOtp] = useState(false); + + const [otpCode, setOtpCode] = useState(""); + + const handleUserAuth = ({ otpCode }: { otpCode: string }) => { + setAwaitingOtp(false); + signer + .authenticate({ + otpCode: otpCode, + type: "otp", + }) + .then((res) => { + console.log("res", res); + setUser(res); + }) + .catch(console.error); + }; + + useEffect(() => { + // get the user if already logged in + signer.getAuthDetails().then(setUser); + }, []); + + return ( + + {awaitingOtp ? ( + <> + + handleUserAuth({ otpCode })} + > + Sign in + + + ) : !user ? ( + <> + + { + signer + .authenticate({ + email, + type: "email", + emailMode: "otp", + }) + .catch(console.error); + setAwaitingOtp(true); + }} + > + Sign in + + + ) : ( + <> + + Currently logged in as: {user.email} + + OrgId: {user.orgId} + Address: {user.address} + + signer.disconnect().then(() => setUser(null))} + > + Sign out + + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + backgroundColor: "#FFFFF", + paddingHorizontal: 20, + }, + textInput: { + width: "100%", + height: 40, + borderColor: "gray", + borderWidth: 1, + paddingHorizontal: 10, + backgroundColor: "rgba(0,0,0,0.05)", + marginTop: 20, + marginBottom: 10, + }, + box: { + width: 60, + height: 60, + marginVertical: 20, + }, + button: { + width: 200, + padding: 10, + height: 50, + backgroundColor: "rgb(147, 197, 253)", + borderRadius: 5, + alignItems: "center", + justifyContent: "center", + marginTop: 20, + }, + buttonText: { + color: "white", + fontWeight: "bold", + textAlign: "center", + }, + userText: { + marginBottom: 10, + fontSize: 18, + }, +}); diff --git a/account-kit/rn-signer/src/client.ts b/account-kit/rn-signer/src/client.ts index 2ba464ec6d..c9d2577a34 100644 --- a/account-kit/rn-signer/src/client.ts +++ b/account-kit/rn-signer/src/client.ts @@ -36,12 +36,19 @@ export class RNSignerClient extends BaseSignerClient { connection, }); } - // TODO: implement OTP + override async submitOtpCode( args: Omit ): Promise<{ bundle: string }> { - console.log("submitOtpCode", args); - throw new Error("Method not implemented."); + this.eventEmitter.emit("authenticating", { type: "otpVerify" }); + const publicKey = await this.stamper.init(); + + const { credentialBundle } = await this.request("/v1/otp", { + ...args, + targetPublicKey: publicKey, + }); + + return { bundle: credentialBundle }; } override async createAccount( @@ -57,7 +64,7 @@ export class RNSignerClient extends BaseSignerClient { const response = await this.request("/v1/signup", { email, - emailMode: "magicLink", + emailMode: params.emailMode, targetPublicKey: publicKey, expirationSeconds, redirectParams: params.redirectParams?.toString(), @@ -72,11 +79,13 @@ export class RNSignerClient extends BaseSignerClient { this.eventEmitter.emit("authenticating", { type: "email" }); let targetPublicKey = await this.stamper.init(); - return this.request("/v1/auth", { + const response = await this.request("/v1/auth", { email: params.email, - emailMode: "magicLink", + emailMode: params.emailMode, targetPublicKey, }); + + return response; } override async completeAuthWithBundle(params: { @@ -86,7 +95,10 @@ export class RNSignerClient extends BaseSignerClient { authenticatingType: AuthenticatingEventMetadata["type"]; idToken?: string; }): Promise { - if (params.authenticatingType !== "email") { + if ( + params.authenticatingType !== "email" && + params.authenticatingType !== "otp" + ) { throw new Error("Unsupported authenticating type"); } diff --git a/account-kit/rn-signer/src/signer.ts b/account-kit/rn-signer/src/signer.ts index 0fbced4403..8c6e284556 100644 --- a/account-kit/rn-signer/src/signer.ts +++ b/account-kit/rn-signer/src/signer.ts @@ -19,8 +19,14 @@ const RNAlchemySignerParamsSchema = z export type RNAlchemySignerParams = z.input; -export class RNAlchemySigner extends BaseAlchemySigner { - constructor(params: RNAlchemySignerParams) { +class RNAlchemySignerSingleton extends BaseAlchemySigner { + private static instance: BaseAlchemySigner; + + private constructor(params: RNAlchemySignerParams) { + if (!!RNAlchemySignerSingleton.instance) { + return RNAlchemySignerSingleton.instance; + } + const { sessionConfig, ...params_ } = RNAlchemySignerParamsSchema.parse(params); @@ -36,4 +42,17 @@ export class RNAlchemySigner extends BaseAlchemySigner { sessionConfig, }); } + + public static getInstance(params: RNAlchemySignerParams) { + if (!this.instance) { + this.instance = new RNAlchemySignerSingleton(params); + } + return this.instance; + } +} + +export function RNAlchemySigner(params: RNAlchemySignerParams) { + const instance = RNAlchemySignerSingleton.getInstance(params); + + return instance; } diff --git a/yarn.lock b/yarn.lock index 4456645797..97ecbfec6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6239,6 +6239,14 @@ invariant "^2.2.4" nullthrows "^1.1.1" +"@react-navigation/bottom-tabs@^7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.2.0.tgz#5b336b823226647a263b4fe743655462796b6aaf" + integrity sha512-1LxjgnbPyFINyf9Qr5d1YE0pYhuJayg5TCIIFQmbcX4PRhX7FKUXV7cX8OzrKXEdZi/UE/VNXugtozPAR9zgvA== + dependencies: + "@react-navigation/elements" "^2.2.5" + color "^4.2.3" + "@react-navigation/core@^7.0.3": version "7.0.3" resolved "https://registry.npmjs.org/@react-navigation/core/-/core-7.0.3.tgz#bac011a459ae62bb91e3bc8adeb862f05ac19a88" @@ -6266,6 +6274,13 @@ dependencies: color "^4.2.3" +"@react-navigation/elements@^2.2.5": + version "2.2.5" + resolved "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.2.5.tgz#0e2ca76e2003e96b417a3d7c2829bf1afd69193f" + integrity sha512-sDhE+W14P7MNWLMxXg1MEVXwkLUpMZJGflE6nQNzLmolJQIHgcia0Mrm8uRa3bQovhxYu1UzEojLZ+caoZt7Fg== + dependencies: + color "^4.2.3" + "@react-navigation/native-stack@7.1.0": version "7.1.0" resolved "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.1.0.tgz#7e4df1dc25daa9832f9677ca4c7c065287e5118f" From 0a6c53c08b351b87349270ac68a5c8b90d8e604e Mon Sep 17 00:00:00 2001 From: Iyk Azorji Date: Wed, 18 Dec 2024 12:04:30 -0500 Subject: [PATCH 30/45] feat: add expo example (#1204) * feat: add expo files * feat: reset expo app * feat: update expo setup to support expo router * feat: update readme to remove viem dependency * fix: fix dependency isses * fix: update react native verions accross monorepo * fix: dependency issues breaking metro config * fix: dependency issues breaking metro config * fix: update metro to add account kit workspace --- account-kit/rn-signer/example/package.json | 14 +- account-kit/rn-signer/package.json | 6 +- .../react-native-bare-example/metro.config.js | 1 + examples/react-native-expo-example/.gitignore | 36 + examples/react-native-expo-example/README.md | 87 + .../android/.gitignore | 16 + .../android/app/build.gradle | 176 ++ .../android/app/debug.keystore | Bin 0 -> 2257 bytes .../android/app/proguard-rules.pro | 14 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 32 + .../reactnativeexpoexample/MainActivity.kt | 61 + .../reactnativeexpoexample/MainApplication.kt | 57 + .../res/drawable-hdpi/splashscreen_logo.png | Bin 0 -> 20754 bytes .../res/drawable-mdpi/splashscreen_logo.png | Bin 0 -> 12863 bytes .../res/drawable-xhdpi/splashscreen_logo.png | Bin 0 -> 29081 bytes .../res/drawable-xxhdpi/splashscreen_logo.png | Bin 0 -> 47123 bytes .../drawable-xxxhdpi/splashscreen_logo.png | Bin 0 -> 66529 bytes .../res/drawable/ic_launcher_background.xml | 6 + .../res/drawable/rn_edit_text_material.xml | 37 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 3300 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 0 -> 8031 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 4103 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 2048 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 0 -> 5079 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 2613 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 4535 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 0 -> 11145 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 5673 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 7345 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 0 -> 18064 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 9091 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 10108 bytes .../ic_launcher_foreground.webp | Bin 0 -> 25030 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 12469 bytes .../app/src/main/res/values-night/colors.xml | 1 + .../app/src/main/res/values/colors.xml | 6 + .../app/src/main/res/values/strings.xml | 5 + .../app/src/main/res/values/styles.xml | 17 + .../android/build.gradle | 41 + .../android/gradle.properties | 56 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../react-native-expo-example/android/gradlew | 252 ++ .../android/gradlew.bat | 94 + .../android/settings.gradle | 38 + examples/react-native-expo-example/app.json | 31 + .../react-native-expo-example/app/_layout.tsx | 10 + .../react-native-expo-example/app/index.tsx | 198 ++ .../assets/adaptive-icon.png | Bin 0 -> 17547 bytes .../assets/favicon.png | Bin 0 -> 1466 bytes .../react-native-expo-example/assets/icon.png | Bin 0 -> 22380 bytes .../assets/splash-icon.png | Bin 0 -> 17547 bytes .../react-native-expo-example/babel.config.js | 15 + examples/react-native-expo-example/env.d.ts | 5 + .../react-native-expo-example/metro.config.js | 31 + .../react-native-expo-example/package.json | 46 + .../redirect-server/index.ts | 23 + .../react-native-expo-example/tsconfig.json | 6 + site/package.json | 2 +- yarn.lock | 2068 ++++++++++++----- 63 files changed, 2977 insertions(+), 535 deletions(-) create mode 100644 examples/react-native-expo-example/.gitignore create mode 100644 examples/react-native-expo-example/README.md create mode 100644 examples/react-native-expo-example/android/.gitignore create mode 100644 examples/react-native-expo-example/android/app/build.gradle create mode 100644 examples/react-native-expo-example/android/app/debug.keystore create mode 100644 examples/react-native-expo-example/android/app/proguard-rules.pro create mode 100644 examples/react-native-expo-example/android/app/src/debug/AndroidManifest.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/AndroidManifest.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainActivity.kt create mode 100644 examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainApplication.kt create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/drawable/rn_edit_text_material.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 examples/react-native-expo-example/android/app/src/main/res/values-night/colors.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/values/colors.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/values/strings.xml create mode 100644 examples/react-native-expo-example/android/app/src/main/res/values/styles.xml create mode 100644 examples/react-native-expo-example/android/build.gradle create mode 100644 examples/react-native-expo-example/android/gradle.properties create mode 100644 examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/react-native-expo-example/android/gradlew create mode 100644 examples/react-native-expo-example/android/gradlew.bat create mode 100644 examples/react-native-expo-example/android/settings.gradle create mode 100644 examples/react-native-expo-example/app.json create mode 100644 examples/react-native-expo-example/app/_layout.tsx create mode 100644 examples/react-native-expo-example/app/index.tsx create mode 100644 examples/react-native-expo-example/assets/adaptive-icon.png create mode 100644 examples/react-native-expo-example/assets/favicon.png create mode 100644 examples/react-native-expo-example/assets/icon.png create mode 100644 examples/react-native-expo-example/assets/splash-icon.png create mode 100644 examples/react-native-expo-example/babel.config.js create mode 100644 examples/react-native-expo-example/env.d.ts create mode 100644 examples/react-native-expo-example/metro.config.js create mode 100644 examples/react-native-expo-example/package.json create mode 100644 examples/react-native-expo-example/redirect-server/index.ts create mode 100644 examples/react-native-expo-example/tsconfig.json diff --git a/account-kit/rn-signer/example/package.json b/account-kit/rn-signer/example/package.json index 8bb4829484..1ee447fc59 100644 --- a/account-kit/rn-signer/example/package.json +++ b/account-kit/rn-signer/example/package.json @@ -18,7 +18,7 @@ "dotenv": "^16.4.5", "express": "^4.21.1", "react": "18.3.1", - "react-native": "0.76.1", + "react-native": "0.76.5", "react-native-config": "1.5.3", "react-native-gesture-handler": "2.21.2", "react-native-mmkv": "3.1.0", @@ -37,12 +37,12 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", "@babel/runtime": "^7.25.0", - "@react-native-community/cli": "15.0.0", - "@react-native-community/cli-platform-android": "15.0.0", - "@react-native-community/cli-platform-ios": "15.0.0", - "@react-native/babel-preset": "0.76.1", - "@react-native/metro-config": "0.76.1", - "@react-native/typescript-config": "0.76.1", + "@react-native-community/cli": "15.0.1", + "@react-native-community/cli-platform-android": "15.0.1", + "@react-native-community/cli-platform-ios": "15.0.1", + "@react-native/babel-preset": "0.76.5", + "@react-native/metro-config": "0.76.5", + "@react-native/typescript-config": "0.76.5", "react-native-builder-bob": "^0.30.3", "tsx": "^4.19.2" }, diff --git a/account-kit/rn-signer/package.json b/account-kit/rn-signer/package.json index 5dd5bff2db..363d9f1b4b 100644 --- a/account-kit/rn-signer/package.json +++ b/account-kit/rn-signer/package.json @@ -63,8 +63,8 @@ "devDependencies": { "@commitlint/config-conventional": "^17.0.2", "@evilmartians/lefthook": "^1.5.0", - "@react-native-community/cli": "15.0.0", - "@react-native/eslint-config": "^0.73.1", + "@react-native-community/cli": "15.0.1", + "@react-native/eslint-config": "^0.76.5", "@release-it/conventional-changelog": "^5.0.0", "@types/jest": "^29.5.5", "@types/react": "^18.2.44", @@ -76,7 +76,7 @@ "jest": "^29.7.0", "prettier": "^3.0.3", "react": "18.3.1", - "react-native": "0.76.1", + "react-native": "0.76.5", "react-native-builder-bob": "^0.30.3", "react-native-mmkv": "^3.1.0", "release-it": "^15.0.0", diff --git a/examples/react-native-bare-example/metro.config.js b/examples/react-native-bare-example/metro.config.js index d7fb852418..e315f1d159 100644 --- a/examples/react-native-bare-example/metro.config.js +++ b/examples/react-native-bare-example/metro.config.js @@ -18,6 +18,7 @@ config.watchFolders = [workspaceRoot, projectRoot]; config.resolver.nodeModulesPaths = [ path.resolve(projectRoot, 'node_modules'), path.resolve(workspaceRoot, 'node_modules'), + path.resolve(workspaceRoot, 'account-kit/rn-signer/node_modules'), ]; // Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` config.resolver.disableHierarchicalLookup = true; diff --git a/examples/react-native-expo-example/.gitignore b/examples/react-native-expo-example/.gitignore new file mode 100644 index 0000000000..d16e1efbb3 --- /dev/null +++ b/examples/react-native-expo-example/.gitignore @@ -0,0 +1,36 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo diff --git a/examples/react-native-expo-example/README.md b/examples/react-native-expo-example/README.md new file mode 100644 index 0000000000..ecacbe78ca --- /dev/null +++ b/examples/react-native-expo-example/README.md @@ -0,0 +1,87 @@ +# Account Kit RN Example using Expo + +This is a sample repo that used expo to get started and integrates with Account Kit + +image + +## How it was made + +1. First create a new expo project (we used `yarn`): + +```bash +yarn create expo-app --template +``` + +This project used a `Blank (typescript)` template. + +2. Ensure you have the latest version of expo and that the new architecture is enabled + +```bash +yarn expo install expo@latest --fix +``` + +```json +// app.json +{ + "newArchEnabled": true +} +``` + +3. Install shims for crypto libraries + +```bash +yarn expo install node-libs-react-native crypto-browserify stream-browserify react-native-get-random-values +``` + +4. Add shims to `metro.config.js`: + +```javascript +// Learn more https://docs.expo.io/guides/customizing-metro +const { getDefaultConfig } = require("expo/metro-config"); + +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname); +config.resolver.extraNodeModules = { + ...config.resolver.extraNodeModules, + ...require("node-libs-react-native"), + crypto: require.resolve("crypto-browserify"), + stream: require.resolve("stream-browserify"), +}; + +module.exports = config; +``` + +5. Setup Expo Router. + +Follow the [expo router docs](https://docs.expo.dev/router/installation/) to setup the router. + +```bash +yarn expo install expo-router +``` + +6. Import global shims in the root layout file: `_layout.tsx`: + +```typescript +import "node-libs-react-native/globals.js"; +import "react-native-get-random-values"; + +// rest of _layout.tsx +``` + +6. Install [Account Kit](https://accountkit.alchemy.com) Packages. At this point you're ready to use the aa-sdk in your project. + +```bash +yarn add @account-kit/react-native-signer @account-kit/signer @account-kit/smart-contracts @account-kit/infra +``` + +7. Add a redirect server to handle the auth request. This is needed to redirect users back to the app after clicking the auth magic link in their email. + +```bash +yarn add express +``` + +8. Run the redirect server: + +```bash +yarn start:redirect-server +``` diff --git a/examples/react-native-expo-example/android/.gitignore b/examples/react-native-expo-example/android/.gitignore new file mode 100644 index 0000000000..8a6be07718 --- /dev/null +++ b/examples/react-native-expo-example/android/.gitignore @@ -0,0 +1,16 @@ +# OSX +# +.DS_Store + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml +*.hprof +.cxx/ + +# Bundle artifacts +*.jsbundle diff --git a/examples/react-native-expo-example/android/app/build.gradle b/examples/react-native-expo-example/android/app/build.gradle new file mode 100644 index 0000000000..65ed0e2380 --- /dev/null +++ b/examples/react-native-expo-example/android/app/build.gradle @@ -0,0 +1,176 @@ +apply plugin: "com.android.application" +apply plugin: "org.jetbrains.kotlin.android" +apply plugin: "com.facebook.react" + +def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() + +/** + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. + */ +react { + entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) + reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() + hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" + codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() + + // Use Expo CLI to bundle the app, this ensures the Metro config + // works correctly with Expo projects. + cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim()) + bundleCommand = "export:embed" + + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '../..' + // root = file("../../") + // The folder where the react-native NPM package is. Default is ../../node_modules/react-native + // reactNativeDir = file("../../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen + // codegenDir = file("../../node_modules/@react-native/codegen") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] + + /* Autolinking */ + autolinkLibrariesWithApp() +} + +/** + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. + */ +def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean() + +/** + * The preferred build flavor of JavaScriptCore (JSC) + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + +android { + ndkVersion rootProject.ext.ndkVersion + + buildToolsVersion rootProject.ext.buildToolsVersion + compileSdk rootProject.ext.compileSdkVersion + + namespace 'com.accountkit.reactnativeexpoexample' + defaultConfig { + applicationId 'com.accountkit.reactnativeexpoexample' + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0.0" + } + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } + buildTypes { + debug { + signingConfig signingConfigs.debug + } + release { + // Caution! In production, you need to generate your own keystore file. + // see https://reactnative.dev/docs/signed-apk-android. + signingConfig signingConfigs.debug + shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true) + } + } + packagingOptions { + jniLibs { + useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false) + } + } + androidResources { + ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~' + } +} + +// Apply static values from `gradle.properties` to the `android.packagingOptions` +// Accepts values in comma delimited lists, example: +// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini +["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop -> + // Split option: 'foo,bar' -> ['foo', 'bar'] + def options = (findProperty("android.packagingOptions.$prop") ?: "").split(","); + // Trim all elements in place. + for (i in 0.. 0) { + println "android.packagingOptions.$prop += $options ($options.length)" + // Ex: android.packagingOptions.pickFirsts += '**/SCCS/**' + options.each { + android.packagingOptions[prop] += it + } + } +} + +dependencies { + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") + + def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true"; + def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true"; + def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true"; + + if (isGifEnabled) { + // For animated gif support + implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}") + } + + if (isWebpEnabled) { + // For webp support + implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}") + if (isWebpAnimatedEnabled) { + // Animated webp support + implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}") + } + } + + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") + } else { + implementation jscFlavor + } +} diff --git a/examples/react-native-expo-example/android/app/debug.keystore b/examples/react-native-expo-example/android/app/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..364e105ed39fbfd62001429a68140672b06ec0de GIT binary patch literal 2257 zcmchYXEfYt8;7T1^dLH$VOTZ%2NOdOH5j5LYLtZ0q7x-V8_6gU5)#7dkq{HTmsfNq zB3ZqcAxeY^G10@?efK?Q&)M(qInVv!xjx+IKEL}p*K@LYvIzo#AZG>st5|P)KF1_Z;y){W{<7K{nl!CPuE z_^(!C(Ol0n8 zK13*rzAtW>(wULKPRYLd7G18F8#1P`V*9`(Poj26eOXYyBVZPno~Cvvhx7vPjAuZo zF?VD!zB~QG(!zbw#qsxT8%BSpqMZ4f70ZPn-3y$L8{EVbbN9$H`B&Z1quk9tgp5FM zuxp3pJ0b8u|3+#5bkJ4SRnCF2l7#DyLYXYY8*?OuAwK4E6J{0N=O3QNVzQ$L#FKkR zi-c@&!nDvezOV$i$Lr}iF$XEcwnybQ6WZrMKuw8gCL^U#D;q3t&HpTbqyD%vG=TeDlzCT~MXUPC|Leb-Uk+ z=vnMd(|>ld?Fh>V8poP;q;;nc@en$|rnP0ytzD&fFkCeUE^kG9Kx4wUh!!rpjwKDP zyw_e|a^x_w3E zP}}@$g>*LLJ4i0`Gx)qltL}@;mDv}D*xR^oeWcWdPkW@Uu)B^X&4W1$p6}ze!zudJ zyiLg@uggoMIArBr*27EZV7djDg@W1MaL+rcZ-lrANJQ%%>u8)ZMWU@R2qtnmG(acP z0d_^!t>}5W zpT`*2NR+0+SpTHb+6Js4b;%LJB;B_-ChhnU5py}iJtku*hm5F0!iql8Hrpcy1aYbT z1*dKC5ua6pMX@@iONI?Hpr%h;&YaXp9n!ND7-=a%BD7v&g zOO41M6EbE24mJ#S$Ui0-brR5ML%@|ndz^)YLMMV1atna{Fw<;TF@>d&F|!Z>8eg>>hkFrV)W+uv=`^F9^e zzzM2*oOjT9%gLoub%(R57p-`TXFe#oh1_{&N-YN z<}artH|m=d8TQuKSWE)Z%puU|g|^^NFwC#N=@dPhasyYjoy(fdEVfKR@cXKHZV-`06HsP`|Ftx;8(YD$fFXumLWbGnu$GMqRncXYY9mwz9$ap zQtfZB^_BeNYITh^hA7+(XNFox5WMeG_LtJ%*Q}$8VKDI_p8^pqX)}NMb`0e|wgF7D zuQACY_Ua<1ri{;Jwt@_1sW9zzdgnyh_O#8y+C;LcZq6=4e^cs6KvmK@$vVpKFGbQ= z$)Eux5C|Fx;Gtmv9^#Y-g@7Rt7*eLp5n!gJmn7&B_L$G?NCN`AP>cXQEz}%F%K;vUs{+l4Q{}eWW;ATe2 zqvXzxoIDy(u;F2q1JH7Sf;{jy_j})F+cKlIOmNfjBGHoG^CN zM|Ho&&X|L-36f}Q-obEACz`sI%2f&k>z5c$2TyTSj~vmO)BW~+N^kt`Jt@R|s!){H ze1_eCrlNaPkJQhL$WG&iRvF*YG=gXd1IyYQ9ew|iYn7r~g!wOnw;@n42>enAxBv*A zEmV*N#sxdicyNM=A4|yaOC5MByts}s_Hpfj|y<6G=o=!3S@eIFKDdpR7|FY>L&Wat&oW&cm&X~ z5Bt>Fcq(fgnvlvLSYg&o6>&fY`ODg4`V^lWWD=%oJ#Kbad2u~! zLECFS*??>|vDsNR&pH=Ze0Eo`sC_G`OjoEKVHY|wmwlX&(XBE<@sx3Hd^gtd-fNwUHsylg06p`U2y_={u}Bc + + + + + diff --git a/examples/react-native-expo-example/android/app/src/main/AndroidManifest.xml b/examples/react-native-expo-example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..3dfa0c6643 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainActivity.kt b/examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainActivity.kt new file mode 100644 index 0000000000..ca716d7eb8 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainActivity.kt @@ -0,0 +1,61 @@ +package com.accountkit.reactnativeexpoexample + +import android.os.Build +import android.os.Bundle + +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate + +import expo.modules.ReactActivityDelegateWrapper + +class MainActivity : ReactActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + // Set the theme to AppTheme BEFORE onCreate to support + // coloring the background, status bar, and navigation bar. + // This is required for expo-splash-screen. + setTheme(R.style.AppTheme); + super.onCreate(null) + } + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "main" + + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate { + return ReactActivityDelegateWrapper( + this, + BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, + object : DefaultReactActivityDelegate( + this, + mainComponentName, + fabricEnabled + ){}) + } + + /** + * Align the back button behavior with Android S + * where moving root activities to background instead of finishing activities. + * @see onBackPressed + */ + override fun invokeDefaultOnBackPressed() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { + if (!moveTaskToBack(false)) { + // For non-root activities, use the default implementation to finish them. + super.invokeDefaultOnBackPressed() + } + return + } + + // Use the default back button implementation on Android S + // because it's doing more than [Activity.moveTaskToBack] in fact. + super.invokeDefaultOnBackPressed() + } +} diff --git a/examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainApplication.kt b/examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainApplication.kt new file mode 100644 index 0000000000..f2d1ce29dc --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/java/com/accountkit/reactnativeexpoexample/MainApplication.kt @@ -0,0 +1,57 @@ +package com.accountkit.reactnativeexpoexample + +import android.app.Application +import android.content.res.Configuration + +import com.facebook.react.PackageList +import com.facebook.react.ReactApplication +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackage +import com.facebook.react.ReactHost +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load +import com.facebook.react.defaults.DefaultReactNativeHost +import com.facebook.react.soloader.OpenSourceMergedSoMapping +import com.facebook.soloader.SoLoader + +import expo.modules.ApplicationLifecycleDispatcher +import expo.modules.ReactNativeHostWrapper + +class MainApplication : Application(), ReactApplication { + + override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( + this, + object : DefaultReactNativeHost(this) { + override fun getPackages(): List { + val packages = PackageList(this).packages + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + return packages + } + + override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" + + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + } + ) + + override val reactHost: ReactHost + get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) + + override fun onCreate() { + super.onCreate() + SoLoader.init(this, OpenSourceMergedSoMapping) + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + load() + } + ApplicationLifecycleDispatcher.onApplicationCreate(this) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) + } +} diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png b/examples/react-native-expo-example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..31df827b18bba369596d713b3ed5f554c432c5db GIT binary patch literal 20754 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png b/examples/react-native-expo-example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ef243aab6c07672628b98d8eb239605edfb97c5c GIT binary patch literal 12863 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 F{}05A4LSe- literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png b/examples/react-native-expo-example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e9d5474519c701b4b518f2f0a24d7d8742e93416 GIT binary patch literal 29081 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≠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 PMTS3g4RtcK&inljbZB-$ literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png b/examples/react-native-expo-example/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d61da15d2410dd60355d0d9ca34bb2aa741267f0 GIT binary patch literal 47123 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png b/examples/react-native-expo-example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4aeed11d00badbb02d5b40a3d24fcf97dbabbd5a GIT binary patch literal 66529 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

^;WArDFIvK1}FmU%q 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 N_G%kxm1)=q{|~x=+3WxS literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable/ic_launcher_background.xml b/examples/react-native-expo-example/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..883b2a080f --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/res/drawable/rn_edit_text_material.xml b/examples/react-native-expo-example/android/app/src/main/res/drawable/rn_edit_text_material.xml new file mode 100644 index 0000000000..5c25e728ea --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..3941bea9b9 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..3941bea9b9 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..7fae0ccbcfa5c1de75a85a4420f0d8a01e2b0744 GIT binary patch literal 3300 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 z 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..afa0a4ef4ba5230871e032f2a6df96ec572d7c49 GIT binary patch literal 4103 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 ULJ*w>3_FmIhrfIM_Q;I?0&7K2ZU6uP literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..c4f6e101ec73d0b675780f81d04a3d1b2ddcb04f GIT binary patch literal 2613 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..7a0f085faac07551f2407aea014b4ada7044a04c GIT binary patch literal 4535 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 Za-wxV!t(TUd!WS@wBOc+(qO|#{9nh>`J4a% literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..6c2d40bf55ead27ea9e0f88465db2de95683b533 GIT binary patch literal 5673 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 KpUXO@geCyPqIRYL literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..345261586ca9baa40c35e3db8d50b048a8a6bff6 GIT binary patch literal 9091 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ΞnADf zu>~(57_sDNp6S038%eM$yo26|*gCYTg4YoYMZyM;zf+a;L5|-$H)KQ?D&n&_@_CzD za>ud#N5Yyp?Gbqk73?4SjCjTwDTZss736Pz8fxPrW3Hm=OSl8CJtf>Ty=_0EnBmQt zKU2Z`-ITSTaX_lqHFy&FOMA6BtPCkPS0VDljito;D?PIBORywO5?*M~D(|4aV61Q) zvDVUIF7o(rZXluYC8JOOFDxTG!-k~8TybH!K!4~$_yX3m!SOGL@ZUV2IcvlBu#|mL8N9N>4dP{2w7MBW?fy literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..b11a322ab4a6750ecea03c2ab21a772a1d293bac GIT binary patch literal 10108 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..49a464ee361fe64ce9d076716724a756c521d807 GIT binary patch literal 25030 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/examples/react-native-expo-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b51fd15c26cdca51913e3696386b5e694327ca8a GIT binary patch literal 12469 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 X_39jKitxW9Fqs-!7~DVQ9Q%I&1uk6m literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/app/src/main/res/values-night/colors.xml b/examples/react-native-expo-example/android/app/src/main/res/values-night/colors.xml new file mode 100644 index 0000000000..3c05de5be8 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/values-night/colors.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/res/values/colors.xml b/examples/react-native-expo-example/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..f387b90114 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + #ffffff + #ffffff + #023c69 + #ffffff + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/res/values/strings.xml b/examples/react-native-expo-example/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..afb63251c1 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + react-native-expo-example + contain + false + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/app/src/main/res/values/styles.xml b/examples/react-native-expo-example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..da525212e4 --- /dev/null +++ b/examples/react-native-expo-example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/examples/react-native-expo-example/android/build.gradle b/examples/react-native-expo-example/android/build.gradle new file mode 100644 index 0000000000..e34231b2ff --- /dev/null +++ b/examples/react-native-expo-example/android/build.gradle @@ -0,0 +1,41 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0' + minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24') + compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35') + targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34') + kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.24' + + ndkVersion = "26.1.10909125" + } + repositories { + google() + mavenCentral() + } + dependencies { + classpath('com.android.tools.build:gradle') + classpath('com.facebook.react:react-native-gradle-plugin') + classpath('org.jetbrains.kotlin:kotlin-gradle-plugin') + } +} + +apply plugin: "com.facebook.react.rootproject" + +allprojects { + repositories { + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android')) + } + maven { + // Android JSC is installed from npm + url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist')) + } + + google() + mavenCentral() + maven { url 'https://www.jitpack.io' } + } +} diff --git a/examples/react-native-expo-example/android/gradle.properties b/examples/react-native-expo-example/android/gradle.properties new file mode 100644 index 0000000000..7531e9eb23 --- /dev/null +++ b/examples/react-native-expo-example/android/gradle.properties @@ -0,0 +1,56 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true + +# Enable AAPT2 PNG crunching +android.enablePngCrunchInReleaseBuilds=true + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +newArchEnabled=true + +# Use this property to enable or disable the Hermes JS engine. +# If set to false, you will be using JSC instead. +hermesEnabled=true + +# Enable GIF support in React Native images (~200 B increase) +expo.gif.enabled=true +# Enable webp support in React Native images (~85 KB increase) +expo.webp.enabled=true +# Enable animated webp support (~3.4 MB increase) +# Disabled by default because iOS doesn't support animated webp +expo.webp.animated=false + +# Enable network inspector +EX_DEV_CLIENT_NETWORK_INSPECTOR=true + +# Use legacy packaging to compress native libraries in the resulting APK. +expo.useLegacyPackaging=false diff --git a/examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.jar b/examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4b76b9530d66f5e68d973ea569d8e19de379189 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.properties b/examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..79eb9d003f --- /dev/null +++ b/examples/react-native-expo-example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/react-native-expo-example/android/gradlew b/examples/react-native-expo-example/android/gradlew new file mode 100755 index 0000000000..f5feea6d6b --- /dev/null +++ b/examples/react-native-expo-example/android/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/react-native-expo-example/android/gradlew.bat b/examples/react-native-expo-example/android/gradlew.bat new file mode 100644 index 0000000000..9d21a21834 --- /dev/null +++ b/examples/react-native-expo-example/android/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/react-native-expo-example/android/settings.gradle b/examples/react-native-expo-example/android/settings.gradle new file mode 100644 index 0000000000..e59a4a6402 --- /dev/null +++ b/examples/react-native-expo-example/android/settings.gradle @@ -0,0 +1,38 @@ +pluginManagement { + includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile().toString()) +} +plugins { id("com.facebook.react.settings") } + +extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> + if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') { + ex.autolinkLibrariesFromCommand() + } else { + def command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'android' + ].toList() + ex.autolinkLibrariesFromCommand(command) + } +} + +rootProject.name = 'react-native-expo-example' + +dependencyResolutionManagement { + versionCatalogs { + reactAndroidLibs { + from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml"))) + } + } +} + +apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle"); +useExpoModules() + +include ':app' +includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile()) diff --git a/examples/react-native-expo-example/app.json b/examples/react-native-expo-example/app.json new file mode 100644 index 0000000000..ed472d7f03 --- /dev/null +++ b/examples/react-native-expo-example/app.json @@ -0,0 +1,31 @@ +{ + "expo": { + "name": "react-native-expo-example", + "slug": "react-native-expo-example", + "scheme": "aa-rn-expo-example", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "newArchEnabled": true, + "splash": { + "image": "./assets/splash-icon.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + }, + "package": "com.accountkit.reactnativeexpoexample" + }, + "web": { + "favicon": "./assets/favicon.png" + }, + "plugins": ["expo-router"] + } +} diff --git a/examples/react-native-expo-example/app/_layout.tsx b/examples/react-native-expo-example/app/_layout.tsx new file mode 100644 index 0000000000..cb44996d68 --- /dev/null +++ b/examples/react-native-expo-example/app/_layout.tsx @@ -0,0 +1,10 @@ +// Add global shims +import "node-libs-react-native/globals.js"; +import "react-native-get-random-values"; + +import React from "react"; +import { Slot } from "expo-router"; + +export default function RootLayout() { + return ; +} diff --git a/examples/react-native-expo-example/app/index.tsx b/examples/react-native-expo-example/app/index.tsx new file mode 100644 index 0000000000..e2c3cff43b --- /dev/null +++ b/examples/react-native-expo-example/app/index.tsx @@ -0,0 +1,198 @@ +import React, { useCallback } from "react"; + +import { RNAlchemySigner } from "@account-kit/react-native-signer"; +import { + LightAccount, + createLightAccountAlchemyClient, +} from "@account-kit/smart-contracts"; +import { alchemy, sepolia } from "@account-kit/infra"; +import type { User } from "@account-kit/signer"; +import { useEffect, useState } from "react"; +import { + View, + Text, + TextInput, + StyleSheet, + Linking, + TouchableOpacity, +} from "react-native"; +import { API_KEY } from "@env"; + +const signer = new RNAlchemySigner({ + client: { connection: { apiKey: API_KEY } }, +}); + +export default function HomeScreen() { + const [email, setEmail] = useState(""); + const [user, setUser] = useState(null); + const [account, setAccount] = useState(null); + const [signerAddress, setSignerAddress] = useState(null); + const [authRequestSent, setAuthRequestSent] = useState(false); + + const handleUserAuth = ({ bundle }: { bundle: string }) => { + signer + .authenticate({ + bundle, + type: "email", + }) + .then(setUser) + .catch(console.error); + }; + + const handleIncomingURL = useCallback((event: { url: string }) => { + const regex = /[?&]([^=#]+)=([^&#]*)/g; + + setAuthRequestSent(false); + + let params: Record = {}; + let match: RegExpExecArray | null; + + while ((match = regex.exec(event.url))) { + if (match[1] && match[2]) { + params[match[1]] = match[2]; + } + } + + if (!params.bundle || !params.orgId) { + return; + } + + handleUserAuth({ + bundle: params.bundle ?? "", + }); + }, []); + + useEffect(() => { + // get the user if already logged in + signer.getAuthDetails().then(setUser); + }, []); + + // Add listener for incoming links + useEffect(() => { + const subscription = Linking.addEventListener("url", handleIncomingURL); + + return () => subscription.remove(); + }, [handleIncomingURL]); + + useEffect(() => { + if (user) { + createLightAccountAlchemyClient({ + signer, + chain: sepolia, + transport: alchemy({ apiKey: API_KEY! }), + }).then((client) => { + setAccount(client.account); + }); + + signer.getAddress().then((address) => { + setSignerAddress(address); + }); + } + }, [user]); + + return ( + + {authRequestSent ? ( + Auth request sent. Please check your email. + ) : ( + <> + {!user ? ( + <> + + { + setAuthRequestSent(true); + signer + .authenticate({ email, type: "email" }) + .catch(console.error); + }} + > + Sign in + + + ) : ( + <> + + Currently logged in as: {user.email} + + + OrgId: {user.orgId} + + + Address: {user.address} + + + Light Account Address: {account?.address} + + + Signer Address: {signerAddress} + + + + signer + .disconnect() + .then(() => setUser(null)) + } + > + Sign out + + + )} + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + backgroundColor: "#FFFFF", + paddingHorizontal: 20, + }, + textInput: { + width: "100%", + height: 40, + borderColor: "gray", + borderWidth: 1, + paddingHorizontal: 10, + backgroundColor: "rgba(0,0,0,0.05)", + marginTop: 20, + marginBottom: 10, + }, + box: { + width: 60, + height: 60, + marginVertical: 20, + }, + button: { + width: 200, + padding: 10, + height: 50, + backgroundColor: "rgb(147, 197, 253)", + borderRadius: 5, + alignItems: "center", + justifyContent: "center", + marginTop: 20, + }, + buttonText: { + color: "white", + fontWeight: "bold", + textAlign: "center", + }, + userText: { + marginBottom: 10, + fontSize: 18, + }, +}); diff --git a/examples/react-native-expo-example/assets/adaptive-icon.png b/examples/react-native-expo-example/assets/adaptive-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03d6f6b6c6727954aec1d8206222769afd178d8d GIT binary patch literal 17547 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/assets/icon.png b/examples/react-native-expo-example/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b1526fc7b78680fd8d733dbc6113e1af695487 GIT binary patch literal 22380 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 literal 0 HcmV?d00001 diff --git a/examples/react-native-expo-example/assets/splash-icon.png b/examples/react-native-expo-example/assets/splash-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03d6f6b6c6727954aec1d8206222769afd178d8d GIT binary patch literal 17547 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 { + const bundle = req.query.bundle; + const orgId = req.query.orgId; + + if (!bundle || !orgId) { + return res.status(400).send("Missing bundle or orgId"); + } + + res.redirect(`${appScheme}:///?bundle=${bundle}&orgId=${orgId}`); +}); + +app.listen(port, () => { + console.log(`Server is running on port ${port}`); +}); diff --git a/examples/react-native-expo-example/tsconfig.json b/examples/react-native-expo-example/tsconfig.json new file mode 100644 index 0000000000..b9567f6052 --- /dev/null +++ b/examples/react-native-expo-example/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true + } +} diff --git a/site/package.json b/site/package.json index 0420d3bbaf..267bfc104e 100644 --- a/site/package.json +++ b/site/package.json @@ -29,7 +29,7 @@ "@account-kit/signer": "^4.0.0-beta.8", "@account-kit/smart-contracts": "^4.0.0-beta.8", "react": "^18.2.0", - "react-native": "0.76.1", + "react-native": "0.76.5", "react-dom": "^18.2.0", "viem": "2.20.0", "vocs": "^1.0.0-alpha.62", diff --git a/yarn.lock b/yarn.lock index 97ecbfec6d..9d839d006d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,17 @@ # yarn lockfile v1 +"@0no-co/graphql.web@^1.0.5", "@0no-co/graphql.web@^1.0.8": + version "1.0.12" + resolved "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.12.tgz#5fab8caaf5e6dae88e31111305f93dfd152de2fb" + integrity sha512-BTDjjsV/zSPy5fqItwm+KWUfh9CSe9tTtR6rCB72ddtkAxdcHbi4Ir4r/L1Et4lyxmL+i7Rb3m9sjLLi9tYrzA== + "@aashutoshrathi/word-wrap@^1.2.3": version "1.2.6" resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@adobe/css-tools@^4.4.0": +"@adobe/css-tools@^4.3.2", "@adobe/css-tools@^4.4.0": version "4.4.0" resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz" integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== @@ -38,6 +43,13 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@babel/code-frame@7.10.4", "@babel/code-frame@~7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": version "7.26.2" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" @@ -115,7 +127,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/eslint-parser@^7.16.3", "@babel/eslint-parser@^7.20.0", "@babel/eslint-parser@^7.25.1": +"@babel/eslint-parser@^7.16.3", "@babel/eslint-parser@^7.25.1": version "7.25.9" resolved "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz" integrity sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ== @@ -135,6 +147,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" +"@babel/generator@^7.20.5": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" + integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== + dependencies: + "@babel/parser" "^7.26.3" + "@babel/types" "^7.26.3" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.22.5", "@babel/helper-annotate-as-pure@^7.24.7", "@babel/helper-annotate-as-pure@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz" @@ -422,6 +445,16 @@ "@babel/template" "^7.25.9" "@babel/types" "^7.26.0" +"@babel/highlight@^7.10.4": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz#8141ce68fc73757946f983b343f1231f4691acc6" + integrity sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.24.5", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2": version "7.26.2" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz" @@ -429,6 +462,13 @@ dependencies: "@babel/types" "^7.26.0" +"@babel/parser@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" + integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== + dependencies: + "@babel/types" "^7.26.3" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.3": version "7.25.3" resolved "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz" @@ -515,6 +555,15 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-proposal-decorators@^7.12.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz#8680707f943d1a3da2cd66b948179920f097e254" + integrity sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-decorators" "^7.25.9" + "@babel/plugin-proposal-decorators@^7.16.4": version "7.23.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.6.tgz" @@ -607,6 +656,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-syntax-decorators@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz#986b4ca8b7b5df3f67cee889cedeffc2e2bf14b3" + integrity sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" @@ -945,6 +1001,13 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" +"@babel/plugin-transform-export-namespace-from@^7.22.11", "@babel/plugin-transform-export-namespace-from@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz" + integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-export-namespace-from@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz" @@ -953,13 +1016,6 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-export-namespace-from@^7.25.9": - version "7.25.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz" - integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-transform-flow-strip-types@^7.16.0", "@babel/plugin-transform-flow-strip-types@^7.25.2", "@babel/plugin-transform-flow-strip-types@^7.25.7", "@babel/plugin-transform-flow-strip-types@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz" @@ -1142,7 +1198,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-object-rest-spread@^7.24.7", "@babel/plugin-transform-object-rest-spread@^7.25.9": +"@babel/plugin-transform-object-rest-spread@^7.12.13", "@babel/plugin-transform-object-rest-spread@^7.24.7", "@babel/plugin-transform-object-rest-spread@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz" integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== @@ -1191,7 +1247,7 @@ "@babel/helper-plugin-utils" "^7.25.9" "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" -"@babel/plugin-transform-parameters@^7.24.7", "@babel/plugin-transform-parameters@^7.25.9": +"@babel/plugin-transform-parameters@^7.22.15", "@babel/plugin-transform-parameters@^7.24.7", "@babel/plugin-transform-parameters@^7.25.9": version "7.25.9" resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz" integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== @@ -1691,6 +1747,18 @@ "@babel/plugin-transform-react-jsx-development" "^7.22.5" "@babel/plugin-transform-react-pure-annotations" "^7.23.3" +"@babel/preset-react@^7.22.15": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa" + integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-transform-react-display-name" "^7.25.9" + "@babel/plugin-transform-react-jsx" "^7.25.9" + "@babel/plugin-transform-react-jsx-development" "^7.25.9" + "@babel/plugin-transform-react-pure-annotations" "^7.25.9" + "@babel/preset-react@^7.24.7": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.25.9.tgz#5f473035dc2094bcfdbc7392d0766bd42dce173e" @@ -1763,13 +1831,20 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.19.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.1", "@babel/runtime@^7.25.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.19.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.1", "@babel/runtime@^7.25.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.25.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz" integrity sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w== dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.20.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.0.0", "@babel/template@^7.22.5", "@babel/template@^7.24.0", "@babel/template@^7.25.0", "@babel/template@^7.25.9", "@babel/template@^7.3.3": version "7.25.9" resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" @@ -1813,6 +1888,14 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@babel/types@^7.26.3": + version "7.26.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz" @@ -3088,6 +3171,345 @@ resolved "https://registry.yarnpkg.com/@evilmartians/lefthook/-/lefthook-1.8.2.tgz#6ccc23195098c85cee810609093ffea7a63cad84" integrity sha512-SZdQk3W9q7tcJwnSwEMUubQqVIK7SHxv52hEAnV7o3nPI+xKcmd+rN0hZIJg07wjBaJRAjzdvoQySKQQYPW5Qw== +"@expo/bunyan@^4.0.0": + version "4.0.1" + resolved "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.1.tgz#ab9e17e36c71c704a0ce72168378a487368da736" + integrity sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg== + dependencies: + uuid "^8.0.0" + +"@expo/cli@0.22.5": + version "0.22.5" + resolved "https://registry.npmjs.org/@expo/cli/-/cli-0.22.5.tgz#c78bd8d1b0069d3dc7d5d2e647552199aa495c72" + integrity sha512-A2wYKtcBbEEyRUAyUeMDd356UROo1xaMl7ZaZC5tQOdIhvWKelRd4f3QCaI56D9B4EMWLg9pVuPVbAMz8zJ4+A== + dependencies: + "@0no-co/graphql.web" "^1.0.8" + "@babel/runtime" "^7.20.0" + "@expo/code-signing-certificates" "^0.0.5" + "@expo/config" "~10.0.4" + "@expo/config-plugins" "~9.0.10" + "@expo/devcert" "^1.1.2" + "@expo/env" "~0.4.0" + "@expo/image-utils" "^0.6.0" + "@expo/json-file" "^9.0.0" + "@expo/metro-config" "~0.19.0" + "@expo/osascript" "^2.0.31" + "@expo/package-manager" "^1.5.0" + "@expo/plist" "^0.2.0" + "@expo/prebuild-config" "^8.0.23" + "@expo/rudder-sdk-node" "^1.1.1" + "@expo/spawn-async" "^1.7.2" + "@expo/xcpretty" "^4.3.0" + "@react-native/dev-middleware" "0.76.5" + "@urql/core" "^5.0.6" + "@urql/exchange-retry" "^1.3.0" + accepts "^1.3.8" + arg "^5.0.2" + better-opn "~3.0.2" + bplist-creator "0.0.7" + bplist-parser "^0.3.1" + cacache "^18.0.2" + chalk "^4.0.0" + ci-info "^3.3.0" + compression "^1.7.4" + connect "^3.7.0" + debug "^4.3.4" + env-editor "^0.4.1" + fast-glob "^3.3.2" + form-data "^3.0.1" + freeport-async "^2.0.0" + fs-extra "~8.1.0" + getenv "^1.0.0" + glob "^10.4.2" + internal-ip "^4.3.0" + is-docker "^2.0.0" + is-wsl "^2.1.1" + lodash.debounce "^4.0.8" + minimatch "^3.0.4" + node-forge "^1.3.1" + npm-package-arg "^11.0.0" + ora "^3.4.0" + picomatch "^3.0.1" + pretty-bytes "^5.6.0" + pretty-format "^29.7.0" + progress "^2.0.3" + prompts "^2.3.2" + qrcode-terminal "0.11.0" + require-from-string "^2.0.2" + requireg "^0.2.2" + resolve "^1.22.2" + resolve-from "^5.0.0" + resolve.exports "^2.0.2" + semver "^7.6.0" + send "^0.19.0" + slugify "^1.3.4" + source-map-support "~0.5.21" + stacktrace-parser "^0.1.10" + structured-headers "^0.4.1" + tar "^6.2.1" + temp-dir "^2.0.0" + tempy "^0.7.1" + terminal-link "^2.1.1" + undici "^6.18.2" + unique-string "~2.0.0" + wrap-ansi "^7.0.0" + ws "^8.12.1" + +"@expo/code-signing-certificates@^0.0.5": + version "0.0.5" + resolved "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz#a693ff684fb20c4725dade4b88a6a9f96b02496c" + integrity sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw== + dependencies: + node-forge "^1.2.1" + nullthrows "^1.1.1" + +"@expo/config-plugins@~9.0.10", "@expo/config-plugins@~9.0.12": + version "9.0.12" + resolved "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.12.tgz#f122b2dca22e135eadf6e73442da3ced0ce8aa0a" + integrity sha512-/Ko/NM+GzvJyRkq8PITm8ms0KY5v0wmN1OQFYRMkcJqOi3PjlhndW+G6bHpJI9mkQXBaUnHwAiGLqIC3+MQ5Wg== + dependencies: + "@expo/config-types" "^52.0.0" + "@expo/json-file" "~9.0.0" + "@expo/plist" "^0.2.0" + "@expo/sdk-runtime-versions" "^1.0.0" + chalk "^4.1.2" + debug "^4.3.5" + getenv "^1.0.0" + glob "^10.4.2" + resolve-from "^5.0.0" + semver "^7.5.4" + slash "^3.0.0" + slugify "^1.6.6" + xcode "^3.0.1" + xml2js "0.6.0" + +"@expo/config-types@^52.0.0": + version "52.0.1" + resolved "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.1.tgz#327af1b72a3a9d4556f41e083e0e284dd8198b96" + integrity sha512-vD8ZetyKV7U29lR6+NJohYeoLYTH+eNYXJeNiSOrWCz0witJYY11meMmEnpEaVbN89EfC6uauSUOa6wihtbyPQ== + +"@expo/config@~10.0.4", "@expo/config@~10.0.6": + version "10.0.6" + resolved "https://registry.npmjs.org/@expo/config/-/config-10.0.6.tgz#85830491bc8cce2af3f19276922a13f5578d2aa8" + integrity sha512-xXkfPElrtxznkOZxFASJ7OPa6E9IHSjcZwj5BQ6XUF2dz5M7AFa2h5sXM8AalSaDU5tEBSgoUOjTh5957TlR8g== + dependencies: + "@babel/code-frame" "~7.10.4" + "@expo/config-plugins" "~9.0.10" + "@expo/config-types" "^52.0.0" + "@expo/json-file" "^9.0.0" + deepmerge "^4.3.1" + getenv "^1.0.0" + glob "^10.4.2" + require-from-string "^2.0.2" + resolve-from "^5.0.0" + resolve-workspace-root "^2.0.0" + semver "^7.6.0" + slugify "^1.3.4" + sucrase "3.35.0" + +"@expo/devcert@^1.1.2": + version "1.1.4" + resolved "https://registry.npmjs.org/@expo/devcert/-/devcert-1.1.4.tgz#d98807802a541847cc42791a606bfdc26e641277" + integrity sha512-fqBODr8c72+gBSX5Ty3SIzaY4bXainlpab78+vEYEKL3fXmsOswMLf0+KE36mUEAa36BYabX7K3EiXOXX5OPMw== + dependencies: + application-config-path "^0.1.0" + command-exists "^1.2.4" + debug "^3.1.0" + eol "^0.9.1" + get-port "^3.2.0" + glob "^10.4.2" + lodash "^4.17.21" + mkdirp "^0.5.1" + password-prompt "^1.0.4" + sudo-prompt "^8.2.0" + tmp "^0.0.33" + tslib "^2.4.0" + +"@expo/env@~0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@expo/env/-/env-0.4.0.tgz#1ff3a15084566d12ca92cb67e5b0a9796a9f0aa7" + integrity sha512-g2JYFqck3xKIwJyK+8LxZ2ENZPWtRgjFWpeht9abnKgzXVXBeSNECFBkg+WQjQocSIdxXhEWM6hz4ZAe7Tc4ng== + dependencies: + chalk "^4.0.0" + debug "^4.3.4" + dotenv "~16.4.5" + dotenv-expand "~11.0.6" + getenv "^1.0.0" + +"@expo/fingerprint@0.11.3": + version "0.11.3" + resolved "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.11.3.tgz#e370ae8f83e0642f752b058e2102e984a0a5bc98" + integrity sha512-9lgXmcIePvZ7Wef63XtvuN3HfCUevF4E4tQPdEbH9/dUWwpOvvwQ3KT4OJ9jdh8JJ3nTdO9eDQ/8k8xr1aQ5Kg== + dependencies: + "@expo/spawn-async" "^1.7.2" + arg "^5.0.2" + chalk "^4.1.2" + debug "^4.3.4" + find-up "^5.0.0" + getenv "^1.0.0" + minimatch "^3.0.4" + p-limit "^3.1.0" + resolve-from "^5.0.0" + semver "^7.6.0" + +"@expo/image-utils@^0.6.0": + version "0.6.3" + resolved "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.6.3.tgz#89c744460beefc686989b969121357bbd5520c8a" + integrity sha512-v/JbCKBrHeudxn1gN1TgfPE/pWJSlLPrl29uXJBgrJFQVkViQvUHQNDhaS+UEa9wYI5HHh7XYmtzAehyG4L+GA== + dependencies: + "@expo/spawn-async" "^1.7.2" + chalk "^4.0.0" + fs-extra "9.0.0" + getenv "^1.0.0" + jimp-compact "0.16.1" + parse-png "^2.1.0" + resolve-from "^5.0.0" + semver "^7.6.0" + temp-dir "~2.0.0" + unique-string "~2.0.0" + +"@expo/json-file@^9.0.0", "@expo/json-file@~9.0.0": + version "9.0.0" + resolved "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.0.tgz#e3688c9b108cfd7e819f1354a9458ba6e93fc943" + integrity sha512-M+55xFVrFzDcgMDf+52lPDLjKB5xwRfStWlv/b/Vu2OLgxGZLWpxoPYjlRoHqxjPbCQIi2ZCbobK+0KuNhsELg== + dependencies: + "@babel/code-frame" "~7.10.4" + json5 "^2.2.3" + write-file-atomic "^2.3.0" + +"@expo/metro-config@0.19.7", "@expo/metro-config@~0.19.0": + version "0.19.7" + resolved "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.19.7.tgz#68272754ed64c0f3485316efabfc2734551ffb8f" + integrity sha512-6Ti05d6AyvXstMpaRGh2EsdGSJzmOh9ju3gMmcjxckn/cimNL39qRQSrnqYc0R/DEZiRFL7N9mVE/0uG668ojw== + dependencies: + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.5" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" + "@expo/config" "~10.0.4" + "@expo/env" "~0.4.0" + "@expo/json-file" "~9.0.0" + "@expo/spawn-async" "^1.7.2" + chalk "^4.1.0" + debug "^4.3.2" + fs-extra "^9.1.0" + getenv "^1.0.0" + glob "^10.4.2" + jsc-safe-url "^0.2.4" + lightningcss "~1.27.0" + minimatch "^3.0.4" + postcss "~8.4.32" + resolve-from "^5.0.0" + +"@expo/metro-runtime@4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-4.0.0.tgz#fedccde1baebe97c02584331194f1f793492abbe" + integrity sha512-+zgCyuXqIzgZVN8h0g36sursGXBy3xqtJW9han7t/iR2HTTrrbEoep5ftW1a27bdSINU96ng+rSsPLbyHYeBvw== + +"@expo/osascript@^2.0.31": + version "2.1.4" + resolved "https://registry.npmjs.org/@expo/osascript/-/osascript-2.1.4.tgz#4918d16ba09d8b01cb393bc5997055e61d31246f" + integrity sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA== + dependencies: + "@expo/spawn-async" "^1.7.2" + exec-async "^2.2.0" + +"@expo/package-manager@^1.5.0": + version "1.6.1" + resolved "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.6.1.tgz#ab845238dec10bb48bca2b90e060dfe8c1525602" + integrity sha512-4rT46wP/94Ll+CWXtFKok1Lbo9XncSUtErFOo/9/3FVughGbIfdG4SKZOAWIpr9wxwEfkyhHfAP9q71ONlWODw== + dependencies: + "@expo/json-file" "^9.0.0" + "@expo/spawn-async" "^1.7.2" + ansi-regex "^5.0.0" + chalk "^4.0.0" + find-up "^5.0.0" + js-yaml "^3.13.1" + micromatch "^4.0.8" + npm-package-arg "^11.0.0" + ora "^3.4.0" + resolve-workspace-root "^2.0.0" + split "^1.0.1" + sudo-prompt "9.1.1" + +"@expo/plist@^0.2.0": + version "0.2.0" + resolved "https://registry.npmjs.org/@expo/plist/-/plist-0.2.0.tgz#beb014c0bfd56a993086c0972ec1ca3ef3f9d36c" + integrity sha512-F/IZJQaf8OIVnVA6XWUeMPC3OH6MV00Wxf0WC0JhTQht2QgjyHUa3U5Gs3vRtDq8tXNsZneOQRDVwpaOnd4zTQ== + dependencies: + "@xmldom/xmldom" "~0.7.7" + base64-js "^1.2.3" + xmlbuilder "^14.0.0" + +"@expo/prebuild-config@^8.0.23": + version "8.0.23" + resolved "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.23.tgz#2ec6d5464f35d308bdb94ba75b7e6aba0ebb507d" + integrity sha512-Zf01kFiN2PISmLb0DhIAJh76v3J2oYUKSjiAtGZLOH0HUz59by/qdyU4mGHWdeyRdCCrLUA21Rct2MBykvRMsg== + dependencies: + "@expo/config" "~10.0.4" + "@expo/config-plugins" "~9.0.10" + "@expo/config-types" "^52.0.0" + "@expo/image-utils" "^0.6.0" + "@expo/json-file" "^9.0.0" + "@react-native/normalize-colors" "0.76.5" + debug "^4.3.1" + fs-extra "^9.0.0" + resolve-from "^5.0.0" + semver "^7.6.0" + xml2js "0.6.0" + +"@expo/rudder-sdk-node@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz#6aa575f346833eb6290282118766d4919c808c6a" + integrity sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ== + dependencies: + "@expo/bunyan" "^4.0.0" + "@segment/loosely-validate-event" "^2.0.0" + fetch-retry "^4.1.1" + md5 "^2.2.1" + node-fetch "^2.6.1" + remove-trailing-slash "^0.1.0" + uuid "^8.3.2" + +"@expo/sdk-runtime-versions@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c" + integrity sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ== + +"@expo/server@^0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@expo/server/-/server-0.5.0.tgz#7d4b9e37663bc22380761715527e4c06d1b6400a" + integrity sha512-bfo5udr9C2feCn+vGQ9LvjRD2zFjMyBEnMWDZLYr5D8eCjqLjazGBpPKOVjWOhFR2SshKA3hUBkWEYrVpun0NQ== + dependencies: + "@remix-run/node" "^2.12.0" + abort-controller "^3.0.0" + debug "^4.3.4" + source-map-support "~0.5.21" + +"@expo/spawn-async@^1.7.2": + version "1.7.2" + resolved "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58" + integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew== + dependencies: + cross-spawn "^7.0.3" + +"@expo/vector-icons@^14.0.0": + version "14.0.4" + resolved "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.4.tgz#fa9d4351877312badf91a806598b2f0bab16039a" + integrity sha512-+yKshcbpDfbV4zoXOgHxCwh7lkE9VVTT5T03OUlBsqfze1PLy6Hi4jp1vSb1GVbY6eskvMIivGVc9SKzIv0oEQ== + dependencies: + prop-types "^15.8.1" + +"@expo/xcpretty@^4.3.0": + version "4.3.2" + resolved "https://registry.npmjs.org/@expo/xcpretty/-/xcpretty-4.3.2.tgz#12dba1295167a9c8dde4be783d74f7e81648ca5d" + integrity sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw== + dependencies: + "@babel/code-frame" "7.10.4" + chalk "^4.1.0" + find-up "^5.0.0" + js-yaml "^4.1.0" + "@fastify/busboy@^2.0.0": version "2.1.1" resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz" @@ -5028,6 +5450,13 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-slot" "1.1.0" +"@radix-ui/react-compose-refs@1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz#37595b1f16ec7f228d698590e78eeed18ff218ae" + integrity sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs@1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz" @@ -5376,6 +5805,14 @@ aria-hidden "^1.1.1" react-remove-scroll "2.6.0" +"@radix-ui/react-slot@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz#e7868c669c974d649070e9ecbec0b367ee0b4d81" + integrity sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-slot@1.0.2": version "1.0.2" resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz" @@ -5570,16 +6007,6 @@ resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz" integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg== -"@react-native-community/cli-clean@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-15.0.0.tgz#10c7cfde8379aaa7a60eaf4bd3920542d4af1b81" - integrity sha512-ndwVj77eYivHTRmwRBmiAhQq0nC012PDr9cqRQ5QUQl9xr9gXlyO26oWA9jJbXNydXf5DHsVqqDVvh97fERsbg== - dependencies: - "@react-native-community/cli-tools" "15.0.0" - chalk "^4.1.2" - execa "^5.0.0" - fast-glob "^3.3.2" - "@react-native-community/cli-clean@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-15.0.1.tgz#80ce09ffe0d62bb265447007f24dc8dcbf8fe7d3" @@ -5600,18 +6027,6 @@ execa "^5.0.0" fast-glob "^3.3.2" -"@react-native-community/cli-config@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-15.0.0.tgz#c49b1f6b0cc374175a827467bbaf3cee9e9f4ceb" - integrity sha512-YwmQ9Q7JerwqYg0kMD+jwPer1x2ajPR7bjxkOzykfLK4AZxEZo+KgpkSTILMvdqW0WyaXwuYFsgtPa/YVaOn0A== - dependencies: - "@react-native-community/cli-tools" "15.0.0" - chalk "^4.1.2" - cosmiconfig "^9.0.0" - deepmerge "^4.3.0" - fast-glob "^3.3.2" - joi "^17.2.1" - "@react-native-community/cli-config@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-15.0.1.tgz#fe44472757ebca4348fe4861ceaf9d4daff26767" @@ -5624,13 +6039,6 @@ fast-glob "^3.3.2" joi "^17.2.1" -"@react-native-community/cli-debugger-ui@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-15.0.0.tgz#b4186b419bebd513a1f5ba1e2324bc999003a2ec" - integrity sha512-S5A3QZv0ujP/TXZ+1lrlvRfetwuAvrSMJiBEcMh5pzObpr4Ura3naU6bh/ue+QFn9qJtNxoapC2c79B9Ngns/w== - dependencies: - serve-static "^1.13.1" - "@react-native-community/cli-debugger-ui@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-15.0.1.tgz#bed0d7af5ecb05222bdb7d6e74e21326a583bcf1" @@ -5638,28 +6046,6 @@ dependencies: serve-static "^1.13.1" -"@react-native-community/cli-doctor@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-15.0.0.tgz#aeaa90574048f9335aa6495dc5f7641ab3a1af58" - integrity sha512-UEavoARx1VRxZrNiiVWseP/6dBbP/qAJ9q7S4qf7iT6wstssxi+XCBwoONCQp5IIRJ8LAwKkxCksBuhoMDGzQg== - dependencies: - "@react-native-community/cli-config" "15.0.0" - "@react-native-community/cli-platform-android" "15.0.0" - "@react-native-community/cli-platform-apple" "15.0.0" - "@react-native-community/cli-platform-ios" "15.0.0" - "@react-native-community/cli-tools" "15.0.0" - chalk "^4.1.2" - command-exists "^1.2.8" - deepmerge "^4.3.0" - envinfo "^7.13.0" - execa "^5.0.0" - node-stream-zip "^1.9.1" - ora "^5.4.1" - semver "^7.5.2" - strip-ansi "^5.2.0" - wcwidth "^1.0.1" - yaml "^2.2.1" - "@react-native-community/cli-doctor@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-15.0.1.tgz#63cc42e7302f2bfa3739b29fea57b68d5d68fa03" @@ -5682,18 +6068,6 @@ wcwidth "^1.0.1" yaml "^2.2.1" -"@react-native-community/cli-platform-android@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-15.0.0.tgz#d668ee89f559eeeab9eb437ae913710ea955db79" - integrity sha512-YQB48ulIdXqe/hEzPmVe5EU13AIQj/PNGZJSqHGoFs4wQYL4jR04iQ7wxIQSuw11TGZO3ne9rG4/rHt+3imE6Q== - dependencies: - "@react-native-community/cli-tools" "15.0.0" - chalk "^4.1.2" - execa "^5.0.0" - fast-glob "^3.3.2" - fast-xml-parser "^4.4.1" - logkitty "^0.7.1" - "@react-native-community/cli-platform-android@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-15.0.1.tgz#9706fe454d0e2af4680c3ea1937830c93041a35f" @@ -5706,18 +6080,6 @@ fast-xml-parser "^4.4.1" logkitty "^0.7.1" -"@react-native-community/cli-platform-apple@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-apple/-/cli-platform-apple-15.0.0.tgz#2111ab9a55ba8cf212c505a9b78845ebb46318a3" - integrity sha512-DUC4AL3AGNjUDkTrK71fBz2B/aloJm+NHc5deTfEicRvDkyHDM16RqkuFwcvrzaKOtnMDwuDNPM7/PSEp8tbVg== - dependencies: - "@react-native-community/cli-tools" "15.0.0" - chalk "^4.1.2" - execa "^5.0.0" - fast-glob "^3.3.2" - fast-xml-parser "^4.4.1" - ora "^5.4.1" - "@react-native-community/cli-platform-apple@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-15.0.1.tgz#af3c9bc910c96e823a488c21e7d68a9b4a07c8d1" @@ -5729,13 +6091,6 @@ execa "^5.0.0" fast-xml-parser "^4.4.1" -"@react-native-community/cli-platform-ios@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-15.0.0.tgz#58b2467006a13be939bc7fb1bf7a0bcdb2b63663" - integrity sha512-2tP9R0tDIEA55ebNoVZFs0fQgz2nrnMy/epmsUrNC2p4+ZmPQEojqjB+OFaZV4Mh0svks+WoPqf9blk39kN7eg== - dependencies: - "@react-native-community/cli-platform-apple" "15.0.0" - "@react-native-community/cli-platform-ios@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-15.0.1.tgz#a1cb78c3d43b9c2bbb411a074ef11364f2a94bbf" @@ -5743,21 +6098,6 @@ dependencies: "@react-native-community/cli-platform-apple" "15.0.1" -"@react-native-community/cli-server-api@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-15.0.0.tgz#07240d25de556d859b1b78e7b9b4583c86855eca" - integrity sha512-ypq/5SghbuSaOFVaC+TGAlYCp5hTN0mZ6zBheBzD3OTWXhTu9UCBGCjubmBPLastXr0E6G0djTy4xZ5rwCrHWw== - dependencies: - "@react-native-community/cli-debugger-ui" "15.0.0" - "@react-native-community/cli-tools" "15.0.0" - compression "^1.7.1" - connect "^3.6.5" - errorhandler "^1.5.1" - nocache "^3.0.1" - pretty-format "^26.6.2" - serve-static "^1.13.1" - ws "^6.2.3" - "@react-native-community/cli-server-api@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-15.0.1.tgz#e7975e7638343248835fd379803d557c0ae24d75" @@ -5773,23 +6113,6 @@ serve-static "^1.13.1" ws "^6.2.3" -"@react-native-community/cli-tools@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-15.0.0.tgz#16a0f46dd0de0e72b5e4ff711d8bf4957390d44a" - integrity sha512-JZzHRJs+6F6or3tloXdbo6aSL2ifbvs7WKsEPjVFuXfaKNEzpQAqWAKMDr95VUEovuX942yD/QRLo6S2W5NTrw== - dependencies: - appdirsjs "^1.2.4" - chalk "^4.1.2" - execa "^5.0.0" - find-up "^5.0.0" - mime "^2.4.1" - open "^6.2.0" - ora "^5.4.1" - prompts "^2.4.2" - semver "^7.5.2" - shell-quote "^1.7.3" - sudo-prompt "^9.0.0" - "@react-native-community/cli-tools@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-15.0.1.tgz#3cc5398da72b5d365eb4a30468ebce2bf37fa591" @@ -5807,13 +6130,6 @@ shell-quote "^1.7.3" sudo-prompt "^9.0.0" -"@react-native-community/cli-types@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-15.0.0.tgz#65497833df1e44764780acd8e88168f71f2394f7" - integrity sha512-sn+h6grsNxJFzKfOdzJX0HOIHbDnWiOo75+T4DBBdREfPTrq0Ao6NybxDWeircdMA6ovYrJLmjByls2MuCQMUA== - dependencies: - joi "^17.2.1" - "@react-native-community/cli-types@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-15.0.1.tgz#ebdb5bc76ade44b2820174fdcb2a3a05999686ec" @@ -5821,28 +6137,6 @@ dependencies: joi "^17.2.1" -"@react-native-community/cli@15.0.0": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-15.0.0.tgz#003e10899fd77e583d3caa3eb038bfdf86b39788" - integrity sha512-IzDIFCoWZsoOHLSKcd8OX9gAXnbH83vsyBIFaj/X6praDUA4VCnDf41mGGSOT/VEarGlarTa3tvRcqZ8aE5l/A== - dependencies: - "@react-native-community/cli-clean" "15.0.0" - "@react-native-community/cli-config" "15.0.0" - "@react-native-community/cli-debugger-ui" "15.0.0" - "@react-native-community/cli-doctor" "15.0.0" - "@react-native-community/cli-server-api" "15.0.0" - "@react-native-community/cli-tools" "15.0.0" - "@react-native-community/cli-types" "15.0.0" - chalk "^4.1.2" - commander "^9.4.1" - deepmerge "^4.3.0" - execa "^5.0.0" - find-up "^5.0.0" - fs-extra "^8.1.0" - graceful-fs "^4.1.3" - prompts "^2.4.2" - semver "^7.5.2" - "@react-native-community/cli@15.0.1": version "15.0.1" resolved "https://registry.npmjs.org/@react-native-community/cli/-/cli-15.0.1.tgz#d703d55cc6540ce3d29fd2fbf3303bea0ffd96f2" @@ -5865,23 +6159,11 @@ prompts "^2.4.2" semver "^7.5.2" -"@react-native/assets-registry@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.76.1.tgz#eadf31b3fa05139516dc405a8355dc4ecd0d5de7" - integrity sha512-1mcDjyvC4Z+XYtY+Abl6pW9P49l/9HJmRChX7EHF1SoXe7zPAPBoAqeZsJNtf8dhJR3u/eGvapr1yJq8T/psEg== - "@react-native/assets-registry@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.5.tgz#3343338813aa6354df9fec52af50d0b5f7f3d013" integrity sha512-MN5dasWo37MirVcKWuysRkRr4BjNc81SXwUtJYstwbn8oEkfnwR9DaqdDTo/hHOnTdhafffLIa2xOOHcjDIGEw== -"@react-native/babel-plugin-codegen@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.1.tgz#460e4aef8adc99fe89fcff4485783953fff2812d" - integrity sha512-V9bGLyEdAF39nvn4L5gaJcPX1SvCHPJhaT3qfpVGvCnl7WPhdRyCq++WsN8HXlpo6WOAf6//oruLnLdl3RNM4Q== - dependencies: - "@react-native/codegen" "0.76.1" - "@react-native/babel-plugin-codegen@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.5.tgz#a7c32274351e51db9c0a7849ce8059940448c087" @@ -5889,57 +6171,6 @@ dependencies: "@react-native/codegen" "0.76.5" -"@react-native/babel-preset@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.76.1.tgz#2b9fd113e7c7889c1e87d6a36b7cb0f36118e7a6" - integrity sha512-b6YRmA13CmVuTQKHRen/Q0glHwmZFZoEDs+MJ1NL0UNHq9V5ytvdwTW1ntkmjtXuTnPMzkwYvumJBN9UTZjkBA== - dependencies: - "@babel/core" "^7.25.2" - "@babel/plugin-proposal-export-default-from" "^7.24.7" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-default-from" "^7.24.7" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.24.7" - "@babel/plugin-transform-async-generator-functions" "^7.25.4" - "@babel/plugin-transform-async-to-generator" "^7.24.7" - "@babel/plugin-transform-block-scoping" "^7.25.0" - "@babel/plugin-transform-class-properties" "^7.25.4" - "@babel/plugin-transform-classes" "^7.25.4" - "@babel/plugin-transform-computed-properties" "^7.24.7" - "@babel/plugin-transform-destructuring" "^7.24.8" - "@babel/plugin-transform-flow-strip-types" "^7.25.2" - "@babel/plugin-transform-for-of" "^7.24.7" - "@babel/plugin-transform-function-name" "^7.25.1" - "@babel/plugin-transform-literals" "^7.25.2" - "@babel/plugin-transform-logical-assignment-operators" "^7.24.7" - "@babel/plugin-transform-modules-commonjs" "^7.24.8" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.24.7" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.7" - "@babel/plugin-transform-numeric-separator" "^7.24.7" - "@babel/plugin-transform-object-rest-spread" "^7.24.7" - "@babel/plugin-transform-optional-catch-binding" "^7.24.7" - "@babel/plugin-transform-optional-chaining" "^7.24.8" - "@babel/plugin-transform-parameters" "^7.24.7" - "@babel/plugin-transform-private-methods" "^7.24.7" - "@babel/plugin-transform-private-property-in-object" "^7.24.7" - "@babel/plugin-transform-react-display-name" "^7.24.7" - "@babel/plugin-transform-react-jsx" "^7.25.2" - "@babel/plugin-transform-react-jsx-self" "^7.24.7" - "@babel/plugin-transform-react-jsx-source" "^7.24.7" - "@babel/plugin-transform-regenerator" "^7.24.7" - "@babel/plugin-transform-runtime" "^7.24.7" - "@babel/plugin-transform-shorthand-properties" "^7.24.7" - "@babel/plugin-transform-spread" "^7.24.7" - "@babel/plugin-transform-sticky-regex" "^7.24.7" - "@babel/plugin-transform-typescript" "^7.25.2" - "@babel/plugin-transform-unicode-regex" "^7.24.7" - "@babel/template" "^7.25.0" - "@react-native/babel-plugin-codegen" "0.76.1" - babel-plugin-syntax-hermes-parser "^0.23.1" - babel-plugin-transform-flow-enums "^0.0.2" - react-refresh "^0.14.0" - "@react-native/babel-preset@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.5.tgz#794ca17e1107e46712153f296a4930de2048e20e" @@ -5991,20 +6222,6 @@ babel-plugin-transform-flow-enums "^0.0.2" react-refresh "^0.14.0" -"@react-native/codegen@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.76.1.tgz#622185f4038fefc173efec84a08bb7869f04bcd9" - integrity sha512-7lE0hk2qq27wVeK5eF654v7XsKoRa7ficrfSwIDEDZ1aLB2xgUzLrsq+glSAP9EuzT6ycHhtD3QyqI+TqnlS/A== - dependencies: - "@babel/parser" "^7.25.3" - glob "^7.1.1" - hermes-parser "0.23.1" - invariant "^2.2.4" - jscodeshift "^0.14.0" - mkdirp "^0.5.1" - nullthrows "^1.1.1" - yargs "^17.6.2" - "@react-native/codegen@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.5.tgz#4d89ec14a023d6946dbc44537c39b03bd006db7b" @@ -6019,22 +6236,6 @@ nullthrows "^1.1.1" yargs "^17.6.2" -"@react-native/community-cli-plugin@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.1.tgz#d54b2c5fbcb2c5b270261aca530dfdef1bd65421" - integrity sha512-dECc1LuleMQDX/WK2oJInrYCpHb3OFBJxYkhPOAXb9HiktMWRA9T93qqpTDshmtLdYqvxeO9AM5eeoSL412WnQ== - dependencies: - "@react-native/dev-middleware" "0.76.1" - "@react-native/metro-babel-transformer" "0.76.1" - chalk "^4.0.0" - execa "^5.1.1" - invariant "^2.2.4" - metro "^0.81.0" - metro-config "^0.81.0" - metro-core "^0.81.0" - node-fetch "^2.2.0" - readline "^1.3.0" - "@react-native/community-cli-plugin@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.5.tgz#e701a9f99565504a2510d1b54713b1c5dd0f1bb4" @@ -6052,33 +6253,11 @@ readline "^1.3.0" semver "^7.1.3" -"@react-native/debugger-frontend@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.76.1.tgz#f2fd057ecf502ed787579242c107d3912331a9ca" - integrity sha512-0gExx7GR8o2ctGfjIZ9+x54iFbg0eP6+kMYzRA6AcgmFAmMGLADMmjtObCN0CqGeZyWtdVVqcv5mAwRwmMlNWA== - "@react-native/debugger-frontend@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.5.tgz#0e89940543fb5029506690b83f12547d0bf42cc4" integrity sha512-5gtsLfBaSoa9WP8ToDb/8NnDBLZjv4sybQQj7rDKytKOdsXm3Pr2y4D7x7GQQtP1ZQRqzU0X0OZrhRz9xNnOqA== -"@react-native/dev-middleware@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.76.1.tgz#028649d0f1893ce04d177aab5bc233a9ebc7339b" - integrity sha512-htaFSN2dwI0CinsMxjRuvIVdSDN6d6TDPeOJczM1bdAYalZX1M58knTKs5LJDComW5tleOCAg5lS5tIeFlM9+Q== - dependencies: - "@isaacs/ttlcache" "^1.4.1" - "@react-native/debugger-frontend" "0.76.1" - chrome-launcher "^0.15.2" - chromium-edge-launcher "^0.2.0" - connect "^3.6.5" - debug "^2.2.0" - nullthrows "^1.1.1" - open "^7.0.3" - selfsigned "^2.4.1" - serve-static "^1.13.1" - ws "^6.2.3" - "@react-native/dev-middleware@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.5.tgz#10d02fcc6c3c9d24f6dc147c2ef95d6fa6bd3787" @@ -6096,7 +6275,7 @@ serve-static "^1.13.1" ws "^6.2.3" -"@react-native/eslint-config@0.76.5": +"@react-native/eslint-config@0.76.5", "@react-native/eslint-config@^0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/eslint-config/-/eslint-config-0.76.5.tgz#c22daaa8478e740b5e4d7e748ad33fe29628ec9e" integrity sha512-FnzjnwuWrpuJaBfjLMEPtGe6dy3d2Mc3cnoOGF5ghDbpHP2JUHp1GoKRZdZpJlGXJyQTi8wULpyKK6v8jM0dOA== @@ -6114,64 +6293,20 @@ eslint-plugin-react-hooks "^4.6.0" eslint-plugin-react-native "^4.0.0" -"@react-native/eslint-config@^0.73.1": - version "0.73.2" - resolved "https://registry.yarnpkg.com/@react-native/eslint-config/-/eslint-config-0.73.2.tgz#40b2cd8ce245e90c885b8ab15fae1219a946bfac" - integrity sha512-YzMfes19loTfbrkbYNAfHBDXX4oRBzc5wnvHs4h2GIHUj6YKs5ZK5lldqSrBJCdZAI3nuaO9Qj+t5JRwou571w== - dependencies: - "@babel/core" "^7.20.0" - "@babel/eslint-parser" "^7.20.0" - "@react-native/eslint-plugin" "0.73.1" - "@typescript-eslint/eslint-plugin" "^5.57.1" - "@typescript-eslint/parser" "^5.57.1" - eslint-config-prettier "^8.5.0" - eslint-plugin-eslint-comments "^3.2.0" - eslint-plugin-ft-flow "^2.0.1" - eslint-plugin-jest "^26.5.3" - eslint-plugin-prettier "^4.2.1" - eslint-plugin-react "^7.30.1" - eslint-plugin-react-hooks "^4.6.0" - eslint-plugin-react-native "^4.0.0" - -"@react-native/eslint-plugin@0.73.1": - version "0.73.1" - resolved "https://registry.yarnpkg.com/@react-native/eslint-plugin/-/eslint-plugin-0.73.1.tgz#79d2c4d90c80bfad8900db335bfbaf1ca599abdc" - integrity sha512-8BNMFE8CAI7JLWLOs3u33wcwcJ821LYs5g53Xyx9GhSg0h8AygTwDrwmYb/pp04FkCNCPjKPBoaYRthQZmxgwA== - "@react-native/eslint-plugin@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/eslint-plugin/-/eslint-plugin-0.76.5.tgz#c7f1240ff85d539b62aec2f1ec950195da3a01b2" integrity sha512-yAd3349bvWXlegStk6o/lOofRVmr/uSLNdAEsFXw18OlxjnBSx9U3teJtQNA91DfquQAcmSgf1lIBv+MUJ+fnw== -"@react-native/gradle-plugin@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.76.1.tgz#715725ce426686f0c677e733345650990285b5de" - integrity sha512-X7rNFltPa9QYxvYrQGaSCw7U57C+y+DwspXf4AnLZj0bQm9tL6UYpijh5vE3VmPcHn76/RNU2bpFjVvWg6gjqw== - "@react-native/gradle-plugin@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.5.tgz#90d55ec3a99c609358db97b2d7444b28fdc35bc0" integrity sha512-7KSyD0g0KhbngITduC8OABn0MAlJfwjIdze7nA4Oe1q3R7qmAv+wQzW+UEXvPah8m1WqFjYTkQwz/4mK3XrQGw== -"@react-native/js-polyfills@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.76.1.tgz#a429e2b8ac24048931aa596d306375bca8450f92" - integrity sha512-HO3fzJ0FnrnQGmxdXxh2lcGGAMfaX9h1Pg1Zh38MkVw35/KnZHxHqxg6cruze6iWwZdfqSoIcQoalmMuAHby7Q== - "@react-native/js-polyfills@0.76.5": - version "0.76.5" - resolved "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.5.tgz#8f35696d96f804de589cd38382c4f0ffbbc248d5" - integrity sha512-ggM8tcKTcaqyKQcXMIvcB0vVfqr9ZRhWVxWIdiFO1mPvJyS6n+a+lLGkgQAyO8pfH0R1qw6K9D0nqbbDo865WQ== - -"@react-native/metro-babel-transformer@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.1.tgz#155afba9bd2c68a02958f41d8cce9a27a533c75b" - integrity sha512-LUAKqgsrioXS2a+pE0jak8sutTbLo3T34KWv7mdVUZ5lUACpqkIql1EFtIQlWjIcR4oZE480CkPbRHBI681tkQ== - dependencies: - "@babel/core" "^7.25.2" - "@react-native/babel-preset" "0.76.1" - hermes-parser "0.23.1" - nullthrows "^1.1.1" + version "0.76.5" + resolved "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.5.tgz#8f35696d96f804de589cd38382c4f0ffbbc248d5" + integrity sha512-ggM8tcKTcaqyKQcXMIvcB0vVfqr9ZRhWVxWIdiFO1mPvJyS6n+a+lLGkgQAyO8pfH0R1qw6K9D0nqbbDo865WQ== "@react-native/metro-babel-transformer@0.76.5": version "0.76.5" @@ -6183,16 +6318,6 @@ hermes-parser "0.23.1" nullthrows "^1.1.1" -"@react-native/metro-config@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/metro-config/-/metro-config-0.76.1.tgz#a58669555d0e0ffbe694e4a4cbeaa12464f437d3" - integrity sha512-RvsflPKsQ1tEaHDJksnMWwW5wtv8fskMRviL/jHlEW/ULEQ/MOE2yjuvJlRQkNvfqlJjkc1mczjy4+RO3mDQ6g== - dependencies: - "@react-native/js-polyfills" "0.76.1" - "@react-native/metro-babel-transformer" "0.76.1" - metro-config "^0.81.0" - metro-runtime "^0.81.0" - "@react-native/metro-config@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.76.5.tgz#0e6ecbb5eca47e827e977a9fe1b57978df2d611d" @@ -6203,34 +6328,16 @@ metro-config "^0.81.0" metro-runtime "^0.81.0" -"@react-native/normalize-colors@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.1.tgz#df8d54d78917a9f075283382fec834f5ccaecefd" - integrity sha512-/+CUk/wGWIdXbJYVLw/q6Fs8Z0x91zzfXIbNiZUdSW1TNEDmytkF371H8a1/Nx3nWa1RqCMVsaZHCG4zqxeDvg== - "@react-native/normalize-colors@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.5.tgz#a33560736311aefcf1d3cb594597befe81a9a53c" integrity sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug== -"@react-native/typescript-config@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/typescript-config/-/typescript-config-0.76.1.tgz#dc3d3f450b2312b65cdd44d8d9127e89fa1d6260" - integrity sha512-KcmgsFG/c3WdAqy7/06Zvfkye3XIc/0zItlFMSGMgAjFFuCTomXqpmJdrtTBheCDy+gbKaR/vWf+snL8C+OVvA== - "@react-native/typescript-config@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/typescript-config/-/typescript-config-0.76.5.tgz#45ec2459404de5c0b16beec727e8f4cb9e138a29" integrity sha512-dRbY4XQTUUxR5Oq+S+2/5JQVU6WL0qvNnAz51jiXllC+hp5L4bljSxlzaj5CJ9vzGNFzm56m5Y9Q6MltoIU4Cw== -"@react-native/virtualized-lists@0.76.1": - version "0.76.1" - resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.1.tgz#590de081d1229de998b8a0905d07386abc6f2a84" - integrity sha512-uWJfv0FC3zmlYORr0Sa17ngbAaw6K9yw4MAkBZyFeTM+W6AJRvTVyR1Mes/MU+vIyGFChnTcyaQrQz8jWqADOA== - dependencies: - invariant "^2.2.4" - nullthrows "^1.1.1" - "@react-native/virtualized-lists@0.76.5": version "0.76.5" resolved "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.5.tgz#394c2d48687db30c79278d183fda8a129a2d24d3" @@ -6260,6 +6367,19 @@ use-latest-callback "^0.2.1" use-sync-external-store "^1.2.2" +"@react-navigation/core@^7.3.1": + version "7.3.1" + resolved "https://registry.npmjs.org/@react-navigation/core/-/core-7.3.1.tgz#c6d4857fa2dd321d12ca87e200478c38c420f157" + integrity sha512-S3KCGvNsoqVk8ErAtQI2EAhg9185lahF5OY01ofrrD4Ij/uk3QEHHjoGQhR5l5DXSCSKr1JbMQA7MEKMsBiWZA== + dependencies: + "@react-navigation/routers" "^7.1.2" + escape-string-regexp "^4.0.0" + nanoid "3.3.8" + query-string "^7.1.3" + react-is "^18.2.0" + use-latest-callback "^0.2.1" + use-sync-external-store "^1.2.2" + "@react-navigation/elements@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.1.0.tgz#a0d8b3f5895aefb2bcc0f42d0381f1cd52b8a529" @@ -6289,6 +6409,14 @@ "@react-navigation/elements" "^2.1.0" warn-once "^0.1.1" +"@react-navigation/native-stack@^7.0.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.2.0.tgz#8aa489f88d662b3543a931b9cb934bb2e09a4893" + integrity sha512-mw7Nq9qQrGsmJmCTwIIWB7yY/3tWYXvQswx+HJScGAadIjemvytJXm1fcl3+YZ9T9Ym0aERcVe5kDs+ny3X4vA== + dependencies: + "@react-navigation/elements" "^2.2.5" + warn-once "^0.1.1" + "@react-navigation/native-stack@^7.1.14": version "7.1.14" resolved "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.1.14.tgz#179a6efdbeff7a2da36102cb04fe43cb7ff5c48e" @@ -6308,6 +6436,17 @@ nanoid "3.3.7" use-latest-callback "^0.2.1" +"@react-navigation/native@^7.0.0": + version "7.0.14" + resolved "https://registry.npmjs.org/@react-navigation/native/-/native-7.0.14.tgz#b3ee2879038dcf0523d26516af88d3adc549ce5e" + integrity sha512-Gi6lLw4VOGSWAhmUdJOMauOKGK51/YA1CprjXm91sNfgERWvznqEMw8QmUQx9SEqYfi0LfZhbzpMst09SJ00lw== + dependencies: + "@react-navigation/core" "^7.3.1" + escape-string-regexp "^4.0.0" + fast-deep-equal "^3.1.3" + nanoid "3.3.8" + use-latest-callback "^0.2.1" + "@react-navigation/routers@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.0.0.tgz#fb55a4d70a14e16b630c386fd09ba43664cc7409" @@ -6315,6 +6454,13 @@ dependencies: nanoid "3.3.7" +"@react-navigation/routers@^7.1.2": + version "7.1.2" + resolved "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.1.2.tgz#647a63e383673de0c4fc10c64a17f551d5da0a17" + integrity sha512-emdEjpVDK8zbiu2GChC8oYIAub9i/OpNuQJekVsbyFCBz4/TzaBzms38Q53YaNhdIFNmiYLfHv/Y1Ub7KYfm3w== + dependencies: + nanoid "3.3.8" + "@release-it/conventional-changelog@^5.0.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@release-it/conventional-changelog/-/conventional-changelog-5.1.1.tgz#5e3affbe8d1814fe47d89777e3375a8a90c073b5" @@ -6728,6 +6874,14 @@ resolved "https://registry.npmjs.org/@segment/isodate/-/isodate-1.0.3.tgz" integrity sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A== +"@segment/loosely-validate-event@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz#87dfc979e5b4e7b82c5f1d8b722dfd5d77644681" + integrity sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw== + dependencies: + component-type "^1.2.1" + join-component "^1.1.0" + "@shikijs/core@1.22.2": version "1.22.2" resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.22.2.tgz#9c22bd4cc8a4d6c062461cfd35e1faa6c617ca25" @@ -7151,14 +7305,14 @@ dependencies: "@storybook/global" "^5.0.0" -"@storybook/addon-interactions@^8.4.4": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-8.4.4.tgz#6c005ba711effc281273fd6336525052122ea42c" - integrity sha512-izqcc6tY0BiKW7DYrEnoXUEH9FYDPWNfQnqqE0nVBv3BS2DoNmm8M9SB8fZx7pPfw53cMJBGt3vrlY0Wtxy1+Q== +"@storybook/addon-interactions@^8.2.8": + version "8.2.9" + resolved "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.2.9.tgz" + integrity sha512-oSxBkqpmp1Vm9v/G8mZeFNXD8k6T1NMgzUWzAx7R5m31rfObhoi5Fo1bKQT5BAhSSsdjjd7owTAFKdhwSotSKg== dependencies: "@storybook/global" "^5.0.0" - "@storybook/instrumenter" "8.4.4" - "@storybook/test" "8.4.4" + "@storybook/instrumenter" "8.2.9" + "@storybook/test" "8.2.9" polished "^4.2.2" ts-dedent "^2.2.0" @@ -7348,7 +7502,7 @@ recast "^0.23.1" ts-dedent "^2.0.0" -"@storybook/csf@0.1.11", "@storybook/csf@^0.1.1", "@storybook/csf@^0.1.11", "@storybook/csf@^0.1.2": +"@storybook/csf@0.1.11", "@storybook/csf@^0.1.1", "@storybook/csf@^0.1.2": version "0.1.11" resolved "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.11.tgz" integrity sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg== @@ -7372,13 +7526,14 @@ resolved "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.10.tgz" integrity sha512-310apKdDcjbbX2VSLWPwhEwAgjxTzVagrwucVZIdGPErwiAppX8KvBuWZgPo+rQLVrtH8S+pw1dbUwjcE6d7og== -"@storybook/instrumenter@8.4.4": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-8.4.4.tgz#90256f7807de6b8092ee2f55055051bd6bd2cddd" - integrity sha512-mq/YVEZrB8jyyio2Of01rQixsQ72z8ssAhJS9ldIlK+cvERQi0VBCpH3pejPmjOB40yiKBJHNqH4HIANVhibgw== +"@storybook/instrumenter@8.2.9": + version "8.2.9" + resolved "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.2.9.tgz" + integrity sha512-+DNjTbsMzlDggsvkhRuOy7aGvQJ4oLCPgunP5Se/3yBjG+M2bYDa0EmC5jC2nwZ3ffpuvbzaVe7fWf7R8W9F2Q== dependencies: "@storybook/global" "^5.0.0" - "@vitest/utils" "^2.1.1" + "@vitest/utils" "^1.3.1" + util "^0.12.4" "@storybook/jest@^0.2.3": version "0.2.3" @@ -7505,19 +7660,19 @@ tempy "^1.0.1" ts-dedent "^2.0.0" -"@storybook/test@8.4.4", "@storybook/test@^8.4.4": - version "8.4.4" - resolved "https://registry.yarnpkg.com/@storybook/test/-/test-8.4.4.tgz#c2116e455ea439990c235475b9b1bbca331257d9" - integrity sha512-tmJd+lxl3MC0Xdu1KW/69V8tibv98OvdopxGqfVR0x5dkRHM3sFK/tv1ZJAUeronlvRyhGySOu1tHUrMjcNqyA== +"@storybook/test@8.2.9": + version "8.2.9" + resolved "https://registry.npmjs.org/@storybook/test/-/test-8.2.9.tgz" + integrity sha512-O5JZ5S8UVVR7V0ru5AiF/uRO+srAVwji0Iik7ihy8gw3V91WQNMmJh2KkdhG0R1enYeBsYZlipOm+AW7f/MmOA== dependencies: - "@storybook/csf" "^0.1.11" - "@storybook/global" "^5.0.0" - "@storybook/instrumenter" "8.4.4" - "@testing-library/dom" "10.4.0" - "@testing-library/jest-dom" "6.5.0" + "@storybook/csf" "0.1.11" + "@storybook/instrumenter" "8.2.9" + "@testing-library/dom" "10.1.0" + "@testing-library/jest-dom" "6.4.5" "@testing-library/user-event" "14.5.2" - "@vitest/expect" "2.0.5" - "@vitest/spy" "2.0.5" + "@vitest/expect" "1.6.0" + "@vitest/spy" "1.6.0" + util "^0.12.4" "@storybook/testing-library@^0.2.2": version "0.2.2" @@ -7716,10 +7871,10 @@ dependencies: "@tanstack/form-core" "0.33.0" -"@testing-library/dom@10.4.0": - version "10.4.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" - integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== +"@testing-library/dom@10.1.0": + version "10.1.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz" + integrity sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -7744,7 +7899,21 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@6.5.0", "@testing-library/jest-dom@^6.1.2": +"@testing-library/jest-dom@6.4.5": + version "6.4.5" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.5.tgz" + integrity sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A== + dependencies: + "@adobe/css-tools" "^4.3.2" + "@babel/runtime" "^7.9.2" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + +"@testing-library/jest-dom@^6.1.2": version "6.5.0" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz" integrity sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA== @@ -8364,6 +8533,14 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/react@~18.3.12": + version "18.3.16" + resolved "https://registry.npmjs.org/@types/react/-/react-18.3.16.tgz#5326789125fac98b718d586ad157442ceb44ff28" + integrity sha512-oh8AMIC4Y2ciKufU8hnKgs+ufgbA/dhPTACaZPM86AbwX9QwnFtSoPWEeRUj8fge+v6kFt78BXcDhAU1SrrAsw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/resolve@^1.20.2": version "1.20.6" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz" @@ -8481,7 +8658,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.5.0", "@typescript-eslint/eslint-plugin@^5.57.1": +"@typescript-eslint/eslint-plugin@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz" integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== @@ -8530,7 +8707,7 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "@typescript-eslint/parser@^5.5.0", "@typescript-eslint/parser@^5.57.1": +"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "@typescript-eslint/parser@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz" integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== @@ -8917,6 +9094,22 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@urql/core@^5.0.0", "@urql/core@^5.0.6": + version "5.1.0" + resolved "https://registry.npmjs.org/@urql/core/-/core-5.1.0.tgz#7f4b81f1aba1ca34ae6354763abeb87ff9af84ff" + integrity sha512-yC3sw8yqjbX45GbXxfiBY8GLYCiyW/hLBbQF9l3TJrv4ro00Y0ChkKaD9I2KntRxAVm9IYBqh0awX8fwWAe/Yw== + dependencies: + "@0no-co/graphql.web" "^1.0.5" + wonka "^6.3.2" + +"@urql/exchange-retry@^1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.0.tgz#c46abdf74aee612645ff47562e6c6868ad1731dd" + integrity sha512-FLt+d81gP4oiHah4hWFDApimc+/xABWMU1AMYsZ1PVB0L0YPtrMCjbOp9WMM7hBzy4gbTDrG24sio0dCfSh/HQ== + dependencies: + "@urql/core" "^5.0.0" + wonka "^6.3.2" + "@vanilla-extract/babel-plugin-debug-ids@^1.0.4": version "1.0.5" resolved "https://registry.npmjs.org/@vanilla-extract/babel-plugin-debug-ids/-/babel-plugin-debug-ids-1.0.5.tgz" @@ -9194,6 +9387,15 @@ "@types/babel__core" "^7.20.5" react-refresh "^0.14.2" +"@vitest/expect@1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz" + integrity sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ== + dependencies: + "@vitest/spy" "1.6.0" + "@vitest/utils" "1.6.0" + chai "^4.3.10" + "@vitest/expect@2.0.4": version "2.0.4" resolved "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.4.tgz" @@ -9204,16 +9406,6 @@ chai "^5.1.1" tinyrainbow "^1.2.0" -"@vitest/expect@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.0.5.tgz#f3745a6a2c18acbea4d39f5935e913f40d26fa86" - integrity sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA== - dependencies: - "@vitest/spy" "2.0.5" - "@vitest/utils" "2.0.5" - chai "^5.1.1" - tinyrainbow "^1.2.0" - "@vitest/pretty-format@2.0.4", "@vitest/pretty-format@^2.0.4": version "2.0.4" resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.4.tgz" @@ -9221,20 +9413,6 @@ dependencies: tinyrainbow "^1.2.0" -"@vitest/pretty-format@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.0.5.tgz#91d2e6d3a7235c742e1a6cc50e7786e2f2979b1e" - integrity sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ== - dependencies: - tinyrainbow "^1.2.0" - -"@vitest/pretty-format@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.5.tgz#bc79b8826d4a63dc04f2a75d2944694039fa50aa" - integrity sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw== - dependencies: - tinyrainbow "^1.2.0" - "@vitest/runner@2.0.4": version "2.0.4" resolved "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.4.tgz" @@ -9252,6 +9430,13 @@ magic-string "^0.30.10" pathe "^1.1.2" +"@vitest/spy@1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz" + integrity sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw== + dependencies: + tinyspy "^2.2.0" + "@vitest/spy@2.0.4": version "2.0.4" resolved "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.4.tgz" @@ -9259,12 +9444,15 @@ dependencies: tinyspy "^3.0.0" -"@vitest/spy@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.0.5.tgz#590fc07df84a78b8e9dd976ec2090920084a2b9f" - integrity sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA== +"@vitest/utils@1.6.0", "@vitest/utils@^1.3.1": + version "1.6.0" + resolved "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz" + integrity sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw== dependencies: - tinyspy "^3.0.0" + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" "@vitest/utils@2.0.4": version "2.0.4" @@ -9276,25 +9464,6 @@ loupe "^3.1.1" tinyrainbow "^1.2.0" -"@vitest/utils@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.0.5.tgz#6f8307a4b6bc6ceb9270007f73c67c915944e926" - integrity sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ== - dependencies: - "@vitest/pretty-format" "2.0.5" - estree-walker "^3.0.3" - loupe "^3.1.1" - tinyrainbow "^1.2.0" - -"@vitest/utils@^2.1.1": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.5.tgz#0e19ce677c870830a1573d33ee86b0d6109e9546" - integrity sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg== - dependencies: - "@vitest/pretty-format" "2.1.5" - loupe "^3.1.2" - tinyrainbow "^1.2.0" - "@wagmi/connectors@5.1.7": version "5.1.7" resolved "https://registry.npmjs.org/@wagmi/connectors/-/connectors-5.1.7.tgz" @@ -9722,6 +9891,16 @@ resolved "https://registry.npmjs.org/@web3-storage/multipart-parser/-/multipart-parser-1.0.0.tgz" integrity sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw== +"@xmldom/xmldom@^0.8.8": + version "0.8.10" + resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + +"@xmldom/xmldom@~0.7.7": + version "0.7.13" + resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz#ff34942667a4e19a9f4a0996a76814daac364cf3" + integrity sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== + "@yarnpkg/fslib@2.10.3": version "2.10.3" resolved "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.3.tgz" @@ -9798,7 +9977,7 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: +accepts@^1.3.7, accepts@^1.3.8, accepts@~1.3.5, accepts@~1.3.7, accepts@~1.3.8: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -9900,6 +10079,20 @@ aggregate-error@^4.0.0: clean-stack "^4.0.0" indent-string "^5.0.0" +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + ajv@8.6.3: version "8.6.3" resolved "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz" @@ -9920,6 +10113,16 @@ ajv@^6.0.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ajv@^8.11.0: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" @@ -10064,6 +10267,11 @@ append-transform@^2.0.0: dependencies: default-require-extensions "^3.0.0" +application-config-path@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz#8b5ac64ff6afdd9bd70ce69f6f64b6998f5f756e" + integrity sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw== + "aproba@^1.0.3 || ^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" @@ -10286,7 +10494,7 @@ arrify@^2.0.1: resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@~2.0.6: +asap@~2.0.3, asap@~2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -10308,6 +10516,11 @@ assert@^1.4.1: object.assign "^4.1.4" util "^0.10.4" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + assertion-error@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" @@ -10597,6 +10810,11 @@ babel-plugin-polyfill-regenerator@^0.6.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.6.2" +babel-plugin-react-native-web@~0.19.13: + version "0.19.13" + resolved "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz#bf919bd6f18c4689dd1a528a82bda507363b953d" + integrity sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ== + babel-plugin-syntax-hermes-parser@^0.23.1: version "0.23.1" resolved "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.23.1.tgz" @@ -10644,6 +10862,21 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" +babel-preset-expo@~12.0.4: + version "12.0.4" + resolved "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-12.0.4.tgz#ec965530d866c8905aac1fa478562cb08ab32a55" + integrity sha512-SAzAwqpyjA+/OFrU95OOioj6oTeCv4+rRfrNmBTy5S/gJswrZKBSPJioFudIaJBy43W+BL7HA5AspBIF6tO/aA== + dependencies: + "@babel/plugin-proposal-decorators" "^7.12.9" + "@babel/plugin-transform-export-namespace-from" "^7.22.11" + "@babel/plugin-transform-object-rest-spread" "^7.12.13" + "@babel/plugin-transform-parameters" "^7.22.15" + "@babel/preset-react" "^7.22.15" + "@babel/preset-typescript" "^7.23.0" + "@react-native/babel-preset" "0.76.5" + babel-plugin-react-native-web "~0.19.13" + react-refresh "^0.14.2" + babel-preset-jest@^28.1.3: version "28.1.3" resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz" @@ -10697,7 +10930,7 @@ base-64@^0.1.0: resolved "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== -base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.0.2, base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -10722,7 +10955,14 @@ before-after-hook@^2.2.0: resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== -big-integer@^1.6.44: +better-opn@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817" + integrity sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ== + dependencies: + open "^8.0.4" + +big-integer@1.6.x, big-integer@^1.6.44: version "1.6.52" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== @@ -10837,6 +11077,27 @@ boxen@^7.0.0: widest-line "^4.0.1" wrap-ansi "^8.1.0" +bplist-creator@0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.7.tgz#37df1536092824b87c42f957b01344117372ae45" + integrity sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA== + dependencies: + stream-buffers "~2.2.0" + +bplist-creator@0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== + dependencies: + stream-buffers "2.2.x" + +bplist-parser@0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" + integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== + dependencies: + big-integer "1.6.x" + bplist-parser@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" @@ -10844,6 +11105,13 @@ bplist-parser@^0.2.0: dependencies: big-integer "^1.6.44" +bplist-parser@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz#3ac79d67ec52c4c107893e0237eb787cbacbced7" + integrity sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ== + dependencies: + big-integer "1.6.x" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -10963,6 +11231,19 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" @@ -10973,6 +11254,11 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" @@ -10992,7 +11278,7 @@ buffer@^4.9.1: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.5.0: +buffer@^5.4.3, buffer@^5.5.0: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -11119,6 +11405,24 @@ cacache@^18.0.0: tar "^6.1.11" unique-filename "^3.0.0" +cacache@^18.0.2: + version "18.0.4" + resolved "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz#4601d7578dadb59c66044e157d02a3314682d6a5" + integrity sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cacheable-lookup@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" @@ -11260,6 +11564,19 @@ ccount@^2.0.0: resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== +chai@^4.3.10: + version "4.5.0" + resolved "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz" + integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.1.0" + chai@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz" @@ -11289,7 +11606,7 @@ chalk@5.3.0, chalk@^5.0.0, chalk@^5.0.1, chalk@^5.2.0, chalk@^5.3.0: resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== -chalk@^2.4.2: +chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -11374,6 +11691,18 @@ chardet@^0.7.0: resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + check-error@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz" @@ -11471,7 +11800,7 @@ ci-info@^2.0.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^3.1.0, ci-info@^3.2.0, ci-info@^3.6.1, ci-info@^3.7.0: +ci-info@^3.1.0, ci-info@^3.2.0, ci-info@^3.3.0, ci-info@^3.6.1, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -11544,6 +11873,13 @@ cli-cursor@3.1.0, cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== + dependencies: + restore-cursor "^2.0.0" + cli-cursor@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz" @@ -11556,7 +11892,7 @@ cli-spinners@2.6.1: resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-spinners@^2.5.0, cli-spinners@^2.6.1, cli-spinners@^2.9.0, cli-spinners@^2.9.2: +cli-spinners@^2.0.0, cli-spinners@^2.5.0, cli-spinners@^2.6.1, cli-spinners@^2.9.0, cli-spinners@^2.9.2: version "2.9.2" resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== @@ -11579,7 +11915,7 @@ cli-width@^4.0.0, cli-width@^4.1.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== -client-only@0.0.1: +client-only@0.0.1, client-only@^0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== @@ -11769,7 +12105,7 @@ comma-separated-tokens@^2.0.0: resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== -command-exists@^1.2.8: +command-exists@^1.2.4, command-exists@^1.2.8: version "1.2.9" resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== @@ -11809,6 +12145,11 @@ commander@^6.2.1: resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^9.0.0, commander@^9.4.1: version "9.5.0" resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" @@ -11840,6 +12181,11 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +component-type@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/component-type/-/component-type-1.2.2.tgz#4458ecc0c1871efc6288bfaff0cbdab08141d079" + integrity sha512-99VUHREHiN5cLeHm3YLq312p6v+HUEcwtLCAtelvUDI6+SH5g5Cr85oNR2S1o6ywzL0ykMbuwLzM2ANocjEOIA== + compressible@~2.0.16: version "2.0.18" resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" @@ -11904,7 +12250,7 @@ confusing-browser-globals@^1.0.11: resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== -connect@^3.6.5: +connect@^3.6.5, connect@^3.7.0: version "3.7.0" resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz" integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== @@ -12427,6 +12773,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + crypto-browserify@^3.12.1: version "3.12.1" resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz#bb8921bec9acc81633379aa8f52d69b0b69e0dac" @@ -12603,7 +12954,7 @@ debug@4.1.1: dependencies: ms "^2.1.1" -debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -12674,6 +13025,13 @@ dedent@^1.0.0, dedent@^1.5.1, dedent@^1.5.3: resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz" integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== +deep-eql@^4.1.3: + version "4.1.4" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz" + integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== + dependencies: + type-detect "^4.0.0" + deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz" @@ -12718,7 +13076,7 @@ deep-object-diff@^1.1.9: resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz" integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA== -deepmerge@^4.2.2, deepmerge@^4.3.0: +deepmerge@^4.2.2, deepmerge@^4.3.0, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== @@ -12741,6 +13099,14 @@ default-browser@^4.0.0: execa "^7.1.1" titleize "^3.0.0" +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + default-require-extensions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz" @@ -13114,6 +13480,13 @@ dotenv-expand@^10.0.0, dotenv-expand@~10.0.0: resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== +dotenv-expand@~11.0.6: + version "11.0.7" + resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz#af695aea007d6fdc84c86cd8d0ad7beb40a0bd08" + integrity sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA== + dependencies: + dotenv "^16.4.5" + dotenv@^16.0.0, dotenv@^16.3.0, dotenv@^16.4.5: version "16.4.5" resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz" @@ -13124,7 +13497,7 @@ dotenv@^16.0.3, dotenv@^16.3.1: resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -dotenv@^16.4.7: +dotenv@^16.4.7, dotenv@~16.4.5: version "16.4.7" resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== @@ -13362,6 +13735,11 @@ env-ci@^5.0.0: fromentries "^1.3.2" java-properties "^1.0.0" +env-editor@^0.4.1: + version "0.4.2" + resolved "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861" + integrity sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA== + env-paths@^2.2.0, env-paths@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" @@ -13382,6 +13760,11 @@ envinfo@^7.7.3: resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz" integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== +eol@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" + integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== + err-code@^2.0.2: version "2.0.3" resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" @@ -14166,13 +14549,6 @@ eslint-plugin-jest@^25.3.0: dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" -eslint-plugin-jest@^26.5.3: - version "26.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.9.0.tgz#7931c31000b1c19e57dbfb71bbf71b817d1bf949" - integrity sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng== - dependencies: - "@typescript-eslint/utils" "^5.10.0" - eslint-plugin-jest@^27.9.0: version "27.9.0" resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b" @@ -14238,13 +14614,6 @@ eslint-plugin-mdx@^3.1.5: unified "^11.0.4" vfile "^6.0.1" -eslint-plugin-prettier@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== - dependencies: - prettier-linter-helpers "^1.0.0" - eslint-plugin-prettier@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95" @@ -14579,6 +14948,11 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +exec-async@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/exec-async/-/exec-async-2.2.0.tgz#c7c5ad2eef3478d38390c6dd3acfe8af0efc8301" + integrity sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw== + execa@3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz" @@ -14755,6 +15129,120 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +expo-asset@~11.0.1: + version "11.0.1" + resolved "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.1.tgz#8608f5ea4639698553725b6690dd621f6f70f206" + integrity sha512-WatvD7JVC89EsllXFYcS/rji3ajVzE2B/USo0TqedsETixwyVCQfrrvCdCPQyuKghrxVNEj8bQ/Qbea/RZLYjg== + dependencies: + "@expo/image-utils" "^0.6.0" + expo-constants "~17.0.0" + invariant "^2.2.4" + md5-file "^3.2.3" + +expo-constants@~17.0.0, expo-constants@~17.0.3: + version "17.0.3" + resolved "https://registry.npmjs.org/expo-constants/-/expo-constants-17.0.3.tgz#a05b38e0417d59759ece1642b4d483889e04dbda" + integrity sha512-lnbcX2sAu8SucHXEXxSkhiEpqH+jGrf+TF+MO6sHWIESjwOUVVYlT8qYdjR9xbxWmqFtrI4KV44FkeJf2DaFjQ== + dependencies: + "@expo/config" "~10.0.4" + "@expo/env" "~0.4.0" + +expo-file-system@~18.0.5: + version "18.0.5" + resolved "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.5.tgz#f7cce475bb6ce64ba8bf3a689e89445971347469" + integrity sha512-vm7gA+PB7j99hfvBBFMRiti8OeazFK3AZWtDmCi6WQCXDxngXkAJViXhkHyF3xwDKljzlP8+4BIGrKCzbfoObg== + dependencies: + web-streams-polyfill "^3.3.2" + +expo-font@~13.0.1: + version "13.0.1" + resolved "https://registry.npmjs.org/expo-font/-/expo-font-13.0.1.tgz#3a7eed7a4238a352fc74a425fd8c020c5f122382" + integrity sha512-8JE47B+6cLeKWr5ql8gU6YsPHjhrz1vMrTqYMm72No/8iW8Sb/uL4Oc0dpmbjq3hLLXBY0xPBQOgU7FQ6Y04Vg== + dependencies: + fontfaceobserver "^2.1.0" + +expo-keep-awake@~14.0.1: + version "14.0.1" + resolved "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.0.1.tgz#77c38feefa95c494aa167e6df5a6eacd17af2358" + integrity sha512-c5mGCAIk2YM+Vsdy90BlEJ4ZX+KG5Au9EkJUIxXWlpnuKmDAJ3N+5nEZ7EUO1ZTheqoSBeAo4jJ8rTWPU+JXdw== + +expo-linking@~7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/expo-linking/-/expo-linking-7.0.3.tgz#307851288ec65e1c533bcc70b57dfb6372f9679e" + integrity sha512-YiDacNzeQZd/bdOwGyi+YlawM4GGbcSRkuFCpDGIK7D1KUGqLinBHwJvxUMb9Zert2Ois5IHtmZaZ1et6g229g== + dependencies: + expo-constants "~17.0.0" + invariant "^2.2.4" + +expo-modules-autolinking@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.4.tgz#28fcd12fb0d066a2933cca3bf3b597da0f6b2f2a" + integrity sha512-e0p+19NhmD50U7s7BV7kWIypWmTNC9n/VlJKlXS05hM/zX7pe6JKmXyb+BFnXJq3SLBalLCUY0tu2gEUF3XeVg== + dependencies: + "@expo/spawn-async" "^1.7.2" + chalk "^4.1.0" + commander "^7.2.0" + fast-glob "^3.2.5" + find-up "^5.0.0" + fs-extra "^9.1.0" + require-from-string "^2.0.2" + resolve-from "^5.0.0" + +expo-modules-core@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.1.1.tgz#970af4cfd70c8aa6fc0096dd0a6578aa003a479f" + integrity sha512-yQzYCLR2mre4BNMXuqkeJ0oSNgmGEMI6BcmIzeNZbC2NFEjiaDpKvlV9bclYCtyVhUEVNbJcEPYMr6c1Y4eR4w== + dependencies: + invariant "^2.2.4" + +expo-router@4.0.11: + version "4.0.11" + resolved "https://registry.npmjs.org/expo-router/-/expo-router-4.0.11.tgz#324364534fc7837a99a7894c611b3cd75e4470c5" + integrity sha512-2Qrd/fk98kC+CTg1umbuUaBaGkpdGStPpkSR99SoAjX6KWC1WhNMCv0hGFn7cRmSNOWQzgIfLGLERhRY1o4myw== + dependencies: + "@expo/metro-runtime" "4.0.0" + "@expo/server" "^0.5.0" + "@radix-ui/react-slot" "1.0.1" + "@react-navigation/bottom-tabs" "^7.0.0" + "@react-navigation/native" "^7.0.0" + "@react-navigation/native-stack" "^7.0.0" + client-only "^0.0.1" + react-helmet-async "^1.3.0" + react-native-helmet-async "2.0.4" + react-native-is-edge-to-edge "^1.1.6" + schema-utils "^4.0.1" + semver "~7.6.3" + server-only "^0.0.1" + +expo-status-bar@~2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-2.0.0.tgz#dd99adc2ace12a24c92718cd0f97b93347103393" + integrity sha512-vxxdpvpNDMTEc5uTiIrbTvySKKUsOACmfl8OZuUdjNle05oGqwtq3v5YObwym/njSByjoyuZX8UpXBZnxvarwQ== + +expo@~52.0.18: + version "52.0.18" + resolved "https://registry.npmjs.org/expo/-/expo-52.0.18.tgz#1a471124f9a05788209aac1363e87de85b2ec0e5" + integrity sha512-z+qdUbH0d5JRknE3VrY0s5k+3j5JpsLx4vXRwV4To8Xm5uf3d642FQ2HbuPWFAAhtSKFQsxQAh3iuAUGAWDBhg== + dependencies: + "@babel/runtime" "^7.20.0" + "@expo/cli" "0.22.5" + "@expo/config" "~10.0.6" + "@expo/config-plugins" "~9.0.12" + "@expo/fingerprint" "0.11.3" + "@expo/metro-config" "0.19.7" + "@expo/vector-icons" "^14.0.0" + babel-preset-expo "~12.0.4" + expo-asset "~11.0.1" + expo-constants "~17.0.3" + expo-file-system "~18.0.5" + expo-font "~13.0.1" + expo-keep-awake "~14.0.1" + expo-modules-autolinking "2.0.4" + expo-modules-core "2.1.1" + fbemitter "^3.0.0" + web-streams-polyfill "^3.3.2" + whatwg-url-without-unicode "8.0.0-3" + exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz" @@ -14915,7 +15403,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1, fast-glob@^3.3.2: +fast-glob@^3.2.11, fast-glob@^3.2.5, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -14946,6 +15434,11 @@ fast-safe-stringify@^2.0.6: resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + fast-xml-parser@^4.4.1: version "4.5.0" resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz" @@ -14981,6 +15474,31 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0: + version "3.0.5" + resolved "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" + integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== + dependencies: + cross-fetch "^3.1.5" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^1.0.35" + fd-package-json@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/fd-package-json/-/fd-package-json-1.2.0.tgz" @@ -15003,6 +15521,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: node-domexception "^1.0.0" web-streams-polyfill "^3.0.3" +fetch-retry@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/fetch-retry/-/fetch-retry-4.1.1.tgz#fafe0bb22b54f4d0a9c788dff6dd7f8673ca63f3" + integrity sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA== + figures@3.2.0, figures@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" @@ -15244,6 +15767,11 @@ follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz" integrity sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig== +fontfaceobserver@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz#5fb392116e75d5024b7ec8e4f2ce92106d1488c8" + integrity sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" @@ -15277,6 +15805,15 @@ form-data-encoder@^2.1.2: resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== +form-data@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz#83ad9ced7c03feaad97e293d6f6091011e1659c8" + integrity sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" @@ -15316,6 +15853,11 @@ fraction.js@^4.3.6, fraction.js@^4.3.7: resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== +freeport-async@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz#6adf2ec0c629d11abff92836acd04b399135bab4" + integrity sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ== + fresh@0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" @@ -15354,14 +15896,24 @@ fs-extra@11.1.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@8.1.0, fs-extra@^8.1.0: +fs-extra@8.1.0, fs-extra@^8.1.0, fs-extra@~8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" + integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^1.0.0" fs-extra@^10.1.0: version "10.1.0" @@ -15381,7 +15933,7 @@ fs-extra@^11.0.0, fs-extra@^11.1.0, fs-extra@^11.1.1, fs-extra@^11.2.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0: +fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -15506,7 +16058,7 @@ get-east-asian-width@^1.0.0: resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz" integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== -get-func-name@^2.0.1: +get-func-name@^2.0.1, get-func-name@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== @@ -15562,6 +16114,11 @@ get-port@5.1.1: resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== +get-port@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== + get-port@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz" @@ -15645,6 +16202,11 @@ get-uri@^6.0.1: debug "^4.3.4" fs-extra "^11.2.0" +getenv@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz#874f2e7544fbca53c7a4738f37de8605c3fcfc31" + integrity sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg== + giget@^1.0.0: version "1.2.3" resolved "https://registry.npmjs.org/giget/-/giget-1.2.3.tgz" @@ -15786,7 +16348,7 @@ glob@^10.0.0: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^10.3.7: +glob@^10.3.7, glob@^10.4.2: version "10.4.5" resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -16807,6 +17369,14 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^6.0.1" +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + internal-slot@^1.0.4, internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz" @@ -16852,6 +17422,11 @@ ioredis@^5.3.2: redis-parser "^3.0.0" standard-as-callback "^2.1.0" +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== + ip@^1.1.8: version "1.1.9" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" @@ -16862,7 +17437,7 @@ ip@^2.0.0: resolved "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz" integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== -ipaddr.js@1.9.1: +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== @@ -16985,6 +17560,11 @@ is-buffer@^2.0.0: resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-builtin-module@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz" @@ -18405,6 +18985,11 @@ jest@^29.6.3, jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" +jimp-compact@0.16.1: + version "0.16.1" + resolved "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz#9582aea06548a2c1e04dd148d7c3ab92075aefa3" + integrity sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww== + jiti@^1.20.0, jiti@^1.21.0: version "1.21.0" resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz" @@ -18421,6 +19006,11 @@ joi@^17.2.1, joi@^17.3.0: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" +join-component@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" + integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== + js-cookie@3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz" @@ -18461,7 +19051,7 @@ jsc-android@^250231.0.0: resolved "https://registry.npmjs.org/jsc-android/-/jsc-android-250231.0.0.tgz" integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== -jsc-safe-url@^0.2.2: +jsc-safe-url@^0.2.2, jsc-safe-url@^0.2.4: version "0.2.4" resolved "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz" integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== @@ -18923,6 +19513,74 @@ lighthouse-logger@^1.0.0: debug "^2.6.9" marky "^1.2.2" +lightningcss-darwin-arm64@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz#565bd610533941cba648a70e105987578d82f996" + integrity sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ== + +lightningcss-darwin-x64@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz#c906a267237b1c7fe08bff6c5ac032c099bc9482" + integrity sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg== + +lightningcss-freebsd-x64@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz#a7c3c4d6ee18dffeb8fa69f14f8f9267f7dc0c34" + integrity sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA== + +lightningcss-linux-arm-gnueabihf@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz#c7c16432a571ec877bf734fe500e4a43d48c2814" + integrity sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA== + +lightningcss-linux-arm64-gnu@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz#cfd9e18df1cd65131da286ddacfa3aee6862a752" + integrity sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A== + +lightningcss-linux-arm64-musl@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz#6682ff6b9165acef9a6796bd9127a8e1247bb0ed" + integrity sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg== + +lightningcss-linux-x64-gnu@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz#714221212ad184ddfe974bbb7dbe9300dfde4bc0" + integrity sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A== + +lightningcss-linux-x64-musl@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz#247958daf622a030a6dc2285afa16b7184bdf21e" + integrity sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA== + +lightningcss-win32-arm64-msvc@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz#64cfe473c264ef5dc275a4d57a516d77fcac6bc9" + integrity sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ== + +lightningcss-win32-x64-msvc@1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz#237d0dc87d9cdc9cf82536bcbc07426fa9f3f422" + integrity sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw== + +lightningcss@~1.27.0: + version "1.27.0" + resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz#d4608e63044343836dd9769f6c8b5d607867649a" + integrity sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ== + dependencies: + detect-libc "^1.0.3" + optionalDependencies: + lightningcss-darwin-arm64 "1.27.0" + lightningcss-darwin-x64 "1.27.0" + lightningcss-freebsd-x64 "1.27.0" + lightningcss-linux-arm-gnueabihf "1.27.0" + lightningcss-linux-arm64-gnu "1.27.0" + lightningcss-linux-arm64-musl "1.27.0" + lightningcss-linux-x64-gnu "1.27.0" + lightningcss-linux-x64-musl "1.27.0" + lightningcss-win32-arm64-msvc "1.27.0" + lightningcss-win32-x64-msvc "1.27.0" + lilconfig@2.1.0, lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" @@ -19229,6 +19887,13 @@ lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.21: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz" @@ -19292,6 +19957,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^2.3.6, loupe@^2.3.7: + version "2.3.7" + resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + loupe@^3.1.0, loupe@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz" @@ -19504,6 +20176,13 @@ marky@^1.2.2: resolved "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz" integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== +md5-file@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/md5-file/-/md5-file-3.2.3.tgz#f9bceb941eca2214a4c0727f5e700314e770f06f" + integrity sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw== + dependencies: + buffer-alloc "^1.1.0" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -19513,6 +20192,15 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" +md5@^2.2.1: + version "2.3.0" + resolved "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + mdast-util-directive@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz" @@ -20688,6 +21376,14 @@ micromatch@^4.0.2: braces "^3.0.3" picomatch "^2.3.1" +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -20723,6 +21419,11 @@ mime@^3.0.0: resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" @@ -21102,6 +21803,11 @@ nanoid@3.3.7, nanoid@^3.3.6, nanoid@^3.3.7: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +nanoid@3.3.8: + version "3.3.8" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + napi-wasm@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/napi-wasm/-/napi-wasm-1.1.0.tgz" @@ -21127,6 +21833,11 @@ neo-async@^2.5.0, neo-async@^2.6.2: resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +nested-error-stacks@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz#d2cc9fc5235ddb371fc44d506234339c8e4b0a4b" + integrity sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A== + netmask@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" @@ -21277,7 +21988,7 @@ node-fetch@3.3.1: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-fetch@^2, node-fetch@^2.0.0, node-fetch@^2.2.0, node-fetch@^2.6.12, node-fetch@^2.6.7: +node-fetch@^2, node-fetch@^2.0.0, node-fetch@^2.2.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -21293,7 +22004,7 @@ node-fetch@^3.3.1: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-forge@^1, node-forge@^1.3.1: +node-forge@^1, node-forge@^1.2.1, node-forge@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== @@ -21763,7 +22474,7 @@ obj-multiplex@^1.0.0: once "^1.4.0" readable-stream "^2.3.3" -object-assign@^4.0.1, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -21898,6 +22609,13 @@ once@~1.3.0: dependencies: wrappy "1" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== + dependencies: + mimic-fn "^1.0.0" + onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" @@ -21944,7 +22662,7 @@ open@^7.0.3, open@^7.4.2: is-docker "^2.0.0" is-wsl "^2.1.1" -open@^8.4.0: +open@^8.0.4, open@^8.4.0: version "8.4.2" resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== @@ -22020,6 +22738,18 @@ ora@6.3.1: strip-ansi "^7.0.1" wcwidth "^1.0.1" +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + ora@^5.4.1: version "5.4.1" resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" @@ -22428,6 +23158,13 @@ parse-path@^7.0.0: dependencies: protocols "^2.0.0" +parse-png@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz#2a42ad719fedf90f81c59ebee7ae59b280d6b338" + integrity sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ== + dependencies: + pngjs "^3.3.0" + parse-url@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz" @@ -22447,6 +23184,14 @@ parseurl@~1.3.3: resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +password-prompt@^1.0.4: + version "1.1.3" + resolved "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.3.tgz#05e539f4e7ca4d6c865d479313f10eb9db63ee5f" + integrity sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw== + dependencies: + ansi-escapes "^4.3.2" + cross-spawn "^7.0.3" + patch-package@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz" @@ -22521,7 +23266,7 @@ path-match@1.2.4: http-errors "~1.4.0" path-to-regexp "^1.0.0" -path-parse@^1.0.7: +path-parse@^1.0.5, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -22601,6 +23346,11 @@ pathe@^1.1.0, pathe@^1.1.1, pathe@^1.1.2: resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + pathval@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz" @@ -22653,11 +23403,21 @@ picocolors@^1.1.0: resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz" integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.0.7, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz#817033161def55ec9638567a2f3bbc876b3e7516" + integrity sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== + pidtree@0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" @@ -22778,6 +23538,15 @@ playwright@^1.14.0: optionalDependencies: fsevents "2.3.2" +plist@^3.0.5: + version "3.1.0" + resolved "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" + integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== + dependencies: + "@xmldom/xmldom" "^0.8.8" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + plur@^3.0.1: version "3.1.1" resolved "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz" @@ -22785,6 +23554,11 @@ plur@^3.0.1: dependencies: irregular-plurals "^2.0.0" +pngjs@^3.3.0: + version "3.4.0" + resolved "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" + integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== + pngjs@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz" @@ -22896,6 +23670,15 @@ postcss@^8.4.45: picocolors "^1.0.1" source-map-js "^1.2.0" +postcss@~8.4.32: + version "8.4.49" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.1" + source-map-js "^1.2.1" + postinstall-postinstall@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz" @@ -22943,6 +23726,11 @@ prettier@^3.2.5: resolved "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz" integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" @@ -23042,6 +23830,11 @@ process@^0.11.0, process@^0.11.10: resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" @@ -23067,6 +23860,13 @@ promise.allsettled@1.0.6: get-intrinsic "^1.1.3" iterate-value "^1.0.2" +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + promise@^8.3.0: version "8.3.0" resolved "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz" @@ -23079,7 +23879,7 @@ promisepipe@3.0.0: resolved "https://registry.npmjs.org/promisepipe/-/promisepipe-3.0.0.tgz" integrity sha512-V6TbZDJ/ZswevgkDNpGt/YqNCiZP9ASfgU+p83uJE6NrGtvSGoOcHLiDCqkMs2+yg7F5qHdLV8d0aS8O26G/KA== -prompts@^2.0.1, prompts@^2.4.0, prompts@^2.4.1, prompts@^2.4.2: +prompts@^2.0.1, prompts@^2.3.2, prompts@^2.4.0, prompts@^2.4.1, prompts@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== @@ -23238,6 +24038,11 @@ qrcode-terminal-nooctal@^0.12.1: resolved "https://registry.npmjs.org/qrcode-terminal-nooctal/-/qrcode-terminal-nooctal-0.12.1.tgz" integrity sha512-jy/kkD0iIMDjTucB+5T6KBsnirlhegDH47vHgrj5MejchSQmi/EAMM0xMFeePgV9CJkkAapNakpVUWYgHvtdKg== +qrcode-terminal@0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e" + integrity sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ== + qrcode@1.5.3: version "1.5.3" resolved "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz" @@ -23366,7 +24171,7 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -rc@1.2.8: +rc@1.2.8, rc@~1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -23435,7 +24240,7 @@ react-element-to-jsx-string@^15.0.0: is-plain-object "5.0.0" react-is "18.1.0" -react-fast-compare@^3.1.1: +react-fast-compare@^3.1.1, react-fast-compare@^3.2.0, react-fast-compare@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== @@ -23445,6 +24250,17 @@ react-freeze@^1.0.0: resolved "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz#cbbea2762b0368b05cbe407ddc9d518c57c6f3ad" integrity sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA== +react-helmet-async@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== + dependencies: + "@babel/runtime" "^7.12.5" + invariant "^2.2.4" + prop-types "^15.7.2" + react-fast-compare "^3.2.0" + shallowequal "^1.1.0" + react-helmet@^6.1.0: version "6.1.0" resolved "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz" @@ -23558,6 +24374,20 @@ react-native-get-random-values@^1.11.0: dependencies: fast-base64-decode "^1.0.0" +react-native-helmet-async@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/react-native-helmet-async/-/react-native-helmet-async-2.0.4.tgz#93f53a1ff22d6898039688a541653a2d6b6866bb" + integrity sha512-m3CkXWss6B1dd6mCMleLpzDCJJGGaHOLQsUzZv8kAASJmMfmVT4d2fx375iXKTRWT25ThBfae3dECuX5cq/8hg== + dependencies: + invariant "^2.2.4" + react-fast-compare "^3.2.2" + shallowequal "^1.1.0" + +react-native-is-edge-to-edge@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.6.tgz#69ec13f70d76e9245e275eed4140d0873a78f902" + integrity sha512-1pHnFTlBahins6UAajXUqeCOHew9l9C2C8tErnpGC3IyLJzvxD+TpYAixnCbrVS52f7+NvMttbiSI290XfwN0w== + react-native-mmkv@3.1.0, react-native-mmkv@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/react-native-mmkv/-/react-native-mmkv-3.1.0.tgz#4b2c321cf11bde2f9da32acf76e0178ecd332ccc" @@ -23571,6 +24401,11 @@ react-native-randombytes@^3.5.1: buffer "^4.9.1" sjcl "^1.0.3" +react-native-safe-area-context@4.12.0: + version "4.12.0" + resolved "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz#17868522a55bbc6757418c94a1b4abdda6b045d9" + integrity sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ== + react-native-safe-area-context@4.14.0: version "4.14.0" resolved "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.14.0.tgz#138f4b2e180cb7517c78bd5f4d4cf91325ba0b1a" @@ -23597,6 +24432,14 @@ react-native-screens@^4.3.0: react-freeze "^1.0.0" warn-once "^0.1.0" +react-native-screens@~4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.1.0.tgz#3f703a1bb4fd65fd7963e616ddc373a97809c254" + integrity sha512-tCBwe7fRMpoi/nIgZxE86N8b2SH8d5PlfGaQO8lgqlXqIyvwqm3u1HJCaA0tsacPyzhW7vVtRfQyq9e1j0S2gA== + dependencies: + react-freeze "^1.0.0" + warn-once "^0.1.0" + react-native-webview@^11.26.0: version "11.26.1" resolved "https://registry.npmjs.org/react-native-webview/-/react-native-webview-11.26.1.tgz" @@ -23605,50 +24448,6 @@ react-native-webview@^11.26.0: escape-string-regexp "2.0.0" invariant "2.2.4" -react-native@0.76.1: - version "0.76.1" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.76.1.tgz#203cd10d6ba03537cda905f9bb5b4bcd937cdfe1" - integrity sha512-z4KnbrnnAvloRs9NGnah3u6/LK3IbtNMrvByxa3ifigbMlsMY4WPRYV9lvt/hH4Mzt8bfuI+utnOxFyJTTq3lg== - dependencies: - "@jest/create-cache-key-function" "^29.6.3" - "@react-native/assets-registry" "0.76.1" - "@react-native/codegen" "0.76.1" - "@react-native/community-cli-plugin" "0.76.1" - "@react-native/gradle-plugin" "0.76.1" - "@react-native/js-polyfills" "0.76.1" - "@react-native/normalize-colors" "0.76.1" - "@react-native/virtualized-lists" "0.76.1" - abort-controller "^3.0.0" - anser "^1.4.9" - ansi-regex "^5.0.0" - babel-jest "^29.7.0" - babel-plugin-syntax-hermes-parser "^0.23.1" - base64-js "^1.5.1" - chalk "^4.0.0" - commander "^12.0.0" - event-target-shim "^5.0.1" - flow-enums-runtime "^0.0.6" - glob "^7.1.1" - invariant "^2.2.4" - jest-environment-node "^29.6.3" - jsc-android "^250231.0.0" - memoize-one "^5.0.0" - metro-runtime "^0.81.0" - metro-source-map "^0.81.0" - mkdirp "^0.5.1" - nullthrows "^1.1.1" - pretty-format "^29.7.0" - promise "^8.3.0" - react-devtools-core "^5.3.1" - react-refresh "^0.14.0" - regenerator-runtime "^0.13.2" - scheduler "0.24.0-canary-efb381bbf-20230505" - semver "^7.1.3" - stacktrace-parser "^0.1.10" - whatwg-fetch "^3.0.0" - ws "^6.2.3" - yargs "^17.6.2" - react-native@0.76.5: version "0.76.5" resolved "https://registry.npmjs.org/react-native/-/react-native-0.76.5.tgz#3ce43d3c31f46cfd98e56ef2dfc70866c04ad185" @@ -24324,6 +25123,11 @@ remark-stringify@^11.0.0: mdast-util-to-markdown "^2.0.0" unified "^11.0.0" +remove-trailing-slash@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" + integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" @@ -24344,6 +25148,15 @@ require-main-filename@^2.0.0: resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requireg@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz#437e77a5316a54c9bcdbbf5d1f755fe093089830" + integrity sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg== + dependencies: + nested-error-stacks "~2.0.1" + rc "~1.2.7" + resolve "~1.7.1" + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" @@ -24401,6 +25214,11 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== +resolve-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz#a0098daa0067cd0efa6eb525c57c8fb4a61e78f8" + integrity sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw== + resolve.exports@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz" @@ -24411,6 +25229,11 @@ resolve.exports@^2.0.0: resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== +resolve.exports@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== + resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.22.8: version "1.22.8" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" @@ -24429,6 +25252,13 @@ resolve@^2.0.0-next.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@~1.7.1: + version "1.7.1" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" + integrity sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw== + dependencies: + path-parse "^1.0.5" + responselike@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" @@ -24436,6 +25266,14 @@ responselike@^3.0.0: dependencies: lowercase-keys "^3.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" @@ -24685,6 +25523,11 @@ safe-stable-stringify@^2.1.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + saxes@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz" @@ -24706,6 +25549,16 @@ scheduler@^0.23.0, scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" +schema-utils@^4.0.1: + version "4.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz#3b669f04f71ff2dfb5aba7ce2d5a9d79b35622c0" + integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + scrypt-js@3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" @@ -24780,7 +25633,7 @@ semver@7.5.4, semver@^7.0.0, semver@^7.1.1, semver@^7.1.3, semver@^7.3.4, semver dependencies: lru-cache "^6.0.0" -semver@^7.6.0: +semver@^7.6.0, semver@~7.6.3: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -24823,6 +25676,25 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@^0.19.0: + version "0.19.1" + resolved "https://registry.npmjs.org/send/-/send-0.19.1.tgz#1c2563b2ee4fe510b806b21ec46f355005a369f9" + integrity sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + serialize-error@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz" @@ -24848,6 +25720,11 @@ serve-static@1.16.2, serve-static@^1.16.2: parseurl "~1.3.3" send "0.19.0" +server-only@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz#0f366bb6afb618c37c9255a314535dc412cd1c9e" + integrity sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" @@ -24899,7 +25776,7 @@ set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" -setimmediate@^1.0.4: +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== @@ -24934,6 +25811,11 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" @@ -25046,6 +25928,15 @@ sigstore@^2.2.0: "@sigstore/tuf" "^2.3.0" "@sigstore/verify" "^0.1.0" +simple-plist@^1.1.0: + version "1.3.1" + resolved "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" + integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== + dependencies: + bplist-creator "0.1.0" + bplist-parser "0.3.1" + plist "^3.0.5" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -25100,6 +25991,11 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" +slugify@^1.3.4, slugify@^1.6.6: + version "1.6.6" + resolved "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b" + integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" @@ -25181,7 +26077,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@~0.5.20: +source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@~0.5.20, source-map-support@~0.5.21: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -25423,6 +26319,11 @@ stream-browserify@^3.0.0: inherits "~2.0.4" readable-stream "^3.5.0" +stream-buffers@2.2.x, stream-buffers@~2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== + stream-http@^2.3.1: version "2.8.3" resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -25734,6 +26635,11 @@ strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" +structured-headers@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz#77abd9410622c6926261c09b9d16cf10592694d1" + integrity sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg== + sturdy-websocket@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/sturdy-websocket/-/sturdy-websocket-0.2.1.tgz" @@ -25760,6 +26666,19 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" +sucrase@3.35.0: + version "3.35.0" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + sucrase@^3.32.0: version "3.34.0" resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz" @@ -25773,6 +26692,16 @@ sucrase@^3.32.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +sudo-prompt@9.1.1: + version "9.1.1" + resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0" + integrity sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== + +sudo-prompt@^8.2.0: + version "8.2.5" + resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.2.5.tgz#cc5ef3769a134bb94b24a631cc09628d4d53603e" + integrity sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw== + sudo-prompt@^9.0.0: version "9.2.1" resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz" @@ -26019,7 +26948,7 @@ tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.2.0: +tar@^6.2.0, tar@^6.2.1: version "6.2.1" resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -26055,7 +26984,7 @@ temp-dir@1.0.0: resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ== -temp-dir@^2.0.0: +temp-dir@^2.0.0, temp-dir@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz" integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== @@ -26072,6 +27001,17 @@ temp@^0.8.4: dependencies: rimraf "~2.6.2" +tempy@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/tempy/-/tempy-0.7.1.tgz#5a654e6dbd1747cdd561efb112350b55cd9c1d46" + integrity sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg== + dependencies: + del "^6.0.0" + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + tempy@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz" @@ -26093,7 +27033,7 @@ tempy@^3.1.0: type-fest "^2.12.2" unique-string "^3.0.0" -terminal-link@^2.0.0: +terminal-link@^2.0.0, terminal-link@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== @@ -26210,6 +27150,11 @@ tinyrainbow@^1.2.0: resolved "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz" integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== +tinyspy@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz" + integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== + tinyspy@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz" @@ -26603,6 +27548,11 @@ type-detect@4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-detect@^4.0.0, type-detect@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== + type-fest@^0.16.0: version "0.16.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz" @@ -26796,7 +27746,12 @@ typescript@^5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== -ua-parser-js@^1.0.39: +typescript@^5.3.3: + version "5.7.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== + +ua-parser-js@^1.0.35, ua-parser-js@^1.0.39: version "1.0.39" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== @@ -26877,6 +27832,11 @@ undici@^6.11.1: resolved "https://registry.npmjs.org/undici/-/undici-6.20.0.tgz" integrity sha512-AITZfPuxubm31Sx0vr8bteSalEbs9wQb/BOBi9FPlD9Qpd6HxZ4Q0+hI742jBhkPb4RT2v5MQzaW5VhRVyj+9A== +undici@^6.18.2: + version "6.21.0" + resolved "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz#4b3d3afaef984e07b48e7620c34ed8a285ed4cd4" + integrity sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw== + unenv@^1.7.4: version "1.8.0" resolved "https://registry.npmjs.org/unenv/-/unenv-1.8.0.tgz" @@ -27006,7 +27966,7 @@ unique-slug@^4.0.0: dependencies: imurmurhash "^0.1.4" -unique-string@^2.0.0: +unique-string@^2.0.0, unique-string@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== @@ -27109,6 +28069,11 @@ universalify@^0.2.0: resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== +universalify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" + integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== + universalify@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" @@ -27299,7 +28264,12 @@ uuid@^11.0.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.2.tgz#a8d68ba7347d051e7ea716cc8dcbbab634d66875" integrity sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ== -uuid@^8.3.2: +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + +uuid@^8.0.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -27694,7 +28664,7 @@ web-streams-polyfill@^3.0.3: resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== -web-streams-polyfill@^3.1.1, web-streams-polyfill@^3.2.1: +web-streams-polyfill@^3.1.1, web-streams-polyfill@^3.2.1, web-streams-polyfill@^3.3.2: version "3.3.3" resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== @@ -27735,6 +28705,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" @@ -27774,6 +28749,15 @@ whatwg-mimetype@^4.0.0: resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== +whatwg-url-without-unicode@8.0.0-3: + version "8.0.0-3" + resolved "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" + integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== + dependencies: + buffer "^5.4.3" + punycode "^2.1.1" + webidl-conversions "^5.0.0" + whatwg-url@^14.0.0: version "14.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz" @@ -27911,6 +28895,11 @@ windows-release@^5.0.1: dependencies: execa "^5.1.1" +wonka@^6.3.2: + version "6.3.4" + resolved "https://registry.npmjs.org/wonka/-/wonka-6.3.4.tgz#76eb9316e3d67d7febf4945202b5bdb2db534594" + integrity sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg== + word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" @@ -28045,7 +29034,7 @@ ws@^7.5.1: resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.18.0, ws@^8.2.3: +ws@^8.12.1, ws@^8.18.0, ws@^8.2.3: version "8.18.0" resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== @@ -28055,6 +29044,14 @@ ws@~8.11.0: resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz" integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== +xcode@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c" + integrity sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA== + dependencies: + simple-plist "^1.1.0" + uuid "^7.0.3" + xdg-app-paths@5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/xdg-app-paths/-/xdg-app-paths-5.1.0.tgz" @@ -28079,11 +29076,34 @@ xml-name-validator@^5.0.0: resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz" integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== +xml2js@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz#07afc447a97d2bd6507a1f76eeadddb09f7a8282" + integrity sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + xml@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== +xmlbuilder@^14.0.0: + version "14.0.0" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-14.0.0.tgz#876b5aec4f05ffd5feb97b0a871c855d16fbeb8c" + integrity sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg== + +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" From 6a594b769889d5cb6ccce059982ebf0c4f05cde2 Mon Sep 17 00:00:00 2001 From: Adam Egyed <5456061+adamegyed@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:47:11 -0800 Subject: [PATCH 31/45] feat: add mekong devnet support (#1239) --- account-kit/infra/src/chains.ts | 18 ++++++++++++++++++ account-kit/infra/src/index.ts | 1 + 2 files changed, 19 insertions(+) diff --git a/account-kit/infra/src/chains.ts b/account-kit/infra/src/chains.ts index 0b1b24d4f2..a67f006df6 100644 --- a/account-kit/infra/src/chains.ts +++ b/account-kit/infra/src/chains.ts @@ -478,3 +478,21 @@ export const arbitrumNova: Chain = { ...vabn.rpcUrls, }, }; + +export const mekong: Chain = defineChain({ + id: 7078815900, + name: "Mekong Pectra Devnet", + nativeCurrency: { name: "eth", symbol: "eth", decimals: 18 }, + rpcUrls: { + default: { + http: ["https://rpc.mekong.ethpandaops.io"], + }, + }, + blockExplorers: { + default: { + name: "Block Explorer", + url: "https://explorer.mekong.ethpandaops.io", + }, + }, + testnet: true, +}); diff --git a/account-kit/infra/src/index.ts b/account-kit/infra/src/index.ts index 814451230d..d0f4b43ae8 100644 --- a/account-kit/infra/src/index.ts +++ b/account-kit/infra/src/index.ts @@ -39,6 +39,7 @@ export { unichainSepolia, inkMainnet, inkSepolia, + mekong, } from "./chains.js"; export type * from "./client/decorators/alchemyEnhancedApis.js"; export { alchemyEnhancedApiActions } from "./client/decorators/alchemyEnhancedApis.js"; From 5c817a7887d74a9eabae293a24025c977dc2f575 Mon Sep 17 00:00:00 2001 From: Rob <30500864+RobChangCA@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:10:57 -0500 Subject: [PATCH 32/45] docs: update export private key (#1197) --- site/pages/signer/export-private-key.mdx | 30 +++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/site/pages/signer/export-private-key.mdx b/site/pages/signer/export-private-key.mdx index d056766a4c..4f6495a80a 100644 --- a/site/pages/signer/export-private-key.mdx +++ b/site/pages/signer/export-private-key.mdx @@ -8,7 +8,35 @@ description: Learn how to enable a user to export their private key with the Alc The Alchemy Signer allows you to export a user's private key, allowing them a right to exit at any time. It is considered a best practice to allow your users to export their private key, as it gives them full control over their account. The private key export method does not rely on Alchemy's infrastructure, so even if Alchemy is down, a user can still export their private key. -## Usage +## Using [useExportAccount](/reference/account-kit/react/hooks/useExportAccount) + +A hook use to export the private key for an account. It returns the mutation functions to kick off the export process, as well as a component to render the account recovery details in an iframe. + +### Import + +```ts +import { useExportAccount } from "@account-kit/react"; +``` + +### Usage + +```ts +import { useExportAccount } from "@account-kit/react"; + +const { + exportAccount, + isExported, + isExporting, + error, + ExportAccountComponent, +} = useExportAccount({ + params: { + iframeContainerId: "my-iframe-container", + }, +}); +``` + +## Using the signer To add export private key functionality to your app, you can use the `exportPrivateKey` method on the signer. From fe962a015097eb47f023233c59e086db73be1ba5 Mon Sep 17 00:00:00 2001 From: Rob <30500864+RobChangCA@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:30:54 -0500 Subject: [PATCH 33/45] docs: tailwind tip (#1198) --- site/pages/react/react-hooks.mdx | 4 ++++ site/pages/react/ui-components.mdx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/site/pages/react/react-hooks.mdx b/site/pages/react/react-hooks.mdx index 1ad9099067..998c446116 100644 --- a/site/pages/react/react-hooks.mdx +++ b/site/pages/react/react-hooks.mdx @@ -7,6 +7,10 @@ description: How to embed our authentication into your customized UI If you don't want to use our pre-built UI components, you can build your own custom UI using the [`useAuthenticate`](/reference/account-kit/react/hooks/useAuthenticate) hook. +:::tip +Tailwind CSS is a required dependency for using Alchemy Account Kit UI components. However, Alchemy Account Kit hooks function independently and do not require Tailwind. +::: + ### Email authentication ```tsx twoslash diff --git a/site/pages/react/ui-components.mdx b/site/pages/react/ui-components.mdx index 59d13f9c7a..0e1bb0db22 100644 --- a/site/pages/react/ui-components.mdx +++ b/site/pages/react/ui-components.mdx @@ -11,6 +11,10 @@ Account Kit allows you to use pre-built, [highly customizable](https://demo.alch - customize [authentication method ui](#customize-authentication-ui) - customize [theme](/react/customization/theme) +:::tip +Tailwind CSS is a required dependency for using Alchemy Account Kit UI components. However, Alchemy Account Kit hooks function independently and do not require Tailwind. +::: + ## Modal auth Assuming your application has been [set up](/react/quickstart), using UI components is the easiest way to authenticate users. All you have to do is leverage the [`useAuthModal`](/reference/account-kit/react/hooks/useAuthModal) hook and provide users a CTA to open the modal. From 446070a204496e56c968311530f7a9644b15824a Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:19:18 -0500 Subject: [PATCH 34/45] fix: review fixes, improve getNonce implementation to be more friendly --- aa-sdk/core/src/errors/client.ts | 9 +++----- .../src/ma-v2/account/semiModularAccountV2.ts | 22 +++++++++---------- .../src/ma-v2/client/client.test.ts | 3 +-- .../smart-contracts/src/ma-v2/index.ts | 0 .../single-signer-validation/module.ts | 4 ---- .../single-signer-validation/signer.ts | 2 +- .../InvalidNonceKeyError/constructor.mdx | 5 ----- 7 files changed, 16 insertions(+), 29 deletions(-) delete mode 100644 account-kit/smart-contracts/src/ma-v2/index.ts diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index f90784ca6d..c24cd64a2d 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -71,7 +71,7 @@ export class InvalidEntityIdError extends BaseError { } /** - * Error class denoting that the provided entity id is invalid because it's too large. + * Error class denoting that the nonce key is invalid because its too large. */ export class InvalidNonceKeyError extends BaseError { override name = "InvalidNonceKeyError"; @@ -80,12 +80,9 @@ export class InvalidNonceKeyError extends BaseError { * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. * * @param {bigint} nonceKey the invalid nonceKey used - * @param {bigint} nonceKeySuffix the expected nonceKeySuffix that must be contained in nonceKey */ - constructor(nonceKey: bigint, nonceKeySuffix: bigint) { - super( - `Nonce key used is ${nonceKey}, but must contain the nonce key suffix of: ${nonceKeySuffix}` - ); + constructor(nonceKey: bigint) { + super(`Nonce key is ${nonceKey} but has to be less than 2**152`); } } diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index 0080734a40..c0e3c8ce5c 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -16,12 +16,11 @@ import { encodeFunctionData, getContract, maxUint32, - toHex, + maxUint152, type Address, type Chain, type Hex, type Transport, - hexToBigInt, zeroAddress, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; @@ -118,7 +117,7 @@ export async function createSMAV2Account( entityId = 0, } = config; - if (entityId >= Number(maxUint32)) { + if (entityId > Number(maxUint32)) { throw new InvalidEntityIdError(entityId); } @@ -165,12 +164,8 @@ export async function createSMAV2Account( // TODO: add deferred action flag const getAccountNonce = async (nonceKey?: bigint): Promise => { - const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ - isGlobalValidation ? "01" : "00" - }`; - - if (nonceKey && toHex(nonceKey, { size: 5 }) !== nonceKeySuffix) { - throw new InvalidNonceKeyError(nonceKey, hexToBigInt(nonceKeySuffix)); + if (nonceKey && nonceKey > maxUint152) { + throw new InvalidNonceKeyError(nonceKey); } const entryPointContract = getContract({ @@ -179,9 +174,14 @@ export async function createSMAV2Account( client, }); + const fullNonceKey: bigint = + (nonceKey ? nonceKey << 40n : 0n) + + BigInt(entityId << 8) + + (isGlobalValidation ? 1n : 0n); + return entryPointContract.read.getNonce([ - accountAddress_, - nonceKey ?? hexToBigInt(nonceKeySuffix), + baseAccount.address, + fullNonceKey, ]) as Promise; }; diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index dc0b4d5894..ab4a0dfd52 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -67,8 +67,7 @@ describe("MA v2 Tests", async () => { }, }); - const txnHash1 = provider.waitForUserOperationTransaction(result); - await expect(txnHash1).resolves.not.toThrowError(); + await provider.waitForUserOperationTransaction(result); await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount diff --git a/account-kit/smart-contracts/src/ma-v2/index.ts b/account-kit/smart-contracts/src/ma-v2/index.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts index 180582d19f..135dde7c2f 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/module.ts @@ -1,10 +1,6 @@ import { encodeAbiParameters, type Address, type Hex } from "viem"; -import { singleSignerValidationModuleAbi } from "./abis/singleSignerValidationModuleAbi.js"; - -// Todo: some unified type for ERC-6900 v0.8 modules. I couldn't figure out how to parameterize the class itself over the abi type parameters for onInstall and onUninstall. export const SingleSignerValidationModule = { - abi: singleSignerValidationModuleAbi, encodeOnInstallData: (args: { entityId: number; signer: Address }): Hex => { const { entityId, signer } = args; diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts index fd1dcd8a33..6c64e5e5a7 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts @@ -41,7 +41,7 @@ export const singleSignerMessageSigner = ( }); }, - signUserOperationHash: (uoHash: `0x${string}`): Promise<`0x${string}`> => { + signUserOperationHash: (uoHash: Hex): Promise => { return signer.signMessage({ raw: uoHash }).then((signature: Hex) => packSignature({ // orderedHookData: [], diff --git a/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx b/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx index 6919c0032c..853c51e8ef 100644 --- a/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx +++ b/site/pages/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor.mdx @@ -23,8 +23,3 @@ import { InvalidNonceKeyError } from "@aa-sdk/core"; `bigint` the invalid nonceKey used - -### nonceKeySuffix - -`bigint` -the expected nonceKeySuffix that must be contained in nonceKey From f4629d62125e92ba326294b8c72e3007f212b41f Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Thu, 19 Dec 2024 02:04:14 -0500 Subject: [PATCH 35/45] chore: update sma type to include entity properties --- .../src/ma-v2/account/semiModularAccountV2.ts | 51 +++++-------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index c0e3c8ce5c..2bdc91af30 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -30,44 +30,25 @@ import { singleSignerMessageSigner } from "../modules/single-signer-validation/s import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; import { modularAccountAbi } from "../abis/modularAccountAbi.js"; import { serializeModuleEntity } from "../actions/common/utils.js"; +import type { + ExecutionDataView, + ValidationDataParams, + ValidationDataView, +} from "../../../dist/esm/src/ma-v2/account/semiModularAccountV2.js"; const executeUserOpSelector: Hex = "0x8DD7712F"; export const DEFAULT_OWNER_ENTITY_ID = 0; -export type CalldataEncoder = { - encodeCallData: (callData: Hex) => Promise; -}; - -export type ExecutionDataView = { - module: Address; - skipRuntimeValidation: boolean; - allowGlobalValidation: boolean; - executionHooks: readonly Hex[]; -}; - -export type ValidationDataView = { - validationHooks: readonly Hex[]; - executionHooks: readonly Hex[]; - selectors: readonly Hex[]; - validationFlags: number; -}; - -export type ValidationDataParams = { - validationModuleAddress?: Address; - entityId?: number; +export type SignerEntity = { + isGlobalValidation: boolean; + entityId: number; }; export type SMAV2Account< - TSigner extends SmartAccountSigner = SmartAccountSigner, - TCalldataEncoder extends CalldataEncoder = CalldataEncoder + TSigner extends SmartAccountSigner = SmartAccountSigner > = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & - TCalldataEncoder & { - getExecutionData: (selector: Hex) => Promise; - getValidationData: ( - args: ValidationDataParams - ) => Promise; - }; + SignerEntity; export type CreateSMAV2AccountParams< TTransport extends Transport = Transport, @@ -83,10 +64,7 @@ export type CreateSMAV2AccountParams< initialOwner?: Address; entryPoint?: EntryPointDef<"0.7.0", Chain>; } & ( - | { - isGlobalValidation: boolean; - entityId: number; - } + | SignerEntity | { isGlobalValidation?: never; entityId?: never; @@ -260,10 +238,7 @@ export async function createSMAV2Account( ...baseAccount, getAccountNonce, getSigner: () => signer, - getExecutionData, - getValidationData, - encodeExecute, - encodeBatchExecute, - encodeCallData, + isGlobalValidation, + entityId, }; } From 79e115720f08c8d170303366ac1e7704d1432040 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 36/45] feat: add install validation, add tests for session key --- .../install-validation/installValidation.ts | 160 ++++++++++++++++ .../src/ma-v2/client/client.test.ts | 174 +----------------- 2 files changed, 170 insertions(+), 164 deletions(-) create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts new file mode 100644 index 0000000000..f2d77d386c --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -0,0 +1,160 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + EntityIdOverrideError, + type GetAccountParameter, + type GetEntryPointFromAccount, + type SendUserOperationResult, + type SmartContractAccount, + type SmartAccountClient, + type UserOperationOverridesParameter, +} from "@aa-sdk/core"; +import { + type Address, + type Hex, + type Chain, + type Transport, + encodeFunctionData, + concatHex, +} from "viem"; + +import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; +import type { HookConfig, ValidationConfig } from "../common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, + serializeModuleEntity, +} from "../common/utils.js"; + +export type InstallValidationParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type UninstallValidationParams< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; +} & UserOperationOverridesParameter> & + GetAccountParameter; + +export type InstallValidationActions< + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined +> = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export const installValidationActions: < + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends SmartContractAccount = SmartContractAccount +>( + client: SmartAccountClient +) => InstallValidationActions = (client) => ({ + installValidation: async ({ + validationConfig, + selectors, + installData, + hooks, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "installValidation", + client + ); + } + + if (validationConfig.entityId === 0) { + throw new EntityIdOverrideError(); + } + + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, + + uninstallValidation: async ({ + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "uninstallValidation", + client + ); + } + + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index ab4a0dfd52..e8eeebd73b 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -15,14 +15,9 @@ import { import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { - getDefaultPaymasterGuardModuleAddress, - getDefaultSingleSignerValidationModuleAddress, -} from "../modules/utils.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; +import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; -import { PaymasterGuardModule } from "../modules/paymaster-guard-module/module.js"; -import { paymaster070 } from "~test/paymaster/paymaster070.js"; -import { HookType } from "../actions/common/types.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -75,7 +70,9 @@ describe("MA v2 Tests", async () => { }); it("adds a session key with no permissions", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); await setBalance(client, { address: provider.getAddress(), @@ -131,7 +128,9 @@ describe("MA v2 Tests", async () => { }); it("uninstalls a session key", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); await setBalance(client, { address: provider.getAddress(), @@ -194,168 +193,15 @@ describe("MA v2 Tests", async () => { ).rejects.toThrowError(); }); - it("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { - let provider = await givenConnectedProvider({ signer, usePaymaster: true }); - - await setBalance(client, { - address: provider.getAddress(), - value: parseEther("2"), - }); - - const paymaster = paymaster070.getPaymasterStubData(); - - const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ - entityId: 0, - paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE - }); - - const installResult = await provider.installValidation({ - validationConfig: { - moduleAddress: zeroAddress, - entityId: 0, - isGlobal: true, - isSignatureValidation: true, - isUserOpValidation: true, - }, - selectors: [], - installData: "0x", - hooks: [ - { - hookConfig: { - address: getDefaultPaymasterGuardModuleAddress(provider.chain), - entityId: 0, // uint32 - hookType: HookType.VALIDATION, - hasPreHooks: true, - hasPostHooks: true, - }, - initData: hookInstallData, - }, - ], - }); - - // verify hook installtion succeeded - await expect( - provider.waitForUserOperationTransaction(installResult) - ).resolves.not.toThrowError(); - - // happy path: with correct paymaster - const result = await provider.sendUserOperation({ - uo: { - target: target, - value: sendAmount, - data: "0x", - }, - }); - - // verify if correct paymaster is used - const txnHash1 = provider.waitForUserOperationTransaction(result); - await expect(txnHash1).resolves.not.toThrowError(); - - const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ - entityId: 0, - }); - - const uninstallResult = await provider.uninstallValidation({ - moduleAddress: zeroAddress, - entityId: 0, - uninstallData: "0x", - hookUninstallDatas: [hookUninstallData], - }); - - // verify uninstall - await expect( - provider.waitForUserOperationTransaction(uninstallResult) - ).resolves.not.toThrowError(); - }); - - it("installs paymaster guard module, verifies use of invalid paymaster, then uninstalls module", async () => { - let provider = await givenConnectedProvider({ signer, usePaymaster: true }); - - await setBalance(client, { - address: provider.getAddress(), - value: parseEther("2"), - }); - - const paymaster = paymaster070.getPaymasterStubData(); - - const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ - entityId: 0, - paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE - }); - - const installResult = await provider.installValidation({ - validationConfig: { - moduleAddress: zeroAddress, - entityId: 0, - isGlobal: true, - isSignatureValidation: true, - isUserOpValidation: true, - }, - selectors: [], - installData: "0x", - hooks: [ - { - hookConfig: { - address: getDefaultPaymasterGuardModuleAddress(provider.chain), - entityId: 0, // uint32 - hookType: HookType.VALIDATION, - hasPreHooks: true, - hasPostHooks: true, - }, - initData: hookInstallData, - }, - ], - }); - - // verify hook installtion succeeded - await expect( - provider.waitForUserOperationTransaction(installResult) - ).resolves.not.toThrowError(); - - // sad path: with wrong paymaster - let providerNoPaymaster = await givenConnectedProvider({ - signer, - usePaymaster: false, - }); - - // TO DO: verify if correct paymaster is used - await expect( - providerNoPaymaster.sendUserOperation({ - uo: { - target: target, - value: sendAmount, - data: "0x", - }, - }) - ).rejects.toThrowError(); - - const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ - entityId: 0, - }); - - console.log("paymaster" in paymaster ? paymaster.paymaster : "0x0"); - const uninstallResult = await provider.uninstallValidation({ - moduleAddress: zeroAddress, - entityId: 0, - uninstallData: "0x", - hookUninstallDatas: [hookUninstallData], - }); - - // verify uninstall - await expect( - provider.waitForUserOperationTransaction(uninstallResult) - ).resolves.not.toThrowError(); - }); - const givenConnectedProvider = async ({ signer, accountAddress, - usePaymaster = false, + usePaymaster, }: { signer: SmartAccountSigner; accountAddress?: `0x${string}`; usePaymaster?: boolean; - }): Promise => + }) => createSMAV2AccountClient({ chain: instance.chain, signer, From 3136edccc4ccbe35ead556bd6845f576cabc4349 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:58:00 -0500 Subject: [PATCH 37/45] fix: review fixes --- account-kit/smart-contracts/src/ma-v2/client/client.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index e8eeebd73b..e19d9089a8 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -64,7 +64,7 @@ describe("MA v2 Tests", async () => { await provider.waitForUserOperationTransaction(result); - await expect(await getTargetBalance()).toEqual( + await expect(getTargetBalance()).resolves.toEqual( startingAddressBalance + sendAmount ); }); @@ -122,7 +122,7 @@ describe("MA v2 Tests", async () => { txnHash = sessionKeyClient.waitForUserOperationTransaction(result); await expect(txnHash).resolves.not.toThrowError(); - await expect(await getTargetBalance()).toEqual( + await expect(getTargetBalance()).resolves.toEqual( startingAddressBalance + sendAmount ); }); From 448e3829d8e5ec9e9031de9e445ef5e6bd5d95d0 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 38/45] feat: add view functions and user op calldata encodings --- .../src/ma-v2/account/semiModularAccountV2.ts | 137 +++++++++++------- .../install-validation/installValidation.ts | 111 +++++++------- .../src/ma-v2/client/client.test.ts | 19 +-- .../src/ma-v2/client/client.ts | 27 ++-- .../smart-contracts/src/ma-v2/utils.ts | 2 + 5 files changed, 159 insertions(+), 137 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index 2bdc91af30..6a7c45f2b3 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -9,6 +9,8 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, + InvalidEntityIdError, + InvalidNonceKeyError, getAccountAddress, } from "@aa-sdk/core"; import { @@ -17,38 +19,62 @@ import { getContract, maxUint32, maxUint152, + zeroAddress, type Address, type Chain, type Hex, type Transport, - zeroAddress, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; -import { getDefaultMAV2FactoryAddress } from "../utils.js"; -import { standardExecutor } from "../../msca/account/standardExecutor.js"; +import { + getDefaultMAV2FactoryAddress, + DEFAULT_OWNER_ENTITY_ID, +} from "../utils.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; -import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; import { modularAccountAbi } from "../abis/modularAccountAbi.js"; import { serializeModuleEntity } from "../actions/common/utils.js"; -import type { - ExecutionDataView, - ValidationDataParams, - ValidationDataView, -} from "../../../dist/esm/src/ma-v2/account/semiModularAccountV2.js"; const executeUserOpSelector: Hex = "0x8DD7712F"; -export const DEFAULT_OWNER_ENTITY_ID = 0; - export type SignerEntity = { isGlobalValidation: boolean; entityId: number; }; +export type ExecutionDataView = { + module: Address; + skipRuntimeValidation: boolean; + allowGlobalValidation: boolean; + executionHooks: readonly Hex[]; +}; + +export type ValidationDataView = { + validationHooks: readonly Hex[]; + executionHooks: readonly Hex[]; + selectors: readonly Hex[]; + validationFlags: number; +}; + +export type ValidationDataParams = + | { + validationModuleAddress: Address; + entityId?: never; + } + | { + validationModuleAddress?: never; + entityId: number; + }; + export type SMAV2Account< TSigner extends SmartAccountSigner = SmartAccountSigner > = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & - SignerEntity; + SignerEntity & { + getExecutionData: (selector: Hex) => Promise; + getValidationData: ( + args: ValidationDataParams + ) => Promise; + encodeCallData: (callData: Hex) => Promise; + }; export type CreateSMAV2AccountParams< TTransport extends Transport = Transport, @@ -92,7 +118,7 @@ export async function createSMAV2Account( accountAddress, entryPoint = getEntryPoint(chain, { version: "0.7.0" }), isGlobalValidation = true, - entityId = 0, + entityId = DEFAULT_OWNER_ENTITY_ID, } = config; if (entityId > Number(maxUint32)) { @@ -122,14 +148,43 @@ export async function createSMAV2Account( ]); }; + const encodeExecute: (tx: AccountOp) => Promise = async ({ + target, + data, + value, + }) => + await encodeCallData( + encodeFunctionData({ + abi: modularAccountAbi, + functionName: "execute", + args: [target, value ?? 0n, data], + }) + ); + + const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => + await encodeCallData( + encodeFunctionData({ + abi: modularAccountAbi, + functionName: "executeBatch", + args: [ + txs.map((tx) => ({ + target: tx.target, + data: tx.data, + value: tx.value ?? 0n, + })), + ], + }) + ); + const baseAccount = await toSmartContractAccount({ transport, chain, entryPoint, accountAddress, source: `SMAV2Account`, + encodeExecute, + encodeBatchExecute, getAccountInitCode, - ...standardExecutor, ...singleSignerMessageSigner(signer), }); @@ -171,7 +226,12 @@ export async function createSMAV2Account( const getExecutionData = async (selector: Hex) => { if (!(await baseAccount.isAccountDeployed())) { - return {} as ExecutionDataView; + return { + module: zeroAddress, + skipRuntimeValidation: false, + allowGlobalValidation: false, + executionHooks: [], + }; } return await accountContract.read.getExecutionData([selector]); @@ -179,7 +239,12 @@ export async function createSMAV2Account( const getValidationData = async (args: ValidationDataParams) => { if (!(await baseAccount.isAccountDeployed())) { - return {} as ValidationDataView; + return { + validationHooks: [], + executionHooks: [], + selectors: [], + validationFlags: 0, + }; } const { validationModuleAddress, entityId } = args; @@ -196,49 +261,19 @@ export async function createSMAV2Account( entityId: Number(entityId), }); - const numHooks = validationData?.executionHooks?.length ?? 0; - - return numHooks ? concatHex([executeUserOpSelector, callData]) : callData; - }; - - const encodeExecute: (tx: AccountOp) => Promise = async ({ - target, - data, - value, - }) => { - let callData = encodeFunctionData({ - abi: modularAccountAbi, - functionName: "execute", - args: [target, value ?? 0n, data], - }); - - callData = (await baseAccount.isAccountDeployed()) - ? await encodeCallData(callData) + return validationData.executionHooks.length + ? concatHex([executeUserOpSelector, callData]) : callData; - - return callData; }; - const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => - encodeCallData( - encodeFunctionData({ - abi: modularAccountAbi, - functionName: "executeBatch", - args: [ - txs.map((tx) => ({ - target: tx.target, - data: tx.data, - value: tx.value ?? 0n, - })), - ], - }) - ); - return { ...baseAccount, getAccountNonce, getSigner: () => signer, isGlobalValidation, entityId, + getExecutionData, + getValidationData, + encodeCallData, }; } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts index f2d77d386c..7d9edf589c 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -3,21 +3,12 @@ import { IncompatibleClientError, isSmartAccountClient, EntityIdOverrideError, - type GetAccountParameter, type GetEntryPointFromAccount, type SendUserOperationResult, - type SmartContractAccount, - type SmartAccountClient, type UserOperationOverridesParameter, + type SmartAccountSigner, } from "@aa-sdk/core"; -import { - type Address, - type Hex, - type Chain, - type Transport, - encodeFunctionData, - concatHex, -} from "viem"; +import { type Address, type Hex, encodeFunctionData, concatHex } from "viem"; import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; import type { HookConfig, ValidationConfig } from "../common/types.js"; @@ -27,10 +18,12 @@ import { serializeModuleEntity, } from "../common/utils.js"; +import { type SMAV2AccountClient } from "../../client/client.js"; +import { type SMAV2Account } from "../../account/semiModularAccountV2.js"; +import { DEFAULT_OWNER_ENTITY_ID } from "../../utils.js"; + export type InstallValidationParams< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined + TSigner extends SmartAccountSigner = SmartAccountSigner > = { validationConfig: ValidationConfig; selectors: Hex[]; @@ -39,41 +32,39 @@ export type InstallValidationParams< hookConfig: HookConfig; initData: Hex; }[]; -} & UserOperationOverridesParameter> & - GetAccountParameter; + account?: SMAV2Account | undefined; +} & UserOperationOverridesParameter< + GetEntryPointFromAccount> +>; export type UninstallValidationParams< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined + TSigner extends SmartAccountSigner = SmartAccountSigner > = { moduleAddress: Address; entityId: number; uninstallData: Hex; hookUninstallDatas: Hex[]; -} & UserOperationOverridesParameter> & - GetAccountParameter; + account?: SMAV2Account | undefined; +} & UserOperationOverridesParameter< + GetEntryPointFromAccount> +>; export type InstallValidationActions< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined + TSigner extends SmartAccountSigner = SmartAccountSigner > = { installValidation: ( - args: InstallValidationParams + args: InstallValidationParams ) => Promise; uninstallValidation: ( - args: UninstallValidationParams + args: UninstallValidationParams ) => Promise; }; export const installValidationActions: < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends SmartContractAccount = SmartContractAccount + TSigner extends SmartAccountSigner = SmartAccountSigner >( - client: SmartAccountClient -) => InstallValidationActions = (client) => ({ + client: SMAV2AccountClient +) => InstallValidationActions = (client) => ({ installValidation: async ({ validationConfig, selectors, @@ -94,22 +85,26 @@ export const installValidationActions: < ); } - if (validationConfig.entityId === 0) { + if (validationConfig.entityId === DEFAULT_OWNER_ENTITY_ID) { throw new EntityIdOverrideError(); } - const callData = encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }); + const { encodeCallData } = account; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); return client.sendUserOperation({ uo: callData, @@ -138,18 +133,22 @@ export const installValidationActions: < ); } - const callData = encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }); + const { encodeCallData } = account; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); return client.sendUserOperation({ uo: callData, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index e19d9089a8..2d65223a3b 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,23 +1,12 @@ -import { custom, parseEther, publicActions, zeroAddress } from "viem"; - -import { - LocalAccountSigner, - type SmartAccountSigner, - type SmartAccountClient, - erc7677Middleware, -} from "@aa-sdk/core"; - -import { - createSMAV2AccountClient, - type InstallValidationActions, -} from "./client.js"; - +import { custom, parseEther, publicActions } from "viem"; +import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; +import { createSMAV2AccountClient, type SMAV2AccountClient } from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { installValidationActions } from "../actions/install-validation/installValidation.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index ac7eac885c..23f7b309c1 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -9,14 +9,14 @@ import { type SendUserOperationResult, } from "@aa-sdk/core"; import { + concatHex, + encodeFunctionData, + zeroAddress, + type Address, type Chain, - type CustomTransport, - type Transport, type Hex, - type Address, - zeroAddress, + type Transport, } from "viem"; -import { concatHex, encodeFunctionData } from "viem"; import { createSMAV2Account, @@ -24,15 +24,17 @@ import { type CreateSMAV2AccountParams, type SMAV2Account, } from "../account/semiModularAccountV2.js"; - -import type { HookConfig, ValidationConfig } from "../actions/common/types.js"; +import type { semiModularAccountBytecodeAbi } from "../abis/semiModularAccountBytecodeAbi.js"; +import type { ValidationConfig, HookConfig } from "../actions/common/types.js"; import { serializeValidationConfig, serializeHookConfig, serializeModuleEntity, } from "../actions/common/utils.js"; -import { semiModularAccountBytecodeAbi } from "../abis/semiModularAccountBytecodeAbi.js"; +export type SMAV2AccountClient< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = SmartAccountClient>; export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, @@ -76,10 +78,7 @@ export function createSMAV2AccountClient< TCalldataEncoder extends CalldataEncoder = CalldataEncoder >( args: CreateSMAV2AccountClientParams -): Promise< - SmartAccountClient & - InstallValidationActions ->; +): Promise>; /** * Creates a MAv2 account client using the provided configuration parameters. @@ -112,9 +111,7 @@ export function createSMAV2AccountClient< */ export async function createSMAV2AccountClient({ ...config -}: CreateSMAV2AccountClientParams): Promise< - SmartAccountClient & InstallValidationActions -> { +}: CreateSMAV2AccountClientParams): Promise { const maV2Account = await createSMAV2Account({ ...config, }); diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index 241f89885b..73e241b70b 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -12,6 +12,8 @@ import { sepolia, } from "@account-kit/infra"; +export const DEFAULT_OWNER_ENTITY_ID = 0; + export type PackSignatureParams = { // orderedHookData: HookData[]; validationSignature: Hex; From 0d57783e540ff3d42abc5dbaa66adbcee5148faa Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:17:46 -0500 Subject: [PATCH 39/45] feat: add install validation, add tests for session key --- aa-sdk/core/src/errors/client.ts | 16 ++ .../src/ma-v2/actions/common/types.ts | 4 +- .../src/ma-v2/actions/common/utils.ts | 10 +- .../install-validation/installValidation.ts | 145 +++++++++--------- .../src/ma-v2/client/client.test.ts | 63 +++++++- 5 files changed, 152 insertions(+), 86 deletions(-) diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index c24cd64a2d..7194f49d97 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -101,3 +101,19 @@ export class EntityIdOverrideError extends BaseError { ); } } + +/** + * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. + */ +export class EntityIdOverrideError extends BaseError { + override name = "InvalidNonceKeyError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. + */ + constructor() { + super( + `Installing entityId of 0 overrides the owner's entity id in the account` + ); + } +} diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts index 14f5739e6e..1a133ae0bc 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts @@ -1,12 +1,12 @@ import type { Address, Hex } from "viem"; export type ModuleEntity = { - moduleAddress: Address; + address: Address; entityId: number; }; export type ValidationConfig = { - moduleAddress: Address; + address: Address; entityId: number; // uint32 isGlobal: boolean; isSignatureValidation: boolean; diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts index 8bd366ab76..2d79719bce 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -1,6 +1,5 @@ import { type Hex, toHex, concatHex } from "viem"; import type { ValidationConfig, HookConfig, ModuleEntity } from "./types"; -import { HookType } from "./types.js"; export function serializeValidationConfig(config: ValidationConfig): Hex { const isUserOpValidationBit = config.isUserOpValidation ? 1 : 0; @@ -15,18 +14,17 @@ export function serializeValidationConfig(config: ValidationConfig): Hex { } export function serializeHookConfig(config: HookConfig): Hex { - const hookTypeBit = config.hookType === HookType.VALIDATION ? 1 : 0; - const hasPostHooksBit = config.hasPostHooks ? 2 : 0; - const hasPreHooksBit = config.hasPreHooks ? 4 : 0; return concatHex([ config.address, toHex(config.entityId, { size: 4 }), - toHex(hookTypeBit + hasPostHooksBit + hasPreHooksBit, { + config.hookType, + // hasPostHook is stored in the first bit, hasPreHook in the second bit + toHex((config.hasPostHooks ? 1 : 0) + (config.hasPreHooks ? 2 : 0), { size: 1, }), ]); } export function serializeModuleEntity(config: ModuleEntity): Hex { - return concatHex([config.moduleAddress, toHex(config.entityId, { size: 4 })]); + return concatHex([config.address, toHex(config.entityId, { size: 4 })]); } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts index 7d9edf589c..6f2e50cb20 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -3,27 +3,33 @@ import { IncompatibleClientError, isSmartAccountClient, EntityIdOverrideError, + type GetAccountParameter, type GetEntryPointFromAccount, type SendUserOperationResult, + type SmartContractAccount, + type SmartAccountClient, type UserOperationOverridesParameter, - type SmartAccountSigner, } from "@aa-sdk/core"; -import { type Address, type Hex, encodeFunctionData, concatHex } from "viem"; +import { + type Address, + type Hex, + type Chain, + type Transport, + encodeFunctionData, + concatHex, +} from "viem"; import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; import type { HookConfig, ValidationConfig } from "../common/types.js"; import { serializeValidationConfig, serializeHookConfig, - serializeModuleEntity, } from "../common/utils.js"; -import { type SMAV2AccountClient } from "../../client/client.js"; -import { type SMAV2Account } from "../../account/semiModularAccountV2.js"; -import { DEFAULT_OWNER_ENTITY_ID } from "../../utils.js"; - export type InstallValidationParams< - TSigner extends SmartAccountSigner = SmartAccountSigner + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined > = { validationConfig: ValidationConfig; selectors: Hex[]; @@ -32,39 +38,41 @@ export type InstallValidationParams< hookConfig: HookConfig; initData: Hex; }[]; - account?: SMAV2Account | undefined; -} & UserOperationOverridesParameter< - GetEntryPointFromAccount> ->; +} & UserOperationOverridesParameter> & + GetAccountParameter; export type UninstallValidationParams< - TSigner extends SmartAccountSigner = SmartAccountSigner + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined > = { moduleAddress: Address; entityId: number; uninstallData: Hex; hookUninstallDatas: Hex[]; - account?: SMAV2Account | undefined; -} & UserOperationOverridesParameter< - GetEntryPointFromAccount> ->; +} & UserOperationOverridesParameter> & + GetAccountParameter; export type InstallValidationActions< - TSigner extends SmartAccountSigner = SmartAccountSigner + TAccount extends SmartContractAccount | undefined = + | SmartContractAccount + | undefined > = { installValidation: ( - args: InstallValidationParams + args: InstallValidationParams ) => Promise; uninstallValidation: ( - args: UninstallValidationParams + args: UninstallValidationParams ) => Promise; }; export const installValidationActions: < - TSigner extends SmartAccountSigner = SmartAccountSigner + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends SmartContractAccount = SmartContractAccount >( - client: SMAV2AccountClient -) => InstallValidationActions = (client) => ({ + client: SmartAccountClient +) => InstallValidationActions = (client) => ({ installValidation: async ({ validationConfig, selectors, @@ -85,26 +93,22 @@ export const installValidationActions: < ); } - if (validationConfig.entityId === DEFAULT_OWNER_ENTITY_ID) { + if (validationConfig.entityId === 0) { throw new EntityIdOverrideError(); } - const { encodeCallData } = account; - - const callData = await encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }) - ); + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }); return client.sendUserOperation({ uo: callData, @@ -114,44 +118,39 @@ export const installValidationActions: < }, uninstallValidation: async ({ - moduleAddress, - entityId, - uninstallData, - hookUninstallDatas, + // moduleAddress, entityId, uninstallData, hookUninstallDatas, account = client.account, overrides, }) => { - if (!account) { - throw new AccountNotFoundError(); - } - - if (!isSmartAccountClient(client)) { - throw new IncompatibleClientError( - "SmartAccountClient", - "uninstallValidation", - client - ); - } - - const { encodeCallData } = account; - - const callData = await encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }) - ); + // if (!account) { + // throw new AccountNotFoundError(); + // } + + // if (!isSmartAccountClient(client)) { + // throw new IncompatibleClientError( + // "SmartAccountClient", + // "uninstallValidation", + // client + // ); + // } + + // const { moduleAddress, entityId, uninstallData, hookUninstallDatas } = args; + + // const callData = encodeFunctionData({ + // abi: semiModularAccountBytecodeAbi, + // functionName: "uninstallValidation", + // args: [ + // serializeModuleEntity({ + // moduleAddress, + // entityId, + // }), + // uninstallData, + // hookUninstallDatas, + // ], + // }); return client.sendUserOperation({ - uo: callData, + uo: "0x", account, overrides, }); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 2d65223a3b..b53e9e689a 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -4,9 +4,10 @@ import { createSMAV2AccountClient, type SMAV2AccountClient } from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; -import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; import { installValidationActions } from "../actions/install-validation/installValidation.js"; +import { addresses } from "../utils.js"; +import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import type { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -28,10 +29,11 @@ describe("MA v2 Tests", async () => { const target = "0x000000000000000000000000000000000000dEaD"; const sendAmount = parseEther("1"); - const getTargetBalance = async (): Promise => - client.getBalance({ + const getTargetBalance = async (): Promise => { + return client.getBalance({ address: target, }); + }; it("sends a simple UO", async () => { const provider = await givenConnectedProvider({ signer }); @@ -53,7 +55,58 @@ describe("MA v2 Tests", async () => { await provider.waitForUserOperationTransaction(result); - await expect(getTargetBalance()).resolves.toEqual( + await expect(await getTargetBalance()).toEqual( + startingAddressBalance + sendAmount + ); + }); + + it("adds a session key with no permissions", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const result = await provider.installValidation({ + validationConfig: { + address: addresses.singleSignerValidationModule, + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + const startingAddressBalance = await getTargetBalance(); + + // connect session key + provider.signer = sessionKey; + provider.entityId = 1n; + + const result2 = await provider.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount ); }); From dc7a5733d53d77f772a672aad29ff5b63be35223 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:34:51 -0500 Subject: [PATCH 40/45] feat: add uninstall validation, tests --- .../src/ma-v2/actions/common/types.ts | 4 +- .../src/ma-v2/actions/common/utils.ts | 2 +- .../install-validation/installValidation.ts | 54 ++++++++++--------- .../src/ma-v2/client/client.test.ts | 48 ++++++++++++++--- 4 files changed, 71 insertions(+), 37 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts index 1a133ae0bc..14f5739e6e 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/types.ts @@ -1,12 +1,12 @@ import type { Address, Hex } from "viem"; export type ModuleEntity = { - address: Address; + moduleAddress: Address; entityId: number; }; export type ValidationConfig = { - address: Address; + moduleAddress: Address; entityId: number; // uint32 isGlobal: boolean; isSignatureValidation: boolean; diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts index 2d79719bce..7af59140a2 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -26,5 +26,5 @@ export function serializeHookConfig(config: HookConfig): Hex { } export function serializeModuleEntity(config: ModuleEntity): Hex { - return concatHex([config.address, toHex(config.entityId, { size: 4 })]); + return concatHex([config.moduleAddress, toHex(config.entityId, { size: 4 })]); } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts index 6f2e50cb20..f2d77d386c 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -24,6 +24,7 @@ import type { HookConfig, ValidationConfig } from "../common/types.js"; import { serializeValidationConfig, serializeHookConfig, + serializeModuleEntity, } from "../common/utils.js"; export type InstallValidationParams< @@ -118,39 +119,40 @@ export const installValidationActions: < }, uninstallValidation: async ({ - // moduleAddress, entityId, uninstallData, hookUninstallDatas, + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, account = client.account, overrides, }) => { - // if (!account) { - // throw new AccountNotFoundError(); - // } - - // if (!isSmartAccountClient(client)) { - // throw new IncompatibleClientError( - // "SmartAccountClient", - // "uninstallValidation", - // client - // ); - // } + if (!account) { + throw new AccountNotFoundError(); + } - // const { moduleAddress, entityId, uninstallData, hookUninstallDatas } = args; + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "uninstallValidation", + client + ); + } - // const callData = encodeFunctionData({ - // abi: semiModularAccountBytecodeAbi, - // functionName: "uninstallValidation", - // args: [ - // serializeModuleEntity({ - // moduleAddress, - // entityId, - // }), - // uninstallData, - // hookUninstallDatas, - // ], - // }); + const callData = encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }); return client.sendUserOperation({ - uo: "0x", + uo: callData, account, overrides, }); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index b53e9e689a..32a4fcceae 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -70,9 +70,9 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const result = await provider.installValidation({ + let result = await provider.installValidation({ validationConfig: { - address: addresses.singleSignerValidationModule, + moduleAddress: addresses.singleSignerValidationModule, entityId: 1, isGlobal: true, isSignatureValidation: true, @@ -91,11 +91,17 @@ describe("MA v2 Tests", async () => { const startingAddressBalance = await getTargetBalance(); - // connect session key - provider.signer = sessionKey; - provider.entityId = 1n; + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1n, + isGlobalValidation: true, + }); - const result2 = await provider.sendUserOperation({ + result = await sessionKeyClient.sendUserOperation({ uo: { target: target, value: sendAmount, @@ -103,14 +109,14 @@ describe("MA v2 Tests", async () => { }, }); - txnHash = provider.waitForUserOperationTransaction(result); + txnHash = sessionKeyClient.waitForUserOperationTransaction(result); await expect(txnHash).resolves.not.toThrowError(); - await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount ); }); +<<<<<<< HEAD it("adds a session key with no permissions", async () => { let provider = (await givenConnectedProvider({ signer })).extend( installValidationActions @@ -169,6 +175,8 @@ describe("MA v2 Tests", async () => { ); }); +======= +>>>>>>> 9e5c594d (feat: add uninstall validation, tests) it("uninstalls a session key", async () => { let provider = (await givenConnectedProvider({ signer })).extend( installValidationActions @@ -181,9 +189,13 @@ describe("MA v2 Tests", async () => { let result = await provider.installValidation({ validationConfig: { +<<<<<<< HEAD moduleAddress: getDefaultSingleSignerValidationModuleAddress( provider.chain ), +======= + moduleAddress: addresses.singleSignerValidationModule, +>>>>>>> 9e5c594d (feat: add uninstall validation, tests) entityId: 1, isGlobal: true, isSignatureValidation: true, @@ -201,9 +213,13 @@ describe("MA v2 Tests", async () => { await expect(txnHash).resolves.not.toThrowError(); result = await provider.uninstallValidation({ +<<<<<<< HEAD moduleAddress: getDefaultSingleSignerValidationModuleAddress( provider.chain ), +======= + moduleAddress: addresses.singleSignerValidationModule, +>>>>>>> 9e5c594d (feat: add uninstall validation, tests) entityId: 1, uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ entityId: 1, @@ -220,6 +236,7 @@ describe("MA v2 Tests", async () => { signer: sessionKey, transport: custom(instance.getClient()), accountAddress: provider.getAddress(), +<<<<<<< HEAD entityId: 1, isGlobalValidation: true, }); @@ -233,6 +250,21 @@ describe("MA v2 Tests", async () => { }, }) ).rejects.toThrowError(); +======= + entityId: 1n, + isGlobalValidation: true, + }); + + result = sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + await expect(result).rejects.toThrowError(); +>>>>>>> 9e5c594d (feat: add uninstall validation, tests) }); const givenConnectedProvider = async ({ From 531d5f80b906d3b79ecdfe5b6933c8b6205faa11 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 41/45] chore: removing arbsepo instance --- .vitest/src/instances.ts | 2 +- .../src/ma-v2/account/account.ts | 157 +++++++++++ .../src/ma-v2/client/client.test.ts | 251 ++---------------- .../smart-contracts/src/ma-v2/utils.ts | 112 ++------ 4 files changed, 202 insertions(+), 320 deletions(-) create mode 100644 account-kit/smart-contracts/src/ma-v2/account/account.ts diff --git a/.vitest/src/instances.ts b/.vitest/src/instances.ts index 0332b7e6e5..489c8b4350 100644 --- a/.vitest/src/instances.ts +++ b/.vitest/src/instances.ts @@ -23,7 +23,7 @@ export const local060Instance = defineInstance({ export const local070Instance = defineInstance({ chain: localhost, - forkBlockNumber: 7303015, + forkBlockNumber: 7299096, forkUrl: process.env.VITEST_SEPOLIA_FORK_URL ?? "https://ethereum-sepolia-rpc.publicnode.com", diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts new file mode 100644 index 0000000000..1f9cce1c1b --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/account/account.ts @@ -0,0 +1,157 @@ +import type { + EntryPointDef, + SmartAccountSigner, + SmartContractAccountWithSigner, + ToSmartContractAccountParams, +} from "@aa-sdk/core"; +import { + createBundlerClient, + getEntryPoint, + toSmartContractAccount, + getAccountAddress, +} from "@aa-sdk/core"; +import { + concatHex, + encodeFunctionData, + getContract, + maxUint32, + toHex, + type Address, + type Chain, + type Hex, + type Transport, + hexToBigInt, +} from "viem"; +import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; +import { addresses } from "../utils.js"; +import { standardExecutor } from "../../msca/account/standardExecutor.js"; +import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; +import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; + +export const DEFAULT_OWNER_ENTITY_ID = 0; + +export type SMAV2Account< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0">; + +export type CreateSMAV2AccountParams< + TTransport extends Transport = Transport, + TSigner extends SmartAccountSigner = SmartAccountSigner +> = Pick< + ToSmartContractAccountParams<"SMAV2Account", TTransport, Chain, "0.7.0">, + "transport" | "chain" | "accountAddress" +> & { + signer: TSigner; + salt?: bigint; + factoryAddress?: Address; + initCode?: Hex; + initialOwner?: Address; + entryPoint?: EntryPointDef<"0.7.0", Chain>; +} & ( + | { + isGlobalValidation: boolean; + entityId: bigint; + } + | { + isGlobalValidation: never; + entityId: never; + } + ); + +export async function createSMAV2Account< + TTransport extends Transport = Transport, + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + config: CreateSMAV2AccountParams +): Promise>; + +export async function createSMAV2Account( + config: CreateSMAV2AccountParams +): Promise { + const { + transport, + chain, + signer, + salt = 0n, + factoryAddress = addresses.accountFactory, + initCode, + initialOwner, + accountAddress, + entryPoint = getEntryPoint(chain, { version: "0.7.0" }), + isGlobalValidation = true, + entityId = 0n, + } = config; + + if (entityId >= maxUint32) { + throw new InvalidEntityIdError(entityId); + } + + const client = createBundlerClient({ + transport, + chain, + }); + + const getAccountInitCode = async () => { + if (initCode) { + return initCode; + } + + // If an initial owner is not provided, use the signer's address + const ownerAddress = initialOwner ?? (await signer.getAddress()); + + return concatHex([ + factoryAddress, + encodeFunctionData({ + abi: accountFactoryAbi, + functionName: "createSemiModularAccount", + args: [ownerAddress, salt], + }), + ]); + }; + + const baseAccount = await toSmartContractAccount({ + transport, + chain, + entryPoint, + accountAddress, + source: `SMAV2Account`, + getAccountInitCode, + ...standardExecutor, + ...singleSignerMessageSigner(signer), + }); + + const accountAddress_ = await getAccountAddress({ + client, + entryPoint, + accountAddress, + getAccountInitCode, + }); + + // TODO: add deferred action flag + const getAccountNonce = async (nonceKey?: bigint): Promise => { + const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ + isGlobalValidation ? "01" : "00" + }`; + + if (nonceKey && toHex(nonceKey, { size: 5 }) !== nonceKeySuffix) { + throw new InvalidNonceKeyError(nonceKey, hexToBigInt(nonceKeySuffix)); + } + + const entryPointContract = getContract({ + address: entryPoint.address, + abi: entryPoint.abi, + client, + }); + + return entryPointContract.read.getNonce([ + accountAddress_, + nonceKey ?? hexToBigInt(nonceKeySuffix), + ]) as Promise; + }; + + return { + ...baseAccount, + getAccountNonce, + getSigner: () => signer, + }; +} diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 32a4fcceae..0dda6a8263 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,18 +1,20 @@ import { custom, parseEther, publicActions } from "viem"; -import { LocalAccountSigner, type SmartAccountSigner } from "@aa-sdk/core"; -import { createSMAV2AccountClient, type SMAV2AccountClient } from "./client.js"; + +import { + erc7677Middleware, + LocalAccountSigner, + type SmartAccountSigner, +} from "@aa-sdk/core"; + +import { createSMAV2AccountClient } from "./client.js"; + import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { installValidationActions } from "../actions/install-validation/installValidation.js"; -import { addresses } from "../utils.js"; -import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; -import type { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; - let client: ReturnType & - ReturnType; + let client: ReturnType; beforeAll(async () => { client = instance.getClient().extend(publicActions); @@ -22,19 +24,6 @@ describe("MA v2 Tests", async () => { accounts.fundedAccountOwner ); - const sessionKey: SmartAccountSigner = new LocalAccountSigner( - accounts.unfundedAccountOwner - ); - - const target = "0x000000000000000000000000000000000000dEaD"; - const sendAmount = parseEther("1"); - - const getTargetBalance = async (): Promise => { - return client.getBalance({ - address: target, - }); - }; - it("sends a simple UO", async () => { const provider = await givenConnectedProvider({ signer }); @@ -43,65 +32,14 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const startingAddressBalance = await getTargetBalance(); + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); - const result = await provider.sendUserOperation({ - uo: { - target: target, - value: sendAmount, - data: "0x", - }, - }); - - await provider.waitForUserOperationTransaction(result); - - await expect(await getTargetBalance()).toEqual( - startingAddressBalance + sendAmount - ); - }); - - it("adds a session key with no permissions", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); - - await setBalance(client, { - address: provider.getAddress(), - value: parseEther("2"), - }); - - let result = await provider.installValidation({ - validationConfig: { - moduleAddress: addresses.singleSignerValidationModule, - entityId: 1, - isGlobal: true, - isSignatureValidation: true, - isUserOpValidation: true, - }, - selectors: [], - installData: SingleSignerValidationModule.encodeOnInstallData({ - entityId: 1, - signer: await sessionKey.getAddress(), - }), - hooks: [], - }); - - let txnHash = provider.waitForUserOperationTransaction(result); - await expect(txnHash).resolves.not.toThrowError(); - - const startingAddressBalance = await getTargetBalance(); - - // connect session key and send tx with session key - let sessionKeyClient = await createSMAV2AccountClient({ - chain: instance.chain, - signer: sessionKey, - transport: custom(instance.getClient()), - accountAddress: provider.getAddress(), - entityId: 1n, - isGlobalValidation: true, + const startingAddressBalance = await client.getBalance({ + address: target, }); - result = await sessionKeyClient.sendUserOperation({ + const result = await provider.sendUserOperation({ uo: { target: target, value: sendAmount, @@ -109,177 +47,28 @@ describe("MA v2 Tests", async () => { }, }); - txnHash = sessionKeyClient.waitForUserOperationTransaction(result); - await expect(txnHash).resolves.not.toThrowError(); - await expect(await getTargetBalance()).toEqual( - startingAddressBalance + sendAmount - ); - }); + const txnHash1 = provider.waitForUserOperationTransaction(result); + await expect(txnHash1).resolves.not.toThrowError(); -<<<<<<< HEAD - it("adds a session key with no permissions", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); - - await setBalance(client, { - address: provider.getAddress(), - value: parseEther("2"), - }); - - let result = await provider.installValidation({ - validationConfig: { - moduleAddress: getDefaultSingleSignerValidationModuleAddress( - provider.chain - ), - entityId: 1, - isGlobal: true, - isSignatureValidation: true, - isUserOpValidation: true, - }, - selectors: [], - installData: SingleSignerValidationModule.encodeOnInstallData({ - entityId: 1, - signer: await sessionKey.getAddress(), - }), - hooks: [], - }); - - let txnHash = provider.waitForUserOperationTransaction(result); - await expect(txnHash).resolves.not.toThrowError(); - - const startingAddressBalance = await getTargetBalance(); - - // connect session key and send tx with session key - let sessionKeyClient = await createSMAV2AccountClient({ - chain: instance.chain, - signer: sessionKey, - transport: custom(instance.getClient()), - accountAddress: provider.getAddress(), - entityId: 1, - isGlobalValidation: true, - }); - - result = await sessionKeyClient.sendUserOperation({ - uo: { - target: target, - value: sendAmount, - data: "0x", - }, + const newAddressBalance = await client.getBalance({ + address: target, }); - txnHash = sessionKeyClient.waitForUserOperationTransaction(result); - await expect(txnHash).resolves.not.toThrowError(); - await expect(getTargetBalance()).resolves.toEqual( + await expect(newAddressBalance).toEqual( startingAddressBalance + sendAmount ); }); -======= ->>>>>>> 9e5c594d (feat: add uninstall validation, tests) - it("uninstalls a session key", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); - - await setBalance(client, { - address: provider.getAddress(), - value: parseEther("2"), - }); - - let result = await provider.installValidation({ - validationConfig: { -<<<<<<< HEAD - moduleAddress: getDefaultSingleSignerValidationModuleAddress( - provider.chain - ), -======= - moduleAddress: addresses.singleSignerValidationModule, ->>>>>>> 9e5c594d (feat: add uninstall validation, tests) - entityId: 1, - isGlobal: true, - isSignatureValidation: true, - isUserOpValidation: true, - }, - selectors: [], - installData: SingleSignerValidationModule.encodeOnInstallData({ - entityId: 1, - signer: await sessionKey.getAddress(), - }), - hooks: [], - }); - - let txnHash = provider.waitForUserOperationTransaction(result); - await expect(txnHash).resolves.not.toThrowError(); - - result = await provider.uninstallValidation({ -<<<<<<< HEAD - moduleAddress: getDefaultSingleSignerValidationModuleAddress( - provider.chain - ), -======= - moduleAddress: addresses.singleSignerValidationModule, ->>>>>>> 9e5c594d (feat: add uninstall validation, tests) - entityId: 1, - uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ - entityId: 1, - }), - hookUninstallDatas: [], - }); - - txnHash = provider.waitForUserOperationTransaction(result); - await expect(txnHash).resolves.not.toThrowError(); - - // connect session key and send tx with session key - let sessionKeyClient = await createSMAV2AccountClient({ - chain: instance.chain, - signer: sessionKey, - transport: custom(instance.getClient()), - accountAddress: provider.getAddress(), -<<<<<<< HEAD - entityId: 1, - isGlobalValidation: true, - }); - - await expect( - sessionKeyClient.sendUserOperation({ - uo: { - target: target, - value: sendAmount, - data: "0x", - }, - }) - ).rejects.toThrowError(); -======= - entityId: 1n, - isGlobalValidation: true, - }); - - result = sessionKeyClient.sendUserOperation({ - uo: { - target: target, - value: sendAmount, - data: "0x", - }, - }); - - await expect(result).rejects.toThrowError(); ->>>>>>> 9e5c594d (feat: add uninstall validation, tests) - }); - const givenConnectedProvider = async ({ signer, - accountAddress, usePaymaster, }: { signer: SmartAccountSigner; - accountAddress?: `0x${string}`; usePaymaster?: boolean; }) => createSMAV2AccountClient({ chain: instance.chain, signer, - accountAddress, transport: custom(instance.getClient()), ...(usePaymaster ? erc7677Middleware() : {}), }); diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index 73e241b70b..3e3940cf12 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -27,92 +27,28 @@ export const packSignature = ({ return concat(["0xFF", "0x00", validationSignature]); }; -export const getDefaultMAV2FactoryAddress = (chain: Chain): Address => { - switch (chain.id) { - // TODO: case mekong.id: - case sepolia.id: - case baseSepolia.id: - case polygon.id: - case mainnet.id: - case polygonAmoy.id: - case optimism.id: - case optimismSepolia.id: - case arbitrum.id: - case arbitrumSepolia.id: - case base.id: - default: - return "0x00000000000017c61b5bEe81050EC8eFc9c6fecd"; - } -}; - -export const getDefaultSMAV2BytecodeAddress = (chain: Chain): Address => { - switch (chain.id) { - // TODO: case mekong.id: - case sepolia.id: - case baseSepolia.id: - case polygon.id: - case mainnet.id: - case polygonAmoy.id: - case optimism.id: - case optimismSepolia.id: - case arbitrum.id: - case arbitrumSepolia.id: - case base.id: - default: - return "0x000000000000c5A9089039570Dd36455b5C07383"; - } -}; - -export const getDefaultSMAV2StorageAddress = (chain: Chain): Address => { - switch (chain.id) { - // TODO: case mekong.id: - case sepolia.id: - case baseSepolia.id: - case polygon.id: - case mainnet.id: - case polygonAmoy.id: - case optimism.id: - case optimismSepolia.id: - case arbitrum.id: - case arbitrumSepolia.id: - case base.id: - default: - return "0x0000000000006E2f9d80CaEc0Da6500f005EB25A"; - } -}; - -export const getDefaultSMAV27702Address = (chain: Chain): Address => { - switch (chain.id) { - // TODO: case mekong.id: - case sepolia.id: - case baseSepolia.id: - case polygon.id: - case mainnet.id: - case polygonAmoy.id: - case optimism.id: - case optimismSepolia.id: - case arbitrum.id: - case arbitrumSepolia.id: - case base.id: - default: - return "0x69007702764179f14F51cdce752f4f775d74E139"; - } -}; - -export const getDefaultMAV2Address = (chain: Chain): Address => { - switch (chain.id) { - // TODO: case mekong.id: - case sepolia.id: - case baseSepolia.id: - case polygon.id: - case mainnet.id: - case polygonAmoy.id: - case optimism.id: - case optimismSepolia.id: - case arbitrum.id: - case arbitrumSepolia.id: - case base.id: - default: - return "0x00000000000002377B26b1EdA7b0BC371C60DD4f"; - } +export const addresses = { + allowlistModule: + "0xe768abEe3ad779Be1318388fC5D6A6fCf658011B" as `0x${string}`, + nativeTokenLimitModule: + "0xbFD077C53ec21f91699cF2DBab5324ea608b4eF0" as `0x${string}`, + paymasterGuardModule: + "0x97018B224C969A1992076293f15482FB9982A271" as `0x${string}`, + singleSignerValidationModule: + "0x2a42a36ee6DC9E8f75d2f8B5ef621EE8F2bD3156" as `0x${string}`, + timeRangeModule: + "0x335a66470B5052DD9F540bD64ca6b7dE205F6f0B" as `0x${string}`, + webauthnValidationModule: + "0x52bB58A05659F2fF4bDb57E602824859De11119A" as `0x${string}`, + executionInstallDelegate: + "0x8Bf909fEb66EBcC4725f04E70F319791Ec9d9628" as `0x${string}`, + modularAccountImpl: + "0x7219030794F2937ff4A322B7CE9a58C070aF08C5" as `0x${string}`, + semiModularAccountBytecodeImpl: + "0xc6176BeF7E32224238ef3A2Ee3F9BaCfA801Cc95" as `0x${string}`, + semiModularAccountStorageOnlyImpl: + "0x8bD01f353058513fE5968C80585dc0792f56961b" as `0x${string}`, + accountFactory: "0x52fd2B39bd2a2c411371514114f9a1b3F9Ba3a64" as `0x${string}`, + accountFactoryOwner: + "0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C" as `0x${string}`, }; From caa976b2087cc0afb085ebdca72a9e5fbbddb198 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 42/45] feat: add install validation, add tests for session key --- aa-sdk/core/src/errors/client.ts | 16 ++ .../src/ma-v2/account/semiModularAccountV2.ts | 1 + .../src/ma-v2/actions/common/utils.ts | 8 +- .../src/ma-v2/client/client.test.ts | 151 ++++++++++++++++-- 4 files changed, 164 insertions(+), 12 deletions(-) diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index 7194f49d97..a773561333 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -117,3 +117,19 @@ export class EntityIdOverrideError extends BaseError { ); } } + +/** + * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. + */ +export class EntityIdOverrideError extends BaseError { + override name = "InvalidNonceKeyError"; + + /** + * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. + */ + constructor() { + super( + `Installing entityId of 0 overrides the owner's entity id in the account` + ); + } +} diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index 6a7c45f2b3..b281bb5960 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -9,6 +9,7 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, + getAccountAddress, InvalidEntityIdError, InvalidNonceKeyError, getAccountAddress, diff --git a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts index 7af59140a2..8bd366ab76 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/common/utils.ts @@ -1,5 +1,6 @@ import { type Hex, toHex, concatHex } from "viem"; import type { ValidationConfig, HookConfig, ModuleEntity } from "./types"; +import { HookType } from "./types.js"; export function serializeValidationConfig(config: ValidationConfig): Hex { const isUserOpValidationBit = config.isUserOpValidation ? 1 : 0; @@ -14,12 +15,13 @@ export function serializeValidationConfig(config: ValidationConfig): Hex { } export function serializeHookConfig(config: HookConfig): Hex { + const hookTypeBit = config.hookType === HookType.VALIDATION ? 1 : 0; + const hasPostHooksBit = config.hasPostHooks ? 2 : 0; + const hasPreHooksBit = config.hasPreHooks ? 4 : 0; return concatHex([ config.address, toHex(config.entityId, { size: 4 }), - config.hookType, - // hasPostHook is stored in the first bit, hasPreHook in the second bit - toHex((config.hasPostHooks ? 1 : 0) + (config.hasPreHooks ? 2 : 0), { + toHex(hookTypeBit + hasPostHooksBit + hasPreHooksBit, { size: 1, }), ]); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 0dda6a8263..2a21ebdb36 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -11,6 +11,9 @@ import { createSMAV2AccountClient } from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; +import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; +import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -24,6 +27,18 @@ describe("MA v2 Tests", async () => { accounts.fundedAccountOwner ); + const sessionKey: SmartAccountSigner = new LocalAccountSigner( + accounts.unfundedAccountOwner + ); + + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); + + const getTargetBalance = async (): Promise => + client.getBalance({ + address: target, + }); + it("sends a simple UO", async () => { const provider = await givenConnectedProvider({ signer }); @@ -32,12 +47,7 @@ describe("MA v2 Tests", async () => { value: parseEther("2"), }); - const target = "0x000000000000000000000000000000000000dEaD"; - const sendAmount = parseEther("1"); - - const startingAddressBalance = await client.getBalance({ - address: target, - }); + const startingAddressBalance = await getTargetBalance(); const result = await provider.sendUserOperation({ uo: { @@ -50,25 +60,148 @@ describe("MA v2 Tests", async () => { const txnHash1 = provider.waitForUserOperationTransaction(result); await expect(txnHash1).resolves.not.toThrowError(); - const newAddressBalance = await client.getBalance({ - address: target, + await expect(await getTargetBalance()).toEqual( + startingAddressBalance + sendAmount + ); + }); + + it("adds a session key with no permissions", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + const startingAddressBalance = await getTargetBalance(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1, + isGlobalValidation: true, }); - await expect(newAddressBalance).toEqual( + result = await sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + txnHash = sessionKeyClient.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + await expect(await getTargetBalance()).toEqual( startingAddressBalance + sendAmount ); }); + it("uninstalls a session key", async () => { + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); + + await setBalance(client, { + address: provider.getAddress(), + value: parseEther("2"), + }); + + let result = await provider.installValidation({ + validationConfig: { + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + isGlobal: true, + isSignatureValidation: true, + isUserOpValidation: true, + }, + selectors: [], + installData: SingleSignerValidationModule.encodeOnInstallData({ + entityId: 1, + signer: await sessionKey.getAddress(), + }), + hooks: [], + }); + + let txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + result = await provider.uninstallValidation({ + moduleAddress: getDefaultSingleSignerValidationModuleAddress( + provider.chain + ), + entityId: 1, + uninstallData: SingleSignerValidationModule.encodeOnUninstallData({ + entityId: 1, + }), + hookUninstallDatas: [], + }); + + txnHash = provider.waitForUserOperationTransaction(result); + await expect(txnHash).resolves.not.toThrowError(); + + // connect session key and send tx with session key + let sessionKeyClient = await createSMAV2AccountClient({ + chain: instance.chain, + signer: sessionKey, + transport: custom(instance.getClient()), + accountAddress: provider.getAddress(), + entityId: 1, + isGlobalValidation: true, + }); + + await expect( + sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }) + ).rejects.toThrowError(); + }); + const givenConnectedProvider = async ({ signer, + accountAddress, usePaymaster, }: { signer: SmartAccountSigner; + accountAddress?: `0x${string}`; usePaymaster?: boolean; }) => createSMAV2AccountClient({ chain: instance.chain, signer, + accountAddress, transport: custom(instance.getClient()), ...(usePaymaster ? erc7677Middleware() : {}), }); From 664fa5ea2523032753a636eb1ba2428f256422c6 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:58:19 -0500 Subject: [PATCH 43/45] feat: add view functions and user op calldata encodings --- .../src/ma-v2/account/semiModularAccountV2.ts | 89 +++++++++++++++---- .../install-validation/installValidation.ts | 61 +++++++------ .../src/ma-v2/client/client.test.ts | 3 +- .../src/ma-v2/client/client.ts | 80 ++--------------- 4 files changed, 115 insertions(+), 118 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index b281bb5960..b5b986c000 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -25,6 +25,8 @@ import { type Chain, type Hex, type Transport, + hexToBigInt, + zeroAddress, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; import { @@ -32,11 +34,14 @@ import { DEFAULT_OWNER_ENTITY_ID, } from "../utils.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; +import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; import { modularAccountAbi } from "../abis/modularAccountAbi.js"; import { serializeModuleEntity } from "../actions/common/utils.js"; const executeUserOpSelector: Hex = "0x8DD7712F"; +const executeUserOpSelector: Hex = "0x8DD7712F"; + export type SignerEntity = { isGlobalValidation: boolean; entityId: number; @@ -66,15 +71,38 @@ export type ValidationDataParams = entityId: number; }; +export type CalldataEncoder = { + encodeCallData: (callData: Hex) => Promise; +}; + +export type ExecutionDataView = { + module: Address; + skipRuntimeValidation: boolean; + allowGlobalValidation: boolean; + executionHooks: readonly Hex[]; +}; + +export type ValidationDataView = { + validationHooks: readonly Hex[]; + executionHooks: readonly Hex[]; + selectors: readonly Hex[]; + validationFlags: number; +}; + +export type ValidationDataParams = { + validationModuleAddress?: Address; + entityId?: number; +}; + export type SMAV2Account< - TSigner extends SmartAccountSigner = SmartAccountSigner + TSigner extends SmartAccountSigner = SmartAccountSigner, + TCalldataEncoder extends CalldataEncoder = CalldataEncoder > = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & - SignerEntity & { + TCalldataEncoder & { getExecutionData: (selector: Hex) => Promise; getValidationData: ( args: ValidationDataParams ) => Promise; - encodeCallData: (callData: Hex) => Promise; }; export type CreateSMAV2AccountParams< @@ -227,12 +255,7 @@ export async function createSMAV2Account( const getExecutionData = async (selector: Hex) => { if (!(await baseAccount.isAccountDeployed())) { - return { - module: zeroAddress, - skipRuntimeValidation: false, - allowGlobalValidation: false, - executionHooks: [], - }; + return {} as ExecutionDataView; } return await accountContract.read.getExecutionData([selector]); @@ -240,12 +263,7 @@ export async function createSMAV2Account( const getValidationData = async (args: ValidationDataParams) => { if (!(await baseAccount.isAccountDeployed())) { - return { - validationHooks: [], - executionHooks: [], - selectors: [], - validationFlags: 0, - }; + return {} as ValidationDataView; } const { validationModuleAddress, entityId } = args; @@ -262,19 +280,52 @@ export async function createSMAV2Account( entityId: Number(entityId), }); - return validationData.executionHooks.length - ? concatHex([executeUserOpSelector, callData]) + const numHooks = validationData?.executionHooks?.length ?? 0; + + return numHooks ? concatHex([executeUserOpSelector, callData]) : callData; + }; + + const encodeExecute: (tx: AccountOp) => Promise = async ({ + target, + data, + value, + }) => { + let callData = encodeFunctionData({ + abi: modularAccountAbi, + functionName: "execute", + args: [target, value ?? 0n, data], + }); + + callData = (await baseAccount.isAccountDeployed()) + ? await encodeCallData(callData) : callData; + + return callData; }; + const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => + encodeCallData( + encodeFunctionData({ + abi: modularAccountAbi, + functionName: "executeBatch", + args: [ + txs.map((tx) => ({ + target: tx.target, + data: tx.data, + value: tx.value ?? 0n, + })), + ], + }) + ); + return { ...baseAccount, getAccountNonce, getSigner: () => signer, - isGlobalValidation, - entityId, getExecutionData, getValidationData, + encodeExecute, + encodeBatchExecute, encodeCallData, }; } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts index f2d77d386c..51d5576d32 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -6,9 +6,9 @@ import { type GetAccountParameter, type GetEntryPointFromAccount, type SendUserOperationResult, - type SmartContractAccount, type SmartAccountClient, type UserOperationOverridesParameter, + type SmartContractAccount, } from "@aa-sdk/core"; import { type Address, @@ -26,6 +26,7 @@ import { serializeHookConfig, serializeModuleEntity, } from "../common/utils.js"; +import type { CalldataEncoder } from "../../account/semiModularAccountV2.js"; export type InstallValidationParams< TAccount extends SmartContractAccount | undefined = @@ -72,7 +73,7 @@ export const installValidationActions: < TChain extends Chain | undefined = Chain | undefined, TAccount extends SmartContractAccount = SmartContractAccount >( - client: SmartAccountClient + client: SmartAccountClient & CalldataEncoder ) => InstallValidationActions = (client) => ({ installValidation: async ({ validationConfig, @@ -98,18 +99,22 @@ export const installValidationActions: < throw new EntityIdOverrideError(); } - const callData = encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }); + const { encodeCallData } = client; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); return client.sendUserOperation({ uo: callData, @@ -138,18 +143,22 @@ export const installValidationActions: < ); } - const callData = encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }); + const { encodeCallData } = client; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); return client.sendUserOperation({ uo: callData, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 2a21ebdb36..611a74764c 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,9 +1,9 @@ import { custom, parseEther, publicActions } from "viem"; import { - erc7677Middleware, LocalAccountSigner, type SmartAccountSigner, + type SmartAccountClient, } from "@aa-sdk/core"; import { createSMAV2AccountClient } from "./client.js"; @@ -14,6 +14,7 @@ import { accounts } from "~test/constants.js"; import { installValidationActions } from "../actions/install-validation/installValidation.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { type CalldataEncoder } from "../account/semiModularAccountV2.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index 23f7b309c1..fdb4182955 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -9,11 +9,9 @@ import { type SendUserOperationResult, } from "@aa-sdk/core"; import { - concatHex, - encodeFunctionData, - zeroAddress, type Address, type Chain, + type CustomTransport, type Hex, type Transport, } from "viem"; @@ -78,7 +76,9 @@ export function createSMAV2AccountClient< TCalldataEncoder extends CalldataEncoder = CalldataEncoder >( args: CreateSMAV2AccountClientParams -): Promise>; +): Promise< + SmartAccountClient & TCalldataEncoder +>; /** * Creates a MAv2 account client using the provided configuration parameters. @@ -111,7 +111,9 @@ export function createSMAV2AccountClient< */ export async function createSMAV2AccountClient({ ...config -}: CreateSMAV2AccountClientParams): Promise { +}: CreateSMAV2AccountClientParams): Promise< + SmartAccountClient & CalldataEncoder +> { const maV2Account = await createSMAV2Account({ ...config, }); @@ -121,74 +123,8 @@ export async function createSMAV2AccountClient({ account: maV2Account, }); - const installValidation = async ({ - validationConfig, - selectors, - installData, - hooks, - overrides, - }: InstallValidationParams) => { - if ( - validationConfig.entityId === 0 && - validationConfig.moduleAddress !== zeroAddress - ) { - throw new EntityIdOverrideError(); - } - - const callData = await maV2Account.encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - ...maV2Account, - overrides, - }); - }; - - const uninstallValidation = async ({ - moduleAddress, - entityId, - uninstallData, - hookUninstallDatas, - overrides, - }: UninstallValidationParams) => { - const callData = await maV2Account.encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - ...maV2Account, - overrides, - }); - }; - return { ...client, - installValidation, - uninstallValidation, + encodeCallData: maV2Account.encodeCallData, }; } From d0e8782b2db07a67da250e6643323be3f54c8ece Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:48:19 -0500 Subject: [PATCH 44/45] fix: move install validation to the client --- .../install-validation/installValidation.ts | 169 ------------------ .../src/ma-v2/client/client.test.ts | 16 +- .../src/ma-v2/client/client.ts | 86 ++++++++- 3 files changed, 85 insertions(+), 186 deletions(-) delete mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts deleted file mode 100644 index 51d5576d32..0000000000 --- a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { - AccountNotFoundError, - IncompatibleClientError, - isSmartAccountClient, - EntityIdOverrideError, - type GetAccountParameter, - type GetEntryPointFromAccount, - type SendUserOperationResult, - type SmartAccountClient, - type UserOperationOverridesParameter, - type SmartContractAccount, -} from "@aa-sdk/core"; -import { - type Address, - type Hex, - type Chain, - type Transport, - encodeFunctionData, - concatHex, -} from "viem"; - -import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; -import type { HookConfig, ValidationConfig } from "../common/types.js"; -import { - serializeValidationConfig, - serializeHookConfig, - serializeModuleEntity, -} from "../common/utils.js"; -import type { CalldataEncoder } from "../../account/semiModularAccountV2.js"; - -export type InstallValidationParams< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined -> = { - validationConfig: ValidationConfig; - selectors: Hex[]; - installData: Hex; - hooks: { - hookConfig: HookConfig; - initData: Hex; - }[]; -} & UserOperationOverridesParameter> & - GetAccountParameter; - -export type UninstallValidationParams< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined -> = { - moduleAddress: Address; - entityId: number; - uninstallData: Hex; - hookUninstallDatas: Hex[]; -} & UserOperationOverridesParameter> & - GetAccountParameter; - -export type InstallValidationActions< - TAccount extends SmartContractAccount | undefined = - | SmartContractAccount - | undefined -> = { - installValidation: ( - args: InstallValidationParams - ) => Promise; - uninstallValidation: ( - args: UninstallValidationParams - ) => Promise; -}; - -export const installValidationActions: < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends SmartContractAccount = SmartContractAccount ->( - client: SmartAccountClient & CalldataEncoder -) => InstallValidationActions = (client) => ({ - installValidation: async ({ - validationConfig, - selectors, - installData, - hooks, - account = client.account, - overrides, - }) => { - if (!account) { - throw new AccountNotFoundError(); - } - - if (!isSmartAccountClient(client)) { - throw new IncompatibleClientError( - "SmartAccountClient", - "installValidation", - client - ); - } - - if (validationConfig.entityId === 0) { - throw new EntityIdOverrideError(); - } - - const { encodeCallData } = client; - - const callData = await encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - account, - overrides, - }); - }, - - uninstallValidation: async ({ - moduleAddress, - entityId, - uninstallData, - hookUninstallDatas, - account = client.account, - overrides, - }) => { - if (!account) { - throw new AccountNotFoundError(); - } - - if (!isSmartAccountClient(client)) { - throw new IncompatibleClientError( - "SmartAccountClient", - "uninstallValidation", - client - ); - } - - const { encodeCallData } = client; - - const callData = await encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - account, - overrides, - }); - }, -}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 611a74764c..4995a7f4ac 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -4,17 +4,19 @@ import { LocalAccountSigner, type SmartAccountSigner, type SmartAccountClient, + erc7677Middleware, } from "@aa-sdk/core"; -import { createSMAV2AccountClient } from "./client.js"; +import { + createSMAV2AccountClient, + type InstallValidationActions, +} from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; -import { installValidationActions } from "../actions/install-validation/installValidation.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; -import { type CalldataEncoder } from "../account/semiModularAccountV2.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; @@ -67,9 +69,7 @@ describe("MA v2 Tests", async () => { }); it("adds a session key with no permissions", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); + let provider = await givenConnectedProvider({ signer }); await setBalance(client, { address: provider.getAddress(), @@ -125,9 +125,7 @@ describe("MA v2 Tests", async () => { }); it("uninstalls a session key", async () => { - let provider = (await givenConnectedProvider({ signer })).extend( - installValidationActions - ); + let provider = await givenConnectedProvider({ signer }); await setBalance(client, { address: provider.getAddress(), diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index fdb4182955..ada7e25342 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -9,12 +9,13 @@ import { type SendUserOperationResult, } from "@aa-sdk/core"; import { - type Address, type Chain, type CustomTransport, - type Hex, type Transport, + type Hex, + type Address, } from "viem"; +import { concatHex, encodeFunctionData } from "viem"; import { createSMAV2Account, @@ -30,9 +31,14 @@ import { serializeModuleEntity, } from "../actions/common/utils.js"; -export type SMAV2AccountClient< - TSigner extends SmartAccountSigner = SmartAccountSigner -> = SmartAccountClient>; +import type { HookConfig, ValidationConfig } from "../actions/common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, + serializeModuleEntity, +} from "../actions/common/utils.js"; + +import { semiModularAccountBytecodeAbi } from "../abis/semiModularAccountBytecodeAbi.js"; export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, @@ -77,7 +83,8 @@ export function createSMAV2AccountClient< >( args: CreateSMAV2AccountClientParams ): Promise< - SmartAccountClient & TCalldataEncoder + SmartAccountClient & + InstallValidationActions >; /** @@ -112,7 +119,7 @@ export function createSMAV2AccountClient< export async function createSMAV2AccountClient({ ...config }: CreateSMAV2AccountClientParams): Promise< - SmartAccountClient & CalldataEncoder + SmartAccountClient & InstallValidationActions > { const maV2Account = await createSMAV2Account({ ...config, @@ -123,8 +130,71 @@ export async function createSMAV2AccountClient({ account: maV2Account, }); + const installValidation = async ({ + validationConfig, + selectors, + installData, + hooks, + overrides, + }: InstallValidationParams) => { + if (validationConfig.entityId === 0) { + throw new EntityIdOverrideError(); + } + + const callData = await maV2Account.encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + ...maV2Account, + overrides, + }); + }; + + const uninstallValidation = async ({ + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, + overrides, + }: UninstallValidationParams) => { + const callData = await maV2Account.encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + ...maV2Account, + overrides, + }); + }; + return { ...client, - encodeCallData: maV2Account.encodeCallData, + installValidation, + uninstallValidation, }; } From 93f0d9801a74933d36e28e09499e34db951b2deb Mon Sep 17 00:00:00 2001 From: linna Date: Fri, 20 Dec 2024 13:43:03 -0500 Subject: [PATCH 45/45] chore: more rebase changes --- aa-sdk/core/src/errors/client.ts | 32 --- .../src/ma-v2/account/account.ts | 157 --------------- .../src/ma-v2/account/semiModularAccountV2.ts | 94 ++------- .../install-validation/installValidation.ts | 159 +++++++++++++++ .../src/ma-v2/client/client.test.ts | 182 ++++++++++++++++-- .../src/ma-v2/client/client.ts | 136 ++----------- .../smart-contracts/src/ma-v2/utils.ts | 112 ++++++++--- 7 files changed, 449 insertions(+), 423 deletions(-) delete mode 100644 account-kit/smart-contracts/src/ma-v2/account/account.ts create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts diff --git a/aa-sdk/core/src/errors/client.ts b/aa-sdk/core/src/errors/client.ts index a773561333..c24cd64a2d 100644 --- a/aa-sdk/core/src/errors/client.ts +++ b/aa-sdk/core/src/errors/client.ts @@ -101,35 +101,3 @@ export class EntityIdOverrideError extends BaseError { ); } } - -/** - * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. - */ -export class EntityIdOverrideError extends BaseError { - override name = "InvalidNonceKeyError"; - - /** - * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. - */ - constructor() { - super( - `Installing entityId of 0 overrides the owner's entity id in the account` - ); - } -} - -/** - * Error class denoting that the provided entity id is invalid because it's overriding the native entity id. - */ -export class EntityIdOverrideError extends BaseError { - override name = "InvalidNonceKeyError"; - - /** - * Initializes a new instance of the error message with a default message indicating that the nonce key is invalid. - */ - constructor() { - super( - `Installing entityId of 0 overrides the owner's entity id in the account` - ); - } -} diff --git a/account-kit/smart-contracts/src/ma-v2/account/account.ts b/account-kit/smart-contracts/src/ma-v2/account/account.ts deleted file mode 100644 index 1f9cce1c1b..0000000000 --- a/account-kit/smart-contracts/src/ma-v2/account/account.ts +++ /dev/null @@ -1,157 +0,0 @@ -import type { - EntryPointDef, - SmartAccountSigner, - SmartContractAccountWithSigner, - ToSmartContractAccountParams, -} from "@aa-sdk/core"; -import { - createBundlerClient, - getEntryPoint, - toSmartContractAccount, - getAccountAddress, -} from "@aa-sdk/core"; -import { - concatHex, - encodeFunctionData, - getContract, - maxUint32, - toHex, - type Address, - type Chain, - type Hex, - type Transport, - hexToBigInt, -} from "viem"; -import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; -import { addresses } from "../utils.js"; -import { standardExecutor } from "../../msca/account/standardExecutor.js"; -import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; -import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; - -export const DEFAULT_OWNER_ENTITY_ID = 0; - -export type SMAV2Account< - TSigner extends SmartAccountSigner = SmartAccountSigner -> = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0">; - -export type CreateSMAV2AccountParams< - TTransport extends Transport = Transport, - TSigner extends SmartAccountSigner = SmartAccountSigner -> = Pick< - ToSmartContractAccountParams<"SMAV2Account", TTransport, Chain, "0.7.0">, - "transport" | "chain" | "accountAddress" -> & { - signer: TSigner; - salt?: bigint; - factoryAddress?: Address; - initCode?: Hex; - initialOwner?: Address; - entryPoint?: EntryPointDef<"0.7.0", Chain>; -} & ( - | { - isGlobalValidation: boolean; - entityId: bigint; - } - | { - isGlobalValidation: never; - entityId: never; - } - ); - -export async function createSMAV2Account< - TTransport extends Transport = Transport, - TSigner extends SmartAccountSigner = SmartAccountSigner ->( - config: CreateSMAV2AccountParams -): Promise>; - -export async function createSMAV2Account( - config: CreateSMAV2AccountParams -): Promise { - const { - transport, - chain, - signer, - salt = 0n, - factoryAddress = addresses.accountFactory, - initCode, - initialOwner, - accountAddress, - entryPoint = getEntryPoint(chain, { version: "0.7.0" }), - isGlobalValidation = true, - entityId = 0n, - } = config; - - if (entityId >= maxUint32) { - throw new InvalidEntityIdError(entityId); - } - - const client = createBundlerClient({ - transport, - chain, - }); - - const getAccountInitCode = async () => { - if (initCode) { - return initCode; - } - - // If an initial owner is not provided, use the signer's address - const ownerAddress = initialOwner ?? (await signer.getAddress()); - - return concatHex([ - factoryAddress, - encodeFunctionData({ - abi: accountFactoryAbi, - functionName: "createSemiModularAccount", - args: [ownerAddress, salt], - }), - ]); - }; - - const baseAccount = await toSmartContractAccount({ - transport, - chain, - entryPoint, - accountAddress, - source: `SMAV2Account`, - getAccountInitCode, - ...standardExecutor, - ...singleSignerMessageSigner(signer), - }); - - const accountAddress_ = await getAccountAddress({ - client, - entryPoint, - accountAddress, - getAccountInitCode, - }); - - // TODO: add deferred action flag - const getAccountNonce = async (nonceKey?: bigint): Promise => { - const nonceKeySuffix: Hex = `${toHex(entityId, { size: 4 })}${ - isGlobalValidation ? "01" : "00" - }`; - - if (nonceKey && toHex(nonceKey, { size: 5 }) !== nonceKeySuffix) { - throw new InvalidNonceKeyError(nonceKey, hexToBigInt(nonceKeySuffix)); - } - - const entryPointContract = getContract({ - address: entryPoint.address, - abi: entryPoint.abi, - client, - }); - - return entryPointContract.read.getNonce([ - accountAddress_, - nonceKey ?? hexToBigInt(nonceKeySuffix), - ]) as Promise; - }; - - return { - ...baseAccount, - getAccountNonce, - getSigner: () => signer, - }; -} diff --git a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts index b5b986c000..7282c3424f 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/semiModularAccountV2.ts @@ -9,7 +9,6 @@ import { createBundlerClient, getEntryPoint, toSmartContractAccount, - getAccountAddress, InvalidEntityIdError, InvalidNonceKeyError, getAccountAddress, @@ -25,8 +24,6 @@ import { type Chain, type Hex, type Transport, - hexToBigInt, - zeroAddress, } from "viem"; import { accountFactoryAbi } from "../abis/accountFactoryAbi.js"; import { @@ -34,14 +31,11 @@ import { DEFAULT_OWNER_ENTITY_ID, } from "../utils.js"; import { singleSignerMessageSigner } from "../modules/single-signer-validation/signer.js"; -import { InvalidEntityIdError, InvalidNonceKeyError } from "@aa-sdk/core"; import { modularAccountAbi } from "../abis/modularAccountAbi.js"; import { serializeModuleEntity } from "../actions/common/utils.js"; const executeUserOpSelector: Hex = "0x8DD7712F"; -const executeUserOpSelector: Hex = "0x8DD7712F"; - export type SignerEntity = { isGlobalValidation: boolean; entityId: number; @@ -71,38 +65,15 @@ export type ValidationDataParams = entityId: number; }; -export type CalldataEncoder = { - encodeCallData: (callData: Hex) => Promise; -}; - -export type ExecutionDataView = { - module: Address; - skipRuntimeValidation: boolean; - allowGlobalValidation: boolean; - executionHooks: readonly Hex[]; -}; - -export type ValidationDataView = { - validationHooks: readonly Hex[]; - executionHooks: readonly Hex[]; - selectors: readonly Hex[]; - validationFlags: number; -}; - -export type ValidationDataParams = { - validationModuleAddress?: Address; - entityId?: number; -}; - export type SMAV2Account< - TSigner extends SmartAccountSigner = SmartAccountSigner, - TCalldataEncoder extends CalldataEncoder = CalldataEncoder + TSigner extends SmartAccountSigner = SmartAccountSigner > = SmartContractAccountWithSigner<"SMAV2Account", TSigner, "0.7.0"> & - TCalldataEncoder & { + SignerEntity & { getExecutionData: (selector: Hex) => Promise; getValidationData: ( args: ValidationDataParams ) => Promise; + encodeCallData: (callData: Hex) => Promise; }; export type CreateSMAV2AccountParams< @@ -131,11 +102,11 @@ export async function createSMAV2Account< TSigner extends SmartAccountSigner = SmartAccountSigner >( config: CreateSMAV2AccountParams -): Promise & CalldataEncoder>; +): Promise>; export async function createSMAV2Account( config: CreateSMAV2AccountParams -): Promise { +): Promise { const { transport, chain, @@ -255,7 +226,12 @@ export async function createSMAV2Account( const getExecutionData = async (selector: Hex) => { if (!(await baseAccount.isAccountDeployed())) { - return {} as ExecutionDataView; + return { + module: zeroAddress, + skipRuntimeValidation: false, + allowGlobalValidation: false, + executionHooks: [], + }; } return await accountContract.read.getExecutionData([selector]); @@ -263,7 +239,12 @@ export async function createSMAV2Account( const getValidationData = async (args: ValidationDataParams) => { if (!(await baseAccount.isAccountDeployed())) { - return {} as ValidationDataView; + return { + validationHooks: [], + executionHooks: [], + selectors: [], + validationFlags: 0, + }; } const { validationModuleAddress, entityId } = args; @@ -280,52 +261,19 @@ export async function createSMAV2Account( entityId: Number(entityId), }); - const numHooks = validationData?.executionHooks?.length ?? 0; - - return numHooks ? concatHex([executeUserOpSelector, callData]) : callData; - }; - - const encodeExecute: (tx: AccountOp) => Promise = async ({ - target, - data, - value, - }) => { - let callData = encodeFunctionData({ - abi: modularAccountAbi, - functionName: "execute", - args: [target, value ?? 0n, data], - }); - - callData = (await baseAccount.isAccountDeployed()) - ? await encodeCallData(callData) + return validationData.executionHooks.length + ? concatHex([executeUserOpSelector, callData]) : callData; - - return callData; }; - const encodeBatchExecute: (txs: AccountOp[]) => Promise = async (txs) => - encodeCallData( - encodeFunctionData({ - abi: modularAccountAbi, - functionName: "executeBatch", - args: [ - txs.map((tx) => ({ - target: tx.target, - data: tx.data, - value: tx.value ?? 0n, - })), - ], - }) - ); - return { ...baseAccount, getAccountNonce, getSigner: () => signer, + isGlobalValidation, + entityId, getExecutionData, getValidationData, - encodeExecute, - encodeBatchExecute, encodeCallData, }; } diff --git a/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts new file mode 100644 index 0000000000..7d9edf589c --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/install-validation/installValidation.ts @@ -0,0 +1,159 @@ +import { + AccountNotFoundError, + IncompatibleClientError, + isSmartAccountClient, + EntityIdOverrideError, + type GetEntryPointFromAccount, + type SendUserOperationResult, + type UserOperationOverridesParameter, + type SmartAccountSigner, +} from "@aa-sdk/core"; +import { type Address, type Hex, encodeFunctionData, concatHex } from "viem"; + +import { semiModularAccountBytecodeAbi } from "../../abis/semiModularAccountBytecodeAbi.js"; +import type { HookConfig, ValidationConfig } from "../common/types.js"; +import { + serializeValidationConfig, + serializeHookConfig, + serializeModuleEntity, +} from "../common/utils.js"; + +import { type SMAV2AccountClient } from "../../client/client.js"; +import { type SMAV2Account } from "../../account/semiModularAccountV2.js"; +import { DEFAULT_OWNER_ENTITY_ID } from "../../utils.js"; + +export type InstallValidationParams< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + validationConfig: ValidationConfig; + selectors: Hex[]; + installData: Hex; + hooks: { + hookConfig: HookConfig; + initData: Hex; + }[]; + account?: SMAV2Account | undefined; +} & UserOperationOverridesParameter< + GetEntryPointFromAccount> +>; + +export type UninstallValidationParams< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + moduleAddress: Address; + entityId: number; + uninstallData: Hex; + hookUninstallDatas: Hex[]; + account?: SMAV2Account | undefined; +} & UserOperationOverridesParameter< + GetEntryPointFromAccount> +>; + +export type InstallValidationActions< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = { + installValidation: ( + args: InstallValidationParams + ) => Promise; + uninstallValidation: ( + args: UninstallValidationParams + ) => Promise; +}; + +export const installValidationActions: < + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + client: SMAV2AccountClient +) => InstallValidationActions = (client) => ({ + installValidation: async ({ + validationConfig, + selectors, + installData, + hooks, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "installValidation", + client + ); + } + + if (validationConfig.entityId === DEFAULT_OWNER_ENTITY_ID) { + throw new EntityIdOverrideError(); + } + + const { encodeCallData } = account; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "installValidation", + args: [ + serializeValidationConfig(validationConfig), + selectors, + installData, + hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => + concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) + ), + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, + + uninstallValidation: async ({ + moduleAddress, + entityId, + uninstallData, + hookUninstallDatas, + account = client.account, + overrides, + }) => { + if (!account) { + throw new AccountNotFoundError(); + } + + if (!isSmartAccountClient(client)) { + throw new IncompatibleClientError( + "SmartAccountClient", + "uninstallValidation", + client + ); + } + + const { encodeCallData } = account; + + const callData = await encodeCallData( + encodeFunctionData({ + abi: semiModularAccountBytecodeAbi, + functionName: "uninstallValidation", + args: [ + serializeModuleEntity({ + moduleAddress, + entityId, + }), + uninstallData, + hookUninstallDatas, + ], + }) + ); + + return client.sendUserOperation({ + uo: callData, + account, + overrides, + }); + }, +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 4995a7f4ac..3a7f91df88 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -1,26 +1,21 @@ import { custom, parseEther, publicActions } from "viem"; - import { + erc7677Middleware, LocalAccountSigner, type SmartAccountSigner, - type SmartAccountClient, - erc7677Middleware, } from "@aa-sdk/core"; - -import { - createSMAV2AccountClient, - type InstallValidationActions, -} from "./client.js"; - +import { createSMAV2AccountClient, type SMAV2AccountClient } from "./client.js"; import { local070Instance } from "~test/instances.js"; import { setBalance } from "viem/actions"; import { accounts } from "~test/constants.js"; import { getDefaultSingleSignerValidationModuleAddress } from "../modules/utils.js"; import { SingleSignerValidationModule } from "../modules/single-signer-validation/module.js"; +import { installValidationActions } from "../actions/install-validation/installValidation.js"; describe("MA v2 Tests", async () => { const instance = local070Instance; - let client: ReturnType; + let client: ReturnType & + ReturnType; beforeAll(async () => { client = instance.getClient().extend(publicActions); @@ -60,16 +55,17 @@ describe("MA v2 Tests", async () => { }, }); - const txnHash1 = provider.waitForUserOperationTransaction(result); - await expect(txnHash1).resolves.not.toThrowError(); + await provider.waitForUserOperationTransaction(result); - await expect(await getTargetBalance()).toEqual( + await expect(getTargetBalance()).resolves.toEqual( startingAddressBalance + sendAmount ); }); it("adds a session key with no permissions", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); await setBalance(client, { address: provider.getAddress(), @@ -119,13 +115,15 @@ describe("MA v2 Tests", async () => { txnHash = sessionKeyClient.waitForUserOperationTransaction(result); await expect(txnHash).resolves.not.toThrowError(); - await expect(await getTargetBalance()).toEqual( + await expect(getTargetBalance()).resolves.toEqual( startingAddressBalance + sendAmount ); }); it("uninstalls a session key", async () => { - let provider = await givenConnectedProvider({ signer }); + let provider = (await givenConnectedProvider({ signer })).extend( + installValidationActions + ); await setBalance(client, { address: provider.getAddress(), @@ -188,6 +186,158 @@ describe("MA v2 Tests", async () => { ).rejects.toThrowError(); }); + // it("installs paymaster guard module, verifies use of valid paymaster, then uninstalls module", async () => { + // let provider = await givenConnectedProvider({ signer, usePaymaster: true }); + + // await setBalance(client, { + // address: provider.getAddress(), + // value: parseEther("2"), + // }); + + // const paymaster = paymaster070.getPaymasterStubData(); + + // const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ + // entityId: 0, + // paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE + // }); + + // const installResult = await provider.installValidation({ + // validationConfig: { + // moduleAddress: zeroAddress, + // entityId: 0, + // isGlobal: true, + // isSignatureValidation: true, + // isUserOpValidation: true, + // }, + // selectors: [], + // installData: "0x", + // hooks: [ + // { + // hookConfig: { + // address: getDefaultPaymasterGuardModuleAddress(provider.chain), + // entityId: 0, // uint32 + // hookType: HookType.VALIDATION, + // hasPreHooks: true, + // hasPostHooks: true, + // }, + // initData: hookInstallData, + // }, + // ], + // }); + + // // verify hook installtion succeeded + // await expect( + // provider.waitForUserOperationTransaction(installResult) + // ).resolves.not.toThrowError(); + + // // happy path: with correct paymaster + // const result = await provider.sendUserOperation({ + // uo: { + // target: target, + // value: sendAmount, + // data: "0x", + // }, + // }); + + // // verify if correct paymaster is used + // const txnHash1 = provider.waitForUserOperationTransaction(result); + // await expect(txnHash1).resolves.not.toThrowError(); + + // const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ + // entityId: 0, + // }); + + // const uninstallResult = await provider.uninstallValidation({ + // moduleAddress: zeroAddress, + // entityId: 0, + // uninstallData: "0x", + // hookUninstallDatas: [hookUninstallData], + // }); + + // // verify uninstall + // await expect( + // provider.waitForUserOperationTransaction(uninstallResult) + // ).resolves.not.toThrowError(); + // }); + + // it("installs paymaster guard module, verifies use of invalid paymaster, then uninstalls module", async () => { + // let provider = await givenConnectedProvider({ signer, usePaymaster: true }); + + // await setBalance(client, { + // address: provider.getAddress(), + // value: parseEther("2"), + // }); + + // const paymaster = paymaster070.getPaymasterStubData(); + + // const hookInstallData = PaymasterGuardModule.encodeOnInstallData({ + // entityId: 0, + // paymaster: "paymaster" in paymaster ? paymaster.paymaster : "0x0", // dummy value for paymaster address if it DNE + // }); + + // const installResult = await provider.installValidation({ + // validationConfig: { + // moduleAddress: zeroAddress, + // entityId: 0, + // isGlobal: true, + // isSignatureValidation: true, + // isUserOpValidation: true, + // }, + // selectors: [], + // installData: "0x", + // hooks: [ + // { + // hookConfig: { + // address: getDefaultPaymasterGuardModuleAddress(provider.chain), + // entityId: 0, // uint32 + // hookType: HookType.VALIDATION, + // hasPreHooks: true, + // hasPostHooks: true, + // }, + // initData: hookInstallData, + // }, + // ], + // }); + + // // verify hook installtion succeeded + // await expect( + // provider.waitForUserOperationTransaction(installResult) + // ).resolves.not.toThrowError(); + + // // sad path: with wrong paymaster + // let providerNoPaymaster = await givenConnectedProvider({ + // signer, + // usePaymaster: false, + // }); + + // // TO DO: verify if correct paymaster is used + // await expect( + // providerNoPaymaster.sendUserOperation({ + // uo: { + // target: target, + // value: sendAmount, + // data: "0x", + // }, + // }) + // ).rejects.toThrowError(); + + // const hookUninstallData = PaymasterGuardModule.encodeOnUninstallData({ + // entityId: 0, + // }); + + // const uninstallResult = await provider.uninstallValidation({ + // moduleAddress: zeroAddress, + // entityId: 0, + // uninstallData: "0x", + // hookUninstallDatas: [hookUninstallData], + // }); + + // // verify uninstall + // await expect( + // provider.waitForUserOperationTransaction(uninstallResult) + // ).resolves.not.toThrowError(); + // }); + const givenConnectedProvider = async ({ signer, accountAddress, diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.ts b/account-kit/smart-contracts/src/ma-v2/client/client.ts index ada7e25342..e413f83f30 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.ts @@ -1,25 +1,20 @@ import { createSmartAccountClient, - EntityIdOverrideError, type SmartAccountClient, type SmartAccountSigner, type SmartAccountClientConfig, - type UserOperationOverridesParameter, - type GetEntryPointFromAccount, - type SendUserOperationResult, + EntityIdOverrideError, } from "@aa-sdk/core"; import { + concatHex, + encodeFunctionData, type Chain, - type CustomTransport, - type Transport, type Hex, - type Address, + type Transport, } from "viem"; -import { concatHex, encodeFunctionData } from "viem"; import { createSMAV2Account, - type CalldataEncoder, type CreateSMAV2AccountParams, type SMAV2Account, } from "../account/semiModularAccountV2.js"; @@ -30,15 +25,14 @@ import { serializeHookConfig, serializeModuleEntity, } from "../actions/common/utils.js"; +import type { + InstallValidationParams, + UninstallValidationParams, +} from "../actions/install-validation/installValidation.js"; -import type { HookConfig, ValidationConfig } from "../actions/common/types.js"; -import { - serializeValidationConfig, - serializeHookConfig, - serializeModuleEntity, -} from "../actions/common/utils.js"; - -import { semiModularAccountBytecodeAbi } from "../abis/semiModularAccountBytecodeAbi.js"; +export type SMAV2AccountClient< + TSigner extends SmartAccountSigner = SmartAccountSigner +> = SmartAccountClient>; export type CreateSMAV2AccountClientParams< TTransport extends Transport = Transport, @@ -50,42 +44,12 @@ export type CreateSMAV2AccountClientParams< "transport" | "account" | "chain" >; -export type InstallValidationActions = { - installValidation: ( - args: InstallValidationParams - ) => Promise; - uninstallValidation: ( - args: UninstallValidationParams - ) => Promise; -}; - -export type InstallValidationParams = { - validationConfig: ValidationConfig; - selectors: Hex[]; - installData: Hex; - hooks: { - hookConfig: HookConfig; - initData: Hex; - }[]; -} & UserOperationOverridesParameter>; - -export type UninstallValidationParams = { - moduleAddress: Address; - entityId: number; - uninstallData: Hex; - hookUninstallDatas: Hex[]; -} & UserOperationOverridesParameter>; - export function createSMAV2AccountClient< TChain extends Chain = Chain, - TSigner extends SmartAccountSigner = SmartAccountSigner, - TCalldataEncoder extends CalldataEncoder = CalldataEncoder + TSigner extends SmartAccountSigner = SmartAccountSigner >( args: CreateSMAV2AccountClientParams -): Promise< - SmartAccountClient & - InstallValidationActions ->; +): Promise>; /** * Creates a MAv2 account client using the provided configuration parameters. @@ -118,83 +82,13 @@ export function createSMAV2AccountClient< */ export async function createSMAV2AccountClient({ ...config -}: CreateSMAV2AccountClientParams): Promise< - SmartAccountClient & InstallValidationActions -> { +}: CreateSMAV2AccountClientParams): Promise { const maV2Account = await createSMAV2Account({ ...config, }); - const client = createSmartAccountClient({ + return createSmartAccountClient({ ...config, account: maV2Account, }); - - const installValidation = async ({ - validationConfig, - selectors, - installData, - hooks, - overrides, - }: InstallValidationParams) => { - if (validationConfig.entityId === 0) { - throw new EntityIdOverrideError(); - } - - const callData = await maV2Account.encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "installValidation", - args: [ - serializeValidationConfig(validationConfig), - selectors, - installData, - hooks.map((hook: { hookConfig: HookConfig; initData: Hex }) => - concatHex([serializeHookConfig(hook.hookConfig), hook.initData]) - ), - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - ...maV2Account, - overrides, - }); - }; - - const uninstallValidation = async ({ - moduleAddress, - entityId, - uninstallData, - hookUninstallDatas, - overrides, - }: UninstallValidationParams) => { - const callData = await maV2Account.encodeCallData( - encodeFunctionData({ - abi: semiModularAccountBytecodeAbi, - functionName: "uninstallValidation", - args: [ - serializeModuleEntity({ - moduleAddress, - entityId, - }), - uninstallData, - hookUninstallDatas, - ], - }) - ); - - return client.sendUserOperation({ - uo: callData, - ...maV2Account, - overrides, - }); - }; - - return { - ...client, - installValidation, - uninstallValidation, - }; } diff --git a/account-kit/smart-contracts/src/ma-v2/utils.ts b/account-kit/smart-contracts/src/ma-v2/utils.ts index 3e3940cf12..73e241b70b 100644 --- a/account-kit/smart-contracts/src/ma-v2/utils.ts +++ b/account-kit/smart-contracts/src/ma-v2/utils.ts @@ -27,28 +27,92 @@ export const packSignature = ({ return concat(["0xFF", "0x00", validationSignature]); }; -export const addresses = { - allowlistModule: - "0xe768abEe3ad779Be1318388fC5D6A6fCf658011B" as `0x${string}`, - nativeTokenLimitModule: - "0xbFD077C53ec21f91699cF2DBab5324ea608b4eF0" as `0x${string}`, - paymasterGuardModule: - "0x97018B224C969A1992076293f15482FB9982A271" as `0x${string}`, - singleSignerValidationModule: - "0x2a42a36ee6DC9E8f75d2f8B5ef621EE8F2bD3156" as `0x${string}`, - timeRangeModule: - "0x335a66470B5052DD9F540bD64ca6b7dE205F6f0B" as `0x${string}`, - webauthnValidationModule: - "0x52bB58A05659F2fF4bDb57E602824859De11119A" as `0x${string}`, - executionInstallDelegate: - "0x8Bf909fEb66EBcC4725f04E70F319791Ec9d9628" as `0x${string}`, - modularAccountImpl: - "0x7219030794F2937ff4A322B7CE9a58C070aF08C5" as `0x${string}`, - semiModularAccountBytecodeImpl: - "0xc6176BeF7E32224238ef3A2Ee3F9BaCfA801Cc95" as `0x${string}`, - semiModularAccountStorageOnlyImpl: - "0x8bD01f353058513fE5968C80585dc0792f56961b" as `0x${string}`, - accountFactory: "0x52fd2B39bd2a2c411371514114f9a1b3F9Ba3a64" as `0x${string}`, - accountFactoryOwner: - "0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C" as `0x${string}`, +export const getDefaultMAV2FactoryAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000017c61b5bEe81050EC8eFc9c6fecd"; + } +}; + +export const getDefaultSMAV2BytecodeAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x000000000000c5A9089039570Dd36455b5C07383"; + } +}; + +export const getDefaultSMAV2StorageAddress = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x0000000000006E2f9d80CaEc0Da6500f005EB25A"; + } +}; + +export const getDefaultSMAV27702Address = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x69007702764179f14F51cdce752f4f775d74E139"; + } +}; + +export const getDefaultMAV2Address = (chain: Chain): Address => { + switch (chain.id) { + // TODO: case mekong.id: + case sepolia.id: + case baseSepolia.id: + case polygon.id: + case mainnet.id: + case polygonAmoy.id: + case optimism.id: + case optimismSepolia.id: + case arbitrum.id: + case arbitrumSepolia.id: + case base.id: + default: + return "0x00000000000002377B26b1EdA7b0BC371C60DD4f"; + } };