diff --git a/packages/ensjs/deploy/00_register_concurrently.cjs b/packages/ensjs/deploy/00_register_concurrently.cjs index cd691d9d..00d896ed 100644 --- a/packages/ensjs/deploy/00_register_concurrently.cjs +++ b/packages/ensjs/deploy/00_register_concurrently.cjs @@ -11,6 +11,7 @@ const { } = require('../utils/legacyNameGenerator.cjs') const { makeNonceManager } = require('../utils/nonceManager.cjs') const { encodeFuses } = require('../dist/cjs/utils/fuses') +const { MAX_DATE_INT } = require('../dist/cjs/utils/consts') const DURATION = 31556000 @@ -33,51 +34,16 @@ const DURATION = 31556000 * }[]} */ -const sameExpiryNames = Array.from({ length: 21 }, (_, index) => ({ - label: `same-expiry-legacy-name-${index}`, - type: 'legacy', - namedOwner: 'owner4', - reverseRecord: true, - duration: DURATION / 4, -})) - -const expiryNames = Array.from({ length: 42 }, (_, index) => ({ - label: - index < 21 ? `expiry-subname-${index}` : `no-expiry-subname-${index - 21}`, - namedOwner: 'owner4', - type: 'wrapped', - expiry: index < 21 ? Math.floor(Date.now() / 1000) + 7200 * (index + 1) : 0, - subnameFuses: encodeFuses({ - input: { - parent: { - named: ['PARENT_CANNOT_CONTROL'], - }, - child: { - named: ['CANNOT_UNWRAP'], - }, - }, - }), -})) - const names = [ - ...sameExpiryNames, - // ...expiryNames, - // { - // label: 'concurrent-legacy-name', - // type: 'legacy', - // namedOwner: 'owner4', - // reverseRecord: true, - // duration: 3600, - // }, - // { - // label: 'concurrent-legacy-name-2', - // type: 'legacy', - // namedOwner: 'owner4', - // reverseRecord: true, - // duration: 3600, - // }, - { - label: 'concurrent-wrapped-name', + ...Array.from({ length: 2 }, (_, index) => ({ + label: `concurrent-legacy-name-${index}`, + type: 'legacy', + namedOwner: 'owner4', + reverseRecord: true, + duration: DURATION, + })), + ...Array.from({ length: 2 }, (_, index) => ({ + label: `concurrent-wrapped-name-${index}`, type: 'wrapped', namedOwner: 'owner4', fuses: encodeFuses({ @@ -87,10 +53,26 @@ const names = [ }, }, }), - reverseRecord: true, - duration: DURATION * 3, - subnames: [...expiryNames], - }, + duration: DURATION, + subnames: [ + { + label: `xyz`, + namedOwner: 'owner4', + type: 'wrapped', + expiry: MAX_DATE_INT, + fuses: encodeFuses({ + input: { + parent: { + named: ['PARENT_CANNOT_CONTROL'], + }, + child: { + named: ['CANNOT_UNWRAP'], + }, + }, + }), + }, + ], + })), ] /** @@ -116,14 +98,9 @@ const func = async function (hre) { data = [], reverseRecord = false, fuses = 0, - subnames, duration = 31536000, }) => { console.log(`Committing commitment for ${label}.eth...`) - // console.log( - // `Committing commitment for ${label}.eth (tx: ${commitTx.hash})...`, - // ) - let commitTx if (type === 'legacy') return legacyNameGenerator.commit({ label, @@ -143,14 +120,12 @@ const func = async function (hre) { ) network.provider.send('evm_mine') - console.log('committing...') await Promise.all( commitTxs.map(async (tx) => { return tx.wait() }), ) - console.log('committed') const oldTimestamp = (await ethers.provider.getBlock('latest')).timestamp await network.provider.send('evm_setNextBlockTimestamp', [oldTimestamp + 60]) await network.provider.send('evm_increaseTime', [300]) @@ -170,7 +145,6 @@ const func = async function (hre) { subnames, duration = 31536000, }) => { - let registerTx if (type === 'legacy') return legacyNameGenerator.register({ label, @@ -204,11 +178,7 @@ const func = async function (hre) { label, namedOwner, type, - data = [], - reverseRecord = false, - fuses = 0, subnames, - duration = 31536000, } of names) { if (!subnames) continue console.log(`Setting subnames for ${label}.eth...`) @@ -240,16 +210,7 @@ const func = async function (hre) { } } - console.log( - 'status after registration', - await network.provider.send('txpool_content'), - ) await network.provider.send('evm_mine') - console.log( - 'status after registration', - await network.provider.send('txpool_content'), - ) - return true } diff --git a/packages/ensjs/deploy/00_register_legacy.cjs b/packages/ensjs/deploy/00_register_legacy.cjs index b5707958..3a79078e 100644 --- a/packages/ensjs/deploy/00_register_legacy.cjs +++ b/packages/ensjs/deploy/00_register_legacy.cjs @@ -359,73 +359,24 @@ const names = [ namedOwner: 'owner2', namedAddr: 'owner2', })), + ...Array.from({ length: 2}, (_, i) => ({ + label: `nonconcurrent-legacy-name-${i}`, + namedOwner: 'owner4', + namedAddr: 'owner4', + duration: 31536000 / 2, + subnames: [ + { + label: `test`, + namedOwner: 'owner4', + }, + { + label: `xyz`, + namedOwner: 'owner4', + } + ] + })) ] - const makeNameGenerator2 = async (hre) => { - const { getNamedAccounts, network } = hre - const allNamedAccts = await getNamedAccounts() - const controller = await ethers.getContract('LegacyETHRegistrarController') - const publicResolver = await ethers.getContract('LegacyPublicResolver') - const registry = await ethers.getContract('ENSRegistry') - - return { - commit: async ({ label, namedOwner, namedAddr }) => { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const registrant = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const addr = allNamedAccts[namedAddr] - - const commitment = await controller.makeCommitmentWithConfig( - label, - registrant, - secret, - resolver, - addr, - ) - - const _controller = controller.connect(await ethers.getSigner(registrant)) - return _controller.commit(commitment) - }, - register: async ({ label, namedOwner, namedAddr, duration = 31536000 }) => { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const registrant = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const addr = allNamedAccts[namedAddr] - const price = await controller.rentPrice(label, duration) - const _controller = controller.connect(await ethers.getSigner(registrant)) - return _controller.registerWithConfig( - label, - registrant, - duration, - secret, - resolver, - addr, - { - value: price, - }, - ) - }, - subname: async ({ label, namedOwner, subnameLabel, namedSubnameOwner }) => { - console.log(`Setting subnames for ${label}.eth...`) - const resolver = publicResolver.address - const registrant = allNamedAccts[namedOwner] - const owner = allNamedAccts[namedSubnameOwner] - const _registry = registry.connect(await ethers.getSigner(registrant)) - return _registry.setSubnodeRecord( - namehash(`${label}.eth`), - labelhash(subnameLabel), - owner, - resolver, - '0', - ) - }, - setSubnameRecords: async () => {}, - configure: async () => {}, - } -} - /** * @type {import('hardhat-deploy/types').DeployFunction} */ @@ -433,7 +384,6 @@ const func = async function (hre) { const { getNamedAccounts, network } = hre const allNamedAccts = await getNamedAccounts() - const controller = await ethers.getContract('LegacyETHRegistrarController') const publicResolver = await ethers.getContract('LegacyPublicResolver') await network.provider.send('anvil_setBlockTimestampInterval', [60]) @@ -448,12 +398,7 @@ const func = async function (hre) { subnames, duration = 31536000, } of names) { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' const registrant = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const addr = allNamedAccts[namedAddr] - const commitTx = await nameGenerator.commit({ label, namedOwner, diff --git a/packages/ensjs/deploy/00_register_wrapped.cjs b/packages/ensjs/deploy/00_register_wrapped.cjs index e323410a..6de1a753 100644 --- a/packages/ensjs/deploy/00_register_wrapped.cjs +++ b/packages/ensjs/deploy/00_register_wrapped.cjs @@ -3,7 +3,6 @@ // eslint-disable-next-line @typescript-eslint/naming-convention const { BigNumber } = require('ethers') const { ethers } = require('hardhat') -const { namehash } = require('viem/ens') const { MAX_DATE_INT } = require('../dist/cjs/utils/consts') const { encodeFuses } = require('../dist/cjs/utils/fuses') const { makeNameGenerator } = require('../utils/wrappedNameGenerator.cjs') @@ -99,6 +98,36 @@ const names = [ }, ], }, + ...Array.from({ length: 2}, (_, index) => ({ + label: `nonconcurrent-wrapped-name-${index}`, + namedOwner: 'owner4', + fuses: encodeFuses({ + input: { + child: { + named: ['CANNOT_UNWRAP'], + }, + }, + }), + duration: 31556000 * 2, + subnames: [ + { + label: `xyz`, + namedOwner: 'owner4', + expiry: MAX_DATE_INT, + fuses: encodeFuses({ + input: { + parent: { + named: ['PARENT_CANNOT_CONTROL'], + }, + child: { + named: ['CANNOT_UNWRAP'], + }, + }, + }), + }, + ], + + })) ] /** @@ -110,7 +139,6 @@ const func = async function (hre) { const nameGenerator = await makeNameGenerator(hre) - const controller = await ethers.getContract('ETHRegistrarController') const publicResolver = await ethers.getContract('PublicResolver') await network.provider.send('anvil_setBlockTimestampInterval', [60]) @@ -124,11 +152,6 @@ const func = async function (hre) { subnames, duration = 31536000, } of names) { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const owner = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const commitTx = await nameGenerator.commit({ label, namedOwner, diff --git a/packages/ensjs/package.json b/packages/ensjs/package.json index e91ef072..a5189bff 100644 --- a/packages/ensjs/package.json +++ b/packages/ensjs/package.json @@ -106,7 +106,8 @@ "dns-packet": "^5.3.1", "graphql": "^16.3.0", "graphql-request": "6.1.0", - "pako": "^2.1.0" + "pako": "^2.1.0", + "ts-pattern": "^5.4.0" }, "devDependencies": { "@ensdomains/buffer": "^0.0.13", diff --git a/packages/ensjs/src/functions/public/getOwner.test.ts b/packages/ensjs/src/functions/public/getOwner.test.ts index d189dd1d..cfaf940b 100644 --- a/packages/ensjs/src/functions/public/getOwner.test.ts +++ b/packages/ensjs/src/functions/public/getOwner.test.ts @@ -84,17 +84,5 @@ describe('getOwner', () => { } `) }) - - it('should return correct ownership level and values for an expired wrapped name', async () => { - const result = await getOwner(publicClient, { - name: 'concurrent-wrapped-name.eth', - }) - expect(result).toMatchInlineSnapshot(` - { - "owner": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - "ownershipLevel": "nameWrapper", - } - `) - }) }) }) diff --git a/packages/ensjs/src/functions/subgraph/filters.ts b/packages/ensjs/src/functions/subgraph/filters.ts index 9fa8d765..21bb78d0 100644 --- a/packages/ensjs/src/functions/subgraph/filters.ts +++ b/packages/ensjs/src/functions/subgraph/filters.ts @@ -1,5 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import { match, P } from 'ts-pattern' import type { InputMaybe, Scalars } from './types.js' +import type { Name } from './utils.js' +import { GRACE_PERIOD_SECONDS } from '../../utils/consts.js' export type BlockChangedFilter = { number_gte: Scalars['Int'] @@ -700,3 +703,146 @@ export type ResolverEventFilter = { and?: InputMaybe>> or?: InputMaybe>> } + +export const getExpiryDateOrderFilter = ({ + orderDirection, + lastDomain, +}: { + orderDirection: 'asc' | 'desc' + lastDomain: Name +}): DomainFilter => { + let lastExpiryDate = lastDomain.expiryDate?.value + ? lastDomain.expiryDate.value / 1000 + : 0 + if (lastDomain.parentName === 'eth') lastExpiryDate += GRACE_PERIOD_SECONDS + + return match({ + lastExpiryDate, + orderDirection, + }) + .with( + { + lastExpiryDate: P.number.lte(0), + orderDirection: 'asc', + }, + () => + ({ + and: [{ expiryDate: null }, { id_gt: lastDomain.id }], + } as DomainFilter), + ) + .with( + { + lastExpiryDate: P.number, + orderDirection: 'asc', + }, + () => + ({ + or: [ + { + and: [ + { + expiryDate_gte: `${lastExpiryDate}`, + }, + { id_gt: lastDomain.id }, + ], + }, + { + expiryDate_gt: `${lastExpiryDate}`, + }, + { + expiryDate: null, + }, + ], + } as DomainFilter), + ) + .with( + { + lastExpiryDate: P.number.lte(0), + orderDirection: 'desc', + }, + () => + ({ + or: [ + { + and: [{ expiryDate: null }, { [`id_lt`]: lastDomain.id }], + }, + { + [`expiryDate_gt`]: 0, + }, + ], + } as DomainFilter), + ) + .with( + { + lastExpiryDate: P.number, + orderDirection: 'desc', + }, + () => + ({ + or: [ + { + and: [ + { expiryDate_lte: `${lastExpiryDate}` }, + { id_lt: lastDomain.id }, + ], + }, + { + expiryDate_lt: `${lastExpiryDate}`, + }, + ], + } as DomainFilter), + ) + .exhaustive() +} + +export const getCreatedAtOrderFilter = ({ + orderDirection, + lastDomain, +}: { + orderDirection: 'asc' | 'desc' + lastDomain: Name +}): DomainFilter => + match({ + orderDirection, + }) + .with( + { + orderDirection: 'asc', + }, + () => + ({ + or: [ + { + and: [ + { + createdAt_gte: `${lastDomain.createdAt.value / 1000}`, + id_gt: lastDomain.id, + }, + ], + }, + { + createdAt_gt: `${lastDomain.createdAt.value / 1000}`, + }, + ], + } as DomainFilter), + ) + .with( + { + orderDirection: 'desc', + }, + () => + ({ + or: [ + { + and: [ + { createdAt_lte: `${lastDomain.createdAt.value / 1000}` }, + { id_lt: lastDomain.id }, + ], + }, + { + createdAt_lt: `${lastDomain.createdAt.value / 1000}`, + }, + ], + } as DomainFilter), + ) + .exhaustive() diff --git a/packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts b/packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts index 47016ecb..bef78b99 100644 --- a/packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts +++ b/packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts @@ -4,9 +4,7 @@ import type { Address } from 'viem' import { beforeAll, describe, expect, it } from 'vitest' import { publicClient, walletClient } from '../../test/addTestContracts.js' import { GRACE_PERIOD_SECONDS } from '../../utils/consts.js' -import getNamesForAddress, { - type NameWithRelation, -} from './getNamesForAddress.js' +import getNamesForAddress from './getNamesForAddress.js' import getOwner from '../public/getOwner.js' import getExpiry from '../public/getExpiry.js' import getWrapperData from '../public/getWrapperData.js' @@ -17,49 +15,38 @@ beforeAll(async () => { accounts = await walletClient.getAddresses() }) -const legacyNamesList = Array.from( - { length: 20 }, - (_, i) => `same-expiry-legacy-name-${i}.eth`, -) -const subnamesList = Array.from( - { length: 42 }, - (_, i) => - `${i > 20 ? 'no-' : ''}expiry-subname-${i}.concurrent-wrapped-name.eth`, -) - const user4 = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65' let expiry: bigint -// describe.only('validate data', () => { -// it.each([ -// ...legacyNamesList, -// 'concurrent-wrapped-name.eth', -// ...subnamesList, -// // 'concurrent-wrapped-name.eth', -// // 'subname-1.concurrent-wrapped-name.eth', -// ])('%s', async (name) => { -// console.log(name) -// const ownerData = await getOwner(publicClient, { name }) +describe('check that concurrent names all have the same expiry date', () => { + it.each([ + ...Array.from({ length: 2 }, (_, i) => `concurrent-legacy-name-${i}.eth`), + ...Array.from({ length: 4 }, (_, i) => { + const index = Math.floor(i / 2) + const isSubname = i % 2 === 1 + return isSubname + ? `xyz.concurrent-wrapped-name-${index}.eth` + : `concurrent-wrapped-name-${index}.eth` + }), + ])('%s', async (name) => { + const ownerData = await getOwner(publicClient, { name }) -// const owner = ownerData?.registrant ?? ownerData?.owner -// // expect(owner).toEqual(user4) -// const expiryData = await getExpiry(publicClient, { name }) -// const expiryValue = expiryData?.expiry?.value || 0n -// console.log('expiryData', expiryData) -// if (!expiry) expiry = expiryValue -// const wrapperData = await getWrapperData(publicClient, { name }) + const owner = ownerData?.registrant ?? ownerData?.owner + expect(owner).toEqual(user4) + const expiryData = await getExpiry(publicClient, { name }) + const expiryValue = expiryData?.expiry?.value || 0n + if (!expiry) expiry = expiryValue -// // expiry value from wrapper datat includes grace period -// const wrappedExpiryValue = -// (wrapperData?.expiry?.value || 0n) - BigInt(GRACE_PERIOD_SECONDS) -// const expectedExpiry = -// ownerData?.ownershipLevel === 'nameWrapper' -// ? wrappedExpiryValue -// : expiryValue -// // console.log(expectedExpiry) -// expect(expectedExpiry).toEqual(expiry) -// }) -// }) + const wrapperData = await getWrapperData(publicClient, { name }) + const wrappedExpiryValue = + (wrapperData?.expiry?.value || 0n) - BigInt(GRACE_PERIOD_SECONDS) + const expectedExpiry = + ownerData?.ownershipLevel === 'nameWrapper' + ? wrappedExpiryValue + : expiryValue + expect(expectedExpiry).toEqual(expiry) + }) +}) it('returns with default values', async () => { const result = await getNamesForAddress(publicClient, { @@ -83,49 +70,113 @@ it('has registration date on .eth names', async () => { } }) -// this is my test -it.only('test', async () => { - let fullResult = await getNamesForAddress(publicClient, { +it('should get ascending names by expiry date correctly, including names with the same expiry date', async () => { + const fullResults = await getNamesForAddress(publicClient, { address: accounts[4], orderBy: 'expiryDate', orderDirection: 'asc', - pageSize: 20, + pageSize: 100, }) - // console.log( - // fullResult.map((name) => ({ - // name: name?.name, - // expiry: name?.expiryDate?.value, - // })), - // ) - // console.log(fullResult, fullResult.length) - if (!fullResult.length) throw new Error('No names found') - for (let i = 0; i < 20; i += 1) { - // console.log(fullResult[i].name) - expect(fullResult[i].name).toContain('same-expiry') - } - const previousPage = fullResult - fullResult = await getNamesForAddress(publicClient, { + const expectedNames = fullResults.map((item) => item.name) + + const names = [] + let previousPage + do { + // eslint-disable-next-line no-await-in-loop + const currentPage = await getNamesForAddress(publicClient, { + address: accounts[4], + orderBy: 'expiryDate', + orderDirection: 'asc', + pageSize: 3, + previousPage, + }) + names.push(...currentPage.map((item) => item.name)) + previousPage = currentPage + } while (previousPage.length) + + expect(names).toEqual(expectedNames) +}) + +it('should get descending names by expiry date correctly, including names with the same expiry date', async () => { + const fullResults = await getNamesForAddress(publicClient, { address: accounts[4], orderBy: 'expiryDate', + orderDirection: 'desc', + pageSize: 100, + }) + const expectedNames = fullResults.map((item) => item.name) + + const names = [] + // initial result + let previousPage + do { + // eslint-disable-next-line no-await-in-loop + const currentPage = await getNamesForAddress(publicClient, { + address: accounts[4], + orderBy: 'expiryDate', + orderDirection: 'desc', + pageSize: 3, + previousPage, + }) + names.push(...currentPage.map((item) => item.name)) + previousPage = currentPage + } while (previousPage.length) + expect(names).toEqual(expectedNames) +}) + +it('should get ascending names by creation date correctly, including names with the same expiry date', async () => { + const fullResults = await getNamesForAddress(publicClient, { + address: accounts[4], + orderBy: 'createdAt', orderDirection: 'asc', - pageSize: 20, - previousPage, + pageSize: 100, }) - // console.log(previousPage, 'BREAK', fullResult) - for (let i = 0; i < 20; i += 1) { - console.log(fullResult[i].name) - // expect(fullResult[i].name).toContain('same-expiry') - } - // console.log( - // fullResult.map((name) => ({ - // name: name?.name, - // expiry: name?.expiryDate?.value, - // })), - // ) - // console.log(fullResult) - // expect(fullResult).toEqual(fullResult) + const expectedNames = fullResults.map((item) => item.name) + + const names = [] + let previousPage + do { + // eslint-disable-next-line no-await-in-loop + const currentPage = await getNamesForAddress(publicClient, { + address: accounts[4], + orderBy: 'createdAt', + orderDirection: 'asc', + pageSize: 3, + previousPage, + }) + names.push(...currentPage.map((item) => item.name)) + previousPage = currentPage + } while (previousPage.length) + + expect(names).toEqual(expectedNames) +}) + +it('should get descending names by creation date correctly, including names with the same expiry date', async () => { + const fullResults = await getNamesForAddress(publicClient, { + address: accounts[4], + orderBy: 'createdAt', + orderDirection: 'desc', + pageSize: 100, + }) + const expectedNames = fullResults.map((item) => item.name) + + const names = [] + // initial result + let previousPage + do { + // eslint-disable-next-line no-await-in-loop + const currentPage = await getNamesForAddress(publicClient, { + address: accounts[4], + orderBy: 'createdAt', + orderDirection: 'desc', + pageSize: 3, + previousPage, + }) + names.push(...currentPage.map((item) => item.name)) + previousPage = currentPage + } while (previousPage.length) + expect(names).toEqual(expectedNames) }) -// end // describe('filter', () => { // it('filters by owner', async () => { diff --git a/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts b/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts index 79b74627..d66adaf9 100644 --- a/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts +++ b/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts @@ -7,9 +7,13 @@ import { InvalidFilterKeyError, InvalidOrderByError, } from '../../errors/subgraph.js' -import { EMPTY_ADDRESS, GRACE_PERIOD_SECONDS } from '../../utils/consts.js' +import { EMPTY_ADDRESS } from '../../utils/consts.js' import { createSubgraphClient } from './client.js' -import type { DomainFilter } from './filters.js' +import { + getExpiryDateOrderFilter, + type DomainFilter, + getCreatedAtOrderFilter, +} from './filters.js' import { domainDetailsFragment, registrationDetailsFragment, @@ -94,65 +98,10 @@ const getOrderByFilter = ({ const operator = orderDirection === 'asc' ? 'gt' : 'lt' switch (orderBy) { case 'expiryDate': { - let lastExpiryDate = lastDomain.expiryDate?.value - ? lastDomain.expiryDate.value / 1000 - : 0 - if (lastDomain.parentName === 'eth') { - lastExpiryDate += GRACE_PERIOD_SECONDS - } - if (orderDirection === 'asc' && lastExpiryDate === 0) { - return { - and: [{ expiryDate: null }, { [`id_${operator}`]: lastDomain.id }], - } - } - if ( - orderDirection === 'asc' && - lastExpiryDate !== 0 && - previousPage.length === 20 - ) { - return { - or: [ - { - and: [ - { [`expiryDate_${operator}e`]: `${lastExpiryDate}` }, - { [`id_${operator}`]: lastDomain.id }, - ], - }, - { - [`expiryDate_${operator}`]: `${lastExpiryDate}`, - }, - { expiryDate: null }, - ], - } - } - if (orderDirection === 'desc' && lastExpiryDate === 0) { - return { - and: [{ expiryDate: null }, { [`id_${operator}`]: lastDomain.id }], - } - } - if (orderDirection === 'desc' && lastExpiryDate !== 0) { - return { - or: [ - { - and: [ - { [`expiryDate_${operator}e`]: `${lastExpiryDate}` }, - { [`id_${operator}`]: lastDomain.id }, - ], - }, - { - [`expiryDate_${operator}`]: `${lastExpiryDate}`, - }, - ], - } - } - return { - or: [ - { - [`expiryDate_${operator}`]: `${lastExpiryDate}`, - }, - { expiryDate: null }, - ], - } + return getExpiryDateOrderFilter({ + orderDirection, + lastDomain, + }) } case 'name': { return { @@ -165,6 +114,7 @@ const getOrderByFilter = ({ } } case 'createdAt': { + return getCreatedAtOrderFilter({ lastDomain, orderDirection }) return { [`createdAt_${operator}`]: `${lastDomain.createdAt.value / 1000}`, } diff --git a/packages/ensjs/src/functions/subgraph/getSubnames.ts b/packages/ensjs/src/functions/subgraph/getSubnames.ts index a53b1a3e..8315531e 100644 --- a/packages/ensjs/src/functions/subgraph/getSubnames.ts +++ b/packages/ensjs/src/functions/subgraph/getSubnames.ts @@ -2,10 +2,14 @@ import { gql } from 'graphql-request' import type { ClientWithEns } from '../../contracts/consts.js' import { InvalidOrderByError } from '../../errors/subgraph.js' -import { EMPTY_ADDRESS, GRACE_PERIOD_SECONDS } from '../../utils/consts.js' +import { EMPTY_ADDRESS } from '../../utils/consts.js' import { namehash } from '../../utils/normalise.js' import { createSubgraphClient } from './client.js' -import type { DomainFilter } from './filters.js' +import { + getExpiryDateOrderFilter, + type DomainFilter, + getCreatedAtOrderFilter, +} from './filters.js' import { domainDetailsWithoutParentFragment, registrationDetailsFragment, @@ -44,46 +48,21 @@ type SubgraphResult = { } const getOrderByFilter = ({ - name, orderBy, orderDirection, previousPage, }: Required< - Pick< - GetSubnamesParameters, - 'name' | 'orderBy' | 'orderDirection' | 'previousPage' - > + Pick >): DomainFilter => { const lastDomain = previousPage[previousPage.length - 1] const operator = orderDirection === 'asc' ? 'gt' : 'lt' switch (orderBy) { case 'expiryDate': { - let lastExpiryDate = lastDomain.expiryDate?.value - ? lastDomain.expiryDate.value / 1000 - : 0 - if (name === 'eth' && lastExpiryDate) { - lastExpiryDate += GRACE_PERIOD_SECONDS - } - - if (orderDirection === 'asc' && lastExpiryDate === 0) { - return { - and: [{ expiryDate: null }, { [`id_${operator}`]: lastDomain.id }], - } - } - if (orderDirection === 'desc' && lastExpiryDate !== 0) { - return { - [`expiryDate_${operator}`]: `${lastExpiryDate}`, - } - } - return { - or: [ - { - [`expiryDate_${operator}`]: `${lastExpiryDate}`, - }, - { expiryDate: null }, - ], - } + return getExpiryDateOrderFilter({ + lastDomain, + orderDirection, + }) } case 'name': { return { @@ -96,9 +75,7 @@ const getOrderByFilter = ({ } } case 'createdAt': { - return { - [`createdAt_${operator}`]: `${lastDomain.createdAt.value / 1000}`, - } + return getCreatedAtOrderFilter({ lastDomain, orderDirection }) } default: throw new InvalidOrderByError({ @@ -146,7 +123,6 @@ const getSubnames = async ( if (previousPage?.length) { whereFilters.push( getOrderByFilter({ - name, orderBy, orderDirection, previousPage, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b4b962f..2dc543b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,6 +150,9 @@ importers: pako: specifier: ^2.1.0 version: 2.1.0 + ts-pattern: + specifier: ^5.4.0 + version: 5.4.0 devDependencies: '@ensdomains/buffer': specifier: ^0.0.13 @@ -4601,6 +4604,9 @@ packages: '@swc/wasm': optional: true + ts-pattern@5.4.0: + resolution: {integrity: sha512-hgfOMfjlrARCnYtGD/xEAkFHDXuSyuqjzFSltyQCbN689uNvoQL20TVN2XFcLMjfNuwSsQGU+xtH6MrjIwhwUg==} + tsconfig-paths@3.14.1: resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} @@ -6936,7 +6942,7 @@ snapshots: axios@0.25.0: dependencies: - follow-redirects: 1.15.1 + follow-redirects: 1.15.1(debug@4.3.4) transitivePeerDependencies: - debug @@ -8407,8 +8413,6 @@ snapshots: dependencies: imul: 1.0.1 - follow-redirects@1.15.1: {} - follow-redirects@1.15.1(debug@4.3.4): optionalDependencies: debug: 4.3.4(supports-color@8.1.1) @@ -10661,6 +10665,8 @@ snapshots: optionalDependencies: '@swc/core': 1.3.68 + ts-pattern@5.4.0: {} + tsconfig-paths@3.14.1: dependencies: '@types/json5': 0.0.29