permissionless 0.4.0
permissionless: ^0.4.0 copied to clipboard
A Dart implementation of permissionless.js for ERC-4337 smart accounts. Supports Safe, Simple, Kernel, and Etherspot accounts with bundler and paymaster clients.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.4.0 - 2026-07-12 #
Parity release: fixes every P0/P1 divergence found by the Dart-vs-JS parity audit and aligns behavior with permissionless.js across accounts, clients, and actions.
Breaking changes #
- Safe default threshold is now
owners.length(all owners must sign), matching permissionless.jstoSafeSmartAccount. Previously defaulted to1. Multi-owner Safes created without an explicitthresholdproduce a different setup calldata and therefore a different counterfactual address. Passthreshold: BigInt.oneto keep the previous 1-of-n default. - Thirdweb salt is now UTF-8-encoded (was hex-decoded), matching permissionless.js
toHex(salt). Any account created with a non-defaultsaltgets differentfactoryDataand a different counterfactual address. The old decoding was a parity bug — addresses it produced never matched the TypeScript SDK's. - ERC-1271
signMessage/signTypedDataoutputs changed across accounts (Light, Thirdweb, Trust, Etherspot, Biconomy, Kernel, Safe). Previous releases double-prefixed digests (EIP-712 wrapper digest signed viapersonal_sign), skipped validator prefixes (Kernel), or omitted the SafeMessage wrapper and eth_sign V adjustment (Safe) — producing signatures that failed on-chainisValidSignature. Signatures now byte-match permissionless.js. - ERC-7579
revertOnErrorexecution-mode bit polarity flipped to match the JS encoding; execution modes built withrevertOnErrornow encode the correctModeSelectorbyte. - ERC-7579 query actions (
supportsModule,supportsExecutionMode,isModuleInstalled,getAccountId) no longer swallow errors tofalse/''. On call failure they retry with the account'sfactory/factoryData(counterfactual path), matching permissionless.js; other errors propagate.
Added #
- PasskeyServerClient (
createPasskeyServerClient) — port of permissionless.jsclients/passkeyServer+actions/passkeyServer. Exposes all fivepks_*RPC methods:startRegistration,verifyRegistration,startAuthentication,verifyAuthentication, andgetCredentials. Placed in the corepermissionlesspackage for JS layout parity (client-side passkey creation remains inpermissionless_passkeys). PublicClient.calldeployless factory path — optionalfactory/factoryDataparameters perform a viem-style deploylesseth_callviadeploylessCallViaFactoryBytecode, enabling counterfactual ERC-7579 reads.BundlerRpcError.aaErrorDescription— short descriptions for known ERC-4337 AA## codes;toStringincludes the description when available.CallsStatus.parseCallsStatusCode— public helper matching viemgetCallsStatusnumeric ranges and legacy string statuses.
Changed #
- Safe batch
encodeCallsroutes through MultiSendCallOnly (not MultiSend), matching permissionless.js safety property (no inner delegatecalls). - Safe stub signatures byte-match permissionless.js (ECDSA ecrecover-path dummy and WebAuthn dummy with realistic authenticatorData / long clientDataFields).
writeContractnow encodes calldata via web3dart's ABI encoder instead of a hand-rolled encoder that mis-encoded dynamicbytesand lacked support for strings, arrays, and tuples.
Fixed #
-
Four wrong hard-coded ERC-7579 selectors (
utils/erc7579.dart):uninstallModule(0xa4d6f1d2→0xa71763a8),isModuleInstalled(0x6d61fe70→0x112d3a7d),supportsModule(0x12d79da3→0xf2dc691d), andaccountId(0x7b60424a→0x9cfd7cff). Previously, uninstall calldata reverted and queries silently returnedfalse/''. -
Kernel v0.2.4
execute/executeBatchnow use the Kernel v2 selectors (0x51945447/0x34fcd5be) with matching parameter encoding; the previous SimpleAccount-style calldata reverted on every v0.2.4 call. -
Nexus
signUserOperationreturns the bare 65-byte signature, matching permissionless.js. Previously the K1 validator address was prepended (85 bytes), causing AA24 signature errors on-chain (Nexus selects the validator via the nonce key). -
Pimlico client actions aligned with the Pimlico API:
validateSponsorshipPoliciessendspm_validateSponsorshipPolicies(was a nonexistentpimlico_*method),estimateErc20PaymasterCostcomputes the cost locally from quotes (was calling a nonexistent RPC method), andsendCompressedUserOperationsends the correct 3-parameter shape. -
Safe v1.5.0
MULTI_SEND_CALL_ONLYaddress corrected to0xA83c336B20401Af773B6219BA5027174338D1836. -
Simple account EntryPoint-version encode/sign paths: v0.6
executeBatchemits the v0.6 selector0x18dfb3c7(address[],bytes[]) instead of the v0.7 3-array form, and v0.8 non-7702 batching/signing follow the v0.8 shapes. -
EntryPoint v0.6
signUserOperationis now implemented for accounts that accept a v0.6 configuration (previously configured-but-unimplemented: accounts derived v0.6 addresses, then threw or mis-packed at signing). -
Safe CREATE2 address derivation fetches
proxyCreationCode()from the configured SafeProxyFactory viapublicClientwhen available, with fallback to the verified hardcoded v1.4.1 constant. Avoids silent getAddress-vs-deploy mismatch for v1.5.0 and custom factories. -
BundlerRpcError.aaErrorCodescans bothmessageanddata, case-insensitively (matches viemgetBundlerError); previously only scanneddatawithAA\d+. -
CallsStatus.fromJsonaccepts walletstatusfield, numeric strings, and legacy"CONFIRMED"/"PENDING"strings without throwing onas int.
Deliberate deviations from permissionless.js (parity audit 018) #
The following items were reviewed and intentionally not ported in this release:
- EIP-712 edge cases (
message_hash.dart): customEIP712Domaintypes, value-range/bytesNvalidation, and UTF-8 type-string hashing remain deferred. Standard ASCII domains and types match JS/viem. - ERC-20 paymaster flow shape (
experimental/pimlico/erc20_paymaster.dart): Dart still uses a stub+real estimation path with a re-fetch rather than JS's stub-substitution + single final data fetch; standalone function API vs JS decorator remains. Functional sponsorship works; round-trip efficiency is a follow-up. - install/uninstall / signMessage / sendTransaction surfaces: per-op
paymaster/authorization/appended-
callsoverrides on install/uninstall, raw-bytessignMessage, and batch/userop-paramssendTransactionare not ported (narrower Dart API; core flows work via existing methods). AccountNotFoundError: structurally unnecessary — Dart always requires an account onSmartAccountClientconstruction, so the JS error cannot arise at action time.
0.3.0 - 2026-04-17 #
Added #
useMultiSendForSetupoption onSafeSmartAccount/createSafeSmartAccount— port of the same option from permissionless.js@0.3.5.- Default
truepreserves the existing MultiSend-wrapped setup — no changes to counterfactual addresses for existing users. - When
falseand there is exactly one setup transaction (the defaultenableModulescall, no WebAuthn owners, no ERC-7579 modules),Safe.setupis called directly with that call'sto/datainstead of being wrapped through MultiSend. - Set
useMultiSendForSetup: falseto produce CREATE2 addresses compatible with Safe Protocol Kit / relay-kit address derivation. - The flag has no effect in ERC-7579 mode (the launchpad path is independent) or when a WebAuthn owner is present (extra setup calls force MultiSend).
- Default
0.2.0 - 2026-02-20 #
Added #
- RIP-7212 Dynamic Detection:
isRip7212Supported(PublicClient)probes the P256 precompile viaeth_callwith a known-valid Wycheproof test vector, detecting support on any chain- Results cached per chain ID for the lifetime of the application
clearRip7212Cache()for testing- Falls back gracefully when the precompile is not deployed (revert →
false)
- RIP-7212 Static Detection:
supportsRip7212(chainId:)andshouldUseP256Precompile(chainId:)check against a curated set of 67 chain IDs known to support the P256 precompile - RIP-7212 Chain Support: Comprehensive
rip7212SupportedChainIdsset. - RIP-7212 Constants:
p256PrecompileAddress(0x0000...0100) - Kernel Precompile Integration: Kernel WebAuthn accounts now dynamically detect P256
precompile support via
isRip7212Supportedwhen apublicClientis availablesignUserOperation,signMessage,signTypedDatause dynamic detection (async)getStubSignatureuses static chain ID list (sync, for gas estimation)- Falls back to static
shouldUseP256Precompilewhen nopublicClientis configured
Changed #
- Dependencies: Bumped
web3dartto^3.0.2 - Monorepo: Restructured as a Dart pub workspace monorepo
- Package moved to
packages/permissionless/ - Uses native Dart pub workspaces (
resolution: workspace) - Melos 7.x for workspace management
- Package moved to
- SDK: Minimum SDK version bumped to
>=3.5.0(required for pub workspaces)
Added #
- Passkeys Package: New
permissionless_passkeyssibling package for WebAuthn/Passkeys support
0.1.3 - 2025-12-29 #
Fixed #
- WebAuthn/Passkeys: Fixed Safe account
isWebAuthndetectionSafeSmartAccount.isWebAuthnnow correctly returnstruewhen WebAuthn owners are configured- This enables proper gas estimation with 900k minimum verification gas for P256 on-chain verification
- Previously always returned
false, causing AA26 (over verificationGasLimit) errors
- WebAuthn/Passkeys: Fixed paymaster gas limit handling for WebAuthn accounts
- Smart fallback logic for paymaster verification and post-op gas limits
- Uses stub values when valid, falls back to bundler estimates when needed
- Fixes AA33 (paymaster reverted) errors for Kernel WebAuthn accounts
- Nonce: Fixed
nonceKeynot passed togetAccountNonce()inSmartAccountClient- Kernel v0.3.x accounts now correctly fetch nonces using their validator-encoded nonce key
- Safe accounts unaffected (use default key 0)
Added #
- Examples: Added WebAuthn examples for Safe and Kernel accounts
example/safe_webauthn_example.dart- Safe with passkey ownerexample/kernel_webauthn_example.dart- Kernel v0.3.1 with WebAuthn validator
0.1.2 - 2025-12-18 #
Changed #
- Documentation: Achieved 100% dartdoc coverage (1192/1192 API elements)
- Added documentation to all
fromJsonfactory constructors - Documented all UserOperation fields for both v0.6 and v0.7
- Added dartdoc comments to Safe, Kernel, Pimlico, and Paymaster types
- Documented all public class constructors and their parameters
- Added field-level documentation for gas limits, fees, and paymaster data
- Added documentation to all
0.1.1 - 2025-12-18 #
Changed #
- BREAKING: Replaced custom
EthAddressclass withEthereumAddressfrom thewalletpackageEthAddress('0x...')→EthereumAddress.fromHex('0x...')EthAddress.zero→zeroAddress(top-level constant)- Added
EthereumAddressExtensionwith.hex,.checksummed,.bytes,.isZero,.toAbiEncoded()methods - Added
StringToAddressextension:'0x...'.toAddress()
- BREAKING: Removed
includeFactoryDataparameter fromSmartAccountClientmethodsprepareUserOperation,prepareUserOperationWithAuth,sendUserOperation,sendUserOperationAndWaitprepareUserOperationV06,sendUserOperationV06,sendUserOperationV06AndWait- The SDK now automatically detects deployment status via
publicClient.isDeployed()and includes factory data only when needed - Important:
publicClientparameter is now required inSmartAccountClientconstructor for auto-detection to work - This simplifies the API - users no longer need to track deployment status themselves
- Added
walletpackage as direct dependency forEthereumAddresstype
Fixed #
- Improved interoperability with web3dart ecosystem by using standard types
- Fixed inconsistency between v0.6 and v0.7 UserOperation preparation
- v0.6 now properly checks deployment status before including
initCode, matching v0.7 behavior
- v0.6 now properly checks deployment status before including
0.1.0 - 2025-12-15 #
Initial release of permissionless.dart - a Dart implementation of permissionless.js for ERC-4337 smart accounts.
Added #
Smart Accounts
- SafeSmartAccount - Gnosis Safe with 4337 module support
- Safe v1.4.1 (EntryPoint v0.6 and v0.7)
- Safe v1.5.0 (EntryPoint v0.7)
- Multi-signature support with threshold
- EIP-712 typed data signing
- SimpleSmartAccount - Minimal single-owner account
- EntryPoint v0.6 and v0.7 support
- Eip7702SimpleSmartAccount - EIP-7702 delegated Simple account
- EOA code delegation support
- EntryPoint v0.7
- KernelSmartAccount - ZeroDev's modular account
- Kernel v0.2.4 (EntryPoint v0.6)
- Kernel v0.3.1 (EntryPoint v0.7, ERC-7579 compliant)
- Eip7702KernelSmartAccount - EIP-7702 delegated Kernel account
- EOA code delegation with modular architecture
- EntryPoint v0.7
- EtherspotSmartAccount - ModularEtherspotWallet (EntryPoint v0.7)
- ERC-7579 modular architecture
- NexusSmartAccount - Biconomy's ERC-7579 modular account (EntryPoint v0.7)
- K1 validator for ECDSA signatures
- LightSmartAccount - Alchemy's gas-efficient account
- Version 1.1.0 (EntryPoint v0.6)
- Version 2.0.0 (EntryPoint v0.7, signature type prefix)
- ThirdwebSmartAccount - Thirdweb SDK smart account
- EntryPoint v0.6 and v0.7 support
- TrustSmartAccount - Trust Wallet's Barz account
- Diamond proxy pattern (EIP-2535)
- EntryPoint v0.6 only
- BiconomySmartAccount (deprecated) - Use NexusSmartAccount instead
Clients
- BundlerClient - ERC-4337 bundler RPC methods
eth_sendUserOperationeth_estimateUserOperationGaseth_getUserOperationReceipteth_getUserOperationByHasheth_supportedEntryPointswaitForUserOperationReceipt- Polling with configurable timeout
- PaymasterClient - Paymaster integration
pm_getPaymasterStubDatapm_getPaymasterDatapm_validateSponsorshipPolicies
- SmartAccountClient - High-level account operations
prepareUserOperationsignUserOperationsendUserOperationsendPreparedUserOperationwaitForReceipt
- PimlicoClient - Pimlico bundler extensions
pimlico_getUserOperationGasPricepimlico_getUserOperationStatuspimlico_sendCompressedUserOperationgetSupportedTokens- Get supported ERC-20 tokens for gas paymentgetTokenQuotes- Get exchange rates and paymaster info for tokens
- EtherspotClient - Etherspot (Skandha) bundler extensions
skandha_getGasPrice
- PublicClient - Standard Ethereum JSON-RPC
eth_chainIdeth_getCode/isDeployedeth_calleth_getTransactionReceiptgetSenderAddressgetAccountNonce
Smart Account Actions
signMessage- EIP-191 personal message signing for all accountssignTypedData- EIP-712 typed data signing for all accounts
ERC-7579 Actions
installModule- Install a single module on the smart accountinstallModules- Batch install multiple modules in one UserOperationuninstallModule- Remove a module from the smart accountisModuleInstalled- Check if a module is installed on an accountsupportsModule- Check if an account supports a module type
ERC-20 Paymaster Support (Experimental)
prepareUserOperationForErc20Paymaster- Prepare UserOperations for ERC-20 gas payment- Automatic token approval injection
- USDT special case handling (approval reset)
- Max cost calculation in tokens
estimateErc20PaymasterCost- Estimate gas cost in ERC-20 tokens
Constants
EntryPointAddresses- Canonical EntryPoint contract addresses- v0.6, v0.7, and v0.8 support
EntryPointVersion- Enum for version selection
Types
UserOperationV06- EntryPoint v0.6 UserOperationUserOperationV07- EntryPoint v0.7 UserOperationEthereumAddress- Ethereum address type (from wallet package) with extensionsCall- Transaction call representationTypedData/TypedDataDomain/TypedDataField- EIP-712 typed dataCallsStatus/CallReceipt- ERC-5792 response typesPackedUserOperation- Packed UserOperation format utilitiesEip7702Authorization- EIP-7702 EOA code delegation authorizationAccountOwner/PrivateKeyOwner- Unified owner interface for all signing modes
Utilities
- Gas Estimation
totalGasLimitcalculationgetRequiredPrefundfor account funding- Gas multipliers for estimation buffers
- ERC-7579 - Modular account call encoding
encode7579Executefor single callsencode7579ExecuteBatchfor batch callsencodeInstallModule/encodeUninstallModuledecode7579Calls
- ABI Encoding - Solidity ABI encoding utilities
AbiEncoder.encodeAddressAbiEncoder.encodeUint256AbiEncoder.encodeBytes
- MultiSend - Safe batch transaction encoding
encodeMultiSendencodeMultiSendCall
- Message Hashing
hashMessage- EIP-191 personal message hashhashTypedData- EIP-712 typed data hash
- PackedUserOperation Utilities
packUserOperation/unpackUserOperationtoPackedUserOperation/fromPackedUserOperation
- ERC-20 Utilities
encodeApprove/encodeTransfer- Token operation encodingErc20Selectors- Function selector constantserc20BalanceOverride- Simulate token balanceserc20AllowanceOverride- Simulate token allowancesmergeStateOverrides- Combine multiple state overrides
- Units - Wei/Gwei/Ether conversions
parseEther/formatEtherparseGwei/formatGweiparseUnits/formatUnits
- Hex Utilities
Hex.concat/Hex.sliceHex.fromBigInt/Hex.toBigIntHex.fromBytes/Hex.decode
- Nonce Utilities
encodeNonce/decodeNonce- 2D nonce encoding
Examples
simple_example.dart- Simple account usagesafe_example.dart- Safe account with multi-sigsafe_7579_example.dart- Safe with ERC-7579 moduleskernel_example.dart- Kernel v0.3.1 (ERC-7579)kernel_v024_example.dart- Kernel v0.2.4 (EntryPoint v0.6)etherspot_example.dart- Etherspot modular accountlight_example.dart- Light account with version comparisonnexus_example.dart- Nexus (Biconomy) accountbiconomy_example.dart- Legacy Biconomy accountthirdweb_example.dart- Thirdweb accounttrust_example.dart- Trust Wallet Barz accounteip7702_simple_example.dart- EIP-7702 Simple account delegationeip7702_kernel_example.dart- EIP-7702 Kernel account delegationerc20_paymaster_example.dart- ERC-20 gas paymenterc7579_modules_example.dart- Module installation and management