Skip to main content

Atlas SDK Quickstart

The Atlas SDK is a TypeScript library designed to interact with the Atlas protocol. It provides a set of tools and interfaces to work with user operations, solver operations, and dApp operations within the Atlas ecosystem.

Installation

To use the Atlas SDK in your TypeScript project, install it using npm or yarn:

npm install atlas-sdk

or

yarn add atlas-sdk

Core Components

AtlasSdk

The AtlasSdk class is the main entry point for interacting with the Atlas protocol. It provides methods for creating and managing operations, simulating transactions, and interacting with the Atlas contracts.

Initialization

import { JsonRpcProvider } from "ethers";
import { AtlasSdk } from "atlas-sdk";
import { MockBackend } from "atlas-sdk/backend";

const chainId = 11155111; // Replace with your network's chain ID
const provider = new JsonRpcProvider("https://rpc.sepolia.org/", chainId);
const backend = new MockBackend(); // Replace with your backend implementation

const sdk = new AtlasSdk(provider, chainId, backend);

Key Methods

  • newUserOperation(userOpParams: UserOperationParams, generateSessionKey?: boolean): Promise<UserOperation>
  • signUserOperation(userOp: UserOperation, signer: AbstractSigner): Promise<UserOperation>
  • submitUserOperation(userOp: UserOperation, hints?: string[]): Promise<SolverOperation[]>
  • sortSolverOperations(userOp: UserOperation, solverOps: SolverOperation[]): Promise<SolverOperation[]>
  • createDAppOperation(userOp: UserOperation, solverOps: SolverOperation[], bundler?: string): Promise<DAppOperation>
  • getMetacallCalldata(userOp: UserOperation, solverOps: SolverOperation[], dAppOp: DAppOperation): string
  • submitBundle(userOp: UserOperation, solverOps: SolverOperation[], dAppOp: DAppOperation): Promise<string>

Configuration

Proper configuration is essential for the seamless operation of the Atlas SDK. This section outlines the necessary configuration parameters and their usage.

Chain Configuration

Specify the blockchain network's chain ID and RPC endpoint.

const chainId = 11155111; // Replace with your network's chain ID
const rpcEndpoint = "https://rpc.sepolia.org/"; // Replace with your network's RPC URL

Backend Configuration

Select and configure the appropriate backend client based on your environment.

import { MockBackend, FastlaneBackend } from "atlas-sdk/backend";

// For testing purposes
const backend = new MockBackend();

// For production use
const backend = new FastlaneBackend({ basePath: "https://api.your-backend.com" });

Note: Ensure that the backend implementation aligns with your deployment environment.

EIP-712 Domain Configuration

Ensure that the EIP-712 domain is correctly set to enable signing and verification of operations.

import { chainConfig } from "atlas-sdk/config";

const eip712Domain = chainConfig[chainId].eip712Domain;

Ensure that the chainConfig accurately reflects your network's configuration.

Types

The SDK defines several important types for working with Atlas operations:

UserOperationParams

Defines the parameters required to create a user operation.

interface UserOperationParams {
from: string;
to?: string;
value?: bigint;
gas?: bigint;
maxFeePerGas?: bigint;
nonce?: bigint;
deadline?: bigint;
dapp?: string;
control?: string;
callConfig?: bigint;
sessionKey?: string;
data?: string;
signature?: string;
}

IBackend

Interface defining the backend client responsible for handling operations. Different backend implementations (e.g., MockBackend, FastlaneBackend) should adhere to this interface.

interface IBackend {
addHooksControllers(hooksControllers: IHooksControllerConstructable[]): void;
submitUserOperation(userOp: UserOperation, hints: string[]): Promise<string>;
getSolverOperations(
userOp: UserOperation,
userOpHash: string,
waitForSolvers: boolean
): Promise<SolverOperation[]>;
submitBundle(bundle: Bundle): Promise<string>;
getBundleHash(userOpHash: string, waitForBundle: boolean): Promise<string>;
}

