diff --git a/.gitignore b/.gitignore index 25f3a93f..d9011236 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,5 @@ dist /generated /generated/ -/build \ No newline at end of file +/build +/ens/modules/airstack/* \ No newline at end of file diff --git a/airstack-modules/modules/airstack/domain-name/domain-name.ts b/airstack-modules/modules/airstack/domain-name/domain-name.ts index 32d62b6b..e8416e5e 100644 --- a/airstack-modules/modules/airstack/domain-name/domain-name.ts +++ b/airstack-modules/modules/airstack/domain-name/domain-name.ts @@ -68,8 +68,10 @@ export namespace domain { if (domain.parent == null) { parentDomain.subdomainCount = parentDomain.subdomainCount.plus(BIGINT_ONE); } - domain.name = name; - domain.labelName = labelName; + if (domain.name == null || domain.name!.includes("]") || domain.name!.includes("[")) { + domain.name = name; + domain.labelName = labelName; + } let ownerAccount = getOrCreateAirAccount(chainId, newOwner, airBlock); ownerAccount.save(); domain.owner = ownerAccount.id; @@ -167,14 +169,26 @@ export namespace domain { let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); // get previous resolver let previousResolverId = domain.resolver; - // create new resolver - let resolverEntity = getOrCreateAirResolver(domain, chainId, airBlock, resolver); - resolverEntity.save(); - // update domain resolver - domain.resolver = resolverEntity.id; - // update domain resolved address - if (resolverEntity.resolvedAddress) { - domain.resolvedAddress = resolverEntity.resolvedAddress; + // check if resolver is zero address + let resolverEntity = getAirResolver(resolver, domain); + if (resolver == ZERO_ADDRESS) { + // if yes, set domain.resolver = null + domain.resolvedAddress = null; + // do recursive subdomain count decrement + saveDomainEntity(domain, airBlock); + recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); + return; + } else { + if (resolverEntity == null) { + // marking domain.resolvedAddress as null as the resolver entity will be created newly + domain.resolvedAddress = null; + // create new resolver entity + resolverEntity = getOrCreateAirResolver(domain, chainId, airBlock, resolver); + resolverEntity.save(); + } else { + domain.resolvedAddress = resolverEntity.resolvedAddress; + } + domain.resolver = resolverEntity.id; } // do recursive subdomain count decrement saveDomainEntity(domain, airBlock); @@ -394,12 +408,27 @@ export namespace domain { airBlock, tokenAddress, )); - // tracking registration cost in domain entity - renewal cost is not being tracked yet + domain.expiryTimestamp = expiryTimestamp; // tracking expiry timestamp in domain entity from renewal and registered events + domain.registrationCost = cost; // tracking registration cost in domain entity - renewal cost is not being tracked yet + let airToken = getOrCreateAirToken(chainId, paymentToken); + airToken.save(); + domain.paymentToken = airToken.id; + if (domain.labelName !== labelName) { + if (!checkValidLabel(labelName, transactionHash) && domain.labelHash) { + const labelHash = domain.labelHash!; + labelName = '[' + labelHash.slice(2) + ']'; + } + domain.labelName = labelName + domain.name = labelName + '.eth' + // creating reverse registrar to get domainId when setting primary domain + if (domain.name) { + let reverseRegistrar = createReverseRegistrar(domain.name!, domain.id, airBlock); + reverseRegistrar.save(); + } + } + saveDomainEntity(domain, airBlock); if (fromRegistrationEvent) { - domain.registrationCost = cost; - let airToken = getOrCreateAirToken(chainId, paymentToken); - airToken.save(); - domain.paymentToken = airToken.id; + // name registration event let txn = getOrCreateAirNameRegisteredTransaction( chainId, airBlock, @@ -413,8 +442,6 @@ export namespace domain { txn.save(); } else { // name renewal event - // updating renewal cost in name renewed transaction entity - domain.expiryTimestamp = expiryTimestamp; let txn = getOrCreateAirNameRenewedTransaction( transactionHash, chainId, @@ -427,21 +454,6 @@ export namespace domain { ); txn.save(); } - if (domain.labelName !== labelName) { - if (!checkValidLabel(labelName, transactionHash) && domain.labelHash) { - const labelHash = domain.labelHash!; - labelName = '[' + labelHash.slice(2) + ']'; - } - domain.labelName = labelName - domain.name = labelName + '.eth' - // creating reverse registrar to get domainId when setting primary domain - if (domain.name) { - let reverseRegistrar = createReverseRegistrar(domain.name!, domain.id, airBlock); - reverseRegistrar.save(); - } - } - saveDomainEntity(domain, airBlock); - //new name registered event } /** @@ -976,6 +988,21 @@ export namespace domain { return AirDomain.load(domainId); } + /** + * @dev this function gets a air resolver entity + * @param resolver resolver address + * @param domain air domain entity + * @returns AirResolver entity or null + */ + export function getAirResolver( + resolver: string, + domain: AirDomain, + ): AirResolver | null { + let id = createResolverEntityId(resolver, domain.id); + let entity = AirResolver.load(id); + return entity; + } + /** * @dev this function does not save the returned entity * @dev this function gets or creates a new air domain owner changed transaction entity diff --git a/ens/modules/airstack/common/index.ts b/ens/modules/airstack/common/index.ts deleted file mode 100644 index e6142f21..00000000 --- a/ens/modules/airstack/common/index.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { - BigInt, - TypedMap, - dataSource, -} from "@graphprotocol/graph-ts"; -import { - AirBlock, - AirEntityCounter, - AirMeta, - AirAccount, - AirToken, -} from "../../../generated/schema"; - -export const AIR_META_ID = "AIR_META"; - -export const EMPTY_STRING = ""; -export const BIGINT_ONE = BigInt.fromI32(1); -export const BIG_INT_ZERO = BigInt.fromI32(0); - -export const SUBGRAPH_SCHEMA_VERSION = "1.0.0"; - -export const SUBGRAPH_NAME = "ens"; -export const SUBGRAPH_VERSION = "v1"; -export const SUBGRAPH_SLUG = "ens_v1"; - -const AIR_CHAIN_ID_MAP = new TypedMap(); -AIR_CHAIN_ID_MAP.set("arbitrum-one", "42161"); -AIR_CHAIN_ID_MAP.set("arweave-mainnet", "174"); -AIR_CHAIN_ID_MAP.set("aurora", "1313161554"); -AIR_CHAIN_ID_MAP.set("avalanche", "43114"); -AIR_CHAIN_ID_MAP.set("boba", "288"); -AIR_CHAIN_ID_MAP.set("bsc", "56"); -AIR_CHAIN_ID_MAP.set("celo", "42220"); -AIR_CHAIN_ID_MAP.set("COSMOS", "cosmos"); -AIR_CHAIN_ID_MAP.set("CRONOS", "25"); -AIR_CHAIN_ID_MAP.set("mainnet", "1"); -AIR_CHAIN_ID_MAP.set("goerli", "5"); -AIR_CHAIN_ID_MAP.set("fantom", "250"); -AIR_CHAIN_ID_MAP.set("fuse", "122"); -AIR_CHAIN_ID_MAP.set("harmony", "1666600000"); -AIR_CHAIN_ID_MAP.set("juno", "juno-1"); -AIR_CHAIN_ID_MAP.set("moonbeam", "1284"); -AIR_CHAIN_ID_MAP.set("moonriver", "1285"); -AIR_CHAIN_ID_MAP.set("near-mainnet", "1313161554"); -AIR_CHAIN_ID_MAP.set("optimism", "10"); -AIR_CHAIN_ID_MAP.set("osmosis", "osmosis-1"); -AIR_CHAIN_ID_MAP.set("matic", "137"); -AIR_CHAIN_ID_MAP.set("gnosis", "100") - -export function getChainId(): string { - const network = dataSource.network(); - const value = AIR_CHAIN_ID_MAP.get(network); - if (value != null) { - return value!; - } - throw new Error("Network not supported"); -} - -// common air entity functions - -/** - * @dev this function updates air entity counter for a given entity id - * @param id entity id for entity to be updated - * @param block air block object - * @returns updated entity count - */ -export function updateAirEntityCounter( - id: string, - block: AirBlock, -): BigInt { - let entity = AirEntityCounter.load(id); - if (entity == null) { - entity = new AirEntityCounter(id); - entity.count = BIGINT_ONE; - entity.createdAt = block.id; - entity.lastUpdatedAt = block.id; - createAirMeta(SUBGRAPH_SLUG, SUBGRAPH_NAME); - } else { - entity.count = entity.count.plus(BIGINT_ONE); - entity.lastUpdatedAt = block.id; - } - entity.save(); - return entity.count as BigInt; -} - -/** - * @dev this function creates air meta entity - * @param slug subgraph slug - * @param name subgraph name - */ -export function createAirMeta( - slug: string, - name: string - // should ideally have version also being passed from here -): void { - let meta = AirMeta.load(AIR_META_ID); - if (meta == null) { - meta = new AirMeta(AIR_META_ID); - let network = dataSource.network().toString(); - // handling special case for matic network - if (network == "matic") { - network = "polygon"; - } - meta.network = network; - meta.schemaVersion = SUBGRAPH_SCHEMA_VERSION; - meta.version = SUBGRAPH_VERSION; - meta.slug = slug; - meta.name = name; - meta.save(); - } -} - -/** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new air block entity - * @param chainId chain id - * @param blockHeight block number - * @param blockHash block hash - * @param blockTimestamp block timestamp - * @returns AirBlock entity - */ -export function getOrCreateAirBlock( - chainId: string, - blockHeight: BigInt, - blockHash: string, - blockTimestamp: BigInt -): AirBlock { - const id = chainId.concat("-").concat(blockHeight.toString()); - let block = AirBlock.load(id); - if (block == null) { - block = new AirBlock(id); - block.hash = blockHash; - block.number = blockHeight; - block.timestamp = blockTimestamp - } - return block as AirBlock; -} - -/** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new air account entity - * @param chainId chain id - * @param address account address - * @param block air block object - * @returns AirAccount entity - */ -export function getOrCreateAirAccount(chainId: string, address: string, block: AirBlock): AirAccount { - const id = chainId.concat("-").concat(address); - let entity = AirAccount.load(id); - if (entity == null) { - entity = new AirAccount(id); - entity.address = address; - entity.createdAt = block.id; - } - return entity as AirAccount; -} - -/** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new air token entity - * @param chainID chain id - * @param address token address - * @returns AirToken entity - */ -export function getOrCreateAirToken(chainID: string, address: string): AirToken { - let entity = AirToken.load(chainID + "-" + address); - if (entity == null) { - entity = new AirToken(chainID + "-" + address); - entity.address = address; - } - return entity as AirToken; -} \ No newline at end of file diff --git a/ens/modules/airstack/domain-name/Readme.md b/ens/modules/airstack/domain-name/Readme.md deleted file mode 100644 index c46caff9..00000000 --- a/ens/modules/airstack/domain-name/Readme.md +++ /dev/null @@ -1,126 +0,0 @@ -# DOMAIN_NAME vertical integration -### After module integration for DOMAIN_NAME vertical is done. Please call the below functions to track the transactions of the domain name vertical. -``` -1. Track transaction when a domain owner is changed - trackDomainOwnerChangedTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - logOrCallIndex: BigInt, #log or call index - used to differentiate between multiple logs or calls in a single transaction - domainId: string, #air domain entity id - needs to be unique for each domain - parentDomainId: string, #air domain entity id for parent domain - needs to be unique for each domain - tokenId: string, #ERC721 token id - keccack256(labelHash) - needs to be unique for each token - labelHash: string, #hex value of a label - labelName: string | null, #label name - eg: 'airswap' in airswap.eth - name: string | null, #domain name - eg: 'airswap.eth' - newOwner: string, #new owner address - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -2. Track transaction when a domain is transferred - trackDomainTransferTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - logOrCallIndex: BigInt, #log or call index - used to differentiate between multiple logs or calls in a single transaction - domainId: string, #air domain entity id - needs to be unique for each domain - newOwnerAddress: string, #address to which the domain is transferred - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -3. Track transaction when a domain resolver is changed - trackDomainNewResolverTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - logOrCallIndex: BigInt, #log or call index - used to differentiate between multiple logs or calls in a single transaction - domainId: string, #air domain entity id - needs to be unique for each domain - resolver: string, #new resolver address linked to the domain - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -4. Track transaction when a domain TTL is changed - trackDomainNewTTLTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - logOrCallIndex: BigInt, #log or call index - used to differentiate between multiple logs or calls in a single transaction - domainId: string, #air domain entity id - needs to be unique for each domain - newTTL: BigInt, #new TTL value for the domain - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -5. Track transaction when a domain is registered - trackNameRegisteredTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - logOrCallIndex: BigInt, #log or call index - used to differentiate between multiple logs or calls in a single transaction - domainId: string, #air domain entity id - needs to be unique for each domain - registrantAddress: string, #address which registered the domain - expiryTimestamp: BigInt, #unix time at which domain expires - cost: BigInt, #cost of domain registration in wei - paymentToken: string, #address of token used for registration cost - labelName: string | null, #label name - eg: 'airswap' in airswap.eth - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -6. Track transaction when a domain is renewed - trackNameRenewedTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - domainId: string, #air domain entity id - needs to be unique for each domain - cost: BigInt | null, #cost of domain renewal in wei - paymentToken: string, #address of token used for renewal cost - renewer: string, #address which renewed the domain - expiryTimestamp: BigInt, #unix time at which domain expires - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -7. Track controller transaction when a domain is renewed or registered - trackNameRenewedOrRegistrationByController( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - domainId: string, #air domain entity id - needs to be unique for each domain - name: string, #domain name - eg: 'airswap.eth' - label: Bytes, #hex value of a label - cost: BigInt, #cost of domain registration or renewal in wei - paymentToken: string, #address of token used for registration or renewal cost - renewer: string | null, #address which renewed the domain - expiryTimestamp: BigInt | null, #unix time at which domain expires - fromRegistrationEvent: boolean, #true if the transaction is from a registration event - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -8. Track transaction when a domain's resolved address is changed - trackResolvedAddressChangedTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - logOrCallIndex: BigInt, #log or call index - used to differentiate between multiple logs or calls in a single transaction - domainId: string, #air domain entity id - needs to be unique for each domain - resolverAddress: string, #address of the resolver contract - resolvedAddress: string, #address which the domain resolves to - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -9. Track transaction when a domain's resolver version is changed - trackResolverVersionChange( - block: ethereum.Block, #ethereum block object in subgraph - domainId: string, #air domain entity id - needs to be unique for each domain - resolverAddress: string, #address of the resolver contract - tokenAddress: string, #token address for ERC721 token - ) -``` -``` -10. Track transaction when an resolved address's primary domain is changed - trackSetPrimaryDomainTransaction( - block: ethereum.Block, #ethereum block object in subgraph - transactionHash: string, #transaction hash - domainName: string, #domain name - eg: 'airswap.eth' - from: string, #address which changed its primary domain to the domainName - tokenAddress: string, #token address for ERC721 token - ) -``` \ No newline at end of file diff --git a/ens/modules/airstack/domain-name/domain-name.ts b/ens/modules/airstack/domain-name/domain-name.ts deleted file mode 100644 index 32d62b6b..00000000 --- a/ens/modules/airstack/domain-name/domain-name.ts +++ /dev/null @@ -1,1055 +0,0 @@ -import { - BigInt, - Bytes, - crypto, - log, - ethereum, -} from "@graphprotocol/graph-ts"; - -import { - AirAccount, - AirBlock, - AirDomain, - AirDomainOwnerChangedTransaction, - AirDomainTransferTransaction, - AirResolver, - AirDomainNewResolverTransaction, - AirDomainNewTTLTransaction, - AirNameRegisteredTransaction, - AirNameRenewedTransaction, - AirResolvedAddressChanged, - AirPrimaryDomainTransaction, - ReverseRegistrar, - PrimaryDomain, - AirExtra, -} from "../../../generated/schema"; -import { createAirExtra, AIR_EXTRA_TTL, AIR_SET_PRIMARY_DOMAIN_ENTITY_COUNTER_ID, AIR_DOMAIN_OWNER_CHANGED_ENTITY_COUNTER_ID, AIR_ADDR_CHANGED_ENTITY_COUNTER_ID, AIR_NAME_RENEWED_ENTITY_COUNTER_ID, AIR_NAME_REGISTERED_ENTITY_COUNTER_ID, AIR_DOMAIN_NEW_TTL_ENTITY_COUNTER_ID, AIR_DOMAIN_NEW_RESOLVER_ENTITY_COUNTER_ID, AIR_DOMAIN_TRANSFER_ENTITY_COUNTER_ID, ZERO_ADDRESS, ETHEREUM_MAINNET_ID, checkValidLabel, saveDomainEntity } from "./utils"; -import { BIGINT_ONE, BIG_INT_ZERO, EMPTY_STRING, getChainId, updateAirEntityCounter, getOrCreateAirBlock, getOrCreateAirAccount, getOrCreateAirToken } from "../common"; - -export namespace domain { - /** - * @dev This function tracks a domain owner change transaction - * @param block ethreum block - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domainId domain id - * @param parentDomainId specifies the parentDomainId - * @param tokenId token id - * @param labelHash specifies the label hash - * @param labelName label name - * @param name domain name - * @param newOwner specifies the new owner of the domain - * @param tokenAddress contract address of nft token - */ - export function trackDomainOwnerChangedTransaction( - block: ethereum.Block, - transactionHash: string, - logOrCallIndex: BigInt, - domainId: string, - parentDomainId: string, - tokenId: string, - labelHash: string, - labelName: string | null, - name: string | null, - newOwner: string, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); - let previousOwnerId = domain.owner; - let parentDomain = getOrCreateAirDomain(new Domain( - parentDomainId, - chainId, - airBlock, - tokenAddress, - )); - if (domain.parent == null) { - parentDomain.subdomainCount = parentDomain.subdomainCount.plus(BIGINT_ONE); - } - domain.name = name; - domain.labelName = labelName; - let ownerAccount = getOrCreateAirAccount(chainId, newOwner, airBlock); - ownerAccount.save(); - domain.owner = ownerAccount.id; - domain.parent = parentDomain.id; - domain.labelHash = labelHash; - domain.tokenId = tokenId; - saveDomainEntity(parentDomain, airBlock); - saveDomainEntity(domain, airBlock); - recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); - - let txn = getOrCreateAirDomainOwnerChangedTransaction( - airBlock, - logOrCallIndex, - chainId, - previousOwnerId, - newOwner, - transactionHash, - tokenId, - domain, - ); - txn.save(); - } - - /** - * @dev This function tracks a domain transfer transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domainId domain id - * @param newOwnerAddress specifies the new owner of the domain - * @param tokenAddress contract address of nft token - */ - export function trackDomainTransferTransaction( - block: ethereum.Block, - transactionHash: string, - logOrCallIndex: BigInt, - domainId: string, - newOwnerAddress: string, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); - let previousOwnerId = domain.owner; - if (previousOwnerId == null) { - let previousOwnerAccount = getOrCreateAirAccount(chainId, ZERO_ADDRESS, airBlock); - previousOwnerAccount.save(); - previousOwnerId = previousOwnerAccount.id; - } - // update domain owner here - let newOwnerAccount = getOrCreateAirAccount(chainId, newOwnerAddress, airBlock); - newOwnerAccount.save(); - domain.owner = newOwnerAccount.id; - // set primary to false when domain is transferred - domain.isPrimary = false; - saveDomainEntity(domain, airBlock); - let id = createEntityId(transactionHash, block.number, logOrCallIndex); - let entity = AirDomainTransferTransaction.load(id); - if (entity == null) { - entity = new AirDomainTransferTransaction(id); - entity.from = previousOwnerId; - entity.to = newOwnerAccount.id; - entity.block = airBlock.id; - entity.transactionHash = transactionHash; - entity.tokenId = domain.tokenId; - entity.domain = domain.id; - entity.index = updateAirEntityCounter(AIR_DOMAIN_TRANSFER_ENTITY_COUNTER_ID, airBlock); - entity.save(); - } - } - - /** - * @dev This function tracks a new resolver transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domainId air domain id - * @param resolver resolver contract address - * @param tokenAddress contract address of nft token - */ - export function trackDomainNewResolverTransaction( - block: ethereum.Block, - transactionHash: string, - logOrCallIndex: BigInt, - domainId: string, - resolver: string, - tokenAddress: string, - ): void { - let chainId = getChainId(); - // get block - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - // get domain - let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); - // get previous resolver - let previousResolverId = domain.resolver; - // create new resolver - let resolverEntity = getOrCreateAirResolver(domain, chainId, airBlock, resolver); - resolverEntity.save(); - // update domain resolver - domain.resolver = resolverEntity.id; - // update domain resolved address - if (resolverEntity.resolvedAddress) { - domain.resolvedAddress = resolverEntity.resolvedAddress; - } - // do recursive subdomain count decrement - saveDomainEntity(domain, airBlock); - recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); - - // create new resolver transaction - let tnx = getOrCreateAirDomainNewResolverTransaction( - previousResolverId, - resolverEntity.address, - airBlock, - transactionHash, - logOrCallIndex, - domain, - ); - tnx.save(); - } - - /** - * @dev This function tracks a new TTL transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domainId air domain id - * @param newTTL new TTL - * @param tokenAddress contract address of nft token - */ - export function trackDomainNewTTLTransaction( - block: ethereum.Block, - transactionHash: string, - logOrCallIndex: BigInt, - domainId: string, - newTTL: BigInt, - tokenAddress: string, - ): void { - let chainId = getChainId(); - // get block - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - // get domain - let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); - // get previous ttl - let oldTTL: BigInt | null = null; - // load extra entity for ttl - let extraId = domainId.concat("-").concat(AIR_EXTRA_TTL); - let extra = AirExtra.load(extraId); - if (extra != null) { - // if exists, assign oldTTL and update value with newTTL - oldTTL = BigInt.fromString(extra.value); - extra.value = newTTL.toString(); - } else { - // else create new extra entity for ttl - extra = createAirExtra(AIR_EXTRA_TTL, newTTL.toString(), extraId); - } - extra.save(); - // update domain extras - let extrasArray = new Array(); - if (domain.extras == null) { - extrasArray.push(extra.id); - } else { - extrasArray = domain.extras!; - if (extrasArray.indexOf(extra.id) == -1) { - extrasArray.push(extra.id); - } - } - domain.extras = extrasArray; - saveDomainEntity(domain, airBlock); - // create AirDomainNewTTLTransaction - let txn = getOrCreateAirDomainNewTTLTransaction( - transactionHash, - block.number, - logOrCallIndex, - oldTTL, - newTTL, - airBlock, - domain, - ); - txn.save(); - } - - /** - * @dev This function tracks a new name registered transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domainId air domain id - * @param registrantAddress registrant address - * @param expiryTimestamp domain expiry date - * @param cost domain registration cost - * @param paymentToken payment token address - * @param labelName domain label name - * @param tokenAddress contract address of nft token - */ - export function trackNameRegisteredTransaction( - block: ethereum.Block, - transactionHash: string, - logOrCallIndex: BigInt, - domainId: string, - registrantAddress: string, - expiryTimestamp: BigInt, - cost: BigInt | null, - paymentToken: string, - labelName: string | null, - tokenAddress: string, - ): void { - let chainId = getChainId(); - // prep mapping data - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let domain = getOrCreateAirDomain(new Domain( - domainId, - chainId, - airBlock, - tokenAddress, - )); - if (labelName != null) { - domain.labelName = labelName - } - domain.expiryTimestamp = expiryTimestamp; - if (cost) { - domain.registrationCost = cost; - } - let airToken = getOrCreateAirToken(chainId, paymentToken); - airToken.save(); - domain.paymentToken = airToken.id; - saveDomainEntity(domain, airBlock); - // create name registered transaction - let txn = getOrCreateAirNameRegisteredTransaction( - chainId, - airBlock, - transactionHash, - domain, - cost, - paymentToken, - registrantAddress, - expiryTimestamp, - ); - txn.save(); - } - - /** - * @dev This function tracks a name renewal transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param domainId air domain id - * @param cost cost of renewal - * @param paymentToken payment token address - * @param renewer renewer address - * @param expiryTimestamp expiry date - * @param tokenAddress token address - */ - export function trackNameRenewedTransaction( - block: ethereum.Block, - transactionHash: string, - domainId: string, - cost: BigInt | null, - paymentToken: string, - renewer: string, - expiryTimestamp: BigInt, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let domain = getOrCreateAirDomain(new Domain( - domainId, - chainId, - airBlock, - tokenAddress, - )); - domain.expiryTimestamp = expiryTimestamp; - saveDomainEntity(domain, airBlock); - // create name renewed transaction - let txn = getOrCreateAirNameRenewedTransaction( - transactionHash, - chainId, - airBlock, - domain, - cost, - paymentToken, - renewer, - expiryTimestamp, - ); - txn.save(); - } - - /** - * @dev This function tracks trackNameRenewedOrRegistrationByController transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param domainId air domain id - * @param labelName domain labelName - * @param cost cost of registration or renewal - * @param paymentToken payment token address - * @param renewerOrRegistrant renewer or registrant address - * @param expiryTimestamp expiry timestamp - * @param fromRegistrationEvent true if called from a registration event - * @param tokenAddress contract address of nft token - */ - export function trackNameRenewedOrRegistrationByController( - block: ethereum.Block, - transactionHash: string, - domainId: string, - labelName: string, - cost: BigInt, - paymentToken: string, - renewerOrRegistrant: string, - expiryTimestamp: BigInt, - fromRegistrationEvent: boolean, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let domain = getOrCreateAirDomain(new Domain( - domainId, - chainId, - airBlock, - tokenAddress, - )); - // tracking registration cost in domain entity - renewal cost is not being tracked yet - if (fromRegistrationEvent) { - domain.registrationCost = cost; - let airToken = getOrCreateAirToken(chainId, paymentToken); - airToken.save(); - domain.paymentToken = airToken.id; - let txn = getOrCreateAirNameRegisteredTransaction( - chainId, - airBlock, - transactionHash, - domain, - cost, - paymentToken, - renewerOrRegistrant, - expiryTimestamp, - ); - txn.save(); - } else { - // name renewal event - // updating renewal cost in name renewed transaction entity - domain.expiryTimestamp = expiryTimestamp; - let txn = getOrCreateAirNameRenewedTransaction( - transactionHash, - chainId, - airBlock, - domain, - cost, - paymentToken, - renewerOrRegistrant, - expiryTimestamp, - ); - txn.save(); - } - if (domain.labelName !== labelName) { - if (!checkValidLabel(labelName, transactionHash) && domain.labelHash) { - const labelHash = domain.labelHash!; - labelName = '[' + labelHash.slice(2) + ']'; - } - domain.labelName = labelName - domain.name = labelName + '.eth' - // creating reverse registrar to get domainId when setting primary domain - if (domain.name) { - let reverseRegistrar = createReverseRegistrar(domain.name!, domain.id, airBlock); - reverseRegistrar.save(); - } - } - saveDomainEntity(domain, airBlock); - //new name registered event - } - - /** - * @dev This function tracks a resolved address changed transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domainId air domain id - * @param resolverAddress resolver contract address - * @param resolvedAddress resolved address of domain - * @param tokenAddress contract address of nft token - */ - export function trackResolvedAddressChangedTransaction( - block: ethereum.Block, - transactionHash: string, - logOrCallIndex: BigInt, - domainId: string, - resolverAddress: string, - resolvedAddress: string, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let addrAccount = getOrCreateAirAccount(chainId, resolvedAddress, airBlock); - addrAccount.save(); - let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); - let previousResolvedAddressId = domain.resolvedAddress; - let resolver = getOrCreateAirResolver(domain, chainId, airBlock, resolverAddress, resolvedAddress); - resolver.domain = domain.id; - resolver.save() - - if (domain.resolver == resolver.id) { - domain.resolvedAddress = addrAccount.id; - saveDomainEntity(domain, airBlock); - let txn = getOrCreateAirResolvedAddressChanged( - chainId, - logOrCallIndex, - resolver, - airBlock, - transactionHash, - previousResolvedAddressId, - resolvedAddress, - domain, - ); - txn.save(); - } - } - - /** - * @dev This function tracks a resolver version change - * @param block ethereum block - * @param domainId air domain id - * @param resolverAddress resolver contract address - * @param tokenAddress contract address of nft token - */ - export function trackResolverVersionChange( - block: ethereum.Block, - domainId: string, - resolverAddress: string, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let domain = getOrCreateAirDomain(new Domain(domainId, chainId, airBlock, tokenAddress)); - let resolver = getOrCreateAirResolver(domain, chainId, airBlock, resolverAddress, null); - resolver.save(); - if (domain && domain.resolver == resolver.id) { - domain.resolvedAddress = null - saveDomainEntity(domain, airBlock); - } - } - - /** - * @dev This function tracks a set primary domain transaction - * @param block ethereum block - * @param transactionHash transaction hash - * @param domainName domain name - * @param from event.from address - * @param tokenAddress contract address of nft token - */ - export function trackSetPrimaryDomainTransaction( - block: ethereum.Block, - transactionHash: string, - domainName: string, - from: string, - tokenAddress: string, - ): void { - let chainId = getChainId(); - let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); - airBlock.save(); - let reverseRegistrar = getReverseRegistrar(domainName); - if (reverseRegistrar == null) { - log.warning("Reverse registrar not found for name {} txhash {}", [domainName, transactionHash]); - return; - } - log.info("Reverse registrar found for name {} domainId {} txHash {}", [domainName, reverseRegistrar.domain, transactionHash]) - let domain = getOrCreateAirDomain(new Domain(reverseRegistrar.domain, chainId, airBlock, tokenAddress)); - let fromAccount = getOrCreateAirAccount(chainId, from, airBlock); - fromAccount.save(); - let oldPrimaryDomain: AirDomain | null = null; - // when domain's resolvedAddress is set as from address - if (domain.resolvedAddress == fromAccount.id) { - // get or create primary domain entity - let primaryDomainEntity = getOrCreatePrimaryDomain(domain, fromAccount, airBlock); - // when primary domain already exists for a resolved address and is not same as new domain - if (primaryDomainEntity.domain != domain.id) { - log.info("Primary domain already exists for resolvedAddressId {} oldDomain {} newDomain {}", [fromAccount.id, primaryDomainEntity.domain, domain.id]) - // unset isPrimary on old domain - oldPrimaryDomain = getOrCreateAirDomain(new Domain(primaryDomainEntity.domain, chainId, airBlock, tokenAddress)); - oldPrimaryDomain.isPrimary = false; - saveDomainEntity(oldPrimaryDomain, airBlock); - // set new primary domain for resolved address - primaryDomainEntity.domain = domain.id; - primaryDomainEntity.lastUpdatedAt = airBlock.id; - } - primaryDomainEntity.save(); - // set isPrimary on new domain - domain.isPrimary = true; - saveDomainEntity(domain, airBlock); - } - // record a set primary domain transaction with new domain - let txn = getOrCreateAirPrimaryDomainTransaction( - airBlock, - transactionHash, - oldPrimaryDomain, - domain, - from, - chainId, - ); - txn.save(); - } - - // end of track functions and start of get or create and helper functions - - /** - * @dev this function does not save the returned entity - * @dev This function gets or creates a primary domain entity - * @param domain air domain - * @param fromAccount air account - * @param block air block - * @returns primary domain entity - */ - function getOrCreatePrimaryDomain( - domain: AirDomain, - fromAccount: AirAccount, - block: AirBlock, - ): PrimaryDomain { - let id = fromAccount.id; - let entity = PrimaryDomain.load(id); - if (entity == null) { - entity = new PrimaryDomain(id); - entity.domain = domain.id; - entity.lastUpdatedAt = block.id; - log.info("Primary domain now for resolvedAddressId {} domain {}", [fromAccount.id, domain.id]) - } - return entity as PrimaryDomain; - } - - /** - * @dev this function does not save the returned entity - * @dev This function gets or creates a air primary domain transaction - * @param block air block - * @param transactionHash transaction hash - * @param previousDomain previous domain or null - * @param domain air domain - * @param resolvedAddress domain resolved address - * @param chainId chain id - * @returns air primary domain transaction - */ - function getOrCreateAirPrimaryDomainTransaction( - block: AirBlock, - transactionHash: string, - previousDomain: AirDomain | null, - domain: AirDomain, - resolvedAddress: string, - chainId: string, - ): AirPrimaryDomainTransaction { - let id = ETHEREUM_MAINNET_ID.concat('-').concat(transactionHash); - let entity = AirPrimaryDomainTransaction.load(id); - if (entity == null) { - entity = new AirPrimaryDomainTransaction(id); - entity.block = block.id; - entity.transactionHash = transactionHash; - entity.tokenId = domain.tokenId; - if (previousDomain != null) { - entity.previousDomain = previousDomain.id; - } - entity.domain = domain.id; - entity.index = updateAirEntityCounter(AIR_SET_PRIMARY_DOMAIN_ENTITY_COUNTER_ID, block); - let resolvedAddressAccount = getOrCreateAirAccount(chainId, resolvedAddress, block); //make sure to remove the old primary ens if changed - resolvedAddressAccount.save(); - entity.resolvedAddress = resolvedAddressAccount.id; - } - return entity as AirPrimaryDomainTransaction; - } - - /** - * @dev this function does not save the returned entity - * @dev This function gets or creates a AirAddrChanged entity - * @param chainId chain id - * @param logOrCallIndex txn log or index - * @param resolver resolver contract address - * @param block air block - * @param transactionHash transaction hash - * @param previousResolvedAddressId air account id of previous resolved address - * @param newResolvedAddress new resolved address - * @param domain domain - * @returns AirAddrChanged entity - */ - function getOrCreateAirResolvedAddressChanged( - chainId: string, - logOrCallIndex: BigInt, - resolver: AirResolver, - block: AirBlock, - transactionHash: string, - previousResolvedAddressId: string | null, - newResolvedAddress: string, - domain: AirDomain, - ): AirResolvedAddressChanged { - let id = createEntityId(transactionHash, block.number, logOrCallIndex); - let entity = AirResolvedAddressChanged.load(id); - if (entity == null) { - entity = new AirResolvedAddressChanged(id); - entity.resolver = resolver.id; - entity.block = block.id; - entity.transactionHash = transactionHash; - entity.previousResolvedAddress = previousResolvedAddressId; - let newResolvedAddressAccount = getOrCreateAirAccount(chainId, newResolvedAddress, block); - newResolvedAddressAccount.save(); - entity.newResolvedAddress = newResolvedAddressAccount.id; - entity.domain = domain.id; - entity.tokenId = domain.tokenId; - entity.index = updateAirEntityCounter(AIR_ADDR_CHANGED_ENTITY_COUNTER_ID, block); - } - return entity as AirResolvedAddressChanged; - } - - /** - * @dev This function creates a resolver entity id - * @param resolver domain resolver address - * @param node domain node - * @returns returns a resolver entity id - */ - function createResolverEntityId(resolver: string, node: string): string { - return resolver.concat("-").concat(node); - } - - /** - * @dev this is a generic function to create ids for entities - * @param transactionHash transaction hash - * @param blockHeight block number in the chain - * @param logOrCallIndex txn log or call index - * @returns entity id in string format - */ - function createEntityId(transactionHash: string, blockHeight: BigInt, logOrCallIndex: BigInt): string { - return transactionHash.concat("-").concat(blockHeight.toString()).concat('-').concat(logOrCallIndex.toString()); - } - - /** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new AirNameRenewedTransaction entity - * @param transactionHash transaction hash - * @param chainId chain id - * @param block air block - * @param domain air domain - * @param cost cost of the transaction - * @param paymentToken payment token - * @param renewer renewer address - * @param expiryTimestamp expiry date of the domain - * @returns AirNameRenewedTransaction entity - */ - function getOrCreateAirNameRenewedTransaction( - transactionHash: string, - chainId: string, - block: AirBlock, - domain: AirDomain, - cost: BigInt | null, - paymentToken: string, - renewer: string, - expiryTimestamp: BigInt, - ): AirNameRenewedTransaction { - let id = transactionHash.concat("-").concat(domain.id); - let entity = AirNameRenewedTransaction.load(id); - if (entity == null) { - entity = new AirNameRenewedTransaction(id); - entity.block = block.id; - entity.transactionHash = transactionHash; - entity.tokenId = domain.tokenId; - entity.domain = domain.id; - entity.cost = cost; - entity.index = updateAirEntityCounter(AIR_NAME_RENEWED_ENTITY_COUNTER_ID, block); - let airToken = getOrCreateAirToken(chainId, paymentToken); - airToken.save(); - entity.paymentToken = airToken.id; - let renewerAccount = getOrCreateAirAccount(chainId, renewer, block); - renewerAccount.save(); - entity.renewer = renewerAccount.id; - entity.expiryTimestamp = expiryTimestamp; - } - // getting renewal events from 2 contracts, old contract gives cost, new contract gives null, so if old contract event is processed first, then we update the cost - // if new contract event is processed first, then we don't update the cost - if (cost && !entity.cost) { - entity.cost = cost; - } - return entity as AirNameRenewedTransaction; - } - - /** - * @dev this function does not save the returned entity - * @dev this function gets or creates an AirNameRegisteredTransaction entity - * @param chainId chain id - * @param block air block entity - * @param transactionHash transaction hash - * @param domain air domain - * @param cost cost of the transaction - * @param paymentToken payment token - can be null - * @param registrant registrant address - * @param expiryTimestamp expiry date of the domain - * @returns returns an AirNameRegisteredTransaction entity - */ - function getOrCreateAirNameRegisteredTransaction( - chainId: string, - block: AirBlock, - transactionHash: string, - domain: AirDomain, - cost: BigInt | null, - paymentToken: string, - registrant: string, - expiryTimestamp: BigInt, - ): AirNameRegisteredTransaction { - let id = domain.id.concat("-").concat(transactionHash); - let entity = AirNameRegisteredTransaction.load(id); - if (entity == null) { - entity = new AirNameRegisteredTransaction(id); - entity.block = block.id; - entity.transactionHash = transactionHash; - entity.tokenId = domain.tokenId; - entity.domain = domain.id; - entity.index = updateAirEntityCounter(AIR_NAME_REGISTERED_ENTITY_COUNTER_ID, block); - let airToken = getOrCreateAirToken(chainId, paymentToken); - airToken.save(); - entity.paymentToken = airToken.id; - entity.expiryTimestamp = expiryTimestamp; - } - let registrantAccount = getOrCreateAirAccount(chainId, registrant, block); - registrantAccount.save(); - entity.registrant = registrantAccount.id; - entity.cost = cost; - return entity as AirNameRegisteredTransaction; - } - - /** - * @dev this function does not save the returned entity - * @dev this function gets or creates an AirResolver entity - * @param domain air domain entity - * @param chainId chain id - * @param block air block - * @param resolver resolver contract address - * @param resolvedAddress address of resolved address or null - * @returns AirResolver entity - */ - function getOrCreateAirResolver( - domain: AirDomain, - chainId: string, - block: AirBlock, - resolver: string, - resolvedAddress: string | null = EMPTY_STRING, - ): AirResolver { - let id = createResolverEntityId(resolver, domain.id); - let entity = AirResolver.load(id); - if (entity == null) { - entity = new AirResolver(id); - let resolverAccount = getOrCreateAirAccount(chainId, resolver, block); - resolverAccount.save(); - entity.address = resolverAccount.id; - entity.domain = domain.id; - } - if (resolvedAddress && resolvedAddress != EMPTY_STRING) { - let resolvedAddressAccount = getOrCreateAirAccount(chainId, resolvedAddress, block); - resolvedAddressAccount.save(); - entity.resolvedAddress = resolvedAddressAccount.id; - } else if (resolvedAddress == null) { - entity.resolvedAddress = null; - } - return entity as AirResolver; - } - - /** - * @dev this function does not save the returned entity - * @dev This function gets or creates a new AirDomainNewTTLTransaction entity - * @param transactionHash transaction hash - * @param blockHeight block number in the chain - * @param logOrCallIndex txn log or call index - * @param oldTTL old ttl value - can be null - * @param newTTL new ttl value - * @param block air block object - * @param domain air domain object - * @returns returns a air domain new ttl transaction entity - */ - function getOrCreateAirDomainNewTTLTransaction( - transactionHash: string, - blockHeight: BigInt, - logOrCallIndex: BigInt, - oldTTL: BigInt | null, - newTTL: BigInt, - block: AirBlock, - domain: AirDomain, - ): AirDomainNewTTLTransaction { - let id = createEntityId(transactionHash, blockHeight, logOrCallIndex); - let entity = AirDomainNewTTLTransaction.load(id); - if (entity == null) { - entity = new AirDomainNewTTLTransaction(id); - entity.oldTTL = oldTTL; - entity.transactionHash = transactionHash; - entity.newTTL = newTTL; - entity.block = block.id; - entity.tokenId = domain.tokenId; - entity.domain = domain.id; - entity.index = updateAirEntityCounter(AIR_DOMAIN_NEW_TTL_ENTITY_COUNTER_ID, block); - } - return entity as AirDomainNewTTLTransaction; - } - - /** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new AirDomainNewResolverTransaction entity - * @param previousResolverId previous resolver Id - can be null - * @param newResolverId new resolver Id - * @param block air block entity - * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index - * @param domain air domain entity - * @returns AirDomainNewResolverTransaction entity - */ - function getOrCreateAirDomainNewResolverTransaction( - previousResolverId: string | null, - newResolverId: string, - block: AirBlock, - transactionHash: string, - logOrCallIndex: BigInt, - domain: AirDomain, - ): AirDomainNewResolverTransaction { - let id = createEntityId(transactionHash, block.number, logOrCallIndex); - let entity = AirDomainNewResolverTransaction.load(id); - if (entity == null) { - entity = new AirDomainNewResolverTransaction(id); - entity.previousResolver = previousResolverId; - entity.newOwnerResolver = newResolverId; - entity.block = block.id; - entity.transactionHash = transactionHash; - entity.tokenId = domain.tokenId; - entity.domain = domain.id; - entity.index = updateAirEntityCounter(AIR_DOMAIN_NEW_RESOLVER_ENTITY_COUNTER_ID, block); - } - return entity as AirDomainNewResolverTransaction; - } - - /** - * @dev this function does not save the returned entity - * @dev this function creates a new reverse registrar entity if it does not exist - * @param name ens name, ex: 'schiller.eth' - * @param domainId air domain id - * @param block air block entity - * @returns ReverseRegistrar entity - */ - function createReverseRegistrar( - name: string, - domainId: string, - block: AirBlock, - ): ReverseRegistrar { - let id = crypto.keccak256(Bytes.fromUTF8(name)).toHexString(); - let entity = ReverseRegistrar.load(id); - if (entity == null) { - entity = new ReverseRegistrar(id); - entity.name = name; - entity.domain = domainId; - entity.createdAt = block.id; - } - return entity as ReverseRegistrar; - } - - /** - * @dev this function gets a reverse registrar entity - * @param name ens name, ex: 'schiller.eth' - * @returns ReverseRegistrar entity - */ - function getReverseRegistrar( - name: string, - ): ReverseRegistrar | null { - let id = crypto.keccak256(Bytes.fromUTF8(name)).toHexString(); - return ReverseRegistrar.load(id); - } - - /** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new air domain entity - * @param domain Domain class object - * @returns AirDomain entity - */ - export function getOrCreateAirDomain( - domain: Domain, - ): AirDomain { - let entity = AirDomain.load(domain.id); - if (entity == null) { - entity = new AirDomain(domain.id); - entity.subdomainCount = BIG_INT_ZERO; - let ownerAccount = getOrCreateAirAccount(domain.chainId, ZERO_ADDRESS, domain.block); - ownerAccount.save(); - entity.owner = ownerAccount.id; - let airToken = getOrCreateAirToken(domain.chainId, domain.tokenAddress); - airToken.save(); - entity.tokenAddress = airToken.id; - entity.isPrimary = false; - entity.expiryTimestamp = BIG_INT_ZERO; - entity.registrationCost = BIG_INT_ZERO; - entity.createdAt = domain.block.id; - entity.lastUpdatedBlock = domain.block.id; - } - return entity as AirDomain; - } - - /** - * @dev this function gets a air domain entity - * @param domainId air domain entity id - * @returns AirDomain entity - null if entity does not exist - */ - export function getAirDomain( - domainId: string, - ): AirDomain | null { - return AirDomain.load(domainId); - } - - /** - * @dev this function does not save the returned entity - * @dev this function gets or creates a new air domain owner changed transaction entity - * @param block air block entity - * @param logOrCallIndex log or call index - * @param chainId chain id - * @param previousOwnerId previous owner id - * @param newOwner new owner address - * @param transactionHash transaction hash - * @param tokenId token id - * @param domain air domain - * @returns AirDomainOwnerChangedTransaction entity - */ - function getOrCreateAirDomainOwnerChangedTransaction( - block: AirBlock, - logOrCallIndex: BigInt, - chainId: string, - previousOwnerId: string, - newOwner: string, - transactionHash: string, - tokenId: string, - domain: AirDomain, - ): AirDomainOwnerChangedTransaction { - let id = createEntityId(transactionHash, block.number, logOrCallIndex); - let entity = AirDomainOwnerChangedTransaction.load(id); - if (entity == null) { - entity = new AirDomainOwnerChangedTransaction(id); - entity.previousOwner = previousOwnerId; - let newOwnerAccount = getOrCreateAirAccount(chainId, newOwner, block); - newOwnerAccount.save(); - entity.newOwner = newOwnerAccount.id; - entity.transactionHash = transactionHash; - entity.tokenId = tokenId; - entity.domain = domain.id; - entity.block = block.id; - entity.index = updateAirEntityCounter(AIR_DOMAIN_OWNER_CHANGED_ENTITY_COUNTER_ID, block); - } - return entity as AirDomainOwnerChangedTransaction; - } - - /** - * @dev this function does a recursive subdomain count decrement - * @param domain air domain entity - * @param chainId chain id - * @param block air block entity - * @param tokenAddress contract address of nft token - */ - function recurseSubdomainCountDecrement(domain: AirDomain, chainId: string, block: AirBlock, tokenAddress: string): void { - if ((domain.resolver == null || domain.resolver!.split("-")[0] == ZERO_ADDRESS) && - domain.owner == getOrCreateAirAccount(chainId, ZERO_ADDRESS, block).id && domain.subdomainCount == BIG_INT_ZERO) { - if (domain.parent) { - const parentDomain = getOrCreateAirDomain(new Domain(domain.parent!, chainId, block, tokenAddress)); - if (parentDomain) { - parentDomain.subdomainCount = parentDomain.subdomainCount.minus(BIGINT_ONE) - saveDomainEntity(parentDomain, block); - recurseSubdomainCountDecrement(parentDomain, chainId, block, tokenAddress) - } - } - } - } - - /** - * @dev this class has all fields required to create a domain entity - * @param id domain id - * @param chainId chain id - * @param block air block entity - * @param tokenAddress token address of domain nft token contract - */ - export class Domain { - constructor( - public id: string, - public chainId: string, - public block: AirBlock, - public tokenAddress: string, - ) { } - } -} \ No newline at end of file diff --git a/ens/modules/airstack/domain-name/index.ts b/ens/modules/airstack/domain-name/index.ts deleted file mode 100644 index 6b6e799a..00000000 --- a/ens/modules/airstack/domain-name/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./domain-name"; \ No newline at end of file diff --git a/ens/modules/airstack/domain-name/utils.ts b/ens/modules/airstack/domain-name/utils.ts deleted file mode 100644 index 08714fa5..00000000 --- a/ens/modules/airstack/domain-name/utils.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { log } from "@graphprotocol/graph-ts"; -import { - AirExtra, - AirDomain, - AirBlock, -} from "../../../generated/schema"; -import { - updateAirEntityCounter -} from "../common/index"; - -export const AIR_DOMAIN_OWNER_CHANGED_ENTITY_COUNTER_ID = "AIR_DOMAIN_OWNER_CHANGED_ENTITY_COUNTER"; -export const AIR_DOMAIN_TRANSFER_ENTITY_COUNTER_ID = "AIR_DOMAIN_TRANSFER_ENTITY_COUNTER"; -export const AIR_DOMAIN_NEW_RESOLVER_ENTITY_COUNTER_ID = "AIR_DOMAIN_NEW_RESOLVER_ENTITY_COUNTER"; -export const AIR_DOMAIN_NEW_TTL_ENTITY_COUNTER_ID = "AIR_DOMAIN_NEW_TTL_ENTITY_COUNTER"; -export const AIR_NAME_REGISTERED_ENTITY_COUNTER_ID = "AIR_NAME_REGISTERED_ENTITY_COUNTER"; -export const AIR_NAME_RENEWED_ENTITY_COUNTER_ID = "AIR_NAME_RENEWED_ENTITY_COUNTER"; -export const AIR_ADDR_CHANGED_ENTITY_COUNTER_ID = "AIR_ADDR_CHANGED_ENTITY_COUNTER"; -export const AIR_SET_PRIMARY_DOMAIN_ENTITY_COUNTER_ID = "AIR_SET_PRIMARY_DOMAIN_ENTITY_COUNTER"; - -export const AIR_DOMAIN_LAST_UPDATED_INDEX_ENTITY_COUNTER_ID = "AIR_DOMAIN_LAST_UPDATED_INDEX_ENTITY_COUNTER"; - -export const AIR_META_ID = "AIR_META"; -export const ETHEREUM_MAINNET_ID = "1"; -export const AIR_EXTRA_TTL = 'ttl'; - -export const ROOT_NODE = '0x0000000000000000000000000000000000000000000000000000000000000000' -export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; - -/** - * @dev this function does not save the returned entity - * @dev this function creates an air extra entity - * @param name air extra name - * @param value air extra value - * @param extraId air extra entity id - * @returns air extra entity - */ -export function createAirExtra( - name: string, - value: string, - id: string, -): AirExtra { - let entity = AirExtra.load(id); - if (entity == null) { - entity = new AirExtra(id); - entity.name = name; - entity.value = value; - } - return entity as AirExtra; -} - -//specific to ens -/** - * @dev this function is used to check if the label is valid to prevent homoglyph attacks (which ens is prone to) - * @param name ens label name - * @param txHash transaction hash - * @returns boolean - true if label is valid - */ -export function checkValidLabel(name: string, txHash: string): boolean { - for (let i = 0; i < name.length; i++) { - let c = name.charCodeAt(i); - if (c === 0) { - log.warning("Invalid label '{}' contained null byte. Skipping. txhash {}", [name, txHash]); - return false; - } else if (c === 46) { - log.warning("Invalid label '{}' contained separator char '.'. Skipping. txhash {}", [name, txHash]); - return false; - } - } - return true; -} - -/** - * @dev this function is used to save air domain entity and update the last updated index and block - * @param domain air domain entity to be saved - * @param airBlock air block entity - */ -export function saveDomainEntity(domain: AirDomain, airBlock: AirBlock): void { - domain.lastUpdatedIndex = updateAirEntityCounter(AIR_DOMAIN_LAST_UPDATED_INDEX_ENTITY_COUNTER_ID, airBlock); - domain.lastUpdatedBlock = airBlock.id; - domain.save(); -} \ No newline at end of file diff --git a/ens/src/ens-registry.ts b/ens/src/ens-registry.ts index 9a4429d3..9db80f17 100644 --- a/ens/src/ens-registry.ts +++ b/ens/src/ens-registry.ts @@ -12,7 +12,7 @@ import { ens, } from "@graphprotocol/graph-ts"; import { ETHEREUM_MAINNET_ID, ROOT_NODE } from "../modules/airstack/domain-name/utils"; -import { getChainId } from "../modules/airstack/common"; +import { getChainId, getOrCreateAirBlock } from "../modules/airstack/common"; import { TOKEN_ADDRESS_ENS, getOrCreateIsMigratedMapping, @@ -21,6 +21,7 @@ import { getNameByLabelHash, } from "./utils"; import { AirDomain } from "../generated/schema"; + /** * @dev this functions maps the NewOwner event to airstack trackDomainOwnerChangedTransaction * @param event NewOwnerEvent from ENS Registry diff --git a/ens/src/eth-registrar.ts b/ens/src/eth-registrar.ts index e6cc21bb..5fa82bd4 100644 --- a/ens/src/eth-registrar.ts +++ b/ens/src/eth-registrar.ts @@ -26,7 +26,7 @@ import { NameRenewed as ControllerNameRenewedEvent } from '../generated/EthRegistrarController/EthRegistrarController'; import { uint256ToByteArray, byteArrayFromHex, createNameRegisteredTransactionVsRegistrant } from './utils'; -import { AirDomain, AirNameRegisteredTransaction } from '../generated/schema'; +import { AirNameRegisteredTransaction } from '../generated/schema'; import { getChainId, getOrCreateAirAccount, getOrCreateAirBlock } from '../modules/airstack/common'; const rootNode: ByteArray = byteArrayFromHex("93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"); @@ -39,18 +39,20 @@ export function handleNameRegistered(event: NameRegisteredEvent): void { let label = uint256ToByteArray(event.params.id); log.info("handleNameRegistered: registrant {} label {} expiry {} txhash {} logIndex {}", [event.params.owner.toHexString(), label.toHexString(), event.params.expires.toString(), event.transaction.hash.toHexString(), event.logIndex.toString()]); let labelName = ens.nameByHash(label.toHexString()); + let domainId = crypto.keccak256(rootNode.concat(label)).toHex(); if (labelName == null) { // try to get the name from the labelhash to name mapping labelName = getNameByLabelHash(label.toHexString()); if (labelName == null) { log.info("handleNameRegistered: labelName is null from getNameByLabelHash label {} txhash {}, converting to brackets", [label.toHexString(), event.transaction.hash.toHexString()]); labelName = '[' + label.toHexString().slice(2) + ']'; + } else { + log.info("handleNameRegistered: labelName {} exists from getNameByLabelHash label {} txhash {}", [labelName!, label.toHexString(), event.transaction.hash.toHexString()]); } } else if (labelName && !checkValidLabel(labelName, event.transaction.hash.toHexString())) { log.info("handleNameRegistered: labelName is invalid for label {} txhash {}", [label.toHexString(), event.transaction.hash.toHexString()]); labelName = '[' + label.toHexString().slice(2) + ']'; } - let domainId = crypto.keccak256(rootNode.concat(label)).toHex(); airstack.domain.trackNameRegisteredTransaction( event.block, event.transaction.hash.toHexString(), @@ -165,6 +167,8 @@ export function handleNameRenewedByController(event: ControllerNameRenewedEvent) log.info("handleNameRenewedByController: name {} label {} cost {} txhash {}", [event.params.name, event.params.label.toHexString(), event.params.cost.toString(), event.transaction.hash.toHexString()]); let domainId = crypto.keccak256(rootNode.concat(event.params.label)).toHex(); let labelName = event.params.name; + // create Labelhash to Name mapping + createLabelhashToNameMapping(event.params.label.toHexString(), labelName, event.block.number.toString()); airstack.domain.trackNameRenewedOrRegistrationByController( event.block, event.transaction.hash.toHexString(), diff --git a/ens/src/resolver.ts b/ens/src/resolver.ts index 3adc136e..7456e4f5 100644 --- a/ens/src/resolver.ts +++ b/ens/src/resolver.ts @@ -2,7 +2,7 @@ import * as airstack from "../modules/airstack/domain-name"; import { AddrChanged as AddrChangedEvent, VersionChanged as VersionChangedEvent, -} from "../generated/Resolver1/Resolver"; +} from "../generated/Resolver/Resolver"; import { log } from "@graphprotocol/graph-ts"; import { TOKEN_ADDRESS_ENS } from "./utils"; /** diff --git a/ens/src/utils.ts b/ens/src/utils.ts index f62af0ef..5a05ba1b 100644 --- a/ens/src/utils.ts +++ b/ens/src/utils.ts @@ -3,6 +3,7 @@ import { ReverseRegistrar, NameRegisteredTransactionVsRegistrant, AirBlock, + AirDomain, LabelhashToNameMapping, } from "../generated/schema"; import { @@ -13,8 +14,9 @@ import { BigInt, log, } from "@graphprotocol/graph-ts"; +import * as airstack from "../modules/airstack/domain-name"; import { - checkValidLabel + checkValidLabel, saveDomainEntity } from "../modules/airstack/domain-name/utils"; // ens constants export const TOKEN_ADDRESS_ENS = "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85"; diff --git a/ens/subgraph.yaml b/ens/subgraph.yaml index c456dd3f..45946aa4 100644 --- a/ens/subgraph.yaml +++ b/ens/subgraph.yaml @@ -194,6 +194,25 @@ dataSources: callHandlers: - function: setName(string) handler: handleSetName + - kind: ethereum/contract + name: ReverseRegistrarOld + network: mainnet + source: + abi: ReverseRegistrar + address: '0x9062C0A6Dbd6108336BcBe4593a3D1cE05512069' + startBlock: 3787060 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: ./src/reverse-registrar.ts + entities: + abis: + - name: ReverseRegistrar + file: ./abis/ReverseRegistrar.json + callHandlers: + - function: setName(string) + handler: handleSetName - kind: ethereum/contract name: NameWrapper network: mainnet diff --git a/ens/tests/eth-registrar.test.ts b/ens/tests/eth-registrar.test.ts index 733279ac..f7c42e9b 100644 --- a/ens/tests/eth-registrar.test.ts +++ b/ens/tests/eth-registrar.test.ts @@ -12,6 +12,7 @@ import { ETHEREUM_MAINNET_ID, ZERO_ADDRESS } from "../modules/airstack/domain-na import { uint256ToByteArray, byteArrayFromHex } from "../src/utils" import { BIGINT_ONE } from "../modules/airstack/common" import { AirDomain } from "../generated/schema" +import { createAirDomain } from "./resolver-utils" const rootNode: ByteArray = byteArrayFromHex("93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"); @@ -123,12 +124,15 @@ describe("Unit tests for eth registrar handlers", () => { test("test handleNameRegisteredByControllerOld", () => { let event = getHandleNameRegisteredByControllerOldEvent(); - handleNameRegisteredByControllerOld(event) // given let domainId = crypto.keccak256(rootNode.concat(event.params.label)).toHex(); let blockId = ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString()); let reverseRegistrarId = crypto.keccak256(Bytes.fromUTF8(event.params.name.concat(".eth"))).toHexString(); - // assert here + let subdomain = createAirDomain("0xea6cc843bbe16a18e678f7050e9183f09ccf900a3b4b74de12dae9ce1f95dff46") + subdomain.name = "def.[hex].[hex].eth" + subdomain.parent = domainId; + subdomain.save(); + handleNameRegisteredByControllerOld(event) // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -151,11 +155,16 @@ describe("Unit tests for eth registrar handlers", () => { test("test handleNameRegisteredByController", () => { let event = getHandleNameRegisteredByControllerEvent(); - handleNameRegisteredByController(event) + // create a subdomain and assign below domainId as its parent // assert here let domainId = crypto.keccak256(rootNode.concat(event.params.label)).toHex(); let blockId = ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString()); let reverseRegistrarId = crypto.keccak256(Bytes.fromUTF8(event.params.name.concat(".eth"))).toHexString(); + let subdomain = createAirDomain("0xea6cc843bbe16a18e678f7050e9183f09ccf900a3b4b74de12dae9ce1f95dff46") + subdomain.name = "def.[hex].[hex].eth" + subdomain.parent = domainId; + subdomain.save(); + handleNameRegisteredByController(event) // assert here // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); @@ -179,12 +188,15 @@ describe("Unit tests for eth registrar handlers", () => { test("test handleNameRenewedByController", () => { let event = getHandleNameRenewedByControllerEvent(); - handleNameRenewedByController(event) // assert here let domainId = crypto.keccak256(rootNode.concat(event.params.label)).toHex(); let blockId = ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString()); let reverseRegistrarId = crypto.keccak256(Bytes.fromUTF8(event.params.name.concat(".eth"))).toHexString(); - // assert here + let subdomain = createAirDomain("0xea6cc843bbe16a18e678f7050e9183f09ccf900a3b4b74de12dae9ce1f95dff46") + subdomain.name = "def.[hex].[hex].eth" + subdomain.parent = domainId; + subdomain.save(); + handleNameRenewedByController(event) // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -206,7 +218,7 @@ describe("Unit tests for eth registrar handlers", () => { assert.fieldEquals("AirToken", airTokenId, "id", ETHEREUM_MAINNET_ID.concat("-").concat(ZERO_ADDRESS)); assert.fieldEquals("AirToken", airTokenId, "address", ZERO_ADDRESS); // AirDomain - assert.fieldEquals("AirDomain", domainId, "registrationCost", "0"); + assert.fieldEquals("AirDomain", domainId, "registrationCost", "1000000000000000000"); assert.fieldEquals("AirDomain", domainId, "expiryTimestamp", event.params.expires.toString()); assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "labelName", event.params.name); diff --git a/ens/tests/resolver-utils.ts b/ens/tests/resolver-utils.ts index a5a28ce8..334f9a75 100644 --- a/ens/tests/resolver-utils.ts +++ b/ens/tests/resolver-utils.ts @@ -1,6 +1,6 @@ import { newMockEvent } from "matchstick-as" import { ethereum, Address, Bytes, BigInt } from "@graphprotocol/graph-ts" -import { AddrChanged, VersionChanged } from "../generated/Resolver1/Resolver" +import { AddrChanged, VersionChanged } from "../generated/Resolver/Resolver" import { getTransactionHash } from "./common-utils" import { AirDomain } from "../generated/schema" import { BIGINT_ONE, BIG_INT_ZERO } from "../modules/airstack/common/index" diff --git a/ens/tests/utils.test.ts b/ens/tests/utils.test.ts index c94f6273..0d250377 100644 --- a/ens/tests/utils.test.ts +++ b/ens/tests/utils.test.ts @@ -1,6 +1,9 @@ import { assert, describe, test } from "matchstick-as"; -import { Bytes } from "@graphprotocol/graph-ts"; +import { Bytes, BigInt } from "@graphprotocol/graph-ts"; import { decodeName } from "../src/utils"; +import { createAirDomain } from "./resolver-utils" +import * as airstackCommon from "../modules/airstack/common" +import * as airstack from "../modules/airstack/domain-name" describe("Unit tests for util functions", () => { test("test decodeName for firstwrappedname.eth", () => {