Skip to content

The core verification instruction. Verifies that a message was signed by the trusted backend and that the signature is valid for the transaction signer.

pub fn verify_range(
ctx: Context<VerifyRange>,
signature: [u8; 64],
message: Vec<u8>,
) -> Result<()>
ParameterTypeDescription
signature[u8; 64]Ed25519 signature from the backend
messageVec<u8>The signed message bytes
AccountTypeDescription
signerSignerTransaction signer (must match pubkey in message)
settingsAccount<Settings>Settings PDA containing verification config
instructions_sysvarAccountInfoInstructions sysvar for Ed25519 verification

The message must follow this format:

{timestamp}_{pubkey}
  • timestamp: Unix timestamp (seconds) when the backend signed
  • pubkey: Base58-encoded public key of the user

Example: 1704067200_7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU

import { buildVerifyRangeInstruction } from './codama-ts-range-custom';
const instruction = await buildVerifyRangeInstruction({
signer: userPublicKey,
admin: settingsAdminPublicKey,
signature: new Uint8Array(signatureBytes),
message: new Uint8Array(messageBytes),
});
const transaction = new Transaction().add(instruction);
await sendTransaction(transaction);
await program.methods
.verifyRange(
Array.from(signature),
Buffer.from(message)
)
.accounts({
signer: user.publicKey,
settings: settingsPda,
instructionsSysvar: SYSVAR_INSTRUCTIONS_PUBKEY,
})
.rpc();

The instruction performs these checks in order:

  1. Signature Verification: Verify Ed25519 signature against settings.range_signer
  2. Message Parsing: Extract timestamp and pubkey from message
  3. Timestamp Validation: Check timestamp is within settings.window_size
  4. Signer Validation: Verify message pubkey matches transaction signer
ErrorCodeCause
CouldntVerifySignature6005Signature doesn’t match range_signer
TimestampParsingFailed6000Invalid timestamp in message
PubkeyParsingFailed6001Invalid pubkey in message
WrongMessageSplitLength6002Message format incorrect
WrongSigner6003Message pubkey doesn’t match tx signer
TimestampOutOfWindow6004Signature expired or from future
import { Connection, Keypair, Transaction } from '@solana/web3.js';
import { buildVerifyRangeInstruction } from './codama-ts-range-custom';
import nacl from 'tweetnacl';
async function verifySignature(
connection: Connection,
user: Keypair,
settingsAdmin: PublicKey,
rangeSignerKeypair: Keypair // Backend's keypair
) {
// 1. Create the message (normally done on backend)
const timestamp = Math.floor(Date.now() / 1000);
const message = `${timestamp}_${user.publicKey.toBase58()}`;
const messageBytes = Buffer.from(message);
// 2. Sign with backend key (normally done on backend)
const signature = nacl.sign.detached(
messageBytes,
rangeSignerKeypair.secretKey
);
// 3. Create verification instruction
const instruction = await buildVerifyRangeInstruction({
signer: user.publicKey,
admin: settingsAdmin,
signature: new Uint8Array(signature),
message: new Uint8Array(messageBytes),
});
// 4. Send transaction
const transaction = new Transaction().add(instruction);
transaction.feePayer = user.publicKey;
transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
transaction.sign(user);
const txSignature = await connection.sendRawTransaction(transaction.serialize());
console.log('Verification successful:', txSignature);
}

On success, emits a VerificationSuccess event:

{
signer: Pubkey,
timestamp: i64,
signature: [u8; 64],
message: Vec<u8>,
clock_slot: u64,
clock_timestamp: i64,
settings_admin: Pubkey,
}

See Events for details on listening to events.