Skip to main content

Overview

Use Dynamic Server Wallets with ZeroDev to sponsor gas for server-initiated EVM transactions. This guide covers both authenticated server wallets (Node EVM) and delegated access flows.
This works exactly like end‑user sponsorship: ZeroDev paymaster/bundler settings are read from your Dynamic Dashboard configuration. No extra server‑side configuration is required beyond enabling Zerodev and setting project IDs in the dashboard.
Make sure you have configured Zerodev project IDs and gas policies, and enabled sponsorship in the Dynamic Dashboard under Sponsor Gas.

Prerequisites

  • Zerodev project(s) and gas policy per network
  • Dynamic Dashboard → Sponsor Gas → enable Zerodev and add project IDs (same as for end users)
  • Environment variables for your Dynamic environment and server credentials

Node EVM: Sponsor gas for a server wallet

1) Authenticate and create a server wallet (or use an existing one)

import { DynamicEvmWalletClient } from '@dynamic-labs-wallet/node-evm'
import { ThresholdSignatureScheme } from '@dynamic-labs-wallet/node'

const ENVIRONMENT_ID = process.env.DYNAMIC_ENVIRONMENT_ID as string
const AUTH_TOKEN = process.env.DYNAMIC_API_TOKEN as string

export const authenticatedEvmClient = async () => {
  const client = new DynamicEvmWalletClient({ environmentId: ENVIRONMENT_ID })
  await client.authenticateApiToken(AUTH_TOKEN)
  return client
}

const evmClient = await authenticatedEvmClient()

// Create a new wallet if you don't already have one
const wallet = await evmClient.createWalletAccount({
  thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
})

2) Create a ZeroDev client and kernel client with sponsorship

import { createZerodevClient } from '@dynamic-labs-wallet/node-evm';

const zerodevClient = await createZerodevClient(evmClient)

const kernelClient = await zerodevClient.createKernelClientForAddress({
  address: wallet.accountAddress as `0x${string}`,
  networkId: '11155111', // Example: Sepolia
  externalServerKeyShares: wallet.externalServerKeyShares,
  withSponsorship: true,
})

3) Send a sponsored transaction

const txHash = await kernelClient.sendTransaction({
  to: '0xRecipientAddress' as `0x${string}`,
  value: BigInt(0),
})

console.log('Transaction Hash:', txHash)

Delegated Access: Sponsor gas for an end-user wallet from your server

Use delegated credentials (walletId, walletApiKey, keyShare) to act on behalf of a user who granted permission.

1) Create a delegated EVM client

import { createDelegatedEvmWalletClient } from '@dynamic-labs-wallet/node-evm'

const ENVIRONMENT_ID = process.env.DYNAMIC_ENVIRONMENT_ID as string
const SERVER_API_KEY = process.env.DYNAMIC_SERVER_API_KEY as string

const delegatedClient = createDelegatedEvmWalletClient({
  environmentId: ENVIRONMENT_ID,
  apiKey: SERVER_API_KEY,
})

2) Create a ZeroDev client and kernel client with delegated signing and sponsorship

import { createZerodevClient } from '@dynamic-labs-wallet/node-evm';

const zerodevClient = await createZerodevClient(delegatedClient)

const kernelClient = await zerodevClient.createKernelClientForAddress({
  address: '0xUserWalletAddress' as `0x${string}`,
  networkId: '11155111', // Example: Sepolia
  delegated: {
    delegatedClient,
    walletId: '<WALLET_ID>',
    walletApiKey: '<WALLET_API_KEY>',
    keyShare: {/* ServerKeyShare object from webhook */},
  },
  withSponsorship: true,
})

3) Send a sponsored transaction

try {
  const txHash = await kernelClient.sendTransaction({
    to: '0xRecipientAddress' as `0x${string}`,
    value: BigInt(0),
  })
  console.log('Transaction Hash:', txHash)
} catch (error) {
  console.error('Transaction failed:', error)
}

Tips

  • Ensure your Zerodev project IDs and gas policies are configured for each target network.
  • Use environment variables for all secrets. Never log or expose wallet key shares or per‑wallet API keys.
  • If a sponsorship policy denies a transaction, you can retry without sponsorship by creating the kernel client with withSponsorship: false.
I