UNPKG

@deliquifiedlabs/uniswapx-sdk

Version:

SDK for the UniswapX protocol

134 lines 5.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCancelMultipleParams = exports.getCancelSingleParams = exports.setBit = exports.getFirstUnsetBit = exports.buildNonce = exports.splitNonce = exports.NonceManager = void 0; const tslib_1 = require("tslib"); const ethers_1 = require("ethers"); const constants_1 = require("../constants"); const contracts_1 = require("../contracts"); const errors_1 = require("../errors"); /** * Helper to track Permit2 nonces for addresses */ class NonceManager { constructor(provider, chainId, permit2Address) { this.provider = provider; if (permit2Address) { this.permit2 = contracts_1.Permit2__factory.connect(permit2Address, provider); } else if (constants_1.PERMIT2_MAPPING[chainId]) { this.permit2 = contracts_1.Permit2__factory.connect(constants_1.PERMIT2_MAPPING[chainId], this.provider); } else { throw new errors_1.MissingConfiguration("permit2", chainId.toString()); } this.currentWord = new Map(); this.currentBitmap = new Map(); } /** * Finds the next unused nonce and returns it * Marks the nonce as used so it won't be returned again from this instance * NOTE: if any nonce usages are in-flight and created outside of this instance, * this function will not know about them and will return duplicates */ useNonce(address) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { word, bitmap } = yield this.getNextOpenWord(address); const bitPos = getFirstUnsetBit(bitmap); this.currentWord.set(address, word); this.currentBitmap.set(address, setBit(bitmap, bitPos)); return buildNonce(word, bitPos); }); } isUsed(address, nonce) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { word, bitPos } = splitNonce(nonce); const bitmap = yield this.permit2.nonceBitmap(address, word); return bitmap.div(ethers_1.BigNumber.from(2).pow(bitPos)).mod(2).eq(1); }); } // Returns the first word that contains empty bits getNextOpenWord(address) { return tslib_1.__awaiter(this, void 0, void 0, function* () { let currentWord = this.currentWord.get(address) || ethers_1.BigNumber.from(0); let bitmap = this.currentBitmap.get(address) || (yield this.permit2.nonceBitmap(address, currentWord)); while (bitmap.eq(ethers_1.ethers.constants.MaxUint256)) { currentWord = currentWord.add(1); bitmap = yield this.permit2.nonceBitmap(address, currentWord); } return { word: currentWord, bitmap: bitmap, }; }); } } exports.NonceManager = NonceManager; // Splits a permit2 nonce into the word and bitPos function splitNonce(nonce) { const word = nonce.div(256); const bitPos = nonce.mod(256); return { word, bitPos }; } exports.splitNonce = splitNonce; // Builds a permit2 nonce from the given word and bitPos function buildNonce(word, bitPos) { // word << 8 const shiftedWord = word.mul(256); return shiftedWord.add(bitPos); } exports.buildNonce = buildNonce; // Returns the position of the first unset bit // Returns -1 if all bits are set function getFirstUnsetBit(bitmap) { // 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.div(ethers_1.BigNumber.from(2).pow(i)).mod(2).eq(0)) { return i; } } return -1; } exports.getFirstUnsetBit = getFirstUnsetBit; // Returns the given bignumber with the given bit set // Does nothing if the given bit is already set function setBit(bitmap, bitPos) { // Optimization if switch to library w/ bitwise operators: // return bitmap & (1 << bitPos) const mask = ethers_1.BigNumber.from(2).pow(bitPos); if (bitmap.div(mask).mod(2).eq(1)) { return bitmap; } return bitmap.add(mask); } exports.setBit = setBit; // Get parameters to cancel a nonce function getCancelSingleParams(nonceToCancel) { const { word, bitPos } = splitNonce(nonceToCancel); const mask = ethers_1.BigNumber.from(2).pow(bitPos); return { word, mask }; } exports.getCancelSingleParams = getCancelSingleParams; // Get parameters to cancel multiple nonces function getCancelMultipleParams(noncesToCancel) { const splitNonces = noncesToCancel.map(splitNonce); const splitNoncesByWord = {}; splitNonces.forEach((splitNonce) => { const word = splitNonce.word.toString(); if (!splitNoncesByWord[word]) { splitNoncesByWord[word] = []; } splitNoncesByWord[word].push(splitNonce); }); return Object.entries(splitNoncesByWord).map(([word, splitNonce]) => { let mask = ethers_1.BigNumber.from(0); splitNonce.forEach((splitNonce) => { mask = mask.or(ethers_1.BigNumber.from(2).pow(splitNonce.bitPos)); }); return { word: ethers_1.BigNumber.from(word), mask }; }); } exports.getCancelMultipleParams = getCancelMultipleParams; //# sourceMappingURL=NonceManager.js.map