The Secure Web3 Operating System for macOS.
Enclave is a native macOS application that fundamentally redesigns how users interact with Web3. Rather than forcing standard Ethereum cryptography (secp256k1) into vulnerable Web2 browser extensions, Enclave leverages Apple's physical Secure Enclave (secp256r1) and Account Abstraction (ERC-4337) to turn your Mac into a literal hardware wallet.
Private keys are generated in silicon, physically cannot be extracted by malware, and are mathematically verified on-chain via modern Layer-2 precompiles (RIP-7212).
The current standard of injecting a global window.ethereum JavaScript object into a standard web browser is fundamentally broken. Users lose billions to front-end compromises, DNS hijacking, poisoned libraries, and malicious browser extensions.
Enclave is built strictly on two uncompromising principles:
- True Hardware Isolation: Keys never leave the hardware. Your private key is physically bound to your Mac's motherboard and biometric sensor. Transactions are signed strictly via Touch ID.
- The Walled Garden: Enclave abandons the browser extension model entirely. dApps run inside isolated, sandboxed native views (
WKWebView) within the wallet application. - No JS Injection:
window.ethereumdoes not exist here. dApps communicate throughwindow.enclaveβ an EIP-1193-like provider with per-app permissions and session expiry, injected at document start viaWKScriptMessageHandler. - Immutable dApps: dApps are explicitly versioned, loaded strictly via IPFS, and must be cryptographically signed by verified developers.
- Transaction Previews: Every transaction is decoded into human-readable actions and shown for approval before Touch ID appears. No blind signing.
- On-Chain Spending Limits: Per-token daily limits enforced at the smart contract level, auto-resetting every 24 hours.
- Native Speed: Optimistic UI updates make Web3 feel like a centralized service. When Touch ID succeeds, balances update instantly while the Bundler handles blockchain settlement seamlessly in the background.
- Gas Abstraction: Users never need to hold native ETH to pay for network fees. Enclave uses ERC-4337 Paymasters to sponsor gas entirely or allow payments in USDC.
- Atomic Batching: Approve a token and execute a swap in a single Touch ID confirmation via
executeBatch. - Human-Readable Previews: Unified transaction previews ensure you know exactly what is happening before the biometric prompt appears (e.g., "π’ Receive 3,000 USDC, π΄ Pay 1 ETH"). No more blind signing hex strings.
- Network Switching: Switch between Arbitrum Sepolia and Arbitrum One from the balance bar.
Enclave bridges Apple's proprietary silicon to the Ethereum Virtual Machine (EVM) by orchestrating three isolated layers. Targeted at Arbitrum L2, which natively supports the RIP-7212 precompile at address 0x0100 for cheap P-256 signature verification.
| File | Purpose |
|---|---|
EnclaveEngine.swift |
Secure Enclave key management, P-256 signing, CREATE2 address computation |
RPC.swift |
JSON-RPC client (balance, nonce, gasPrice, getCode, eth_call) |
Bundler.swift |
ERC-4337 bundler API (sendUserOperation, estimateGas, paymaster) |
UserOperation.swift |
UserOp construction, hashing, execute/executeBatch calldata |
CalldataDecoder.swift |
Decode execute calldata into human-readable DecodedAction enum |
ProviderBridge.swift |
EIP-1193-like window.enclave provider for dApp communication |
AppPermissions.swift |
Per-dApp permission model with session expiry |
ContentView.swift |
Main UI: wallet selector, balance display, send with tx preview |
WalledGardenWebView.swift |
WebView with injected provider script |
Config.swift |
Network config, contract addresses, creation bytecode |
| File | Purpose |
|---|---|
EnclaveWallet.sol |
ERC-4337 account with P-256 verification, EIP-1271, daily spending limits |
EnclaveWalletFactory.sol |
CREATE2 deterministic deployment factory |
| Method | Touch ID? | Description |
|---|---|---|
eth_requestAccounts / eth_accounts |
No | Return connected wallet address |
eth_chainId |
No | Current chain ID |
personal_sign |
Yes | Sign an arbitrary message |
eth_signTypedData_v4 |
Yes | Sign EIP-712 typed data |
eth_sendTransaction |
Yes | Build UserOp, preview, sign, submit |
eth_getBalance / eth_call / eth_blockNumber / eth_estimateGas |
No | Proxied to RPC |
- Secure Enclave key generation and Touch ID signing
- CREATE2 counterfactual address computation
- EVM high-S malleability fix
- Multi-wallet support with Keychain persistence
- ERC-4337 UserOp construction, gas estimation, bundler submission
- Send/Receive UI with ETH and USDC
- Dynamic gas prices (eth_gasPrice, eth_maxPriorityFeePerGas)
- Deployment detection via eth_getCode
- Transaction calldata decoder (transfer, approve, execute, executeBatch)
- Transaction preview sheet with approve/reject before signing
- EIP-1271
isValidSignature(P-256 via RIP-7212) - On-chain daily spending limits with auto-reset
- EIP-1193-like provider bridge (
window.enclave) - Per-dApp permissions with session expiry
- executeBatch support for atomic multi-call
- Paymaster integration (pm_sponsorUserOperation)
- Network switching (Arbitrum Sepolia / One)
- Factory deployment to Arbitrum Sepolia
- Session keys for off-chain signing (secp256k1 + EIP-1271)
- secp256k1 EOA wallets for universal dApp compatibility
- IPFS dApp loading with content verification
- App manifest system
We welcome Swift engineers, Solidity developers, and cryptographers.
- macOS 14.0+ (Requires a physical Mac with Touch ID; Enclave cannot be fully simulated in a VM)
- Xcode 15.0+
- Foundry (for smart contract testing)
git clone https://github.com/Plether-fi/enclave.git
cd enclave
# Build
/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild \
-project EnclaveWallet.xcodeproj -scheme EnclaveWallet -configuration Release build
# Run
pkill -f EnclaveWallet; sleep 1 && open ~/Library/Developer/Xcode/DerivedData/EnclaveWallet-*/Build/Products/Release/EnclaveWallet.appOr open EnclaveWallet.xcodeproj in Xcode and hit Cmd + R.
CRITICAL: Go to Project Settings β Target β Signing & Capabilities. You must select your Apple Developer Team and add the Keychain Sharing capability. If you do not do this, CryptoKit will instantly crash due to access control restrictions when attempting to reach the Secure Enclave.
cd contracts
forge test -vv17 tests covering factory deployment, EIP-1271 signature validation, and spending limit enforcement.
cd contracts
PRIVATE_KEY=<funded-deployer-key> forge script script/Deploy.s.sol \
--rpc-url https://sepolia-rollup.arbitrum.io/rpc --broadcastAfter deployment, update Config.factoryAddress in Config.swift with the logged address, then run forge inspect EnclaveWallet bytecode and update Config.walletCreationCode.
The Kitchen Sink dApp exercises the provider bridge:
- Connect Wallet β returns your counterfactual address
- Get Chain ID / Balance / Block Number β read-only RPC calls proxied through Swift
- Sign Message β triggers Touch ID, returns P-256 signature
- Send 0.0001 ETH β builds a UserOp, shows transaction preview, signs, submits to bundler
If you are contributing to the cryptographic engine or the Smart Contract, please be aware of the following:
Apple's Secure Enclave does not care about Ethereum's strict malleability rules. It randomly generates signatures with an s value in the upper half of the curve (~50% of the time). The EVM strictly rejects high s values to prevent transaction replay attacks.
The Rule: In EnclaveEngine.swift, we capture the raw signature, check if s > N/2 using the BigInt library, and mathematically flip it (s = N - s). Do not alter this logic, or ~50% of Arbitrum transactions will mysteriously fail.
The Mac app does not send a standard transaction to "create" the wallet. It mathematically calculates what the address will be using the CREATE2 opcode. The wallet is natively deployed by the Arbitrum Bundler during the user's very first outbound transaction via the initCode field.
Enclave uses two distinct signing paths depending on what's being signed:
Transactions (UserOps) always require a P-256 Secure Enclave signature (Touch ID). Session keys cannot submit transactions β _validateSignature only accepts 64-byte P-256 signatures. This is the hard security boundary.
Off-chain signatures (personal_sign, eth_signTypedData_v4) use secp256k1 session keys verified via EIP-1271. The flow:
- dApp requests a signature (via WalletConnect or the JS bridge)
- Enclave generates a secp256k1 key per dApp, stored in Keychain
- If the key isn't registered on-chain yet, a UserOp calling
addSessionKey(address)is submitted (this is the only step requiring Touch ID) - The message hash is signed with the secp256k1 session key, producing a 65-byte signature (r + s + v)
- The dApp verifies via
isValidSignature(hash, sig)on the wallet contract, which callsecrecoverand checks thesessionKeysmapping
Subsequent signatures for the same dApp are instant β no Touch ID, no on-chain transaction. The contract's isValidSignature dispatches on signature length: 65 bytes β secp256k1 session key path, 64 bytes β P-256 owner key path.
Session keys are revocable by the owner via removeSessionKey(address) (also a UserOp). They can prove identity but never move funds.
EnclaveWallet.sol enforces per-token daily spending limits at the contract level. Limits are set by the wallet itself (via execute calling setDailyLimit), checked on every execute call for ETH value and ERC-20 transfer/approve amounts, and auto-reset every 24 hours based on block timestamp.
Enclave supports two fundamentally different wallet types, selectable when creating a new wallet:
P-256 Smart Wallet (Secure Enclave)
- Private key lives inside the Secure Enclave chip β physically cannot be extracted, even by malware with root access
- Uses ERC-4337 account abstraction: transactions are submitted as UserOperations signed with P-256 ECDSA, verified on-chain via the RIP-7212 precompile
- Address is a CREATE2 counterfactual address derived from the factory, public key coordinates, and a salt
- Requires on-chain deployment (automatically handled on first transaction)
- Off-chain signing uses per-dApp secp256k1 session keys registered on the wallet contract and verified via EIP-1271
- Supports batched transactions, paymaster gas sponsorship, and on-chain spending limits
secp256k1 Traditional Wallet (EOA)
- Standard Ethereum externally owned account, compatible with every dApp and protocol without exception
- Private key is generated in software and stored in macOS Keychain, which encrypts it using the Secure Enclave's hardware key β the key is protected by the same biometric/passcode policy but is not physically bound to the chip
- Address is derived the standard way:
keccak256(uncompressed_pubkey)[last 20 bytes] - Signs directly with
ecrecover-compatible 65-byte signatures (r + s + v) β no session keys, no EIP-1271, no on-chain registration needed - Off-chain signing is instant with no Touch ID prompt and no on-chain transactions
- Transaction sending (
eth_sendTransaction) is not yet supported for EOA wallets β currently limited to off-chain signing
Both wallet types coexist in the same wallet selector. Keys are stored in separate Keychain accounts (key.{index} for P-256, eoa.{index} for secp256k1) and wallet type metadata is persisted in UserDefaults.
Enclave is an open-source project. The following areas have been implemented or are actively looking for help:
Transaction Simulation Engine: Calldata decoder and transaction preview sheet now show human-readable actions before Touch ID appears.βBundler Networking: Full ERC-4337 UserOp construction, gas estimation, and bundler submission.βEIP-1271 Implementation:βisValidSignaturewith P-256 verification via RIP-7212 precompile.- IPFS dApp Resolver: Building the native Swift architecture to securely fetch, verify developer signatures, and load versioned HTML/JS payloads from IPFS directly into the Walled Garden.
Session Keys: secp256k1 session keys for off-chain signing, one per dApp, registered on-chain via UserOp, verified via EIP-1271.β- App Manifest System: Replace the hardcoded app list with manifest-driven dApp catalog.
This project is released under the MIT License.