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
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,
})
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,
})
const txHash = await kernelClient.sendTransaction({
to: '0xRecipientAddress' as `0x${string}`,
value: BigInt(0),
})
console.log('Transaction Hash:', txHash)
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,
})
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,
})
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
.