Skip to main content
Authenticators validate governance actions (proposals, votes, updates) before they are executed on-chain. The SDK provides authenticators for both EVM and Starknet networks.

Overview

Authenticators are responsible for:
  • Creating authenticated contract calls
  • Validating signatures
  • Encoding authentication data
  • Supporting different signature schemes

Getting Authenticators

Authenticators are retrieved based on network configuration:
import { clients } from '@snapshot-labs/sx';

// For EVM networks
const evmAuthenticator = clients.evm.getAuthenticator(
  authenticatorAddress,
  networkConfig
);

// For Starknet
const starknetAuthenticator = clients.starknet.getAuthenticator(
  authenticatorAddress,
  networkConfig
);

EVM Authenticators

Vanilla Authenticator

Direct transaction-based authentication without signatures:
import { createVanillaAuthenticator } from '@snapshot-labs/sx/authenticators/evm';

const authenticator = createVanillaAuthenticator();
Use case: When users submit transactions directly from their wallets.

EthSig Authenticator

Ethereum signature-based authentication using EIP-712:
import { createEthSigAuthenticator } from '@snapshot-labs/sx/authenticators/evm';

const authenticator = createEthSigAuthenticator('ethSig');
// or
const authenticatorV2 = createEthSigAuthenticator('ethSigV2');

const call = authenticator.createCall(
  envelope,     // Contains signature data
  selector,     // Function selector
  calldata      // Function arguments
);
ethSigV2 is an updated version with improved gas efficiency and security features.
Use case: Off-chain signing for gasless voting via relayers.

EthTx Authenticator

Transaction-based authentication with additional validation:
import { createEthTxAuthenticator } from '@snapshot-labs/sx/authenticators/evm';

const authenticator = createEthTxAuthenticator();
Use case: When you need transaction-level validation beyond vanilla authentication.

Starknet Authenticators

Vanilla Authenticator

Direct transaction authentication for Starknet:
import { createVanillaAuthenticator } from '@snapshot-labs/sx/authenticators/starknet';

const authenticator = createVanillaAuthenticator();

EthSig Authenticator

Ethereum signature authentication on Starknet:
import { createEthSigAuthenticator } from '@snapshot-labs/sx/authenticators/starknet';

const authenticator = createEthSigAuthenticator();

// Create a propose call
const call = authenticator.createProposeCall(envelope, {
  author: '0x...',
  metadataUri: 'ipfs://...',
  executionStrategy: {
    address: '0x...',
    params: []
  },
  strategiesParams: []
});

// Create a vote call
const voteCall = authenticator.createVoteCall(envelope, {
  voter: '0x...',
  proposalId: 1,
  choice: 1,
  votingStrategies: [],
  metadataUri: ''
});

// Create an update proposal call
const updateCall = authenticator.createUpdateProposalCall(envelope, {
  author: '0x...',
  proposalId: 1,
  executionStrategy: {
    address: '0x...',
    params: []
  },
  metadataUri: 'ipfs://...'
});
Use case: Cross-chain governance where Ethereum signatures are verified on Starknet.

EthTx Authenticator

Ethereum transaction verification on Starknet:
import { createEthTxAuthenticator } from '@snapshot-labs/sx/authenticators/starknet';

const authenticator = createEthTxAuthenticator();
Use case: Verifying Ethereum L1 transactions on Starknet L2.

StarkSig Authenticator

Native Starknet signature authentication:
import { createStarkSigAuthenticator } from '@snapshot-labs/sx/authenticators/starknet';

const authenticator = createStarkSigAuthenticator();
Use case: When using Starknet wallets and signature schemes.

StarkTx Authenticator

Starknet transaction-based authentication:
import { createStarkTxAuthenticator } from '@snapshot-labs/sx/authenticators/starknet';

const authenticator = createStarkTxAuthenticator();

Authenticator Types

Authenticators implement specific interfaces based on the network:

EVM Authenticator Interface

interface Authenticator {
  type: 'vanilla' | 'ethSig' | 'ethSigV2' | 'ethTx';
  createCall(
    envelope: Envelope<Propose | UpdateProposal | Vote>,
    selector: string,
    calldata: string[]
  ): Call;
}

Starknet Authenticator Interface

interface Authenticator {
  type: 'vanilla' | 'ethSig' | 'ethTx' | 'starkSig' | 'starkTx';
  createProposeCall(envelope: Envelope<Propose>, args: ProposeCallArgs): Call;
  createVoteCall(envelope: Envelope<Vote>, args: VoteCallArgs): Call;
  createUpdateProposalCall(envelope: Envelope<UpdateProposal>, args: UpdateProposalCallArgs): Call;
}

Signature Envelopes

Authenticators use envelopes that contain both the action data and signature:
interface Envelope<T> {
  data: T;
  signatureData?: {
    signature: string;      // The actual signature
    message: {
      salt: string;         // Nonce/salt for replay protection
      // ... other message fields
    };
  };
}

Common Patterns

Gasless Voting with EthSig

// 1. User signs message off-chain
const signature = await signer.signTypedData(domain, types, message);

// 2. Create envelope with signature
const envelope = {
  data: voteData,
  signatureData: {
    signature,
    message: { salt: '0x00' }
  }
};

// 3. Authenticator creates the call
const authenticator = createEthSigAuthenticator('ethSig');
const call = authenticator.createCall(envelope, selector, calldata);

// 4. Relayer submits the transaction

Direct Transaction with Vanilla

// 1. Create envelope without signature
const envelope = {
  data: proposeData
};

// 2. Authenticator creates the call
const authenticator = createVanillaAuthenticator();
const call = authenticator.createCall(envelope, selector, calldata);

// 3. User submits transaction directly

Error Handling

Authenticators throw errors when required data is missing:
try {
  const call = authenticator.createCall(envelope, selector, calldata);
} catch (error) {
  if (error.message.includes('signatureData is required')) {
    // Handle missing signature
  }
}
Always ensure the envelope contains the required signatureData when using signature-based authenticators (EthSig, StarkSig, etc.).

Security Considerations

Replay Protection

Use unique salts in signature messages to prevent replay attacks

Signature Verification

Authenticators verify signatures on-chain before executing actions

Network Matching

Ensure authenticators match the target network (EVM vs Starknet)

Type Safety

Use TypeScript to catch type mismatches at compile time

Utils

Encoding and signature utilities

Strategies

Voting power calculation

Executors

Execution strategies

Build docs developers (and LLMs) love