diff --git a/airstack-modules/graphql/airstack-domain-name-schema.graphql.ts b/airstack-modules/graphql/airstack-domain-name-schema.graphql.ts index 61e665d4..d7f84ea3 100644 --- a/airstack-modules/graphql/airstack-domain-name-schema.graphql.ts +++ b/airstack-modules/graphql/airstack-domain-name-schema.graphql.ts @@ -60,6 +60,7 @@ type AirDomain @entity { createdAt: AirBlock! lastUpdatedBlock: AirBlock #- NA extras: [AirExtra!] + lastUpdatedIndex: BigInt! } type AirDomainTransferTransaction implements AirDomainEvent @entity { @@ -167,6 +168,7 @@ type AirNameRenewedTransaction implements AirDomainEvent & AirDomainRegistration type AirPrimaryDomainTransaction implements AirDomainEvent @entity { id: ID! + previousDomain: AirDomain block: AirBlock! transactionHash: String! tokenId: String # dec(labelhash) # - NA diff --git a/airstack-modules/modules/airstack/domain-name/domain-name.ts b/airstack-modules/modules/airstack/domain-name/domain-name.ts index 6d65e23f..32d62b6b 100644 --- a/airstack-modules/modules/airstack/domain-name/domain-name.ts +++ b/airstack-modules/modules/airstack/domain-name/domain-name.ts @@ -3,8 +3,6 @@ import { Bytes, crypto, log, - ByteArray, - ens, ethereum, } from "@graphprotocol/graph-ts"; @@ -25,7 +23,7 @@ import { 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 } from "./utils"; +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 { @@ -69,7 +67,6 @@ export namespace domain { )); if (domain.parent == null) { parentDomain.subdomainCount = parentDomain.subdomainCount.plus(BIGINT_ONE); - parentDomain.lastUpdatedBlock = airBlock.id; } domain.name = name; domain.labelName = labelName; @@ -79,9 +76,8 @@ export namespace domain { domain.parent = parentDomain.id; domain.labelHash = labelHash; domain.tokenId = tokenId; - domain.lastUpdatedBlock = airBlock.id; - parentDomain.save(); - domain.save(); + saveDomainEntity(parentDomain, airBlock); + saveDomainEntity(domain, airBlock); recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); let txn = getOrCreateAirDomainOwnerChangedTransaction( @@ -128,8 +124,9 @@ export namespace domain { let newOwnerAccount = getOrCreateAirAccount(chainId, newOwnerAddress, airBlock); newOwnerAccount.save(); domain.owner = newOwnerAccount.id; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + // 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) { @@ -180,8 +177,7 @@ export namespace domain { domain.resolvedAddress = resolverEntity.resolvedAddress; } // do recursive subdomain count decrement - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); // create new resolver transaction @@ -244,8 +240,7 @@ export namespace domain { } } domain.extras = extrasArray; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); // create AirDomainNewTTLTransaction let txn = getOrCreateAirDomainNewTTLTransaction( transactionHash, @@ -298,14 +293,13 @@ export namespace domain { domain.labelName = labelName } domain.expiryTimestamp = expiryTimestamp; - domain.lastUpdatedBlock = airBlock.id; if (cost) { domain.registrationCost = cost; } let airToken = getOrCreateAirToken(chainId, paymentToken); airToken.save(); domain.paymentToken = airToken.id; - domain.save(); + saveDomainEntity(domain, airBlock); // create name registered transaction let txn = getOrCreateAirNameRegisteredTransaction( chainId, @@ -351,8 +345,7 @@ export namespace domain { tokenAddress, )); domain.expiryTimestamp = expiryTimestamp; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); // create name renewed transaction let txn = getOrCreateAirNameRenewedTransaction( transactionHash, @@ -372,8 +365,7 @@ export namespace domain { * @param block ethereum block * @param transactionHash transaction hash * @param domainId air domain id - * @param name domain name - * @param labelHash label hash + * @param labelName domain labelName * @param cost cost of registration or renewal * @param paymentToken payment token address * @param renewerOrRegistrant renewer or registrant address @@ -385,8 +377,7 @@ export namespace domain { block: ethereum.Block, transactionHash: string, domainId: string, - name: string, - labelHash: Bytes, + labelName: string, cost: BigInt, paymentToken: string, renewerOrRegistrant: string, @@ -395,19 +386,6 @@ export namespace domain { tokenAddress: string, ): void { let chainId = getChainId(); - const calculatedLabelHash = crypto.keccak256(ByteArray.fromUTF8(name)); - if (!calculatedLabelHash.equals(labelHash)) { - log.warning( - "Expected '{}' to hash to {}, but got {} instead. Skipping.", - [name, calculatedLabelHash.toHex(), labelHash.toHex()] - ); - return; - } - - if (name.indexOf(".") !== -1) { - log.warning("Invalid label '{}'. Skipping.", [name]); - return; - } let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); airBlock.save(); let domain = getOrCreateAirDomain(new Domain( @@ -416,7 +394,6 @@ export namespace domain { airBlock, tokenAddress, )); - // tracking registration cost in domain entity - renewal cost is not being tracked yet if (fromRegistrationEvent) { domain.registrationCost = cost; @@ -450,17 +427,20 @@ export namespace domain { ); txn.save(); } - if (domain.labelName !== name) { - domain.labelName = name - domain.name = name + '.eth' + 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(); } } - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); //new name registered event } @@ -496,8 +476,7 @@ export namespace domain { if (domain.resolver == resolver.id) { domain.resolvedAddress = addrAccount.id; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); let txn = getOrCreateAirResolvedAddressChanged( chainId, logOrCallIndex, @@ -533,8 +512,7 @@ export namespace domain { resolver.save(); if (domain && domain.resolver == resolver.id) { domain.resolvedAddress = null - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); } } @@ -565,6 +543,7 @@ export namespace domain { 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 @@ -573,24 +552,23 @@ export namespace 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 - let oldPrimaryDomain = getOrCreateAirDomain(new Domain(primaryDomainEntity.domain, chainId, airBlock, tokenAddress)); + oldPrimaryDomain = getOrCreateAirDomain(new Domain(primaryDomainEntity.domain, chainId, airBlock, tokenAddress)); oldPrimaryDomain.isPrimary = false; - oldPrimaryDomain.lastUpdatedBlock = airBlock.id; - oldPrimaryDomain.save(); + saveDomainEntity(oldPrimaryDomain, airBlock); // set new primary domain for resolved address primaryDomainEntity.domain = domain.id; primaryDomainEntity.lastUpdatedAt = airBlock.id; - primaryDomainEntity.save(); } + primaryDomainEntity.save(); // set isPrimary on new domain domain.isPrimary = true; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); } // record a set primary domain transaction with new domain let txn = getOrCreateAirPrimaryDomainTransaction( airBlock, transactionHash, + oldPrimaryDomain, domain, from, chainId, @@ -629,6 +607,7 @@ export namespace domain { * @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 @@ -637,6 +616,7 @@ export namespace domain { function getOrCreateAirPrimaryDomainTransaction( block: AirBlock, transactionHash: string, + previousDomain: AirDomain | null, domain: AirDomain, resolvedAddress: string, chainId: string, @@ -648,6 +628,9 @@ export namespace domain { 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 @@ -960,7 +943,7 @@ export namespace domain { * @param domain Domain class object * @returns AirDomain entity */ - function getOrCreateAirDomain( + export function getOrCreateAirDomain( domain: Domain, ): AirDomain { let entity = AirDomain.load(domain.id); @@ -1047,8 +1030,7 @@ export namespace domain { const parentDomain = getOrCreateAirDomain(new Domain(domain.parent!, chainId, block, tokenAddress)); if (parentDomain) { parentDomain.subdomainCount = parentDomain.subdomainCount.minus(BIGINT_ONE) - parentDomain.lastUpdatedBlock = block.id; - parentDomain.save(); + saveDomainEntity(parentDomain, block); recurseSubdomainCountDecrement(parentDomain, chainId, block, tokenAddress) } } diff --git a/airstack-modules/modules/airstack/domain-name/utils.ts b/airstack-modules/modules/airstack/domain-name/utils.ts index 4a9fc0ab..08714fa5 100644 --- a/airstack-modules/modules/airstack/domain-name/utils.ts +++ b/airstack-modules/modules/airstack/domain-name/utils.ts @@ -1,6 +1,12 @@ +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"; @@ -11,6 +17,8 @@ export const AIR_NAME_RENEWED_ENTITY_COUNTER_ID = "AIR_NAME_RENEWED_ENTITY_COUNT 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'; @@ -38,4 +46,36 @@ export function createAirExtra( 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/airstack-modules/modules/airstack/nft-marketplace/Readme.md b/airstack-modules/modules/airstack/nft-marketplace/Readme.md index 42822133..1a20f034 100644 --- a/airstack-modules/modules/airstack/nft-marketplace/Readme.md +++ b/airstack-modules/modules/airstack/nft-marketplace/Readme.md @@ -5,11 +5,11 @@ ``` 1. Track NFT trade transaction trackNFTSaleTransactions( - block: string, #block ethereum block + block: ethereum.Block, #block ethereum block transactionHash: string, #transaction hash logOrCallIndex: BigInt, #transaction index - call or log index sale: Sale, #sale object - protocolType: string #protocol type (eg: NFT_MARKET_PLACE) + protocolType: string, #protocol type (eg: NFT_MARKET_PLACE) protocolActionType: string, #protocol action type - ["BUY", "SELL"] ) ``` diff --git a/ens/abis/EthRegistrarControllerNew.json b/ens/abis/EthRegistrarControllerNew.json new file mode 100644 index 00000000..cf5c7c85 --- /dev/null +++ b/ens/abis/EthRegistrarControllerNew.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract BaseRegistrarImplementation","name":"_base","type":"address"},{"internalType":"contract IPriceOracle","name":"_prices","type":"address"},{"internalType":"uint256","name":"_minCommitmentAge","type":"uint256"},{"internalType":"uint256","name":"_maxCommitmentAge","type":"uint256"},{"internalType":"contract ReverseRegistrar","name":"_reverseRegistrar","type":"address"},{"internalType":"contract INameWrapper","name":"_nameWrapper","type":"address"},{"internalType":"contract ENS","name":"_ens","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"CommitmentTooNew","type":"error"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"CommitmentTooOld","type":"error"},{"inputs":[{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"DurationTooShort","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[],"name":"MaxCommitmentAgeTooHigh","type":"error"},{"inputs":[],"name":"MaxCommitmentAgeTooLow","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"NameNotAvailable","type":"error"},{"inputs":[],"name":"ResolverRequiredWhenDataSupplied","type":"error"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"UnexpiredCommitmentExists","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":true,"internalType":"bytes32","name":"label","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseCost","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expires","type":"uint256"}],"name":"NameRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":true,"internalType":"bytes32","name":"label","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"cost","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expires","type":"uint256"}],"name":"NameRenewed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"MIN_REGISTRATION_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"available","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"commit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"commitments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"}],"name":"makeCommitment","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"maxCommitmentAge","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minCommitmentAge","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nameWrapper","outputs":[{"internalType":"contract INameWrapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prices","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"renew","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"rentPrice","outputs":[{"components":[{"internalType":"uint256","name":"base","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"}],"internalType":"struct IPriceOracle.Price","name":"price","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reverseRegistrar","outputs":[{"internalType":"contract ReverseRegistrar","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"valid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/ens/abis/NameWrapper.json b/ens/abis/NameWrapper.json new file mode 100644 index 00000000..da09743c --- /dev/null +++ b/ens/abis/NameWrapper.json @@ -0,0 +1,1372 @@ +[ + { + "inputs": [ + { + "internalType": "contract ENS", + "name": "_ens", + "type": "address" + }, + { + "internalType": "contract IBaseRegistrar", + "name": "_registrar", + "type": "address" + }, + { + "internalType": "contract IMetadataService", + "name": "_metadataService", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CannotUpgrade", + "type": "error" + }, + { + "inputs": [], + "name": "IncompatibleParent", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "IncorrectTargetOwner", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectTokenType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "labelHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedLabelhash", + "type": "bytes32" + } + ], + "name": "LabelMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "label", + "type": "string" + } + ], + "name": "LabelTooLong", + "type": "error" + }, + { + "inputs": [], + "name": "LabelTooShort", + "type": "error" + }, + { + "inputs": [], + "name": "NameIsNotWrapped", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "OperationProhibited", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "Unauthorised", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "controller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "ControllerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "name": "ExpiryExtended", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "fuses", + "type": "uint32" + } + ], + "name": "FusesSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "NameUnwrapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "name", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "fuses", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "name": "NameWrapped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_tokens", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "fuseMask", + "type": "uint32" + } + ], + "name": "allFusesBurned", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "canModifyName", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "controllers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ens", + "outputs": [ + { + "internalType": "contract ENS", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parentNode", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "labelhash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "name": "extendExpiry", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getData", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint32", + "name": "fuses", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "isWrapped", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "metadataService", + "outputs": [ + { + "internalType": "contract IMetadataService", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "names", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "recoverFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "address", + "name": "wrappedOwner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + }, + { + "internalType": "uint16", + "name": "ownerControlledFuses", + "type": "uint16" + } + ], + "name": "registerAndWrapETH2LD", + "outputs": [ + { + "internalType": "uint256", + "name": "registrarExpiry", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "registrar", + "outputs": [ + { + "internalType": "contract IBaseRegistrar", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "renew", + "outputs": [ + { + "internalType": "uint256", + "name": "expires", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parentNode", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "labelhash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "fuses", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "name": "setChildFuses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "controller", + "type": "address" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "setController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "ownerControlledFuses", + "type": "uint16" + } + ], + "name": "setFuses", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IMetadataService", + "name": "_metadataService", + "type": "address" + } + ], + "name": "setMetadataService", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + }, + { + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "setRecord", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "setResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parentNode", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint32", + "name": "fuses", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "name": "setSubnodeOwner", + "outputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parentNode", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + }, + { + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "fuses", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "expiry", + "type": "uint64" + } + ], + "name": "setSubnodeRecord", + "outputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "ttl", + "type": "uint64" + } + ], + "name": "setTTL", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract INameWrapperUpgrade", + "name": "_upgradeAddress", + "type": "address" + } + ], + "name": "setUpgradeContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parentNode", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "labelhash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "unwrap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "labelhash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "registrant", + "type": "address" + }, + { + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "unwrapETH2LD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parentNode", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "address", + "name": "wrappedOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "upgrade", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "upgradeContract", + "outputs": [ + { + "internalType": "contract INameWrapperUpgrade", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "address", + "name": "wrappedOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "upgradeETH2LD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "name", + "type": "bytes" + }, + { + "internalType": "address", + "name": "wrappedOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "wrap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "address", + "name": "wrappedOwner", + "type": "address" + }, + { + "internalType": "uint16", + "name": "ownerControlledFuses", + "type": "uint16" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ], + "name": "wrapETH2LD", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/ens/modules/airstack/common/index.ts b/ens/modules/airstack/common/index.ts index e00f2888..e6142f21 100644 --- a/ens/modules/airstack/common/index.ts +++ b/ens/modules/airstack/common/index.ts @@ -8,7 +8,6 @@ import { AirEntityCounter, AirMeta, AirAccount, - AirExtra, AirToken, } from "../../../generated/schema"; @@ -22,30 +21,7 @@ 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_NETWORK_MAP = new TypedMap(); -AIR_NETWORK_MAP.set("arbitrum-one", "ARBITRUM_ONE"); -AIR_NETWORK_MAP.set("arweave-mainnet", "ARWEAVE_MAINNET"); -AIR_NETWORK_MAP.set("aurora", "AURORA"); -AIR_NETWORK_MAP.set("avalanche", "AVALANCHE"); -AIR_NETWORK_MAP.set("boba", "BOBA"); -AIR_NETWORK_MAP.set("bsc", "BSC"); -AIR_NETWORK_MAP.set("celo", "CELO"); -AIR_NETWORK_MAP.set("COSMOS", "COSMOS"); -AIR_NETWORK_MAP.set("CRONOS", "CRONOS"); -AIR_NETWORK_MAP.set("mainnet", "MAINNET"); -AIR_NETWORK_MAP.set("fantom", "FANTOM"); -AIR_NETWORK_MAP.set("fuse", "FUSE"); -AIR_NETWORK_MAP.set("harmony", "HARMONY"); -AIR_NETWORK_MAP.set("juno", "JUNO"); -AIR_NETWORK_MAP.set("moonbeam", "MOONBEAM"); -AIR_NETWORK_MAP.set("moonriver", "MOONRIVER"); -AIR_NETWORK_MAP.set("near-mainnet", "NEAR_MAINNET"); -AIR_NETWORK_MAP.set("optimism", "OPTIMISM"); -AIR_NETWORK_MAP.set("osmosis", "OSMOSIS"); -AIR_NETWORK_MAP.set("matic", "MATIC"); -AIR_NETWORK_MAP.set("xdai", "XDAI"); +export const SUBGRAPH_SLUG = "ens_v1"; const AIR_CHAIN_ID_MAP = new TypedMap(); AIR_CHAIN_ID_MAP.set("arbitrum-one", "42161"); @@ -58,6 +34,7 @@ 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"); @@ -68,22 +45,18 @@ 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("xdai", "100"); - -export function getNetwork(network: string): string { - const value = AIR_NETWORK_MAP.get(network); - const result: string = value !== null ? value : "unknown"; - return result; -} +AIR_CHAIN_ID_MAP.set("gnosis", "100") export function getChainId(): string { - let network = dataSource.network(); + const network = dataSource.network(); const value = AIR_CHAIN_ID_MAP.get(network); - const result: string = value !== null ? value : "unknown"; - return result; + if (value != null) { + return value!; + } + throw new Error("Network not supported"); } -//air entity funcitons +// common air entity functions /** * @dev this function updates air entity counter for a given entity id @@ -123,7 +96,12 @@ export function createAirMeta( let meta = AirMeta.load(AIR_META_ID); if (meta == null) { meta = new AirMeta(AIR_META_ID); - meta.network = getNetwork(dataSource.network()); + 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; @@ -147,8 +125,7 @@ export function getOrCreateAirBlock( blockHash: string, blockTimestamp: BigInt ): AirBlock { - let id = chainId.concat("-").concat(blockHeight.toString()); - + const id = chainId.concat("-").concat(blockHeight.toString()); let block = AirBlock.load(id); if (block == null) { block = new AirBlock(id); @@ -168,7 +145,7 @@ export function getOrCreateAirBlock( * @returns AirAccount entity */ export function getOrCreateAirAccount(chainId: string, address: string, block: AirBlock): AirAccount { - let id = chainId.concat("-").concat(address); + const id = chainId.concat("-").concat(address); let entity = AirAccount.load(id); if (entity == null) { entity = new AirAccount(id); @@ -178,28 +155,6 @@ export function getOrCreateAirAccount(chainId: string, address: string, block: A return entity as AirAccount; } -/** - * @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; -} - /** * @dev this function does not save the returned entity * @dev this function gets or creates a new air token entity diff --git a/ens/modules/airstack/domain-name/domain-name.ts b/ens/modules/airstack/domain-name/domain-name.ts index 4567b951..32d62b6b 100644 --- a/ens/modules/airstack/domain-name/domain-name.ts +++ b/ens/modules/airstack/domain-name/domain-name.ts @@ -3,14 +3,11 @@ import { Bytes, crypto, log, - ByteArray, - ens, ethereum, } from "@graphprotocol/graph-ts"; import { AirAccount, - AirToken, AirBlock, AirDomain, AirDomainOwnerChangedTransaction, @@ -26,8 +23,8 @@ import { PrimaryDomain, AirExtra, } from "../../../generated/schema"; -import { 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 } from "./utils"; -import { BIGINT_ONE, BIG_INT_ZERO, EMPTY_STRING, getChainId, updateAirEntityCounter, getOrCreateAirBlock, getOrCreateAirAccount, createAirExtra, getOrCreateAirToken } from "../common"; +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 { /** @@ -70,7 +67,6 @@ export namespace domain { )); if (domain.parent == null) { parentDomain.subdomainCount = parentDomain.subdomainCount.plus(BIGINT_ONE); - parentDomain.lastUpdatedBlock = airBlock.id; } domain.name = name; domain.labelName = labelName; @@ -80,9 +76,8 @@ export namespace domain { domain.parent = parentDomain.id; domain.labelHash = labelHash; domain.tokenId = tokenId; - domain.lastUpdatedBlock = airBlock.id; - parentDomain.save(); - domain.save(); + saveDomainEntity(parentDomain, airBlock); + saveDomainEntity(domain, airBlock); recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); let txn = getOrCreateAirDomainOwnerChangedTransaction( @@ -129,8 +124,9 @@ export namespace domain { let newOwnerAccount = getOrCreateAirAccount(chainId, newOwnerAddress, airBlock); newOwnerAccount.save(); domain.owner = newOwnerAccount.id; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + // 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) { @@ -181,8 +177,7 @@ export namespace domain { domain.resolvedAddress = resolverEntity.resolvedAddress; } // do recursive subdomain count decrement - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); recurseSubdomainCountDecrement(domain, chainId, airBlock, tokenAddress); // create new resolver transaction @@ -245,8 +240,7 @@ export namespace domain { } } domain.extras = extrasArray; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); // create AirDomainNewTTLTransaction let txn = getOrCreateAirDomainNewTTLTransaction( transactionHash, @@ -299,22 +293,18 @@ export namespace domain { domain.labelName = labelName } domain.expiryTimestamp = expiryTimestamp; - domain.lastUpdatedBlock = airBlock.id; if (cost) { domain.registrationCost = cost; } let airToken = getOrCreateAirToken(chainId, paymentToken); airToken.save(); domain.paymentToken = airToken.id; - domain.save(); + saveDomainEntity(domain, airBlock); // create name registered transaction let txn = getOrCreateAirNameRegisteredTransaction( chainId, - block.number, - block.hash.toHexString(), - block.timestamp, + airBlock, transactionHash, - logOrCallIndex, domain, cost, paymentToken, @@ -355,8 +345,7 @@ export namespace domain { tokenAddress, )); domain.expiryTimestamp = expiryTimestamp; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); // create name renewed transaction let txn = getOrCreateAirNameRenewedTransaction( transactionHash, @@ -376,12 +365,11 @@ export namespace domain { * @param block ethereum block * @param transactionHash transaction hash * @param domainId air domain id - * @param name domain name - * @param labelHash label hash - * @param cost cost - still needs to be recorded + * @param labelName domain labelName + * @param cost cost of registration or renewal * @param paymentToken payment token address - * @param renewer renewer address - can be null - * @param expiryTimestamp expiry date - can be null + * @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 */ @@ -389,29 +377,15 @@ export namespace domain { block: ethereum.Block, transactionHash: string, domainId: string, - name: string, - labelHash: Bytes, + labelName: string, cost: BigInt, paymentToken: string, - renewer: string | null, - expiryTimestamp: BigInt | null, + renewerOrRegistrant: string, + expiryTimestamp: BigInt, fromRegistrationEvent: boolean, tokenAddress: string, ): void { let chainId = getChainId(); - const calculatedLabelHash = crypto.keccak256(ByteArray.fromUTF8(name)); - if (!calculatedLabelHash.equals(labelHash)) { - log.warning( - "Expected '{}' to hash to {}, but got {} instead. Skipping.", - [name, calculatedLabelHash.toHex(), labelHash.toHex()] - ); - return; - } - - if (name.indexOf(".") !== -1) { - log.warning("Invalid label '{}'. Skipping.", [name]); - return; - } let airBlock = getOrCreateAirBlock(chainId, block.number, block.hash.toHexString(), block.timestamp); airBlock.save(); let domain = getOrCreateAirDomain(new Domain( @@ -420,17 +394,27 @@ export namespace domain { 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!; + domain.expiryTimestamp = expiryTimestamp; let txn = getOrCreateAirNameRenewedTransaction( transactionHash, chainId, @@ -438,22 +422,25 @@ export namespace domain { domain, cost, paymentToken, - renewer!, - expiryTimestamp!, + renewerOrRegistrant, + expiryTimestamp, ); txn.save(); } - if (domain.labelName !== name) { - domain.labelName = name - domain.name = name + '.eth' + 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(); } } - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); //new name registered event } @@ -489,21 +476,19 @@ export namespace domain { if (domain.resolver == resolver.id) { domain.resolvedAddress = addrAccount.id; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); + let txn = getOrCreateAirResolvedAddressChanged( + chainId, + logOrCallIndex, + resolver, + airBlock, + transactionHash, + previousResolvedAddressId, + resolvedAddress, + domain, + ); + txn.save(); } - - let txn = getOrCreateAirResolvedAddressChanged( - chainId, - logOrCallIndex, - resolver, - airBlock, - transactionHash, - previousResolvedAddressId, - resolvedAddress, - domain, - ); - txn.save(); } /** @@ -527,9 +512,8 @@ export namespace domain { resolver.save(); if (domain && domain.resolver == resolver.id) { domain.resolvedAddress = null - domain.lastUpdatedBlock = airBlock.id; + saveDomainEntity(domain, airBlock); } - domain.save(); } /** @@ -559,6 +543,7 @@ export namespace domain { 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 @@ -567,24 +552,23 @@ export namespace 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 - let oldPrimaryDomain = getOrCreateAirDomain(new Domain(primaryDomainEntity.domain, chainId, airBlock, tokenAddress)); + oldPrimaryDomain = getOrCreateAirDomain(new Domain(primaryDomainEntity.domain, chainId, airBlock, tokenAddress)); oldPrimaryDomain.isPrimary = false; - oldPrimaryDomain.lastUpdatedBlock = airBlock.id; - oldPrimaryDomain.save(); + saveDomainEntity(oldPrimaryDomain, airBlock); // set new primary domain for resolved address primaryDomainEntity.domain = domain.id; primaryDomainEntity.lastUpdatedAt = airBlock.id; - primaryDomainEntity.save(); } + primaryDomainEntity.save(); // set isPrimary on new domain domain.isPrimary = true; - domain.lastUpdatedBlock = airBlock.id; - domain.save(); + saveDomainEntity(domain, airBlock); } // record a set primary domain transaction with new domain let txn = getOrCreateAirPrimaryDomainTransaction( airBlock, transactionHash, + oldPrimaryDomain, domain, from, chainId, @@ -623,6 +607,7 @@ export namespace domain { * @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 @@ -631,6 +616,7 @@ export namespace domain { function getOrCreateAirPrimaryDomainTransaction( block: AirBlock, transactionHash: string, + previousDomain: AirDomain | null, domain: AirDomain, resolvedAddress: string, chainId: string, @@ -642,6 +628,9 @@ export namespace domain { 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 @@ -766,11 +755,8 @@ export namespace domain { * @dev this function does not save the returned entity * @dev this function gets or creates an AirNameRegisteredTransaction entity * @param chainId chain id - * @param blockHeight block height - * @param blockHash block hash - * @param blockTimestamp block timestamp + * @param block air block entity * @param transactionHash transaction hash - * @param logOrCallIndex txn log or call index * @param domain air domain * @param cost cost of the transaction * @param paymentToken payment token - can be null @@ -780,20 +766,15 @@ export namespace domain { */ function getOrCreateAirNameRegisteredTransaction( chainId: string, - blockHeight: BigInt, - blockHash: string, - blockTimestamp: BigInt, + block: AirBlock, transactionHash: string, - logOrCallIndex: BigInt, domain: AirDomain, cost: BigInt | null, paymentToken: string, registrant: string, expiryTimestamp: BigInt, ): AirNameRegisteredTransaction { - let block = getOrCreateAirBlock(chainId, blockHeight, blockHash, blockTimestamp); - block.save(); - let id = createEntityId(transactionHash, block.number, logOrCallIndex); + let id = domain.id.concat("-").concat(transactionHash); let entity = AirNameRegisteredTransaction.load(id); if (entity == null) { entity = new AirNameRegisteredTransaction(id); @@ -802,15 +783,15 @@ export namespace domain { entity.tokenId = domain.tokenId; entity.domain = domain.id; entity.index = updateAirEntityCounter(AIR_NAME_REGISTERED_ENTITY_COUNTER_ID, block); - entity.cost = cost; let airToken = getOrCreateAirToken(chainId, paymentToken); airToken.save(); entity.paymentToken = airToken.id; - let registrantAccount = getOrCreateAirAccount(chainId, registrant, block); - registrantAccount.save(); - entity.registrant = registrantAccount.id; entity.expiryTimestamp = expiryTimestamp; } + let registrantAccount = getOrCreateAirAccount(chainId, registrant, block); + registrantAccount.save(); + entity.registrant = registrantAccount.id; + entity.cost = cost; return entity as AirNameRegisteredTransaction; } @@ -962,7 +943,7 @@ export namespace domain { * @param domain Domain class object * @returns AirDomain entity */ - function getOrCreateAirDomain( + export function getOrCreateAirDomain( domain: Domain, ): AirDomain { let entity = AirDomain.load(domain.id); @@ -973,6 +954,7 @@ export namespace domain { 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; @@ -1048,8 +1030,7 @@ export namespace domain { const parentDomain = getOrCreateAirDomain(new Domain(domain.parent!, chainId, block, tokenAddress)); if (parentDomain) { parentDomain.subdomainCount = parentDomain.subdomainCount.minus(BIGINT_ONE) - parentDomain.lastUpdatedBlock = block.id; - parentDomain.save(); + saveDomainEntity(parentDomain, block); recurseSubdomainCountDecrement(parentDomain, chainId, block, tokenAddress) } } diff --git a/ens/modules/airstack/domain-name/utils.ts b/ens/modules/airstack/domain-name/utils.ts index e2afbe7a..08714fa5 100644 --- a/ens/modules/airstack/domain-name/utils.ts +++ b/ens/modules/airstack/domain-name/utils.ts @@ -1,3 +1,13 @@ +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"; @@ -7,9 +17,65 @@ export const AIR_NAME_RENEWED_ENTITY_COUNTER_ID = "AIR_NAME_RENEWED_ENTITY_COUNT 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"; \ No newline at end of file +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/schema.graphql b/ens/schema.graphql index f48087b8..0858e624 100644 --- a/ens/schema.graphql +++ b/ens/schema.graphql @@ -17,180 +17,17 @@ type DomainVsIsMigratedMapping @entity { lastUpdatedAt: AirBlock! } -# -# --Airstack Schemas-- - -type AirBlock @entity { - id: ID! #chain-number - hash: String! - number: BigInt! - timestamp: BigInt! -} - -type AirMeta @entity { - id: ID! # air_meta - network: String! - schemaVersion: String! - slug: String! #Opensea_V1 - name: String! # Opeasea V1 - version: String! -} - -type AirEntityCounter @entity { - id: ID! #AIR_DAILY_STATS_ACCOUNT - count: BigInt! - createdAt: AirBlock! - lastUpdatedAt: AirBlock! -} - -type AirExtra @entity { - id: ID! # Concatenation of domainId and name - name: String! - value: String! -} - -type AirAccount @entity { - id: ID! - address: String! - createdAt: AirBlock! -} - -type AirToken @entity { - id: ID! - address: String! -} - -type AirDomain @entity { - id: ID! # The namehash of the name - name: String # The human readable name, if known. Unknown portions replaced with hash in square brackets (eg, foo.[1234].eth) - labelName: String # The human readable label name (imported from CSV), if known - labelHash: String # keccak256(labelName) - tokenId: String # dec(labelHash) - parent: AirDomain # The namehash (id) of the parent name - subdomains: [AirDomain!]! @derivedFrom(field: "parent") # Can count domains from length of array - subdomainCount: BigInt! # The number of subdomains - resolvedAddress: AirAccount # Address logged from current resolver, if any - owner: AirAccount! - resolver: AirResolver - isPrimary: Boolean! # - NA # Is the primary domain for the resolved address - expiryTimestamp: BigInt! - registrationCost: BigInt! # Cost of domain registration in wei - paymentToken: AirToken # Token used to pay for registration - tokenAddress: AirToken! # Domain (eg: ens) token contract address - createdAt: AirBlock! - lastUpdatedBlock: AirBlock #- NA - extras: [AirExtra!] -} - -type AirDomainTransferTransaction implements AirDomainEvent @entity { - id: ID! - from: AirAccount! # - NA - to: AirAccount! # - NA - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! - index: BigInt! # - NA -} - -type AirDomainOwnerChangedTransaction implements AirDomainEvent @entity { - id: ID! - previousOwner: AirAccount! # - NA - newOwner: AirAccount! # - owner - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! - index: BigInt! # - NA -} - -type AirDomainNewResolverTransaction implements AirDomainEvent @entity { - id: ID! - previousResolver: AirAccount # - NA - newOwnerResolver: AirAccount! # - resolver - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! - index: BigInt! # - NA -} - -type AirDomainNewTTLTransaction implements AirDomainEvent @entity { - id: ID! - oldTTL: BigInt # - NA - newTTL: BigInt! # - ttl - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! - index: BigInt! # - NA -} - -type AirResolver @entity { - id: ID! # Concatenation of resolver address and namehash - domain: AirDomain - address: AirAccount! # Address of resolver contract - resolvedAddress: AirAccount # Current value of resolved address record (per events) -} - -type AirResolvedAddressChanged implements AirDomainEvent @entity { +type NameRegisteredTransactionVsRegistrant @entity { id: ID! - resolver: AirResolver! - previousResolvedAddress: AirAccount # previous resolved address record - newResolvedAddress: AirAccount! # new resolved address record - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) - NA - domain: AirDomain! - index: BigInt! # - NA -} - -interface AirDomainEvent { - block: AirBlock! + tokenId: String! transactionHash: String! - tokenId: String # dec(labelhash) - NA - domain: AirDomain! - index: BigInt! # - NA -} - -interface AirDomainRegistrationEvent { - cost: BigInt - paymentToken: AirToken - expiryTimestamp: BigInt! -} - -type AirNameRegisteredTransaction implements AirDomainEvent & AirDomainRegistrationEvent @entity { - id: ID! - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! - index: BigInt! # - NA - cost: BigInt - paymentToken: AirToken - registrant: AirAccount! - expiryTimestamp: BigInt! # - expiryTimestamp to be added to air domain -} - -type AirNameRenewedTransaction implements AirDomainEvent & AirDomainRegistrationEvent @entity { - id: ID! - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! - index: BigInt! # - NA - cost: BigInt - paymentToken: AirToken - renewer: AirAccount! - expiryTimestamp: BigInt! # - expiryTimestamp to be added to air domain + oldRegistrant: AirAccount! + newRegistrant: AirAccount! + createdAt: AirBlock! } -type AirPrimaryDomainTransaction implements AirDomainEvent @entity { - id: ID! - block: AirBlock! - transactionHash: String! - tokenId: String # dec(labelhash) # - NA - domain: AirDomain! # - name - index: BigInt! # - NA - resolvedAddress: AirAccount! #make sure to remove the old primary ens if changed +type LabelhashToNameMapping @entity { + id: ID! #labelhash + name: String! + createdAt: String! } diff --git a/ens/src/ens-registry.ts b/ens/src/ens-registry.ts index 81d177eb..9a4429d3 100644 --- a/ens/src/ens-registry.ts +++ b/ens/src/ens-registry.ts @@ -5,6 +5,7 @@ import { Transfer as TransferEvent } from "../generated/EnsRegistry/EnsRegistry" import * as airstack from "../modules/airstack/domain-name"; +import { checkValidLabel } from "../modules/airstack/domain-name/utils"; import { log, BigInt, @@ -17,6 +18,7 @@ import { getOrCreateIsMigratedMapping, createAirDomainEntityId, createReverseRegistrar, + getNameByLabelHash, } from "./utils"; import { AirDomain } from "../generated/schema"; /** @@ -38,7 +40,16 @@ export function handleNewOwner(event: NewOwnerEvent): void { isMigratedMapping.save(); // creating labelName from label let labelName = ens.nameByHash(event.params.label.toHexString()); - if (labelName === null) { + if (labelName == null) { + // try to get the name from the labelhash to name mapping + labelName = getNameByLabelHash(event.params.label.toHexString()); + if (labelName == null) { + log.info("handleNewOwner: labelName is null from getNameByLabelHash label {} txhash {}, converting to brackets", [event.params.label.toHexString(), event.transaction.hash.toHexString()]); + labelName = '[' + event.params.label.toHexString().slice(2) + ']'; + } + log.info("handleNewOwner: labelName is null for label {} txhash {}", [event.params.label.toHexString(), event.transaction.hash.toHexString()]); + } else if (labelName && !checkValidLabel(labelName, event.transaction.hash.toHexString())) { + log.info("handleNewOwner: labelName is invalid for label {} txhash {}", [event.params.label.toHexString(), event.transaction.hash.toHexString()]); labelName = '[' + event.params.label.toHexString().slice(2) + ']'; } // creating name from labelName and parentName @@ -53,7 +64,7 @@ export function handleNewOwner(event: NewOwnerEvent): void { } else { parentName = ""; } - name = labelName + "." + parentName!; + name = labelName! + "." + parentName!; } if (name) { let reverseRegistrar = createReverseRegistrar(name, domainId, ETHEREUM_MAINNET_ID, event.block); @@ -179,7 +190,15 @@ export function handleNewOwnerOldRegistry(event: NewOwnerEvent): void { isMigratedMapping.save(); // creating labelName from label let labelName = ens.nameByHash(event.params.label.toHexString()); - if (labelName === null) { + if (labelName == null) { + // try to get the name from the labelhash to name mapping, it can be null if mapping not present + labelName = getNameByLabelHash(event.params.label.toHexString()); + if (labelName == null) { + log.info("handleNewOwnerOldRegistry: labelName is null from getNameByLabelHash label {} txhash {}, converting to brackets", [event.params.label.toHexString(), event.transaction.hash.toHexString()]); + labelName = '[' + event.params.label.toHexString().slice(2) + ']'; + } + } else if (labelName && !checkValidLabel(labelName, event.transaction.hash.toHexString())) { + log.info("handleNewOwnerOldRegistry: labelName is invalid for label {} txhash {}", [event.params.label.toHexString(), event.transaction.hash.toHexString()]); labelName = '[' + event.params.label.toHexString().slice(2) + ']'; } // creating name from labelName and parentName @@ -194,7 +213,7 @@ export function handleNewOwnerOldRegistry(event: NewOwnerEvent): void { } else { parentName = ""; } - name = labelName + "." + parentName!; + name = labelName! + "." + parentName!; } if (name) { let reverseRegistrar = createReverseRegistrar(name, domainId, getChainId(), event.block); diff --git a/ens/src/eth-registrar.ts b/ens/src/eth-registrar.ts index 8efb8cbc..e6cc21bb 100644 --- a/ens/src/eth-registrar.ts +++ b/ens/src/eth-registrar.ts @@ -5,22 +5,29 @@ import { ens, crypto, } from '@graphprotocol/graph-ts' -import { TOKEN_ADDRESS_ENS } from "./utils"; +import { TOKEN_ADDRESS_ENS, createLabelhashToNameMapping, getNameByLabelHash } from "./utils"; import { ZERO_ADDRESS } from '../modules/airstack/domain-name/utils'; import * as airstack from "../modules/airstack/domain-name"; +import { checkValidLabel } from "../modules/airstack/domain-name/utils"; // Import event types from the registry contract ABI import { NameRegistered as NameRegisteredEvent, NameRenewed as NameRenewedEvent, + Transfer as NameRegisteredTransferEvent, } from '../generated/BaseRegistrar/BaseRegistrar'; import { NameRegistered as ControllerNameRegisteredEventOld, } from '../generated/EthRegistrarControllerOld/EthRegistrarControllerOld'; +import { + NameRegistered as ControllerNameRegisteredEventNew, +} from '../generated/EthRegistrarControllerNew/EthRegistrarControllerNew'; import { NameRegistered as ControllerNameRegisteredEvent, NameRenewed as ControllerNameRenewedEvent } from '../generated/EthRegistrarController/EthRegistrarController'; -import { uint256ToByteArray, byteArrayFromHex } from './utils'; +import { uint256ToByteArray, byteArrayFromHex, createNameRegisteredTransactionVsRegistrant } from './utils'; +import { AirDomain, AirNameRegisteredTransaction } from '../generated/schema'; +import { getChainId, getOrCreateAirAccount, getOrCreateAirBlock } from '../modules/airstack/common'; const rootNode: ByteArray = byteArrayFromHex("93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"); @@ -29,9 +36,20 @@ const rootNode: ByteArray = byteArrayFromHex("93cdeb708b7545dc668eb9280176169d1c * @param event NameRegisteredEvent from BaseRegistrar contract */ export function handleNameRegistered(event: NameRegisteredEvent): void { - log.info("handleNameRegistered: registrant {} label {} expiry {} txhash {}", [event.params.owner.toHexString(), event.params.id.toHexString(), event.params.expires.toString(), event.transaction.hash.toHexString()]); 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()); + 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 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, @@ -74,12 +92,14 @@ export function handleNameRenewed(event: NameRenewedEvent): void { export function handleNameRegisteredByControllerOld(event: ControllerNameRegisteredEventOld): void { log.info("handleNameRegisteredByControllerOld: 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(), domainId, - event.params.name, - event.params.label, + labelName, event.params.cost, ZERO_ADDRESS, event.params.owner.toHexString(), @@ -89,6 +109,30 @@ export function handleNameRegisteredByControllerOld(event: ControllerNameRegiste ) } +/** + * @dev this function maps the NameRegistered from the EthRegistrarControllerNew contract + * @param event ControllerNameRegisteredEventNew from EthRegistrarControllerNew contract + */ +export function handleNameRegisteredByControllerNew(event: ControllerNameRegisteredEventNew): void { + log.info("handleNameRegisteredByControllerNew: name {} label {} baseCost {} premium {} txhash {}", [event.params.name, event.params.label.toHexString(), event.params.baseCost.toString(), event.params.premium.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(), + domainId, + labelName, + event.params.baseCost.plus(event.params.premium), + ZERO_ADDRESS, + event.params.owner.toHexString(), + event.params.expires, + true, + TOKEN_ADDRESS_ENS, + ) +} + /** * @dev this function maps the NameRegistered event from the EthRegistrarController contract * @param event ControllerNameRegisteredEvent from EthRegistrarController contract @@ -96,12 +140,14 @@ export function handleNameRegisteredByControllerOld(event: ControllerNameRegiste export function handleNameRegisteredByController(event: ControllerNameRegisteredEvent): void { log.info("handleNameRegisteredByController: 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(), domainId, - event.params.name, - event.params.label, + labelName, event.params.cost, ZERO_ADDRESS, event.params.owner.toHexString(), @@ -118,12 +164,12 @@ export function handleNameRegisteredByController(event: ControllerNameRegistered export function handleNameRenewedByController(event: ControllerNameRenewedEvent): void { 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; airstack.domain.trackNameRenewedOrRegistrationByController( event.block, event.transaction.hash.toHexString(), domainId, - event.params.name, - event.params.label, + labelName, event.params.cost, ZERO_ADDRESS, event.transaction.from.toHexString(), @@ -131,4 +177,39 @@ export function handleNameRenewedByController(event: ControllerNameRenewedEvent) false, TOKEN_ADDRESS_ENS, ) +} + +/** + * @dev this function updates the registrant of a name registered transaction + * @param event NameTransferredEvent from BaseRegistrar contract (token transfer event) + */ +export function handleNameTransferred(event: NameRegisteredTransferEvent): void { + log.info("handleNameTransferred: from {} to {} tokenId {} txhash {} logIndex {}", [event.params.from.toHexString(), event.params.to.toHexString(), event.params.tokenId.toString(), event.transaction.hash.toHexString(), event.logIndex.toString()]); + let chainId = getChainId(); + let label = uint256ToByteArray(event.params.tokenId); + let domainId = crypto.keccak256(rootNode.concat(label)).toHex(); + const nameRegistrationTransactionEntityId = domainId.concat("-").concat(event.transaction.hash.toHexString()); + let nameRegistrationTransaction = AirNameRegisteredTransaction.load(nameRegistrationTransactionEntityId); + if (nameRegistrationTransaction == null) { + log.debug("handleNameTransferred: name registration transaction not found for id {} txhash {}", [label.toHexString(), event.transaction.hash.toHexString()]); + return; + } + let oldRegistrantId = nameRegistrationTransaction.registrant; + let airBlock = getOrCreateAirBlock(chainId, event.block.number, event.block.hash.toHexString(), event.block.timestamp); + airBlock.save(); + let airAccount = getOrCreateAirAccount(chainId, event.params.to.toHexString(), airBlock); + airAccount.save(); + // override the registrant as the new owner (helps in case of ens migrations) + nameRegistrationTransaction.registrant = airAccount.id; + nameRegistrationTransaction.save(); + // create a mapping for tracking the transaction and registrants + createNameRegisteredTransactionVsRegistrant( + event.transaction.hash.toHexString(), + airBlock, + nameRegistrationTransactionEntityId, + event.params.tokenId.toString(), + oldRegistrantId, + nameRegistrationTransaction.registrant, + ); + log.info("handleNameTransferred: nameRegistrationTransactionEntityId {} txhash {} logIndex {} registrantOld {} registrantNew {}", [nameRegistrationTransactionEntityId, event.transaction.hash.toHexString(), event.logIndex.toString(), oldRegistrantId, nameRegistrationTransaction.registrant]); } \ No newline at end of file diff --git a/ens/src/name-wrapper.ts b/ens/src/name-wrapper.ts new file mode 100644 index 00000000..d8b1ac06 --- /dev/null +++ b/ens/src/name-wrapper.ts @@ -0,0 +1,31 @@ +import * as airstack from "../modules/airstack/domain-name"; +import { + NameWrapped as NameWrappedEvent, +} from "../generated/NameWrapper/NameWrapper"; +import { decodeName } from "./utils"; +import { getChainId, getOrCreateAirBlock, updateAirEntityCounter } from "../modules/airstack/common"; +import { saveDomainEntity } from "../modules/airstack/domain-name/utils"; +import { TOKEN_ADDRESS_ENS } from "./utils"; +import { + log, +} from "@graphprotocol/graph-ts"; + +export function handleNameWrapped(event: NameWrappedEvent): void { + log.info("handleNameWrapped: node {} name {} owner {} txhash {}", [event.params.node.toHexString(), event.params.name.toHexString(), event.params.owner.toHexString(), event.transaction.hash.toHexString()]); + let decoded = decodeName(event.params.name, event.transaction.hash.toHexString()); + let label: string | null = null; + let name: string | null = null; + if (decoded !== null) { + label = decoded[0]; + name = decoded[1]; + } + const chainId = getChainId(); + let airBlock = getOrCreateAirBlock(chainId, event.block.number, event.block.hash.toHexString(), event.block.timestamp); + airBlock.save(); + const domain = airstack.domain.getOrCreateAirDomain(new airstack.domain.Domain(event.params.node.toHexString(), chainId, airBlock, TOKEN_ADDRESS_ENS)); + if (!domain.labelName && label) { + domain.labelName = label; + domain.name = name; + saveDomainEntity(domain, airBlock); + } +} \ No newline at end of file diff --git a/ens/src/utils.ts b/ens/src/utils.ts index 4cbd8e58..f62af0ef 100644 --- a/ens/src/utils.ts +++ b/ens/src/utils.ts @@ -1,6 +1,9 @@ import { DomainVsIsMigratedMapping, ReverseRegistrar, + NameRegisteredTransactionVsRegistrant, + AirBlock, + LabelhashToNameMapping, } from "../generated/schema"; import { Bytes, @@ -8,8 +11,11 @@ import { ethereum, ByteArray, BigInt, + log, } from "@graphprotocol/graph-ts"; - +import { + checkValidLabel +} from "../modules/airstack/domain-name/utils"; // ens constants export const TOKEN_ADDRESS_ENS = "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85"; @@ -66,6 +72,55 @@ export function createReverseRegistrar( return entity as ReverseRegistrar; } +/** + * @dev this function creates a new NameRegisteredTransactionVsRegistrant entity + * @param transactionHash transaction hash + * @param block air block entity + * @param id domainId-transactionHash + * @param tokenId transferred token id + * @param oldRegistrantId name registered txn old registrant id + * @param newRegistrantId name registered txn new registrant id + * @returns NameRegisteredTransactionVsRegistrant entity + */ +export function createNameRegisteredTransactionVsRegistrant( + transactionHash: string, + block: AirBlock, + id: string, + tokenId: string, + oldRegistrantId: string, + newRegistrantId: string, +): NameRegisteredTransactionVsRegistrant { + let entity = NameRegisteredTransactionVsRegistrant.load(id); + if (entity == null) { + entity = new NameRegisteredTransactionVsRegistrant(id); + entity.oldRegistrant = oldRegistrantId; + entity.newRegistrant = newRegistrantId; + entity.transactionHash = transactionHash; + entity.tokenId = tokenId; + entity.createdAt = block.id; + entity.save(); + } + return entity as NameRegisteredTransactionVsRegistrant; +} + +export function createLabelhashToNameMapping(labelhash: string, name: string, createdAt: string): void { + let entity = LabelhashToNameMapping.load(labelhash); + if (entity == null) { + entity = new LabelhashToNameMapping(labelhash); + entity.name = name; + entity.createdAt = createdAt; + entity.save(); + } +} + +export function getNameByLabelHash(labelhash: string): string | null { + let entity = LabelhashToNameMapping.load(labelhash); + if (entity == null) { + return null; + } + return entity.name; +} + // specific to ens export function byteArrayFromHex(s: string): ByteArray { if (s.length % 2 !== 0) { @@ -81,4 +136,35 @@ export function byteArrayFromHex(s: string): ByteArray { export function uint256ToByteArray(i: BigInt): ByteArray { let hex = i.toHex().slice(2).padStart(64, '0') return byteArrayFromHex(hex) +} + +export function decodeName(buf: Bytes, txHash: string): Array | null { + let offset = 0; + let list = new ByteArray(0); + let dot = Bytes.fromHexString("2e"); + let len = buf[offset++]; + let hex = buf.toHexString(); + let firstLabel = ""; + if (len === 0) { + return [firstLabel, "."]; + } + + while (len) { + let label = hex.slice((offset + 1) * 2, (offset + 1 + len) * 2); + let labelBytes = Bytes.fromHexString(label); + + if (!checkValidLabel(labelBytes.toString(), txHash)) { + return null; + } + + if (offset > 1) { + list = list.concat(dot); + } else { + firstLabel = labelBytes.toString(); + } + list = list.concat(labelBytes); + offset += len; + len = buf[offset++]; + } + return [firstLabel, list.toString()]; } \ No newline at end of file diff --git a/ens/subgraph.yaml b/ens/subgraph.yaml index bd896a3c..c456dd3f 100644 --- a/ens/subgraph.yaml +++ b/ens/subgraph.yaml @@ -14,21 +14,6 @@ dataSources: apiVersion: 0.0.7 language: wasm/assemblyscript entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: EnsRegistry file: ./abis/EnsRegistry.json @@ -54,21 +39,6 @@ dataSources: apiVersion: 0.0.7 language: wasm/assemblyscript entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: EnsRegistry file: ./abis/EnsRegistry.json @@ -95,21 +65,6 @@ dataSources: language: wasm/assemblyscript file: ./src/eth-registrar.ts entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: BaseRegistrar file: ./abis/BaseRegistrar.json @@ -130,21 +85,6 @@ dataSources: language: wasm/assemblyscript file: ./src/resolver.ts entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: Resolver file: ./abis/PublicResolver.json @@ -166,21 +106,6 @@ dataSources: language: wasm/assemblyscript file: ./src/reverse-registrar.ts entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: ReverseRegistrar file: ./abis/ReverseRegistrar.json @@ -200,21 +125,6 @@ dataSources: language: wasm/assemblyscript file: ./src/eth-registrar.ts entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: EthRegistrarController file: ./abis/EthRegistrarController.json @@ -236,21 +146,6 @@ dataSources: language: wasm/assemblyscript file: ./src/eth-registrar.ts entities: - - AirAccount - - AirBlock - - AirMeta - - AirEntityCounter - - AirToken - - AirDomain - - AirDomainTransferTransaction - - AirDomainOwnerChangedTransaction - - AirDomainNewResolverTransaction - - AirDomainNewTTLTransaction - - AirResolver - - AirAddrChanged - - AirNameRegisteredTransaction - - AirNameRenewedTransaction - - AirPrimaryDomainTransaction abis: - name: EthRegistrarControllerOld file: ./abis/EthRegistrarControllerOld.json @@ -259,3 +154,63 @@ dataSources: handler: handleNameRegisteredByControllerOld - event: NameRenewed(string,indexed bytes32,uint256,uint256) handler: handleNameRenewedByController + - kind: ethereum/contract + name: ETHRegistrarControllerNew + network: mainnet + source: + address: '0x253553366Da8546fC250F225fe3d25d0C782303b' + abi: ETHRegistrarControllerNew + startBlock: 16925618 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: ./src/eth-registrar.ts + entities: + abis: + - name: ETHRegistrarControllerNew + file: ./abis/ETHRegistrarControllerNew.json + eventHandlers: + - event: NameRegistered(string,indexed bytes32,indexed address,uint256,uint256,uint256) + handler: handleNameRegisteredByControllerNew + - event: NameRenewed(string,indexed bytes32,uint256,uint256) + handler: handleNameRenewedByController + - kind: ethereum/contract + name: ReverseRegistrarNew + network: mainnet + source: + abi: ReverseRegistrar + address: '0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb' + startBlock: 16925606 + 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 + source: + address: '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401' + abi: NameWrapper + startBlock: 16925608 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: ./src/name-wrapper.ts + entities: + - NameWrapper + abis: + - name: NameWrapper + file: ./abis/NameWrapper.json + eventHandlers: + - event: NameWrapped(indexed bytes32,bytes,address,uint32,uint64) + handler: handleNameWrapped diff --git a/ens/tests/common-utils.ts b/ens/tests/common-utils.ts index 1a194efe..e3244334 100644 --- a/ens/tests/common-utils.ts +++ b/ens/tests/common-utils.ts @@ -1,4 +1,4 @@ -import { BigInt, Bytes } from "@graphprotocol/graph-ts" +import { Bytes } from "@graphprotocol/graph-ts" export function getTransactionHash(): Bytes { return Bytes.fromHexString("0xafb6d7ac92f6beb3f3df6a9bbfaeb2f99b9db020ee69199af95f2e8ea5253467") as Bytes diff --git a/ens/tests/ens-registry-utils.ts b/ens/tests/ens-registry-utils.ts index 5f56223f..f4e9caf2 100644 --- a/ens/tests/ens-registry-utils.ts +++ b/ens/tests/ens-registry-utils.ts @@ -1,7 +1,7 @@ import { newMockEvent } from "matchstick-as" import { ethereum, Address, Bytes, BigInt, ens } from "@graphprotocol/graph-ts" import { getTransactionHash } from "./common-utils" -import { BIG_INT_ZERO } from "../modules/airstack/common/index" +import { BIGINT_ONE, BIG_INT_ZERO } from "../modules/airstack/common/index" import { NewOwner as NewOwnerEvent, NewResolver as NewResolverEvent, @@ -154,5 +154,6 @@ export function createParentDomain(parentDomainId: string): void { entity.registrationCost = BIG_INT_ZERO; entity.createdAt = "1-472668903"; entity.lastUpdatedBlock = "1-472668903"; + entity.lastUpdatedIndex = BIGINT_ONE; entity.save(); } \ No newline at end of file diff --git a/ens/tests/ens-registry.test.ts b/ens/tests/ens-registry.test.ts index f10caa9c..04d46ea2 100644 --- a/ens/tests/ens-registry.test.ts +++ b/ens/tests/ens-registry.test.ts @@ -31,10 +31,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -76,6 +76,7 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirDomain", domainId, "registrationCost", BIG_INT_ZERO.toString()); assert.fieldEquals("AirDomain", domainId, "expiryTimestamp", BIG_INT_ZERO.toString()); assert.fieldEquals("AirDomain", domainId, "isPrimary", "false"); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "2"); assert.fieldEquals("AirDomain", domainId, "tokenAddress", ETHEREUM_MAINNET_ID.concat("-").concat(TOKEN_ADDRESS_ENS)); // AirDomainOwnerChangedTransaction let domainOwnerChangedEntityId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); @@ -101,10 +102,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -146,6 +147,7 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirDomain", domainId, "registrationCost", BIG_INT_ZERO.toString()); assert.fieldEquals("AirDomain", domainId, "expiryTimestamp", BIG_INT_ZERO.toString()); assert.fieldEquals("AirDomain", domainId, "isPrimary", "false"); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "2"); assert.fieldEquals("AirDomain", domainId, "tokenAddress", ETHEREUM_MAINNET_ID.concat("-").concat(TOKEN_ADDRESS_ENS)); // AirDomainOwnerChangedTransaction let domainOwnerChangedEntityId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); @@ -167,10 +169,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -197,6 +199,7 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirEntityCounter", airEntityCounterId, "lastUpdatedAt", blockId); // AirDomain assert.fieldEquals("AirDomain", domainId, "owner", ETHEREUM_MAINNET_ID.concat("-").concat(event.params.owner.toHexString())); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1"); // AirDomainTransferTransaction let domainOwnerChangedEntityId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); assert.fieldEquals("AirDomainTransferTransaction", domainOwnerChangedEntityId, "from", ETHEREUM_MAINNET_ID.concat("-").concat(ZERO_ADDRESS)); @@ -217,10 +220,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -247,6 +250,7 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirEntityCounter", airEntityCounterId, "lastUpdatedAt", blockId); // AirDomain assert.fieldEquals("AirDomain", domainId, "owner", ETHEREUM_MAINNET_ID.concat("-").concat(event.params.owner.toHexString())); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1"); // AirDomainTransferTransaction let domainOwnerChangedEntityId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); assert.fieldEquals("AirDomainTransferTransaction", domainOwnerChangedEntityId, "from", ETHEREUM_MAINNET_ID.concat("-").concat(ZERO_ADDRESS)); @@ -268,10 +272,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -304,6 +308,7 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirDomain", domainId, "resolver", resolverId); assert.fieldEquals("AirDomain", domainId, "subdomainCount", BIG_INT_ZERO.toString()); assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1"); // AirDomainNewResolverTransaction let domainNewResolverEntityId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); assert.fieldEquals("AirDomainNewResolverTransaction", domainNewResolverEntityId, "previousResolver", "null"); @@ -325,10 +330,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -361,6 +366,7 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirDomain", domainId, "resolver", resolverId); assert.fieldEquals("AirDomain", domainId, "subdomainCount", BIG_INT_ZERO.toString()); assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1"); // AirDomainNewResolverTransaction let domainNewResolverEntityId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); assert.fieldEquals("AirDomainNewResolverTransaction", domainNewResolverEntityId, "previousResolver", "null"); @@ -381,10 +387,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -408,6 +414,7 @@ describe("Unit tests for ens registry handlers", () => { // AirDomain assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "extras", "[0xad3988d642ba25a8ca9d8889e0cfd6c550060e35455c55c936be87f9cfb97407-ttl]"); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1"); // AirExtra assert.fieldEquals("AirExtra", domainId.concat("-").concat('ttl'), "id", domainId.concat("-").concat('ttl')); assert.fieldEquals("AirExtra", domainId.concat("-").concat('ttl'), "name", "ttl"); @@ -427,10 +434,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event1.block.number.toString()); @@ -454,6 +461,7 @@ describe("Unit tests for ens registry handlers", () => { // AirDomain assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "extras", "[0xad3988d642ba25a8ca9d8889e0cfd6c550060e35455c55c936be87f9cfb97407-ttl]"); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "2"); // AirExtra assert.fieldEquals("AirExtra", domainId.concat("-").concat('ttl'), "id", domainId.concat("-").concat('ttl')); assert.fieldEquals("AirExtra", domainId.concat("-").concat('ttl'), "name", "ttl"); @@ -478,10 +486,10 @@ describe("Unit tests for ens registry handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -505,6 +513,7 @@ describe("Unit tests for ens registry handlers", () => { // AirDomain assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "extras", "[0xad3988d642ba25a8ca9d8889e0cfd6c550060e35455c55c936be87f9cfb97407-ttl]"); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1"); // AirExtra assert.fieldEquals("AirExtra", domainId.concat("-").concat('ttl'), "id", domainId.concat("-").concat('ttl')); assert.fieldEquals("AirExtra", domainId.concat("-").concat('ttl'), "name", "ttl"); @@ -519,4 +528,10 @@ describe("Unit tests for ens registry handlers", () => { assert.fieldEquals("AirDomainNewTTLTransaction", domainNewResolverEntityId, "domain", domainId); assert.fieldEquals("AirDomainNewTTLTransaction", domainNewResolverEntityId, "index", BIGINT_ONE.toString()); }) + + test("ens name by hash", () => { + const labelHash = "0x8e60bd7bd6a2886831b6d6ef4b70051b734b9caab02b0edca7b63f8385600063"; + let labelName = ens.nameByHash(labelHash); + log.info("labelName: {}", [labelName!]); + }) }) \ No newline at end of file diff --git a/ens/tests/eth-registrar-utils.ts b/ens/tests/eth-registrar-utils.ts index 00b3a941..65cc77e4 100644 --- a/ens/tests/eth-registrar-utils.ts +++ b/ens/tests/eth-registrar-utils.ts @@ -1,9 +1,10 @@ import { newMockEvent } from "matchstick-as" -import { ethereum, Address, Bytes, BigInt } from "@graphprotocol/graph-ts" +import { ethereum, Address, Bytes, BigInt, ByteArray } from "@graphprotocol/graph-ts" import { getTransactionHash } from "./common-utils" import { NameRegistered as NameRegisteredEvent, NameRenewed as NameRenewedEvent, + Transfer as NameRegisteredTransferEvent, } from '../generated/BaseRegistrar/BaseRegistrar'; import { NameRegistered as ControllerNameRegisteredEventOld, @@ -33,6 +34,36 @@ export function getHandleNameRenewedByControllerEvent(): ControllerNameRenewedEv return createHandleNameRenewedByControllerEvent() } + +export function getHandleNameTransferredEvent(): NameRegisteredTransferEvent { + return createHandleNameTransferredEvent() +} + +export function createHandleNameTransferredEvent(): NameRegisteredTransferEvent { + let event = changetype(newMockEvent()) + + event.parameters = new Array() + event.block.number = BigInt.fromI32(10098239); + event.block.timestamp = BigInt.fromI32(2879823); + event.block.hash = Bytes.fromHexString("0x701633854b23364112e8528a85254a039abf8d1d81d629f88426196819e0b0b5") + event.transaction.hash = getTransactionHash() + event.logIndex = BigInt.fromI32(78) + event.transaction.from = Address.fromString("0x084b1c3c81545d370f3634392de611caabff8148") + event.transaction.value = BigInt.fromString("1000000000000000000") + + event.parameters.push( + new ethereum.EventParam("from", ethereum.Value.fromAddress(Address.fromString("0x084b1c3c81545d370f3634392de611caabff8148") as Address)) + ) + event.parameters.push( + new ethereum.EventParam("to", ethereum.Value.fromAddress(Address.fromString("0x084b1c3c81545d370f3634392de611caabff66cc") as Address)) + ) + event.parameters.push( + new ethereum.EventParam("tokenId", ethereum.Value.fromUnsignedBigInt(BigInt.fromString("91429126920367530313023827682976888360097522553506880517423103419682943364318"))) + ) + + return event +} + export function createHandleNameRegisteredEvent( ): NameRegisteredEvent { let event = changetype(newMockEvent()) @@ -157,4 +188,21 @@ export function createHandleNameRenewedByControllerEvent( new ethereum.EventParam("expires", ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(10098239))) ) return event +} + +// specific to ens +export function byteArrayFromHex(s: string): ByteArray { + if (s.length % 2 !== 0) { + throw new TypeError("Hex string must have an even number of characters") + } + let out = new Uint8Array(s.length / 2) + for (var i = 0; i < s.length; i += 2) { + out[i / 2] = parseInt(s.substring(i, i + 2), 16) as u32 + } + return changetype(out) +} + +export function uint256ToByteArray(i: BigInt): ByteArray { + let hex = i.toHex().slice(2).padStart(64, '0') + return byteArrayFromHex(hex) } \ No newline at end of file diff --git a/ens/tests/eth-registrar.test.ts b/ens/tests/eth-registrar.test.ts index 6d031f1a..733279ac 100644 --- a/ens/tests/eth-registrar.test.ts +++ b/ens/tests/eth-registrar.test.ts @@ -6,8 +6,8 @@ import { afterEach, } from "matchstick-as/assembly/index" import { ByteArray, crypto, Bytes } from "@graphprotocol/graph-ts" -import { handleNameRenewedByController, handleNameRegistered, handleNameRenewed, handleNameRegisteredByControllerOld, handleNameRegisteredByController } from "../src/eth-registrar" -import { getHandleNameRenewedByControllerEvent, getHandleNameRegisteredEvent, getHandleNameRenewedEvent, getHandleNameRegisteredByControllerOldEvent, getHandleNameRegisteredByControllerEvent } from "./eth-registrar-utils" +import { handleNameTransferred, handleNameRenewedByController, handleNameRegistered, handleNameRenewed, handleNameRegisteredByControllerOld, handleNameRegisteredByController } from "../src/eth-registrar" +import { getHandleNameTransferredEvent, getHandleNameRenewedByControllerEvent, getHandleNameRegisteredEvent, getHandleNameRenewedEvent, getHandleNameRegisteredByControllerOldEvent, getHandleNameRegisteredByControllerEvent } from "./eth-registrar-utils" import { ETHEREUM_MAINNET_ID, ZERO_ADDRESS } from "../modules/airstack/domain-name/utils" import { uint256ToByteArray, byteArrayFromHex } from "../src/utils" import { BIGINT_ONE } from "../modules/airstack/common" @@ -29,10 +29,10 @@ describe("Unit tests for eth registrar handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -57,8 +57,9 @@ describe("Unit tests for eth registrar handlers", () => { assert.fieldEquals("AirDomain", domainId, "expiryTimestamp", event.params.expires.toString()); assert.fieldEquals("AirDomain", domainId, "paymentToken", ETHEREUM_MAINNET_ID.concat("-").concat(ZERO_ADDRESS)); assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") // AirNameRegisteredTransaction - let nameRegisteredId = event.transaction.hash.toHexString().concat("-").concat(event.block.number.toString()).concat("-").concat(event.logIndex.toString()); + let nameRegisteredId = domainId.concat("-").concat(event.transaction.hash.toHexString()); assert.fieldEquals("AirNameRegisteredTransaction", nameRegisteredId, "paymentToken", airTokenId); assert.fieldEquals("AirNameRegisteredTransaction", nameRegisteredId, "block", blockId); assert.fieldEquals("AirNameRegisteredTransaction", nameRegisteredId, "transactionHash", event.transaction.hash.toHexString()); @@ -79,10 +80,10 @@ describe("Unit tests for eth registrar handlers", () => { // assert here // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirBlock assert.fieldEquals("AirBlock", blockId, "id", blockId); assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); @@ -106,6 +107,7 @@ describe("Unit tests for eth registrar handlers", () => { // AirDomain assert.fieldEquals("AirDomain", domainId, "expiryTimestamp", event.params.expires.toString()); assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") // AirNameRenewedTransaction let nameRenewedId = event.transaction.hash.toHexString().concat("-").concat(domainId); assert.fieldEquals("AirNameRenewedTransaction", nameRenewedId, "paymentToken", airTokenId); @@ -138,6 +140,7 @@ describe("Unit tests for eth registrar handlers", () => { assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "labelName", event.params.name); assert.fieldEquals("AirDomain", domainId, "name", event.params.name.concat(".eth")); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") // ReverseRegistrar let domain = AirDomain.load(domainId)!; assert.fieldEquals("ReverseRegistrar", reverseRegistrarId, "id", crypto.keccak256(Bytes.fromUTF8(event.params.name.concat(".eth"))).toHexString()); @@ -165,6 +168,7 @@ describe("Unit tests for eth registrar handlers", () => { assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "labelName", event.params.name); assert.fieldEquals("AirDomain", domainId, "name", event.params.name.concat(".eth")); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") // ReverseRegistrar let domain = AirDomain.load(domainId)!; assert.fieldEquals("ReverseRegistrar", reverseRegistrarId, "id", crypto.keccak256(Bytes.fromUTF8(event.params.name.concat(".eth"))).toHexString()); @@ -207,6 +211,7 @@ describe("Unit tests for eth registrar handlers", () => { assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId); assert.fieldEquals("AirDomain", domainId, "labelName", event.params.name); assert.fieldEquals("AirDomain", domainId, "name", event.params.name.concat(".eth")); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") // AirNameRenewedTransaction let nameRenewedId = event.transaction.hash.toHexString().concat("-").concat(domainId); assert.fieldEquals("AirNameRenewedTransaction", nameRenewedId, "block", blockId); @@ -225,4 +230,45 @@ describe("Unit tests for eth registrar handlers", () => { assert.fieldEquals("ReverseRegistrar", reverseRegistrarId, "domain", domainId); assert.fieldEquals("ReverseRegistrar", reverseRegistrarId, "createdAt", blockId); }) + + test("Test handleNameTransferred", () => { + // create a name registration txn to test the transfer function + let nameRegisteredEvent = getHandleNameRegisteredEvent(); + handleNameRegistered(nameRegisteredEvent) + // create event params for name transferred event + // call handleNameTransferred + // assert here + let event = getHandleNameTransferredEvent(); + handleNameTransferred(event) + // assert here + let blockId = ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString()); + // NameRegisteredTransactionVsRegistrant + let label = uint256ToByteArray(event.params.tokenId); + let domainId = crypto.keccak256(rootNode.concat(label)).toHex(); + let nameRegisteredTransactionVsRegistrantId = domainId.concat("-").concat(event.transaction.hash.toHexString()); + assert.fieldEquals("NameRegisteredTransactionVsRegistrant", nameRegisteredTransactionVsRegistrantId, "id", domainId.concat("-").concat(event.transaction.hash.toHexString())); + assert.fieldEquals("NameRegisteredTransactionVsRegistrant", nameRegisteredTransactionVsRegistrantId, "oldRegistrant", ETHEREUM_MAINNET_ID.concat("-").concat(nameRegisteredEvent.params.owner.toHexString())); + assert.fieldEquals("NameRegisteredTransactionVsRegistrant", nameRegisteredTransactionVsRegistrantId, "newRegistrant", ETHEREUM_MAINNET_ID.concat("-").concat(event.params.to.toHexString())); + assert.fieldEquals("NameRegisteredTransactionVsRegistrant", nameRegisteredTransactionVsRegistrantId, "transactionHash", event.transaction.hash.toHexString()); + assert.fieldEquals("NameRegisteredTransactionVsRegistrant", nameRegisteredTransactionVsRegistrantId, "tokenId", event.params.tokenId.toString()); + assert.fieldEquals("NameRegisteredTransactionVsRegistrant", nameRegisteredTransactionVsRegistrantId, "createdAt", ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString())); + // AirBlock + assert.fieldEquals("AirBlock", blockId, "id", blockId); + assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); + assert.fieldEquals("AirBlock", blockId, "hash", event.block.hash.toHexString()); + assert.fieldEquals("AirBlock", blockId, "timestamp", event.block.timestamp.toString()); + // AirAccount + let oldRegistrant = ETHEREUM_MAINNET_ID.concat("-").concat(nameRegisteredEvent.params.owner.toHexString()); + assert.fieldEquals("AirAccount", oldRegistrant, "id", oldRegistrant); + assert.fieldEquals("AirAccount", oldRegistrant, "address", nameRegisteredEvent.params.owner.toHexString()); + assert.fieldEquals("AirAccount", oldRegistrant, "createdAt", blockId); + let newRegistrant = ETHEREUM_MAINNET_ID.concat("-").concat(event.params.to.toHexString()); + assert.fieldEquals("AirAccount", newRegistrant, "id", newRegistrant); + assert.fieldEquals("AirAccount", newRegistrant, "address", event.params.to.toHexString()); + assert.fieldEquals("AirAccount", newRegistrant, "createdAt", blockId); + // AirNameRegisteredTransaction + let nameRegisteredId = domainId.concat("-").concat(event.transaction.hash.toHexString()); + assert.fieldEquals("AirNameRegisteredTransaction", nameRegisteredId, "registrant", ETHEREUM_MAINNET_ID.concat("-").concat(event.params.to.toHexString())); + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") + }) }) \ No newline at end of file diff --git a/ens/tests/name-wrapper-utils.ts b/ens/tests/name-wrapper-utils.ts new file mode 100644 index 00000000..4ef530fc --- /dev/null +++ b/ens/tests/name-wrapper-utils.ts @@ -0,0 +1,37 @@ +import { ethereum, Address, Bytes, BigInt } from "@graphprotocol/graph-ts" +import { + NameWrapped, +} from "../generated/NameWrapper/NameWrapper"; +import { newMockEvent } from "matchstick-as" +import { getTransactionHash } from "./common-utils" + +export function createNameWrappedEvent(): NameWrapped { + let event = changetype(newMockEvent()) + + event.parameters = new Array() + event.block.number = BigInt.fromI32(10098239); + event.block.timestamp = BigInt.fromI32(2879823); + event.block.hash = Bytes.fromHexString("0x701633854b23364112e8528a85254a039abf8d1d81d629f88426196819e0b0b5") + event.transaction.hash = getTransactionHash() + event.logIndex = BigInt.fromI32(78) + event.transaction.from = Address.fromString("0x084b1c3c81545d370f3634392de611caabff8148") + event.transaction.value = BigInt.fromString("1000000000000000000") + + event.parameters.push( + new ethereum.EventParam("node", ethereum.Value.fromFixedBytes(Bytes.fromHexString("0xc44eec7fb870ae46d4ef4392d33fbbbdc164e7817a86289a1fe30e5f4d98ae85") as Bytes)) + ) + event.parameters.push( + new ethereum.EventParam("name", ethereum.Value.fromFixedBytes(Bytes.fromHexString("106669727374777261707065646E616D650365746800") as Bytes)) + ) + event.parameters.push( + new ethereum.EventParam("owner", ethereum.Value.fromAddress(Address.fromString("0x29a82E07B96c405aC99a8023F767D2971546DE70") as Address)) + ) + event.parameters.push( + new ethereum.EventParam("fuses", ethereum.Value.fromUnsignedBigInt(BigInt.fromString("196608"))) + ) + event.parameters.push( + new ethereum.EventParam("expiry", ethereum.Value.fromUnsignedBigInt(BigInt.fromString("1719447755"))) + ) + return event +} + diff --git a/ens/tests/name-wrapper.test.ts b/ens/tests/name-wrapper.test.ts new file mode 100644 index 00000000..07d0b8da --- /dev/null +++ b/ens/tests/name-wrapper.test.ts @@ -0,0 +1,43 @@ +import { + assert, + describe, + test, + clearStore, + afterEach, +} from "matchstick-as/assembly/index" +import { createNameWrappedEvent } from "./name-wrapper-utils" +import { handleNameWrapped } from "../src/name-wrapper" +import { ETHEREUM_MAINNET_ID } from "../modules/airstack/domain-name/utils" + +describe("Unit tests for resolver handlers", () => { + afterEach(() => { + clearStore() + }) + + test("Test handleNameWrapped", () => { + let event = createNameWrappedEvent(); + // call event handler + handleNameWrapped(event) + // assert here + let domainId = event.params.node.toHexString(); + let blockId = ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString()); + // AirMeta + assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") + assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") + assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") + // AirBlock + assert.fieldEquals("AirBlock", blockId, "id", blockId); + assert.fieldEquals("AirBlock", blockId, "number", event.block.number.toString()); + assert.fieldEquals("AirBlock", blockId, "hash", event.block.hash.toHexString()); + assert.fieldEquals("AirBlock", blockId, "timestamp", event.block.timestamp.toString()); + // AirDomain + assert.fieldEquals("AirDomain", domainId, "id", domainId) + assert.fieldEquals("AirDomain", domainId, "labelName", "firstwrappedname") + assert.fieldEquals("AirDomain", domainId, "name", "firstwrappedname.eth") + assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId) + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") + }) +}) + diff --git a/ens/tests/resolver-utils.ts b/ens/tests/resolver-utils.ts index b40fafce..a5a28ce8 100644 --- a/ens/tests/resolver-utils.ts +++ b/ens/tests/resolver-utils.ts @@ -3,7 +3,7 @@ import { ethereum, Address, Bytes, BigInt } from "@graphprotocol/graph-ts" import { AddrChanged, VersionChanged } from "../generated/Resolver1/Resolver" import { getTransactionHash } from "./common-utils" import { AirDomain } from "../generated/schema" -import { BIG_INT_ZERO } from "../modules/airstack/common/index" +import { BIGINT_ONE, BIG_INT_ZERO } from "../modules/airstack/common/index" export function getHandleAddrChangedEvent(): AddrChanged { return createHandleAddrChangedEvent( @@ -70,6 +70,7 @@ export function createAirDomain(domainId: string): AirDomain { entity.registrationCost = BIG_INT_ZERO; entity.createdAt = "1-472668903"; entity.lastUpdatedBlock = "1-472668903"; + entity.lastUpdatedIndex = BIGINT_ONE; entity.save(); return entity as AirDomain; } \ No newline at end of file diff --git a/ens/tests/resolver.test.ts b/ens/tests/resolver.test.ts index 007ebd3d..03e2b350 100644 --- a/ens/tests/resolver.test.ts +++ b/ens/tests/resolver.test.ts @@ -8,7 +8,6 @@ import { import { handleAddrChanged, handleVersionChanged } from "../src/resolver" import { createAirDomain, getHandleAddrChangedEvent, getHandleVersionChangedEvent } from "./resolver-utils" import { ETHEREUM_MAINNET_ID } from "../modules/airstack/domain-name/utils" -import { log } from "@graphprotocol/graph-ts" describe("Unit tests for resolver handlers", () => { afterEach(() => { @@ -28,10 +27,10 @@ describe("Unit tests for resolver handlers", () => { let blockId = ETHEREUM_MAINNET_ID.concat("-").concat(event.block.number.toString()); // AirMeta assert.fieldEquals("AirMeta", "AIR_META", "name", "ens") - assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens-v1") + assert.fieldEquals("AirMeta", "AIR_META", "slug", "ens_v1") assert.fieldEquals("AirMeta", "AIR_META", "version", "v1") assert.fieldEquals("AirMeta", "AIR_META", "schemaVersion", "1.0.0") - assert.fieldEquals("AirMeta", "AIR_META", "network", "MAINNET") + assert.fieldEquals("AirMeta", "AIR_META", "network", "mainnet") // AirResolver assert.fieldEquals("AirResolver", resolverId, "id", resolverId) assert.fieldEquals("AirResolver", resolverId, "domain", domainId) @@ -56,6 +55,7 @@ describe("Unit tests for resolver handlers", () => { // AirDomain assert.fieldEquals("AirDomain", domainId, "id", domainId) assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId) + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") assert.fieldEquals("AirDomain", domainId, "resolvedAddress", ETHEREUM_MAINNET_ID.concat("-").concat(event.params.a.toHexString())) // AirResolvedAddressChanged assert.fieldEquals("AirResolvedAddressChanged", addrChangedId, "resolver", resolverId) @@ -91,6 +91,7 @@ describe("Unit tests for resolver handlers", () => { assert.fieldEquals("AirDomain", domainId, "id", domainId) assert.fieldEquals("AirDomain", domainId, "resolvedAddress", "null") assert.fieldEquals("AirDomain", domainId, "lastUpdatedBlock", blockId) + assert.fieldEquals("AirDomain", domainId, "lastUpdatedIndex", "1") // AirResolver let resolverId = event.address.toHexString().concat("-").concat(event.params.node.toHexString()) assert.fieldEquals("AirResolver", resolverId, "id", resolverId) diff --git a/ens/tests/utils.test.ts b/ens/tests/utils.test.ts new file mode 100644 index 00000000..c94f6273 --- /dev/null +++ b/ens/tests/utils.test.ts @@ -0,0 +1,70 @@ +import { assert, describe, test } from "matchstick-as"; +import { Bytes } from "@graphprotocol/graph-ts"; +import { decodeName } from "../src/utils"; + +describe("Unit tests for util functions", () => { + test("test decodeName for firstwrappedname.eth", () => { + const nameBytes = Bytes.fromHexString("106669727374777261707065646E616D650365746800"); + const txHash = "0x0"; + const expectedName = "firstwrappedname.eth"; + // test function + let decoded = decodeName(nameBytes, txHash); + let label: string | null = null; + let name: string | null = null; + if (decoded !== null) { + label = decoded[0]; + name = decoded[1]; + } + // assert here + assert.stringEquals(label!, "firstwrappedname"); + assert.stringEquals(name!, expectedName); + }) + test("test decodeName for 0xacadian.eth", () => { + const nameBytes = Bytes.fromHexString("0930786163616469616E0365746800"); + const txHash = "0x0"; + const expectedName = "0xacadian.eth"; + // test function + let decoded = decodeName(nameBytes, txHash); + let label: string | null = null; + let name: string | null = null; + if (decoded !== null) { + label = decoded[0]; + name = decoded[1]; + } + // assert here + assert.stringEquals(label!, "0xacadian"); + assert.stringEquals(name!, expectedName); + }) + test("test decodeName for vault.im.kevindot.eth", () => { + const nameBytes = Bytes.fromHexString("057661756C7402696D086B6576696E646F740365746800"); + const txHash = "0x0"; + const expectedName = "vault.im.kevindot.eth"; + // test function + let decoded = decodeName(nameBytes, txHash); + let label: string | null = null; + let name: string | null = null; + if (decoded !== null) { + label = decoded[0]; + name = decoded[1]; + } + // assert here + assert.stringEquals(label!, "vault"); + assert.stringEquals(name!, expectedName); + }) + test("test decodeName for whatismyip.eth", () => { + const nameBytes = Bytes.fromHexString("0A7768617469736D7969700365746800"); + const txHash = "0x0"; + const expectedName = "whatismyip.eth"; + // test function + let decoded = decodeName(nameBytes, txHash); + let label: string | null = null; + let name: string | null = null; + if (decoded !== null) { + label = decoded[0]; + name = decoded[1]; + } + // assert here + assert.stringEquals(label!, "whatismyip"); + assert.stringEquals(name!, expectedName); + }) +}) \ No newline at end of file diff --git a/rarible-exchange-v1/abis/ERC721.json b/rarible-exchange-v1/abis/ERC721.json new file mode 100644 index 00000000..04d441bd --- /dev/null +++ b/rarible-exchange-v1/abis/ERC721.json @@ -0,0 +1,332 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "_approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] \ No newline at end of file diff --git a/rarible-exchange-v1/abis/ERC721Sale.json b/rarible-exchange-v1/abis/ERC721Sale.json new file mode 100644 index 00000000..c95577c3 --- /dev/null +++ b/rarible-exchange-v1/abis/ERC721Sale.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract TransferProxy","name":"_transferProxy","type":"address"},{"internalType":"contract ERC721SaleNonceHolder","name":"_nonceHolder","type":"address"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Cancel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"sellerFee","type":"uint256"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct AbstractSale.Sig","name":"signature","type":"tuple"}],"name":"buy","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"buyerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nonceHolder","outputs":[{"internalType":"contract ERC721SaleNonceHolder","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_beneficiary","type":"address"}],"name":"setBeneficiary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_buyerFee","type":"uint256"}],"name":"setBuyerFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transferProxy","outputs":[{"internalType":"contract TransferProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/rarible-exchange-v1/abis/Erc1155Sale1.json b/rarible-exchange-v1/abis/Erc1155Sale1.json new file mode 100644 index 00000000..13d4414c --- /dev/null +++ b/rarible-exchange-v1/abis/Erc1155Sale1.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract TransferProxy","name":"_transferProxy","type":"address"},{"internalType":"contract ERC1155SaleNonceHolder","name":"_nonceHolder","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"CloseOrder","type":"event"},{"constant":false,"inputs":[{"internalType":"contract IERC1155","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address payable","name":"owner","type":"address"},{"internalType":"uint256","name":"selling","type":"uint256"},{"internalType":"uint256","name":"buying","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buy","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancel","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"nonceHolder","outputs":[{"internalType":"contract ERC1155SaleNonceHolder","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"transferProxy","outputs":[{"internalType":"contract TransferProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/rarible-exchange-v1/abis/Erc1155Sale2.json b/rarible-exchange-v1/abis/Erc1155Sale2.json new file mode 100644 index 00000000..8160296e --- /dev/null +++ b/rarible-exchange-v1/abis/Erc1155Sale2.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract TransferProxy","name":"_transferProxy","type":"address"},{"internalType":"contract ERC1155SaleNonceHolder","name":"_nonceHolder","type":"address"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"CloseOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address payable","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC1155","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address payable","name":"owner","type":"address"},{"internalType":"uint256","name":"selling","type":"uint256"},{"internalType":"uint256","name":"buying","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"sellerFee","type":"uint256"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct AbstractSale.Sig","name":"signature","type":"tuple"}],"name":"buy","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"buyerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancel","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nonceHolder","outputs":[{"internalType":"contract ERC1155SaleNonceHolder","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_beneficiary","type":"address"}],"name":"setBeneficiary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_buyerFee","type":"uint256"}],"name":"setBuyerFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transferProxy","outputs":[{"internalType":"contract TransferProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/rarible-exchange-v1/abis/TokenSale.json b/rarible-exchange-v1/abis/TokenSale.json new file mode 100644 index 00000000..942265da --- /dev/null +++ b/rarible-exchange-v1/abis/TokenSale.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Cancel","type":"event"},{"constant":false,"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buy","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getPositionKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/rarible-exchange-v1/src/exchange-v-1.ts b/rarible-exchange-v1/src/exchange-v-1.ts index 8ad7d9fa..6138b7b5 100644 --- a/rarible-exchange-v1/src/exchange-v-1.ts +++ b/rarible-exchange-v1/src/exchange-v-1.ts @@ -1,8 +1,13 @@ -import { BigInt } from "@graphprotocol/graph-ts"; +import { Address, BigInt, dataSource, log } from "@graphprotocol/graph-ts"; import { ExchangeCall } from "../generated/ExchangeV1/ExchangeV1"; -import * as airstack from "../modules/airstack"; +import { BuyCall as BuyCall721, Buy as Buy721Sale } from "../generated/ERC721Sale1/ERC721Sale"; +import { BuyCall as BuyCall1155Sale1 } from "../generated/ERC1155Sale1/ERC1155Sale1"; +import { BuyCall as BuyCall1155Sale2 } from "../generated/ERC1155Sale2/ERC1155Sale2"; +import { Buy as BuyTokenSale721 } from "../generated/TokenSaleErc721/TokenSale"; +import * as airstack from "../modules/airstack/nft-marketplace"; import * as utils from "./utils"; -import { AirProtocolType, AirProtocolActionType, ETHEREUM_MAINNET_ID } from "./utils"; +import { AirProtocolType, AirProtocolActionType, ETHEREUM_MAINNET_ID, getProtocolFeeDetails } from "./utils"; +import { BIGINT_ONE } from "../modules/airstack/common"; export function handleExchange(call: ExchangeCall): void { let sellAsset = call.inputs.order.key.sellAsset; @@ -35,10 +40,10 @@ export function handleExchange(call: ExchangeCall): void { call.inputs.amount, ) - let nftSales = new airstack.nft.Sale( + let sale = new airstack.nft.Sale( call.from, call.inputs.order.key.owner, - nft, + [nft], paymentAmount, buyAsset.token, beneficiaryDetails.beneficiaryFee, @@ -47,15 +52,12 @@ export function handleExchange(call: ExchangeCall): void { ) airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, + call.block, call.transaction.hash.toHexString(), call.transaction.index, - [nftSales], + sale, AirProtocolType.NFT_MARKET_PLACE, AirProtocolActionType.BUY, - call.block.timestamp, - call.block.number, - call.block.hash.toHexString(), ); } else { @@ -85,10 +87,10 @@ export function handleExchange(call: ExchangeCall): void { call.inputs.amount, ) - let nftSales = new airstack.nft.Sale( + let sale = new airstack.nft.Sale( call.inputs.order.key.owner, call.from, - nft, + [nft], paymentAmount, sellAsset.token, beneficiaryDetails.beneficiaryFee, @@ -97,15 +99,187 @@ export function handleExchange(call: ExchangeCall): void { ) airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, + call.block, call.transaction.hash.toHexString(), call.transaction.index, - [nftSales], + sale, AirProtocolType.NFT_MARKET_PLACE, AirProtocolActionType.SELL, - call.block.timestamp, - call.block.number, - call.block.hash.toHexString(), ); } } + +export function handleBuyTokenSaleErc721(event: BuyTokenSale721): void { + const sale = new airstack.nft.Sale( + event.params.buyer, + event.params.seller, + [new airstack.nft.NFT( + event.params.token, + "ERC721", + event.params.tokenId, + BIGINT_ONE, + )], + event.transaction.value, + utils.zeroAddress, + utils.BIGINT_ZERO, + utils.zeroAddress, + new Array(), + ); + airstack.nft.trackNFTSaleTransactions( + event.block, + event.transaction.hash.toHexString(), + event.logIndex, + sale, + AirProtocolType.NFT_MARKET_PLACE, + AirProtocolActionType.BUY, + ); + log.info("handleBuyTokenSaleErc721 price {} token {} tokenId {} buyer {} seller {} exchange {} txhash {}", [ + event.params.price.toString(), + event.params.token.toHexString(), + event.params.tokenId.toString(), + event.params.buyer.toHexString(), + event.params.seller.toHexString(), + dataSource.address().toHexString(), + event.transaction.hash.toHexString(), + ]); +} + +export function handleBuyErc1155Sale1(call: BuyCall1155Sale1): void { + const royalties = utils.getRoyaltyDetailsErc1155Sale1( + call.inputs.tokenId, + call.inputs.token, + call.transaction.value, + ); + const sale = new airstack.nft.Sale( + call.transaction.from, + call.inputs.owner, + [new airstack.nft.NFT( + call.inputs.token, + "ERC1155", + call.inputs.tokenId, + call.inputs.buying, + )], + call.transaction.value, + utils.zeroAddress, + utils.BIGINT_ZERO, + utils.zeroAddress, + royalties, + ); + airstack.nft.trackNFTSaleTransactions( + call.block, + call.transaction.hash.toHexString(), + call.transaction.index, + sale, + AirProtocolType.NFT_MARKET_PLACE, + AirProtocolActionType.BUY, + ); + log.info("handleBuyErc1155Sale1 total {} token {} tokenId {} buyer {} seller {} royalty lenght {} exchange {} txhash {}", [ + call.transaction.value.toString(), + call.inputs.token.toHexString(), + call.inputs.tokenId.toString(), + call.transaction.from.toHexString(), + call.inputs.owner.toHexString(), + royalties.length.toString(), + dataSource.address().toHexString(), + call.transaction.hash.toHexString(), + ]); +} + +export function handleBuyErc1155Sale2(call: BuyCall1155Sale2): void { + const total = call.inputs.price.times(call.inputs.buying); + const protocolFeeDetails = getProtocolFeeDetails( + dataSource.address(), + total, + call.inputs.sellerFee, + ); + const royalties = utils.getRoyaltyDetails( + call.inputs.tokenId, + call.inputs.token, + protocolFeeDetails.restValue, + total + ); + const sale = new airstack.nft.Sale( + call.transaction.from, + call.inputs.owner, + [new airstack.nft.NFT( + call.inputs.token, + "ERC1155", + call.inputs.tokenId, + call.inputs.buying, + )], + call.transaction.value, + utils.zeroAddress, + protocolFeeDetails.beneficiaryFee, + protocolFeeDetails.beneficiary, + royalties, + ); + airstack.nft.trackNFTSaleTransactions( + call.block, + call.transaction.hash.toHexString(), + call.transaction.index, + sale, + AirProtocolType.NFT_MARKET_PLACE, + AirProtocolActionType.BUY, + ); + log.info("handleBuyErc1155Sale2 price {} sellerFee {} token {} tokenId {} buyer {} seller {} royalty length {} protocolfee {} protocol beneficiary {} exchange {} txhash {}", [ + call.inputs.price.toString(), + call.inputs.sellerFee.toString(), + call.inputs.token.toHexString(), + call.inputs.tokenId.toString(), + call.transaction.from.toHexString(), + call.inputs.owner.toHexString(), + royalties.length.toString(), + protocolFeeDetails.beneficiaryFee.toString(), + protocolFeeDetails.beneficiary.toHexString(), + dataSource.address().toHexString(), + call.transaction.hash.toHexString(), + ]); +} + +export function handleBuyErc721(event: Buy721Sale): void { + const protocolFeeDetails = getProtocolFeeDetails( + dataSource.address(), + event.params.price, + utils.BIGINT_ZERO, + ); + const royalties = utils.getRoyaltyDetails( + event.params.tokenId, + event.params.token, + protocolFeeDetails.restValue, + event.params.price, + ); + const sale = new airstack.nft.Sale( + event.params.buyer, + event.params.seller, + [new airstack.nft.NFT( + event.params.token, + "ERC721", + event.params.tokenId, + BIGINT_ONE, + )], + event.transaction.value, + utils.zeroAddress, + protocolFeeDetails.beneficiaryFee, + protocolFeeDetails.beneficiary, + royalties, + ); + airstack.nft.trackNFTSaleTransactions( + event.block, + event.transaction.hash.toHexString(), + event.logIndex, + sale, + AirProtocolType.NFT_MARKET_PLACE, + AirProtocolActionType.BUY, + ); + log.info("handleBuyErc721 token {} tokenId {} buyer {} seller {} royalty length {} protocolfee {} protocol beneficiary {} exchange {} txhash {}", [ + event.params.token.toHexString(), + event.params.tokenId.toString(), + event.params.buyer.toHexString(), + event.params.seller.toHexString(), + royalties.length.toString(), + protocolFeeDetails.beneficiaryFee.toString(), + protocolFeeDetails.beneficiary.toHexString(), + dataSource.address().toHexString(), + event.transaction.hash.toHexString(), + ]); +} \ No newline at end of file diff --git a/rarible-exchange-v1/src/utils.ts b/rarible-exchange-v1/src/utils.ts index 6077bcb3..4186447c 100644 --- a/rarible-exchange-v1/src/utils.ts +++ b/rarible-exchange-v1/src/utils.ts @@ -2,12 +2,10 @@ import { Address, BigInt, Bytes, - ethereum, - log, - TypedMap, - crypto, } from "@graphprotocol/graph-ts"; import { ExchangeV1 } from "../generated/ExchangeV1/ExchangeV1"; +import { ERC721 } from "../generated/ERC721Sale1/ERC721"; +import { ERC721Sale } from "../generated/ERC721Sale1/ERC721Sale"; import { SecondarySaleFees } from "../generated/ExchangeV1/SecondarySaleFees"; import { nft } from "../modules/airstack"; @@ -166,3 +164,120 @@ export function getFeeBeneficiaryDetails( restValue: subFeeInBpResponse.newValue, }; } + +/** + * @dev this function is used to get token owner of an erc721 token + * @param tokenAddress erc721 token contract address + * @param tokenId erc721 token id + * @returns + */ +export function getTokenOwnerErc721( + tokenAddress: Address, + tokenId: BigInt, +): Address { + let contractInstance = ERC721.bind(tokenAddress); + let ownerResult = contractInstance.try_ownerOf(tokenId); + if (!ownerResult.reverted) { + return ownerResult.value; + } else { + return zeroAddress; + } +} + +/** + * @dev this function is used to get protocol fee details of erc721 token + * @param exchangeAddress exchange contract address + * @param total transaction value amount + * @param sellerFee seller fee in basis point + * @returns protocol beneficiary fee details + */ +export function getProtocolFeeDetails( + exchangeAddress: Address, + total: BigInt, + sellerFee: BigInt, +): BeneficiaryDetails { + let subFeeInBpResponse = subFee(total, bp(total, sellerFee)); + const buyerFeeAndBeneficiaryAddress = getBuyerFeeAndBeneficiaryAddressFromExchange(exchangeAddress); + const buyerFeeValue = bp(total, buyerFeeAndBeneficiaryAddress.buyerFee); + const beneficiaryFee = buyerFeeValue.plus(subFeeInBpResponse.realFee); + return { + beneficiaryFee, + beneficiary: buyerFeeAndBeneficiaryAddress.beneficiary, + restValue: subFeeInBpResponse.newValue, + } +} + +/** + * dev this class has beneficiary and buyer fee details + */ +class BuyerAndBeneficiaryAddress { + beneficiary: Address; + buyerFee: BigInt; +} + +/** + * @dev this function is used to get buyer fee and protocol beneficiary address from exchange contract + * @param exchangeAddress exchange contract address + * @returns buyer and beneficiary address + */ +export function getBuyerFeeAndBeneficiaryAddressFromExchange( + exchangeAddress: Address, +): BuyerAndBeneficiaryAddress { + let contractInstance = ERC721Sale.bind(exchangeAddress); + let beneficiaryResult = contractInstance.try_beneficiary(); + let buyerFeeResult = contractInstance.try_buyerFee(); + + let beneficiary: Address; + let buyerFee: BigInt; + + if (!beneficiaryResult.reverted) { + beneficiary = beneficiaryResult.value; + } else { + beneficiary = zeroAddress; + } + + if (!buyerFeeResult.reverted) { + buyerFee = buyerFeeResult.value; + } else { + buyerFee = BIGINT_ZERO; + } + + return { + beneficiary, + buyerFee, + }; +} + +/** + * @dev this function is used to get royalty details for erc1155Sale1 transaction + * @param tokenId erc1155 token id + * @param tokenAddress erc1155 token contract address + * @param total total payment amount of payment asset + * @returns creator royalty details + */ +export function getRoyaltyDetailsErc1155Sale1( + tokenId: BigInt, + tokenAddress: Address, + total: BigInt, +): nft.CreatorRoyalty[] { + // extract data from contract logic comes here + let contractInstance = SecondarySaleFees.bind(tokenAddress); + let supportsInterface = contractInstance.try_supportsInterface(INTERFACE_ID_FEES); + let creatorRoyalties: nft.CreatorRoyalty[] = []; + + if (!supportsInterface.reverted && supportsInterface.value) { + let royaltyRecipients = contractInstance.getFeeRecipients(tokenId); + let royaltyAmounts = contractInstance.getFeeBps(tokenId); + for (let i = 0; i < royaltyAmounts.length; i++) { + let royaltyFee = bp(total, royaltyAmounts[i]); + creatorRoyalties.push( + new nft.CreatorRoyalty( + royaltyFee, + royaltyRecipients[i], + ) + ); + total = total.minus(royaltyFee); + } + }; + return creatorRoyalties; +} \ No newline at end of file diff --git a/rarible-exchange-v1/subgraph.yaml b/rarible-exchange-v1/subgraph.yaml index bda4efb2..ddc13337 100644 --- a/rarible-exchange-v1/subgraph.yaml +++ b/rarible-exchange-v1/subgraph.yaml @@ -36,3 +36,114 @@ dataSources: - function: exchange(((address,uint256,(address,uint256,uint8),(address,uint256,uint8)),uint256,uint256,uint256),(uint8,bytes32,bytes32),uint256,(uint8,bytes32,bytes32),uint256,address) handler: handleExchange file: ./src/exchange-v-1.ts + - kind: ethereum + name: ERC721Sale1 + network: mainnet + source: + address: '0x131aebbfe55bca0c9eaad4ea24d386c5c082dd58' + abi: ERC721Sale + startBlock: 10786971 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Erc721saleTempMapping + abis: + - name: ERC721Sale + file: ./abis/ERC721Sale.json + - name: ERC721 + file: ./abis/ERC721.json + - name: SecondarySaleFees + file: ./abis/SecondarySaleFees.json + eventHandlers: + - event: Buy(indexed address,indexed uint256,address,address,uint256,uint256) + handler: handleBuyErc721 + file: ./src/exchange-v-1.ts + - kind: ethereum + name: ERC721Sale2 + network: mainnet + source: + address: '0xa5af48b105ddf2fa73cbaac61d420ea31b3c2a07' + abi: ERC721Sale + startBlock: 10147841 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Erc721saleTempMapping + abis: + - name: ERC721Sale + file: ./abis/ERC721Sale.json + - name: ERC721 + file: ./abis/ERC721.json + - name: SecondarySaleFees + file: ./abis/SecondarySaleFees.json + eventHandlers: + - event: Buy(indexed address,indexed uint256,address,address,uint256,uint256) + handler: handleBuyErc721 + file: ./src/exchange-v-1.ts + - kind: ethereum + name: ERC1155Sale1 + network: mainnet + source: + address: '0x8c530a698b6e83d562db09079bc458d4dad4e6c5' + abi: ERC1155Sale1 + startBlock: 10147909 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + abis: + - name: ERC1155Sale1 + file: ./abis/ERC1155Sale1.json + - name: SecondarySaleFees + file: ./abis/SecondarySaleFees.json + callHandlers: + - function: buy(address,uint256,address,uint256,uint256,uint8,bytes32,bytes32) + handler: handleBuyErc1155Sale1 + file: ./src/exchange-v-1.ts + - kind: ethereum + name: ERC1155Sale2 + network: mainnet + source: + address: '0x93f2a75d771628856f37f256da95e99ea28aafbe' + abi: ERC1155Sale2 + startBlock: 10786886 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + abis: + - name: ERC1155Sale2 + file: ./abis/ERC1155Sale2.json + - name: ERC721Sale + file: ./abis/ERC721Sale.json + - name: SecondarySaleFees + file: ./abis/SecondarySaleFees.json + callHandlers: + - function: buy(address,uint256,address,uint256,uint256,uint256,uint256,(uint8,bytes32,bytes32)) + handler: handleBuyErc1155Sale2 + file: ./src/exchange-v-1.ts + - kind: ethereum + name: TokenSaleErc721 + network: mainnet + source: + address: '0xf2ee97405593bc7b6275682b0331169a48fedec7' + abi: TokenSale + startBlock: 8803300 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + abis: + - name: TokenSale + file: ./abis/TokenSale.json + eventHandlers: + - event: Buy(indexed address,indexed uint256,address,address,uint256,uint256) + handler: handleBuyTokenSaleErc721 + file: ./src/exchange-v-1.ts diff --git a/rarible-exchange-v2/abis/Erc721GenFactory.json b/rarible-exchange-v2/abis/Erc721GenFactory.json new file mode 100644 index 00000000..a2226825 --- /dev/null +++ b/rarible-exchange-v2/abis/Erc721GenFactory.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_implementation","type":"address"},{"internalType":"address","name":"_transferProxy","type":"address"},{"internalType":"address","name":"_operatorProxy","type":"address"},{"internalType":"string","name":"_baseURI","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"CollectionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"changeBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"changeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"components":[{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint96","name":"value","type":"uint96"}],"internalType":"struct LibPart.Part[]","name":"_royalties","type":"tuple[]"},{"components":[{"internalType":"uint256[]","name":"rarities","type":"uint256[]"}],"internalType":"struct Traits.Trait[]","name":"_traits","type":"tuple[]"},{"internalType":"uint256","name":"_total","type":"uint256"},{"internalType":"uint256","name":"_maxValue","type":"uint256"}],"name":"createCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operatorProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/rarible-exchange-v2/schema.graphql b/rarible-exchange-v2/schema.graphql index 9492d020..e95a1b02 100644 --- a/rarible-exchange-v2/schema.graphql +++ b/rarible-exchange-v2/schema.graphql @@ -113,31 +113,19 @@ interface AirTransaction { protocolActionType: AirProtocolActionType! } -type AirNftTransaction implements AirTransaction @entity { +type Erc721GenCollection @entity { id: ID! - from: AirAccount! - to: AirAccount! - hash: String! - block: AirBlock! - index: BigInt! - protocolType: AirProtocolType! - protocolActionType: AirProtocolActionType! - tokenId: BigInt! #nft - tokenAmount:BigInt! #nft - transactionToken: AirToken! #nft - paymentToken: AirToken #payment - paymentAmount: BigInt #payment - royalties: [AirNftSaleRoyalty!] @derivedFrom(field: "nftTransaction") - feeAmount: BigInt - feeBeneficiary: AirAccount - extraData: AirExtraData + contractAddress: String! + transactionHash: String! + createdAtBlock: BigInt! } -type AirNftSaleRoyalty @entity{ - id: ID! #AirNftTransaction(ID) + royalty beneficiary - amount: BigInt! - beneficiary: AirAccount! - nftTransaction: AirNftTransaction! +type TxnHashVsTokenIdMapping @entity { + id: ID! + transactionHash: String! + tokenIds: [BigInt!] + createdAtBlock: BigInt! + updatedAtBlock: BigInt! } type AirExtraData @entity { diff --git a/rarible-exchange-v2/src/erc-721-gen.ts b/rarible-exchange-v2/src/erc-721-gen.ts new file mode 100644 index 00000000..2475e620 --- /dev/null +++ b/rarible-exchange-v2/src/erc-721-gen.ts @@ -0,0 +1,34 @@ +import { GenArtMint } from '../generated/templates/ERC721Gen/ERC721Gen' +import { CollectionCreated } from '../generated/ERC721GenFactory/ERC721GenFactory' +import { GenArtMintEvent, Erc721GenCollection } from '../generated/schema' +import { ERC721Gen } from '../generated/templates' +import { createTxnHashVsTokenIdMapping } from './utils'; + +export function handleGenArtMint(event: GenArtMint): void { + const id = event.address.toHexString().concat("-").concat(event.params.tokenId.toString()); + let genArtEvent = new GenArtMintEvent(id); + genArtEvent.tokenId = event.params.tokenId.toString(); + genArtEvent.contractAddress = event.address.toHexString(); + genArtEvent.transactionHash = event.transaction.hash.toHexString(); + genArtEvent.createdAtBlock = event.block.number; + genArtEvent.save(); + + // create the txn hash vs token id mapping + createTxnHashVsTokenIdMapping( + event.transaction.hash, + event.params.tokenId, + event.block, + ) +} + +export function handleCollectionCreated(event: CollectionCreated): void { + let collection = new Erc721GenCollection(event.transaction.hash.toHexString()); + collection.contractAddress = event.params.collection.toHexString(); + collection.transactionHash = event.transaction.hash.toHexString(); + collection.createdAtBlock = event.block.number; + collection.save(); + + // dynamically create a new datasource for the newly created collection + // this is done so that we can listen to the events of the newly created collection + ERC721Gen.create(event.params.collection); +} \ No newline at end of file diff --git a/rarible-exchange-v2/src/exchange-v-2.ts b/rarible-exchange-v2/src/exchange-v-2.ts index d3eafedf..f1be375e 100644 --- a/rarible-exchange-v2/src/exchange-v-2.ts +++ b/rarible-exchange-v2/src/exchange-v-2.ts @@ -1,4 +1,4 @@ -import { BigInt, dataSource, log } from "@graphprotocol/graph-ts"; +import { Bytes, BigInt, dataSource, log } from "@graphprotocol/graph-ts"; import { DirectAcceptBidCall, DirectPurchaseCall, MatchOrdersCall } from "../generated/ExchangeV2/ExchangeV2"; import { ETH, @@ -9,16 +9,15 @@ import { AirProtocolType, zeroAddress, BIGINT_ZERO, - getOriginFeeArray, matchAndTransfer, LibOrder, - LibDealSide, LibAsset, LibAssetType, getPaymentAssetType, getOtherOrderType, generateOrderData, - ETHEREUM_MAINNET_ID, + BIGINT_MINUS_ONE, + RANDOM_MINT_721, } from "./utils"; import * as airstack from "../modules/airstack"; @@ -45,7 +44,23 @@ export function handleMatchOrders(call: MatchOrdersCall): void { if (leftAssetType == ETH || leftAssetType == ERC20) { // rightAsset is NFT - // log.info("{} {} {} {} address id and data and hash leftasset is nft", [rightAsset.address.toHexString(), rightAsset.id.toString(), orderRight.makeAsset.assetType.data.toHexString(), transactionHash.toHexString()]); + log.info("{} {} {} {} address id and type and hash rightAsset is nft", [rightAsset.address.toHexString(), rightAsset.id.toString(), rightAssetType, transactionHash.toHexString()]); + + let orderData = generateOrderData(orderLeft, orderRight, transactionHash); + + let matchAndTransferResult = matchAndTransfer(orderData.orderLeftInput, orderData.orderRightInput, call.from, dataSource.address(), transactionHash, true); + log.info("{} {} match and transfer result for rightasset is nft transaction hash {}", [matchAndTransferResult.originFee.value.toString(), matchAndTransferResult.payment.toString(), transactionHash.toHexString()]); + + let nftTokenId = matchAndTransferResult.nftData.tokenId; + + if (nftTokenId == BIGINT_MINUS_ONE && getClass(Bytes.fromHexString(matchAndTransferResult.nftData.standard)) == RANDOM_MINT_721) { + // assign tokenId from mapping + const txnHashVsTokenIdMapping = TxnHashVsTokenIdMapping.load(call.transaction.hash.toHexString()); + if (txnHashVsTokenIdMapping != null && txnHashVsTokenIdMapping.tokenIds != null) { + nftTokenId = txnHashVsTokenIdMapping.tokenIds![0]; + log.info("assigning tokenId from mapping {} txnhash {} nftcollection {}", [nftTokenId.toString(), call.transaction.hash.toHexString(), leftAsset.address.toHexString()]); + } + } let nft = new airstack.nft.NFT( rightAsset.address, @@ -69,10 +84,20 @@ export function handleMatchOrders(call: MatchOrdersCall): void { royalties.push(royalty); } - let nftSales = new airstack.nft.Sale( - orderLeft.maker, //to + let toAddress = orderLeft.maker; + + if (toAddress.toHexString() == zeroAddress.toHexString()) { + // get to address from paymentside.payouts array + if (matchAndTransferResult.paymentSidePayouts.length > 0) { + toAddress = matchAndTransferResult.paymentSidePayouts[0].address; + log.info("txhash {} to address from payment side {} payoutArrayLength {}", [transactionHash.toHexString(), toAddress.toHexString(), matchAndTransferResult.paymentSidePayouts.length.toString()]); + } + } + + let sale = new airstack.nft.Sale( + toAddress, //to orderRight.maker, //from - nft, //nft + [nft], //nft matchAndTransferResult.payment, //payment amount leftAsset.address, //payment token matchAndTransferResult.originFee.value, //protocol fees @@ -81,34 +106,62 @@ export function handleMatchOrders(call: MatchOrdersCall): void { ) airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, + call.block, transactionHash.toHexString(), call.transaction.index, - [nftSales], + sale, AirProtocolType.NFT_MARKET_PLACE, AirProtocolActionType.SELL, - call.block.timestamp, - call.block.number, - call.block.hash.toHexString() ); } else { // leftAsset is NFT - // log.info("{} {} {} {} address id and data and hash leftasset is nft", [leftAsset.address.toHexString(), leftAsset.id.toString(), orderLeft.makeAsset.assetType.data.toHexString(), transactionHash.toHexString()]); + log.info("{} {} {} {} address id and type and hash leftasset is nft", [leftAsset.address.toHexString(), leftAsset.id.toString(), leftAssetType, transactionHash.toHexString()]); - let nft = new airstack.nft.NFT( - leftAsset.address, - leftAssetType, - leftAsset.id, - orderRight.takeAsset.value, - ) + let orderData = generateOrderData(orderLeft, orderRight, transactionHash); - let orderData = generateOrderData(orderLeft, orderRight, false, transactionHash); + let matchAndTransferResult = matchAndTransfer(orderData.orderLeftInput, orderData.orderRightInput, call.from, dataSource.address(), transactionHash, true); + log.info("{} {} match and transfer result for leftasset is nft transaction hash {}", [matchAndTransferResult.originFee.value.toString(), matchAndTransferResult.payment.toString(), transactionHash.toHexString()]); - let matchAndTransferResult = matchAndTransfer(orderData.paymentSide, orderData.nftSide, orderData.orderLeftInput, orderData.orderRightInput, call.from, dataSource.address(), transactionHash, true); - // log.info("{} {} match and transfer result for leftasset transaction hash {}", [matchAndTransferResult.originFee.value.toString(), matchAndTransferResult.payment.toString(), transactionHash.toHexString()]); + let toAddress = orderRight.maker; - let royalties: airstack.nft.CreatorRoyalty[] = []; + if (toAddress.toHexString() == zeroAddress.toHexString()) { + // get to address from paymentside.payouts array + if (matchAndTransferResult.paymentSidePayouts.length > 0) { + toAddress = matchAndTransferResult.paymentSidePayouts[0].address; + log.info("txhash {} to address from payment side {} payoutArrayLength {}", [transactionHash.toHexString(), toAddress.toHexString(), matchAndTransferResult.paymentSidePayouts.length.toString()]); + } + } + let nftTokenId = matchAndTransferResult.nftData.tokenId; + + let nfts = new Array(); + // handling random 721 mint case with mutiple tokenIds + if (nftTokenId == BIGINT_MINUS_ONE && getClass(Bytes.fromHexString(matchAndTransferResult.nftData.standard)) == RANDOM_MINT_721) { + log.info("its a random721 mint case txhash {}", [transactionHash.toHexString()]); + // get tokenIds from mapping + const txnHashVsTokenIdMapping = TxnHashVsTokenIdMapping.load(call.transaction.hash.toHexString()); + if (txnHashVsTokenIdMapping && txnHashVsTokenIdMapping.tokenIds) { + for (var i = 0; i < txnHashVsTokenIdMapping.tokenIds!.length; i++) { + nftTokenId = txnHashVsTokenIdMapping.tokenIds![i]; + log.info("txnHashVsTokenIdMapping tokenId {} txnhash {} nftcollection {}", [nftTokenId.toString(), call.transaction.hash.toHexString(), leftAsset.address.toHexString()]); + let nft = new airstack.nft.NFT( + leftAsset.address, + matchAndTransferResult.nftData.standard, + nftTokenId, + matchAndTransferResult.nftData.amount.div(BigInt.fromI32(txnHashVsTokenIdMapping.tokenIds!.length)), + ) + nfts.push(nft); + } + } + } else { //handling other normal cases + nfts = [new airstack.nft.NFT( + leftAsset.address, + matchAndTransferResult.nftData.standard, + nftTokenId, + matchAndTransferResult.nftData.amount, + )]; + } + let royalties: airstack.nft.CreatorRoyalty[] = []; for (let i = 0; i < matchAndTransferResult.royalty.length; i++) { let royalty = new airstack.nft.CreatorRoyalty( matchAndTransferResult.royalty[i].value, @@ -116,11 +169,10 @@ export function handleMatchOrders(call: MatchOrdersCall): void { ); royalties.push(royalty); } - - let nftSales = new airstack.nft.Sale( - orderRight.maker, //to + let sale = new airstack.nft.Sale( + toAddress, //to orderLeft.maker, //from - nft, + nfts, matchAndTransferResult.payment, //payment amount rightAsset.address, //payment token matchAndTransferResult.originFee.value, //protocol fees @@ -129,15 +181,12 @@ export function handleMatchOrders(call: MatchOrdersCall): void { ) airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, + call.block, transactionHash.toHexString(), call.transaction.index, - [nftSales], + sale, AirProtocolType.NFT_MARKET_PLACE, AirProtocolActionType.BUY, - call.block.timestamp, - call.block.number, - call.block.hash.toHexString() ); } } @@ -237,10 +286,10 @@ export function handleDirectAcceptBid(call: DirectAcceptBidCall): void { royalties.push(royalty); } - let nftSales = new airstack.nft.Sale( + let sale = new airstack.nft.Sale( direct.bidMaker, //to call.from, //from - nft, + [nft], matchAndTransferResult.payment, //payment amount direct.paymentToken, //payment token matchAndTransferResult.originFee.value, //protocol fees @@ -249,15 +298,12 @@ export function handleDirectAcceptBid(call: DirectAcceptBidCall): void { ) airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, + call.block, transactionHash.toHexString(), call.transaction.index, - [nftSales], + sale, AirProtocolType.NFT_MARKET_PLACE, AirProtocolActionType.BUY, - call.block.timestamp, - call.block.number, - call.block.hash.toHexString() ); } @@ -359,10 +405,10 @@ export function handleDirectPurchase(call: DirectPurchaseCall): void { royalties.push(royalty); } - let nftSales = new airstack.nft.Sale( + let sale = new airstack.nft.Sale( call.from, //to direct.sellOrderMaker, //from - nft, + [nft], matchAndTransferResult.payment, //payment amount direct.paymentToken, //payment token matchAndTransferResult.originFee.value, //protocol fees @@ -371,14 +417,11 @@ export function handleDirectPurchase(call: DirectPurchaseCall): void { ) airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, + call.block, transactionHash.toHexString(), call.transaction.index, - [nftSales], + sale, AirProtocolType.NFT_MARKET_PLACE, AirProtocolActionType.BUY, - call.block.timestamp, - call.block.number, - call.block.hash.toHexString() ); } \ No newline at end of file diff --git a/rarible-exchange-v2/src/utils.ts b/rarible-exchange-v2/src/utils.ts index 0258e254..3f30e1d1 100644 --- a/rarible-exchange-v2/src/utils.ts +++ b/rarible-exchange-v2/src/utils.ts @@ -165,6 +165,30 @@ export class Asset { } } +/** + * @dev this is a temp mapping for txn hash and tokenId + * @param transactionHash transaction hash + * @param tokenId nft token id + */ +export function createTxnHashVsTokenIdMapping(transactionHash: Bytes, tokenId: BigInt, block: ethereum.Block): void { + let txnHashVsTokenId = TxnHashVsTokenIdMapping.load(transactionHash.toHexString()); + if (txnHashVsTokenId == null) { + txnHashVsTokenId = new TxnHashVsTokenIdMapping(transactionHash.toHexString()); + txnHashVsTokenId.transactionHash = transactionHash.toHexString(); + let tokenIds = new Array(); + tokenIds.push(tokenId); + txnHashVsTokenId.tokenIds = tokenIds; + txnHashVsTokenId.createdAtBlock = block.number; + txnHashVsTokenId.updatedAtBlock = block.number; + } else { + let temp = txnHashVsTokenId.tokenIds!; + temp.push(tokenId); + txnHashVsTokenId.tokenIds = temp; + txnHashVsTokenId.updatedAtBlock = block.number; + } + txnHashVsTokenId.save(); +} + /** * @dev decodes asset data * @param data encoded asset data diff --git a/rarible-exchange-v2/subgraph.yaml b/rarible-exchange-v2/subgraph.yaml index af28d2c1..53e2e21d 100644 --- a/rarible-exchange-v2/subgraph.yaml +++ b/rarible-exchange-v2/subgraph.yaml @@ -40,3 +40,42 @@ dataSources: - function: directPurchase((address,uint256,bytes4,bytes,uint256,address,uint256,uint256,uint256,bytes4,bytes,bytes,uint256,uint256,bytes)) handler: handleDirectPurchase file: ./src/exchange-v-2.ts + - kind: ethereum + name: ERC721GenFactory + network: mainnet + source: + address: '0xB19E3E12854CE643f29B551c7F40cf0FA87341De' + abi: ERC721GenFactory + startBlock: 13360525 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Erc721GenFactory + abis: + - name: ERC721GenFactory + file: ./abis/ERC721GenFactory.json + eventHandlers: + - event: CollectionCreated(address,address,address) + handler: handleCollectionCreated + file: ./src/erc-721-gen.ts +templates: + - kind: ethereum + name: ERC721Gen + network: mainnet + source: + abi: ERC721Gen + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - TxnHashVsTokenIdMapping + abis: + - name: ERC721Gen + file: ./abis/ERC721Gen.json + eventHandlers: + - event: GenArtMint(indexed uint256,uint256) + handler: handleGenArtMint + file: ./src/erc-721-gen.ts diff --git a/seaport-subgraph/schema.graphql b/seaport-subgraph/schema.graphql index c0d7a253..29e1b9c6 100644 --- a/seaport-subgraph/schema.graphql +++ b/seaport-subgraph/schema.graphql @@ -1,8 +1,30 @@ -type WrappedEtherTransaction @entity { +type PartialRoyalty @entity { id: ID! + amount: BigInt! + beneficiary: String! +} +type PartialNft @entity { + id: ID! # hash+nftCount + standard: String! + tokenId: BigInt! + tokenAmount: BigInt! #nft + transactionToken: String! #nft +} +type PartialNftTransaction @entity { + id: ID! + hash: String! from: String! to: String! - hash: String! + paymentToken: String! #payment + totalOfferAmount: BigInt! #payment + totalAmountToSeller: BigInt! # amount to address actually sends to from + totalPaymentAmount: BigInt! + totalFeeAmount: BigInt! + feeBeneficiary: String! + isComplete: Boolean! # used to figure out whether we got all data or wait for the other corresponding event + totalRoyalty: BigInt! + royaltyCount: BigInt! # used to generate keys + partialNFT: [PartialNft!]! } # diff --git a/seaport-subgraph/src/seaport.ts b/seaport-subgraph/src/seaport.ts index adc49efc..a057ac7c 100644 --- a/seaport-subgraph/src/seaport.ts +++ b/seaport-subgraph/src/seaport.ts @@ -1,242 +1,422 @@ -import { Address, BigDecimal, BigInt, log } from "@graphprotocol/graph-ts"; - +import { Address, BigDecimal, BigInt, Bytes, log } from "@graphprotocol/graph-ts" +import { OrderFulfilled } from "../generated/Seaport/Seaport" import { - OrderFulfilled, -} from "../generated/Seaport/Seaport" -import { isERC1155, isERC721, isOpenSeaFeeAccount, NftStandard, ETHEREUM_MAINNET_ID, TRANSACTION_TYPE_SALE, MARKET_PLACE_TYPE, PROTOCOL_SELL_ACTION_TYPE } from "./utils"; - -import * as airstack from "../modules/airstack"; -import { WrappedEtherTransaction } from "../generated/schema"; - -export enum ItemType { - NATIVE = 0, - ERC20 = 1, - ERC721 = 2, - ERC1155 = 3, - ERC721_WITH_CRITERIA = 4, - ERC1155_WITH_CRITERIA = 5, + isOpenSeaFeeAccount, + ETHEREUM_MAINNET_ID, + MARKET_PLACE_TYPE, + PROTOCOL_SELL_ACTION_TYPE, + BIGINT_ZERO, + isNFTEntity, + getNFTStandard, +} from "./utils" +import * as airstack from "../modules/airstack/nft-marketplace" +import { PartialRoyalty, PartialNftTransaction, PartialNft } from "../generated/schema" +import { BIGINT_ONE, BIG_INT_ZERO } from "../modules/airstack/common" +export function handleOrderFulfilled(event: OrderFulfilled): void { + let isPartialEvent = event.params.recipient == Address.zero() + if (isPartialEvent) { + handlePartialEvent(event) + } else { + handleCompleteEvent(event) + } } +// one event will have complete info. about transaction +function handleCompleteEvent(event: OrderFulfilled): void { + let txHash = event.transaction.hash + let offerer = event.params.offerer + let recipient = event.params.recipient + let seller: Address = Address.zero(), + buyer: Address = Address.zero() -function isNFTEntity(itemType: number): bool { - return itemType >= 2; -} - + let allSales = new Array() + let paymentToken = Address.zero() + let offerAmount = BIGINT_ZERO + for (let i = 0; i < event.params.offer.length; i++) { + const offer = event.params.offer[i] -export function handleOrderFulfilled(event: OrderFulfilled): void { - let txHash = event.transaction.hash; - log.warning("new tx {}", [txHash.toHexString()]); + let isNFT = isNFTEntity(offer.itemType) + if (isNFT) { + let standard = getNFTStandard(offer.itemType) + let nft = new airstack.nft.NFT(offer.token, standard, offer.identifier, offer.amount) + buyer = recipient + seller = offerer + let sale = new airstack.nft.Sale( + buyer, + seller, + nft, + BigInt.fromI32(0), + Address.zero(), + BigInt.fromI32(0), + Address.zero(), + new Array() + ) + allSales.push(sale) + } else { + buyer = offerer + seller = recipient + paymentToken = offer.token + offerAmount = offer.amount + } + } + let totalAmountToSeller = BIGINT_ZERO + let feeRecipient = Address.zero() + let totalfeeAmount = BIGINT_ZERO + let totalRoyaltyFees = BIGINT_ZERO + let totalRoyaltyArr = new Array() + for (let i = 0; i < event.params.consideration.length; i++) { + const consideration = event.params.consideration[i] + let isNFT = isNFTEntity(consideration.itemType) - let offerer = event.params.offerer; - let recipient = event.params.recipient; + if (!isNFT) { + paymentToken = consideration.token + if (consideration.recipient == seller) { + totalAmountToSeller = totalAmountToSeller.plus(consideration.amount) + } else if (isOpenSeaFeeAccount(consideration.recipient)) { + feeRecipient = consideration.recipient + totalfeeAmount = totalfeeAmount.plus(consideration.amount) + } else { + // royalty case + totalRoyaltyFees = totalRoyaltyFees.plus(consideration.amount) + let royalty = new airstack.nft.CreatorRoyalty(consideration.amount, consideration.recipient) + totalRoyaltyArr.push(royalty) + } + } else { + let standard = getNFTStandard(consideration.itemType) + let nft = new airstack.nft.NFT( + consideration.token, + standard, + consideration.identifier, + consideration.amount + ) + let sale = new airstack.nft.Sale( + buyer, + seller, + nft, + offerAmount, + paymentToken, + BigInt.fromI32(0), + Address.zero(), + new Array() + ) + allSales.push(sale) + } + } - let paymentAmount = BigInt.fromI32(0); - let paymentToken: Address = Address.zero(); - let seller: Address = Address.zero(), - buyer: Address = Address.zero(); - let royaltyFees = BigInt.fromI32(0); - let royaltyBeneficiary = Address.zero(); - let protocolFees = BigInt.fromI32(0); - let protocolBeneficiary = Address.zero(); + let totalPaymentAmount = BIGINT_ZERO + if (totalAmountToSeller == BIGINT_ZERO) { + totalPaymentAmount = offerAmount + } else { + totalPaymentAmount = totalRoyaltyFees.plus(totalfeeAmount).plus(totalAmountToSeller) + } - let allSales = new Array(); + for (let i = 0; i < allSales.length; i++) { + // for dividing fees + let sale = allSales[i] + sale.protocolFeesBeneficiary = feeRecipient + sale.protocolFees = totalfeeAmount.div(BigInt.fromI64(allSales.length)) + sale.paymentAmount = totalPaymentAmount.div(BigInt.fromI64(allSales.length)) + sale.paymentToken = paymentToken + let finalRoyaltyArr = new Array() + for (let j = 0; j < totalRoyaltyArr.length; j++) { + let totalRoyalty = totalRoyaltyArr[j] + let royalty = new airstack.nft.CreatorRoyalty( + totalRoyalty.fee.div(BigInt.fromI64(allSales.length)), + totalRoyalty.beneficiary + ) + finalRoyaltyArr.push(royalty) + } + sale.royalties = finalRoyaltyArr + } + // logging + for (let i = 0; i < allSales.length; i++) { + let sale = allSales[i] + let royalties = sale.royalties + log.debug( + "buyer {}, seller {} paymentAmount {} paymentToken {} protocolFees {} protocolFeesBeneficiary {} ", + [ + sale.buyer.toHexString(), + sale.seller.toHexString(), + sale.paymentAmount.toString(), + sale.paymentToken.toHexString(), + sale.protocolFees.toString(), + sale.protocolFeesBeneficiary.toHexString(), + ] + ) + for (let i = 0; i < royalties.length; i++) { + let royalty = royalties[i] + log.debug("royalty: fee {}, beneficiary {}", [ + royalty.fee.toString(), + royalty.beneficiary.toHexString(), + ]) + } + airstack.nft.trackNFTSaleTransactions( + ETHEREUM_MAINNET_ID, + txHash.toHexString(), + event.transaction.index, + allSales, + allSales.length>1, + MARKET_PLACE_TYPE, + PROTOCOL_SELL_ACTION_TYPE, + event.block.timestamp, + event.block.number, + event.block.hash.toHexString() + ) + } +} +// two events under same hash is required to get complete info about transaction +function handlePartialEvent(event: OrderFulfilled): void { + let txHash = event.transaction.hash + let offerer = event.params.offerer + let recipient = event.params.recipient - log.info("Parameters txHash {} offerer {} recipient {} orderHash {}", [txHash.toHexString(), offerer.toHexString(), recipient.toHexString(), event.params.orderHash.toHexString()]); + // in partialEvents, recipient == Address.zero() + if (recipient != Address.zero()) { + log.error("recipient {} not expected for partial event", [recipient.toHexString()]) + throw new Error("") + } + let partialRecord = getOrCreatePartialNftTransaction(txHash) + + if ( + partialRecord.from != Address.zero().toHexString() || + partialRecord.to != Address.zero().toHexString() + ) { + partialRecord.isComplete = true + } + + for (let i = 0; i < event.params.offer.length; i++) { + const offer = event.params.offer[i] + let isNFT = isNFTEntity(offer.itemType) + if (!isNFT) { + partialRecord.totalOfferAmount = offer.amount + partialRecord.paymentToken = offer.token.toHexString() + partialRecord.to = offerer.toHexString() + } else { + let standard = getNFTStandard(offer.itemType) + let partialNft = getOrCreatePartialNft( + txHash, + offer.token.toHexString(), + offer.identifier, + offer.amount, + standard + ) + partialNft.save() - for (let i = 0; i < event.params.offer.length; i++) { - let offer = event.params.offer[i]; - - let isNFT = isNFTEntity(offer.itemType); - - log.info("offer type txHash {} logindex {} itemType {} isNFT {} index {}", [ - txHash.toHexString(), - event.logIndex.toString(), - offer.itemType.toString(), - isNFT.toString(), - i.toString(), - ]); - - if (!isNFT) { - paymentToken = offer.token; - paymentAmount = offer.amount; - buyer = offerer; - seller = recipient; - - log.info( - "txHash offer log tx {} logindex {} paymentToken {} paymentAmount {} buyer {} seller {} index {}", - [ - txHash.toHexString(), - event.logIndex.toString(), - paymentToken.toHexString(), - paymentAmount.toString(), - buyer.toHexString(), - seller.toHexString(), - i.toString(), - ] - ); - } else { - let standard = isERC721(offer.itemType) - ? NftStandard.ERC721 - : isERC1155(offer.itemType) - ? NftStandard.ERC1155 - : NftStandard.UNKNOWN; - let nft = new airstack.nft.NFT(offer.token, standard, offer.identifier, offer.amount); - - buyer = recipient; - seller = offerer; + // checking if this NFT already added during last event or not + let alreadyExists = false + let partialNftArr = partialRecord.partialNFT + for (let j = 0; j < partialNftArr.length; j++) { + const partialNftKey = partialNftArr[j] + if (partialNftKey == partialNft.id) { + alreadyExists = true + break + } + } + if (!alreadyExists) { + partialNftArr.push(partialNft.id) + partialRecord.partialNFT = partialNftArr + } + partialRecord.from = offerer.toHexString() + } + } + + for (let i = 0; i < event.params.consideration.length; i++) { + const consideration = event.params.consideration[i] + let isNFT = isNFTEntity(consideration.itemType) + if (!isNFT) { + partialRecord.paymentToken = consideration.token.toHexString() + + if (consideration.recipient.toHexString() == partialRecord.from) { + // calculating amount sent back to seller/from address + let amountToSeller = partialRecord.totalAmountToSeller + partialRecord.totalAmountToSeller = amountToSeller.plus(consideration.amount) + } else if (isOpenSeaFeeAccount(consideration.recipient)) { + // calculating total platform fees + let feeAmount = partialRecord.totalFeeAmount + partialRecord.totalFeeAmount = feeAmount.plus(consideration.amount) + partialRecord.feeBeneficiary = consideration.recipient.toHexString() + } else if (consideration.recipient.toHexString() != partialRecord.to) { + // calculating royalty + let royaltyCount = partialRecord.royaltyCount + let partialRoyalty = getOrCreatePartialRoyalty(txHash, royaltyCount.toI64()) + partialRecord.totalRoyalty = partialRecord.totalRoyalty.plus(consideration.amount) + partialRoyalty.amount = consideration.amount + partialRoyalty.beneficiary = consideration.recipient.toHexString() + partialRoyalty.save() + partialRecord.royaltyCount = royaltyCount.plus(BIGINT_ONE) + } + } else { + let standard = getNFTStandard(consideration.itemType) + let partialNft = getOrCreatePartialNft( + txHash, + consideration.token.toHexString(), + consideration.identifier, + consideration.amount, + standard + ) + partialNft.save() - let sale = new airstack.nft.Sale(buyer, seller, nft, paymentAmount, paymentToken, protocolFees, protocolBeneficiary, new Array()); - allSales.push(sale); - - log.info( - "txHash offer log tx {} logindex {} nftContract {} NFTId {} index {} buyer {} seller {}", - [ - txHash.toHexString(), - event.logIndex.toString(), - offer.token.toHexString(), - offer.identifier.toString(), - i.toString(), - buyer.toHexString(), - seller.toHexString(), - ] - ); + // checking if this NFT already added during last event or not + let alreadyExists = false + let partialNftArr = partialRecord.partialNFT + for (let j = 0; j < partialNftArr.length; j++) { + const partialNftKey = partialNftArr[j] + if (partialNftKey == partialNft.id) { + alreadyExists = true + break } + } + if (!alreadyExists) { + partialNftArr.push(partialNft.id) + partialRecord.partialNFT = partialNftArr + } + partialRecord.to = consideration.recipient.toHexString() } + } + + if (partialRecord.isComplete) { - for (let i = 0; i < event.params.consideration.length; i++) { - let consideration = event.params.consideration[i]; - let isNFT = isNFTEntity(consideration.itemType); + // calculating paymentAmount + if (partialRecord.totalAmountToSeller != BIGINT_ZERO) { + partialRecord.totalPaymentAmount = partialRecord.totalAmountToSeller + .plus(partialRecord.totalFeeAmount) + .plus(partialRecord.totalRoyalty) + } else { + log.info("verify more, partialRecord txhash {} totalPaymentAmount took as offerAmount", [ + txHash.toHexString(), + ]) + partialRecord.totalPaymentAmount = partialRecord.totalOfferAmount + } + } + partialRecord.save() + + if (partialRecord.isComplete) { - log.info( - "consideration type txHash {} logindex {} itemType {} isNFT {} index {}", - [ - txHash.toHexString(), - event.logIndex.toString(), - consideration.itemType.toString(), - isNFT.toString(), - i.toString(), - ] - ); - if (!isNFT) { - paymentToken = consideration.token; - paymentAmount = paymentAmount.plus(consideration.amount); - if(isOpenSeaFeeAccount(consideration.recipient)){ - protocolFees = protocolFees.plus(consideration.amount) - protocolBeneficiary = consideration.recipient - } - if(!isOpenSeaFeeAccount(consideration.recipient) && consideration.recipient != seller){ - royaltyFees = royaltyFees.plus(consideration.amount) - royaltyBeneficiary = consideration.recipient - } - log.info( - "txHash consideration log tx {} logindex {} paymentToken {} paymentAmount {} recipient {} buyer {} seller {} index {}", - [ - txHash.toHexString(), - event.logIndex.toString(), - paymentToken.toHexString(), - paymentAmount.toString(), - consideration.recipient.toHexString(), - buyer.toHexString(), - seller.toHexString(), - i.toString(), - ] - ); + // creating airstack Royalty array + let royaltyArr = new Array() + for (let i = 0; i < partialRecord.royaltyCount.toI64(); i++) { + let royaltyKey = getPartialRoyaltyKey(txHash, i) + let royalty = PartialRoyalty.load(royaltyKey) + if (royalty != null) { + if (partialRecord.partialNFT.length == 0) { + log.error(" txHash {} partialRecord.partialNFT.length is zero", [txHash.toHexString()]) } else { - if (buyer == Address.zero()){ - buyer = consideration.recipient; - } - let standard = isERC721(consideration.itemType) - ? NftStandard.ERC721 - : isERC1155(consideration.itemType) - ? NftStandard.ERC1155 - : NftStandard.UNKNOWN; - let nft = new airstack.nft.NFT(consideration.token, standard, consideration.identifier, consideration.amount); - - let sale = new airstack.nft.Sale(buyer, seller, nft, paymentAmount, paymentToken, protocolFees, protocolBeneficiary, new Array()); - allSales.push(sale); - - log.info( - "txHash consideration log tx {} logindex {} nftContract {} NFTId {} index {} recipient {} buyer {} seller {}", - [ - txHash.toHexString(), - event.logIndex.toString(), - consideration.token.toHexString(), - consideration.identifier.toString(), - i.toString(), - consideration.recipient.toHexString(), - buyer.toHexString(), - seller.toHexString(), - ] - ); + let royaltyRec = new airstack.nft.CreatorRoyalty( + royalty.amount.div(BigInt.fromI64(partialRecord.partialNFT.length)), + Address.fromString(royalty.beneficiary) + ) + royaltyArr.push(royaltyRec) } + } } - for(let i = 0; i () + for (let i = 0; i < partialRecord.partialNFT.length; i++) { + let partialNftKey = partialRecord.partialNFT[i] + let partialNft = PartialNft.load(partialNftKey) + if (partialNft != null) { + let nft = new airstack.nft.NFT( + Address.fromString(partialNft.transactionToken), + partialNft.standard, + partialNft.tokenId, + partialNft.tokenAmount ) - if (allSales[i].seller == Address.zero() || allSales[i].buyer == Address.zero()){ - let tokenExist = WrappedEtherTransaction.load( - generateWrappedEtherTransactionID( - ETHEREUM_MAINNET_ID, - txHash.toHexString(), - allSales[i].nft.collection.toHexString(), - allSales[i].nft.tokenId.toString() - ) - ); - if (tokenExist == null){ - tokenExist = new WrappedEtherTransaction( - generateWrappedEtherTransactionID( - ETHEREUM_MAINNET_ID, - txHash.toHexString(), - allSales[i].nft.collection.toHexString(), - allSales[i].nft.tokenId.toString() - ) - ); - tokenExist.from = allSales[i].seller.toHexString(); - tokenExist.to = allSales[i].buyer.toHexString(); - tokenExist.hash = txHash.toHexString(); - tokenExist.save(); - }else{ - if (allSales[i].seller == Address.zero()){ - allSales[i].seller = Address.fromString(tokenExist.from); - } - } - } + let sale = new airstack.nft.Sale( + Address.fromString(partialRecord.to), + Address.fromString(partialRecord.from), + nft, + partialRecord.totalPaymentAmount.div(BigInt.fromI64(partialRecord.partialNFT.length)), + Address.fromString(partialRecord.paymentToken), + partialRecord.totalFeeAmount.div(BigInt.fromI64(partialRecord.partialNFT.length)), + Address.fromString(partialRecord.feeBeneficiary), + royaltyArr + ) + allSales.push(sale) + } } - log.info("txHash {} allSales length {}", [txHash.toHexString(), allSales.length.toString()]); if (allSales.length > 0) { - airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, - txHash.toHexString(), - event.transaction.index, - allSales, - MARKET_PLACE_TYPE, - PROTOCOL_SELL_ACTION_TYPE, - event.block.timestamp, - event.block.number, - event.block.hash.toHexString() - ) + // calling standard function + airstack.nft.trackNFTSaleTransactions( + ETHEREUM_MAINNET_ID, + txHash.toHexString(), + event.transaction.index, + allSales, + allSales.length > 1, + MARKET_PLACE_TYPE, + PROTOCOL_SELL_ACTION_TYPE, + event.block.timestamp, + event.block.number, + event.block.hash.toHexString() + ) + } else { + log.error("txHash {} not valid since no nft sales involved", [txHash.toHexString()]) } + } } -export function generateWrappedEtherTransactionID( - chainID: string, - txHash: string, - contractAddress: string, - tokenID: string +function getOrCreatePartialNftTransaction(txHash: Bytes): PartialNftTransaction { + let partialRecord = PartialNftTransaction.load(txHash.toHexString()) + if (partialRecord == null) { + partialRecord = new PartialNftTransaction(txHash.toHexString()) + partialRecord.hash = txHash.toHexString() + partialRecord.from = Address.zero().toHexString() + partialRecord.to = Address.zero().toHexString() + partialRecord.paymentToken = Address.zero().toHexString() + partialRecord.totalOfferAmount = BIG_INT_ZERO + partialRecord.totalAmountToSeller = BIG_INT_ZERO + partialRecord.totalPaymentAmount = BIG_INT_ZERO + partialRecord.totalFeeAmount = BIG_INT_ZERO + partialRecord.totalRoyalty = BIG_INT_ZERO + partialRecord.feeBeneficiary = Address.zero().toHexString() + partialRecord.isComplete = false + partialRecord.royaltyCount = BIGINT_ZERO + partialRecord.partialNFT = [] + } + return partialRecord +} + +function getPartialRoyaltyKey(txHash: Bytes, index: i64): string { + return txHash.toHexString() + "-" + index.toString() +} +function getPartialNftKey( + txHash: Bytes, + transactionToken: String, + tokenId: BigInt, + tokenAmount: BigInt ): string { - return chainID + "-" + txHash + "-" + contractAddress + tokenID; + return ( + txHash.toHexString() + + "-" + + transactionToken + + "-" + + tokenId.toString() + + "-" + + tokenAmount.toString() + ) +} +function getOrCreatePartialRoyalty(txHash: Bytes, index: i64): PartialRoyalty { + let royaltyKey = getPartialRoyaltyKey(txHash, index) + let partialRoyalty = PartialRoyalty.load(royaltyKey) + if (partialRoyalty == null) { + partialRoyalty = new PartialRoyalty(royaltyKey) + } + return partialRoyalty +} +function getOrCreatePartialNft( + txHash: Bytes, + transactionToken: string, + tokenId: BigInt, + tokenAmount: BigInt, + standard: string +): PartialNft { + let nftKey = getPartialNftKey(txHash, transactionToken, tokenId, tokenAmount) + let partialNft = PartialNft.load(nftKey) + if (partialNft == null) { + partialNft = new PartialNft(nftKey) + partialNft.transactionToken = transactionToken + partialNft.tokenId = tokenId + partialNft.tokenAmount = tokenAmount + partialNft.standard = standard + } + return partialNft } diff --git a/seaport-subgraph/src/utils.ts b/seaport-subgraph/src/utils.ts index bbb606f9..8df4776c 100644 --- a/seaport-subgraph/src/utils.ts +++ b/seaport-subgraph/src/utils.ts @@ -1,73 +1,76 @@ -import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts"; +import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts" -export const BIGINT_ZERO = BigInt.zero(); -export const BIGDECIMAL_HUNDRED = BigInt.fromI32(100).toBigDecimal(); +export const BIGINT_ZERO = BigInt.zero() +export const BIGDECIMAL_HUNDRED = BigInt.fromI32(100).toBigDecimal() -export const ETHEREUM_MAINNET_ID = "1"; -export const TRANSACTION_TYPE_SALE = "SALE"; -export const MARKET_PLACE_TYPE = "NFT_MARKET_PLACE"; -export const PROTOCOL_SELL_ACTION_TYPE = "SELL"; +export const ETHEREUM_MAINNET_ID = "1" +export const TRANSACTION_TYPE_SALE = "SALE" +export const MARKET_PLACE_TYPE = "NFT_MARKET_PLACE" +export const PROTOCOL_SELL_ACTION_TYPE = "SELL" -export const EXCHANGE_ADDRESS = Address.fromString( - "0x00000000006c3852cbef3e08e8df289169ede581" - ); - export const WETH_ADDRESS = Address.fromString( - "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - ); - export const ERC721_INTERFACE_IDENTIFIER = "0x80ac58cd"; - export const ERC1155_INTERFACE_IDENTIFIER = "0xd9b67a26"; - +export const EXCHANGE_ADDRESS = Address.fromString("0x00000000006c3852cbef3e08e8df289169ede581") +export const WETH_ADDRESS = Address.fromString("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") +export const ERC721_INTERFACE_IDENTIFIER = "0x80ac58cd" +export const ERC1155_INTERFACE_IDENTIFIER = "0xd9b67a26" export namespace SeaportItemType { - export const NATIVE = 0; - export const ERC20 = 1; - export const ERC721 = 2; - export const ERC1155 = 3; - export const ERC721_WITH_CRITERIA = 4; - export const ERC1155_WITH_CRITERIA = 5; + export const NATIVE = 0 + export const ERC20 = 1 + export const ERC721 = 2 + export const ERC1155 = 3 + export const ERC721_WITH_CRITERIA = 4 + export const ERC1155_WITH_CRITERIA = 5 } export function isERC721(itemType: i32): boolean { - return ( - itemType == SeaportItemType.ERC721 || - itemType == SeaportItemType.ERC721_WITH_CRITERIA - ); + return itemType == SeaportItemType.ERC721 || itemType == SeaportItemType.ERC721_WITH_CRITERIA } export function isERC1155(itemType: i32): boolean { - return ( - itemType == SeaportItemType.ERC1155 || - itemType == SeaportItemType.ERC1155_WITH_CRITERIA - ); + return itemType == SeaportItemType.ERC1155 || itemType == SeaportItemType.ERC1155_WITH_CRITERIA } export namespace NftStandard { - export const ERC721 = "ERC721"; - export const ERC1155 = "ERC1155"; - export const UNKNOWN = "UNKNOWN"; + export const ERC721 = "ERC721" + export const ERC1155 = "ERC1155" + export const UNKNOWN = "UNKNOWN" } export function isMoney(itemType: i32): boolean { - return ( - itemType == SeaportItemType.NATIVE || itemType == SeaportItemType.ERC20 - ); + return itemType == SeaportItemType.NATIVE || itemType == SeaportItemType.ERC20 } export function isOpenSeaFeeAccount(address: Address): boolean { - const OPENSEA_WALLET_ADDRESS = Address.fromString( - "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073" - ); - const OPENSEA_FEES_ACCOUNT = Address.fromString( - "0x8de9c5a032463c561423387a9648c5c7bcc5bc90" - ); - // This can be found https://github.com/web3w/seaport-js/blob/399fa568c04749fd8f96829fa7a6b73d1e440458/src/contracts/index.ts#L30 - const OPENSEA_ETHEREUM_FEE_COLLECTOR = Address.fromString( - "0x0000a26b00c1F0DF003000390027140000fAa719" - ); - - return ( - address == OPENSEA_WALLET_ADDRESS || - address == OPENSEA_FEES_ACCOUNT || - address == OPENSEA_ETHEREUM_FEE_COLLECTOR - ); + const OPENSEA_WALLET_ADDRESS = Address.fromString("0x5b3256965e7c3cf26e11fcaf296dfc8807c01073") + const OPENSEA_FEES_ACCOUNT = Address.fromString("0x8de9c5a032463c561423387a9648c5c7bcc5bc90") + // This can be found https://github.com/web3w/seaport-js/blob/399fa568c04749fd8f96829fa7a6b73d1e440458/src/contracts/index.ts#L30 + const OPENSEA_ETHEREUM_FEE_COLLECTOR = Address.fromString( + "0x0000a26b00c1F0DF003000390027140000fAa719" + ) + + return ( + address == OPENSEA_WALLET_ADDRESS || + address == OPENSEA_FEES_ACCOUNT || + address == OPENSEA_ETHEREUM_FEE_COLLECTOR + ) +} + +export enum ItemType { + NATIVE = 0, + ERC20 = 1, + ERC721 = 2, + ERC1155 = 3, + ERC721_WITH_CRITERIA = 4, + ERC1155_WITH_CRITERIA = 5, +} + +export function isNFTEntity(itemType: number): bool { + return itemType >= 2 +} +export function getNFTStandard(itemType: i32): string { + return isERC721(itemType) + ? NftStandard.ERC721 + : isERC1155(itemType) + ? NftStandard.ERC1155 + : NftStandard.UNKNOWN } diff --git a/seaport-subgraph/subgraph.yaml b/seaport-subgraph/subgraph.yaml index 158e57d3..9d57736e 100644 --- a/seaport-subgraph/subgraph.yaml +++ b/seaport-subgraph/subgraph.yaml @@ -6,7 +6,7 @@ dataSources: name: Seaport network: mainnet source: - address: '0x00000000006c3852cbef3e08e8df289169ede581' + address: "0x00000000006c3852cbef3e08e8df289169ede581" abi: Seaport startBlock: 14946474 mapping: @@ -14,15 +14,9 @@ dataSources: apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - CounterIncremented - - OrderCancelled - - OrderFulfilled - - OrderValidated - - AirAccount, - - AirNftTransaction, - - AirToken, - - AirBlock, - - AirNftSaleRoyalty + - PartialRoyalty + - PartialNftTransaction + - PartialNft abis: - name: Seaport file: ./abis/Seaport.json diff --git a/seaport-subgraph/tests/example.ts b/seaport-subgraph/tests/example.ts new file mode 100644 index 00000000..171286d5 --- /dev/null +++ b/seaport-subgraph/tests/example.ts @@ -0,0 +1,1789 @@ +import { OrderFulfilledEventType } from "./types" + +// hash : 0x000000dfb557b6c661360094194d2523e1c0ad1033903efcf15d2d56c5212171 +export const batchTransfer: OrderFulfilledEventType = { + orderHash: "0xc8f020f13eef9620e0dfa5579e4dbf81c7e05eb810dabf228a72f84a56e85647", + offerer: "0x05fb51ae421bdd4a8d8d8357b16bc8db8c059f9f", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x66f59e9a3329a9100fe59b487f71644f1849e480", + offer: [ + { + itemType: 2, + token: "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + identifier: "8977", + amount: "1", + }, + { + itemType: 2, + token: "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + identifier: "8976", + amount: "1", + }, + { + itemType: 2, + token: "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + identifier: "8974", + amount: "1", + }, + { + itemType: 2, + token: "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + identifier: "8975", + amount: "1", + }, + ], + consideration: [ + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "9250000000000", + recipient: "0x05fb51ae421bdd4a8d8d8357b16bc8db8c059f9f", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "250000000000", + recipient: "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "500000000000", + recipient: "0x7d82c0fc40d417ef89870a2c08d1a3c6a1315703", + }, + ], +} +export const singleNftOffer: OrderFulfilledEventType = { + orderHash: "0x76a40418e959d17642b0e510095d08d6a352132615e72d12837abb179b6ab728", + offerer: "0xa26edf96b6a921a9f4b2c961e3db573547a5d701", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x39327ba65a22701d8563d9f3a7d001bd83f147d1", + offer: [ + { + itemType: 3, + token: "0xa604060890923ff400e8c6f5290461a83aedacec", + identifier: + "73470577800278525308063113538359163815840392689226212689732198568968744599562", + amount: "1", + }, + ], + consideration: [ + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "9550000000000000", + recipient: "0xa26edf96b6a921a9f4b2c961e3db573547a5d701", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "250000000000000", + recipient: "0x0000a26b00c1f0df003000390027140000faa719", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "200000000000000", + recipient: "0xa26edf96b6a921a9f4b2c961e3db573547a5d701", + }, + ], +} +// hash : 0x4808f58fa27b662f60012163d8587a7f51bbf544c8b36329382c7c4abceef0cf +export const multiEvent1: OrderFulfilledEventType = { + orderHash: "0x04003783d04cd8d7b36d3737d62036bdc7ef381ddd3d6212ce93ddcd55956699", + offerer: "0x7e8dbf5d60f93b91d2e59abd326840772bb073d8", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 1, + token: "0x6b175474e89094c44da98b954eedeac495271d0f", + identifier: "0", + amount: "10000000000000000000", + }, + ], + consideration: [ + { + itemType: 2, + token: "0x515dc98b0c660bdcb1ad656473907b4d1900ba1b", + identifier: "0", + amount: "1", + recipient: "0x7e8dbf5d60f93b91d2e59abd326840772bb073d8", + }, + { + itemType: 1, + token: "0x6b175474e89094c44da98b954eedeac495271d0f", + identifier: "0", + amount: "250000000000000000", + recipient: "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + }, + ], +} +export const multiEvent2: OrderFulfilledEventType = { + orderHash: "0xf26682f7fa170bccd2fc941733fcd9ab2e615f579f45e099e2024d905a598a89", + offerer: "0xaf3e4a9126729ba97da99df29ae7f7e53d1c4a1a", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 2, + token: "0x515dc98b0c660bdcb1ad656473907b4d1900ba1b", + identifier: "0", + amount: "1", + }, + ], + consideration: [ + { + itemType: 1, + token: "0x6b175474e89094c44da98b954eedeac495271d0f", + identifier: "0", + amount: "4875000000000000000", + recipient: "0xaf3e4a9126729ba97da99df29ae7f7e53d1c4a1a", + }, + { + itemType: 1, + token: "0x6b175474e89094c44da98b954eedeac495271d0f", + identifier: "0", + amount: "125000000000000000", + recipient: "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + }, + ], +} +// hash: 0x00022378fc4e138a01aa0b007a82468f6868c4a9d3ab2992d0c6a157ecf58c3d +export const multipleRoyalties1: OrderFulfilledEventType = { + orderHash: "0x09325b3c7e38084fa0c060c57931b4401a00f2b14d8ea45f1d99ee11cfc7ed78", + offerer: "0xc1ef02f7b0d81fb5c55d6097c18b4d6592184b64", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 2, + token: "0xb18380485f7ba9c23deb729bedd3a3499dbd4449", + identifier: "2243", + amount: "1", + }, + ], + consideration: [ + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "75850000000000000", + recipient: "0xc1ef02f7b0d81fb5c55d6097c18b4d6592184b64", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "2050000000000000", + recipient: "0x0000a26b00c1f0df003000390027140000faa719", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "4100000000000000", + recipient: "0xa394070c35090b57342b3064c6ba7f4082eba122", + }, + { + itemType: 2, + token: "0xb18380485f7ba9c23deb729bedd3a3499dbd4449", + identifier: "2243", + amount: "1", + recipient: "0xd91095ec5a892a1b7e42030f01868e86ef0fd935", + }, + ], +} + +export const multipleRoyalties2: OrderFulfilledEventType = { + orderHash: "0xda3ff554aa4d1a5dc6fef2b5de50bc9b0955b4fb7b9c7e6d642c2c59a81c9d6c", + offerer: "0xd91095ec5a892a1b7e42030f01868e86ef0fd935", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "82000000000000000", + }, + ], + consideration: [], +} +// hash: 0x6a31c5444516a53c6a8e1ffd7ff81a697b6e59f737b814810df1de5b21b79c48 +export const offersToken: OrderFulfilledEventType = { + orderHash: "0x8d2553ff70345ee30ec505c8b099696ebd7dae2af0d1cce60a8f7c5952a28441", + offerer: "0x3b52ad533687ce908ba0485ac177c5fb42972962", + zone: "0x9b814233894cd227f561b78cc65891aa55c62ad2", + recipient: "0xf5a48858f6895674c3937bd059d4b0a4ea0a63c6", + offer: [ + { + itemType: 1, + token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + identifier: "0", + amount: "100000", + }, + ], + consideration: [ + { + itemType: 2, + token: "0x3f53082981815ed8142384edb1311025ca750ef1", + identifier: "30", + amount: "1", + recipient: "0x3b52ad533687ce908ba0485ac177c5fb42972962", + }, + { + itemType: 1, + token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + identifier: "0", + amount: "2500", + recipient: "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + }, + ], +} +// hash: 0x804b2bd3c0e019acb70c91ed7724e8dd28aa9346ed0d7746ef5987fbd3b58440 +export const offersNFTAndToken1: OrderFulfilledEventType = { + orderHash: "0xf6a8f7c4cd428f4d060208b82514e5f5b8989631160fa14586618477db78f88f", + offerer: "0xc515af393e0eb6bc05c0071361cb027fd89bfa33", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 2, + token: "0xc99c679c50033bbc5321eb88752e89a93e9e83c5", + identifier: "1429", + amount: "1", + }, + { + itemType: 1, + token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + identifier: "0", + amount: "40457500000000000", + }, + ], + consideration: [ + { + itemType: 2, + token: "0xc99c679c50033bbc5321eb88752e89a93e9e83c5", + identifier: "1429", + amount: "1", + recipient: "0x7c8af8638248586d3ba775c8a4178f59ef993d05", + }, + { + itemType: 1, + token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + identifier: "0", + amount: "40457500000000000", + recipient: "0x7c8af8638248586d3ba775c8a4178f59ef993d05", + }, + ], +} +export const offersNFTAndToken2: OrderFulfilledEventType = { + orderHash: "0xba1a878c1e009a0c68efe93ad42aa96dfeba0c72897bcc1aa9e0d13fc3afcc0f", + offerer: "0xfd7bd3578b01cfafeefde581d8a3ac2cf6e02c11", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [], + consideration: [], +} +// hash: 0x1fb6a58785719dab58132ecc8bd2b9a5c8d477039510a104d9ec347aa2568599 +export const huge1: OrderFulfilledEventType = { + orderHash: "0x1623825d3a471cd8b28e44a713d4f251a64e4c9f2f3a87a2a2af0b7fae09d260", + offerer: "0x9b6cd0ff5789748aa352a9257c13be99126f3fb2", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1787", + amount: "1", + }, + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1705", + amount: "1", + }, + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1702", + amount: "1", + }, + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1704", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1739", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1738", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1737", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1736", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1231", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1230", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1229", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1228", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1079", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1078", + amount: "1", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1077", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2760", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2750", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2713", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2128", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2189", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2127", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1921", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1949", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1591", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1131", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1172", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1132", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "971", + amount: "1", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "386", + amount: "1", + }, + { + itemType: 2, + token: "0x1e7e43efa27e2b0a0359cccdfb16b14b877ec508", + identifier: "3325", + amount: "1", + }, + { + itemType: 2, + token: "0x1e7e43efa27e2b0a0359cccdfb16b14b877ec508", + identifier: "3324", + amount: "1", + }, + { + itemType: 2, + token: "0x1e7e43efa27e2b0a0359cccdfb16b14b877ec508", + identifier: "3323", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6990", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6989", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6988", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6987", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6986", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6350", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6349", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6348", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6347", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6346", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4876", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4851", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4850", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4875", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4849", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4874", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4873", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4848", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4872", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4847", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2982", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3127", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2981", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3126", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2980", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3125", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2979", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2978", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3123", + amount: "1", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2642", + amount: "1", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "301", + amount: "1", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "300", + amount: "1", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "299", + amount: "1", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "298", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9492", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9348", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9174", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9156", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "8657", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "7559", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "7458", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "7037", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6698", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6417", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6318", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6040", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "5720", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "5677", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "5192", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "4856", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "4553", + amount: "1", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "2805", + amount: "1", + }, + { + itemType: 2, + token: "0xbfd70ab61d5670da395eb81d27f6f1cb9c6f0142", + identifier: "2651", + amount: "1", + }, + { + itemType: 2, + token: "0xbfd70ab61d5670da395eb81d27f6f1cb9c6f0142", + identifier: "2168", + amount: "1", + }, + { + itemType: 2, + token: "0xbfd70ab61d5670da395eb81d27f6f1cb9c6f0142", + identifier: "2169", + amount: "1", + }, + { + itemType: 2, + token: "0x3bb22ba8e479f0821a8d6b84aab567cb73d790b6", + identifier: "1802", + amount: "1", + }, + { + itemType: 2, + token: "0x3bb22ba8e479f0821a8d6b84aab567cb73d790b6", + identifier: "1801", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "351", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "350", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "349", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "348", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "347", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "346", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "345", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "344", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "343", + amount: "1", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "342", + amount: "1", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "7272", + amount: "1", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "7268", + amount: "1", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "6000", + amount: "1", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "5998", + amount: "1", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3814", + amount: "1", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3813", + amount: "1", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3812", + amount: "1", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3811", + amount: "1", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3810", + amount: "1", + }, + ], + consideration: [ + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1787", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1705", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1702", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1851c63671a2c96babd482030d5b712c184139de", + identifier: "1704", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1739", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1738", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1737", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1736", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1231", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1230", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1229", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1228", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1079", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1078", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1df8eb166fa72f6490ee5020f2767bc08a1ebb3e", + identifier: "1077", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2760", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2750", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2713", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2128", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2189", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "2127", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1921", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1949", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1591", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1131", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1172", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "1132", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "971", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x542a92f16072a7e8bb571822b9f8d4b827a4032b", + identifier: "386", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1e7e43efa27e2b0a0359cccdfb16b14b877ec508", + identifier: "3325", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1e7e43efa27e2b0a0359cccdfb16b14b877ec508", + identifier: "3324", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x1e7e43efa27e2b0a0359cccdfb16b14b877ec508", + identifier: "3323", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6990", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6989", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6988", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6987", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6986", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6350", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6349", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6348", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6347", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "6346", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4876", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4851", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4850", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4875", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4849", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4874", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4873", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4848", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4872", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "4847", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2982", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3127", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2981", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3126", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2980", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3125", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2979", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2978", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "3123", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xde1ac5fdf1a7203af036f590bcc4c0b91b2d3e3f", + identifier: "2642", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "301", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "300", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "299", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xc3e044c9a9878f5ccabf741da5d8d1659c59db98", + identifier: "298", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9492", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9348", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9174", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "9156", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "8657", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "7559", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "7458", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "7037", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6698", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6417", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6318", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "6040", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "5720", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "5677", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "5192", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "4856", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "4553", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x8e3e155a4ad7da9d3aeea0aeecaedb5b4a5fa626", + identifier: "2805", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xbfd70ab61d5670da395eb81d27f6f1cb9c6f0142", + identifier: "2651", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xbfd70ab61d5670da395eb81d27f6f1cb9c6f0142", + identifier: "2168", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xbfd70ab61d5670da395eb81d27f6f1cb9c6f0142", + identifier: "2169", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x3bb22ba8e479f0821a8d6b84aab567cb73d790b6", + identifier: "1802", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x3bb22ba8e479f0821a8d6b84aab567cb73d790b6", + identifier: "1801", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "351", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "350", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "349", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "348", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "347", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "346", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "345", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "344", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "343", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xdea74dbe19ebcb7246dad8e35dcae3bfc5f62b8f", + identifier: "342", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "7272", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "7268", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "6000", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0xa5349c4097607e54fe9c5426f5e882f70d0bce68", + identifier: "5998", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3814", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3813", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3812", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3811", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + { + itemType: 2, + token: "0x690626a28676301c3254ce909d4eb0437ebfade3", + identifier: "3810", + amount: "1", + recipient: "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + }, + ], +} +export const huge2: OrderFulfilledEventType = { + orderHash: "0xbb12a87832e3a081365df87aa8960cf09506e3058a3c376f489ad234f47b7cbd", + offerer: "0x19fdef979d3d9b6fd720f77935b11734ed233ca9", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [], + consideration: [], +} + +// hash: 0xb5892d5bdec28c091efa683ae4d178251db587a2295d49e7ae77c0adc235c43d (transfering WETH only) +// https://etherscan.io/tx/0xb5892d5bdec28c091efa683ae4d178251db587a2295d49e7ae77c0adc235c43d +export const transferingWETHOnly1: OrderFulfilledEventType = { + orderHash: "0x028b227bb21dea69ee26615f25defd3a56c996fbc89a6890de32b90d27c92ba2", + offerer: "0xb4f6c6f8243d3d3c57a8e9f1b08f1f21ecfb53bc", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [ + { + itemType: 1, + token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + identifier: "0", + amount: "10000000000000000", + }, + ], + consideration: [ + { + itemType: 1, + token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + identifier: "0", + amount: "10000000000000000", + recipient: "0x7c8af8638248586d3ba775c8a4178f59ef993d05", + }, + ], +} +export const transferingWETHOnly2: OrderFulfilledEventType = { + orderHash: "0xf37f2b7f7b297add31f0c26157bbe3ec261fcd8b244692f556d98570bb32f288", + offerer: "0xfd7bd3578b01cfafeefde581d8a3ac2cf6e02c11", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0x0000000000000000000000000000000000000000", + offer: [], + consideration: [], +} +// hash 0x2efcb4989bfc25fead3d7f399b365537adcf21b884e7448fe4444d59f335ea05 +export const zeroBeneficiary: OrderFulfilledEventType = { + orderHash: "0x3b46d40541dea0e03c7d30c560438b735c3ae7ef1de222f1c1d8793a78b3a27b", + offerer: "0xaa6e633be7fe76331b67fa4a897f803d79fe53b3", + zone: "0x004c00500000ad104d7dbd00e3ae0a5c00560c00", + recipient: "0xd2f86cbed86db85c859903ff5f110b15d95b1350", + offer: [ + { + itemType: 2, + token: "0xa2830b0519ab246a52a85ba09cf0f11f36b105db", + identifier: "183", + amount: "1", + }, + ], + consideration: [ + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "131250000000000000", + recipient: "0xaa6e633be7fe76331b67fa4a897f803d79fe53b3", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "3750000000000000", + recipient: "0x0000a26b00c1f0df003000390027140000faa719", + }, + { + itemType: 0, + token: "0x0000000000000000000000000000000000000000", + identifier: "0", + amount: "15000000000000000", + recipient: "0x0000000000000000000000000000000000000000", + }, + ], +} diff --git a/seaport-subgraph/tests/seaport-utils.ts b/seaport-subgraph/tests/seaport-utils.ts index b94789c7..3ecf6619 100644 --- a/seaport-subgraph/tests/seaport-utils.ts +++ b/seaport-subgraph/tests/seaport-utils.ts @@ -1,119 +1,201 @@ import { newMockEvent } from "matchstick-as" -import { ethereum, BigInt, Address, Bytes } from "@graphprotocol/graph-ts" +import { ethereum, BigInt, Address, Bytes, log } from "@graphprotocol/graph-ts" import { CounterIncremented, OrderCancelled, OrderFulfilled, - OrderValidated + OrderValidated, } from "../generated/Seaport/Seaport" - -export function createCounterIncrementedEvent( - newCounter: BigInt, - offerer: Address -): CounterIncremented { - let counterIncrementedEvent = changetype(newMockEvent()) - - counterIncrementedEvent.parameters = new Array() - - counterIncrementedEvent.parameters.push( - new ethereum.EventParam( - "newCounter", - ethereum.Value.fromUnsignedBigInt(newCounter) +import { + AirNftSaleRoyaltyExpectedResponse, + AirNftTransactionExpectedResponse, + OrderFulfilledEventType, +} from "./types" +import * as airstack from "../modules/airstack/nft-marketplace" +import { assert } from "matchstick-as/assembly/index" + +// -------------- EVENT -------------- +// event OrderFulfilled( +// bytes32 orderHash, +// address indexed offerer, +// address indexed zone, +// address recipient, +// SpentItem[] offer, +// ReceivedItem[] consideration +// ); + +// struct SpentItem { +// ItemType itemType; +// address token; +// uint256 identifier; +// uint256 amount; +// } + +// struct ReceivedItem { +// ItemType itemType; +// address token; +// uint256 identifier; +// uint256 amount; +// address payable recipient; +// } + +export function convertObjectToEvent(eventJson: OrderFulfilledEventType): OrderFulfilled { + let orderFulfilledEvent = changetype(newMockEvent()) + // preparing offer Array + let offerTupleArr: Array = [] + for (let i = 0; i < eventJson.offer.length; i++) { + let offerArr: Array = [] + const offer = eventJson.offer[i] + offerArr.push(ethereum.Value.fromI32(offer.itemType)) + offerArr.push(ethereum.Value.fromAddress(Address.fromString(offer.token))) + offerArr.push(ethereum.Value.fromUnsignedBigInt(BigInt.fromString(offer.identifier))) + offerArr.push(ethereum.Value.fromUnsignedBigInt(BigInt.fromString(offer.amount))) + let offerTuple = changetype(offerArr) + offerTupleArr.push(offerTuple) + } + // preparing consideration Array + let considerationTupleArr: Array = [] + for (let i = 0; i < eventJson.consideration.length; i++) { + let considerationArr: Array = [] + const consideration = eventJson.consideration[i] + considerationArr.push(ethereum.Value.fromI32(consideration.itemType)) + considerationArr.push(ethereum.Value.fromAddress(Address.fromString(consideration.token))) + considerationArr.push( + ethereum.Value.fromUnsignedBigInt(BigInt.fromString(consideration.identifier)) ) - ) - counterIncrementedEvent.parameters.push( - new ethereum.EventParam("offerer", ethereum.Value.fromAddress(offerer)) - ) - - return counterIncrementedEvent -} - -export function createOrderCancelledEvent( - orderHash: Bytes, - offerer: Address, - zone: Address -): OrderCancelled { - let orderCancelledEvent = changetype(newMockEvent()) - - orderCancelledEvent.parameters = new Array() - - orderCancelledEvent.parameters.push( - new ethereum.EventParam( - "orderHash", - ethereum.Value.fromFixedBytes(orderHash) + considerationArr.push( + ethereum.Value.fromUnsignedBigInt(BigInt.fromString(consideration.amount)) ) + considerationArr.push(ethereum.Value.fromAddress(Address.fromString(consideration.recipient))) + let considerationTuple = changetype(considerationArr) + considerationTupleArr.push(considerationTuple) + } + // preparing rest of the event + let orderHash = new ethereum.EventParam( + "orderHash", + ethereum.Value.fromBytes(Bytes.fromHexString(eventJson.orderHash)) ) - orderCancelledEvent.parameters.push( - new ethereum.EventParam("offerer", ethereum.Value.fromAddress(offerer)) + let offerer = new ethereum.EventParam( + "offerer", + ethereum.Value.fromAddress(Address.fromString(eventJson.offerer)) ) - orderCancelledEvent.parameters.push( - new ethereum.EventParam("zone", ethereum.Value.fromAddress(zone)) + let zone = new ethereum.EventParam( + "zone", + ethereum.Value.fromAddress(Address.fromString(eventJson.zone)) ) - - return orderCancelledEvent -} - -export function createOrderFulfilledEvent( - orderHash: Bytes, - offerer: Address, - zone: Address, - recipient: Address, - offer: Array, - consideration: Array -): OrderFulfilled { - let orderFulfilledEvent = changetype(newMockEvent()) - - orderFulfilledEvent.parameters = new Array() - - orderFulfilledEvent.parameters.push( - new ethereum.EventParam( - "orderHash", - ethereum.Value.fromFixedBytes(orderHash) - ) - ) - orderFulfilledEvent.parameters.push( - new ethereum.EventParam("offerer", ethereum.Value.fromAddress(offerer)) - ) - orderFulfilledEvent.parameters.push( - new ethereum.EventParam("zone", ethereum.Value.fromAddress(zone)) + let recipient = new ethereum.EventParam( + "recipient", + ethereum.Value.fromAddress(Address.fromString(eventJson.recipient)) ) - orderFulfilledEvent.parameters.push( - new ethereum.EventParam("recipient", ethereum.Value.fromAddress(recipient)) + let offer = new ethereum.EventParam("displayName", ethereum.Value.fromTupleArray(offerTupleArr)) + let consideration = new ethereum.EventParam( + "displayName", + ethereum.Value.fromTupleArray(considerationTupleArr) ) - orderFulfilledEvent.parameters.push( - new ethereum.EventParam("offer", ethereum.Value.fromTupleArray(offer)) + orderFulfilledEvent.parameters = [orderHash, offerer, zone, recipient, offer, consideration] + return orderFulfilledEvent +} +export function createAirNftTransactionExpectedResponse( + from: string, + to: string, + hash: string, + tokenId: string, + tokenAmount: string, + transactionToken: string, + paymentToken: string, + paymentAmount: string, + feeAmount: string, + feeBeneficiary: string, + isBundle:boolean +): AirNftTransactionExpectedResponse { + let prefix = "1-" + return new AirNftTransactionExpectedResponse( + prefix + from, + prefix + to, + hash, + tokenId, + tokenAmount, + prefix + transactionToken, + prefix + paymentToken, + paymentAmount, + feeAmount, + prefix + feeBeneficiary, + isBundle ) - orderFulfilledEvent.parameters.push( - new ethereum.EventParam( - "consideration", - ethereum.Value.fromTupleArray(consideration) - ) +} +export function assertAirNftSaleRoyaltyExpectedResponse( + txIndex: BigInt, + transactionToken: string, + tokenId: string, + hash: string, + beneficiary: string, + amount: string +): void { + let saleId = airstack.nft.getNFTSaleTransactionId( + "1", + hash, + txIndex, + transactionToken, + BigInt.fromString(tokenId) ) - - return orderFulfilledEvent + let royaltyId = saleId + "-" + beneficiary + let expected = new AirNftSaleRoyaltyExpectedResponse(amount, "1-" + beneficiary) + assert.fieldEquals("AirNftSaleRoyalty", royaltyId, "amount", expected.amount) + assert.fieldEquals("AirNftSaleRoyalty", royaltyId, "beneficiary", expected.beneficiary) } - -export function createOrderValidatedEvent( - orderHash: Bytes, - offerer: Address, - zone: Address -): OrderValidated { - let orderValidatedEvent = changetype(newMockEvent()) - - orderValidatedEvent.parameters = new Array() - - orderValidatedEvent.parameters.push( - new ethereum.EventParam( - "orderHash", - ethereum.Value.fromFixedBytes(orderHash) - ) +export function assertAirNftTransactionExpectedResponse( + txIndex: BigInt, + from: string, + to: string, + hash: string, + tokenId: string, + tokenAmount: string, + transactionToken: string, + paymentToken: string, + paymentAmount: string, + feeAmount: string, + feeBeneficiary: string, + isBundle: boolean +): void { + let expectedResponse = createAirNftTransactionExpectedResponse( + from, + to, + hash, + tokenId, + tokenAmount, + transactionToken, + paymentToken, + paymentAmount, + feeAmount, + feeBeneficiary, + isBundle ) - orderValidatedEvent.parameters.push( - new ethereum.EventParam("offerer", ethereum.Value.fromAddress(offerer)) + let entityId = airstack.nft.getNFTSaleTransactionId( + "1", + hash, + txIndex, + transactionToken, + BigInt.fromString(tokenId) ) - orderValidatedEvent.parameters.push( - new ethereum.EventParam("zone", ethereum.Value.fromAddress(zone)) + assert.fieldEquals("AirNftTransaction", entityId, "from", expectedResponse.from) + assert.fieldEquals("AirNftTransaction", entityId, "to", expectedResponse.to) + assert.fieldEquals("AirNftTransaction", entityId, "hash", expectedResponse.hash) + assert.fieldEquals("AirNftTransaction", entityId, "tokenId", expectedResponse.tokenId) + assert.fieldEquals("AirNftTransaction", entityId, "tokenAmount", expectedResponse.tokenAmount) + assert.fieldEquals("AirNftTransaction", entityId, "isBundle", expectedResponse.isBundle.toString()) + assert.fieldEquals( + "AirNftTransaction", + entityId, + "transactionToken", + expectedResponse.transactionToken + ) + assert.fieldEquals("AirNftTransaction", entityId, "paymentToken", expectedResponse.paymentToken) + assert.fieldEquals("AirNftTransaction", entityId, "paymentAmount", expectedResponse.paymentAmount) + assert.fieldEquals("AirNftTransaction", entityId, "feeAmount", expectedResponse.feeAmount) + assert.fieldEquals( + "AirNftTransaction", + entityId, + "feeBeneficiary", + expectedResponse.feeBeneficiary ) - - return orderValidatedEvent } diff --git a/seaport-subgraph/tests/seaport.test.ts b/seaport-subgraph/tests/seaport.test.ts index 07c162ee..f3509afc 100644 --- a/seaport-subgraph/tests/seaport.test.ts +++ b/seaport-subgraph/tests/seaport.test.ts @@ -1,58 +1,337 @@ import { - assert, - describe, - test, - clearStore, - beforeAll, - afterAll + assert, + describe, + test, + clearStore, + beforeAll, + afterAll, + afterEach, } from "matchstick-as/assembly/index" -import { BigInt, Address, Bytes } from "@graphprotocol/graph-ts" -import { ExampleEntity } from "../generated/schema" -import { CounterIncremented } from "../generated/Seaport/Seaport" -import { handleCounterIncremented } from "../src/seaport" -import { createCounterIncrementedEvent } from "./seaport-utils" + +import { BigInt, Address, Bytes, log } from "@graphprotocol/graph-ts" +import { handleOrderFulfilled } from "../src/seaport" +import { + assertAirNftSaleRoyaltyExpectedResponse, + assertAirNftTransactionExpectedResponse, + convertObjectToEvent, +} from "./seaport-utils" +import { + batchTransfer, + singleNftOffer, + multiEvent1, + multiEvent2, + multipleRoyalties1, + multipleRoyalties2, + offersToken, + offersNFTAndToken1, + offersNFTAndToken2, + huge2, + huge1, + transferingWETHOnly1, + transferingWETHOnly2, + zeroBeneficiary, +} from "./example" +import * as airstack from "../modules/airstack/nft-marketplace" // Tests structure (matchstick-as >=0.5.0) // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 describe("Describe entity assertions", () => { - beforeAll(() => { - let newCounter = BigInt.fromI32(234) - let offerer = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let newCounterIncrementedEvent = createCounterIncrementedEvent( - newCounter, - offerer - ) - handleCounterIncremented(newCounterIncrementedEvent) - }) - - afterAll(() => { - clearStore() - }) - - // For more test scenarios, see: - // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test - - test("ExampleEntity created and stored", () => { - assert.entityCount("ExampleEntity", 1) + afterEach(() => { + clearStore() + }) - // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "newCounter", - "234" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "offerer", - "0x0000000000000000000000000000000000000001" - ) + // For more test scenarios, see: + // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test - // More assert options: - // https://thegraph.com/docs/en/developer/matchstick/#asserts - }) + test("handling batchTransfer", () => { + let fulFillEvent = convertObjectToEvent(batchTransfer) + handleOrderFulfilled(fulFillEvent) + let txHash = fulFillEvent.transaction.hash.toHexString() + let txIndex = fulFillEvent.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0x05fb51ae421bdd4a8d8d8357b16bc8db8c059f9f", + "0x66f59e9a3329a9100fe59b487f71644f1849e480", + txHash.toString(), + "8977", + "1", + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "0x0000000000000000000000000000000000000000", + "2500000000000", + "62500000000", + "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + true + ) + assertAirNftSaleRoyaltyExpectedResponse( + txIndex, + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "8977", + txHash, + "0x7d82c0fc40d417ef89870a2c08d1a3c6a1315703", + "125000000000" + ) + assertAirNftTransactionExpectedResponse( + txIndex, + "0x05fb51ae421bdd4a8d8d8357b16bc8db8c059f9f", + "0x66f59e9a3329a9100fe59b487f71644f1849e480", + txHash.toString(), + "8976", + "1", + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "0x0000000000000000000000000000000000000000", + "2500000000000", + "62500000000", + "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + true + ) + assertAirNftSaleRoyaltyExpectedResponse( + txIndex, + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "8976", + txHash, + "0x7d82c0fc40d417ef89870a2c08d1a3c6a1315703", + "125000000000" + ) + assertAirNftTransactionExpectedResponse( + txIndex, + "0x05fb51ae421bdd4a8d8d8357b16bc8db8c059f9f", + "0x66f59e9a3329a9100fe59b487f71644f1849e480", + txHash.toString(), + "8974", + "1", + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "0x0000000000000000000000000000000000000000", + "2500000000000", + "62500000000", + "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + true + ) + assertAirNftSaleRoyaltyExpectedResponse( + txIndex, + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "8974", + txHash, + "0x7d82c0fc40d417ef89870a2c08d1a3c6a1315703", + "125000000000" + ) + assertAirNftTransactionExpectedResponse( + txIndex, + "0x05fb51ae421bdd4a8d8d8357b16bc8db8c059f9f", + "0x66f59e9a3329a9100fe59b487f71644f1849e480", + txHash.toString(), + "8975", + "1", + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "0x0000000000000000000000000000000000000000", + "2500000000000", + "62500000000", + "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + true + ) + assertAirNftSaleRoyaltyExpectedResponse( + txIndex, + "0xb9d9455ea8ba8e244b3ea9d46ba106642cb99b97", + "8975", + txHash, + "0x7d82c0fc40d417ef89870a2c08d1a3c6a1315703", + "125000000000" + ) + // More assert options: + // https://thegraph.com/docs/en/developer/matchstick/#asserts + }) + test("handling singleNftOffer", () => { + let fulFillEvent = convertObjectToEvent(singleNftOffer) + let txHash = fulFillEvent.transaction.hash.toHexString() + let txIndex = fulFillEvent.transaction.index + handleOrderFulfilled(fulFillEvent) + assertAirNftTransactionExpectedResponse( + txIndex, + "0xa26edf96b6a921a9f4b2c961e3db573547a5d701", + "0x39327ba65a22701d8563d9f3a7d001bd83f147d1", + txHash.toString(), + "73470577800278525308063113538359163815840392689226212689732198568968744599562", + "1", + "0xa604060890923ff400e8c6f5290461a83aedacec", + "0x0000000000000000000000000000000000000000", + "10000000000000000", + "250000000000000", + "0x0000a26b00c1f0df003000390027140000faa719", + false + ) + }) + test("handling multiEvent1 & multiEvent2", () => { + let multiEvent_1 = convertObjectToEvent(multiEvent1) + handleOrderFulfilled(multiEvent_1) + let multiEvent_2 = convertObjectToEvent(multiEvent2) + handleOrderFulfilled(multiEvent_2) + let txHash = multiEvent_2.transaction.hash.toHexString() + let txIndex = multiEvent_2.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0xaf3e4a9126729ba97da99df29ae7f7e53d1c4a1a", + "0x7e8dbf5d60f93b91d2e59abd326840772bb073d8", + txHash, + "0", + "1", + "0x515dc98b0c660bdcb1ad656473907b4d1900ba1b", + "0x6b175474e89094c44da98b954eedeac495271d0f", + "5250000000000000000", + "375000000000000000", + "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + false + ) + }) + test("handling multiEvent2 & multiEvent1", () => { + let multiEvent_2 = convertObjectToEvent(multiEvent2) + handleOrderFulfilled(multiEvent_2) + let multiEvent_1 = convertObjectToEvent(multiEvent1) + handleOrderFulfilled(multiEvent_1) + let txHash = multiEvent_1.transaction.hash.toHexString() + let txIndex = multiEvent_1.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0xaf3e4a9126729ba97da99df29ae7f7e53d1c4a1a", + "0x7e8dbf5d60f93b91d2e59abd326840772bb073d8", + txHash, + "0", + "1", + "0x515dc98b0c660bdcb1ad656473907b4d1900ba1b", + "0x6b175474e89094c44da98b954eedeac495271d0f", + "5250000000000000000", + "375000000000000000", + "0x8de9c5a032463c561423387a9648c5c7bcc5bc90", + false + ) + }) + test("handling multipleRoyalties1 & multipleRoyalties1", () => { + let multipleRoyalties1_event = convertObjectToEvent(multipleRoyalties1) + handleOrderFulfilled(multipleRoyalties1_event) + let multipleRoyalties2_event = convertObjectToEvent(multipleRoyalties2) + handleOrderFulfilled(multipleRoyalties2_event) + let txHash = multipleRoyalties2_event.transaction.hash.toHexString() + let txIndex = multipleRoyalties2_event.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0xc1ef02f7b0d81fb5c55d6097c18b4d6592184b64", + "0xd91095ec5a892a1b7e42030f01868e86ef0fd935", + txHash, + "2243", + "1", + "0xb18380485f7ba9c23deb729bedd3a3499dbd4449", + "0x0000000000000000000000000000000000000000", + "82000000000000000", + "2050000000000000", + "0x0000a26b00c1f0df003000390027140000faa719", + false + ) + assertAirNftSaleRoyaltyExpectedResponse( + txIndex, + "0xb18380485f7ba9c23deb729bedd3a3499dbd4449", + "2243", + txHash, + "0xa394070c35090b57342b3064c6ba7f4082eba122", + "4100000000000000" + ) + }) + test("handling case where event offers token", () => { + let offersToken_event = convertObjectToEvent(offersToken) + handleOrderFulfilled(offersToken_event) + let txHash = offersToken_event.transaction.hash.toHexString() + let txIndex = offersToken_event.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0xf5a48858f6895674c3937bd059d4b0a4ea0a63c6", + "0x3b52ad533687ce908ba0485ac177c5fb42972962", + txHash, + "30", + "1", + "0x3f53082981815ed8142384edb1311025ca750ef1", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "100000", + "2500", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + false + ) + }) + test("handling case where offer has nft and token & triggered by someone else", () => { + let event1 = convertObjectToEvent(offersNFTAndToken1) + handleOrderFulfilled(event1) + let event2 = convertObjectToEvent(offersNFTAndToken2) + handleOrderFulfilled(event2) + let txHash = event2.transaction.hash.toHexString() + let txIndex = event2.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0xc515af393e0eb6bc05c0071361cb027fd89bfa33", + "0x7c8af8638248586d3ba775c8a4178f59ef993d05", + txHash, + "1429", + "1", + "0xc99c679c50033bbc5321eb88752e89a93e9e83c5", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "40457500000000000", + "0", + "0x0000000000000000000000000000000000000000", + false + ) + }) + test("handling huge numeber of nft transactions in a txn", () => { + let event1 = convertObjectToEvent(huge1) + handleOrderFulfilled(event1) + let event2 = convertObjectToEvent(huge2) + handleOrderFulfilled(event2) + let txHash = event2.transaction.hash.toHexString() + let txIndex = event2.transaction.index + for (let i = 0; i < huge1.offer.length; i++) { + assertAirNftTransactionExpectedResponse( + txIndex, + "0x9b6cd0ff5789748aa352a9257c13be99126f3fb2", + "0xc858598bed0b11098c917cbf9fd3e0f435bab303", + txHash, + huge1.offer[i].identifier, + "1", + huge1.offer[i].token, + "0x0000000000000000000000000000000000000000", + "0", + "0", + "0x0000000000000000000000000000000000000000", + true + ) + } + }) + test("handling transaction without NFT", () => { + let event1 = convertObjectToEvent(transferingWETHOnly1) + handleOrderFulfilled(event1) + let event2 = convertObjectToEvent(transferingWETHOnly2) + handleOrderFulfilled(event2) + }) + test("handling no beneficiary case ", () => { + let zbEvent = convertObjectToEvent(zeroBeneficiary) + handleOrderFulfilled(zbEvent) + let txHash = zbEvent.transaction.hash.toHexString() + let txIndex = zbEvent.transaction.index + assertAirNftTransactionExpectedResponse( + txIndex, + "0xaa6e633be7fe76331b67fa4a897f803d79fe53b3", + "0xd2f86cbed86db85c859903ff5f110b15d95b1350", + txHash, + "183", + "1", + "0xa2830b0519ab246a52a85ba09cf0f11f36b105db", + "0x0000000000000000000000000000000000000000", + "150000000000000000", + "3750000000000000", + "0x0000a26b00c1f0df003000390027140000faa719", + false + ) + assertAirNftSaleRoyaltyExpectedResponse( + txIndex, + "0xa2830b0519ab246a52a85ba09cf0f11f36b105db", + "183", + txHash, + "0x0000000000000000000000000000000000000000", + "15000000000000000" + ) + }) }) diff --git a/seaport-subgraph/tests/types.ts b/seaport-subgraph/tests/types.ts new file mode 100644 index 00000000..7fd8171e --- /dev/null +++ b/seaport-subgraph/tests/types.ts @@ -0,0 +1,40 @@ +export class Offer { + itemType: i32 + token: string + identifier: string + amount: string +} +export class Consideration { + itemType: i32 + token: string + identifier: string + amount: string + recipient: string +} +export class OrderFulfilledEventType { + orderHash: string + offerer: string + zone: string + recipient: string + offer: Offer[] + consideration: Consideration[] +} + +export class AirNftTransactionExpectedResponse { + constructor( + public from: string, + public to: string, + public hash: string, + public tokenId: string, + public tokenAmount: string, + public transactionToken: string, + public paymentToken: string, + public paymentAmount: string, + public feeAmount: string, + public feeBeneficiary: string, + public isBundle:boolean + ) {} +} +export class AirNftSaleRoyaltyExpectedResponse { + constructor(public amount: string, public beneficiary: string) {} +} diff --git a/wyvern-exchange/package.json b/wyvern-exchange/package.json index 563ab78a..817b51d6 100644 --- a/wyvern-exchange/package.json +++ b/wyvern-exchange/package.json @@ -11,7 +11,7 @@ "test": "graph test" }, "dependencies": { - "@airstack/subgraph-generator": "file:../airstack-modules/airstack-subgraph-generator-1.0.0-alpha.3.tgz", + "@airstack/subgraph-generator": "^1.0.2-alpha.2", "@graphprotocol/graph-cli": "0.35.0", "@graphprotocol/graph-ts": "0.28.1", "@protofire/subgraph-toolkit": "^0.1.2" diff --git a/wyvern-exchange/schema.graphql b/wyvern-exchange/schema.graphql index 9492d020..4287ca86 100644 --- a/wyvern-exchange/schema.graphql +++ b/wyvern-exchange/schema.graphql @@ -1,147 +1 @@ -# -# --Airstack Schemas-- - -enum AirNetwork { - ARBITRUM_ONE - ARWEAVE_MAINNET - AURORA - AVALANCHE - BOBA - BSC # aka BNB Chain - CELO - COSMOS - CRONOS - MAINNET # Ethereum Mainnet - FANTOM - FUSE - HARMONY - JUNO - MOONBEAM - MOONRIVER - NEAR_MAINNET - OPTIMISM - OSMOSIS - MATIC # aka Polygon - XDAI # aka Gnosis Chain -} -enum AirProtocolType { - GENERIC - EXCHANGE - LENDING - YIELD - BRIDGE - DAO - NFT_MARKET_PLACE - STAKING - P2E #play to earn - LAUNCHPAD -} - -enum AirProtocolActionType { - ALL ##to track all action stats of a dapp - ### NFT Marketplace/Tokens ### - BUY - SELL - MINT - BURN # TODO check this later - ### NFT (ex: Poap) ### - ATTEND - ### P2E (NFT + Utility) ### - EARN - ### DEX ### - SWAP - ADD_LIQUIDITY - REMOVE_LIQUIDITY - ADD_TO_FARM - REMOVE_FROM_FARM - CLAIM_FARM_REWARD - ### Lending ### - LEND - BORROW - FLASH_LOAN - ### Staking / Delegating ### - STAKE - RESTAKE - UNSTAKE - DELEGATE - CLAIM_REWARDS -} - - -type AirBlock @entity { - id: ID! #chainID-number - hash: String! - number: BigInt! - timestamp: BigInt! -} - -type AirMeta @entity { - id: ID! # air_meta - network: AirNetwork! - schemaVersion: String! - slug: String! #Opensea_V1 - name: String! # Opeasea/Rarible - version: String! #Seaport/Exchange V1 -} - -type AirEntityCounter @entity { - id: ID! #Air_DAILY_STATS_ACCOUNT - count: BigInt! - createdAt: AirBlock! - lastUpdatedAt: AirBlock! -} - -type AirAccount @entity { - id: ID! - address: String! - createdAt: AirBlock! -} - -type AirToken @entity { - id: ID! #chain-address - address: String! -} - -interface AirTransaction { - id: ID ! #chain-hash-logIndex-< add as per context > NFT marketplace will have tokenId - from: AirAccount! - to: AirAccount! - hash: String! - block: AirBlock! - index: BigInt! - protocolType: AirProtocolType! - protocolActionType: AirProtocolActionType! -} - -type AirNftTransaction implements AirTransaction @entity { - id: ID! - from: AirAccount! - to: AirAccount! - hash: String! - block: AirBlock! - index: BigInt! - protocolType: AirProtocolType! - protocolActionType: AirProtocolActionType! - tokenId: BigInt! #nft - tokenAmount:BigInt! #nft - transactionToken: AirToken! #nft - paymentToken: AirToken #payment - paymentAmount: BigInt #payment - royalties: [AirNftSaleRoyalty!] @derivedFrom(field: "nftTransaction") - feeAmount: BigInt - feeBeneficiary: AirAccount - extraData: AirExtraData -} - -type AirNftSaleRoyalty @entity{ - id: ID! #AirNftTransaction(ID) + royalty beneficiary - amount: BigInt! - beneficiary: AirAccount! - nftTransaction: AirNftTransaction! -} - -type AirExtraData @entity { - id: ID! - name: String! - value: String! -} +# \ No newline at end of file diff --git a/wyvern-exchange/src/abi.ts b/wyvern-exchange/src/abi.ts index 296bb7e8..34c301b1 100644 --- a/wyvern-exchange/src/abi.ts +++ b/wyvern-exchange/src/abi.ts @@ -1,297 +1,332 @@ -import { Address, BigInt, ByteArray, Bytes, ethereum } from "@graphprotocol/graph-ts"; -import { log } from "matchstick-as"; +import { Address, BigInt, ByteArray, Bytes, crypto, ethereum } from "@graphprotocol/graph-ts" +import { ADDRESS_ZERO } from "@protofire/subgraph-toolkit" +import { log } from "matchstick-as" +// import { MissingFunctionSig, MissingTxns } from "../generated/schema" +import { BIGINT_ONE, BIG_INT_ZERO } from "../modules/airstack/common" export namespace abi { - - - export class Decoded_atomicize_Result { - method: string - addressList: Array
- transfers: Array - constructor( - _method: string, - _addressList: Array
, - _transfers: Array, - ) { - this.method = _method - this.addressList = _addressList - this.transfers = _transfers - } - } - - export class Decoded_TransferFrom_Result { - method: string - from: Address - to: Address - token: BigInt - amount: BigInt - contract: Address - constructor( - _method: string, - _from: Address, - _to: Address, - _token: BigInt, - _amount: BigInt, - _contract: Address - ) { - this.method = _method - this.from = _from - this.to = _to - this.token = _token - this.amount = _amount - this.contract = _contract - } - - public toStringArray(): string[] { - return [this.method, this.from.toHexString(), this.to.toHexString(), this.token.toString()] - } - - /* - * toString method to be used for debbuging only - */ - public toString(): string { - return this.toLogFormattedString() - } - - public toLogFormattedString(): string { - return `\n · · · · · · method( ${this.method} )\n · · · · · · from( ${this.from.toHexString()} ) \n · · · · · · to( ${this.to.toHexString()} )\n · · · · · · id( ${this.token} ) ` - } - } - - export function checkFunctionSelector(functionSelector: string): boolean { - log.info("@@checkFunctionSelector\n selector ( {} ) \n", [functionSelector]) - - return functionSelector == "0x23b872dd" || functionSelector == "0x23b872dd" || functionSelector == "0x42842e0e" || functionSelector == "0xf242432a" - } - /* - -// `matchERC1155UsingCriteria( -// address from, -// address to, -// address token, -// uint256 tokenId, -// uint256 amount, -// bytes32 root, -// bytes32[] calldata proof -// )` - + export class Decoded_atomicize_Result { + method: string + addressList: Array
+ transfers: Array + constructor( + _method: string, + _addressList: Array
, + _transfers: Array + ) { + this.method = _method + this.addressList = _addressList + this.transfers = _transfers + } + } + export class Decoded_Token { + contract: Address + tokenId: BigInt + amount: BigInt + + constructor(_contract: Address, _tokenId: BigInt, _amount: BigInt) { + this.contract = _contract + this.tokenId = _tokenId + this.amount = _amount + } + } + export class Decoded_TransferFrom_Result { + method: string + from: Address + to: Address + tokens: Decoded_Token[] + constructor(_method: string, _from: Address, _to: Address, _tokens: Decoded_Token[]) { + this.method = _method + this.from = _from + this.to = _to + this.tokens = _tokens + } + } + + export function checkFunctionSelector(functionSelector: string): boolean { + log.info("@@checkFunctionSelector\n selector ( {} ) \n", [functionSelector]) + + return ( + functionSelector == "0x23b872dd" || + functionSelector == "0xb88d4fde" || // safeTransferFrom(address,address,uint256,bytes) + functionSelector == "0x23b872dd" || + functionSelector == "0x42842e0e" || + functionSelector == "0xf242432a" || + functionSelector == "0x00f24243" // safeTransferFrom(address from,address to,uint256 tokenId,bytes memory data) + ) + } + + export function checkERC1155FunctionSelector(functionSelector: string): boolean { + return functionSelector == "0xfe99049a" || functionSelector == "0x00fe9904" + } + /* + `matchERC1155UsingCriteria( + address from, + address to, + address token, + uint256 tokenId, + uint256 amount, + bytes32 root, + bytes32[] calldata proof + )` */ - export function checkMatchERC1155UsingCriteria(functionSelector: string): boolean { - log.info("@@functionCheckMatchERC1155UsingCriteria\n selector ( {} ) \n", [functionSelector]) - return functionSelector == "0x96809f90" || functionSelector == "0x0096809f"; - } - - export function checkCallDataFunctionSelector(callData: Bytes): boolean { - let functionSelector = changetype(callData.subarray(0, 4)).toHexString() - log.info("@@checkCallDataFunctionSelector\n selector ( {} ) \n data ( {} )", [functionSelector, callData.toHexString()]) - return checkFunctionSelector(functionSelector) - } - - export function decodeBatchNftData( - buyCallData: Bytes, sellCallData: Bytes, replacementPattern: Bytes - ): Decoded_atomicize_Result { - /** - * - * atomicize(address[],uint256[],uint256[],bytes) - * - * The calldata input is formated as: - * Format => METHOD_ID (atomicize) | ? | ADDRESS_LIST_LENGTH | ADDRESS_LIST - * Size => X | Y * 4 | Y | Y * Z - * ... continues ... - * Format => ADDRESS_LIST_LENGTH | ADDRESS_LIST - * Size => Y | Y * Z - * ... continues ... - * Format => VALUES_LIST_LENGTH | VALUES_LIST - * Size => Y | Y * Z - * ... continues ... - * Format => CALLDATAS_LENGTHS_LIST_LENGTH | CALLDATAS_LENGTHS_LIST - * Size => Y | Y * Z - * ... continues ... - * Format => ... | CALLDATAS_LENGTH | CALL_DATAS - * Size => ... | Y | 2 * L - * - * Where : - * - X = 32 bits (8 hex chars) (4 Bytes) - * - Y = 256 bits (64 hex chars) (32 Bytes) - * - Z = value stored in "ADDRESS_LIST_LENGTH" section (amount of array entries), - * each address has a "Y" length - * - L = value stored in "CALLDATAS_LENGTH" section (amount of bytes), - * the total length of the callDatas Bytes bundle - */ - - - let mergedCallData = guardedArrayReplace(buyCallData, sellCallData, replacementPattern); - return decodeAbi_Atomicize_Method(mergedCallData) - } - - export function decodeAbi_Atomicize_Method(_callData: Bytes,): Decoded_atomicize_Result { - - let dataWithoutFunctionSelector: Bytes = changetype( - _callData.subarray(4) - ); - - - // As function encoding is not handled yet by the lib, we first need to reach the offset of where the - // actual params are located. As they are all dynamic we can just fetch the offset of the first param - // and then start decoding params from there as known sized types - let offset: i32 = ethereum - .decode("uint256", changetype(dataWithoutFunctionSelector))! - .toBigInt() - .toI32(); - - // Get the length of the first array. All arrays must have same length so fetching only this one is enough - let arrayLength: i32 = ethereum - .decode( - "uint256", - changetype(dataWithoutFunctionSelector.subarray(offset)) - )! - .toBigInt() - .toI32(); - offset += 1 * 32; - - - // Now that we know the size of each params we can decode them one by one as know sized types - // function atomicize(address[] addrs,uint256[] values,uint256[] calldataLengths,bytes calldatas) - let decodedAddresses: Address[] = ethereum - .decode( - `address[${arrayLength}]`, - changetype(dataWithoutFunctionSelector.subarray(offset)) - )! - .toAddressArray(); - offset += arrayLength * 32; - - offset += 1 * 32; - // We don't need those values, just move the offset forward - // let decodedValues: BigInt[] = ethereum.decode( - // `uint256[${arrayLength}]`, - // changetype(dataWithoutFunctionSelector.subarray(offset)) - // )!.toBigIntArray(); - offset += arrayLength * 32; - - offset += 1 * 32; - let decodedCalldataIndividualLengths = ethereum - .decode( - `uint256[${arrayLength}]`, - changetype(dataWithoutFunctionSelector.subarray(offset)) - )! - .toBigIntArray() - .map(e => e.toI32()); - offset += arrayLength * 32; - - let decodedCallDatasLength = ethereum - .decode( - "uint256", - changetype(dataWithoutFunctionSelector.subarray(offset)) - )! - .toBigInt() - .toI32(); - offset += 1 * 32; - - let callDatas: Bytes = changetype( - dataWithoutFunctionSelector.subarray( - offset, - offset + decodedCallDatasLength - ) - ); - - let addressList = new Array
() - let transfersList = new Array() - - let calldataOffset = 0; - for (let i = 0; i < decodedAddresses.length; i++) { - let callDataLength = decodedCalldataIndividualLengths[i]; - let calldata: Bytes = changetype( - callDatas.subarray(calldataOffset, calldataOffset + callDataLength) - ); - - // Sometime the call data is not a transferFrom (ie: https://etherscan.io/tx/0xe8629bfc57ab619a442f027c46d63e1f101bd934232405fa8e8eaf156bfca848) - // Ignore if not transferFrom - if (checkCallDataFunctionSelector(calldata)) { - addressList.push(decodedAddresses[i]); - - log.info("call data {}", [calldata.toHexString()]); - let decoded = abi.decodeAbi_transferFrom_Method(calldata) - if(decoded != null) { - transfersList.push(decoded) - } - - } - - calldataOffset += callDataLength; - } - let functionSelector = Bytes.fromUint8Array(_callData.subarray(0, 4)).toHex().slice(2) - - return new Decoded_atomicize_Result( - functionSelector, - addressList, - transfersList - ) - - } - - export function decodeSingleNftData( - txHash: string, - buyCallData: Bytes, sellCallData: Bytes, replacementPattern: Bytes,blockNo :BigInt - ): Decoded_TransferFrom_Result | null { - /** - * - * transferFrom(address,address,uint256) - * - * The calldata input is formated as: - * Format => METHOD_ID (transferFrom) | FROM | TO | TOKEN_ID - * Size => X | Y | Y | Y - * Where : - * - X = 32 bits (8 hex chars) (4 Bytes) - * - Y = 256 bits (64 hex chars) (32 Bytes) - * - * - */ - - // todo Debug this call - log.info("Before guarded Array replacement, txhash {} {} {} {}", [txHash, buyCallData.toHexString(), sellCallData.toHexString(), replacementPattern.toHexString()]) - let mergedCallData = guardedArrayReplace(buyCallData, sellCallData, replacementPattern) - return decodeAbi_transferFrom_Method(mergedCallData, txHash,blockNo) - - } - - export function decodeAbi_transferFrom_Method(callData: Bytes, txHash: string = "dummy",blockNo:BigInt = BigInt.fromI32(0)): Decoded_TransferFrom_Result | null { - /** - * callData as bytes doesn't have a trailing 0x but represents a hex string - * first 4 Bytes cointains 8 hex chars for the function selector - * 0.5 Bytes == 4 bits == 1 hex char - */ - - - let functionSelector = changetype(callData.subarray(0, 4)).toHexString() - - let dataWithoutFunctionSelector = Bytes.fromUint8Array(callData.subarray(4)) - - if(dataWithoutFunctionSelector.equals(ByteArray.fromHexString("0x"))) { - log.warning("Issue with decoding", []); - return null; - } - - if(checkFunctionSelector(functionSelector)) { - let decoded = ethereum.decode( - "(address,address,uint256)", dataWithoutFunctionSelector - )!.toTuple() - - let functionSelector = Bytes.fromUint8Array(callData.subarray(0, 4)).toHex().slice(2) - let senderAddress = decoded[0].toAddress() - let recieverAddress = decoded[1].toAddress() - let tokenId = decoded[2].toBigInt() - log.info("decoded data txHash {} {} {} {}", [txHash ,senderAddress.toHexString(), recieverAddress.toHexString(), tokenId.toString()]); - return new Decoded_TransferFrom_Result( - functionSelector, - senderAddress, - recieverAddress, - tokenId, - BigInt.fromI32(1), - Address.zero() - ) - } else if(checkMatchERC1155UsingCriteria(functionSelector)) { - - /* + export function checkPhishing(functionSelector: string): boolean { + return functionSelector == "0x7a7b6449" + } + export function checkENSFunctionSelector(functionSelector: string): boolean { + return functionSelector == "0x1e59c529" // register(string,address) + } + export function checkSafeTransferFrom(functionSelector: string): boolean { + return functionSelector == "0x00b88d4f" || functionSelector == "0xb88d4fde" // safeTransferFrom(address,address,uint256,bytes) + } + export function checkDelegateCall(functionSelector: string): boolean { + return functionSelector == "0x00d6d2b6" || functionSelector == "0xd6d2b6ba" // delegate(address,bytes) + } + export function checkBatchTxn(functionSelector: string): boolean { + return functionSelector == "0x7049e961" + } + export function checkMatchERC1155UsingCriteria(functionSelector: string): boolean { + log.info("@@functionCheckMatchERC1155UsingCriteria\n selector ( {} ) \n", [ + functionSelector, + ]) + return functionSelector == "0x96809f90" || functionSelector == "0x0096809f" + } + export function checkMatchERC721UsingCriteria(functionSelector: string): boolean { + return functionSelector == "0xfb16a595" || functionSelector == "0x00fb16a5" // matchERC721UsingCriteria(address,address,address,uint256,bytes32,bytes32[]) + } + export function checkEnjinCase(functionSelector: string): boolean { + // transferFrom(address,address,uint256,uint256) + return functionSelector == "0x00fe9904" || functionSelector == "0xfe99049a" + } + + export function checkSharedStorefront(functionSelector: string): boolean { + // mintFrom(address,address,uint256) + return functionSelector == "0x6d5cb2f5" + } + export function checkCallDataFunctionSelector(callData: Bytes): boolean { + let functionSelector = changetype(callData.subarray(0, 4)).toHexString() + log.info("@@checkCallDataFunctionSelector\n selector ( {} ) \n data ( {} )", [ + functionSelector, + callData.toHexString(), + ]) + return checkFunctionSelector(functionSelector) + } + + // took from https://etherscan.io/address/0x699c7f511c9e2182e89f29b3bfb68bd327919d17 + export function getEnsId(ensName: string): BigInt { + const nameBytes = Bytes.fromUTF8(ensName) + const hashBytes = crypto.keccak256(nameBytes) + let decoded = ethereum + .decode("(uint256)", Bytes.fromHexString(hashBytes.toHexString()))! + .toTuple() + return decoded[0].toBigInt() + } + + export function decodeBatchNftData( + buyCallData: Bytes, + sellCallData: Bytes, + replacementPattern: Bytes + ): Decoded_atomicize_Result { + /** + * + * atomicize(address[],uint256[],uint256[],bytes) + * + * The calldata input is formated as: + * Format => METHOD_ID (atomicize) | ? | ADDRESS_LIST_LENGTH | ADDRESS_LIST + * Size => X | Y * 4 | Y | Y * Z + * ... continues ... + * Format => ADDRESS_LIST_LENGTH | ADDRESS_LIST + * Size => Y | Y * Z + * ... continues ... + * Format => VALUES_LIST_LENGTH | VALUES_LIST + * Size => Y | Y * Z + * ... continues ... + * Format => CALLDATAS_LENGTHS_LIST_LENGTH | CALLDATAS_LENGTHS_LIST + * Size => Y | Y * Z + * ... continues ... + * Format => ... | CALLDATAS_LENGTH | CALL_DATAS + * Size => ... | Y | 2 * L + * + * Where : + * - X = 32 bits (8 hex chars) (4 Bytes) + * - Y = 256 bits (64 hex chars) (32 Bytes) + * - Z = value stored in "ADDRESS_LIST_LENGTH" section (amount of array entries), + * each address has a "Y" length + * - L = value stored in "CALLDATAS_LENGTH" section (amount of bytes), + * the total length of the callDatas Bytes bundle + */ + + let mergedCallData = guardedArrayReplace(buyCallData, sellCallData, replacementPattern) + return decodeAbi_Atomicize_Method(mergedCallData) + } + + export function decodeAbi_Atomicize_Method(_callData: Bytes): Decoded_atomicize_Result { + let dataWithoutFunctionSelector: Bytes = changetype(_callData.subarray(4)) + // As function encoding is not handled yet by the lib, we first need to reach the offset of where the + // actual params are located. As they are all dynamic we can just fetch the offset of the first param + // and then start decoding params from there as known sized types + let offset: i32 = ethereum + .decode("uint256", changetype(dataWithoutFunctionSelector))! + .toBigInt() + .toI32() + + // Get the length of the first array. All arrays must have same length so fetching only this one is enough + let arrayLength: i32 = ethereum + .decode("uint256", changetype(dataWithoutFunctionSelector.subarray(offset)))! + .toBigInt() + .toI32() + offset += 1 * 32 + + // Now that we know the size of each params we can decode them one by one as know sized types + // function atomicize(address[] addrs,uint256[] values,uint256[] calldataLengths,bytes calldatas) + let decodedAddresses: Address[] = ethereum + .decode( + `address[${arrayLength}]`, + changetype(dataWithoutFunctionSelector.subarray(offset)) + )! + .toAddressArray() + + offset += arrayLength * 32 + + offset += 1 * 32 + // We don't need those values, just move the offset forward + // let decodedValues: BigInt[] = ethereum.decode( + // `uint256[${arrayLength}]`, + // changetype(dataWithoutFunctionSelector.subarray(offset)) + // )!.toBigIntArray(); + offset += arrayLength * 32 + + offset += 1 * 32 + let decodedCalldataIndividualLengths = ethereum + .decode( + `uint256[${arrayLength}]`, + changetype(dataWithoutFunctionSelector.subarray(offset)) + )! + .toBigIntArray() + .map((e) => e.toI32()) + offset += arrayLength * 32 + + let decodedCallDatasLength = ethereum + .decode("uint256", changetype(dataWithoutFunctionSelector.subarray(offset)))! + .toBigInt() + .toI32() + offset += 1 * 32 + + let callDatas: Bytes = changetype( + dataWithoutFunctionSelector.subarray(offset, offset + decodedCallDatasLength) + ) + let addressList = new Array
() + let transfersList = new Array() + + let calldataOffset = 0 + for (let i = 0; i < decodedAddresses.length; i++) { + let callDataLength = decodedCalldataIndividualLengths[i] + let calldata: Bytes = changetype( + callDatas.subarray(calldataOffset, calldataOffset + callDataLength) + ) + + // Sometime the call data is not a transferFrom (ie: https://etherscan.io/tx/0xe8629bfc57ab619a442f027c46d63e1f101bd934232405fa8e8eaf156bfca848) + // Ignore if not transferFrom + if (checkCallDataFunctionSelector(calldata)) { + addressList.push(decodedAddresses[i]) + + // log.info("decodedAddresses--> {}", [decodedAddresses[i].toHexString()]) + let decoded = abi.decodeAbi_transferFrom_Method(calldata) + if (decoded != null) { + transfersList.push(decoded) + log.debug("decoded len {}", [decoded.tokens.length.toString()]) + } + } + + calldataOffset += callDataLength + } + let functionSelector = Bytes.fromUint8Array(_callData.subarray(0, 4)) + .toHex() + .slice(2) + + return new Decoded_atomicize_Result(functionSelector, addressList, transfersList) + } + + export function decodeSingleNftData( + txHash: string, + buyCallData: Bytes, + sellCallData: Bytes, + replacementPattern: Bytes + // blockNo: BigInt + ): Decoded_TransferFrom_Result | null { + /** + * + * transferFrom(address,address,uint256) + * + * The calldata input is formated as: + * Format => METHOD_ID (transferFrom) | FROM | TO | TOKEN_ID + * Size => X | Y | Y | Y + * Where : + * - X = 32 bits (8 hex chars) (4 Bytes) + * - Y = 256 bits (64 hex chars) (32 Bytes) + * + * + */ + + // todo Debug this call + log.info("Before guarded Array replacement, txhash {} {} {} {}", [ + txHash, + buyCallData.toHexString(), + sellCallData.toHexString(), + replacementPattern.toHexString(), + ]) + let mergedCallData = guardedArrayReplace(buyCallData, sellCallData, replacementPattern) + return decodeAbi_transferFrom_Method(mergedCallData, txHash) + } + + export function decodeAbi_transferFrom_Method( + callData: Bytes, + txHash: string = "dummy" + ): Decoded_TransferFrom_Result | null { + /** + * callData as bytes doesn't have a trailing 0x but represents a hex string + * first 4 Bytes cointains 8 hex chars for the function selector + * 0.5 Bytes == 4 bits == 1 hex char + */ + + let functionSelector = changetype(callData.subarray(0, 4)).toHexString() + + let dataWithoutFunctionSelector = Bytes.fromUint8Array(callData.subarray(4)) + + if (dataWithoutFunctionSelector.equals(ByteArray.fromHexString("0x"))) { + log.error("Verify,Issue with decoding hash {}", [txHash]) + return null + } + log.debug("hash {} functionSelector {}", [txHash, functionSelector.toString()]) + if (checkFunctionSelector(functionSelector)) { + let decoded = ethereum + .decode("(address,address,uint256)", dataWithoutFunctionSelector)! + .toTuple() + + let functionSelector = Bytes.fromUint8Array(callData.subarray(0, 4)) + .toHex() + .slice(2) + let senderAddress = decoded[0].toAddress() + let recieverAddress = decoded[1].toAddress() + let tokenId = decoded[2].toBigInt() + log.info("functionSelector {} decoded data txHash {} {} {} {}", [ + functionSelector, + txHash, + senderAddress.toHexString(), + recieverAddress.toHexString(), + tokenId.toString(), + ]) + let tokens = new Array() + let token = new Decoded_Token(Address.zero(), tokenId, BigInt.fromI64(1)) + tokens.push(token) + return new Decoded_TransferFrom_Result( + functionSelector, + senderAddress, + recieverAddress, + tokens + ) + } else if (checkMatchERC1155UsingCriteria(functionSelector)) { + /* // address from, // address to, // address token, @@ -302,72 +337,261 @@ export namespace abi { */ - let dataWithoutFunctionSelector = Bytes.fromUint8Array(callData.subarray(5)) - - log.info("in special case data {} {}", [txHash, dataWithoutFunctionSelector.toHexString()]); - log.info("decodng...",[]) - let decoded = ethereum.decode( - "(address,address,address,uint256,uint256)", dataWithoutFunctionSelector - )!.toTuple() - - let functionSelector = Bytes.fromUint8Array(callData.subarray(0, 4)).toHex().slice(2); - let senderAddress = decoded[0].toAddress(); - let recieverAddress = decoded[1].toAddress(); - let contract = decoded[2].toAddress(); - let tokenId = decoded[3].toBigInt(); - let amount = decoded[4].toBigInt(); + let dataWithoutFunctionSelector = Bytes.fromUint8Array(callData.subarray(5)) + let decoded = ethereum + .decode("(address,address,address,uint256,uint256)", dataWithoutFunctionSelector)! + .toTuple() + + let functionSelector = Bytes.fromUint8Array(callData.subarray(0, 4)) + .toHex() + .slice(2) + let senderAddress = decoded[0].toAddress() + let recieverAddress = decoded[1].toAddress() + let contract = decoded[2].toAddress() + let tokenId = decoded[3].toBigInt() + let amount = decoded[4].toBigInt() log.info( - "ERC1155 blockNo {} txHash {} \n senderAddress {} \n recieverAddress {} \n tokenId {} \n contract {} amount {}", - [ blockNo.toString(), - txHash, - senderAddress.toHexString(), - recieverAddress.toHexString(), - tokenId.toString(), - contract.toHexString(), - amount.toString(), - ] + "ERC1155 txHash {} \n senderAddress {} \n recieverAddress {} \n tokenId {} \n contract {} amount {} functionSelector {}", + [ + txHash, + senderAddress.toHexString(), + recieverAddress.toHexString(), + tokenId.toString(), + contract.toHexString(), + amount.toString(), + functionSelector, + ] + ) + let tokens = new Array() + let token = new Decoded_Token(contract, tokenId, amount) + tokens.push(token) + return new Decoded_TransferFrom_Result( + functionSelector, + senderAddress, + recieverAddress, + tokens + ) + } else if (checkMatchERC721UsingCriteria(functionSelector)) { + // address from, + // address to, + // IERC721 token, + // uint256 tokenId, + // bytes32 root, + // bytes32[] calldata proof + let dataWithoutFunctionSelector = Bytes.fromUint8Array(callData.subarray(5)) + let decoded = ethereum + .decode("(address,address,address,uint256)", dataWithoutFunctionSelector)! + .toTuple() + + let functionSelector = Bytes.fromUint8Array(callData.subarray(0, 4)) + .toHex() + .slice(2) + let senderAddress = decoded[0].toAddress() + let recieverAddress = decoded[1].toAddress() + let contract = decoded[2].toAddress() + let tokenId = decoded[3].toBigInt() + log.info( + "ERC721 txHash {} \n senderAddress {} \n recieverAddress {} \n tokenId {} \n contract {} functionSelector {}", + [ + txHash, + senderAddress.toHexString(), + recieverAddress.toHexString(), + tokenId.toString(), + contract.toHexString(), + functionSelector, + ] + ) + let tokens = new Array() + let token = new Decoded_Token(contract, tokenId, BIGINT_ONE) + tokens.push(token) + return new Decoded_TransferFrom_Result( + functionSelector, + senderAddress, + recieverAddress, + tokens + ) + } else if (checkENSFunctionSelector(functionSelector)) { + // hash 0xa360c37342d9f4dc76039ddda2c1ac3e1d5290aadc9ae73dda1df397482e2aa6 + let contract = Address.fromString("0xfac7bea255a6990f749363002136af6556b31e04") //TODO: verify with future txns + + let prefixStr = "0x0000000000000000000000000000000000000000000000000000000000000020" + let fixedData = prefixStr + dataWithoutFunctionSelector.toHexString().substring(2) + let decoded = ethereum + .decode("(string,address)", Bytes.fromHexString(fixedData))! + .toTuple() + let ensName = decoded[0].toString() + let recieverAddress = decoded[1].toAddress() + let tokenId = getEnsId(ensName) + + let tokens = new Array() + let token = new Decoded_Token(contract, tokenId, BIGINT_ONE) + tokens.push(token) + return new Decoded_TransferFrom_Result( + functionSelector, + Address.fromString(ADDRESS_ZERO), + recieverAddress, + tokens + ) + } else if (checkSharedStorefront(functionSelector)) { + let contract = Address.fromString("0x5fbef9fcb449d56154980e52e165d9650b9f6ec2") //TODO: verify with future txns + + let decoded = ethereum + .decode("(address,address,uint256)", dataWithoutFunctionSelector)! + .toTuple() + let from = decoded[0].toAddress() + let to = decoded[1].toAddress() + let tokenId = decoded[2].toBigInt() + log.debug("checkSharedStorefront txHash {} from {} to {} tokenId {}", [ + txHash, + from.toHexString(), + to.toHexString(), + tokenId.toString(), + ]) + let tokens = new Array() + let token = new Decoded_Token(contract, tokenId, BIGINT_ONE) + tokens.push(token) + return new Decoded_TransferFrom_Result(functionSelector, from, to, tokens) + } else if (checkEnjinCase(functionSelector)) { + let contract = Address.fromString("0x8562c38485B1E8cCd82E44F89823dA76C98eb0Ab") //TODO: verify with future txns + let dataWithoutFunctionSelectorStr = "0x" + callData.toHexString().split("fe99049a")[1] + let decoded = ethereum + .decode( + "(address,address,uint256,uint256)", + Bytes.fromHexString(dataWithoutFunctionSelectorStr) + )! + .toTuple() + let from = decoded[0].toAddress() + let to = decoded[1].toAddress() + let tokenId = decoded[2].toBigInt() + let openseaAmt = decoded[3].toBigInt() + if (openseaAmt.gt(BigInt.fromI64(1))) { + log.debug("nonNftcase txHash {} has some issue with openseaAmt {} ", [ + txHash, + openseaAmt.toString(), + ]) + throw new Error("Enjin case tokenAmount greater than one") + } + log.debug("checkEnjinCase txHash {} from {} to {} tokenId {}", [ + txHash, + from.toHexString(), + to.toHexString(), + tokenId.toString(), + ]) + let tokens = new Array() + let token = new Decoded_Token(contract, tokenId, BIGINT_ONE) + tokens.push(token) + + return new Decoded_TransferFrom_Result(functionSelector, from, to, tokens) + } else if (checkSafeTransferFrom(functionSelector)) { + let dataWithoutFunctionSelectorStr = "0x" + callData.toHexString().split("b88d4fde")[1] + let decoded = ethereum + .decode( + "(address,address,uint256)", + Bytes.fromHexString(dataWithoutFunctionSelectorStr) + )! + .toTuple() + let from = decoded[0].toAddress() + let to = decoded[1].toAddress() + let tokenId = decoded[2].toBigInt() + log.debug("checkSafeTransferFrom txhash {} from {} to {} tokenId {} ", [ + txHash, + from.toHexString(), + to.toHexString(), + tokenId.toString(), + ]) + let tokens = new Array() + let token = new Decoded_Token(Address.zero(), tokenId, BIGINT_ONE) + tokens.push(token) + return new Decoded_TransferFrom_Result(functionSelector, from, to, tokens) + } else if (checkDelegateCall(functionSelector)) { + let dataWithoutFunctionSelectorStr = "0x" + callData.toHexString().substring(212) + log.error("checkDelegateCall txhash {} dataWithoutFunctionSelectorStr {} ", [ + txHash, + dataWithoutFunctionSelectorStr, + ]) + let decoded = ethereum + .decode( + "(address,address,address,address,address,address,address,uint256)", + Bytes.fromHexString(dataWithoutFunctionSelectorStr) + )! + .toTuple() + + let from = decoded[0].toAddress() + let to = decoded[1].toAddress() + let contract = decoded[5].toAddress() + let tokenId = decoded[7].toBigInt() + + log.error("checkDelegateCall txhash {} from {} to {} contract {} tokenId {} ", [ + txHash, + from.toHexString(), + to.toHexString(), + contract.toHexString(), + tokenId.toString(), + ]) + let tokens = new Array() + let token = new Decoded_Token(contract, tokenId, BIGINT_ONE) + tokens.push(token) + return new Decoded_TransferFrom_Result(functionSelector, from, to, tokens) + } else if (checkBatchTxn(functionSelector)) { + let dataWithoutFunctionSelectorStr = "0x" + callData.toHexString().substring(74) + + let decoded = ethereum + .decode( + "(address,address,uint256)", + Bytes.fromHexString(dataWithoutFunctionSelectorStr) + )! + .toTuple() + let from = decoded[0].toAddress() + let to = decoded[1].toAddress() + let arrayLen = decoded[2].toBigInt() + let encodedTokenArray = dataWithoutFunctionSelectorStr.substring(194) + let tokens = new Array() + for (let i = 0; i < arrayLen.toI64(); i++) { + let encoded = encodedTokenArray.substring(i * 128, i * 128 + 128) + let decoded = ethereum + .decode("(address,uint256)", Bytes.fromHexString("0x" + encoded))! + .toTuple() + let tokenAddress = decoded[0].toAddress() + let tokenId = decoded[1].toBigInt() + let token = new Decoded_Token(tokenAddress, tokenId, BIGINT_ONE) + tokens.push(token) + } + return new Decoded_TransferFrom_Result(functionSelector, from, to, tokens) + } else if (checkPhishing(functionSelector)) { + log.error("Verify,phishing case,txHash {}", [txHash]) + return null + } else { + log.error( + `We dont understanding decoding {} functionSelector {} dataWithoutFunctionSelector {} callData {}`, + [txHash, functionSelector, dataWithoutFunctionSelector.toHex(), callData.toHex()] ) - - return new Decoded_TransferFrom_Result( - functionSelector, - senderAddress, - recieverAddress, - tokenId, - amount, - contract - ) - - } else { - log.warning(`We dont understanding decoding {} `,[txHash]); - return null; - } - - } - - export function guardedArrayReplace(_array: Bytes, _replacement: Bytes, _mask: Bytes): Bytes { - // Sometime the replacementPattern is empty, meaning that both arrays (buyCallData and sellCallData) are identicall and - // no merging is necessary. In such a case randomly return the first array (buyCallData) - if (_mask.length == 0) { - return _array; - } - - // copies Bytes Array to avoid buffer overwrite - let array = Bytes.fromUint8Array(_array.slice(0)) - let replacement = Bytes.fromUint8Array(_replacement.slice(0)) - let mask = Bytes.fromUint8Array(_mask.slice(0)) - - array.reverse(); - replacement.reverse(); - mask.reverse(); - - let bigIntgArray = BigInt.fromUnsignedBytes(array); - let bigIntReplacement = BigInt.fromUnsignedBytes(replacement); - let bigIntMask = BigInt.fromUnsignedBytes(mask); - - // array |= replacement & mask; - bigIntReplacement = bigIntReplacement.bitAnd(bigIntMask); - bigIntgArray = bigIntgArray.bitOr(bigIntReplacement); - return changetype(Bytes.fromBigInt(bigIntgArray).reverse()); - - } -} \ No newline at end of file + throw new Error("We dont understanding decoding") + } + } + + export function guardedArrayReplace(_array: Bytes, _replacement: Bytes, _mask: Bytes): Bytes { + // Sometime the replacementPattern is empty, meaning that both arrays (buyCallData and sellCallData) are identicall and + // no merging is necessary. In such a case randomly return the first array (buyCallData) + if (_mask.length == 0) { + return _array + } + + // copies Bytes Array to avoid buffer overwrite + let array = Bytes.fromUint8Array(_array.slice(0)) + let replacement = Bytes.fromUint8Array(_replacement.slice(0)) + let mask = Bytes.fromUint8Array(_mask.slice(0)) + + array.reverse() + replacement.reverse() + mask.reverse() + + let bigIntgArray = BigInt.fromUnsignedBytes(array) + let bigIntReplacement = BigInt.fromUnsignedBytes(replacement) + let bigIntMask = BigInt.fromUnsignedBytes(mask) + + // array |= replacement & mask; + bigIntReplacement = bigIntReplacement.bitAnd(bigIntMask) + bigIntgArray = bigIntgArray.bitOr(bigIntReplacement) + return changetype(Bytes.fromBigInt(bigIntgArray).reverse()) + } +} diff --git a/wyvern-exchange/src/utils.ts b/wyvern-exchange/src/utils.ts index 3745c825..20c38f9b 100644 --- a/wyvern-exchange/src/utils.ts +++ b/wyvern-exchange/src/utils.ts @@ -2,3 +2,4 @@ export const ETHEREUM_MAINNET_ID = "1"; export const TRANSACTION_TYPE_SALE = "SALE"; export const MARKET_PLACE_TYPE = "NFT_MARKET_PLACE"; export const PROTOCOL_SELL_ACTION_TYPE = "SELL"; +export const ENS_WALLET = "0x699c7f511c9e2182e89f29b3bfb68bd327919d17" \ No newline at end of file diff --git a/wyvern-exchange/src/wyvern-exchange.ts b/wyvern-exchange/src/wyvern-exchange.ts index 14e9f1db..91bffed5 100644 --- a/wyvern-exchange/src/wyvern-exchange.ts +++ b/wyvern-exchange/src/wyvern-exchange.ts @@ -1,231 +1,362 @@ -import { Address, BigDecimal, BigInt, log } from "@graphprotocol/graph-ts" +import { Address, BigDecimal, BigInt, Bytes, log } from "@graphprotocol/graph-ts" -import { - AtomicMatch_Call -} from "../generated/WyvernExchange/WyvernExchange" +import { AtomicMatch_Call } from "../generated/WyvernExchange/WyvernExchange" -import { orders } from "./orders"; +import { orders } from "./orders" -import * as airstack from "../modules/airstack"; -import { abi } from "./abi"; -import { BIGINT_HUNDRED, EXCHANGE_MARKETPLACE_FEE, INVERSE_BASIS_POINT } from "./shared"; -import { ETHEREUM_MAINNET_ID, MARKET_PLACE_TYPE, PROTOCOL_SELL_ACTION_TYPE } from "./utils"; +import * as airstack from "../modules/airstack/nft-marketplace" +import { abi } from "./abi" +import { BIGINT_HUNDRED, EXCHANGE_MARKETPLACE_FEE, INVERSE_BASIS_POINT } from "./shared" +import { + ENS_WALLET, + ETHEREUM_MAINNET_ID, + MARKET_PLACE_TYPE, + PROTOCOL_SELL_ACTION_TYPE, +} from "./utils" +import { BIGINT_ONE, BIG_INT_ZERO } from "../modules/airstack/common" -export function handleAtomicMatch_(call: AtomicMatch_Call): void { - log.info("txHash {}", [call.transaction.hash.toHexString()]); - let txHash = call.transaction.hash; - let timestamp = call.block.timestamp; - let sellTakerAddress = call.inputs.addrs[9]; - let paymentToken = call.inputs.addrs[6]; - - let saleTarget = call.inputs.addrs[11]; - let isBundleSale = - saleTarget.toHexString() === orders.constants.WYVERN_ATOMICIZER_ADDRESS; - - let contractAddress = call.inputs.addrs[11]; - - // buy Order - let buyOrder: orders.Order = new orders.Order( - call.inputs.addrs[0], //exchange: - call.inputs.addrs[1].toHexString(), //maker: - call.inputs.addrs[2].toHexString(), // taker: - call.inputs.uints[0], //makerRelayerFee: - call.inputs.uints[1], //takerRelayerFee: - call.inputs.uints[2], //makerProtocolFee: - call.inputs.uints[3], //takerProtocolFee: - call.inputs.addrs[3].toHex(), //feeRecipient: - orders.helpers.getFeeMethod(call.inputs.feeMethodsSidesKindsHowToCalls[0]), //feeMethod: - orders.helpers.getOrderSide(call.inputs.feeMethodsSidesKindsHowToCalls[1]), //side: - orders.helpers.getSaleKind(call.inputs.feeMethodsSidesKindsHowToCalls[2]), //saleKind: - call.inputs.addrs[4].toHexString(), //target: - orders.helpers.getHowToCall(call.inputs.feeMethodsSidesKindsHowToCalls[3]), //howToCall: - call.inputs.calldataBuy, //callData: - call.inputs.replacementPatternBuy, //replacementPattern: - call.inputs.addrs[5], //staticTarget: - call.inputs.staticExtradataBuy, //staticExtradata: - paymentToken.toHexString(), //paymentToken: - call.inputs.uints[4], //basePrice: - call.inputs.uints[5], //extra: - call.inputs.uints[6], //listingTime: - call.inputs.uints[7], //expirationTIme: - call.inputs.uints[8] // salt: - ); - - log.info("txHash {} buyOrder maker {} taker {} makerProtocolFee {} takerProtocolFee {} makerRelayerFee {} takerRelayerFee {} feeRecipient {} feeMethod {} side {} saleKind {} exchange {} target{}", - [ - txHash.toHexString(), - buyOrder.maker, - buyOrder.taker, - buyOrder.makerProtocolFee.toString(), - buyOrder.takerProtocolFee.toString(), - buyOrder.makerRelayerFee.toString(), - buyOrder.takerRelayerFee.toString(), - buyOrder.feeRecipient, - buyOrder.feeMethod, - buyOrder.side, - buyOrder.saleKind, - buyOrder.exchange.toHexString(), - buyOrder.target - ] - ) - - let sellOrder: orders.Order = new orders.Order( - call.inputs.addrs[7], //exchange: - call.inputs.addrs[8].toHexString(), //maker: - sellTakerAddress.toHexString(), //taker: - call.inputs.uints[9], //makerRelayerFee: - call.inputs.uints[10], // takerRelayerFee: - call.inputs.uints[11], //makerProtocolFee: - call.inputs.uints[12], //takerProtocolFee: - call.inputs.addrs[10].toHexString(), // feeRecipient: - orders.helpers.getFeeMethod(call.inputs.feeMethodsSidesKindsHowToCalls[4]), //feeMethod: - orders.helpers.getOrderSide(call.inputs.feeMethodsSidesKindsHowToCalls[5]), //side: - orders.helpers.getSaleKind(call.inputs.feeMethodsSidesKindsHowToCalls[6]), //saleKind: - call.inputs.addrs[11].toHexString(), //target: - orders.helpers.getHowToCall(call.inputs.feeMethodsSidesKindsHowToCalls[7]), //howToCall: - call.inputs.calldataSell, // callData: - call.inputs.replacementPatternSell, //replacementPattern: - call.inputs.addrs[12], //staticTarget: - call.inputs.staticExtradataSell, //staticExtradata: - paymentToken.toHexString(), //paymentToken: - call.inputs.uints[13], //basePrice: - call.inputs.uints[14], //extra: - call.inputs.uints[15], //listingTime: - call.inputs.uints[16], //expirationTIme: - call.inputs.uints[17] //salt: - ); - - log.info("txHash {} sellOrder maker {} taker {} makerProtocolFee {} takerProtocolFee {} makerRelayerFee {} takerRelayerFee {} feeRecipient {} feeMethod {} side {} saleKind {} exchange {} target{}", - [ - txHash.toHexString(), - sellOrder.maker, - sellOrder.taker, - sellOrder.makerProtocolFee.toString(), - sellOrder.takerProtocolFee.toString(), - sellOrder.makerRelayerFee.toString(), - sellOrder.takerRelayerFee.toString(), - sellOrder.feeRecipient, - sellOrder.feeMethod, - sellOrder.side, - sellOrder.saleKind, - sellOrder.exchange.toHexString(), - sellOrder.target - ] - ) - - let matchPrice = orders.helpers.calculateMatchPrice( - buyOrder, - sellOrder, - timestamp - ); - - let allSales = new Array(); - - if (isBundleSale) { - // log.info("txHash {} bundle sale", [txHash.toHexString()]); - let decoded = abi.decodeBatchNftData( - buyOrder.callData!, - sellOrder.callData!, - buyOrder.replacementPattern! - ); - - for (let i = 0; i < decoded.transfers.length; i++) { - log.info("txHash {} transfer method {}", [txHash.toHexString(), decoded.transfers[i].method]); - let nft = new airstack.nft.NFT(decoded.addressList[i], decoded.transfers[i].method, decoded.transfers[i].token, decoded.transfers[i].amount); - // let feeAmount = BigInt.zero(); - - let royaltyDetails = new royaltyResult(); - if (sellOrder.feeRecipient!=Address.zero().toHexString()) { - royaltyDetails = calculateRoyality(sellOrder.makerRelayerFee, sellOrder.feeRecipient , matchPrice); - }else{ - royaltyDetails = calculateRoyality(sellOrder.takerRelayerFee, sellOrder.feeRecipient , matchPrice); - } - let marketplaceRevenueETH = royaltyDetails.marketplaceRevenueETH; - let feeRecipient = royaltyDetails.feeRecipient; - log.info("txHash bundleSale {} feeRecipient {}", [txHash.toHexString(), feeRecipient]); - let sale = new airstack.nft.Sale(decoded.transfers[i].to, decoded.transfers[i].from, nft, matchPrice, paymentToken, marketplaceRevenueETH, Address.fromString(feeRecipient), new Array()); - allSales.push(sale); - } - } else { - log.info("txHash {} not bundle sale", [txHash.toHexString()]); - let decoded = abi.decodeSingleNftData( - txHash.toHexString(), - buyOrder.callData!, - sellOrder.callData!, - buyOrder.replacementPattern!, - call.block.number +export function atomicMatch( + txHash: string, + blockTimeStamp: BigInt, + addrs: Array
, + uints: Array, + feeMethodsSidesKindsHowToCalls: Array, + calldataBuy: Bytes, + calldataSell: Bytes, + replacementPatternBuy: Bytes, + replacementPatternSell: Bytes, + staticExtradataBuy: Bytes, + staticExtradataSell: Bytes, + vs: Array, + rssMetadata: Array +): airstack.nft.Sale | null { + let sellTakerAddress = addrs[9] + let paymentToken = addrs[6] + let saleTarget = addrs[11] + let isBundleSale = saleTarget.toHexString() == orders.constants.WYVERN_ATOMICIZER_ADDRESS + let contractAddress = addrs[11] + // buy Order + let buyOrder: orders.Order = new orders.Order( + addrs[0], //exchange: + addrs[1].toHexString(), //maker: + addrs[2].toHexString(), // taker: + uints[0], //makerRelayerFee: + uints[1], //takerRelayerFee: + uints[2], //makerProtocolFee: + uints[3], //takerProtocolFee: + addrs[3].toHex(), //feeRecipient: + orders.helpers.getFeeMethod(feeMethodsSidesKindsHowToCalls[0]), //feeMethod: + orders.helpers.getOrderSide(feeMethodsSidesKindsHowToCalls[1]), //side: + orders.helpers.getSaleKind(feeMethodsSidesKindsHowToCalls[2]), //saleKind: + addrs[4].toHexString(), //target: + orders.helpers.getHowToCall(feeMethodsSidesKindsHowToCalls[3]), //howToCall: + calldataBuy, //callData: + replacementPatternBuy, //replacementPattern: + addrs[5], //staticTarget: + staticExtradataBuy, //staticExtradata: + paymentToken.toHexString(), //paymentToken: + uints[4], //basePrice: + uints[5], //extra: + uints[6], //listingTime: + uints[7], //expirationTIme: + uints[8] // salt: ) - if (decoded == null) { - return; - } + log.info( + "txHash {} buyOrder maker {} taker {} makerProtocolFee {} takerProtocolFee {} makerRelayerFee {} takerRelayerFee {} feeRecipient {} feeMethod {} side {} saleKind {} exchange {} target{}", + [ + txHash, + buyOrder.maker, + buyOrder.taker, + buyOrder.makerProtocolFee.toString(), + buyOrder.takerProtocolFee.toString(), + buyOrder.makerRelayerFee.toString(), + buyOrder.takerRelayerFee.toString(), + buyOrder.feeRecipient, + buyOrder.feeMethod, + buyOrder.side, + buyOrder.saleKind, + buyOrder.exchange.toHexString(), + buyOrder.target, + ] + ) + let sellOrder: orders.Order = new orders.Order( + addrs[7], //exchange: + addrs[8].toHexString(), //maker: + sellTakerAddress.toHexString(), //taker: + uints[9], //makerRelayerFee: + uints[10], // takerRelayerFee: + uints[11], //makerProtocolFee: + uints[12], //takerProtocolFee: + addrs[10].toHexString(), // feeRecipient: + orders.helpers.getFeeMethod(feeMethodsSidesKindsHowToCalls[4]), //feeMethod: + orders.helpers.getOrderSide(feeMethodsSidesKindsHowToCalls[5]), //side: + orders.helpers.getSaleKind(feeMethodsSidesKindsHowToCalls[6]), //saleKind: + addrs[11].toHexString(), //target: + orders.helpers.getHowToCall(feeMethodsSidesKindsHowToCalls[7]), //howToCall: + calldataSell, // callData: + replacementPatternSell, //replacementPattern: + addrs[12], //staticTarget: + staticExtradataSell, //staticExtradata: + paymentToken.toHexString(), //paymentToken: + uints[13], //basePrice: + uints[14], //extra: + uints[15], //listingTime: + uints[16], //expirationTIme: + uints[17] //salt: + ) + log.info( + "txHash {} sellOrder maker {} taker {} makerProtocolFee {} takerProtocolFee {} makerRelayerFee {} takerRelayerFee {} feeRecipient {} feeMethod {} side {} saleKind {} exchange {} target{}", + [ + txHash, + sellOrder.maker, + sellOrder.taker, + sellOrder.makerProtocolFee.toString(), + sellOrder.takerProtocolFee.toString(), + sellOrder.makerRelayerFee.toString(), + sellOrder.takerRelayerFee.toString(), + sellOrder.feeRecipient, + sellOrder.feeMethod, + sellOrder.side, + sellOrder.saleKind, + sellOrder.exchange.toHexString(), + sellOrder.target, + ] + ) + + let matchPrice = orders.helpers.calculateMatchPrice(buyOrder, sellOrder, blockTimeStamp) + + if (isBundleSale) { + log.debug("txHash {} isbundlesale", [txHash]) + let decoded = abi.decodeBatchNftData( + buyOrder.callData, + sellOrder.callData, + buyOrder.replacementPattern + ) + if (decoded.addressList.length != decoded.transfers.length) { + log.error("bundleSale addressList len != transfers len,txHash {}", [txHash]) + } + let from = Address.zero() + let to = Address.zero() + let nfts: airstack.nft.NFT[] = [] + for (let i = 0; i < decoded.transfers.length; i++) { + let transfer = decoded.transfers[i] + log.debug("from {} to {} method {} ", [ + transfer.from.toHexString(), + transfer.to.toHexString(), + transfer.method, + ]) + if (from == Address.zero()) { + from = transfer.from + } else if (from != transfer.from) { + log.error("from address change not expected,txhash {}", [txHash]) + throw new Error("from address change not expected") + } + if (to == Address.zero()) { + to = transfer.to + } else if (to != transfer.to) { + log.error("to address change not expected,txhash {}", [txHash]) + throw new Error("to address change not expected") + } + if (transfer.tokens.length > 1) { + log.error("transferlen >1 isn't expected,txhash {}", [txHash]) + throw new Error("transferlen >1 isn't expected") + } + let token = transfer.tokens[0] + let nft = new airstack.nft.NFT(decoded.addressList[i], token.tokenId, token.amount) + log.debug("bundleSale hash {} collection {} tokenId {} amount {}", [ + txHash, + nft.collection.toHexString(), + nft.tokenId.toString(), + nft.amount.toString(), + ]) + nfts.push(nft) + } + let royaltyDetails = new royaltyResult() + if (sellOrder.feeRecipient != Address.zero().toHexString()) { + royaltyDetails = calculateRoyality( + sellOrder.makerRelayerFee, + sellOrder.feeRecipient, + matchPrice + ) + } else { + royaltyDetails = calculateRoyality( + sellOrder.takerRelayerFee, + sellOrder.feeRecipient, + matchPrice + ) + } + let feeRecipient = royaltyDetails.feeRecipient + let totalRevenueETH = royaltyDetails.totalRevenueETH + if (from == Address.zero() || to == Address.zero()) { + log.error("from or to is zero, from {} to {} ,txHash {}", [ + from.toHexString(), + to.toHexString(), + txHash, + ]) + throw new Error("from or to is zero address") + } + log.error( + "bundle hash {} from {} to {} matchPrice {} paymentToken {} feeRecipient {} protocolFees {}", + [ + txHash, + from.toHexString(), + to.toHexString(), + matchPrice.toString(), + paymentToken.toHexString(), + feeRecipient.toString(), + totalRevenueETH.toString(), + ] + ) + + let sale = new airstack.nft.Sale( + to, + from, + nfts, + matchPrice, + paymentToken, + totalRevenueETH, + Address.fromString(feeRecipient), + new Array() + ) + return sale + } else { + log.info("txHash {} not bundle sale", [txHash]) + let decoded = abi.decodeSingleNftData( + txHash, + buyOrder.callData, + sellOrder.callData, + buyOrder.replacementPattern + ) + if (decoded == null) { + log.debug("missing record {} ", [txHash]) + return null + } - contractAddress = - decoded.contract != Address.zero() ? decoded.contract : contractAddress; + let royaltyDetails = new royaltyResult() + if (sellOrder.feeRecipient != Address.zero().toHexString()) { + royaltyDetails = calculateRoyality( + sellOrder.makerRelayerFee, + sellOrder.feeRecipient, + matchPrice + ) + } else { + royaltyDetails = calculateRoyality( + sellOrder.takerRelayerFee, + buyOrder.feeRecipient, + matchPrice + ) + } + let feeRecipient = royaltyDetails.feeRecipient + let totalRevenueETH = royaltyDetails.totalRevenueETH + // let creatorRevenueETH = royaltyDetails.creatorRevenueETH + // creatorRevenueETH = matchPrice.minus(totalRevenueETH) + let nfts: airstack.nft.NFT[] = [] + for (let i = 0; i < decoded.tokens.length; i++) { + const token = decoded.tokens[i] + contractAddress = token.contract != Address.zero() ? token.contract : contractAddress + let nft = new airstack.nft.NFT(contractAddress, token.tokenId, token.amount) + nfts.push(nft) + } - log.info("txHash {} transfer method {}", [txHash.toHexString(), decoded.method]); - let nft = new airstack.nft.NFT(contractAddress, decoded.method, decoded.token, decoded.amount); - let royaltyDetails = new royaltyResult(); - if (sellOrder.feeRecipient!=Address.zero().toHexString()) { - royaltyDetails = calculateRoyality(sellOrder.makerRelayerFee, sellOrder.feeRecipient , matchPrice); - }else{ - royaltyDetails = calculateRoyality(sellOrder.takerRelayerFee, sellOrder.feeRecipient , matchPrice); + log.debug( + // protocolFeesBeneficiary: Address + "sale decoded buyer {} seller {} paymentAmount {} paymentToken {} protocolFees {} protocolFeesBeneficiary {} ", + [ + decoded.to.toHexString(), + decoded.from.toHexString(), + matchPrice.toString(), + paymentToken.toHexString(), + totalRevenueETH.toString(), + feeRecipient, + ] + ) + for (let i = 0; i < nfts.length; i++) { + const nft = nfts[i] + log.debug("nft collection {} tokenId {} amount {} ", [ + nft.collection.toHexString(), + nft.tokenId.toString(), + nft.amount.toString(), + ]) + } + let sale = new airstack.nft.Sale( + decoded.to, + decoded.from, + nfts, + matchPrice, + paymentToken, + totalRevenueETH, + Address.fromString(feeRecipient), + new Array() + ) + return sale + } +} + +export function handleAtomicMatch_(call: AtomicMatch_Call): void { + log.error("txhash {} blockNo {}", [ + call.transaction.hash.toHexString(), + call.block.number.toString(), + ]) + let sale = atomicMatch( + call.transaction.hash.toHexString(), + call.block.timestamp, + call.inputs.addrs, + call.inputs.uints, + call.inputs.feeMethodsSidesKindsHowToCalls, + call.inputs.calldataBuy, + call.inputs.calldataSell, + call.inputs.replacementPatternBuy, + call.inputs.replacementPatternSell, + call.inputs.staticExtradataBuy, + call.inputs.staticExtradataSell, + call.inputs.vs, + call.inputs.rssMetadata + ) + if (sale != null) { + airstack.nft.trackNFTSaleTransactions( + call.block, + call.transaction.hash.toHexString(), + call.transaction.index, + sale, + MARKET_PLACE_TYPE, + PROTOCOL_SELL_ACTION_TYPE + ) } - let feeRecipient = royaltyDetails.feeRecipient; - let totalRevenueETH = royaltyDetails.totalRevenueETH; - let creatorRevenueETH = royaltyDetails.creatorRevenueETH; - creatorRevenueETH = matchPrice.minus(totalRevenueETH); - log.info("txHash not bundleSale {} totalRevenueETH {} creatorRevenueETH {} feeRecipient {}", [txHash.toHexString(), totalRevenueETH.toString(), creatorRevenueETH.toString(), feeRecipient]); - let sale = new airstack.nft.Sale(decoded.to, decoded.from, nft, matchPrice, paymentToken, totalRevenueETH, Address.fromString(feeRecipient), new Array()); - allSales.push(sale); - } - - airstack.nft.trackNFTSaleTransactions( - ETHEREUM_MAINNET_ID, - txHash.toHexString(), - call.transaction.index, - allSales, - MARKET_PLACE_TYPE, - PROTOCOL_SELL_ACTION_TYPE, - timestamp, - call.block.number, - call.block.hash.toHexString() - ) } class royaltyResult { - constructor( - public creatorRoyaltyFeePercentage: BigInt = BigInt.zero(), - public totalRevenueETH: BigInt= BigInt.zero(), - public marketplaceRevenueETH: BigInt= BigInt.zero(), - public creatorRevenueETH: BigInt= BigInt.zero(), - public feeRecipient: string = '') {} + constructor( + public creatorRoyaltyFeePercentage: BigInt = BigInt.zero(), + public totalRevenueETH: BigInt = BigInt.zero(), + public marketplaceRevenueETH: BigInt = BigInt.zero(), + public creatorRevenueETH: BigInt = BigInt.zero(), + public feeRecipient: string = "" + ) {} } // Calculate the royalty/revenue fees by using the known opensea marketplace fee of 2.5% on all trades // Here we calculate the royalty by taking fee payment and subtracting it from the marketplace fee. // https://github.com/ProjectWyvern/wyvern-ethereum/blob/master/contracts/exchange/ExchangeCore.sol -export function calculateRoyality(fee: BigInt, feeRecipient: string, matchPrice: BigInt): royaltyResult { - let royaltyDetails = new royaltyResult(); - royaltyDetails.creatorRoyaltyFeePercentage = EXCHANGE_MARKETPLACE_FEE.le( - fee - ) - ? fee - .minus(EXCHANGE_MARKETPLACE_FEE) - .div(BIGINT_HUNDRED) - : BigInt.zero(); - - royaltyDetails.totalRevenueETH = fee - .times(matchPrice) - .div(INVERSE_BASIS_POINT); - - royaltyDetails.marketplaceRevenueETH = EXCHANGE_MARKETPLACE_FEE.le(fee) - ? EXCHANGE_MARKETPLACE_FEE - .times(matchPrice) - .div(INVERSE_BASIS_POINT) - : BigInt.zero(); - royaltyDetails.creatorRevenueETH = royaltyDetails.totalRevenueETH.minus(royaltyDetails.marketplaceRevenueETH); - - royaltyDetails.feeRecipient =feeRecipient; - return royaltyDetails; -} \ No newline at end of file +export function calculateRoyality( + fee: BigInt, + feeRecipient: string, + matchPrice: BigInt +): royaltyResult { + let royaltyDetails = new royaltyResult() + royaltyDetails.creatorRoyaltyFeePercentage = EXCHANGE_MARKETPLACE_FEE.le(fee) + ? fee.minus(EXCHANGE_MARKETPLACE_FEE).div(BIGINT_HUNDRED) + : BigInt.zero() + + royaltyDetails.totalRevenueETH = fee.times(matchPrice).div(INVERSE_BASIS_POINT) + + royaltyDetails.marketplaceRevenueETH = EXCHANGE_MARKETPLACE_FEE.le(fee) + ? EXCHANGE_MARKETPLACE_FEE.times(matchPrice).div(INVERSE_BASIS_POINT) + : BigInt.zero() + royaltyDetails.creatorRevenueETH = royaltyDetails.totalRevenueETH.minus( + royaltyDetails.marketplaceRevenueETH + ) + + royaltyDetails.feeRecipient = feeRecipient + return royaltyDetails +} diff --git a/wyvern-exchange/subgraph.yaml b/wyvern-exchange/subgraph.yaml index b04b45eb..06682cab 100644 --- a/wyvern-exchange/subgraph.yaml +++ b/wyvern-exchange/subgraph.yaml @@ -14,17 +14,7 @@ dataSources: apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - OrderApprovedPartOne - - OrderApprovedPartTwo - - OrderCancelled - - OrdersMatched - - OwnershipRenounced - - OwnershipTransferred - - AirAccount, - - AirNftTransaction, - - AirToken, - - AirBlock, - - AirNftSaleRoyalty + abis: - name: WyvernExchange file: ./abis/WyvernExchange.json @@ -44,18 +34,7 @@ dataSources: apiVersion: 0.0.6 language: wasm/assemblyscript entities: - - ContractOrderApprovedPartOne - - ContractOrderApprovedPartTwo - - ContractOrderCancelled - - ContractOrdersMatched - - NonceIncremented - - ContractOwnershipRenounced - - ContractOwnershipTransferred - - AirAccount, - - AirNftTransaction, - - AirToken, - - AirBlock, - - AirNftSaleRoyalty + abis: - name: Contract file: ./abis/Contract.json diff --git a/wyvern-exchange/tests/contract-utils.ts b/wyvern-exchange/tests/contract-utils.ts deleted file mode 100644 index 7b81a778..00000000 --- a/wyvern-exchange/tests/contract-utils.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { newMockEvent } from "matchstick-as" -import { ethereum, Bytes, Address, BigInt } from "@graphprotocol/graph-ts" -import { - ContractOrderApprovedPartOne, - ContractOrderApprovedPartTwo, - ContractOrderCancelled, - ContractOrdersMatched, - NonceIncremented, - ContractOwnershipRenounced, - ContractOwnershipTransferred -} from "../generated/Contract/Contract" - -export function createContractOrderApprovedPartOneEvent( - hash: Bytes, - exchange: Address, - maker: Address, - taker: Address, - makerRelayerFee: BigInt, - takerRelayerFee: BigInt, - makerProtocolFee: BigInt, - takerProtocolFee: BigInt, - feeRecipient: Address, - feeMethod: i32, - side: i32, - saleKind: i32, - target: Address -): ContractOrderApprovedPartOne { - let contractOrderApprovedPartOneEvent = changetype< - ContractOrderApprovedPartOne - >(newMockEvent()) - - contractOrderApprovedPartOneEvent.parameters = new Array() - - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("hash", ethereum.Value.fromFixedBytes(hash)) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("exchange", ethereum.Value.fromAddress(exchange)) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("taker", ethereum.Value.fromAddress(taker)) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "makerRelayerFee", - ethereum.Value.fromUnsignedBigInt(makerRelayerFee) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "takerRelayerFee", - ethereum.Value.fromUnsignedBigInt(takerRelayerFee) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "makerProtocolFee", - ethereum.Value.fromUnsignedBigInt(makerProtocolFee) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "takerProtocolFee", - ethereum.Value.fromUnsignedBigInt(takerProtocolFee) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "feeRecipient", - ethereum.Value.fromAddress(feeRecipient) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "feeMethod", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(feeMethod)) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "side", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(side)) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "saleKind", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(saleKind)) - ) - ) - contractOrderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("target", ethereum.Value.fromAddress(target)) - ) - - return contractOrderApprovedPartOneEvent -} - -export function createContractOrderApprovedPartTwoEvent( - hash: Bytes, - howToCall: i32, - calldata: Bytes, - replacementPattern: Bytes, - staticTarget: Address, - staticExtradata: Bytes, - paymentToken: Address, - basePrice: BigInt, - extra: BigInt, - listingTime: BigInt, - expirationTime: BigInt, - salt: BigInt, - orderbookInclusionDesired: boolean -): ContractOrderApprovedPartTwo { - let contractOrderApprovedPartTwoEvent = changetype< - ContractOrderApprovedPartTwo - >(newMockEvent()) - - contractOrderApprovedPartTwoEvent.parameters = new Array() - - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("hash", ethereum.Value.fromFixedBytes(hash)) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "howToCall", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(howToCall)) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("calldata", ethereum.Value.fromBytes(calldata)) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "replacementPattern", - ethereum.Value.fromBytes(replacementPattern) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "staticTarget", - ethereum.Value.fromAddress(staticTarget) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "staticExtradata", - ethereum.Value.fromBytes(staticExtradata) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "paymentToken", - ethereum.Value.fromAddress(paymentToken) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "basePrice", - ethereum.Value.fromUnsignedBigInt(basePrice) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("extra", ethereum.Value.fromUnsignedBigInt(extra)) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "listingTime", - ethereum.Value.fromUnsignedBigInt(listingTime) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "expirationTime", - ethereum.Value.fromUnsignedBigInt(expirationTime) - ) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("salt", ethereum.Value.fromUnsignedBigInt(salt)) - ) - contractOrderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "orderbookInclusionDesired", - ethereum.Value.fromBoolean(orderbookInclusionDesired) - ) - ) - - return contractOrderApprovedPartTwoEvent -} - -export function createContractOrderCancelledEvent( - hash: Bytes -): ContractOrderCancelled { - let contractOrderCancelledEvent = changetype( - newMockEvent() - ) - - contractOrderCancelledEvent.parameters = new Array() - - contractOrderCancelledEvent.parameters.push( - new ethereum.EventParam("hash", ethereum.Value.fromFixedBytes(hash)) - ) - - return contractOrderCancelledEvent -} - -export function createContractOrdersMatchedEvent( - buyHash: Bytes, - sellHash: Bytes, - maker: Address, - taker: Address, - price: BigInt, - metadata: Bytes -): ContractOrdersMatched { - let contractOrdersMatchedEvent = changetype( - newMockEvent() - ) - - contractOrdersMatchedEvent.parameters = new Array() - - contractOrdersMatchedEvent.parameters.push( - new ethereum.EventParam("buyHash", ethereum.Value.fromFixedBytes(buyHash)) - ) - contractOrdersMatchedEvent.parameters.push( - new ethereum.EventParam("sellHash", ethereum.Value.fromFixedBytes(sellHash)) - ) - contractOrdersMatchedEvent.parameters.push( - new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)) - ) - contractOrdersMatchedEvent.parameters.push( - new ethereum.EventParam("taker", ethereum.Value.fromAddress(taker)) - ) - contractOrdersMatchedEvent.parameters.push( - new ethereum.EventParam("price", ethereum.Value.fromUnsignedBigInt(price)) - ) - contractOrdersMatchedEvent.parameters.push( - new ethereum.EventParam("metadata", ethereum.Value.fromFixedBytes(metadata)) - ) - - return contractOrdersMatchedEvent -} - -export function createNonceIncrementedEvent( - maker: Address, - newNonce: BigInt -): NonceIncremented { - let nonceIncrementedEvent = changetype(newMockEvent()) - - nonceIncrementedEvent.parameters = new Array() - - nonceIncrementedEvent.parameters.push( - new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)) - ) - nonceIncrementedEvent.parameters.push( - new ethereum.EventParam( - "newNonce", - ethereum.Value.fromUnsignedBigInt(newNonce) - ) - ) - - return nonceIncrementedEvent -} - -export function createContractOwnershipRenouncedEvent( - previousOwner: Address -): ContractOwnershipRenounced { - let contractOwnershipRenouncedEvent = changetype( - newMockEvent() - ) - - contractOwnershipRenouncedEvent.parameters = new Array() - - contractOwnershipRenouncedEvent.parameters.push( - new ethereum.EventParam( - "previousOwner", - ethereum.Value.fromAddress(previousOwner) - ) - ) - - return contractOwnershipRenouncedEvent -} - -export function createContractOwnershipTransferredEvent( - previousOwner: Address, - newOwner: Address -): ContractOwnershipTransferred { - let contractOwnershipTransferredEvent = changetype< - ContractOwnershipTransferred - >(newMockEvent()) - - contractOwnershipTransferredEvent.parameters = new Array() - - contractOwnershipTransferredEvent.parameters.push( - new ethereum.EventParam( - "previousOwner", - ethereum.Value.fromAddress(previousOwner) - ) - ) - contractOwnershipTransferredEvent.parameters.push( - new ethereum.EventParam("newOwner", ethereum.Value.fromAddress(newOwner)) - ) - - return contractOwnershipTransferredEvent -} diff --git a/wyvern-exchange/tests/contract.test.ts b/wyvern-exchange/tests/contract.test.ts deleted file mode 100644 index b15ebd7b..00000000 --- a/wyvern-exchange/tests/contract.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { - assert, - describe, - test, - clearStore, - beforeAll, - afterAll -} from "matchstick-as/assembly/index" -import { Bytes, Address, BigInt } from "@graphprotocol/graph-ts" -import { ContractOrderApprovedPartOne } from "../generated/schema" -import { ContractOrderApprovedPartOne as ContractOrderApprovedPartOneEvent } from "../generated/Contract/Contract" -import { handleContractOrderApprovedPartOne } from "../src/contract" -import { createContractOrderApprovedPartOneEvent } from "./contract-utils" - -// Tests structure (matchstick-as >=0.5.0) -// https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 - -describe("Describe entity assertions", () => { - beforeAll(() => { - let hash = Bytes.fromI32(1234567890) - let exchange = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let maker = Address.fromString("0x0000000000000000000000000000000000000001") - let taker = Address.fromString("0x0000000000000000000000000000000000000001") - let makerRelayerFee = BigInt.fromI32(234) - let takerRelayerFee = BigInt.fromI32(234) - let makerProtocolFee = BigInt.fromI32(234) - let takerProtocolFee = BigInt.fromI32(234) - let feeRecipient = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let feeMethod = 123 - let side = 123 - let saleKind = 123 - let target = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let newContractOrderApprovedPartOneEvent = createContractOrderApprovedPartOneEvent( - hash, - exchange, - maker, - taker, - makerRelayerFee, - takerRelayerFee, - makerProtocolFee, - takerProtocolFee, - feeRecipient, - feeMethod, - side, - saleKind, - target - ) - handleContractOrderApprovedPartOne(newContractOrderApprovedPartOneEvent) - }) - - afterAll(() => { - clearStore() - }) - - // For more test scenarios, see: - // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test - - test("ContractOrderApprovedPartOne created and stored", () => { - assert.entityCount("ContractOrderApprovedPartOne", 1) - - // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "hash", - "1234567890" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "exchange", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "maker", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "taker", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "makerRelayerFee", - "234" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "takerRelayerFee", - "234" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "makerProtocolFee", - "234" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "takerProtocolFee", - "234" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "feeRecipient", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "feeMethod", - "123" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "side", - "123" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "saleKind", - "123" - ) - assert.fieldEquals( - "ContractOrderApprovedPartOne", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", - "target", - "0x0000000000000000000000000000000000000001" - ) - - // More assert options: - // https://thegraph.com/docs/en/developer/matchstick/#asserts - }) -}) diff --git a/wyvern-exchange/tests/example.ts b/wyvern-exchange/tests/example.ts new file mode 100644 index 00000000..6e862b26 --- /dev/null +++ b/wyvern-exchange/tests/example.ts @@ -0,0 +1,458 @@ +import { Address, BigInt, Bytes, log } from "@graphprotocol/graph-ts" +import { AtomicMatchInput, WyvernInput } from "./types" + +export const sample1: WyvernInput = { + addrs: [ + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0x30559da92dcaf2786afcc71541b46dd292d274b5", + "0x2ebcd2b0872df5447386ea0e58f6a5b4296ee63f", + "0x0000000000000000000000000000000000000000", + "0xbaf2127b49fc93cbca6269fade0f7f31df4c88a7", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0x2ebcd2b0872df5447386ea0e58f6a5b4296ee63f", + "0x0000000000000000000000000000000000000000", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0xbaf2127b49fc93cbca6269fade0f7f31df4c88a7", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "250", + "0", + "0", + "0", + "20000000000000000", + "0", + "1659294650", + "0", + "30580174732120081340103128706204574194273460117898893754075548156560528585514", + "250", + "0", + "0", + "0", + "20000000000000000", + "0", + "1650738072", + "1666290146", + "29644834317056685471211109483260380400204674755010065806884587362500543546212", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: + "0x96809f90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030559da92dcaf2786afcc71541b46dd292d274b5000000000000000000000000045ec645abb6b768206b079cddb59677cdb071a500000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", + calldataSell: + "0x96809f900000000000000000000000002ebcd2b0872df5447386ea0e58f6a5b4296ee63f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045ec645abb6b768206b079cddb59677cdb071a500000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", + replacementPatternBuy: + "0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + replacementPatternSell: + "0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [28, 28], + rssMetadata: [ + "0x51727d41bc3b7d6b2368cf4c72bb3da7c5193c5ddbd5faff641ec95beab1d94d", + "0x06e08d6ceaa98bd71d1dc170f2194e0b9aca3b745eed8465e63a4236ddf04ac2", + "0x51727d41bc3b7d6b2368cf4c72bb3da7c5193c5ddbd5faff641ec95beab1d94d", + "0x06e08d6ceaa98bd71d1dc170f2194e0b9aca3b745eed8465e63a4236ddf04ac2", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} + +export const batchInSingle: WyvernInput = { + addrs: [ + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0x579be2bd871fadda71308890d5bb7efcda9a8675", + "0xbe71e51ea3a288278302a1541119242f41a2b7ff", + "0x0000000000000000000000000000000000000000", + "0x41ea0994076a97c23dcd4d8fe7500a56191d1e84", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0xbe71e51ea3a288278302a1541119242f41a2b7ff", + "0x579be2bd871fadda71308890d5bb7efcda9a8675", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0x41ea0994076a97c23dcd4d8fe7500a56191d1e84", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "250", + "0", + "0", + "0", + "0", + "0", + "1659271301", + "0", + "6492277194982488052591782532578874204427709473500549074465580805787894", + "250", + "0", + "0", + "0", + "0", + "0", + "1659270996", + "1661900996", + "5972905111212054229775419728460965373354183017356649117682584065107874", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: + "0x7049e9610000000000000000000000000000000000000000000000000000000000000060000000000000000000000000be71e51ea3a288278302a1541119242f41a2b7ff000000000000000000000000252c0fda7d557a5cb80fad0a31218caa8f805c340000000000000000000000000000000000000000000000000000000000000003000000000000000000000000366e3b64ef9060eb4b2b0908d7cd165c26312a2300000000000000000000000000000000000000000000000000000000000005a50000000000000000000000009f35425c2ef3616dd024d866082e0b61023fbfe10000000000000000000000000000000000000000000000000000000000000bc9000000000000000000000000f80bd8c7f0f68ccf76e638edb53fdb922e05295c0000000000000000000000000000000000000000000000000000000000001c34", + calldataSell: + "0x7049e9610000000000000000000000000000000000000000000000000000000000000060000000000000000000000000be71e51ea3a288278302a1541119242f41a2b7ff000000000000000000000000252c0fda7d557a5cb80fad0a31218caa8f805c340000000000000000000000000000000000000000000000000000000000000003000000000000000000000000366e3b64ef9060eb4b2b0908d7cd165c26312a2300000000000000000000000000000000000000000000000000000000000005a50000000000000000000000009f35425c2ef3616dd024d866082e0b61023fbfe10000000000000000000000000000000000000000000000000000000000000bc9000000000000000000000000f80bd8c7f0f68ccf76e638edb53fdb922e05295c0000000000000000000000000000000000000000000000000000000000001c34", + replacementPatternBuy: "0x", + replacementPatternSell: "0x", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [27, 27], + rssMetadata: [ + "0x012c931683c97f3f7f011a3275c86cd25bbca776af18f5cd08a09f441c092b00", + "0x38224c3bb91453408825c42bbf0b8bf884dbb36fe9e6c68180bfa8d2df7c8019", + "0xfe4f3296e5ea4a6be130f5d4894483dad1933ea45a687f33ce8cb9b2f88402bf", + "0x47aa6ce482d823cd9ed419bc4f8635961634d1354721909c009a4929efc47b98", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} + +export const sharedStorefront: WyvernInput = { + addrs: [ + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x72bfb4d1ae160552b44f9c99ff56d9311b5941f4", + "0x1b532293e000c53761b7af98255fc66ef5df750c", + "0x0000000000000000000000000000000000000000", + "0x5fbef9fcb449d56154980e52e165d9650b9f6ec2", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x1b532293e000c53761b7af98255fc66ef5df750c", + "0x0000000000000000000000000000000000000000", + "0xb4e7843ebf4e9e4bb618627d6f7697096ae1be7a", + "0x5fbef9fcb449d56154980e52e165d9650b9f6ec2", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "250", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "42301341445824248357767741147826153384983696408879876366681874847722741706561", + "250", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "42301341445824248357767741147826153384983696408879876366681874847722741706561", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 0, 1, 1, 0, 0], + calldataBuy: + "0x6d5cb2f50000000000000000000000001b532293e000c53761b7af98255fc66ef5df750c00000000000000000000000072bfb4d1ae160552b44f9c99ff56d9311b5941f44d3602213e19d54235b0f2e04e62fc4d7a92991c13dccc9c0339ad32193fafef", + calldataSell: + "0x6d5cb2f50000000000000000000000001b532293e000c53761b7af98255fc66ef5df750c00000000000000000000000011111111111111111111111111111111111111114d3602213e19d54235b0f2e04e62fc4d7a92991c13dccc9c0339ad32193fafef", + replacementPatternBuy: "0x", + replacementPatternSell: + "0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [27, 28], + rssMetadata: [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xb8373b7699d10337c2dc341cc4e26bbc4b3378cbfd77f757b7c21a3a74ae426e", + "0x11a451dfb029a2fb6ff6a3ab8e607d1c1a8bfe72273e887d30598b7e233af95f", + "0x00000000000000000000000000000000000000000000000000000000000abcde", + ], +} + +export const batchTransfer: WyvernInput = { + addrs: [ + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0x2de69a20482210f50e7c754d635e2d3e18cce28a", + "0x27792e7d87a5f05334b52c0584ad8497852aef8b", + "0x0000000000000000000000000000000000000000", + "0xc99f70bfd82fb7c8f8191fdfbfb735606b15e5c5", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0x27792e7d87a5f05334b52c0584ad8497852aef8b", + "0x0000000000000000000000000000000000000000", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0xc99f70bfd82fb7c8f8191fdfbfb735606b15e5c5", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "1250", + "0", + "0", + "0", + "0", + "0", + "1659353749", + "0", + "58781726514734076456503640048148570959598284082761433218487171344215569091453", + "1250", + "0", + "0", + "0", + "0", + "0", + "1658135978", + "1665911988", + "56234994111982507363892104720915792867170221247478138916347216301184362205859", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: + "0x68f0bcaa00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000003d4f242432a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002de69a20482210f50e7c754d635e2d3e18cce28a27792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3b0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002de69a20482210f50e7c754d635e2d3e18cce28a27792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3c0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002de69a20482210f50e7c754d635e2d3e18cce28a27792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3d0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002de69a20482210f50e7c754d635e2d3e18cce28a27792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3e0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000002de69a20482210f50e7c754d635e2d3e18cce28a27792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3f0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + calldataSell: + "0x68f0bcaa00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000003d4f242432a00000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b000000000000000000000000000000000000000000000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3b0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b000000000000000000000000000000000000000000000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3c0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b000000000000000000000000000000000000000000000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3d0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b000000000000000000000000000000000000000000000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3e0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f242432a00000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b000000000000000000000000000000000000000000000000000000000000000027792e7d87a5f05334b52c0584ad8497852aef8b00000000000e3f0000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + replacementPatternBuy: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + replacementPatternSell: + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [27, 27], + rssMetadata: [ + "0x5be2bcc92a23e7cddd48b88aab033523f9d2977001324a3bd88af154bdbf91bd", + "0x2be15a1dc0d1db1db617e17f41fa852185a47add306b2f4ee9cc7aad0d3822a7", + "0x5be2bcc92a23e7cddd48b88aab033523f9d2977001324a3bd88af154bdbf91bd", + "0x2be15a1dc0d1db1db617e17f41fa852185a47add306b2f4ee9cc7aad0d3822a7", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} + +// 0x00008f324bf193b214a701d5321ca3e493f638936ca48bdf095a61dacbf77bbc +export const tokenAmountGt1: WyvernInput = { + addrs: [ + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x6a95931f9e0ff1810b5a04be3f1c29152209b482", + "0x235e14f5cde92a098d9b38a3f3ac07e7058cd40c", + "0x0000000000000000000000000000000000000000", + "0xbaf2127b49fc93cbca6269fade0f7f31df4c88a7", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x235e14f5cde92a098d9b38a3f3ac07e7058cd40c", + "0x0000000000000000000000000000000000000000", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0xbaf2127b49fc93cbca6269fade0f7f31df4c88a7", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "750", + "0", + "0", + "0", + "1760000000000000000", + "0", + "1644856033", + "0", + "87310520320136919974077900782176529476731655132772175384162654385760728919933", + "750", + "0", + "0", + "0", + "1760000000000000000", + "0", + "1644843048", + "1644929523", + "64010242210000299395306488461030683984625476958903336310046088359604206195959", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: + "0x96809f9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a95931f9e0ff1810b5a04be3f1c29152209b4820000000000000000000000005079fc4e96338be1b5aff236ff4b00ec4452b2d300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", + calldataSell: + "0x96809f90000000000000000000000000235e14f5cde92a098d9b38a3f3ac07e7058cd40c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000005079fc4e96338be1b5aff236ff4b00ec4452b2d300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", + replacementPatternBuy: + "0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + replacementPatternSell: + "0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [27, 27], + rssMetadata: [ + "0x893571771ea1c667862d35493c469cc4823a2497f1d992ea8efaeff90f517ff9", + "0x25e0ba8e3f3e68a07d9d0088007f9546d90f383b401c28aab316183ad0b86bc2", + "0x893571771ea1c667862d35493c469cc4823a2497f1d992ea8efaeff90f517ff9", + "0x25e0ba8e3f3e68a07d9d0088007f9546d90f383b401c28aab316183ad0b86bc2", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} + +// 0x7deac5070dfd74998bc5059db3e602167433fea6b620e13dad2ff73ac4236e2a +// safeTransferFrom(address,address,uint256,bytes) +export const safeTransferFromWithBytes: WyvernInput = { + addrs: [ + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x3baec890358f50f0d173101829b63c3033d790be", + "0xb3169e54c6994609aa42377d2525168ac958f3ff", + "0x0000000000000000000000000000000000000000", + "0xc99f70bfd82fb7c8f8191fdfbfb735606b15e5c5", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0xb3169e54c6994609aa42377d2525168ac958f3ff", + "0x3baec890358f50f0d173101829b63c3033d790be", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0xc99f70bfd82fb7c8f8191fdfbfb735606b15e5c5", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "750", + "0", + "0", + "0", + "10000000", + "0", + "1639085475", + "0", + "8218937218321973210382109382197321832392139213871289372109382198751", + "750", + "0", + "0", + "0", + "10000000", + "0", + "1638970444", + "0", + "8218937218321973210382109382197321832392139213871289372109382198737", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: + "0x68f0bcaa000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bc4ca0eda7647a8ab7c2061c2e118a18a936f13d0000000000000000000000008a90cab2b38dba80c64b7734e58ee1db38b8992e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000a40000000000000000000000000000000000000000000000000000000000000148b88d4fde000000000000000000000000b3169e54c6994609aa42377d2525168ac958f3ff0000000000000000000000003baec890358f50f0d173101829b63c3033d790be0000000000000000000000000000000000000000000000000000000000001fe700000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000b88d4fde000000000000000000000000b3169e54c6994609aa42377d2525168ac958f3ff0000000000000000000000003baec890358f50f0d173101829b63c3033d790be0000000000000000000000000000000000000000000000000000000000000adb00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + calldataSell: + "0x68f0bcaa000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bc4ca0eda7647a8ab7c2061c2e118a18a936f13d0000000000000000000000008a90cab2b38dba80c64b7734e58ee1db38b8992e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000a40000000000000000000000000000000000000000000000000000000000000148b88d4fde000000000000000000000000b3169e54c6994609aa42377d2525168ac958f3ff0000000000000000000000003baec890358f50f0d173101829b63c3033d790be0000000000000000000000000000000000000000000000000000000000001fe700000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000b88d4fde000000000000000000000000b3169e54c6994609aa42377d2525168ac958f3ff0000000000000000000000003baec890358f50f0d173101829b63c3033d790be0000000000000000000000000000000000000000000000000000000000000adb00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + replacementPatternBuy: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + replacementPatternSell: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [27, 27], + rssMetadata: [ + "0x417a6b00171a7843fa2732abb686b6ae8eb2fe3fbc651d4547f3e75143344670", + "0x1d534615b2361b7705d5c8b055810a52ad8c52fae39cca58db760db931db49a2", + "0xfc520454206ae580b8dbc73b8966aa6945c5c3a19d44b3fb452ba5cf2cc4b263", + "0x77171338b10450e2e9694d3f79bf11a89e4bfd11302697541972b3ac548d1a5c", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} +// 0x337a707baaa15f3aa4004f0a2c9b5dcf38efc5c00fe162b27787409097cf20f8 +export const phishing: WyvernInput = { + addrs: [ + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x3e0defb880cd8e163bad68abe66437f99a7a8a74", + "0x20c3cc9e8869adc1b7efad187f10969a449653f5", + "0x0000000000000000000000000000000000000000", + "0xa2c0946ad444dccf990394c5cbe019a858a945bd", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b", + "0x20c3cc9e8869adc1b7efad187f10969a449653f5", + "0x0000000000000000000000000000000000000000", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0xa2c0946ad444dccf990394c5cbe019a858a945bd", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "2252585565542185245059838779648950429440613649397344615579690820176452855504", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "62963637869139619923677086108020324018809324287039301974196725733220251841442", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: "0x7a7b6449000000000000000000000000a2c0946ad444dccf990394c5cbe019a858a945bd", + calldataSell: "0x7a7b6449000000000000000000000000a2c0946ad444dccf990394c5cbe019a858a945bd", + replacementPatternBuy: "0x", + replacementPatternSell: "0x", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [27, 28], + rssMetadata: [ + "0x6448ee1bbdf15fbffe04e2154942d73fb474a9068686734d089c55e13170b41e", + "0x7931fae9b9224ff26128d43ae614942bfe68ff0ebf10446e84742c48af542b58", + "0xcdb73e03fd1ab0e61a69de29a07f3abf08371ff80de95b72f03d6b88fff07166", + "0x6ba1ab73d0f0c226fa516563e02036fc1f58309099f65d91c7f4ddc5179e441a", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} +// 0x5c7b15aa1328efa1ff5dcc1a619b9be7813ae873e514b695dfe700268e2e6c50 +export const noPayment: WyvernInput = { + addrs: [ + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0xb9f871fb528248851e93b2e599f3380641256d65", + "0x0671ae9e2f0456834d50aac2b4c99920a77f3048", + "0x0000000000000000000000000000000000000000", + "0xf111ed5312c77e18ef49e86de23673b4538daa6f", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x7f268357a8c2552623316e2562d90e642bb538e5", + "0x0671ae9e2f0456834d50aac2b4c99920a77f3048", + "0xb9f871fb528248851e93b2e599f3380641256d65", + "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "0xf111ed5312c77e18ef49e86de23673b4538daa6f", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + ], + uints: [ + "250", + "0", + "0", + "0", + "0", + "0", + "1654697438", + "0", + "30848704950318481391109514188368502714061885924427623244750953621924564272002", + "250", + "0", + "0", + "0", + "0", + "0", + "1654697296", + "1672444800", + "7476252379766171268149469673387808413412176417697238359409795031618653", + ], + feeMethodsSidesKindsHowToCalls: [1, 0, 0, 1, 1, 1, 0, 1], + calldataBuy: "0x1c14edea", + calldataSell: "0x1c14edea", + replacementPatternBuy: "0xffffffff", + replacementPatternSell: "0xffffffff", + staticExtradataBuy: "0x", + staticExtradataSell: "0x", + vs: [28, 28], + rssMetadata: [ + "0xb4be1444ccd216b91f902e3917e122ffd06fa0b9e1e932652d9321db62f4c5e9", + "0x4c584138ee328573a2f7dfd3a1f2c0fb133c895e8cea1dc218fb27b048135a21", + "0xb4be1444ccd216b91f902e3917e122ffd06fa0b9e1e932652d9321db62f4c5e9", + "0x4c584138ee328573a2f7dfd3a1f2c0fb133c895e8cea1dc218fb27b048135a21", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ], +} diff --git a/wyvern-exchange/tests/types.ts b/wyvern-exchange/tests/types.ts new file mode 100644 index 00000000..1824af21 --- /dev/null +++ b/wyvern-exchange/tests/types.ts @@ -0,0 +1,54 @@ +import { Address, Bytes, BigInt, log } from "@graphprotocol/graph-ts" + +export class WyvernInput { + addrs: string[] + uints: string[] + feeMethodsSidesKindsHowToCalls: number[] + calldataBuy: string + calldataSell: string + replacementPatternBuy: string + replacementPatternSell: string + staticExtradataBuy: string + staticExtradataSell: string + vs: number[] + rssMetadata: string[] +} + +export class AtomicMatchInput { + addrs: Array
= [] + uints: Array = [] + feeMethodsSidesKindsHowToCalls: Array = [] + calldataBuy: Bytes = Bytes.fromHexString("") + calldataSell: Bytes = Bytes.fromHexString("") + replacementPatternBuy: Bytes = Bytes.fromHexString("") + replacementPatternSell: Bytes = Bytes.fromHexString("") + staticExtradataBuy: Bytes = Bytes.fromHexString("") + staticExtradataSell: Bytes = Bytes.fromHexString("") + vs: Array = [] + rssMetadata: Array = [] + constructor(sample: WyvernInput) { + for (let i = 0; i < sample.addrs.length; i++) { + this.addrs.push(Address.fromString(sample.addrs[i])) + } + for (let i = 0; i < sample.uints.length; i++) { + this.uints.push(BigInt.fromString(sample.uints[i])) + } + for (let i = 0; i < sample.feeMethodsSidesKindsHowToCalls.length; i++) { + let temp = sample.feeMethodsSidesKindsHowToCalls[i] + this.feeMethodsSidesKindsHowToCalls.push(temp) + } + this.calldataBuy = Bytes.fromHexString(sample.calldataBuy) + this.calldataSell = Bytes.fromHexString(sample.calldataSell) + this.replacementPatternBuy = Bytes.fromHexString(sample.replacementPatternBuy) + this.replacementPatternSell = Bytes.fromHexString(sample.replacementPatternSell) + this.staticExtradataBuy = Bytes.fromHexString(sample.staticExtradataBuy) + this.staticExtradataSell = Bytes.fromHexString(sample.staticExtradataSell) + for (let i = 0; i < sample.vs.length; i++) { + let vsI32 = sample.vs[i] + this.vs.push(vsI32) + } + for (let i = 0; i < sample.rssMetadata.length; i++) { + this.rssMetadata.push(Bytes.fromHexString(sample.rssMetadata[i])) + } + } +} diff --git a/wyvern-exchange/tests/wyvern-exchange-utils.ts b/wyvern-exchange/tests/wyvern-exchange-utils.ts deleted file mode 100644 index 0fed7b93..00000000 --- a/wyvern-exchange/tests/wyvern-exchange-utils.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { newMockEvent } from "matchstick-as" -import { ethereum, Bytes, Address, BigInt } from "@graphprotocol/graph-ts" -import { - OrderApprovedPartOne, - OrderApprovedPartTwo, - OrderCancelled, - OrdersMatched, - OwnershipRenounced, - OwnershipTransferred -} from "../generated/WyvernExchange/WyvernExchange" - -export function createOrderApprovedPartOneEvent( - hash: Bytes, - exchange: Address, - maker: Address, - taker: Address, - makerRelayerFee: BigInt, - takerRelayerFee: BigInt, - makerProtocolFee: BigInt, - takerProtocolFee: BigInt, - feeRecipient: Address, - feeMethod: i32, - side: i32, - saleKind: i32, - target: Address -): OrderApprovedPartOne { - let orderApprovedPartOneEvent = changetype( - newMockEvent() - ) - - orderApprovedPartOneEvent.parameters = new Array() - - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("hash", ethereum.Value.fromFixedBytes(hash)) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("exchange", ethereum.Value.fromAddress(exchange)) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("taker", ethereum.Value.fromAddress(taker)) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "makerRelayerFee", - ethereum.Value.fromUnsignedBigInt(makerRelayerFee) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "takerRelayerFee", - ethereum.Value.fromUnsignedBigInt(takerRelayerFee) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "makerProtocolFee", - ethereum.Value.fromUnsignedBigInt(makerProtocolFee) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "takerProtocolFee", - ethereum.Value.fromUnsignedBigInt(takerProtocolFee) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "feeRecipient", - ethereum.Value.fromAddress(feeRecipient) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "feeMethod", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(feeMethod)) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "side", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(side)) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam( - "saleKind", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(saleKind)) - ) - ) - orderApprovedPartOneEvent.parameters.push( - new ethereum.EventParam("target", ethereum.Value.fromAddress(target)) - ) - - return orderApprovedPartOneEvent -} - -export function createOrderApprovedPartTwoEvent( - hash: Bytes, - howToCall: i32, - calldata: Bytes, - replacementPattern: Bytes, - staticTarget: Address, - staticExtradata: Bytes, - paymentToken: Address, - basePrice: BigInt, - extra: BigInt, - listingTime: BigInt, - expirationTime: BigInt, - salt: BigInt, - orderbookInclusionDesired: boolean -): OrderApprovedPartTwo { - let orderApprovedPartTwoEvent = changetype( - newMockEvent() - ) - - orderApprovedPartTwoEvent.parameters = new Array() - - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("hash", ethereum.Value.fromFixedBytes(hash)) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "howToCall", - ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(howToCall)) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("calldata", ethereum.Value.fromBytes(calldata)) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "replacementPattern", - ethereum.Value.fromBytes(replacementPattern) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "staticTarget", - ethereum.Value.fromAddress(staticTarget) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "staticExtradata", - ethereum.Value.fromBytes(staticExtradata) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "paymentToken", - ethereum.Value.fromAddress(paymentToken) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "basePrice", - ethereum.Value.fromUnsignedBigInt(basePrice) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("extra", ethereum.Value.fromUnsignedBigInt(extra)) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "listingTime", - ethereum.Value.fromUnsignedBigInt(listingTime) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "expirationTime", - ethereum.Value.fromUnsignedBigInt(expirationTime) - ) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam("salt", ethereum.Value.fromUnsignedBigInt(salt)) - ) - orderApprovedPartTwoEvent.parameters.push( - new ethereum.EventParam( - "orderbookInclusionDesired", - ethereum.Value.fromBoolean(orderbookInclusionDesired) - ) - ) - - return orderApprovedPartTwoEvent -} - -export function createOrderCancelledEvent(hash: Bytes): OrderCancelled { - let orderCancelledEvent = changetype(newMockEvent()) - - orderCancelledEvent.parameters = new Array() - - orderCancelledEvent.parameters.push( - new ethereum.EventParam("hash", ethereum.Value.fromFixedBytes(hash)) - ) - - return orderCancelledEvent -} - -export function createOrdersMatchedEvent( - buyHash: Bytes, - sellHash: Bytes, - maker: Address, - taker: Address, - price: BigInt, - metadata: Bytes -): OrdersMatched { - let ordersMatchedEvent = changetype(newMockEvent()) - - ordersMatchedEvent.parameters = new Array() - - ordersMatchedEvent.parameters.push( - new ethereum.EventParam("buyHash", ethereum.Value.fromFixedBytes(buyHash)) - ) - ordersMatchedEvent.parameters.push( - new ethereum.EventParam("sellHash", ethereum.Value.fromFixedBytes(sellHash)) - ) - ordersMatchedEvent.parameters.push( - new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)) - ) - ordersMatchedEvent.parameters.push( - new ethereum.EventParam("taker", ethereum.Value.fromAddress(taker)) - ) - ordersMatchedEvent.parameters.push( - new ethereum.EventParam("price", ethereum.Value.fromUnsignedBigInt(price)) - ) - ordersMatchedEvent.parameters.push( - new ethereum.EventParam("metadata", ethereum.Value.fromFixedBytes(metadata)) - ) - - return ordersMatchedEvent -} - -export function createOwnershipRenouncedEvent( - previousOwner: Address -): OwnershipRenounced { - let ownershipRenouncedEvent = changetype(newMockEvent()) - - ownershipRenouncedEvent.parameters = new Array() - - ownershipRenouncedEvent.parameters.push( - new ethereum.EventParam( - "previousOwner", - ethereum.Value.fromAddress(previousOwner) - ) - ) - - return ownershipRenouncedEvent -} - -export function createOwnershipTransferredEvent( - previousOwner: Address, - newOwner: Address -): OwnershipTransferred { - let ownershipTransferredEvent = changetype( - newMockEvent() - ) - - ownershipTransferredEvent.parameters = new Array() - - ownershipTransferredEvent.parameters.push( - new ethereum.EventParam( - "previousOwner", - ethereum.Value.fromAddress(previousOwner) - ) - ) - ownershipTransferredEvent.parameters.push( - new ethereum.EventParam("newOwner", ethereum.Value.fromAddress(newOwner)) - ) - - return ownershipTransferredEvent -} diff --git a/wyvern-exchange/tests/wyvern-exchange.test.ts b/wyvern-exchange/tests/wyvern-exchange.test.ts index 5718cb08..35f9f70e 100644 --- a/wyvern-exchange/tests/wyvern-exchange.test.ts +++ b/wyvern-exchange/tests/wyvern-exchange.test.ts @@ -1,150 +1,301 @@ import { - assert, - describe, - test, - clearStore, - beforeAll, - afterAll + assert, + describe, + test, + clearStore, + beforeAll, + afterAll, } from "matchstick-as/assembly/index" -import { Bytes, Address, BigInt } from "@graphprotocol/graph-ts" -import { ExampleEntity } from "../generated/schema" -import { OrderApprovedPartOne } from "../generated/WyvernExchange/WyvernExchange" -import { handleOrderApprovedPartOne } from "../src/wyvern-exchange" -import { createOrderApprovedPartOneEvent } from "./wyvern-exchange-utils" +import { Address, BigDecimal, BigInt, Bytes, log } from "@graphprotocol/graph-ts" -// Tests structure (matchstick-as >=0.5.0) -// https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 +import { + batchInSingle, + batchTransfer, + noPayment, + phishing, + safeTransferFromWithBytes, + sample1, + sharedStorefront, + tokenAmountGt1, +} from "./example" +import { AtomicMatchInput } from "./types" +import * as airstack from "../modules/airstack/nft-marketplace" +import { atomicMatch } from "../src/wyvern-exchange" +export function callAtomicMatch( + hash: string, + blockTimeStamp: BigInt, + input: AtomicMatchInput +): airstack.nft.Sale | null { + return atomicMatch( + hash, + blockTimeStamp, + input.addrs, + input.uints, + input.feeMethodsSidesKindsHowToCalls, + input.calldataBuy, + input.calldataSell, + input.replacementPatternBuy, + input.replacementPatternSell, + input.staticExtradataBuy, + input.staticExtradataSell, + input.vs, + input.rssMetadata + ) +} describe("Describe entity assertions", () => { - beforeAll(() => { - let hash = Bytes.fromI32(1234567890) - let exchange = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let maker = Address.fromString("0x0000000000000000000000000000000000000001") - let taker = Address.fromString("0x0000000000000000000000000000000000000001") - let makerRelayerFee = BigInt.fromI32(234) - let takerRelayerFee = BigInt.fromI32(234) - let makerProtocolFee = BigInt.fromI32(234) - let takerProtocolFee = BigInt.fromI32(234) - let feeRecipient = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let feeMethod = 123 - let side = 123 - let saleKind = 123 - let target = Address.fromString( - "0x0000000000000000000000000000000000000001" - ) - let newOrderApprovedPartOneEvent = createOrderApprovedPartOneEvent( - hash, - exchange, - maker, - taker, - makerRelayerFee, - takerRelayerFee, - makerProtocolFee, - takerProtocolFee, - feeRecipient, - feeMethod, - side, - saleKind, - target - ) - handleOrderApprovedPartOne(newOrderApprovedPartOneEvent) - }) + test("should handle CheckMatchERC1155UsingCriteria case", () => { + let sample = new AtomicMatchInput(sample1) + let hash = "0x00726bb566eea40542346f2bef251729de2468f0ee2993cf000cd06993b04af5" + let blockTimeStamp = BigInt.fromString("1659294740") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) - afterAll(() => { - clearStore() - }) + let expectedNfts: airstack.nft.NFT[] = [] + let expectedNFT1 = new airstack.nft.NFT( + Address.fromString("0x045Ec645aBB6b768206B079cdDb59677cDb071A5"), + BigInt.fromString("2"), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT1) + let expectedSale = new airstack.nft.Sale( + Address.fromString("0x30559Da92dCAf2786aFcc71541B46DD292d274b5"), + Address.fromString("0x2eBCd2b0872DF5447386Ea0e58F6A5B4296eE63F"), + expectedNfts, + BigInt.fromString("20000000000000000"), + Address.zero(), + BigInt.fromString("500000000000000"), + Address.fromString("0x5b3256965e7c3cf26e11fcaf296dfc8807c01073"), + [] + ) + assert.assertNotNull(sale) + if (sale != null) { + checkSaledata(sale, expectedSale) + } + }) + test("should handle batch transfer in single case", () => { + let sample = new AtomicMatchInput(batchInSingle) + let hash = "0x1c7b53bffbbae31380d4e53ee31825d97fbcded698c8a0d5004bba3a36d3035b" + let blockTimeStamp = BigInt.fromString("1659274589") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) - // For more test scenarios, see: - // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test + let expectedNfts: airstack.nft.NFT[] = [] + let expectedNFT1 = new airstack.nft.NFT( + Address.fromString("0x366E3B64Ef9060EB4B2B0908d7cD165C26312A23"), + BigInt.fromString("1445"), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT1) - test("ExampleEntity created and stored", () => { - assert.entityCount("ExampleEntity", 1) + let expectedNFT2 = new airstack.nft.NFT( + Address.fromString("0x9f35425c2ef3616dd024d866082e0b61023fbfe1"), + BigInt.fromString("3017"), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT2) + let expectedNFT3 = new airstack.nft.NFT( + Address.fromString("0xF80bd8c7f0f68CCF76E638Edb53fDB922e05295c"), + BigInt.fromString("7220"), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT3) - // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "hash", - "1234567890" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "exchange", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "maker", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "taker", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "makerRelayerFee", - "234" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "takerRelayerFee", - "234" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "makerProtocolFee", - "234" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "takerProtocolFee", - "234" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "feeRecipient", - "0x0000000000000000000000000000000000000001" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "feeMethod", - "123" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "side", - "123" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "saleKind", - "123" - ) - assert.fieldEquals( - "ExampleEntity", - "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", - "target", - "0x0000000000000000000000000000000000000001" - ) + let expectedSale = new airstack.nft.Sale( + Address.fromString("0x252C0fDa7d557a5cB80FAD0A31218CAa8f805C34"), + Address.fromString("0xbE71E51ea3A288278302a1541119242f41a2B7Ff"), + expectedNfts, + BigInt.fromString("0"), + Address.zero(), + BigInt.fromString("0"), + Address.fromString("0x5b3256965e7c3cf26e11fcaf296dfc8807c01073"), + [] + ) + assert.assertNotNull(sale) + if (sale != null) { + checkSaledata(sale, expectedSale) + } + }) + test("should handle sharedStorefront case", () => { + let sample = new AtomicMatchInput(sharedStorefront) + let hash = "0x02694067af289079289648b1706510c9a521637c28cf476f1eeeaef440b610d0" + let blockTimeStamp = BigInt.fromString("1623211679") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) + let expectedNfts: airstack.nft.NFT[] = [] + let expectedNFT1 = new airstack.nft.NFT( + Address.fromString("0x5fbef9fcb449d56154980e52e165d9650b9f6ec2"), + BigInt.fromString( + "34923513782120467435984595847068866881775186546998211107474682676588369129455" + ), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT1) + let expectedSale = new airstack.nft.Sale( + Address.fromString("0x72BFb4d1Ae160552B44F9c99Ff56d9311b5941f4"), + Address.fromString("0x1b532293e000C53761b7aF98255fC66EF5dF750C"), + expectedNfts, + BigInt.fromString("0"), + Address.zero(), + BigInt.fromString("0"), + Address.fromString("0xb4e7843ebf4e9e4bb618627d6f7697096ae1be7a"), + [] + ) + assert.assertNotNull(sale) + if (sale != null) { + checkSaledata(sale, expectedSale) + } + }) + test("should handle batch case", () => { + let sample = new AtomicMatchInput(batchTransfer) + let hash = "0xa5b0b2d7773741598016e877dc351e7956605ce22c07c5a1675648409608b4f8" + let blockTimeStamp = BigInt.fromString("1659353957") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) + let expectedNfts: airstack.nft.NFT[] = [] + let expectedNFT1 = new airstack.nft.NFT( + Address.fromString("0x495f947276749ce646f68ac8c248420045cb7b5e"), + BigInt.fromString( + "17854310454196282035870592841598074249268846845716858963664664195636899348481" + ), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT1) + let expectedNFT2 = new airstack.nft.NFT( + Address.fromString("0x495f947276749ce646f68ac8c248420045cb7b5e"), + BigInt.fromString( + "17854310454196282035870592841598074249268846845716858963664664196736410976257" + ), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT2) + let expectedNFT3 = new airstack.nft.NFT( + Address.fromString("0x495f947276749ce646f68ac8c248420045cb7b5e"), + BigInt.fromString( + "17854310454196282035870592841598074249268846845716858963664664197835922604033" + ), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT3) + let expectedNFT4 = new airstack.nft.NFT( + Address.fromString("0x495f947276749ce646f68ac8c248420045cb7b5e"), + BigInt.fromString( + "17854310454196282035870592841598074249268846845716858963664664198935434231809" + ), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT4) + let expectedNFT5 = new airstack.nft.NFT( + Address.fromString("0x495f947276749ce646f68ac8c248420045cb7b5e"), + BigInt.fromString( + "17854310454196282035870592841598074249268846845716858963664664200034945859585" + ), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT5) + let expectedSale = new airstack.nft.Sale( + Address.fromString("0x2De69A20482210f50E7C754D635e2d3E18CcE28a"), + Address.fromString("0x27792E7d87a5f05334B52C0584Ad8497852aEf8B"), + expectedNfts, + BigInt.fromString("0"), + Address.zero(), + BigInt.fromString("0"), + Address.fromString("0x5b3256965e7C3cF26E11FCAf296DfC8807C01073"), + [] + ) + assert.assertNotNull(sale) + if (sale != null) { + checkSaledata(sale, expectedSale) + } + }) + test("should handle tokenAmountGt1 case", () => { + let sample = new AtomicMatchInput(tokenAmountGt1) + let hash = "0x00008f324bf193b214a701d5321ca3e493f638936ca48bdf095a61dacbf77bbc" + let blockTimeStamp = BigInt.fromString("1644856231") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) + let expectedNfts: airstack.nft.NFT[] = [] + let expectedNFT1 = new airstack.nft.NFT( + Address.fromString("0x5079FC4e96338be1B5aff236ff4b00eC4452B2D3"), + BigInt.fromString("1"), + BigInt.fromString("4") + ) + expectedNfts.push(expectedNFT1) + let expectedSale = new airstack.nft.Sale( + Address.fromString("0x6A95931f9E0FF1810B5A04BE3f1c29152209b482"), + Address.fromString("0x235E14F5cDe92a098D9B38a3f3ac07E7058cD40c"), + expectedNfts, + BigInt.fromString("1760000000000000000"), + Address.zero(), + BigInt.fromString("132000000000000000"), + Address.fromString("0x5b3256965e7C3cF26E11FCAf296DfC8807C01073"), + [] + ) + assert.assertNotNull(sale) + if (sale != null) { + checkSaledata(sale, expectedSale) + } + }) + test("should handle safeTransferFromWithBytes case with BundleSale", () => { + let sample = new AtomicMatchInput(safeTransferFromWithBytes) + let hash = "0x7deac5070dfd74998bc5059db3e602167433fea6b620e13dad2ff73ac4236e2a" + let blockTimeStamp = BigInt.fromString("13773433") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) + let expectedNfts: airstack.nft.NFT[] = [] + let expectedNFT1 = new airstack.nft.NFT( + Address.fromString("0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"), + BigInt.fromString("8167"), + BigInt.fromString("1") + ) + let expectedNFT2 = new airstack.nft.NFT( + Address.fromString("0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e"), + BigInt.fromString("2779"), + BigInt.fromString("1") + ) + expectedNfts.push(expectedNFT1) + expectedNfts.push(expectedNFT2) + let expectedSale = new airstack.nft.Sale( + Address.fromString("0x3baec890358f50f0d173101829b63c3033d790be"), + Address.fromString("0xb3169e54c6994609aa42377d2525168ac958f3ff"), + expectedNfts, + BigInt.fromString("10000000"), + Address.zero(), + BigInt.fromString("750000"), + Address.fromString("0x5b3256965e7C3cF26E11FCAf296DfC8807C01073"), + [] + ) + assert.assertNotNull(sale) + if (sale != null) { + checkSaledata(sale, expectedSale) + } + }) - // More assert options: - // https://thegraph.com/docs/en/developer/matchstick/#asserts - }) + test("should handle Phishing case", () => { + let sample = new AtomicMatchInput(phishing) + let hash = "0x337a707baaa15f3aa4004f0a2c9b5dcf38efc5c00fe162b27787409097cf20f8" + let blockTimeStamp = BigInt.fromString("14238192") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) + assert.assertNull(sale) + }) + test("should handle case without payment", () => { + let sample = new AtomicMatchInput(noPayment) + let hash = "0x337a707baaa15f3aa4004f0a2c9b5dcf38efc5c00fe162b27787409097cf20f8" + let blockTimeStamp = BigInt.fromString("14238192") + let sale = callAtomicMatch(hash, blockTimeStamp, sample) + assert.assertNull(sale) + }) }) + +function checkSaledata(sale: airstack.nft.Sale, expectedSale: airstack.nft.Sale): void { + assert.addressEquals(sale.buyer, expectedSale.buyer) + assert.addressEquals(sale.seller, expectedSale.seller) + assert.addressEquals(sale.paymentToken, expectedSale.paymentToken) + assert.addressEquals(sale.protocolFeesBeneficiary, expectedSale.protocolFeesBeneficiary) + assert.bigIntEquals(sale.paymentAmount, expectedSale.paymentAmount) + assert.bigIntEquals(sale.protocolFees, expectedSale.protocolFees) + if (expectedSale.nfts.length == 0) { + throw new Error("expectedSale.nfts.length is zero") + } + for (let i = 0; i < expectedSale.nfts.length; i++) { + const nft = sale.nfts[i] + const expectedNft = expectedSale.nfts[i] + assert.addressEquals(nft.collection, expectedNft.collection) + assert.bigIntEquals(nft.tokenId, expectedNft.tokenId) + assert.bigIntEquals(nft.amount, expectedNft.amount) + } +}