diff --git a/.changeset/six-pillows-pump.md b/.changeset/six-pillows-pump.md new file mode 100644 index 000000000..32b241430 --- /dev/null +++ b/.changeset/six-pillows-pump.md @@ -0,0 +1,7 @@ +--- +"@ensnode/ensnode-schema": minor +"@ensnode/datasources": minor +"ensindexer": minor +--- + +Initial launch of ENS TokenScope with support for indexing Seaport sales. diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml index 0df678c7f..97e6b51fb 100644 --- a/.github/workflows/test_ci.yml +++ b/.github/workflows/test_ci.yml @@ -59,7 +59,7 @@ jobs: # We use private RPC URLs from GitHub Secrets to avoid rate limits. # Public RPC URLs are used as fallbacks for repository forks # that don't have the relevant secrets configured. - PLUGINS: subgraph,basenames,lineanames,threedns,reverse-resolvers,referrals + PLUGINS: subgraph,basenames,lineanames,threedns,reverse-resolvers,referrals,tokenscope RPC_URL_1: ${{ secrets.MAINNET_RPC_URL || 'https://eth.drpc.org' }} RPC_URL_10: ${{ secrets.OPTIMISM_RPC_URL || 'https://optimism.drpc.org' }} RPC_URL_8453: ${{ secrets.BASE_RPC_URL || 'https://base.drpc.org' }} diff --git a/apps/ensadmin/src/lib/chains.ts b/apps/ensadmin/src/lib/chains.ts index 00a61dc4c..6fef0f9e7 100644 --- a/apps/ensadmin/src/lib/chains.ts +++ b/apps/ensadmin/src/lib/chains.ts @@ -1,4 +1,4 @@ -import { Datasource, type ENSNamespaceId, getENSNamespace } from "@ensnode/datasources"; +import { type Datasource, type ENSNamespaceId, getENSNamespace } from "@ensnode/datasources"; import { type Chain } from "viem"; import { anvil } from "viem/chains"; diff --git a/apps/ensindexer/.env.local.example b/apps/ensindexer/.env.local.example index 18f696710..c9927f14d 100644 --- a/apps/ensindexer/.env.local.example +++ b/apps/ensindexer/.env.local.example @@ -40,6 +40,7 @@ PORT=42069 # === ENS Namespace: Mainnet === # Ethereum Mainnet # - required if the configured namespace is mainnet +# - required by plugins: subgraph, reverse-resolvers, referrals, tokenscope RPC_URL_1= RPC_REQUEST_RATE_LIMIT_1=500 @@ -71,6 +72,7 @@ RPC_REQUEST_RATE_LIMIT_534352=500 # === ENS Namespace: Sepolia === # Ethereum Sepolia (public testnet) # - required if the configured namespace is sepolia +# - required by plugins: subgraph, reverse-resolvers, referrals, tokenscope RPC_URL_11155111= RPC_REQUEST_RATE_LIMIT_11155111=500 @@ -102,6 +104,7 @@ RPC_REQUEST_RATE_LIMIT_534351=500 # === ENS Namespace: Holesky === # Ethereum Holesky (public testnet) # - required if the configured namespace is holesky +# - required by plugins: subgraph, reverse-resolvers, referrals RPC_URL_17000= RPC_REQUEST_RATE_LIMIT_17000=500 @@ -145,7 +148,7 @@ NAMESPACE=mainnet # - for subgraph-compatible indexing, the only valid configuration is `PLUGINS=subgraph`. # - for protocol acceleration of primary name lookups (reverse resolution), enable the # reverse-resolvers plugin. -PLUGINS=subgraph,basenames,lineanames,threedns,reverse-resolvers,referrals +PLUGINS=subgraph,basenames,lineanames,threedns,reverse-resolvers,referrals,tokenscope # ENSRainbow service URL # Required. This is the URL of the ENSRainbow server that ENSIndexer will use to heal diff --git a/apps/ensindexer/package.json b/apps/ensindexer/package.json index 88f229b3b..25bb025f7 100644 --- a/apps/ensindexer/package.json +++ b/apps/ensindexer/package.json @@ -43,6 +43,7 @@ "@opentelemetry/sdk-trace-node": "^2.0.1", "@opentelemetry/semantic-conventions": "^1.34.0", "@types/dns-packet": "^5.6.5", + "caip": "^1.1.1", "date-fns": "catalog:", "deepmerge-ts": "^7.1.5", "dns-packet": "^5.6.1", diff --git a/apps/ensindexer/src/lib/currencies.ts b/apps/ensindexer/src/lib/currencies.ts new file mode 100644 index 000000000..52c5134a7 --- /dev/null +++ b/apps/ensindexer/src/lib/currencies.ts @@ -0,0 +1,149 @@ +import { + base, + baseSepolia, + holesky, + linea, + lineaSepolia, + mainnet, + optimism, + sepolia, +} from "viem/chains"; + +import { AccountId, ChainId, accountIdEqual } from "@ensnode/ensnode-sdk"; +import { Address, zeroAddress } from "viem"; + +/** + * Identifiers for supported currencies. + * + * TODO: Add support for WETH + */ +export const CurrencyIds = { + ETH: "ETH", + USDC: "USDC", + DAI: "DAI", +} as const; + +export type CurrencyId = (typeof CurrencyIds)[keyof typeof CurrencyIds]; + +export interface Price { + currency: CurrencyId; + + /** + * The amount of the currency in the smallest unit of the currency. (see + * decimals of the CurrencyConfig for the currency). + * + * Guaranteed to be non-negative. + */ + amount: bigint; +} + +export interface CurrencyInfo { + id: CurrencyId; + name: string; + decimals: number; +} + +const currencyInfo: Record = { + [CurrencyIds.ETH]: { + id: CurrencyIds.ETH, + name: "Ethereum", + decimals: 18, + }, + [CurrencyIds.USDC]: { + id: CurrencyIds.USDC, + name: "USDC", + decimals: 6, + }, + [CurrencyIds.DAI]: { + id: CurrencyIds.DAI, + name: "Dai Stablecoin", + decimals: 18, + }, +}; + +export const getCurrencyInfo = (currencyId: CurrencyId): CurrencyInfo => currencyInfo[currencyId]; + +// NOTE: this mapping currently only considers the subset of chains where we have +// supported token issuing contracts. +const KNOWN_CURRENCY_CONTRACTS: Record> = { + /** mainnet namespace */ + [mainnet.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0xA0b86a33E6417c5Dd4Baf8C54e5de49E293E9169", + [CurrencyIds.DAI]: "0x6B175474E89094C44Da98b954EedeAC495271d0F", + }, + [base.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + [CurrencyIds.DAI]: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", + }, + [optimism.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", + [CurrencyIds.DAI]: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", + }, + [linea.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x176211869cA2b568f2A7D4EE941E073a821EE1ff", + [CurrencyIds.DAI]: "0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5", + }, + + /** sepolia namespace */ + [sepolia.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", + [CurrencyIds.DAI]: "0x3e622317f8C93f7328350cF0B56d9eD4C620C5d6", + }, + [baseSepolia.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + [CurrencyIds.DAI]: "0x7368C6C68a4b2b68F90DB2e8F5E3b8E1E5e4F5c7", + }, + [lineaSepolia.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x176211869cA2b568f2A7D4EE941E073a821EE1ff", + [CurrencyIds.DAI]: "0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5", + }, + + /** holesky namespace */ + [holesky.id]: { + [CurrencyIds.ETH]: zeroAddress, + [CurrencyIds.USDC]: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", + [CurrencyIds.DAI]: "0x3e622317f8C93f7328350cF0B56d9eD4C620C5d6", + }, +} as const; + +/** + * Gets the supported currency contracts for a given chain as a Record + * + * @param chainId - The chain ID to get supported currency contracts for + * @returns a record of currency ids to AccountIds for the given chain + */ +const getSupportedCurrencyContractsForChain = (chainId: ChainId): Record => { + return Object.fromEntries( + Object.entries(KNOWN_CURRENCY_CONTRACTS[chainId] ?? {}).map(([currencyId, address]) => [ + currencyId, + { chainId, address }, + ]), + ) as Record; +}; + +/** + * Gets the currency id for the given contract + * + * @param contract - The AccountId of the contract to get the currency id for + * @returns the currency id for the given contract in the specified namespace, or + * null if the contract is not a supported currency contract in the + * specified namespace + */ +export const getCurrencyIdForContract = (contract: AccountId): CurrencyId | null => { + const supportedCurrencyContracts = getSupportedCurrencyContractsForChain(contract.chainId); + + const found = Object.entries(supportedCurrencyContracts).find(([, accountId]) => + accountIdEqual(accountId, contract), + ); + + if (!found) return null; + + return found[0] as CurrencyId; +}; diff --git a/apps/ensindexer/src/lib/datasource-helpers.ts b/apps/ensindexer/src/lib/datasource-helpers.ts new file mode 100644 index 000000000..0fef5cfaf --- /dev/null +++ b/apps/ensindexer/src/lib/datasource-helpers.ts @@ -0,0 +1,38 @@ +import { + DatasourceName, + ENSNamespaceId, + getENSNamespace, + maybeGetDatasource, +} from "@ensnode/datasources"; +import { AccountId, ChainId, uniq } from "@ensnode/ensnode-sdk"; + +/** + * Gets the AccountId for the contract in the specified namespace, datasource, and + * contract name, or undefined if it is not defined or is not a single AccountId. + * + * This is useful when you want to retrieve the AccountId for a contract by its name + * where it may or may not actually be defined for the given namespace and datasource. + * + * @param namespaceId - The ENSNamespace identifier (e.g. 'mainnet', 'sepolia', 'holesky', + * 'ens-test-env') + * @param datasourceName - The name of the Datasource to search for contractName in + * @param contractName - The name of the contract to retrieve + * @returns The AccountId of the contract with the given namespace, datasource, + * and contract name, or undefined if it is not found or is not a single AccountId + */ +export const maybeGetDatasourceContract = ( + namespaceId: ENSNamespaceId, + datasourceName: DatasourceName, + contractName: string, +): AccountId | undefined => { + const datasource = maybeGetDatasource(namespaceId, datasourceName); + if (!datasource) return undefined; + + const address = datasource.contracts[contractName]?.address; + if (address === undefined || Array.isArray(address)) return undefined; + + return { + chainId: datasource.chain.id, + address, + }; +}; diff --git a/apps/ensindexer/src/lib/ponder-helpers.ts b/apps/ensindexer/src/lib/ponder-helpers.ts index 4439c1e2c..25a3403d8 100644 --- a/apps/ensindexer/src/lib/ponder-helpers.ts +++ b/apps/ensindexer/src/lib/ponder-helpers.ts @@ -11,7 +11,6 @@ import { Address, PublicClient } from "viem"; import * as z from "zod/v4"; import { ContractConfig } from "@ensnode/datasources"; -import { EnsRainbowApiClient } from "@ensnode/ensrainbow-sdk"; import type { BlockInfo, PonderStatus } from "@ensnode/ponder-metadata"; import { ENSIndexerConfig } from "@/config/types"; diff --git a/apps/ensindexer/src/lib/tokenscope/assets.ts b/apps/ensindexer/src/lib/tokenscope/assets.ts new file mode 100644 index 000000000..9f2f971ca --- /dev/null +++ b/apps/ensindexer/src/lib/tokenscope/assets.ts @@ -0,0 +1,14 @@ +/** + * An enum representing the possible CAIP-19 Asset Namespace values. + */ +export const AssetNamespaces = { + ERC721: "erc721", + ERC1155: "erc1155", +} as const; + +export type AssetNamespace = (typeof AssetNamespaces)[keyof typeof AssetNamespaces]; + +/** + * A uint256 value that identifies a specific token within an NFT contract. + */ +export type TokenId = bigint; diff --git a/apps/ensindexer/src/lib/tokenscope/sales.ts b/apps/ensindexer/src/lib/tokenscope/sales.ts new file mode 100644 index 000000000..64c4e5535 --- /dev/null +++ b/apps/ensindexer/src/lib/tokenscope/sales.ts @@ -0,0 +1,24 @@ +import { AccountId, Node } from "@ensnode/ensnode-sdk"; +import { Address, Hex } from "viem"; + +import { Price } from "@/lib/currencies"; +import { AssetNamespace, TokenId } from "@/lib/tokenscope/assets"; + +export interface SupportedNFT { + assetNamespace: AssetNamespace; + contract: AccountId; + tokenId: TokenId; + domainId: Node; +} + +export interface SupportedPayment { + price: Price; +} + +export interface SupportedSale { + orderHash: Hex; + nft: SupportedNFT; + payment: SupportedPayment; + seller: Address; + buyer: Address; +} diff --git a/apps/ensindexer/src/lib/tokenscope/seaport-types.ts b/apps/ensindexer/src/lib/tokenscope/seaport-types.ts new file mode 100644 index 000000000..8cb05b9cb --- /dev/null +++ b/apps/ensindexer/src/lib/tokenscope/seaport-types.ts @@ -0,0 +1,136 @@ +import { EventWithArgs } from "@/lib/ponder-helpers"; +import { Address, Hex } from "viem"; + +/** + * Seaport's ItemType enum, re-implemented here to avoid a dependency on seaport-js. + * + * NOTE: if we require further types functionality from seaport-js, we should simply depend on it + * and import them directly. + * + * @see https://github.com/ProjectOpenSea/seaport-js/blob/c4d4756c8000a7143fc1ed9a5aad71b444ae90b4/src/constants.ts#L89 + */ +export enum ItemType { + NATIVE = 0, + ERC20 = 1, + ERC721 = 2, + ERC1155 = 3, + ERC721_WITH_CRITERIA = 4, + ERC1155_WITH_CRITERIA = 5, +} + +/** + * A Seaport OrderFulfilled Event from Ponder, semantically typed with descriptions. + */ +export type OrderFulfilledEvent = EventWithArgs<{ + /** + * The unique hash identifier of the fulfilled order within Seaport. + * Used to track and reference specific orders on-chain. + */ + orderHash: Hex; + + /** + * The address of the account that created and signed the original order. + * This is the party offering items for trade. + */ + offerer: Address; + + /** + * The address of the zone contract that implements custom validation rules. + * Zones can enforce additional restrictions like allowlists, time windows, + * or other custom logic before order fulfillment. Can be zero address if + * no additional validation is required. + */ + zone: Address; + + /** + * The address that receives the offered items from the order. + * This is typically the order fulfiller or their designated recipient. + */ + recipient: Address; + + /** + * Array of items that the offerer is giving up in this order. + * For listings: NFTs/tokens being sold + * For offers: ETH/ERC20 tokens being offered as payment + */ + offer: readonly OfferItem[]; + + /** + * Array of items that the offerer expects to receive in return. + * For listings: ETH/ERC20 tokens expected as payment + * For offers: NFTs/tokens being requested in exchange + */ + consideration: readonly ConsiderationItem[]; +}>; + +export type OfferItem = { + /** + * The type of item in the offer. + * For example, ERC20, ERC721, ERC1155, or NATIVE (ETH) + */ + itemType: ItemType; + + /** + * The contract address of the token. + * - For ERC721/ERC1155: The NFT contract address + * - For ERC20: The token contract address + * - For NATIVE (ETH): Zero address (0x0000000000000000000000000000000000000000) + */ + token: Address; + + /** + * The identifier field has different meanings based on itemType: + * - For ERC721/ERC1155: The specific token ID of the NFT + * - For ERC20: Always 0 (not used for fungible tokens) + * - For NATIVE (ETH): Always 0 (not used for native currency) + */ + identifier: bigint; + + /** + * The amount field has different meanings based on itemType: + * - For ERC721: Always 1 (you can only transfer 1 unique NFT) + * - For ERC1155: The quantity of tokens with the specified identifier (for our purposes, always 1) + * - For ERC20: The amount of tokens (in wei/smallest unit) + * - For NATIVE (ETH): The amount of ETH (in wei) + */ + amount: bigint; +}; + +export type ConsiderationItem = { + /** + * The type of item in the consideration. + * For example, ERC20, ERC721, ERC1155, or NATIVE (ETH) + */ + itemType: ItemType; + + /** + * The contract address of the token. + * - For ERC721/ERC1155: The NFT contract address + * - For ERC20: The token contract address + * - For NATIVE (ETH): Zero address (0x0000000000000000000000000000000000000000) + */ + token: Address; + + /** + * The identifier field has different meanings based on itemType: + * - For ERC721/ERC1155: The specific token ID of the NFT + * - For ERC20: Always 0 (not used for fungible tokens) + * - For NATIVE (ETH): Always 0 (not used for native currency) + */ + identifier: bigint; + + /** + * The amount field has different meanings based on itemType: + * - For ERC721: Always 1 (you can only transfer 1 unique NFT) + * - For ERC1155: The quantity of tokens with the specified identifier + * - For ERC20: The amount of tokens (in wei/smallest unit) + * - For NATIVE (ETH): The amount of ETH (in wei) + */ + amount: bigint; + + /** + * The address that receives the consideration items from the order. + * This is typically the order fulfiller or their designated recipient. + */ + recipient: Address; +}; diff --git a/apps/ensindexer/src/lib/tokenscope/seaport.ts b/apps/ensindexer/src/lib/tokenscope/seaport.ts new file mode 100644 index 000000000..ffe55895e --- /dev/null +++ b/apps/ensindexer/src/lib/tokenscope/seaport.ts @@ -0,0 +1,235 @@ +import { CurrencyIds, getCurrencyIdForContract } from "@/lib/currencies"; +import { AssetNamespace, AssetNamespaces } from "@/lib/tokenscope/assets"; +import { SupportedNFT, SupportedPayment, SupportedSale } from "@/lib/tokenscope/sales"; +import { + ConsiderationItem, + ItemType, + OfferItem, + OrderFulfilledEvent, +} from "@/lib/tokenscope/seaport-types"; +import { getKnownTokenIssuer } from "@/lib/tokenscope/token-issuers"; +import { ENSNamespaceId } from "@ensnode/datasources"; +import { ChainId, uniq } from "@ensnode/ensnode-sdk"; + +/** + * Gets the supported TokenScope Asset Namespace for a given Seaport ItemType. + * + * @param itemType - The Seaport item type to get the supported TokenScope asset namespace for + * @returns the supported TokenScope Asset Namespace for the given Seaport ItemType, or null + * if the Seaport item type is not supported. + */ +const getAssetNamespace = (itemType: ItemType): AssetNamespace | null => { + switch (itemType) { + case ItemType.ERC721: + return AssetNamespaces.ERC721; + case ItemType.ERC1155: + return AssetNamespaces.ERC1155; + default: + return null; + } +}; + +/** + * Gets the supported NFT from a given Seaport item. + * + * @param namespaceId - The ENSNamespace identifier (e.g. 'mainnet', 'sepolia', 'holesky', + * 'ens-test-env') + * @param chainId - The chain ID of the Seaport item + * @param item - The Seaport item to get the supported NFT from + * @returns the supported NFT from the given Seaport item, or `null` if the Seaport item is + * not a supported NFT + */ +const getSupportedNFT = ( + namespaceId: ENSNamespaceId, + chainId: ChainId, + item: OfferItem | ConsiderationItem, +): SupportedNFT | null => { + // validate as exactly 1 item + if (item.amount !== 1n) return null; + + // validate item as an ERC721/ERC1155 NFT + const assetNamespace = getAssetNamespace(item.itemType); + if (!assetNamespace) return null; + + // validate that the token is a known token issuing contract + const tokenIssuer = getKnownTokenIssuer(namespaceId, { + chainId, + address: item.token, + }); + if (!tokenIssuer) return null; + + const contract = tokenIssuer.contract; + const tokenId = item.identifier; + const domainId = tokenIssuer.getDomainId(tokenId); + + return { + assetNamespace, + contract, + tokenId, + domainId, + }; +}; + +const getSupportedPayment = ( + chainId: ChainId, + item: OfferItem | ConsiderationItem, +): SupportedPayment | null => { + // only NATIVE and ERC20 Payments are supported at the moment + if (item.itemType !== ItemType.NATIVE && item.itemType !== ItemType.ERC20) return null; + + // validate that the item is a supported currency + const currencyId = getCurrencyIdForContract({ chainId, address: item.token }); + if (!currencyId) return null; + + // Sanity Check: if ItemType.NATIVE, the inferred Currency must be ETH + if (item.itemType === ItemType.NATIVE && currencyId !== CurrencyIds.ETH) return null; + + // Sanity Check: if ItemType.ERC20, the inferred Currency must NOT be ETH + if (item.itemType === ItemType.ERC20 && currencyId === CurrencyIds.ETH) return null; + + // Sanity Check: amount of currency must be >=0 + if (item.amount < 0n) return null; + + // finally, a valid, SupportedPayment + return { + price: { + currency: currencyId, + amount: item.amount, + }, + }; +}; + +interface SeaportItemExtractions { + nfts: SupportedNFT[]; + + /** + * Seaport supports multiple payments in a single order. + * + * Example cases include: + * - Payments are being made in multiple currencies. + * - Multiple payments in the same currency, but where one payment is for marketplace fees while + * another payment is for the seller. + */ + payments: SupportedPayment[]; +} + +const getSeaportItemExtractions = ( + namespaceId: ENSNamespaceId, + chainId: ChainId, + items: readonly (OfferItem | ConsiderationItem)[], +): SeaportItemExtractions => { + const extractions: SeaportItemExtractions = { + nfts: [], + payments: [], + }; + + // each item is either a supported NFT, a supported payment, or unsupported + for (const item of items) { + // if the item is an nft, push to nfts + const nft = getSupportedNFT(namespaceId, chainId, item); + if (nft) { + extractions.nfts.push(nft); + continue; + } + + // if the item is a payment, push to payments + const payment = getSupportedPayment(chainId, item); + if (payment) { + extractions.payments.push(payment); + continue; + } + } + + return extractions; +}; + +const consolidateSupportedNFTs = (nfts: SupportedNFT[]): SupportedNFT | null => { + // Either no NFT or multiple NFTs + if (nfts.length !== 1) return null; + return nfts[0]!; +}; + +const consolidateSupportedPayments = (payments: SupportedPayment[]): SupportedPayment | null => { + // Get the set of distinct currencies in the payment + const uniqueCurrencies = uniq(payments.map((payment) => payment.price.currency)); + + // Either no payment or multiple payments in mixed currencies + if (uniqueCurrencies.length !== 1) return null; + + // consolidate multiple payments in the same currency into one. + const totalAmount = payments.reduce((total, payment) => total + payment.price.amount, 0n); + + return { + price: { + currency: uniqueCurrencies[0]!, // we verified above there's exactly one currency + amount: totalAmount, + }, + }; +}; + +/** + * Maps from Seaport-specific OrderFulfilled event into our more generic TokenScope `SupportedSale`, + * if possible. TokenScope aims to deliver a simpler datamodel than Seaport provides but still + * support the majority of real-world use cases. + */ +export const getSupportedSaleFromOrderFulfilledEvent = ( + namespaceId: ENSNamespaceId, + chainId: ChainId, + event: OrderFulfilledEvent, +): SupportedSale | null => { + const { offer, consideration, orderHash, offerer, recipient } = event.args; + + const { nfts: offerNFTs, payments: offerPayments } = getSeaportItemExtractions( + namespaceId, + chainId, + offer, + ); + + const { nfts: considerationNFTs, payments: considerationPayments } = getSeaportItemExtractions( + namespaceId, + chainId, + consideration, + ); + + const consolidatedOfferNFT = consolidateSupportedNFTs(offerNFTs); + const consolidatedConsiderationNFT = consolidateSupportedNFTs(considerationNFTs); + const consolidatedOfferPayment = consolidateSupportedPayments(offerPayments); + const consolidatedConsiderationPayment = consolidateSupportedPayments(considerationPayments); + + // offer is exactly 1 supported NFT and consideration consolidates to 1 supported payment + // therefore the offerer is the seller and the recipient is the buyer + if ( + consolidatedOfferNFT && + !consolidatedConsiderationNFT && + !consolidatedOfferPayment && + consolidatedConsiderationPayment + ) { + return { + orderHash, + nft: consolidatedOfferNFT, + payment: consolidatedConsiderationPayment, + seller: offerer, + buyer: recipient, + }; + } + + // consideration is exactly 1 supported NFT and offer consolidates to 1 supported payment + // therefore the recipient is the seller and the offerer is the buyer + if ( + !consolidatedOfferNFT && + consolidatedConsiderationNFT && + consolidatedOfferPayment && + !consolidatedConsiderationPayment + ) { + return { + orderHash, + nft: consolidatedConsiderationNFT, + payment: consolidatedOfferPayment, + seller: recipient, + buyer: offerer, + }; + } + + // otherwise, unsupported sale + return null; +}; diff --git a/apps/ensindexer/src/lib/tokenscope/token-issuers.ts b/apps/ensindexer/src/lib/tokenscope/token-issuers.ts new file mode 100644 index 000000000..5365e8dc6 --- /dev/null +++ b/apps/ensindexer/src/lib/tokenscope/token-issuers.ts @@ -0,0 +1,171 @@ +import { maybeGetDatasourceContract } from "@/lib/datasource-helpers"; +import { TokenId } from "@/lib/tokenscope/assets"; +import { DatasourceNames, ENSNamespaceId } from "@ensnode/datasources"; +import { + AccountId, + BASENAMES_NODE, + ETH_NODE, + LINEANAMES_NODE, + LabelHash, + type Node, + accountIdEqual, + makeSubdomainNode, + uint256ToHex32, +} from "@ensnode/ensnode-sdk"; + +/** + * A contract that issues tokenized ENS names. + */ +export interface TokenIssuer { + /** + * The AccountId of the token issuer contract. + */ + contract: AccountId; + + /** + * Applies the token issuer contract's logic for converting from the token id + * representation of a domain to the domain id (Node) representation of a domain. + */ + getDomainId: (tokenId: TokenId) => Node; +} + +/** + * Converts the tokenId from an ENS name token-issuing contract to a Node + * for the case that the contract generates each tokenId using namehash of + * the full name. + * + * @param tokenId - The tokenId to convert + * @returns The Node of the tokenId + */ +const nameHashGeneratedTokenIdToNode = (tokenId: TokenId): Node => { + return uint256ToHex32(tokenId); +}; + +/** + * Converts the tokenId from an ENS name token-issuing contract to a Node + * for the case that the contract generates each tokenId using labelhash of + * the direct subname of the parent node. + * + * @param tokenId - The tokenId to convert + * @param parentNode - the parent Node that the token issuing contract issues subnames under + * @returns The Node of the tokenId issued under the parentNode + */ +const labelHashGeneratedTokenIdToNode = (tokenId: TokenId, parentNode: Node): Node => { + const labelHash: LabelHash = uint256ToHex32(tokenId); + return makeSubdomainNode(labelHash, parentNode); +}; + +/** + * Gets the contracts known to provide tokenized name ownership within the + * specified namespace. + * + * @param namespaceId - The ENSNamespace identifier (e.g. 'mainnet', 'sepolia', 'holesky', 'ens-test-env') + * @returns an array of 0 or more known TokenIssuer for the specified namespace + */ +const getKnownTokenIssuers = (namespaceId: ENSNamespaceId): TokenIssuer[] => { + const ethBaseRegistrar = maybeGetDatasourceContract( + namespaceId, + DatasourceNames.ENSRoot, + "BaseRegistrar", + ); + const nameWrapper = maybeGetDatasourceContract( + namespaceId, + DatasourceNames.ENSRoot, + "NameWrapper", + ); + const threeDnsBaseRegistrar = maybeGetDatasourceContract( + namespaceId, + DatasourceNames.ThreeDNSBase, + "ThreeDNSToken", + ); + const threeDnsOptimismRegistrar = maybeGetDatasourceContract( + namespaceId, + DatasourceNames.ThreeDNSOptimism, + "ThreeDNSToken", + ); + const lineanamesRegistrar = maybeGetDatasourceContract( + namespaceId, + DatasourceNames.Lineanames, + "BaseRegistrar", + ); + const basenamesRegistrar = maybeGetDatasourceContract( + namespaceId, + DatasourceNames.Basenames, + "BaseRegistrar", + ); + + const result: TokenIssuer[] = []; + + if (ethBaseRegistrar) { + result.push({ + contract: ethBaseRegistrar, + getDomainId: (tokenId: TokenId): Node => { + return labelHashGeneratedTokenIdToNode(tokenId, ETH_NODE); + }, + }); + } + + if (nameWrapper) { + result.push({ + contract: nameWrapper, + getDomainId: (tokenId: TokenId): Node => { + return nameHashGeneratedTokenIdToNode(tokenId); + }, + }); + } + + if (threeDnsBaseRegistrar) { + result.push({ + contract: threeDnsBaseRegistrar, + getDomainId: (tokenId: TokenId): Node => { + return nameHashGeneratedTokenIdToNode(tokenId); + }, + }); + } + + if (threeDnsOptimismRegistrar) { + result.push({ + contract: threeDnsOptimismRegistrar, + getDomainId: (tokenId: TokenId): Node => { + return nameHashGeneratedTokenIdToNode(tokenId); + }, + }); + } + + if (lineanamesRegistrar) { + result.push({ + contract: lineanamesRegistrar, + getDomainId: (tokenId: TokenId): Node => { + return labelHashGeneratedTokenIdToNode(tokenId, LINEANAMES_NODE); + }, + }); + } + + if (basenamesRegistrar) { + result.push({ + contract: basenamesRegistrar, + getDomainId: (tokenId: TokenId): Node => { + return labelHashGeneratedTokenIdToNode(tokenId, BASENAMES_NODE); + }, + }); + } + + return result; +}; + +/** + * Gets the known token issuer for the given contract in the specified namespace. + * + * @param namespaceId - The ENSNamespace identifier (e.g. 'mainnet', 'sepolia', 'holesky', + * 'ens-test-env') + * @param contract - The AccountId of the contract to get the known token issuer for + * @returns the known token issuer for the given contract, or null + * if the contract is not a known token issuer in the specified namespace + */ +export const getKnownTokenIssuer = ( + namespaceId: ENSNamespaceId, + contract: AccountId, +): TokenIssuer | null => { + const tokenIssuers = getKnownTokenIssuers(namespaceId); + return tokenIssuers.find((tokenIssuer) => accountIdEqual(tokenIssuer.contract, contract)) ?? null; +}; diff --git a/apps/ensindexer/src/plugins/index.ts b/apps/ensindexer/src/plugins/index.ts index be238d8a5..bec622776 100644 --- a/apps/ensindexer/src/plugins/index.ts +++ b/apps/ensindexer/src/plugins/index.ts @@ -7,12 +7,14 @@ import referralsPlugin from "./referrals/plugin"; import reverseResolversPlugin from "./reverse-resolvers/plugin"; import subgraphPlugin from "./subgraph/plugin"; import threednsPlugin from "./threedns/plugin"; +import tokenScopePlugin from "./tokenscope/plugin"; export const ALL_PLUGINS = [ subgraphPlugin, basenamesPlugin, lineaNamesPlugin, threednsPlugin, + tokenScopePlugin, reverseResolversPlugin, referralsPlugin, ] as const; diff --git a/apps/ensindexer/src/plugins/referrals/handlers/UnwrappedEthRegistrarController.ts b/apps/ensindexer/src/plugins/referrals/handlers/UnwrappedEthRegistrarController.ts index 525838996..268f27723 100644 --- a/apps/ensindexer/src/plugins/referrals/handlers/UnwrappedEthRegistrarController.ts +++ b/apps/ensindexer/src/plugins/referrals/handlers/UnwrappedEthRegistrarController.ts @@ -1,11 +1,8 @@ import { ponder } from "ponder:registry"; -import { namehash } from "viem"; import { handleRegistrationReferral, handleRenewalReferral } from "@/handlers/Referrals"; import { namespaceContract } from "@/lib/plugin-helpers"; -import { PluginName, makeSubdomainNode } from "@ensnode/ensnode-sdk"; - -const ETH_NODE = namehash("eth"); +import { ETH_NODE, PluginName, makeSubdomainNode } from "@ensnode/ensnode-sdk"; /** * Registers event handlers with Ponder. diff --git a/apps/ensindexer/src/plugins/tokenscope/event-handlers.ts b/apps/ensindexer/src/plugins/tokenscope/event-handlers.ts new file mode 100644 index 000000000..f48928573 --- /dev/null +++ b/apps/ensindexer/src/plugins/tokenscope/event-handlers.ts @@ -0,0 +1,9 @@ +import config from "@/config"; +import { PluginName } from "@ensnode/ensnode-sdk"; + +import attach_Seaport from "./handlers/Seaport"; + +// conditionally attach event handlers when Ponder executes this file +if (config.plugins.includes(PluginName.TokenScope)) { + attach_Seaport(); +} diff --git a/apps/ensindexer/src/plugins/tokenscope/handlers/Seaport.ts b/apps/ensindexer/src/plugins/tokenscope/handlers/Seaport.ts new file mode 100644 index 000000000..45faaa886 --- /dev/null +++ b/apps/ensindexer/src/plugins/tokenscope/handlers/Seaport.ts @@ -0,0 +1,61 @@ +import { ponder } from "ponder:registry"; +import schema from "ponder:schema"; +import { PluginName, uint256ToHex32 } from "@ensnode/ensnode-sdk"; +import { AssetId } from "caip"; + +import config from "@/config"; +import { upsertAccount } from "@/lib/db-helpers"; +import { makeEventId } from "@/lib/ids"; +import { namespaceContract } from "@/lib/plugin-helpers"; +import { getSupportedSaleFromOrderFulfilledEvent } from "@/lib/tokenscope/seaport"; + +/** + * Registers event handlers with Ponder. + */ +export default function () { + const pluginName = PluginName.TokenScope; + + ponder.on(namespaceContract(pluginName, "Seaport:OrderFulfilled"), async ({ context, event }) => { + const sale = getSupportedSaleFromOrderFulfilledEvent(config.namespace, context.chain.id, event); + + // the Seaport sale in the event is not supported by TokenScope, no-op + // this can happen for a number of reasons, including: + // - the sale was for a NFT that is not recognized as being associated with an ENS name + // (note how this event is triggered for any sale made through Seaport, not just ENS names) + // - the sale was not paid for with a supported currency (e.g. ETH, USDC, etc) + // - the sale received payments in multiple currencies + // - the sale was for multiple NFTs (not just one) + // TokenScope purposefully does not support these cases as we believe they overall add + // more complexity than benefit. We believe it's better to prioritize better simplicity of + // building apps on TokenScope than supporting these more complex and uncommon cases. + if (!sale) return; + + // upsert buyer and seller accounts + await upsertAccount(context, sale.seller); + await upsertAccount(context, sale.buyer); + + // insert NameSale entity + await context.db.insert(schema.nameSales).values({ + id: makeEventId(context.chain.id, event.block.number, event.log.logIndex), + chainId: sale.nft.contract.chainId, + blockNumber: event.block.number, + logIndex: event.log.logIndex, + transactionHash: event.transaction.hash, + orderHash: sale.orderHash, + contractAddress: sale.nft.contract.address, + tokenId: sale.nft.tokenId, + assetNamespace: sale.nft.assetNamespace, + assetId: AssetId.format({ + chainId: { namespace: "eip155", reference: sale.nft.contract.chainId.toString() }, + assetName: { namespace: sale.nft.assetNamespace, reference: sale.nft.contract.address }, + tokenId: uint256ToHex32(sale.nft.tokenId), + }), + domainId: sale.nft.domainId, + buyer: sale.buyer, + seller: sale.seller, + currency: sale.payment.price.currency, + amount: sale.payment.price.amount, + timestamp: event.block.timestamp, + }); + }); +} diff --git a/apps/ensindexer/src/plugins/tokenscope/plugin.ts b/apps/ensindexer/src/plugins/tokenscope/plugin.ts new file mode 100644 index 000000000..84dace2a1 --- /dev/null +++ b/apps/ensindexer/src/plugins/tokenscope/plugin.ts @@ -0,0 +1,44 @@ +/** + * The TokenScope plugin describes indexing behavior for marketplace contracts (e.g. Seaport) on all supported networks. + */ + +import { + createPlugin, + getDatasourceAsFullyDefinedAtCompileTime, + namespaceContract, +} from "@/lib/plugin-helpers"; +import { chainConfigForContract, chainsConnectionConfig } from "@/lib/ponder-helpers"; +import { DatasourceNames } from "@ensnode/datasources"; +import { PluginName } from "@ensnode/ensnode-sdk"; +import * as ponder from "ponder"; + +const pluginName = PluginName.TokenScope; + +export default createPlugin({ + name: pluginName, + requiredDatasourceNames: [DatasourceNames.Seaport], + createPonderConfig(config) { + const seaport = getDatasourceAsFullyDefinedAtCompileTime( + config.namespace, + DatasourceNames.Seaport, + ); + + return ponder.createConfig({ + chains: { + ...chainsConnectionConfig(config.rpcConfigs, seaport.chain.id), + }, + contracts: { + [namespaceContract(pluginName, "Seaport")]: { + chain: { + ...chainConfigForContract( + config.globalBlockrange, + seaport.chain.id, + seaport.contracts.Seaport1_5, + ), + }, + abi: seaport.contracts.Seaport1_5.abi, + }, + }, + }); + }, +}); diff --git a/apps/ensindexer/test/token-issuers.test.ts b/apps/ensindexer/test/token-issuers.test.ts new file mode 100644 index 000000000..358f98319 --- /dev/null +++ b/apps/ensindexer/test/token-issuers.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; + +import { DatasourceNames, ENSNamespaceIds, getDatasource } from "@ensnode/datasources"; +import { getKnownTokenIssuer } from "../src/lib/tokenscope/token-issuers"; + +const { + contracts: { NameWrapper }, +} = getDatasource(ENSNamespaceIds.Mainnet, DatasourceNames.ENSRoot); + +describe("token-issuers", () => { + it("works for expected mainnet contract", () => { + const issuer = getKnownTokenIssuer(ENSNamespaceIds.Mainnet, { + chainId: 1, + address: NameWrapper.address, + }); + + expect(issuer).toBeDefined(); + }); +}); diff --git a/docs/ensnode.io/src/content/docs/docs/usage/api.mdx b/docs/ensnode.io/src/content/docs/docs/usage/api.mdx index 31099ef20..2a7fa9c61 100644 --- a/docs/ensnode.io/src/content/docs/docs/usage/api.mdx +++ b/docs/ensnode.io/src/content/docs/docs/usage/api.mdx @@ -113,7 +113,8 @@ The response includes several important configuration categories: "lineanames", "threedns", "reverse-resolvers", - "referrals" + "referrals", + "tokenscope" ], "dependencyInfo": { "nodejs": "22.18.0", diff --git a/docs/ensnode.io/src/content/docs/docs/usage/hosted-ensnode-instances.mdx b/docs/ensnode.io/src/content/docs/docs/usage/hosted-ensnode-instances.mdx index 3847ff472..bd29f43a0 100644 --- a/docs/ensnode.io/src/content/docs/docs/usage/hosted-ensnode-instances.mdx +++ b/docs/ensnode.io/src/content/docs/docs/usage/hosted-ensnode-instances.mdx @@ -31,7 +31,7 @@ These ENSNode instances focus on pushing the boundaries of what's possible with connectWithENSAdminURL="https://admin.ensnode.io/connect?ensnode=https%3A%2F%2Fapi.alpha.ensnode.io" namespace="mainnet" plugins="subgraph, basenames, lineanames, threedns, reverse-resolvers, referrals" - purpose="Demonstration of ENSNode's multichain ENS indexing capability for the ENS namespace rooted on Mainnet. Provides a superset of the data indexed by the ENS Subgraph. Indexes additional Resolver record values in preparation for ENS Protocol Acceleration." + purpose="Demonstration of ENSNode's multichain ENS indexing capability for the ENS namespace rooted on Mainnet. Provides a superset of the data indexed by the ENS Subgraph. Offers a preview of ENS Protocol Acceleration." /> #### ENSNode 'Alpha-Sepolia' @@ -40,7 +40,7 @@ These ENSNode instances focus on pushing the boundaries of what's possible with connectWithENSAdminURL="https://admin.ensnode.io/connect?ensnode=https%3A%2F%2Fapi.alpha-sepolia.ensnode.io" namespace="sepolia" plugins="subgraph, basenames, lineanames, reverse-resolvers, referrals" - purpose="Demonstration of ENSNode's multichain ENS indexing capability for the ENS namespace rooted on Sepolia. Provides a superset of the data indexed by the ENS Subgraph. Indexes additional Resolver record values in preparation for ENS Protocol Acceleration." + purpose="Demonstration of ENSNode's multichain ENS indexing capability for the ENS namespace rooted on Sepolia. Provides a superset of the data indexed by the ENS Subgraph. Offers a preview of ENS Protocol Acceleration." /> ### Subgraph-style Deployments diff --git a/packages/datasources/src/abis/seaport/Seaport1.5.ts b/packages/datasources/src/abis/seaport/Seaport1.5.ts new file mode 100644 index 000000000..6d4e14858 --- /dev/null +++ b/packages/datasources/src/abis/seaport/Seaport1.5.ts @@ -0,0 +1,2002 @@ +export const Seaport = [ + { + inputs: [{ internalType: "address", name: "conduitController", type: "address" }], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "BadContractSignature", type: "error" }, + { + inputs: [], + name: "BadFraction", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { + internalType: "address", + name: "from", + type: "address", + }, + { internalType: "address", name: "to", type: "address" }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "BadReturnValueFromERC20OnTransfer", + type: "error", + }, + { + inputs: [{ internalType: "uint8", name: "v", type: "uint8" }], + name: "BadSignatureV", + type: "error", + }, + { inputs: [], name: "CannotCancelOrder", type: "error" }, + { + inputs: [], + name: "ConsiderationCriteriaResolverOutOfRange", + type: "error", + }, + { + inputs: [], + name: "ConsiderationLengthNotEqualToTotalOriginal", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "considerationIndex", + type: "uint256", + }, + { internalType: "uint256", name: "shortfallAmount", type: "uint256" }, + ], + name: "ConsiderationNotMet", + type: "error", + }, + { inputs: [], name: "CriteriaNotEnabledForItem", type: "error" }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { internalType: "address", name: "from", type: "address" }, + { + internalType: "address", + name: "to", + type: "address", + }, + { internalType: "uint256[]", name: "identifiers", type: "uint256[]" }, + { + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + ], + name: "ERC1155BatchTransferGenericFailure", + type: "error", + }, + { inputs: [], name: "InexactFraction", type: "error" }, + { + inputs: [], + name: "InsufficientNativeTokensSupplied", + type: "error", + }, + { inputs: [], name: "Invalid1155BatchTransferEncoding", type: "error" }, + { + inputs: [], + name: "InvalidBasicOrderParameterEncoding", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "conduit", type: "address" }], + name: "InvalidCallToConduit", + type: "error", + }, + { + inputs: [ + { internalType: "bytes32", name: "conduitKey", type: "bytes32" }, + { + internalType: "address", + name: "conduit", + type: "address", + }, + ], + name: "InvalidConduit", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + name: "InvalidContractOrder", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "amount", type: "uint256" }], + name: "InvalidERC721TransferAmount", + type: "error", + }, + { inputs: [], name: "InvalidFulfillmentComponentData", type: "error" }, + { + inputs: [ + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "InvalidMsgValue", + type: "error", + }, + { inputs: [], name: "InvalidNativeOfferItem", type: "error" }, + { + inputs: [], + name: "InvalidProof", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + name: "InvalidRestrictedOrder", + type: "error", + }, + { inputs: [], name: "InvalidSignature", type: "error" }, + { + inputs: [], + name: "InvalidSigner", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "startTime", type: "uint256" }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + ], + name: "InvalidTime", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "fulfillmentIndex", type: "uint256" }], + name: "MismatchedFulfillmentOfferAndConsiderationComponents", + type: "error", + }, + { + inputs: [{ internalType: "enum Side", name: "side", type: "uint8" }], + name: "MissingFulfillmentComponentOnAggregation", + type: "error", + }, + { inputs: [], name: "MissingItemAmount", type: "error" }, + { + inputs: [], + name: "MissingOriginalConsiderationItems", + type: "error", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "NativeTokenTransferGenericFailure", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "NoContract", + type: "error", + }, + { inputs: [], name: "NoReentrantCalls", type: "error" }, + { + inputs: [], + name: "NoSpecifiedOrdersAvailable", + type: "error", + }, + { inputs: [], name: "OfferAndConsiderationRequiredOnFulfillment", type: "error" }, + { + inputs: [], + name: "OfferCriteriaResolverOutOfRange", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + name: "OrderAlreadyFilled", + type: "error", + }, + { + inputs: [{ internalType: "enum Side", name: "side", type: "uint8" }], + name: "OrderCriteriaResolverOutOfRange", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + name: "OrderIsCancelled", + type: "error", + }, + { + inputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + name: "OrderPartiallyFilled", + type: "error", + }, + { inputs: [], name: "PartialFillsNotEnabledForOrder", type: "error" }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { internalType: "address", name: "from", type: "address" }, + { + internalType: "address", + name: "to", + type: "address", + }, + { internalType: "uint256", name: "identifier", type: "uint256" }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "TokenTransferGenericFailure", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "considerationIndex", + type: "uint256", + }, + ], + name: "UnresolvedConsiderationCriteria", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "offerIndex", + type: "uint256", + }, + ], + name: "UnresolvedOfferCriteria", + type: "error", + }, + { inputs: [], name: "UnusedItemParameters", type: "error" }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "uint256", name: "newCounter", type: "uint256" }, + { + indexed: true, + internalType: "address", + name: "offerer", + type: "address", + }, + ], + name: "CounterIncremented", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "bytes32", name: "orderHash", type: "bytes32" }, + { + indexed: true, + internalType: "address", + name: "offerer", + type: "address", + }, + { indexed: true, internalType: "address", name: "zone", type: "address" }, + ], + name: "OrderCancelled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "bytes32", name: "orderHash", type: "bytes32" }, + { + indexed: true, + internalType: "address", + name: "offerer", + type: "address", + }, + { indexed: true, internalType: "address", name: "zone", type: "address" }, + { + indexed: false, + internalType: "address", + name: "recipient", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifier", + type: "uint256", + }, + { internalType: "uint256", name: "amount", type: "uint256" }, + ], + indexed: false, + internalType: "struct SpentItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifier", + type: "uint256", + }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { + internalType: "address payable", + name: "recipient", + type: "address", + }, + ], + indexed: false, + internalType: "struct ReceivedItem[]", + name: "consideration", + type: "tuple[]", + }, + ], + name: "OrderFulfilled", + type: "event", + }, + { + anonymous: false, + inputs: [ + { indexed: false, internalType: "bytes32", name: "orderHash", type: "bytes32" }, + { + components: [ + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + indexed: false, + internalType: "struct OrderParameters", + name: "orderParameters", + type: "tuple", + }, + ], + name: "OrderValidated", + type: "event", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "bytes32[]", name: "orderHashes", type: "bytes32[]" }], + name: "OrdersMatched", + type: "event", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "counter", type: "uint256" }, + ], + internalType: "struct OrderComponents[]", + name: "orders", + type: "tuple[]", + }, + ], + name: "cancel", + outputs: [{ internalType: "bool", name: "cancelled", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "uint120", name: "numerator", type: "uint120" }, + { + internalType: "uint120", + name: "denominator", + type: "uint120", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + { + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + internalType: "struct AdvancedOrder", + name: "", + type: "tuple", + }, + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "enum Side", name: "side", type: "uint8" }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { internalType: "uint256", name: "identifier", type: "uint256" }, + { + internalType: "bytes32[]", + name: "criteriaProof", + type: "bytes32[]", + }, + ], + internalType: "struct CriteriaResolver[]", + name: "", + type: "tuple[]", + }, + { internalType: "bytes32", name: "fulfillerConduitKey", type: "bytes32" }, + { + internalType: "address", + name: "recipient", + type: "address", + }, + ], + name: "fulfillAdvancedOrder", + outputs: [{ internalType: "bool", name: "fulfilled", type: "bool" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "uint120", name: "numerator", type: "uint120" }, + { + internalType: "uint120", + name: "denominator", + type: "uint120", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + { + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + internalType: "struct AdvancedOrder[]", + name: "", + type: "tuple[]", + }, + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "enum Side", name: "side", type: "uint8" }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { internalType: "uint256", name: "identifier", type: "uint256" }, + { + internalType: "bytes32[]", + name: "criteriaProof", + type: "bytes32[]", + }, + ], + internalType: "struct CriteriaResolver[]", + name: "", + type: "tuple[]", + }, + { + components: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "itemIndex", + type: "uint256", + }, + ], + internalType: "struct FulfillmentComponent[][]", + name: "", + type: "tuple[][]", + }, + { + components: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "itemIndex", + type: "uint256", + }, + ], + internalType: "struct FulfillmentComponent[][]", + name: "", + type: "tuple[][]", + }, + { internalType: "bytes32", name: "fulfillerConduitKey", type: "bytes32" }, + { + internalType: "address", + name: "recipient", + type: "address", + }, + { internalType: "uint256", name: "maximumFulfilled", type: "uint256" }, + ], + name: "fulfillAvailableAdvancedOrders", + outputs: [ + { + internalType: "bool[]", + name: "", + type: "bool[]", + }, + { + components: [ + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifier", + type: "uint256", + }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { + internalType: "address payable", + name: "recipient", + type: "address", + }, + ], + internalType: "struct ReceivedItem", + name: "item", + type: "tuple", + }, + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + ], + internalType: "struct Execution[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct Order[]", + name: "", + type: "tuple[]", + }, + { + components: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "itemIndex", + type: "uint256", + }, + ], + internalType: "struct FulfillmentComponent[][]", + name: "", + type: "tuple[][]", + }, + { + components: [ + { internalType: "uint256", name: "orderIndex", type: "uint256" }, + { + internalType: "uint256", + name: "itemIndex", + type: "uint256", + }, + ], + internalType: "struct FulfillmentComponent[][]", + name: "", + type: "tuple[][]", + }, + { internalType: "bytes32", name: "fulfillerConduitKey", type: "bytes32" }, + { + internalType: "uint256", + name: "maximumFulfilled", + type: "uint256", + }, + ], + name: "fulfillAvailableOrders", + outputs: [ + { + internalType: "bool[]", + name: "", + type: "bool[]", + }, + { + components: [ + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifier", + type: "uint256", + }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { + internalType: "address payable", + name: "recipient", + type: "address", + }, + ], + internalType: "struct ReceivedItem", + name: "item", + type: "tuple", + }, + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + ], + internalType: "struct Execution[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "considerationToken", + type: "address", + }, + { + internalType: "uint256", + name: "considerationIdentifier", + type: "uint256", + }, + { + internalType: "uint256", + name: "considerationAmount", + type: "uint256", + }, + { internalType: "address payable", name: "offerer", type: "address" }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { internalType: "address", name: "offerToken", type: "address" }, + { + internalType: "uint256", + name: "offerIdentifier", + type: "uint256", + }, + { + internalType: "uint256", + name: "offerAmount", + type: "uint256", + }, + { + internalType: "enum BasicOrderType", + name: "basicOrderType", + type: "uint8", + }, + { internalType: "uint256", name: "startTime", type: "uint256" }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + { internalType: "bytes32", name: "zoneHash", type: "bytes32" }, + { + internalType: "uint256", + name: "salt", + type: "uint256", + }, + { internalType: "bytes32", name: "offererConduitKey", type: "bytes32" }, + { + internalType: "bytes32", + name: "fulfillerConduitKey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "totalOriginalAdditionalRecipients", + type: "uint256", + }, + { + components: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct AdditionalRecipient[]", + name: "additionalRecipients", + type: "tuple[]", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct BasicOrderParameters", + name: "parameters", + type: "tuple", + }, + ], + name: "fulfillBasicOrder", + outputs: [{ internalType: "bool", name: "fulfilled", type: "bool" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "considerationToken", + type: "address", + }, + { + internalType: "uint256", + name: "considerationIdentifier", + type: "uint256", + }, + { + internalType: "uint256", + name: "considerationAmount", + type: "uint256", + }, + { internalType: "address payable", name: "offerer", type: "address" }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { internalType: "address", name: "offerToken", type: "address" }, + { + internalType: "uint256", + name: "offerIdentifier", + type: "uint256", + }, + { + internalType: "uint256", + name: "offerAmount", + type: "uint256", + }, + { + internalType: "enum BasicOrderType", + name: "basicOrderType", + type: "uint8", + }, + { internalType: "uint256", name: "startTime", type: "uint256" }, + { + internalType: "uint256", + name: "endTime", + type: "uint256", + }, + { internalType: "bytes32", name: "zoneHash", type: "bytes32" }, + { + internalType: "uint256", + name: "salt", + type: "uint256", + }, + { internalType: "bytes32", name: "offererConduitKey", type: "bytes32" }, + { + internalType: "bytes32", + name: "fulfillerConduitKey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "totalOriginalAdditionalRecipients", + type: "uint256", + }, + { + components: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct AdditionalRecipient[]", + name: "additionalRecipients", + type: "tuple[]", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct BasicOrderParameters", + name: "parameters", + type: "tuple", + }, + ], + name: "fulfillBasicOrder_efficient_6GL6yc", + outputs: [{ internalType: "bool", name: "fulfilled", type: "bool" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct Order", + name: "", + type: "tuple", + }, + { internalType: "bytes32", name: "fulfillerConduitKey", type: "bytes32" }, + ], + name: "fulfillOrder", + outputs: [{ internalType: "bool", name: "fulfilled", type: "bool" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "contractOfferer", type: "address" }], + name: "getContractOffererNonce", + outputs: [{ internalType: "uint256", name: "nonce", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "offerer", type: "address" }], + name: "getCounter", + outputs: [{ internalType: "uint256", name: "counter", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "counter", type: "uint256" }, + ], + internalType: "struct OrderComponents", + name: "", + type: "tuple", + }, + ], + name: "getOrderHash", + outputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "orderHash", type: "bytes32" }], + name: "getOrderStatus", + outputs: [ + { internalType: "bool", name: "isValidated", type: "bool" }, + { + internalType: "bool", + name: "isCancelled", + type: "bool", + }, + { internalType: "uint256", name: "totalFilled", type: "uint256" }, + { + internalType: "uint256", + name: "totalSize", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "incrementCounter", + outputs: [{ internalType: "uint256", name: "newCounter", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "information", + outputs: [ + { internalType: "string", name: "version", type: "string" }, + { + internalType: "bytes32", + name: "domainSeparator", + type: "bytes32", + }, + { internalType: "address", name: "conduitController", type: "address" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "uint120", name: "numerator", type: "uint120" }, + { + internalType: "uint120", + name: "denominator", + type: "uint120", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + { + internalType: "bytes", + name: "extraData", + type: "bytes", + }, + ], + internalType: "struct AdvancedOrder[]", + name: "", + type: "tuple[]", + }, + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "enum Side", name: "side", type: "uint8" }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { internalType: "uint256", name: "identifier", type: "uint256" }, + { + internalType: "bytes32[]", + name: "criteriaProof", + type: "bytes32[]", + }, + ], + internalType: "struct CriteriaResolver[]", + name: "", + type: "tuple[]", + }, + { + components: [ + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "uint256", name: "itemIndex", type: "uint256" }, + ], + internalType: "struct FulfillmentComponent[]", + name: "offerComponents", + type: "tuple[]", + }, + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "uint256", name: "itemIndex", type: "uint256" }, + ], + internalType: "struct FulfillmentComponent[]", + name: "considerationComponents", + type: "tuple[]", + }, + ], + internalType: "struct Fulfillment[]", + name: "", + type: "tuple[]", + }, + { internalType: "address", name: "recipient", type: "address" }, + ], + name: "matchAdvancedOrders", + outputs: [ + { + components: [ + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifier", + type: "uint256", + }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { + internalType: "address payable", + name: "recipient", + type: "address", + }, + ], + internalType: "struct ReceivedItem", + name: "item", + type: "tuple", + }, + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + ], + internalType: "struct Execution[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct Order[]", + name: "", + type: "tuple[]", + }, + { + components: [ + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "uint256", name: "itemIndex", type: "uint256" }, + ], + internalType: "struct FulfillmentComponent[]", + name: "offerComponents", + type: "tuple[]", + }, + { + components: [ + { + internalType: "uint256", + name: "orderIndex", + type: "uint256", + }, + { internalType: "uint256", name: "itemIndex", type: "uint256" }, + ], + internalType: "struct FulfillmentComponent[]", + name: "considerationComponents", + type: "tuple[]", + }, + ], + internalType: "struct Fulfillment[]", + name: "", + type: "tuple[]", + }, + ], + name: "matchOrders", + outputs: [ + { + components: [ + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifier", + type: "uint256", + }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { + internalType: "address payable", + name: "recipient", + type: "address", + }, + ], + internalType: "struct ReceivedItem", + name: "item", + type: "tuple", + }, + { internalType: "address", name: "offerer", type: "address" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + ], + internalType: "struct Execution[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "offerer", + type: "address", + }, + { + internalType: "address", + name: "zone", + type: "address", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + ], + internalType: "struct OfferItem[]", + name: "offer", + type: "tuple[]", + }, + { + components: [ + { + internalType: "enum ItemType", + name: "itemType", + type: "uint8", + }, + { internalType: "address", name: "token", type: "address" }, + { + internalType: "uint256", + name: "identifierOrCriteria", + type: "uint256", + }, + { internalType: "uint256", name: "startAmount", type: "uint256" }, + { + internalType: "uint256", + name: "endAmount", + type: "uint256", + }, + { internalType: "address payable", name: "recipient", type: "address" }, + ], + internalType: "struct ConsiderationItem[]", + name: "consideration", + type: "tuple[]", + }, + { internalType: "enum OrderType", name: "orderType", type: "uint8" }, + { + internalType: "uint256", + name: "startTime", + type: "uint256", + }, + { internalType: "uint256", name: "endTime", type: "uint256" }, + { + internalType: "bytes32", + name: "zoneHash", + type: "bytes32", + }, + { internalType: "uint256", name: "salt", type: "uint256" }, + { + internalType: "bytes32", + name: "conduitKey", + type: "bytes32", + }, + { internalType: "uint256", name: "totalOriginalConsiderationItems", type: "uint256" }, + ], + internalType: "struct OrderParameters", + name: "parameters", + type: "tuple", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct Order[]", + name: "", + type: "tuple[]", + }, + ], + name: "validate", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + { stateMutability: "payable", type: "receive" }, +] as const; diff --git a/packages/datasources/src/lib/types.ts b/packages/datasources/src/lib/types.ts index f6a415ee1..58325a6c3 100644 --- a/packages/datasources/src/lib/types.ts +++ b/packages/datasources/src/lib/types.ts @@ -1,4 +1,4 @@ -import type { Abi, Address, Chain } from "viem"; +import { type Abi, type Address, type Chain, isAddressEqual } from "viem"; /** * ENSNamespaceIds encodes the set of identifiers for well-known ENS namespaces. @@ -55,6 +55,7 @@ export const DatasourceNames = { ENSRoot: "ensroot", Basenames: "basenames", Lineanames: "lineanames", + Seaport: "seaport", ThreeDNSOptimism: "threedns-optimism", ThreeDNSBase: "threedns-base", ReverseResolverRoot: "reverse-resolver-root", diff --git a/packages/datasources/src/mainnet.ts b/packages/datasources/src/mainnet.ts index b95c37f8a..8e165d39a 100644 --- a/packages/datasources/src/mainnet.ts +++ b/packages/datasources/src/mainnet.ts @@ -25,6 +25,7 @@ import { NameWrapper as linea_NameWrapper } from "./abis/lineanames/NameWrapper" import { Registry as linea_Registry } from "./abis/lineanames/Registry"; import { ThreeDNSToken } from "./abis/threedns/ThreeDNSToken"; +import { Seaport as Seaport1_5 } from "./abis/seaport/Seaport1.5"; // Shared ABIs import { StandaloneReverseRegistrar } from "./abis/shared/StandaloneReverseRegistrar"; import { ResolverABI, ResolverFilter } from "./lib/resolver"; @@ -426,4 +427,15 @@ export default { }, }, }, + + [DatasourceNames.Seaport]: { + chain: mainnet, + contracts: { + Seaport1_5: { + abi: Seaport1_5, // Seaport 1.5 + address: "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC", + startBlock: 17129405, + }, + }, + }, } satisfies ENSNamespace; diff --git a/packages/datasources/src/sepolia.ts b/packages/datasources/src/sepolia.ts index 33ca2651c..d2030885f 100644 --- a/packages/datasources/src/sepolia.ts +++ b/packages/datasources/src/sepolia.ts @@ -30,6 +30,7 @@ import { EthRegistrarController as linea_EthRegistrarController } from "./abis/l import { NameWrapper as linea_NameWrapper } from "./abis/lineanames/NameWrapper"; import { Registry as linea_Registry } from "./abis/lineanames/Registry"; +import { Seaport as Seaport1_5 } from "./abis/seaport/Seaport1.5"; // Shared ABIs import { StandaloneReverseRegistrar } from "./abis/shared/StandaloneReverseRegistrar"; import { ResolverABI, ResolverFilter } from "./lib/resolver"; @@ -351,4 +352,15 @@ export default { }, }, }, + + [DatasourceNames.Seaport]: { + chain: sepolia, + contracts: { + Seaport1_5: { + abi: Seaport1_5, // Seaport 1.5 + address: "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC", + startBlock: 3365529, + }, + }, + }, } satisfies ENSNamespace; diff --git a/packages/ensnode-schema/src/ponder.schema.ts b/packages/ensnode-schema/src/ponder.schema.ts index f2a58f971..0c132ab22 100644 --- a/packages/ensnode-schema/src/ponder.schema.ts +++ b/packages/ensnode-schema/src/ponder.schema.ts @@ -3,6 +3,7 @@ */ export * from "./subgraph.schema"; export * from "./resolver-records.schema"; +export * from "./tokenscope.schema"; export * from "./resolver-relations.schema"; export * from "./referrals.schema"; export * from "./primary-names.schema"; diff --git a/packages/ensnode-schema/src/tokenscope.schema.ts b/packages/ensnode-schema/src/tokenscope.schema.ts new file mode 100644 index 000000000..64f7de1f1 --- /dev/null +++ b/packages/ensnode-schema/src/tokenscope.schema.ts @@ -0,0 +1,113 @@ +import { index, onchainTable } from "ponder"; + +export const nameSales = onchainTable( + "ext_name_sales", + (t) => ({ + /** + * Unique and deterministic identifier of the onchain event associated with the sale. + * + * Composite key format: "{chainId}-{blockNumber}-{logIndex}" (e.g., "1-1234567-5") + */ + id: t.text().primaryKey(), + + /** + * The chain where the sale occurred. + */ + chainId: t.integer().notNull(), + + /** + * The block number on chainId where the sale occurred. + */ + blockNumber: t.bigint().notNull(), + + /** + * The log index position of the sale event within blockNumber. + */ + logIndex: t.integer().notNull(), + + /** + * The EVM transaction hash on chainId associated with the sale. + */ + transactionHash: t.hex().notNull(), + + /** + * The Seaport order hash. + */ + orderHash: t.hex().notNull(), + + /** + * The address of the contract on chainId that manages tokenId. + */ + contractAddress: t.hex().notNull(), + + /** + * The tokenId managed by contractAddress that was sold. + * + * Interpretation depends on 'assetNamespace': + * - erc721: Unique token within contract + * - erc1155: Token type identifier (multiple copies may exist) + */ + tokenId: t.bigint().notNull(), + + /** + * The CAIP-19 Asset Namespace of the token that was sold. Either `erc721` or `erc1155`. + * + * @see https://chainagnostic.org/CAIPs/caip-19 + */ + assetNamespace: t.text().notNull(), + + /** + * The CAIP-19 Asset ID of token that was sold. This is a globally unique reference to the + * specific asset in question. + * + * @see https://chainagnostic.org/CAIPs/caip-19 + */ + assetId: t.text().notNull(), + + /** + * The namehash of the ENS domain that was sold. + */ + domainId: t.hex().notNull(), + + /** + * The account that bought the token controlling ownership of domainId from + * the seller for the amount of currency associated with the sale. + */ + buyer: t.hex().notNull(), + + /** + * The account that sold the token controlling ownership of domainId to + * buyer for the amount of currency associated with the sale. + */ + seller: t.hex().notNull(), + + /** + * Currency of the payment (ETH, USDC or DAI) from buyer to seller in exchange for tokenId. + */ + currency: t.text().notNull(), + + /** + * The amount of currency paid from buyer to seller in exchange for tokenId. + * + * Denominated in the smallest unit of currency. + * + * Amount interpretation depends on currency: + * - ETH/WETH: Amount in wei (1 ETH = 10^18 wei) + * - USDC: Amount in micro-units (1 USDC = 10^6 units) + * - DAI: Amount in wei-equivalent (1 DAI = 10^18 units) + */ + amount: t.bigint().notNull(), + + /** + * Unix timestamp of the block timestamp when the sale occurred. + */ + timestamp: t.bigint().notNull(), + }), + (t) => ({ + idx_domainId: index().on(t.domainId), + idx_assetId: index().on(t.assetId), + idx_buyer: index().on(t.buyer), + idx_seller: index().on(t.seller), + idx_timestamp: index().on(t.timestamp), + }), +); diff --git a/packages/ensnode-sdk/src/ens/constants.ts b/packages/ensnode-sdk/src/ens/constants.ts index 18736b3ed..23b6fe02f 100644 --- a/packages/ensnode-sdk/src/ens/constants.ts +++ b/packages/ensnode-sdk/src/ens/constants.ts @@ -3,6 +3,9 @@ import { namehash } from "viem"; import type { Node } from "./types"; export const ROOT_NODE: Node = namehash(""); +export const ETH_NODE = namehash("eth"); +export const BASENAMES_NODE = namehash("base.eth"); +export const LINEANAMES_NODE = namehash("linea.eth"); /** * A set of nodes whose children are used for reverse resolution. diff --git a/packages/ensnode-sdk/src/ens/subname-helpers.ts b/packages/ensnode-sdk/src/ens/subname-helpers.ts index 80ca5de63..ac93c7b99 100644 --- a/packages/ensnode-sdk/src/ens/subname-helpers.ts +++ b/packages/ensnode-sdk/src/ens/subname-helpers.ts @@ -1,4 +1,4 @@ -import { Address, concat, isAddress, isHash, keccak256, toHex } from "viem"; +import { Address, Hex, concat, isAddress, isHash, keccak256, toHex } from "viem"; import { labelhash } from "viem/ens"; import { addrReverseLabel } from "./reverse-name"; @@ -60,7 +60,7 @@ export const maybeHealLabelByReverseAddress = ({ * into Node or LabelHash, which is a common behavior in the ENS ecosystem. * (see NameWrapper, ETHRegistrarController) */ -export const uint256ToHex32 = (num: bigint) => toHex(num, { size: 32 }); +export const uint256ToHex32 = (num: bigint): Hex => toHex(num, { size: 32 }); /** * These characters are prohibited in normalized ENS names per the ENSIP-15 diff --git a/packages/ensnode-sdk/src/ensindexer/config/types.ts b/packages/ensnode-sdk/src/ensindexer/config/types.ts index e4a69ca06..6ce9d8287 100644 --- a/packages/ensnode-sdk/src/ensindexer/config/types.ts +++ b/packages/ensnode-sdk/src/ensindexer/config/types.ts @@ -1,11 +1,7 @@ -import type { ENSNamespaceId } from "../../ens"; -import type { - EnsRainbowClientLabelSet, - EnsRainbowServerLabelSet, - LabelSetId, - LabelSetVersion, -} from "../../ensrainbow"; -import type { ChainId } from "../../shared"; +import { ENSNamespaceId } from "@ensnode/datasources"; + +import type { EnsRainbowClientLabelSet } from "../../ensrainbow"; +import { ChainId } from "../../shared"; /** * A PluginName is a unique id for a 'plugin': we use the notion of @@ -18,6 +14,7 @@ export enum PluginName { ThreeDNS = "threedns", ReverseResolvers = "reverse-resolvers", Referrals = "referrals", + TokenScope = "tokenscope", } /** diff --git a/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts index d70de450d..141064785 100644 --- a/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts @@ -46,7 +46,7 @@ describe("ENSIndexer: Config", () => { ).toContain("Plugins cannot contain duplicate values"); expect(formatParseError(makePluginsListSchema().safeParse([]))).toContain( - "Plugins must be a list with at least one valid plugin name. Valid plugins are: subgraph, basenames, lineanames, threedns, reverse-resolvers, referrals", + "Plugins must be a list with at least one valid plugin name. Valid plugins are: subgraph, basenames, lineanames, threedns, reverse-resolvers, referrals, tokenscope", ); }); diff --git a/packages/ensnode-sdk/src/shared/account-id.ts b/packages/ensnode-sdk/src/shared/account-id.ts new file mode 100644 index 000000000..cdafc2a42 --- /dev/null +++ b/packages/ensnode-sdk/src/shared/account-id.ts @@ -0,0 +1,9 @@ +import { isAddressEqual } from "viem"; +import { AccountId } from "./types"; + +/** + * Determines where the provided AccountId values represent the same address on the same chain. + */ +export const accountIdEqual = (a: AccountId, b: AccountId): boolean => { + return a.chainId === b.chainId && isAddressEqual(a.address, b.address); +}; diff --git a/packages/ensnode-sdk/src/shared/index.ts b/packages/ensnode-sdk/src/shared/index.ts index 82d06500e..8a15fed28 100644 --- a/packages/ensnode-sdk/src/shared/index.ts +++ b/packages/ensnode-sdk/src/shared/index.ts @@ -13,3 +13,4 @@ export { deserializeUrl, } from "./deserialize"; export * from "./is-normalized"; +export * from "./account-id"; diff --git a/packages/ensnode-sdk/src/shared/serialized-types.ts b/packages/ensnode-sdk/src/shared/serialized-types.ts index 783114910..f14de2ef4 100644 --- a/packages/ensnode-sdk/src/shared/serialized-types.ts +++ b/packages/ensnode-sdk/src/shared/serialized-types.ts @@ -1,5 +1,3 @@ -import type { ChainId } from "./types"; - /** * A string representation of {@link ChainId}. **/ diff --git a/packages/ensnode-sdk/src/shared/types.ts b/packages/ensnode-sdk/src/shared/types.ts index c1ba24e32..1be985e58 100644 --- a/packages/ensnode-sdk/src/shared/types.ts +++ b/packages/ensnode-sdk/src/shared/types.ts @@ -1,3 +1,5 @@ +import { Address } from "viem"; + /** * Chain ID * @@ -6,6 +8,16 @@ **/ export type ChainId = number; +/** + * Represents an account (contract or EOA) at `address` on chain `chainId`. + * + * @see https://chainagnostic.org/CAIPs/caip-10 + */ +export interface AccountId { + chainId: ChainId; + address: Address; +} + /** * Block Number * diff --git a/packages/ensnode-sdk/src/shared/zod-schemas.ts b/packages/ensnode-sdk/src/shared/zod-schemas.ts index bbee7e190..aba8a1580 100644 --- a/packages/ensnode-sdk/src/shared/zod-schemas.ts +++ b/packages/ensnode-sdk/src/shared/zod-schemas.ts @@ -8,7 +8,7 @@ */ import z from "zod/v4"; import { ENSNamespaceIds } from "../ens"; -import type { BlockRef, ChainId, Datetime, Duration, UnixTimestamp } from "./types"; +import type { BlockRef, Datetime, Duration, UnixTimestamp } from "./types"; /** * Zod `.check()` function input. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa6e3e019..e8362beee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -321,6 +321,9 @@ importers: '@types/dns-packet': specifier: ^5.6.5 version: 5.6.5 + caip: + specifier: ^1.1.1 + version: 1.1.1 date-fns: specifier: 'catalog:' version: 4.1.0 @@ -4025,6 +4028,9 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + caip@1.1.1: + resolution: {integrity: sha512-a3v5lteUUOoyRI0U6qe5ayCCGkF2mCmJ5zQMDnOD2vRjgRg6sm9p8TsRC2h4D4beyqRN9RYniphAPnj/+jQC6g==} + call-bind-apply-helpers@1.0.1: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} @@ -8579,7 +8585,7 @@ snapshots: dependencies: '@astrojs/compiler': 2.12.2 '@astrojs/yaml2ts': 0.2.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@volar/kit': 2.4.11(typescript@5.7.3) '@volar/language-core': 2.4.11 '@volar/language-server': 2.4.11 @@ -8767,7 +8773,7 @@ snapshots: '@babel/core@7.26.9': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.0 '@babel/helper-compilation-targets': 7.26.5 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) @@ -8777,7 +8783,7 @@ snapshots: '@babel/traverse': 7.27.0 '@babel/types': 7.27.0 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.1 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -8811,7 +8817,7 @@ snapshots: dependencies: '@babel/core': 7.26.9 '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 '@babel/traverse': 7.27.0 transitivePeerDependencies: - supports-color @@ -8853,18 +8859,18 @@ snapshots: '@babel/template@7.27.0': dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 '@babel/parser': 7.27.0 '@babel/types': 7.27.0 '@babel/traverse@7.27.0': dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.0 '@babel/parser': 7.27.0 '@babel/template': 7.27.0 '@babel/types': 7.27.0 - debug: 4.4.0 + debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -9794,13 +9800,12 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/sourcemap-codec@1.5.5': - optional: true + '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: @@ -11600,7 +11605,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.24.0 '@typescript-eslint/visitor-keys': 8.24.0 - debug: 4.4.0 + debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -11643,7 +11648,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0 + debug: 4.4.1 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 @@ -11661,7 +11666,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0 + debug: 4.4.1 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 @@ -12288,6 +12293,8 @@ snapshots: cac@6.7.14: {} + caip@1.1.1: {} + call-bind-apply-helpers@1.0.1: dependencies: es-errors: 1.3.0 @@ -13440,7 +13447,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.0 + debug: 4.4.1 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -14332,7 +14339,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.0 + debug: 4.4.1 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color