package
0.0.121
Repository: https://github.com/layer-3/clearsync.git
Documentation: pkg.go.dev

# README

UserOp Golang Library

Overview

The UserOp library is a Golang package designed to simplify the creation and interaction with user operations in a decentralized application. It provides functionalities to work with smart walets, create user operations, and send them to the client bundler for execution.

Features

  • Smart Wallet Support: Enables the use of smart wallets for transactions, including deploying new wallets and performing transactions through them.
  • Multi-Call Operations: Supports bundling multiple operations into a single userOp, reducing the need for multiple transactions.
  • Gas Optimization: Offers configurable gas pricing strategies to optimize transaction costs.
  • Paymaster Integration: Integrates with paymasters to enable gasless transactions, where transaction fees can be paid using ERC20 tokens.
  • Infrastructure Flexibility: Allows configuration for different infrastructure providers by setting the provider and bundler URLs.
  • Account abstraction interoperability: Provides a unified interface for interacting with smart wallets, regardless of the underlying smart wallet implementation.

Currently supported Smart Contract providers

Smart Wallets

Paymasters

Signers

Installation

To use this library in your Golang project, you can install it using the following command:

go get github.com/layer-3/clearsync/pkg/userop

Usage

UserOp Client

All the functionalities provided by the UserOp library are accessed through the UserOp client. The client is responsible for checking smart account information, creating user operations and sending them to the client bundler.

UserOp client implements the following interface:

type Client interface {
  IsAccountDeployed(ctx context.Context, owner common.Address, index decimal.Decimal) (bool, error)
  GetAccountAddress(ctx context.Context, owner common.Address, index decimal.Decimal) (common.Address, error)
  NewUserOp(
    ctx context.Context,
    sender common.Address,
    signer Signer,
    calls []Call,
    walletDeploymentOpts *WalletDeploymentOpts,
    gasLimitOverrides *GasLimitOverrides,
  ) (UserOperation, error)
  SendUserOp(ctx context.Context, op UserOperation) (done <-chan Receipt, err error)
}

Configuration

The UserOp client requires a configuration struct to be created. The configuration struct allows you to specify the provider, bundler, wallet, paymaster and other details.

Below is an example of a configuration struct (testing/config.example.go):

var (
  exampleConfig = userop.ClientConfig{
    ProviderURL: "https://YOUR_PROVIDER_URL",
    BundlerURL:  "https://YOUR_BUNDLER_URL",
    PollPeriod:  100 * time.Millisecond,
    EntryPoint:  common.HexToAddress("ENTRY_POINT_ADDRESS"),
    SmartWallet: userop.SmartWalletConfig{
      // Example of a Kernel Smart Wallet config with Kernel v2.2.
      Type: &userop.SmartWalletKernel,
      Factory: common.HexToAddress("0x5de4839a76cf55d0c90e2061ef4386d962E15ae3"), // Zerodev Kernel factory address:
      Logic:          common.HexToAddress("0x0DA6a956B9488eD4dd761E59f52FDc6c8068E6B5"), // Zerodev Kernel implementation (logic) address:
      ECDSAValidator: common.HexToAddress("0xd9AB5096a832b9ce79914329DAEE236f8Eea0390"),
    },
    Paymaster: userop.PaymasterConfig{
      // Example of a Pimlico USDC.e ERC20 Paymaster config.
      Type:    &userop.PaymasterPimlicoERC20,
      URL:     "", // Pimlico ERC20 Paymaster does not require a URL.
      Address: common.HexToAddress("0xa683b47e447De6c8A007d9e294e87B6Db333Eb18"),
      PimlicoERC20: userop.PimlicoERC20Config{
        VerificationGasOverhead: decimal.RequireFromString("10000"), // verification gas overhead to be added to user op verification gas limit
      },
    },
    Gas: userop.GasConfig{
      // These are default values.
      MaxPriorityFeePerGasMultiplier: decimal.RequireFromString("1.13"),
      MaxFeePerGasMultiplier:         decimal.RequireFromString("2"),
    },
  }

  // wallet deployment options are used when creating a new user op for the smart wallet that is not deployed yet
  walletDeploymentOpts = &userop.WalletDeploymentOpts{
    Owner: common.HexToAddress("YOUR_OWNER_ADDRESS"),
    Index: decimal.NewFromInt(0),
  }

  // You can set either of gas limits when creating an user op to override the bundler's estimation.
  // Or you can set all of them to disable the bundler's estimation.
  gasLimitOverrides = &userop.GasLimitOverrides{
    CallGasLimit:         big.NewInt(42),
    VerificationGasLimit: big.NewInt(42),
    PreVerificationGas:   big.NewInt(42),
  }

  // signer is used to sign the user op upon creation
  exampleSigner = userop.SignerForKernel(must(crypto.HexToECDSA(
  "0xYOUR_PRIVATE_KEY")))
)

