import { Address, PublicActions, concat, hexToBigInt, maxUint256, pad, toHex, } from 'viem'; import { Permit2ABI } from '../abis/Permit2'; import { PERMIT2_ADDRESS } from '../constants'; function defaultGetNowInDays() { return Math.floor(new Date().getTime() / 1000 / 3600 / 24); } export async function getPermit2Nonce( client: PublicActions, account: Address, spender: Address, getNowInDays = defaultGetNowInDays ) { // To reduce gas costs, use the same wordPos for one day const now = getNowInDays(); // wordPos is 248 bits, so the size is 31 bytes const currentWordPos = hexToBigInt( pad(concat([pad(spender, { size: 20 }), toHex(now, { size: 4 })]), { size: 31, dir: 'right', }) ); const { word, bitmap } = await getNextOpenWord( client, PERMIT2_ADDRESS, account, currentWordPos ); return buildNonce(word, getFirstUnsetBit(bitmap)); } async function getNextOpenWord( client: PublicActions, address: Address, account: Address, initialWord: bigint ) { let currentWord = initialWord; let bitmap = 0n; do { bitmap = await client.readContract({ address, abi: Permit2ABI, functionName: 'nonceBitmap', args: [account, BigInt(currentWord)], }); currentWord = currentWord + 1n; } while (bitmap === maxUint256); return { word: currentWord - 1n, bitmap: bitmap, }; } function getFirstUnsetBit(bitmap: bigint): number { // Optimization if switch to library w/ bitwise operators: // return ~bitmap + (bitmap + 1) // instead we have to do a loop for (let i = 0; i < 256; i++) { if ((bitmap / 2n ** BigInt(i)) % 2n === 0n) { return i; } } return -1; } function buildNonce(word: bigint, bitPos: number): bigint { // word << 8 const shiftedWord = word * 256n; return shiftedWord + BigInt(bitPos); }