Code Monkey home page Code Monkey logo

smart-wallet's Introduction

Smart Wallet

This repository contains code for a new, ERC-4337 compliant smart contract wallet from Coinbase.

It supports

  • Multiple owners
  • Passkey owners and Ethereum address owners
  • Cross-chain replayability for owner updates and other actions: sign once, update everywhere.

Multiple Owners

Our smart wallet supports a practically unlimited number of concurrent owners (max 2^256). Each owner can transact independently, without sign off from any other owner.

Owners are identified as bytes to allow both Ethereum address owners and passkey (Secp256r1) public key owners.

Passkey owners and Ethereum address owners

Ethereum address owners can call directly to the smart contract wallet to transact and also transact via user operations.

In the ERC-4337 context, we expect UserOperation.signature to be the ABI encoding of a SignatureWrapper struct

struct SignatureWrapper {
    uint8 ownerIndex;
    bytes signatureData;
}

Owner index identifies the owner who signed the user operation. This must be passed because secp256r1 verifiers require the public key as an input. This differs from ecrecover, which returns the signer address.

We pass an ownerIndex rather than the public key itself to optimize for calldata, which is currently the main cost driver on Ethereum layer 2 rollups, like Base.

If the signer is an Ethereum address, signatureData should be the packed ABI encoding of the r, s, and v signature values.

If the signer is a secp256r1 public key, signatureData should be the the ABI encoding of a WebAuthnAuth struct. See webauthn-sol for more details.

Cross-chain replayability

If a user changes an owner or upgrade their smart wallet, they likely want this change applied to all instances of your smart wallet, across various chains. Our smart wallet allows users to sign a single user operation which can be permissionlessly replayed on other chains.

There is a special function, executeWithoutChainIdValidation, which can only be called by the EntryPoint contract (v0.6).

In validateUserOp we check if this function is being called. If it is, we recompute the userOpHash (which will be used for signature validation) to exclude the chain ID.

Code excerpt from validateUserOp

// 0xbf6ba1fc = bytes4(keccak256("executeWithoutChainIdValidation(bytes)"))
if (userOp.callData.length >= 4 && bytes4(userOp.callData[0:4]) == 0xbf6ba1fc) {
    userOpHash = getUserOpHashWithoutChainId(userOp);
    if (key != REPLAYABLE_NONCE_KEY) {
        revert InvalidNonceKey(key);
    }
} else {
    if (key == REPLAYABLE_NONCE_KEY) {
        revert InvalidNonceKey(key);
    }
}

To help keep these cross-chain replayable user operations organized and sequential, we reserve a specific nonce key for only these user operations.

executeWithoutChainIdValidation can only be used for calls to self and can only call a whitelisted set of functions.

function executeWithoutChainIdValidation(bytes calldata data) public payable virtual onlyEntryPoint {
    bytes4 selector = bytes4(data[0:4]);
    if (!canSkipChainIdValidation(selector)) {
        revert SelectorNotAllowed(selector);
    }

    _call(address(this), 0, data);
}

canSkipChainIdValidation can be used to check which functions can be called.

Today, allowed are

  • MultiOwnable.addOwnerPublicKey
  • MultiOwnable.addOwnerAddress
  • MultiOwnable.addOwnerAddressAtIndex
  • MultiOwnable.addOwnerPublicKeyAtIndex
  • MultiOwnable.removeOwnerAtIndex
  • UUPSUpgradeable.upgradeToAndCall

Deployments

Factory and implementation are deployed via Safe Singleton Factory, which today will give the same address across 248 chains. See "Deploying" below for instructions on how to deploy to new chains.

Version Factory Address
1 0x0BA5ED0c6AA8c49038F819E587E2633c4A9F428a

Developing

After cloning the repo, run the tests using Forge, from Foundry

forge test

Deploying

To deploy on a new chain, in your .env set

#`cast wallet` name
ACCOUNT=
# Node RPC URL
RPC_URL=
# Optional Etherscan API key for contract verification
ETHERSCAN_API_KEY=

See here for more details on cast wallet.

Then run

make deploy

Influences

Much of the code in this repository started from Solady's ERC4337 implementation. We were also influenced by DaimoAccount, which pioneered using passkey signers on ERC-4337 accounts, and LightAccount.

smart-wallet's People

Contributors

wilsoncusack avatar xenoliss avatar stevieraykatz avatar lukasrosario avatar redlikerosesss avatar pegahcarter avatar wbnns avatar worm-emoji avatar

Stargazers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.