GithubHelp home page GithubHelp logo

2023-05-ambire's Introduction

Ambire Wallet - Invitational audit details

Automated Findings / Publicly Known Issues

Automated findings output for the audit can be found here within 24 hours of audit opening.

Note for C4 wardens: Anything included in the automated findings output is considered a publicly known issue and is ineligible for awards.

๐Ÿ”ฅ Ambire Wallet ๐Ÿ”ฅ

The Web3 wallet that makes crypto self-custody easy and secure for everyone, built via account abstraction.

Ambire Wallet

Useful links

Hello Wardens ๐Ÿ‘‹

We are looking forward to you diving into our code!

Feel free to ask us anything you want, no matter if it's a minor nitpick or a severe issue. We remain available around the clock in the Code4rena Discord, and don't hestitate to tag @Ivo#8114

Good luck and enjoy hunting! ๐Ÿ›๐Ÿšซ

We hope you're excited about finally seeing a usable and powerful smart contract wallet on Ethereum!

Scope

Files in scope

File SLOC
Contracts (2)
ambire-common/contracts/AmbireAccountFactory.sol ๐Ÿ–ฅ ๐Ÿงฎ ๐ŸŒ€ 45
ambire-common/contracts/AmbireAccount.sol ๐Ÿ–ฅ ๐Ÿ’ฐ ๐Ÿ‘ฅ ๐Ÿงฎ 182
Libraries (2)
ambire-common/contracts/libs/Bytes.sol ๐Ÿ–ฅ 17
ambire-common/contracts/libs/SignatureValidator.sol ๐Ÿงฎ ๐Ÿ”– ฮฃ 85
Total (over 4 files): 329

Changes compared to last Code4rena audit contest

Compared to the last Code4rena audit contest, we made the following changes:

  • Identity renamed to AmbireAccount
  • QuickAccManager dropped
  • Implemented a user-settable fallback handler, allowing for wallet accounts to be upgraded by user choice
  • Recovery signatures introduced, which are equivalent to time timelocked recovery procedure that was previously in QuickAccManager
  • Added Schnorr signature type to SignatureValidatorhttps://eips.ethereum.org/EIPS/eip-6492
  • Added Multisig signature type to SignatureValidator
  • Added executeMultiple

The production version of Ambire still runs the contracts that were previously audited in this contest.

Architecture

Ambire is a smart contract wallet a.k.a account abstraction wallet. Each user is represented by a smart contract, which is a minimal proxy (EIP-1167) for AmbireAccount.sol (example) - we call "account". Many addresses can control each account - we call this "privileges" in the contract and "keys" in the UI.

The main contract everything is centered around is AmbireAccount.sol, which is the actual smart wallet.

Accounts can execute multiple calls in the same on-chain transaction. We call the array of user transactions a "user bundle" - the user signs the hash of this array along with anti-replay data such as nonce, chainID and others. Once it's signed, anyone can execute it by calling AmbireAccount(account).execute

The addresses that control an account (privileges) can be EOAs but they can also be smart contracts themselves, thanks to the SmartWallet signature mode in SignatureValidator which enables EIP-1271 signatures to be used.

To allow more sophisticated authentication schemes without upgradability, we use a very simple relationship: a periphery contract that only deals with the specific authentication scheme can be added to privileges. For example, if a user wants to convert their account to a multisig, they can remove all other privileges and only authorize a single one: a multisig manager contract, that will verify N/M signatures and call AmbireAccount(account).executeBySender upon successful verification. This also works for EIP-1271 signatures since AmbireAccount.isValidSignature uses SignatureValidator, which supports EIP-1271 itself, so it will propagate the call down to the multisig manager contract.

There are a few ways for a user bundle to get executed:

  • Directly, when a user's EOA pays for gas
  • Through a Relayer that takes the signed message that authorizes a user bundle, and broadcasts it itself, paying for gas. The user bundle will have to contain an ERC-20 transaction that pays the Relayer to reimburse it for gas. Currently we have a proprietary relayer that does all of this.
  • Through ERC-4337