UserOperation

Represents a user operation within the Atlas network.

class UserOperation {
from: string;
to: string;
value: bigint;
gas: bigint;
maxFeePerGas: bigint;
nonce: bigint;
deadline: bigint;
dapp: string;
control: string;
callConfig: bigint;
sessionKey: string;
data: string;
signature: string;

constructor(params: UserOperationParams) {
// ... initialization ...
}

// Additional methods for validation, signing, etc.
}

SolverOperation

Represents a solver operation that processes user operations.

class SolverOperation {
from: string;
to: string;
value: bigint;
gas: bigint;
maxFeePerGas: bigint;
deadline: bigint;
solver: string;
control: string;
userOpHash: string;
bidToken: string;
bidAmount: bigint;
data: string;
signature: string;
score: number;

constructor(params: SolverOperationParams, score: number = 0) {
// ... initialization ...
}

// Additional methods for validation, scoring, etc.
}

DAppOperation

Represents a dApp operation that coordinates user and solver operations.

class DAppOperation {
from: string;
to: string;
nonce: bigint;
deadline: bigint;
control: string;
bundler: string;
userOpHash: string;
callChainHash: string;
signature: string;

constructor(params: DAppOperationParams) {
// ... initialization ...
}

// Additional methods for validation, signing, etc.
}

Utilities

The SDK provides various utility functions to help with common tasks:

Validation Functions

Ensure that inputs conform to expected formats and constraints.

import { isAddress } from "ethers";

export function validateAddress(address: string): boolean {
return isAddress(address) && address.length === 42;
}

export function validateUint32(value: bigint): boolean {
return value <= 2n ** 32n - 1n;
}

export function validateUint256(value: bigint): boolean {
return value <= 2n ** 256n - 1n;
}

export function validateBytes32(value: string): boolean {
return /^0x[0-9a-f]{64}$/.test(value);
}

export function validateBytes(value: string): boolean {
return /^0x([0-9a-f][0-9a-f])*$/.test(value);
}

Encoding Functions

Encode operations using ABI encoding standards.

import { AbiCoder } from "ethers";

const abiCoder = new AbiCoder();

export function abiEncodeUserOperation(userOp: UserOperation): string {
const fields = [
userOp.from,
userOp.to,
userOp.value,
userOp.gas,
userOp.maxFeePerGas,
userOp.nonce,
userOp.deadline,
userOp.dapp,
userOp.control,
userOp.callConfig,
userOp.sessionKey,
userOp.data,
userOp.signature,
];
return abiCoder.encode(
["tuple(address from,address to,uint256 value,uint256 gas,uint256 maxFeePerGas,uint256 nonce,uint256 deadline,address dapp,address control,uint32 callConfig,address sessionKey,bytes data,bytes signature)"],
[fields]
);
}

Call Chain Hash Computation

Compute the call chain hash for operations.

import { keccak256, solidityPacked } from "ethers";
import { UserOperation, SolverOperation } from "../operation";

export function getCallChainHash(
userOp: UserOperation,
solverOps: SolverOperation[],
requirePreOps: boolean,
dAppControl: string
): string {
let callSequence = "0x";

if (requirePreOps) {
callSequence = solidityPacked(["address"], [dAppControl]);
}

callSequence = solidityPacked(
["bytes", "bytes", "bytes"],
[
callSequence,
userOp.abiEncode(),
SolverOperation.abiEncodeArray(solverOps),
]
);

return keccak256(callSequence);
}

Signing and Recovery

Ensure the authenticity and integrity of operations through digital signatures.

Signing Operations

Use Ethereum signers to sign operations following the EIP-712 standard.

import { Wallet } from "ethers";

// Initialize signer
const signer = Wallet.fromMnemonic("your mnemonic phrase");

// Sign a user operation
const signedUserOp = await sdk.signUserOperation(userOp, signer);

Verifying Signatures

Validate the signatures to ensure operations are authorized.

