Skip to content

Commit

Permalink
Merge pull request #144 from ensdomains/deleted-name-filter
Browse files Browse the repository at this point in the history
feat: deleted name filtering via subgraph
  • Loading branch information
TateB authored Jul 28, 2023
2 parents 70abc28 + a73144f commit 8d607ae
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 4 deletions.
Binary file modified packages/ensjs/archive.tar.lz4
Binary file not shown.
11 changes: 11 additions & 0 deletions packages/ensjs/deploy/00_register_legacy.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,17 @@ const names = [
namedAddr: 'owner',
duration: 2419200,
},
{
label: 'deletable',
namedOwner: 'owner',
namedAddr: 'owner',
subnames: [
{ label: 'xyz', namedOwner: 'owner2' },
{ label: 'test', namedOwner: 'owner' },
{ label: 'unwrapped-deleted', namedOwner: 'owner' },
{ label: 'wrapped-deleted', namedOwner: 'owner' },
],
},
...Array.from({ length: 34 }, (_, i) => ({
label: `${i}-dummy`,
namedOwner: 'owner2',
Expand Down
65 changes: 65 additions & 0 deletions packages/ensjs/deploy/01_delete_names.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-await-in-loop */
const { ethers } = require('hardhat')
const { namehash, toHex, labelhash } = require('viem')
const { EMPTY_ADDRESS } = require('../dist/cjs/utils/consts')
const { packetToBytes } = require('../dist/cjs/utils/hexEncodedName')

/**
* @type {import('hardhat-deploy/types').DeployFunction}
*/
const func = async function (hre) {
const { getNamedAccounts } = hre
const allNamedAccts = await getNamedAccounts()

const nameWrapper = await ethers.getContract(
'NameWrapper',
await ethers.getSigner(allNamedAccts.owner),
)
const registry = await ethers.getContract(
'ENSRegistry',
await ethers.getSigner(allNamedAccts.owner),
)

/**
* @param {string} name
*/
const deleteName = async (name) => {
const labels = name.split('.')
const label = labelhash(labels.shift())
const node = namehash(labels.join('.'))

const tx = await registry.setSubnodeRecord(
node,
label,
EMPTY_ADDRESS,
EMPTY_ADDRESS,
0,
)
await tx.wait()
}

const name1 = 'wrapped-deleted.deletable.eth'
const name2 = 'unwrapped-deleted.deletable.eth'

// wrap wrapped-deleted.deletable.eth
const approveTx = await registry.setApprovalForAll(nameWrapper.address, true)
await approveTx.wait()
const wrapTx = await nameWrapper.wrap(
toHex(packetToBytes(name1)),
allNamedAccts.owner,
EMPTY_ADDRESS,
)
await wrapTx.wait()

await deleteName(name1)
await deleteName(name2)

return true
}

func.id = 'delete-names'
func.tags = ['delete-names']
func.runAtTheEnd = true

module.exports = func
2 changes: 1 addition & 1 deletion packages/ensjs/hardhat.config.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require('@nomiclabs/hardhat-ethers')
require('dotenv/config')
require('@nomiclabs/hardhat-ethers')
require('hardhat-deploy')
const { resolve } = require('path')

Expand Down
1 change: 1 addition & 0 deletions packages/ensjs/src/clients/decorators/subgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export type EnsSubgraphActions = {
name,
searchString,
allowExpired,
allowDeleted,
orderBy,
orderDirection,
pageSize,
Expand Down
30 changes: 30 additions & 0 deletions packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type { Address } from 'viem'
import { publicClient, walletClient } from '../../test/addTestContracts.js'
import { EMPTY_ADDRESS } from '../../utils/consts.js'
import getNamesForAddress, {
type NameWithRelation,
} from './getNamesForAddress.js'
Expand Down Expand Up @@ -132,6 +133,35 @@ describe('filter', () => {
)
expect(reverseRecordNames.length).toBeGreaterThan(0)
})
it('does not include deleted names by default', async () => {
const result = await getNamesForAddress(publicClient, {
address: accounts[1],
pageSize: 1000,
})
if (!result.length) throw new Error('No names found')
const deletedNames = result.filter(
(x) => x.parentName === 'deletable.eth' && x.owner === EMPTY_ADDRESS,
)
expect(deletedNames.length).toBe(0)
})
it('allows including deleted names', async () => {
const result = await getNamesForAddress(publicClient, {
address: accounts[1],
pageSize: 1000,
filter: {
owner: true,
registrant: true,
resolvedAddress: true,
wrappedOwner: true,
allowDeleted: true,
},
})
if (!result.length) throw new Error('No names found')
const deletedNames = result.filter(
(x) => x.parentName === 'deletable.eth' && x.owner === EMPTY_ADDRESS,
)
expect(deletedNames.length).toBe(1)
})
})

describe.each([
Expand Down
32 changes: 30 additions & 2 deletions packages/ensjs/src/functions/subgraph/getNamesForAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
InvalidFilterKeyError,
InvalidOrderByError,
} from '../../errors/subgraph.js'
import { GRACE_PERIOD_SECONDS } from '../../utils/consts.js'
import { EMPTY_ADDRESS, GRACE_PERIOD_SECONDS } from '../../utils/consts.js'
import { createSubgraphClient } from './client.js'
import type { DomainFilter } from './filters.js'
import {
Expand Down Expand Up @@ -47,6 +47,8 @@ type GetNamesForAddressFilter = GetNamesForAddressRelation & {
allowExpired?: boolean
/** Allows reverse record nodes to be included (default: false) */
allowReverseRecord?: boolean
/** Allows deleted names to be included (default: false) */
allowDeleted?: boolean
}

export type GetNamesForAddressParameters = {
Expand Down Expand Up @@ -165,6 +167,7 @@ const getNamesForAddress = async (
resolvedAddress: true,
wrappedOwner: true,
allowExpired: false,
allowDeleted: false,
allowReverseRecord: false,
},
orderBy = 'name',
Expand All @@ -175,7 +178,7 @@ const getNamesForAddress = async (
): Promise<GetNamesForAddressReturnType> => {
const subgraphClient = createSubgraphClient({ client })

const { allowExpired, allowReverseRecord, ...filters } = filter
const { allowExpired, allowDeleted, allowReverseRecord, ...filters } = filter
const ownerWhereFilters: DomainFilter[] = Object.entries(filters).reduce(
(prev, [key, value]) => {
if (value) {
Expand Down Expand Up @@ -240,6 +243,31 @@ const getNamesForAddress = async (
})
}

if (!allowDeleted) {
// exclude "deleted" domains
// when owner/resolver/registrant = null
whereFilters.push({
or: [
{
owner_not: EMPTY_ADDRESS,
},
{
resolver_not: null,
},
{
and: [
{
registrant_not: EMPTY_ADDRESS,
},
{
registrant_not: null,
},
],
},
],
})
}

const whereFilter: DomainFilter =
whereFilters.length > 1 ? { and: whereFilters } : whereFilters[0]

Expand Down
15 changes: 15 additions & 0 deletions packages/ensjs/src/functions/subgraph/getSubnames.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ it('allows including expired names - other', async () => {
)
expect(expiredNames.length).toBeGreaterThan(0)
})
it('does not include deleted names by default', async () => {
const result = await getSubnames(publicClient, {
name: 'deletable.eth',
})
if (!result.length) throw new Error('No names found')
expect(result.length).toBe(2)
})
it('allows including deleted names', async () => {
const result = await getSubnames(publicClient, {
name: 'deletable.eth',
allowDeleted: true,
})
if (!result.length) throw new Error('No names found')
expect(result.length).toBe(4)
})

describe.each([
{
Expand Down
27 changes: 26 additions & 1 deletion packages/ensjs/src/functions/subgraph/getSubnames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { gql } from 'graphql-request'
import type { ClientWithEns } from '../../contracts/consts.js'
import { InvalidOrderByError } from '../../errors/subgraph.js'
import { GRACE_PERIOD_SECONDS } from '../../utils/consts.js'
import { EMPTY_ADDRESS, GRACE_PERIOD_SECONDS } from '../../utils/consts.js'
import { namehash } from '../../utils/normalise.js'
import { createSubgraphClient } from './client.js'
import type { DomainFilter } from './filters.js'
Expand All @@ -23,6 +23,8 @@ export type GetSubnamesParameters = {
searchString?: string
/** Allows expired names to be included (default: false) */
allowExpired?: boolean
/** Allows deleted names to be included (default: false) */
allowDeleted?: boolean
/** Parameter to order names by (default: name) */
orderBy?: GetSubnamesOrderBy
/** Direction to order names in (default: asc) */
Expand Down Expand Up @@ -129,6 +131,7 @@ const getSubnames = async (
name,
searchString,
allowExpired = false,
allowDeleted = false,
orderBy = 'name',
orderDirection = 'asc',
pageSize = 100,
Expand Down Expand Up @@ -161,6 +164,28 @@ const getSubnames = async (
})
}

if (!allowDeleted) {
// exclude "deleted" domains
// when owner/resolver/registrant = null
whereFilters.push({
or: [
{
owner_not: EMPTY_ADDRESS,
},
{
resolver_not: null,
},
...(name.toLowerCase() === 'eth'
? [
{
registrant_not: EMPTY_ADDRESS,
},
]
: []),
],
})
}

if (searchString) {
// using labelName_contains instead of name_contains because name_contains
// includes the parent name
Expand Down

0 comments on commit 8d607ae

Please sign in to comment.