The actual proxy for each account is deployed counterfactually, when the first user bundle is executed.

Because user bundles are authorized as signed messages, there's no need for hardware wallets to support EIP-1559 directly.

Similar products include Argent, Safe and Sequence. The most notable differences is that the Ambire contracts are designed to be as simple as possible.

Testing and JS libs

The contracts in scope can also be found in this repo: https://github.com/AmbireTech/ambire-common/tree/5c54f8005e90ad481df8e34e85718f3d2bfa2ace/contracts.

The code is frozen for review on commit 5c54f8005e90ad481df8e34e85718f3d2bfa2ace in the repo ambire-common.

There are tests in the ambire-common repo. You can find them in ./test.

First, clone the repo recursively:

git clone https://github.com/code-423n4/2023-05-ambire --recursive
cd 2023-05-ambire/ambire-common
npm i

To test, run separately:

npx hardhat node

And then:

npm run test

Output should look like this:

$ npm run test
npx hardhat compile; npx hardhat test; jest
NFT 721 and 1155 tests original contract tests
    โœ“ successfully deploys the ambire account (1936512 gas)
    โœ“ should call onERC721Received and return its signature (1723363 gas)
    โœ“ should call onERC1155Received and return its signature (1723363 gas)
    โœ“ should call onERC1155BatchReceived and return its signature (1723363 gas)

Basic Ambire Account tests
    โœ“ successfully deploys the ambire account (3446726 gas)
    โœ“ ONLY_IDENTITY_CAN_CALL on setAddrPrivilege (1723363 gas)
    ...
...
ยท---------------------------------------------|---------------------------|--------------|----------------------------ยท
|            Solc version: 0.8.20             ยท  Optimizer enabled: true  ยท  Runs: 1000  ยท  Block limit: 6718946 gas  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  Methods                                    ยท               30 gwei/gas                ยท      1815.47 usd/eth       โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  Contract              ยท  Method            ยท  Min        ยท  Max        ยท  Avg         ยท  # calls     ยท  usd (avg)  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccount         ยท  execute           ยท      43802  ยท      83728  ยท       69218  ยท          18  ยท       3.77  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccount         ยท  executeBySender   ยท      29365  ยท      51637  ยท       42496  ยท          13  ยท       2.31  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccount         ยท  executeMultiple   ยท      88839  ยท     109869  ยท       99354  ยท           4  ยท       5.41  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccountFactory  ยท  deploy            ยท     151214  ยท    1733861  ยท      678763  ยท           3  ยท      36.97  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccountFactory  ยท  deployAndExecute  ยท     213137  ยท    1793788  ยท     1003466  ยท           4  ยท      54.65  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  Deployments                                ยท                                          ยท  % of limit  ยท             โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccount                              ยท          -  ยท          -  ยท     1723363  ยท      25.6 %  ยท      93.86  โ”‚
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
|  AmbireAccountFactory                       ยท          -  ยท          -  ยท      471341  ยท         7 %  ยท      25.67  โ”‚
ยท---------------------------------------------|-------------|-------------|--------------|--------------|-------------ยท

 PASS  v2/libs/gasPrice/tests/non1559Network.test.ts
 PASS  v2/libs/gasPrice/tests/1559Network.test.ts
 PASS  v2/libs/portfolio/portfolio.test.ts (5.963 s)
 PASS  v2/libs/deployless/compile.test.ts (7.218 s)
 PASS  v2/libs/keystore/keystore.test.ts (8.041 s)
 PASS  v2/libs/deployless/deployless.test.ts (11.617 s)

Test Suites: 6 passed, 6 total
Tests:       38 passed, 38 total
Snapshots:   0 total
Time:        11.846 s, estimated 12 s

