Documentation Index Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
Use this file to discover all available pages before exploring further.
Try it Live Run Authorization examples in the interactive playground
Validation
Authorization structure and signature validation.
validate
Validate authorization structure and signature parameters.
Authorization . validate . call ( auth : Authorization . Item ): void
Parameters:
auth: Authorization to validate
Throws: Authorization.ValidationError if invalid
Validation Checks:
Chain ID must be non-zero
Address cannot be zero address
yParity must be 0 or 1
Signature r must be non-zero
Signature s must be non-zero
r must be < SECP256K1_N (curve order)
s must be ≤ SECP256K1_HALF_N (prevents malleable signatures)
Basic Usage
import { Authorization } from 'tevm' ;
const auth : Authorization . Item = {
chainId: 1 n ,
address: contractAddress ,
nonce: 0 n ,
yParity: 0 ,
r: 0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef n ,
s: 0x0edcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 n
};
try {
Authorization . validate . call ( auth );
console . log ( 'Authorization is valid' );
} catch ( e ) {
if ( e instanceof Authorization . ValidationError ) {
console . error ( `Invalid: ${ e . message } ` );
}
}
Validation Errors
ValidationError
class ValidationError extends Error {
name : 'ValidationError' ;
}
Error Messages:
"Chain ID must be non-zero" - chainId is 0
"Address cannot be zero address" - address is 0x0000…0000
"yParity must be 0 or 1" - yParity not 0 or 1
"Signature r cannot be zero" - r is 0
"Signature s cannot be zero" - s is 0
"Signature r must be less than curve order" - r >= SECP256K1_N
"Signature s too high (malleable signature)" - s > SECP256K1_HALF_N
Validation Rules
Chain ID
Chain ID must be non-zero to indicate which chain authorization is valid on.
// Valid
const auth = { chainId: 1 n , ... }; // Mainnet
const auth = { chainId: 137 n , ... }; // Polygon
// Invalid
const auth = { chainId: 0 n , ... }; // Throws ValidationError
Rationale: Zero chain ID is reserved and indicates invalid authorization.
Zero Address
Address cannot be zero address (0x0000…0000).
// Valid
const auth = {
address: Address ( '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2' ),
...
};
// Invalid
const auth = {
address: Address . zero (), // Throws ValidationError
...
};
Rationale: Zero address has no code and delegation would be meaningless.
yParity
yParity must be 0 or 1 (ECDSA signature parity bit).
// Valid
const auth = { yParity: 0 , ... };
const auth = { yParity: 1 , ... };
// Invalid
const auth = { yParity: 2 , ... }; // Throws ValidationError
const auth = { yParity: 27 , ... }; // Throws ValidationError (use 0/1, not 27/28)
Rationale: ECDSA signatures have two possible y-coordinates (even/odd). yParity indicates which.
Signature r
Signature r must be non-zero and less than curve order.
// Valid
const auth = {
r: 0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef n ,
...
};
// Invalid
const auth = { r: 0 n , ... }; // Throws ValidationError
// Invalid (r >= SECP256K1_N)
const auth = {
r: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 n ,
...
}; // Throws ValidationError
Rationale: r is ECDSA signature component and must be in valid range [1, N-1].
Signature s
Signature s must be non-zero and at most SECP256K1_HALF_N.
// Valid (s <= N/2)
const auth = {
s: 0x0edcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 n ,
...
};
// Invalid (s = 0)
const auth = { s: 0 n , ... }; // Throws ValidationError
// Invalid (s > N/2 - malleable)
const auth = {
s: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 n ,
...
}; // Throws ValidationError
Rationale: Restricting s ≤ N/2 prevents signature malleability. For any valid signature (r, s), there exists another valid signature (r, -s mod N). Requiring s ≤ N/2 ensures only one canonical signature.
Malleability Prevention
What is Signature Malleability?
ECDSA signatures have inherent malleability: given valid signature (r, s), signature (r, N - s) is also valid for same message.
Without malleability protection:
// Both signatures are valid for same authorization
const sig1 = { r , s: 0x123 n , v: 0 };
const sig2 = { r , s: SECP256K1_N - 0x123 n , v: 1 };
Protection
validate() rejects high s values:
import { Authorization } from 'tevm' ;
const auth = {
chainId: 1 n ,
address: contractAddress ,
nonce: 0 n ,
yParity: 1 ,
r: 0x123 n ,
s: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364000 n // High s
};
// Throws: "Signature s too high (malleable signature)"
Authorization . validate . call ( auth );
Normalizing High s
If you receive signature with high s, normalize it:
function normalizeSignature ( r : bigint , s : bigint , v : number ) : {
r : bigint ;
s : bigint ;
v : number ;
} {
const SECP256K1_N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 n ;
const SECP256K1_HALF_N = SECP256K1_N >> 1 n ;
if ( s > SECP256K1_HALF_N ) {
return {
r ,
s: SECP256K1_N - s ,
v: v === 0 ? 1 : 0 // Flip parity
};
}
return { r , s , v };
}
Validation Patterns
Validate Before Processing
Always validate before any operations:
import { Authorization } from 'tevm' ;
function processAuthorization ( auth : Authorization . Item ) : void {
// Validate first
Authorization . validate . call ( auth );
// Then process
const authority = Authorization . verify . call ( auth );
const gas = Authorization . getGasCost . call ( auth , false );
console . log ( `Authority: ${ authority } , Gas: ${ gas } ` );
}
Batch Validation
Validate entire authorization list:
import { Authorization } from 'tevm' ;
function validateAuthList ( authList : Authorization . Item []) : {
valid : Authorization . Item [];
invalid : Array <{ auth : Authorization . Item ; error : string }>;
} {
const valid : Authorization . Item [] = [];
const invalid : Array <{ auth : Authorization . Item ; error : string }> = [];
for ( const auth of authList ) {
try {
Authorization . validate . call ( auth );
valid . push ( auth );
} catch ( e ) {
if ( e instanceof Authorization . ValidationError ) {
invalid . push ({ auth , error: e . message });
} else {
invalid . push ({ auth , error: String ( e ) });
}
}
}
return { valid , invalid };
}
const { valid , invalid } = validateAuthList ( authList );
console . log ( `Valid: ${ valid . length } , Invalid: ${ invalid . length } ` );
Early Validation
Validate as soon as authorization is received:
import { Authorization } from 'tevm' ;
class AuthorizationQueue {
private queue : Authorization . Item [] = [];
add ( auth : Authorization . Item ) : void {
// Validate immediately
Authorization . validate . call ( auth );
// Only add valid authorizations
this . queue . push ( auth );
}
processAll () : void {
// All items in queue are pre-validated
for ( const auth of this . queue ) {
this . process ( auth );
}
}
private process ( auth : Authorization . Item ) : void {
// No need to validate again
const authority = Authorization . verify . call ( auth );
console . log ( `Processing: ${ authority } ` );
}
}
Validation with Type Guards
Combine type guards with validation:
import { Authorization } from 'tevm' ;
function validateUnknown ( data : unknown ) : Authorization . Item {
// First check type
if ( ! Authorization . isItem ( data )) {
throw new Error ( 'Not an authorization item' );
}
// Then validate structure
Authorization . validate . call ( data );
return data ;
}
const auth = validateUnknown ( apiResponse );
Error Handling
Handle specific validation errors:
import { Authorization } from 'tevm' ;
function validateWithHandling ( auth : Authorization . Item ) : boolean {
try {
Authorization . validate . call ( auth );
return true ;
} catch ( e ) {
if ( ! ( e instanceof Authorization . ValidationError )) {
throw e ; // Re-throw non-validation errors
}
// Handle specific errors
if ( e . message . includes ( 'Chain ID' )) {
console . error ( 'Invalid chain ID' );
} else if ( e . message . includes ( 'Address' )) {
console . error ( 'Invalid address' );
} else if ( e . message . includes ( 'malleable' )) {
console . error ( 'Signature malleability detected' );
} else {
console . error ( `Validation failed: ${ e . message } ` );
}
return false ;
}
}
Validation Cost
Validation is O(1) with constant-time checks:
// All checks are constant time
- chainId !== 0 n // O(1)
- address . every () // O(20) = O(1)
- yParity === 0 || 1 // O(1)
- r !== 0 n // O(1)
- s !== 0 n // O(1)
- r < SECP256K1_N // O(1)
- s <= SECP256K1_HALF_N // O(1)
Optimization
Validation is already optimized. For batch validation, consider:
// Sequential validation
for ( const auth of authList ) {
Authorization . validate . call ( auth );
}
// Stop on first error
function validateAll ( authList : Authorization . Item []) : void {
for ( const auth of authList ) {
Authorization . validate . call ( auth ); // Throws on first error
}
}
// Collect all errors
function validateAllWithErrors ( authList : Authorization . Item []) : string [] {
const errors : string [] = [];
for ( const auth of authList ) {
try {
Authorization . validate . call ( auth );
} catch ( e ) {
if ( e instanceof Authorization . ValidationError ) {
errors . push ( e . message );
}
}
}
return errors ;
}
Testing
Valid Authorization
import { Authorization , Address } from 'tevm' ;
const validAuth : Authorization . Item = {
chainId: 1 n ,
address: Address ( '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb2' ),
nonce: 0 n ,
yParity: 0 ,
r: 0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef n ,
s: 0x0edcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 n
};
// Should not throw
Authorization . validate . call ( validAuth );
Invalid Cases
import { Authorization } from 'tevm' ;
// Zero chain ID
expect (() => {
Authorization . validate . call ({ ... validAuth , chainId: 0 n });
}). toThrow ( 'Chain ID must be non-zero' );
// Zero address
expect (() => {
Authorization . validate . call ({ ... validAuth , address: Address . zero () });
}). toThrow ( 'Address cannot be zero address' );
// Invalid yParity
expect (() => {
Authorization . validate . call ({ ... validAuth , yParity: 2 });
}). toThrow ( 'yParity must be 0 or 1' );
// Zero r
expect (() => {
Authorization . validate . call ({ ... validAuth , r: 0 n });
}). toThrow ( 'Signature r cannot be zero' );
// Zero s
expect (() => {
Authorization . validate . call ({ ... validAuth , s: 0 n });
}). toThrow ( 'Signature s cannot be zero' );
// High s (malleable)
expect (() => {
Authorization . validate . call ({
... validAuth ,
s: Authorization . SECP256K1_HALF_N + 1 n
});
}). toThrow ( 'Signature s too high (malleable signature)' );
See Also