Creating a UserOp Client

To create a UserOp client, you need to provide a configuration struct. If you want to change any of configuration components, you should modify the configuration and create a new client.

import "github.com/layer-3/clearsync/pkg/userop"

// Create a UserOp client
client, err := userop.NewClient(exampleConfig)
if err != nil {
    panic(errors.New("Error creating UserOp client:", err))
}

Smart Account Operations

The UserOp client provides the following smart account-related functionalities:

Check if Smart Account is Deployed

Use client.IsAccountDeployed to check if a smart account is deployed for a given owner and index.

// IsAccountDeployed checks whether the smart wallet for the specified owner EOA and index is deployed.
//
// Parameters:
//   - owner - is the EOA address of the smart wallet owner.
//   - index - is the index of the smart wallet, 0 by default. SW index allows to deploy multiple smart wallets for the same owner.
//
// Returns:
//   - bool - true if the smart wallet is deployed, false if not
//   - error - if failed to check.
func (c *backend) IsAccountDeployed(ctx context.Context, owner common.Address, index decimal.Decimal) (bool, error)

Usage example:

ownerEOA := "0xEOAownerAddress"
accountIndex := 0 // Index of the smart wallet, 0 by default. EOA can have multiple smart wallets.

ctx := context.Background()
deployed, err := client.IsAccountDeployed(ctx, ownerEOA, accountIndex)
if err != nil {
    log.Fatal("Error checking if Smart Account is deployed:", err)
}
fmt.Printf("Smart Account for owner %s and index %d is deployed: %t\n", ownerEOA, accountIndex, deployed)

Calculate Smart Account Address

Use client.GetAccountAddress to calculate the address of a smart account for a given owner and index.

// GetAccountAddress returns the address of the smart wallet for the specified owner EOA and index.
//
// Parameters:
//   - owner - is the EOA address of the smart wallet owner.
//   - index - is the index of the smart wallet, 0 by default. SW index allows to deploy multiple smart wallets for the same owner.
//
// Returns:
//   - common.Address - an address of the smart wallet
//   - error - if failed to calculate it.
func (c *backend) GetAccountAddress(ctx context.Context, owner common.Address, index decimal.Decimal) (common.Address, error)

Usage example:

ownerEOA := "0xEOAownerAddress"
accountIndex := 0

ctx := context.Background()
address, err := client.GetAccountAddress(ctx, ownerEOA, accountIndex)
if err != nil {
    log.Fatal("Error calculating Smart Account address:", err)
}
fmt.Printf("Smart Account address for owner %s and index %d is %s\n", ownerEOA, accountIndex, address)

User Operation Creation and Execution

Create User Operation

Use client.NewUserOp to create a new user operation.

// NewUserOp builds a new UserOperation and fills all the fields.
//
// Parameters:
//   - ctx - is the context of the operation.
//   - smartWallet - is the address of the smart wallet that will execute the user operation.
//   - signer - is the signer function that will sign the user operation.
//   - calls - is the list of calls to be executed in the user operation.
//   - walletDeploymentOpts - are the options for the smart wallet deployment. Can be nil if the smart wallet is already deployed.
//
// Returns:
//   - UserOperation - user operation with all fields filled in.
//   - error - if failed to build the user operation.
func (c *backend) NewUserOp(
  ctx context.Context,
  smartWallet common.Address,
  signer Signer,
  calls []Call,
  walletDeploymentOpts *WalletDeploymentOpts,
) (UserOperation, error)

Usage example:

sender := common.HexToAddress("0xsmartWalletAddress")
call := userop.Call{
    To:      common.HexToAddress("0xtoAddress"),
    Value:   1000000000000000000, // 1 ETH in wei
}

