From ae220d611dd7d14d633470f81f7b49111427b570 Mon Sep 17 00:00:00 2001 From: Rahul Gupta Date: Wed, 8 Feb 2023 11:20:42 +0530 Subject: [PATCH] rarible v1 subgraph with nft module update in ens subgraph --- .../modules/airstack/common/index.ts | 176 ++++++++++++++++ .../airstack/nft-marketplace/Readme.md | 47 +++++ .../modules/airstack/nft-marketplace/index.ts | 1 + .../modules/airstack/nft-marketplace/nft.ts | 198 ++++++++++++++++++ .../modules/airstack/nft-marketplace/utils.ts | 1 + rarible-exchange-v1/package-lock.json | 4 +- 6 files changed, 425 insertions(+), 2 deletions(-) create mode 100644 rarible-exchange-v1/modules/airstack/common/index.ts create mode 100644 rarible-exchange-v1/modules/airstack/nft-marketplace/Readme.md create mode 100644 rarible-exchange-v1/modules/airstack/nft-marketplace/index.ts create mode 100644 rarible-exchange-v1/modules/airstack/nft-marketplace/nft.ts create mode 100644 rarible-exchange-v1/modules/airstack/nft-marketplace/utils.ts diff --git a/rarible-exchange-v1/modules/airstack/common/index.ts b/rarible-exchange-v1/modules/airstack/common/index.ts new file mode 100644 index 00000000..8205d8cc --- /dev/null +++ b/rarible-exchange-v1/modules/airstack/common/index.ts @@ -0,0 +1,176 @@ +import { + BigInt, + TypedMap, + dataSource, +} from "@graphprotocol/graph-ts"; +import { + AirBlock, + AirEntityCounter, + AirMeta, + AirAccount, +} from "../../../generated/schema"; + +export const AIR_META_ID = "AIR_META"; + +export const EMPTY_STRING = ""; +export const BIGINT_ONE = BigInt.fromI32(1); +export const BIG_INT_ZERO = BigInt.fromI32(0); + +export const SUBGRAPH_SCHEMA_VERSION = "1.0.0"; + +export const SUBGRAPH_NAME = "rarible"; +export const SUBGRAPH_VERSION = "exchange-v1"; +export const SUBGRAPH_SLUG = "rarible-exchange-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"); + +const AIR_CHAIN_ID_MAP = new TypedMap(); +AIR_CHAIN_ID_MAP.set("arbitrum-one", "42161"); +AIR_CHAIN_ID_MAP.set("arweave-mainnet", "174"); +AIR_CHAIN_ID_MAP.set("aurora", "1313161554"); +AIR_CHAIN_ID_MAP.set("avalanche", "43114"); +AIR_CHAIN_ID_MAP.set("boba", "288"); +AIR_CHAIN_ID_MAP.set("bsc", "56"); +AIR_CHAIN_ID_MAP.set("celo", "42220"); +AIR_CHAIN_ID_MAP.set("COSMOS", "cosmos"); +AIR_CHAIN_ID_MAP.set("CRONOS", "25"); +AIR_CHAIN_ID_MAP.set("mainnet", "1"); +AIR_CHAIN_ID_MAP.set("fantom", "250"); +AIR_CHAIN_ID_MAP.set("fuse", "122"); +AIR_CHAIN_ID_MAP.set("harmony", "1666600000"); +AIR_CHAIN_ID_MAP.set("juno", "juno-1"); +AIR_CHAIN_ID_MAP.set("moonbeam", "1284"); +AIR_CHAIN_ID_MAP.set("moonriver", "1285"); +AIR_CHAIN_ID_MAP.set("near-mainnet", "1313161554"); +AIR_CHAIN_ID_MAP.set("optimism", "10"); +AIR_CHAIN_ID_MAP.set("osmosis", "osmosis-1"); +AIR_CHAIN_ID_MAP.set("matic", "137"); +AIR_CHAIN_ID_MAP.set("xdai", "100"); + +export function processNetwork(network: string): string { + const value = AIR_NETWORK_MAP.get(network); + const result: string = value !== null ? value : "unknown"; + return result; +} + +export function processChainId(): string { + let network = dataSource.network(); + const value = AIR_CHAIN_ID_MAP.get(network); + const result: string = value !== null ? value : "unknown"; + return result; +} + +//air entity funcitons + +/** + * @dev this function updates air entity counter for a given entity id + * @param id entity id for entity to be updated + * @param block air block object + * @returns updated entity count + */ +export function updateAirEntityCounter( + id: string, + block: AirBlock, +): BigInt { + let entity = AirEntityCounter.load(id); + if (entity == null) { + entity = new AirEntityCounter(id); + entity.count = BIGINT_ONE; + entity.createdAt = block.id; + entity.lastUpdatedAt = block.id; + createAirMeta(SUBGRAPH_SLUG, SUBGRAPH_NAME); + } else { + entity.count = entity.count.plus(BIGINT_ONE); + entity.lastUpdatedAt = block.id; + } + entity.save(); + return entity.count as BigInt; +} + +/** + * @dev this function creates air meta entity + * @param slug subgraph slug + * @param name subgraph name + */ +export function createAirMeta( + slug: string, + name: string + // should ideally have version also being passed from here +): void { + let meta = AirMeta.load(AIR_META_ID); + if (meta == null) { + meta = new AirMeta(AIR_META_ID); + meta.network = processNetwork(dataSource.network()); + meta.schemaVersion = SUBGRAPH_SCHEMA_VERSION; + meta.version = SUBGRAPH_VERSION; + meta.slug = slug; + meta.name = name; + meta.save(); + } +} + +/** + * @dev this function gets or creates a new air block entity + * @param chainId chain id + * @param blockHeight block number + * @param blockHash block hash + * @param blockTimestamp block timestamp + * @returns AirBlock entity + */ +export function getOrCreateAirBlock( + chainId: string, + blockHeight: BigInt, + blockHash: string, + blockTimestamp: BigInt +): AirBlock { + let id = chainId.concat("-").concat(blockHeight.toString()); + + let block = AirBlock.load(id); + if (block == null) { + block = new AirBlock(id); + block.hash = blockHash; + block.number = blockHeight; + block.timestamp = blockTimestamp + block.save() + } + return block as AirBlock; +} + +/** + * @dev this function gets or creates a new air account entity + * @param chainId chain id + * @param address account address + * @param block air block object + * @returns AirAccount entity + */ +export function getOrCreateAirAccount(chainId: string, address: string, block: AirBlock): AirAccount { + let id = chainId.concat("-").concat(address); + let entity = AirAccount.load(id); + if (entity == null) { + entity = new AirAccount(id); + entity.address = address; + entity.createdAt = block.id; + } + return entity as AirAccount; +} diff --git a/rarible-exchange-v1/modules/airstack/nft-marketplace/Readme.md b/rarible-exchange-v1/modules/airstack/nft-marketplace/Readme.md new file mode 100644 index 00000000..91df5ec7 --- /dev/null +++ b/rarible-exchange-v1/modules/airstack/nft-marketplace/Readme.md @@ -0,0 +1,47 @@ +# NFT_MARKET_PLACE vertical integration +### After module integration for NFT_MARKET_PLACE vertical is done. Please call the below functions to track the transactions of the domain name vertical. + +``` +1. Track NFT trade transaction + trackNFTSaleTransactions( + chainID: string, #chain id of the network + txHash: string, #transaction hash + txIndex: BigInt, #transaction index - call or log index + NftSales: Sale[], #array of Sale objects + protocolType: string, #protocol type - SALE + protocolActionType: string, #protocol action type - ["BUY", "SELL"] + timestamp: BigInt, #timestamp of the transaction block + blockHeight: BigInt, #block number of the transaction block + blockHash: string #block hash of the transaction block + ) +``` + +## Class definitions for NFT trade transaction + +``` +class Sale { + buyer: Address, #nft buyer address + seller: Address, #nft seller address + nft: NFT, #NFT class object + paymentAmount: BigInt, #payment amount in wei + paymentToken: Address, #payment token address used for payment amount + protocolFees: BigInt, #protocol fees in wei + protocolFeesBeneficiary: Address, #protocol fees beneficiary address + royalties: CreatorRoyalty[] #array of CreatorRoyalty class objects +} +``` + +``` +class CreatorRoyalty { + fee: BigInt, #royalty fee in wei + beneficiary: Address #royalty beneficiary address +} +``` +``` +class NFT { + collection: Address, #nft collection address + standard: string, #nft standard - ["ERC721", "ERC1155"] + tokenId: BigInt, #nft token id + amount: BigInt #nft amount - 1 for ERC721 and n for ERC1155 +} +``` \ No newline at end of file diff --git a/rarible-exchange-v1/modules/airstack/nft-marketplace/index.ts b/rarible-exchange-v1/modules/airstack/nft-marketplace/index.ts new file mode 100644 index 00000000..7c531888 --- /dev/null +++ b/rarible-exchange-v1/modules/airstack/nft-marketplace/index.ts @@ -0,0 +1 @@ +export * from "./nft"; \ No newline at end of file diff --git a/rarible-exchange-v1/modules/airstack/nft-marketplace/nft.ts b/rarible-exchange-v1/modules/airstack/nft-marketplace/nft.ts new file mode 100644 index 00000000..920e8838 --- /dev/null +++ b/rarible-exchange-v1/modules/airstack/nft-marketplace/nft.ts @@ -0,0 +1,198 @@ +import { + Address, + BigInt, + dataSource, + log, +} from "@graphprotocol/graph-ts"; + +import { + AirAccount, + AirEntityCounter, + AirNftTransaction, + AirToken, + AirBlock, + AirNftSaleRoyalty, + AirMeta +} from "../../../generated/schema"; + +import { AIR_NFT_SALE_ENTITY_ID } from "./utils"; +import { updateAirEntityCounter, getOrCreateAirBlock, getOrCreateAirAccount } from "../common"; + +export namespace nft { + export function trackNFTSaleTransactions( + chainID: string, + txHash: string, + txIndex: BigInt, + NftSales: Sale[], + protocolType: string, + protocolActionType: string, + timestamp: BigInt, + blockHeight: BigInt, + blockHash: string + ): void { + if (NftSales.length == 0) { + return; + } + + let block = getOrCreateAirBlock(chainID, blockHeight, blockHash, timestamp); + + let transactionCount = NftSales.length; + for (let i = 0; i < transactionCount; i++) { + // Payment Token + let paymentToken = getOrCreateAirToken(chainID, NftSales[i].paymentToken.toHexString()); + + // Account + let buyerAccount = getOrCreateAirAccount(chainID, NftSales[i].buyer.toHexString(), block); + let sellerAccount = getOrCreateAirAccount(chainID, NftSales[i].seller.toHexString(), block); + let feeAccount = getOrCreateAirAccount(chainID, NftSales[i].protocolFeesBeneficiary.toHexString(), block); + buyerAccount.save(); + sellerAccount.save(); + feeAccount.save(); + // Sale Token + let saleToken = getOrCreateAirToken( + chainID, NftSales[i].nft.collection.toHexString() + ); + + // Transaction + let transactionId = getNFTSaleTransactionId( + chainID, + txHash, + txIndex, + NftSales[i].nft.collection.toHexString(), + NftSales[i].nft.tokenId + ) + + let transaction = AirNftTransaction.load(transactionId); + if (transaction == null) { + transaction = getOrCreateAirNftTransaction( + transactionId + ); + + transaction.index = updateAirEntityCounter(AIR_NFT_SALE_ENTITY_ID, block); + } + + transaction.to = buyerAccount.id; + transaction.from = sellerAccount.id; + transaction.protocolType = protocolType; + transaction.protocolActionType = protocolActionType; + transaction.tokenId = NftSales[i].nft.tokenId; + transaction.tokenAmount = NftSales[i].nft.amount; + transaction.paymentToken = paymentToken.id; + transaction.paymentAmount = NftSales[i].paymentAmount; + transaction.feeAmount = NftSales[i].protocolFees; + transaction.feeBeneficiary = feeAccount.id; + + transaction.transactionToken = saleToken.id; + transaction.hash = txHash; + transaction.block = block.id; + + // Creator Royalty + for (let j = 0; j < NftSales[i].royalties.length; j++) { + let royaltyAccount = getOrCreateAirAccount( + chainID, + NftSales[i].royalties[j].beneficiary.toHexString(), + block + ); + royaltyAccount.save(); + let royaltyId = transactionId + "-" + NftSales[i].royalties[j].beneficiary.toHexString(); + let royalty = getOrCreateRoyalty(royaltyId); + + royalty.amount = NftSales[i].royalties[j].fee + royalty.beneficiary = royaltyAccount.id + royalty.nftTransaction = transactionId + + royalty.save() + + log.debug("txId {} royaltyBeneficiary {} Amount {}", + [ + transactionId, + royalty.beneficiary, + royalty.amount.toString(), + ] + ) + + } + transaction.save(); + } + } + + export function getNFTSaleTransactionId( + chainID: string, + txHash: string, + txIndex: BigInt, + contractAddress: string, + nftId: BigInt + ): string { + return chainID + .concat("-") + .concat(txHash) + .concat("-") + .concat(txIndex.toString()) + .concat("-") + .concat(contractAddress) + .concat("-") + .concat(nftId.toString()); + } + + export function getOrCreateAirToken(chainID: string, address: string): AirToken { + let entity = AirToken.load(chainID + "-" + address); + if (entity == null) { + entity = new AirToken(chainID + "-" + address); + entity.address = address; + entity.save(); + } + return entity as AirToken; + } + + export function getOrCreateAirNftTransaction( + id: string + ): AirNftTransaction { + let transaction = AirNftTransaction.load(id); + + if (transaction == null) { + transaction = new AirNftTransaction(id); + } + + return transaction as AirNftTransaction; + } + + export function getOrCreateRoyalty( + id: string + ): AirNftSaleRoyalty { + let royalty = AirNftSaleRoyalty.load(id); + if (royalty == null) { + royalty = new AirNftSaleRoyalty(id); + } + return royalty as AirNftSaleRoyalty; + } + + export class Sale { + constructor( + public buyer: Address, + public seller: Address, + public nft: NFT, + public paymentAmount: BigInt, + public paymentToken: Address, + public protocolFees: BigInt, + public protocolFeesBeneficiary: Address, + public royalties: CreatorRoyalty[] + ) { } + } + + export class CreatorRoyalty { + constructor( + public fee: BigInt, + public beneficiary: Address + ) { } + } + + export class NFT { + constructor( + public readonly collection: Address, + public readonly standard: string, //ERC1155 or ERC721 + public readonly tokenId: BigInt, + public readonly amount: BigInt + ) { } + } + +} \ No newline at end of file diff --git a/rarible-exchange-v1/modules/airstack/nft-marketplace/utils.ts b/rarible-exchange-v1/modules/airstack/nft-marketplace/utils.ts new file mode 100644 index 00000000..ba04a4d2 --- /dev/null +++ b/rarible-exchange-v1/modules/airstack/nft-marketplace/utils.ts @@ -0,0 +1 @@ +export const AIR_NFT_SALE_ENTITY_ID = "AIR_NFT_SALE_TRANSACTION_COUNTER"; \ No newline at end of file diff --git a/rarible-exchange-v1/package-lock.json b/rarible-exchange-v1/package-lock.json index f027807b..f0e628c1 100644 --- a/rarible-exchange-v1/package-lock.json +++ b/rarible-exchange-v1/package-lock.json @@ -18,7 +18,7 @@ "node_modules/@airstack/subgraph-generator": { "version": "1.0.0-alpha.3", "resolved": "file:../airstack-modules/airstack-subgraph-generator-1.0.0-alpha.3.tgz", - "integrity": "sha512-7FTiIJvjy1593kwMgOOTXydwmtMQ6YVDweZaeR8WMBn8YCtkijZcHsIEEPGC0m5HFKnCKpJddBotLMa8R1BscQ==", + "integrity": "sha512-KUx7JlVuelaN3cD0OK/+fawUBlWSmCuNe7sgspeOpe4CEzDQR2i+ab5NYNRVMQ5bjEddgoO1ydvpJoYtR2Kqng==", "license": "ISC", "dependencies": { "@graphprotocol/graph-cli": "0.33.0", @@ -5416,7 +5416,7 @@ "dependencies": { "@airstack/subgraph-generator": { "version": "file:../airstack-modules/airstack-subgraph-generator-1.0.0-alpha.3.tgz", - "integrity": "sha512-7FTiIJvjy1593kwMgOOTXydwmtMQ6YVDweZaeR8WMBn8YCtkijZcHsIEEPGC0m5HFKnCKpJddBotLMa8R1BscQ==", + "integrity": "sha512-KUx7JlVuelaN3cD0OK/+fawUBlWSmCuNe7sgspeOpe4CEzDQR2i+ab5NYNRVMQ5bjEddgoO1ydvpJoYtR2Kqng==", "requires": { "@graphprotocol/graph-cli": "0.33.0", "@graphprotocol/graph-ts": "0.27.0",