@@ -6,6 +6,9 @@ import type { ContractTransaction } from '@ethersproject/contracts';
66import getUnixTime from 'date-fns/getUnixTime' ;
77import { v4 } from 'uuid' ;
88import warning from 'tiny-warning' ;
9+ import invariant from 'tiny-invariant' ;
10+ import padEnd from 'lodash/padEnd' ;
11+ import padStart from 'lodash/padStart' ;
912import {
1013 ERC1155__factory ,
1114 ERC20__factory ,
@@ -322,7 +325,9 @@ export const generateErc721Order = (
322325 } ;
323326 } ) ?? [ ] ,
324327 expiry : expiry ,
325- nonce : orderData . nonce ?. toString ( ) ?? generateRandomV4OrderNonce ( ) ,
328+ nonce :
329+ orderData . nonce ?. toString ( ) ??
330+ generateRandomV4OrderNonce ( orderData . appId ) ,
326331 taker : orderData . taker ?. toLowerCase ( ) ?? NULL_ADDRESS ,
327332 } ;
328333
@@ -367,21 +372,81 @@ export const generateErc1155Order = (
367372 } ;
368373 } ) ?? [ ] ,
369374 expiry : expiry ,
370- nonce : orderData . nonce ?. toString ( ) ?? generateRandomV4OrderNonce ( ) ,
375+ nonce :
376+ orderData . nonce ?. toString ( ) ??
377+ generateRandomV4OrderNonce ( orderData . appId ) ,
371378 taker : orderData . taker ?. toLowerCase ( ) ?? NULL_ADDRESS ,
372379 } ;
373380
374381 return erc1155Order ;
375382} ;
376383
384+ // Number of digits in base 10 128bit nonce
385+ // floor(log_10(2^128 - 1)) + 1
386+ export const ONE_TWENTY_EIGHT_BIT_LENGTH = 39 ;
387+
388+ // Max nonce digit length in base 10
389+ // floor(log_10(2^256 - 1)) + 1
390+ export const TWO_FIFTY_SIX_BIT_LENGTH = 78 ;
391+
392+ const checkIfStringContainsOnlyNumbers = ( val : string ) => {
393+ const onlyNumbers = / ^ \d + $ / . test ( val ) ;
394+ return onlyNumbers ;
395+ } ;
396+
397+ export const RESERVED_APP_ID_PREFIX = '1001' ;
398+ const RESERVED_APP_ID_PREFIX_DIGITS = RESERVED_APP_ID_PREFIX . length ;
399+
400+ export const DEFAULT_APP_ID = '314159' ;
401+
402+ const verifyAppIdOrThrow = ( appId : string ) => {
403+ const isCorrectLength =
404+ appId . length <= ONE_TWENTY_EIGHT_BIT_LENGTH - RESERVED_APP_ID_PREFIX_DIGITS ;
405+ const hasOnlyNumbers = checkIfStringContainsOnlyNumbers ( appId ) ;
406+ invariant ( isCorrectLength , 'appId must be 39 digits or less' ) ;
407+ invariant (
408+ hasOnlyNumbers ,
409+ 'appId must be numeric only (no alpha or special characters, only numbers)'
410+ ) ;
411+ } ;
412+
377413/**
414+ * Generates a 256bit nonce.
415+ * The format:
416+ * First 128bits: ${SDK_PREFIX}${APP_ID}000000 (right padded zeroes to fill)
417+ * Second 128bits: ${RANDOM_GENERATED_128BIT_ORDER_HASH}
378418 * @returns 128bit nonce as string (0x orders can handle up to 256 bit nonce)
379419 */
380- export const generateRandomV4OrderNonce = ( ) : string => {
420+ export const generateRandomV4OrderNonce = (
421+ appId : string = DEFAULT_APP_ID
422+ ) : string => {
423+ if ( appId ) {
424+ verifyAppIdOrThrow ( appId ) ;
425+ }
426+ const order128 = padStart (
427+ generateRandom128BitNumber ( ) ,
428+ ONE_TWENTY_EIGHT_BIT_LENGTH ,
429+ '0'
430+ ) ;
431+ const appId128 = padEnd (
432+ `${ RESERVED_APP_ID_PREFIX } ${ appId } ` ,
433+ ONE_TWENTY_EIGHT_BIT_LENGTH ,
434+ '0'
435+ ) ;
436+ const final256BitNonce = `${ appId128 } ${ order128 } ` ;
437+ invariant (
438+ final256BitNonce . length <= TWO_FIFTY_SIX_BIT_LENGTH ,
439+ 'Invalid nonce size'
440+ ) ;
441+ return final256BitNonce ;
442+ } ;
443+
444+ // uuids are 128bits
445+ export const generateRandom128BitNumber = ( base = 10 ) : string => {
381446 const hex = '0x' + v4 ( ) . replace ( / - / g, '' ) ;
382447 const value = BigInt ( hex ) ;
383- const decimal = value . toString ( ) ; // don't convert this to a number, will lose precision
384- return decimal ;
448+ const valueBase10String = value . toString ( base ) ; // don't convert this to a number, will lose precision
449+ return valueBase10String ;
385450} ;
386451
387452export const serializeNftOrder = (
0 commit comments