There's one additional part that is not yet added to the repo, and this is the deploy mechanism implemented here in IdentityProxyDeploy. Instead of deploying the whole AmbireAccount contract every time, we use minimal proxies. This is pretty standard, but most smart contract wallets use an initialize() function that can only be called once to set the privileges of the contract, because minimal proxies normally don't have constructors. Instead of this approach, which is quite unsafe, we use IdentityProxyDeploy, which generates deploy bytecode which directly does SSTORE in the correct storage slots to set the privileges for the relevant keys.

You can test Ambire itself at wallet.ambire.com, where it uses an older version of the contracts - one that was audited before through a Code4rena contest.

Design decisions

The contracts are free of inheritance and external dependencies.

There is no code upgradability and no ownership (onlyOwner) or pausability, to ensure immutability. For easier readability, there are no modifiers, while keeping the code DRY.

Storage usage is cut down to the minimum: when bigger data structures need to be saved, we take advantage of the cheap calldata and always pass them in, verifying the hash against a storage slot in the process.

Smart contract summary

Every contract in here is in scope, including the libraries.

AmbireAccount.sol

The core of the Ambire smart wallet. Each user is a minimal proxy with this contract as a base. It contains very few methods, with the most notable being:

  • execute: executes a signed user bundle
  • executeBySender: executes a bundle as long as msg.sender is authorized

There's a few methods that can only be called by the AmbireAccount itself, which means the only way to call them is through a call through execute/executeBySender, ensuring it's authorized. Those methods are setAddrPrivilege, tipMiner and tryCatch.

It's only dependency is an internal one, SignatureValidator, which in turn relies on Bytes.

SignatureValidator.sol

Validates signatures in a few modes: EIP-712, EthSign, SmartWallet, Multisig, Schnoor and Spoof. The first two verify signed messages using ecrecover, the only difference being that EthSign expects the "Ethereum signed message:" prefix. SmartWallet is for ERC-1271 signatures (smart contract signatures), and Spoof is for spoofed signatures that only work when tx.origin == address(1).

AmbireAccountFactory.sol

A simple CREATE2 factory contract designed to deploy minimal proxies for users. The most notable point here is deploySafe, which is a method that protects us from griefing conditions: CREATE2 will fail if a contract has already been deployed, and this method essentially ensures a contract is deployed without failing if it already is.

The use case of this is counterfactual deployment: the proxy of each account will be deployed when the first user bundle is executed, but we don't want to fail the whole bundle in case the contract has already been deployed.

There is a method allowing the original deployer to execute arbitrary calls from this contract - this is absolutely safe, as the contract is merely a factory, and allows recovering stuck funds or airdrops from it.

Known tradeoffs

