Account Abstraction (AA) enables powerful smart contract wallet functionality like batched transactions, gas sponsorship, and advanced access controls. Para Server SDK supports integration with leading AA providers, allowing you to implement these capabilities in server-side environments.

When using Account Abstraction with Para, your Para wallet acts as the EOA (Externally Owned Account) signer that controls the smart contract wallet. The AA provider deploys and manages the smart contract wallet on-chain, while Para handles the secure signing required for authorizing transactions.

Server-side AA implementation functions identically to client-side implementation, with the additional requirement of signature byte adjustment for contract verification.

Understanding Signature Adjustment

Account Abstraction providers use on-chain signature verification within their smart contracts. Para’s 2/2 MPC wallet produces non-deterministic signatures where the last byte (v recovery byte) may need adjustment for proper on-chain verification.

Critical: Para signatures may require adjustment of the last byte for compatibility with AA providers. Without this adjustment, transactions might fail at the network level despite signature success.

Implementation Steps

Let’s break down the process of implementing Account Abstraction on the server into manageable steps:

1. Set Up Para Server Client

First, initialize the Para Server SDK and authenticate with an imported session:

import { Para as ParaServer, Environment } from "@getpara/server-sdk";

// Initialize Para Server SDK
const para = new ParaServer(Environment.BETA, "YOUR_API_KEY");

// Import a client session
await para.importSession(session);

If you need more details on setup please refer tot he Server Setup Guide.

2. Create a Viem Client

Next, use Para’s Viem integration to create a Viem client:

import { createParaAccount, createParaViemClient } from "@getpara/viem-integration";
import { http } from "viem/transport";
import { arbitrumSepolia } from "viem/chains";

// Create a Para account for Viem
const viemParaAccount = createParaAccount(para);

// Initialize the Viem client
const viemClient = createParaViemClient(para, {
  account: viemParaAccount,
  chain: arbitrumSepolia,
  transport: http("YOUR_RPC_URL"),
});

3. Implement Signature Adjustment

Create a helper function to properly adjust the signature byte for on-chain verification:

import { hashMessage, type Hash, type SignableMessage } from "viem";

function hexStringToBase64(hexString) {
  return Buffer.from(hexString, "hex").toString("base64");
}

// Custom sign message function with signature byte adjustment
async function customSignMessage(para, message) {
  // Get the first wallet from Para client
  const wallet = para.wallets ? Object.values(para.wallets)[0] : null;
  if (!wallet) {
    throw new Error("Para wallet not available for signing.");
  }
  
  // Hash and convert the message
  const hashedMessage = hashMessage(message);
  const messagePayload = hashedMessage.startsWith("0x") ? hashedMessage.substring(2) : hashedMessage;
  const messageBase64 = hexStringToBase64(messagePayload);
  
  // Sign with Para
  const res = await para.signMessage({
    walletId: wallet.id,
    messageBase64: messageBase64,
  });
  
  if (!("signature" in res)) {
    throw new Error("Signature failed");
  }
  
  // Adjust the signature's 'v' value for on-chain verification
  let signature = res.signature;
  const vHex = signature.slice(-2);
  const v = parseInt(vHex, 16);
  
  if (!isNaN(v) && v < 27) {
    const adjustedVHex = (v + 27).toString(16).padStart(2, "0");
    signature = signature.slice(0, -2) + adjustedVHex;
  }
  
  return `0x${signature}`;
}

As mentioned earlier, this function adjusts the last byte of the signature to ensure compatibility with on-chain verification. This is crucial for successful transaction execution when working with Account Abstraction providers.

4. Override Viem Client’s Sign Function

Apply the custom signing function to the Viem client:

// Override sign message function with custom implementation
viemClient.signMessage = async ({ message }) => customSignMessage(para, message);

5. Create the AA Provider Client

Finally, create the Account Abstraction client using your preferred provider. Here’s an example with Alchemy:

import { WalletClientSigner } from "@alchemy/aa-core";
import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy";

// Create Alchemy AA client with the customized Viem client
const walletClientSigner = new WalletClientSigner(viemClient, "para");
const alchemyClient = await createModularAccountAlchemyClient({
  apiKey: "YOUR_ALCHEMY_API_KEY",
  chain: arbitrumSepolia,
  signer: walletClientSigner,
  gasManagerConfig: {
    policyId: "YOUR_ALCHEMY_GAS_POLICY_ID", // Optional: For gas sponsorship
  },
});

Using the AA Smart Wallet

Once your AA client is set up, you can perform transactions through the smart contract wallet:

Get Smart Wallet Address

First, retrieve the smart wallet address (this is different from your Para wallet address):

// Get the smart wallet address
const smartWalletAddress = await alchemyClient.getAddress();
console.log("Smart Wallet Address:", smartWalletAddress);

Be aware that funds should be sent to the smart wallet address (returned by getAddress()) rather than the Para wallet address. The smart wallet is the entity that holds funds and executes transactions on-chain.

Send Transactions

Execute transactions through the smart wallet:

import { parseEther } from "viem";

// Send a simple transfer
const txHash = await alchemyClient.sendTransaction({
  to: "0xRecipientAddress",
  value: parseEther("0.01"),
  data: "0x", // Optional for simple transfers
});

console.log("Transaction Hash:", txHash);

Execute Contract Interactions

Interact with smart contracts through your AA wallet:

// Interact with a smart contract
const tokenContract = "0xTokenContractAddress";
const data = "0x..." // Encoded contract function call

const contractTxHash = await alchemyClient.sendTransaction({
  to: tokenContract,
  data: data,
});

Supported AA Providers

Para integrates with several leading Account Abstraction providers:

Troubleshooting

Best Practices

  1. Always adjust signatures: Implement the signature adjustment function to ensure compatibility with on-chain verification.

  2. Test thoroughly: Before deploying to production, test your AA implementation in a test environment with minimal funds.

  3. Handle errors gracefully: Implement proper error handling for signature failures and transaction rejections.

  4. Monitor gas costs: Be aware of the additional gas costs associated with AA transactions, especially for contract deployments.

  5. Secure session management: Follow secure practices for managing Para sessions in server environments.

  6. Consider gas sponsorship: Many AA providers offer gas sponsorship, which can improve user experience by eliminating gas fees.

Learn More

For more detailed information on Account Abstraction concepts and implementation, refer to our web documentation:

Examples

Explore our server-side Account Abstraction examples: