Skip to main content

Function Signature

revokeDelegation(
  client: DelegatedEvmWalletClient,
  params: {
    walletId: string;
    walletApiKey: string;
  }
): Promise<void>

Description

Revokes delegated access for a specific wallet, invalidating the delegation credentials (wallet API key and key share). After revocation, the credentials can no longer be used for signing operations. Use this function when:
  • A user explicitly requests to revoke delegation
  • Delegation is no longer needed for a workflow
  • Security concerns require immediate credential invalidation
  • Delegation credentials may have been compromised

Parameters

Required Parameters

  • client (DelegatedEvmWalletClient) - The delegated client instance created with createDelegatedEvmWalletClient()
  • walletId (string) - The wallet ID from the delegation webhook
  • walletApiKey (string) - The wallet-specific API key from the delegation webhook

Returns

Promise<void> - Resolves when delegation is successfully revoked

Example

Basic Revocation

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

const delegatedClient = createDelegatedEvmWalletClient({
  environmentId: 'your-environment-id',
  apiKey: 'your-server-api-key',
});

await revokeDelegation(delegatedClient, {
  walletId: 'wallet-id-from-webhook',
  walletApiKey: 'wallet-api-key-from-webhook',
});

console.log('Delegation revoked successfully');

With Error Handling

try {
  await revokeDelegation(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
  });
  
  console.log('Delegation revoked successfully');
  await deleteDelegationCredentials(userId);
} catch (error) {
  console.error('Failed to revoke delegation:', error);
  throw error;
}

Complete Cleanup Flow

async function cleanupDelegation(userId: string): Promise<void> {
  try {
    const credentials = await getDelegationCredentials(userId);
    
    if (!credentials) {
      console.log('No active delegation found');
      return;
    }

    await revokeDelegation(delegatedClient, {
      walletId: credentials.walletId,
      walletApiKey: credentials.walletApiKey,
    });

    await deleteDelegationCredentials(userId);

    await auditLog({
      userId,
      action: 'delegation_revoked',
      timestamp: new Date(),
    });

    console.log('Delegation cleanup complete');
  } catch (error) {
    console.error('Cleanup failed:', error);
    throw error;
  }
}

Common Use Cases

User-Requested Revocation

async function handleUserRevocationRequest(userId: string): Promise<void> {
  const credentials = await getDelegationCredentials(userId);
  
  if (!credentials) {
    throw new Error('No active delegation to revoke');
  }

  await revokeDelegation(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
  });

  await deleteDelegationCredentials(userId);

  await notifyUser(userId, 'Delegation access has been revoked');
}

Automatic Revocation After Task Completion

async function executeTaskWithAutoRevoke(
  userId: string,
  task: () => Promise<void>
): Promise<void> {
  const credentials = await getDelegationCredentials(userId);
  
  try {
    await task();
  } finally {
    await revokeDelegation(delegatedClient, {
      walletId: credentials.walletId,
      walletApiKey: credentials.walletApiKey,
    });
    
    await deleteDelegationCredentials(userId);
  }
}

Batch Revocation

async function revokeAllDelegations(userIds: string[]): Promise<void> {
  const results = await Promise.allSettled(
    userIds.map(async (userId) => {
      const credentials = await getDelegationCredentials(userId);
      
      if (credentials) {
        await revokeDelegation(delegatedClient, {
          walletId: credentials.walletId,
          walletApiKey: credentials.walletApiKey,
        });
        
        await deleteDelegationCredentials(userId);
      }
    })
  );

  const failed = results.filter(r => r.status === 'rejected');
  
  if (failed.length > 0) {
    console.error(`Failed to revoke ${failed.length} delegations`);
  }
}

Security-Triggered Revocation

async function emergencyRevoke(userId: string, reason: string): Promise<void> {
  const credentials = await getDelegationCredentials(userId);
  
  await revokeDelegation(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
  });

  await deleteDelegationCredentials(userId);

  await securityLog({
    userId,
    action: 'emergency_revocation',
    reason,
    timestamp: new Date(),
  });

  await notifySecurityTeam({
    userId,
    reason,
    action: 'delegation_revoked',
  });
}

Error Handling

The function throws an error if:
  • The wallet ID or wallet API key is invalid
  • The delegation has already been revoked
  • Network communication with Dynamic services fails
  • The client is not properly configured
try {
  await revokeDelegation(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
  });
} catch (error) {
  if (error.message.includes('not found')) {
    console.log('Delegation already revoked or does not exist');
  } else if (error.message.includes('unauthorized')) {
    console.error('Invalid credentials');
  } else {
    console.error('Revocation failed:', error);
    throw error;
  }
}

Security Considerations

  • Immediate Cleanup: Delete stored credentials immediately after revocation
  • Audit Logging: Log all revocation operations for security auditing
  • Error Handling: Handle revocation failures gracefully and retry if necessary
  • User Notification: Notify users when their delegation is revoked
  • Idempotency: Ensure revocation is idempotent and can be safely retried

Best Practices

Revocation Service

class DelegationRevocationService {
  private client: DelegatedEvmWalletClient;

  constructor() {
    this.client = createDelegatedEvmWalletClient({
      environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
      apiKey: process.env.DYNAMIC_API_KEY!,
    });
  }

  async revokeWithRetry(
    walletId: string,
    walletApiKey: string,
    maxRetries: number = 3
  ): Promise<void> {
    let lastError: Error | null = null;

    for (let i = 0; i < maxRetries; i++) {
      try {
        await revokeDelegation(this.client, {
          walletId,
          walletApiKey,
        });
        return;
      } catch (error) {
        lastError = error as Error;
        console.warn(`Revocation attempt ${i + 1} failed:`, error);
        
        if (i < maxRetries - 1) {
          await this.delay(Math.pow(2, i) * 1000);
        }
      }
    }

    throw lastError;
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}
I