Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/delegation-core/src/caveats/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { createExactCalldataTerms } from './exactCalldata';
export { createNativeTokenStreamingTerms } from './nativeTokenStreaming';
export { createERC20StreamingTerms } from './erc20Streaming';
export { createERC20TokenPeriodTransferTerms } from './erc20TokenPeriodTransfer';
export { createNonceTerms } from './nonce';
86 changes: 86 additions & 0 deletions packages/delegation-core/src/caveats/nonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { isHexString } from '@metamask/utils';
import type { BytesLike } from '@metamask/utils';

import {
bytesLikeToHex,
defaultOptions,
prepareResult,
type EncodingOptions,
type ResultValue,
} from '../returns';
import type { Hex } from '../types';

// char length of 32 byte hex string (including 0x prefix)
const MAX_NONCE_STRING_LENGTH = 66;

/**
* Terms for configuring a Nonce caveat.
*/
export type NonceTerms = {
/** The nonce as BytesLike (0x-prefixed hex string or Uint8Array) to allow bulk revocation of delegations. */
nonce: BytesLike;
};

/**
* Creates terms for a Nonce caveat that uses a nonce value for bulk revocation of delegations.
*
* @param terms - The terms for the Nonce caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as a 32-byte hex string.
* @throws Error if the nonce is invalid.
*/
export function createNonceTerms(
terms: NonceTerms,
encodingOptions?: EncodingOptions<'hex'>,
): Hex;
export function createNonceTerms(
terms: NonceTerms,
encodingOptions: EncodingOptions<'bytes'>,
): Uint8Array;
/**
* Creates terms for a Nonce caveat that uses a nonce value for bulk revocation of delegations.
*
* @param terms - The terms for the Nonce caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as a 32-byte padded value in the specified encoding format.
* @throws Error if the nonce is invalid or empty.
*/
export function createNonceTerms(
terms: NonceTerms,
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
): Hex | Uint8Array {
const { nonce } = terms;

// Handle zero-length Uint8Array specifically
if (nonce instanceof Uint8Array && nonce.length === 0) {
throw new Error('Invalid nonce: Uint8Array must not be empty');
}

// Validate that strings have 0x prefix (as required by BytesLike)
if (typeof nonce === 'string' && !nonce.startsWith('0x')) {
throw new Error('Invalid nonce: string must have 0x prefix');
}

// Convert to hex string for consistent processing
const hexNonce = bytesLikeToHex(nonce);

// Check for empty hex string (0x) first - more specific error
if (hexNonce === '0x') {
throw new Error('Invalid nonce: must not be empty');
}

if (!isHexString(hexNonce)) {
throw new Error('Invalid nonce: must be a valid BytesLike value');
}

if (hexNonce.length > MAX_NONCE_STRING_LENGTH) {
throw new Error('Invalid nonce: must be 32 bytes or less in length');
}

// Remove '0x' prefix for padding, then add it back
const nonceWithoutPrefix = hexNonce.slice(2);
const paddedNonce = nonceWithoutPrefix.padStart(64, '0'); // 64 hex chars = 32 bytes
const hexValue = `0x${paddedNonce}`;

return prepareResult(hexValue, encodingOptions);
}
1 change: 1 addition & 0 deletions packages/delegation-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
createNativeTokenStreamingTerms,
createERC20StreamingTerms,
createERC20TokenPeriodTransferTerms,
createNonceTerms,
} from './caveats';

export {
Expand Down
Loading
Loading