NOTE: "bundle"/"user bundle" in this context means array of AmbireAccount-level transactions (AmbireAccount.Transaction[])

  • Account recovery security model: Recovery signatures allow users to recover access to their accounts if they lose their keys. Timelocked transactions can be sent or cancelled by any recovery key. This means that if the recovery key is compromised AND the user key is lost, the attacker can cause grief by cancelling every attempt of the user to recover their funds. We consider this possibility to be extremely rare (both events to happen at once).
  • Storing additional data in privileges: instead of boolean values, we use bytes32 for the privileges mapping and treat any nonzero value as true. Utilizing a storage slot has the same gas costs no matter if true or hash is stored. This is used for recovery signatures, which allow timelocked account recovery procedures to be performed.
  • Anti-bricking mechanism: the execute methods do not allow a signer key to de-authorize themselves - this is done by checking privileges[signerKey] at the end of each type of execute operation. This is done so as to ensure the contract cannot be bricked (left without a valid signer key). This mechanism cannot be implemented in the fallback method, and we're relying on fallback handlers implementing their own checks.
  • ERC-4337 support left for a later stage: while we do have ERC-4337 support implemented, we are choosing not to include it in the scope so as to keep things simple for the intial launch, which will use our own relayer instead of ERC-4337 anyway. There's various reasons for this: 1) faster time to market, 2) gas savings, 3) relying on existing griefing attack protection s
  • ERC-20 fees taken through the transaction batch: there's no special mechanism for reimbursing the relayer for the gas fee. Instead, the relayer looks at the bundle (Transactions[]) and sees if one or more of those transactions are ERC-20 transfers that send tokens to it. The relayer is responsible for checking whether the fee token and amount is acceptable for it, as well as checking it the transaction will execute before broadcasting it to the mempool. This is also a tradeoff cause the internal transactions may fail, in which case the whole bundle reverts and the fee is not paid, but the relayer will pay for gas. This is worked around on the Relayer end by utilizing Flashbots and Eden to avoid mining failing transactions, and by simulating the transactions right before trying to mine them. The reason we don't try/catch the errors int he AmbireAccount is because we want user bundles to succeed/fail as a whole (atomically), and the transaction to show as failing on Etherscan.
  • Signature spoof mode: the SignatureValidator.sol contract has a mode which allows signatures to be spoofed. The purpose of this is to allow easier simulation through eth_call and eth_estimateGas before having a signature from the user, since without this we would have a cyclical dependency that takes two steps to resolve (fee is unknown, user signs once to estimate the fee, then user signs a second time cause the bundle changed). This spoofing should not be allowed when calling through anywhere else other than AmbireAccount(account).execute, and it only works if tx.origin == address(1).
  • no nonce in executeBySender: the purpose of a nonce is to prevent replay attacks for transactions. executeBySender is called directly by an EOA or another contract who is authorized, and doesn't rely on a user signature, and the replay protection of execute doesn't apply to it. The concern arrises that a user might sign a SCW transaction bundle meant to be executed via execute, broadcast it, and then for whatever reason call executeBySender themselves to execute it (eg relayer goes down), allowing the original signed bundle to still be executed. This must be solved in the front-end: once a transaction bundle is signed, if the user wants to apply it with their EOA rather, we should call execute with that original signature rather than executeBySender.
  • Signature validation before deployment: due to the nature of EIP-1271, signatures cannot be validated before the user account is deployed. However, we solved this by creating ERC-6492, which is implemented in Ambire Wallet.

Networks

The contracts will be deployed on Ethereum, Polygon, Fantom, BSC, Avalanche, Arbitrum, Optimism and other popular EVM chains.

Scoping Details

- If you have a public code repo, please share it here:  https://github.com/AmbireTech/ambire-common/tree/5c54f8005e90ad481df8e34e85718f3d2bfa2ace/contracts
- How many contracts are in scope?:   5
- Total SLoC for these contracts?:  500
- How many external imports are there?: 0 
- How many separate interfaces and struct definitions are there for the contracts within scope?:  6
- Does most of your code generally use composition or inheritance?:   Composition
- How many external calls?:   999999
- What is the overall line coverage percentage provided by your tests?:  80
- Is there a need to understand a separate part of the codebase / get context in order to audit this part of the protocol?:  
- Please describe required context:   
- Does it use an oracle?:  false
- Does the token conform to the ERC20 standard?: 
- Are there any novel or unique curve logic or mathematical models?: N/A
- Does it use a timelock function?:  true
- Is it an NFT?: false
- Does it have an AMM?:   false
- Is it a fork of a popular project?:   
- Does it use rollups?:   false
- Is it multi-chain?:  true
- Does it use a side-chain?: false

Final notes

if you're excited about building an easy to use, but powerful account abstraction, feel free to reach out at [email protected] ๐Ÿ”ฅ

2023-05-ambire's People

Contributors

ivshti avatar kartoonjoy avatar borislav-itskov avatar paroxism avatar itsmetechjay avatar illilli000 avatar jtenberg avatar

Watchers

 avatar  avatar Ashok avatar  avatar  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.