Introducing FunKit: The Complete Wallet Development Platform
Our goal at Fun is to empower developers by making web3 application development as easy as any other kind of web development. Today, we are excited to announce the mainnet launch of the FunKit smart wallet protocol, alongside the v0.8.0 release of the FunKit Core SDK and React SDK.
In this series of blog posts, we will explain the technical design decisions and innovations underlying FunKit. This first post shows how our novel smart contract protocols allow FunKit to support powerful wallet functionality without compromising on security or decentralization.
Thoughts? Questions? Come chat with us on Discord!
Design Motivations: Pushing Web3 Forward, Together
The next generation of web3 applications require a new wallet experience that is both effortless and powerful. Users should not need technical knowledge of gas, bridging, and different signature types in order to safely and effectively use applications. Similarly, tomorrow’s web3 developers will not need to understand mempools, blockchain indexing, or internal details of the EVM in order to build groundbreaking web3 applications.
FunKit is a major step toward this future. In everything we do, our aim is to give developers practical, trusted tools that are as useful as possible from day one. This means that FunKit SDKs and infrastructure are opinionated and pragmatic, while leaving the door open to customization as needed. Most wallet features — from onramping and bridging, to swapping and staking — can be implemented in a single line of code with the FunKit Web SDK.
Our smart contracts were designed with this same philosophy in mind. Fun Wallets are ERC-4337 compatible, but their features go far beyond the basics of account abstraction. We took the best learnings from previous work in the space to design a comprehensive smart wallet platform that, out of the box, is ready to deploy wallets with the most powerful features available on the market today. Furthermore, we have designed Fun Wallets to be fully modular and future-proof without compromising on security or decentralization.
We invite you to learn what makes our smart contract platform unique, and to begin contributing to and benefitting from this new ecosystem today.
The Fun Wallet
At the core of our smart control protocol is the Fun Wallet, a non-custodial smart contract wallet designed for simplicity, security, and extensibility. Like other wallets, a Fun Wallet can hold digital assets, interact with dapps, and administer smart contracts. As a developer, you can use FunKit to provision Fun Wallets to your users with the features you need, while ensuring that each user has full sovereignty over their wallets and assets.
Non-Custodial
Anyone can create a Fun Wallet through the FunWalletFactory contract. After a new Fun Wallet is deployed, it can only be operated on by calls initiated via the wallet’s specified ERC-4337 entry point. This ensures that only the wallet’s owners are able to manage the wallet and authorize user operations on its behalf. The exact access control model is customizable via the validation modules, as described in Authentication: Beyond Multisigs. Upgrades to the wallet’s code are opt-in, and must be authenticated through the same means.
Featureful
At its core, a Fun Wallet is an ERC-4337 compatible smart wallet capable of making arbitrary external calls in response to a validated ERC-4337 user operation. Where Fun Wallets differ from basic ERC-4337 accounts is that they come ready with additional capabilities including:
- Validation modules for common and advanced authentication schemes, including flexible role-based access control (RBAC);
- Execution modules for common operations (ERC-20 swaps, etc.) which integrate with RBAC to support fine-grained, function-level access controls;
- Standard paymasters to support most gas abstraction use cases;
- Automated actions, triggered trustlessly based on time or state-based conditions;
- Hooks for developer monetization on a per-transaction basis, via native token or ERC-20.
As a developer, you can leverage FunKit SDKs to expose the functionality you need in your application, with most features requiring just a single line of code. All features are implemented in the smart contracts in a trustless, non-custodial manner to ensure that wallet owners are always in full control of their accounts and assets. Technical details of each feature are described in the rest of this article.
FunWallet.sol (partial view)
contract FunWallet {
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external override returns (uint256 sigTimeRange) { /* ... */ }
function execFromEntryPoint(
address dest,
uint256 value,
bytes calldata data
) public { /* ... */ }
function execFromEntryPointWithFee(
address dest,
uint256 value,
bytes calldata data,
UserOperationFee memory feedata
) public { /* ... */ }
function executeBatch(
address[] calldata dest,
uint256[] calldata value,
bytes[] calldata data
) public { /* ... */ }
// ...
}
Future-Proof
The account abstraction ecosystem is quickly growing, and technical standards in the space are rapidly evolving. It is important for smart wallets to be implemented in a way that fosters interoperability and the development and adoption of shared, open standards. That is why our wallets are designed with forwards-compatibility as a priority. Fun Wallets are fully upgradeable via UUPS upgradeability, allowing wallet owners to opt in to new versions as we contribute to and adapt to emerging standards.
The FunKit SDKs will retain backwards-compatibility with old versions of the Fun Wallet smart contracts so that old wallets always continue to operate as expected.
UUPSUpgradeable.sol (partial view, inherited by FunWallet)
contract UUPSUpgradeable {
function upgradeTo(
address newImplementation
) external virtual onlyProxy { /* ... */ }
function upgradeToAndCall(
address newImplementation,
bytes memory data
) external payable virtual onlyProxy { /* ... */ }
// ...
}
Modular Architecture
With Fun Wallets, we have designed and implemented a novel standard for modular accounts. Modules improve development speed and interoperability by encouraging the development and re-use of high quality, standardized feature implementations.
Fun Wallets use two types of modules:
- Validations implement authentication checks that must be passed before an operation is executed by a wallet.
- Actions implement and bundle calls together, and can be used together with RBAC and automation to implement more sophisticated, smart behaviors.
Module Storage
Our unique approach to module storage was carefully chosen to optimize for simplicity, security, and extensibility. When a Fun Wallet interacts with a module, the module receives permission to write to dedicated storage space within the wallet’s smart contract storage. The storage space is scoped to the module instance, and access is limited to the duration of the interaction.
By enforcing these storage boundaries, we are able to greatly constrain the security risks posed by third-party modules, allowing them to be audited more easily. At the same time, the design is flexible enough that third-party modules can easily implement any powerful and useful wallet capabilities that they may need.
Validation Modules
Validations are a type of module implementing authentication checks that run before user operations are executed. The WalletValidation base contract exposes functions allowing wallet owners to manage the list of active validations on the wallet. Wallet owners can further configure these validations for fine-grained access control.
Currently, we have released two validation modules that are general enough to cover a wide range of authentication uses cases:
- UserAuthentication: Supports single-signer and multi-signer threshold signature schemes over ECDSA signatures.
- RoleBasedAccessControl: Supports the use of access control rules including spending limits, target addresses, and allowed functions.
See Authentication: Beyond Multisigs for more information.
WalletValidation.sol (partial view, inherited by FunWallet)
contract WalletValidation {
function addValidation(
address validation,
bytes calldata initdata
) public { /* ... */ }
function updateValidation(
address prevValidation,
address oldValidation,
address newValidation,
bytes calldata newValidationInitData
) public { /* ... */ }
function removeValidation(
address prevValidation,
address validation
) public { /* ... */ }
// ...
}
Custom Modules
We encourage developers to contribute their own modules to the Fun Wallet ecosystem. Validation and action modules can be created by implementing the IValidation and IModule smart contract interfaces, respectively. Our testnet deployments allow modules to be added permissionlessly. If you’d like to integrate a Fun Wallet module on mainnet, get in touch via Discord or email.
IValidation.sol
interface IValidation {
function init(
bytes calldata initData
) external;
function authenticateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
bytes memory helperData
) external view returns (uint256 sigTimeRange);
function isValidAction(
address target,
uint256 value,
bytes memory data,
bytes memory signature,
bytes32 _hash
) external view returns (uint256);
}
IModule.sol
interface IModule {
function execute(bytes calldata data) external;
function moduleId() external view returns (bytes32);
}
Authentication: Beyond Multisigs
By leveraging the modular architecture described above, we have launched FunKit with a suite of smart contracts and SDK tools that securely implement a broad range of authentication patterns — delivering on the promises of account abstraction, today.
Social Login, MFA, and More
FunKit supports a wide range of authentication types including social login, passkeys, and self-custodied wallets. These can be combined as desired using the standard Validation Modules to support MFA, Account Recovery, and multi-sig schemes.
A typical setup uses one or more multisig schemes over Ethereum EOAs, where each EOA may be backed by a different authentication type. For example, Apple login with an OTP 2FA requirement can be implemented as a 2-of-2 multisig using the UserAuthentication module. Social recovery can be implemented with RoleBasedAccessControl by adding as an owner a second Fun Wallet that is authenticated by a threshold signing scheme over a group of semi-trusted parties. Under the hood, we leverage Magic wallets to support these features non-custodially and securely.
Role Based Access Control
Powerful role-based access control (RBAC) features are available as a standard validation module. The smart contract design is highly general and flexible, but on the SDK level we have exposed a number of simplified interfaces for common use cases.
The RBAC module allows wallets owners to define and assign multiple roles, each of which consists of a list of rules. A rule is used to enforce constraints as follows:
- deadline: Timestamp upon which access is revoked.
- actionValueLimit: Maximum native token value that can be sent in each call.
- feeValueLimit: Maximum fee value (native token or ERC-20) that can be sent in each call.
- targetSelectorMerkleRootHash: Root of a Merkle tree specifying a set of allowed function call targets and function selectors.
- feeRecipientTokenMerkleRootHash: Root of a Merkle tree specifying a set of allowed fee recipients and fee tokens.
Each constraint within a rule is optional. The constraints can be combined as needed, and all constraints within a rule must pass in order for an action to be valid under that rule.
struct Rule {
uint256 deadline;
uint256 actionValueLimit;
bytes32 targetSelectorMerkleRootHash;
uint256 feeValueLimit;
bytes32 feeRecipientTokenMerkleRootHash;
}
contract RoleBasedAccessControl is Validation {
function setRule(
bytes32 ruleId,
Rule calldata rule
) public { /* ... */ }
function deleteRule(
bytes32 ruleId
) public { /* ... */ }
function addRuleToRole(
bytes32 roleId,
bytes32 ruleId
) public { /* ... */ }
function removeRuleFromRole(
bytes32 roleId,
bytes32 ruleId
) public { /* ... */ }
function addUserToRole(
bytes32 roleId,
bytes32 userId
) public { /* ... */ }
function removeUserFromRole(
bytes32 roleId,
bytes32 userId
) public { /* ... */ }
// ...
}
Session Keys
Session keys are a relatively new feature in web3, adopted from the concept used ubiquitously in web2 applications. In FunKit, they are implemented as a special case of RBAC, and are a powerful tool to help users limit risk and exposure while interacting with dapps. A session key is scoped to an operator address (e.g. the smart contract of third-party dapp) and provides that operator with time-limited access to perform a certain set of operations.
Gas Abstraction
Our paymaster design extends previous work by Pimlico Labs. We’ve made it easy for developers to turn any wallet into a full-service sponsor, paying the native token transaction fees needed to cover the gas expenses of their users.
Gasless Transactions
Often, we would like to abstract gas away from the user experience entirely, by covering user gas expenses without expecting anything in return. This is easily achieved by funding and configuring a sponsor address on the GaslessPaymaster contract. Sponsors can be configured to operate in one of two modes:
- Whitelist mode: The sponsor will pay gas for user operations sent by any sender explicitly added to the whitelist.
- Blacklist mode: The sponsor will pay gas for any user operations, except those sent by senders explicitly added to the blacklist.
ERC-20 Gas Payments
The TokenPaymaster contract is designed to allow sponsors to cover gas expenses in exchange for ERC-20 token payments of equal value. Price conversions are calculated using on-chain oracles. Users sending user operations with this paymaster must either set an ERC-20 allowance for the token being used as payment, or they must provide a permit signature as part of the user operation payload.
As with the GasslessPaymaster, sponsors can configure the TokenPaymaster to operate in either whitelist or blacklist mode. They should also specify the whitelist or blacklist mode, and corresponding list, of the ERC-20 token addresses that they are willing to accept as payment.
Custom Paymasters
The provided paymasters should support most gas abstraction use cases, but you are welcome to use Fun Wallets with any paymaster implementation that you like. Custom paymasters are supported at the smart contract level via the standard ERC-4337 interfaces, and they are also supported by the FunKit SDKs.
Automated Actions
An automated action is a Module-based action that is intended to run automatically and trustlessly on behalf of a wallet owner, without intervention by the owner and while the owner may be offline. Automated actions implement two functions:
- validate(bytes data), which determines whether the conditions have been met to allow execution with a given payload (for example by checking the timestamp and/or contract state);
- execute(bytes data), which performs an operation with the given payload.
Both of these functions should be callable by any address, and may offer an incentive to the caller as a reward for executing the action whenever it is valid. This pattern can be used to implement features on Fun Wallets such as “cron” jobs or limit orders for token swaps.
We have provided one such limit order implementation in the form of the UniswapV3LimitOrder contract, and it is currently available for use by all Fun Wallets. Using FunKit, developers can register their own automated actions to have them be triggered automatically by our relayer service.
Developer Monetization
Fun Wallets include hooks that support developer monetization on a per-transaction basis, via either native token or ERC-20. Web3 applications can be easily monetized by using the FunKit SDK to specify fee parameters for user operations. This fee information is part of the signed UserOperation and must be confirmed by the wallet owner.
Developer fees are configured for each user operation as follows:
- token: Specifies either an ERC-20 token or the native network token.
- recipient: The address receiving the developer fee.
- amount: The token fee amount to charge for this user operation.
Next Steps
Our goal is to make it easier than ever for developers to build powerful, intuitive, and secure web3 user experiences, and this initial launch is our first big step. You can start using FunKit today to provision non-custodial, future-proof, ERC-4337 compatible wallets for your user base, with access to all of the features described above.
FunKit is an end-to-end toolkit and comprehensive platform designed to make web3 developers as productive as possible. At the same time, FunKit’s design is modular and forward-looking, and is part of a broader ecosystem fueled by collaboration and emerging standards. We greatly appreciate any feedback and engagement with the community — come talk to us on Discord!