/variance-dart

Account Abstraction SDK written in Dart

Primary LanguageDartBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

Variance SDK

Variance is an Account Abstraction Developer toolkit that is designed to simplify the development of Ethereum smart accounts and Entrypoint interactions. It relies on the Web3dart library.

Features

  • ABI Encoding/Decoding: Easily encode and decode ABI data for Ethereum smart contract and Entrypoint interactions.
  • Transaction Handling: Simplify the process of creating and sending UserOperations.
  • Token Operations: Work with ERC20 and ERC721 tokens, including transfer and approval functionalities.
  • Web3 Functionality: Interact with Ethereum nodes and bundlers.
  • SecP256r1 Signatures: Sign transactions with Passkeys.

Getting Started

Installation

open your terminal and run the following command:

flutter pub add variance_dart
flutter pub add web3_signers
flutter pub add web3dart

Usage

// Import the packages
import 'package:web3_signers/web3_signers.dart';
import 'package:variance_dart/variance_dart.dart';
import 'package:web3dart/web3dart.dart';

Chain Configuration

static const rpc = "https://api.pimlico.io/v2/84532/rpc?apikey=API_KEY";
final Uint256 salt = Uint256.zero;

final Chain chain = Chains.getChain(Network.baseTestnet)
    ..accountFactory = Constants.lightAccountFactoryAddressv07
    ..bundlerUrl = rpc
    ..paymasterUrl = rpc;

For Safe Accounts, use Constants.safeProxyFactoryAddress as the account factory. There are 8 available networks: ethereum, polygon, optimism, base, arbitrum, linea, fuse and scroll. 2 available testnets: sepolia and baseTestent.

Additionally, you can specify a different Entrypoint address. By default, the entrypoin v0.7 is used. If you wish to use v0.6, you can do so as follows:

final EntryPointAddress entrypointAddress = EntryPointAddress.v06;
chain.entrypoint = entrypointAddress;

// OR append it to the chain
   ..entrypoint = EntryPointAddress.v06;

By default the paymaster is set to null. If wish to use paymasters with your smart wallet you can do so by specifying the rpc endpoint of the paymaster. This would add a paymaster Plugin to the smart wallet.

If you have additional context for the paymaster, you will be able to add it to the smart wallet after creation or before initiating a transaction.

wallet.plugin<Paymaster>('paymaster').context = {'key': 'value'};

Signers

In order to create a smart wallet client you need to set up a signer, which will sign useroperation hashes to be verified onchain. Only signers available in the web3signers package can be used.

You have to use the correct signer for the type of account you want to create.

  1. PrivateKeys - use with light accounts and safe accounts
  2. Passkey - use with safe Passkey accounts only
  3. EOA Wallet - use with light smart accounts and safe accounts

For more information, please check out the web3signers package documentation.

What is the difference between PrivateKeys and EOA Wallet?
  • The PrivateKey signer generates only a single private key for use with a smart account. The private key requires a password, and the private key can be serialized into JSON format for storage.
  • The EOA Wallet signer generates a HD wallet with multiple accounts for use with a smart account. Additionally the EOA Wallet signer allows the smart account to sign transactions with different accounts by specifying the account index.

With EOA Wallet signer, users receive the mnemonic phrase but with PrivateKey signer they dont.

Smart Wallet Factory

The smart wallet factory handles the creation of smart wallet instances. Make sure you have created a signer from the previous step.

final SmartWalletFactory smartWalletFactory = SmartWalletFactory(chain, signer);

To Create an Alchemy Light Account

The Alchemy Light Account requires the signer to have a prefix. When creating a web3_signer for your smart wallet make sure to provide a Uint8 value prefix. This will be appended to the signature generated by the signers. Example:

const prefix = const SignatureOptions(prefix: [0])
final signer = EOAWallet.createWallet(WordLength.word_12, prefix);
final smartWalletFactory = SmartWalletFactory(chain, signer);

final Smartwallet wallet = await smartWalletFactory.createAlchemyLightAccount(salt);
print("light account wallet address: ${wallet.address.hex}");

To create a Safe Smart Account

// No prefix is required. word_12 is the default
final signer = EOAWallet.createWallet();
final smartWalletFactory = SmartWalletFactory(chain, signer);

final Smartwallet wallet = await smartWalletFactory.createSafeAccount(salt);
print("safe wallet address: ${wallet.address.hex}");

To create a Safe Smart Account with Passkey

final sharedWebauthnSigner = EthereumAddress.fromHex("0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9");
final options = PassKeysOptions(
        ...
        sharedWebauthnSigner: sharedWebauthnSigner,);

final signer = PassKeySigner(options: options);
final smartWalletFactory = SmartWalletFactory(chain, signer);
final keypair = await signer.register(name, displayName); // email can be used in place of name

final Smartwallet wallet = await smartWalletFactory.createSafeAccountWithPasskey(
           keypair, salt, sharedWebauthnSigner);
print("p256 wallet address: ${wallet.address.hex}");

Your keypair must be a PassKeyPair object received when registering with your PasskeySigner signer. Keypair can serialized and stored by the app

Additionally, you can pass a p256Verifier address to the createSafeAccountWithPasskey method, by default the RIP-7212 precompile is used.

final Smartwallet wallet = await smartWalletFactory.createSafeAccountWithPasskey(
          keypair, salt, sharedWebauthnSigner, p256Verifier);
print("p256 wallet address: ${wallet.address.hex}");

Interacting with the Smart Wallet

// retrieve the balance of a smart wallet
final EtherAmount balance = await wallet.balance;
print("account balance: ${balance.getInWei}");

// retrive the account nonce
final Uint256 nonce = await wallet.nonce;
print("account nonce: ${nonce.toInt()}");

// check if a smart wallet has been deployed
final bool deployed = await wallet.deployed;
print("account deployed: $deployed");

// get the init code of the smart wallet
final String initCode = wallet.initCode;
print("account init code: $initCode");

// perform a simple transaction (send ether to another account)
// account must be prefunded with native token. paymaster is not yet implemented
await wallet.send(
  EthereumAddress.fromHex(
      "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"), // receive address
  EtherAmount.fromInt(EtherUnit.ether, 0.7142), // 0.7142 ether
);

For detailed usage and examples, refer to the documentation. Additional refer to the example for use in a flutter app.

API Reference

Detailed API reference and examples can be found in the API reference.

Contributing

We are committed to maintaining variance as an open source sdk, take a look at existing issues, open a pull request etc.

License

This project is licensed under the BSD-3-Clause - see the LICENSE file for details.