ctx := context.Background()
userOp, err := client.NewUserOp(ctx, sender, signer, userop.Call[]{call}, nil)
if err != nil {
    log.Fatal("Error creating User Operation:", err)
}
fmt.Printf("User Operation created: %v\n", userOp)

Send User Operation to Bundler

Use client.SendUserOp to send a user operation to the client bundler for execution.

type Receipt struct {
  UserOpHash    common.Hash
  TxHash        common.Hash
  Sender        common.Address
  Nonce         decimal.Decimal
  Success       bool
  ActualGasCost decimal.Decimal
  ActualGasUsed decimal.Decimal
  RevertData    []byte // non-empty if Success is false and EntryPoint was able to catch revert reason.
}

// SendUserOp submits a user operation to a bundler and returns a channel to await for the userOp receipt.
//
// Parameters:
//   - ctx - is the context of the operation.
//   - op - is the user operation to be sent.
//
// Returns:
//   - <-chan Receipt - a channel to await for the userOp receipt.
//   - error - if failed to send the user operation
func SendUserOp(ctx context.Context, op UserOperation) (done <-chan Receipt, err error)

Usage example:

ctx := context.Background()
receiptChannel, err := client.SendUserOp(ctx, userOp)
if err != nil {
    log.Fatal("Error sending User Operation to bundler:", err)
}

// Await the receipt from the channel
result := <-receiptChannel
fmt.Printf("User Operation result: %v\n", result)

Contributing

Feel free to contribute by opening issues or submitting pull requests to this repository.

License

This UserOp library is licensed under the MIT License.

# Packages

No description provided by the author

# Functions

NewClient is a factory that builds a new user operation client based on the provided configuration.
NewClientConfigFromEnv reads the client configuration from environment variables.
NewClientConfigFromFile reads the client configuration from a file.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Variables

ErrInvalidECDSAValidatorAddress is returned when the ECDSA validator address is invalid.
ErrInvalidEntryPointAddress is returned when the entrypoint address is invalid.
ErrInvalidFactoryAddress is returned when the factory address is invalid.
ErrInvalidLogicAddress is returned when the logic address is invalid.
ErrInvalidPaymasterAddress is returned when the paymaster address is invalid.
ErrInvalidPaymasterURL is returned when the paymaster URL is invalid.
ErrInvalidPollDuration is returned when blockchain poll duration is non-present or its format is invalid.
ErrNoCalls is returned when the calls are not specified.
ErrNoSigner is returned when the signer is not specified.
ErrNoWalletDeploymentOpts is returned when the wallet deployment opts are required to build and submit userop, but they are not specified.
ErrNoWalletOwnerInWDO is returned when the wallet owner is not specified.
ErrPaymasterNotSupported is returned on attempt to build client with an unsupported paymaster type.
not tested.
not tested.
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

BiconomyERC20Config represents the configuration for the Biconomy ERC20 paymaster.
BiconomySmartAccountInfo represents the configuration for the Biconomy smart contract that sponsors transactions.
BiconomySponsoringConfig represents the configuration for the Biconomy Sponsoring paymaster.
BiconomySponsorshipInfoConfig represents the configuration for transaction sponsoring for the Biconomy Sponsoring paymaster.
BiconomyTokenInfo represents the token used to pay for fees for the Biconomy paymaster.
ClientConfig represents the configuration for the user operation client.
GasConfig represents the configuration for the userop transaction gas fees.
GasEstimate holds gas estimates for a user operation.
These override the bundler's estimation.
These override provider's estimation.
No description provided by the author
Implements a nonce key rotation for 2d nonces.
Each field overrides the corresponding middleware during the user operation creation.
PaymasterConfig represents the configuration for the paymaster to be used with the client.
PaymasterType represents an enum for supported ERC-4337 paymaster that can be used with the client to sponsor user operations.
PimlicoERC20Config represents the configuration for the Pimlico ERC20 paymaster.
PimlicoVerifyingConfig represents the configuration for the Pimlico Verifying paymaster.
No description provided by the author
UserOperation represents an EIP-4337 style transaction for a smart contract account.
WalletDeploymentOpts represents data required 1.

# Interfaces

Client represents a client for creating and posting user operations.
ECDSASigner represents a handler that signs a message using ecdsa private key.
No description provided by the author
No description provided by the author

# Type aliases

Signer represents a handler that signs a user operation.