import { verifyTypedData } from "ethers";

const signerAddress = verifyTypedData(
eip712Domain,
userOp.toTypedDataTypes(),
userOp.toTypedDataValues(),
userOp.signature
);

if (signerAddress !== userOp.from) {
throw new Error("Invalid signature");
}

Call Config Flags

Configure operation behaviors using predefined flags. These flags control various aspects of how operations are processed.

Flag Definitions

export enum CallConfigIndex {
UserNoncesSequential,
DAppNoncesSequential,
RequirePreOps,
TrackPreOpsReturnData,
TrackUserReturnData,
DelegateUser,
PreSolver,
PostSolver,
RequirePostOpsCall,
ZeroSolvers,
ReuseUserOp,
UserAuctioneer,
SolverAuctioneer,
UnknownAuctioneer,
VerifyCallChainHash,
ForwardReturnData,
RequireFulfillment,
TrustedOpHash,
InvertBidValue,
ExPostBids,
AllowAllocateValueFailure,
}

Flag Utilities

Check if specific flags are set within the callConfig.

export function flagUserNoncesSequential(callConfig: number): boolean {
return (
(Number(callConfig) & (1 << CallConfigIndex.UserNoncesSequential)) != 0
);
}

export function flagZeroSolvers(callConfig: number): boolean {
return (Number(callConfig) & (1 << CallConfigIndex.ZeroSolvers)) != 0;
}

// Add similar functions for other flags as needed

Example Usage:

const callConfig = userOp.callConfig();

if (flagUserNoncesSequential(callConfig)) {
console.log("User nonces are sequential.");
}

if (flagZeroSolvers(callConfig)) {
console.log("Zero solvers are allowed.");
}

Usage Examples

Practical examples demonstrate how to utilize the Atlas SDK's key functionalities.

Initializing the SDK

import { JsonRpcProvider } from "ethers";
import { AtlasSdk } from "atlas-sdk";
import { MockBackend } from "atlas-sdk/backend";

const chainId = 11155111; // Replace with your network's chain ID
const provider = new JsonRpcProvider("https://rpc.sepolia.org/", chainId);
const backend = new MockBackend(); // Replace with your backend implementation

const sdk = new AtlasSdk(provider, chainId, backend);

Creating and Simulating a User Operation

import { Wallet } from "ethers";

// Initialize signer
const signer = Wallet.fromMnemonic("your mnemonic phrase");

// Define user operation parameters
const userOpParams: UserOperationParams = {
from: signer.address,
to: "0xRecipientAddress",
value: 0n,
gas: 100000n,
maxFeePerGas: 30000000000n,
deadline: 1638316800n, // Example timestamp
dapp: "0xDAppControlAddress",
control: "0xDAppControlAddress",
callConfig: 0n, // Replace with your call configuration
data: "0x", // Transaction data
};

// Create a new user operation
let userOp = await sdk.newUserOperation(userOpParams, true);

// Sign the user operation
userOp = await sdk.signUserOperation(userOp, signer);

// Submit the user operation
const solverOps = await sdk.submitUserOperation(userOp);

// Sort solver operations
const sortedSolverOps = await sdk.sortSolverOperations(userOp, solverOps);

// Create a dApp operation
const dAppOp = await sdk.createDAppOperation(userOp, sortedSolverOps);

// Submit the bundle
const atlasTxHash = await sdk.submitBundle(userOp, sortedSolverOps, dAppOp);

console.log("Atlas Transaction Hash:", atlasTxHash);

Executing a Metacall

const metacallCalldata = sdk.getMetacallCalldata(userOp, sortedSolverOps, dAppOp);

// Execute the metacall using your provider or smart contract interaction
const tx = await provider.sendTransaction({
to: "0xAtlasContractAddress",
data: metacallCalldata,
// Include other transaction parameters as needed
});

console.log("Metacall Transaction Hash:", tx.hash);

Disclaimer: This SDK is provided "as is" without warranty of any kind. Use at your own risk.