Skip to content

Commit

Permalink
feat: index sales (decentraland#451)
Browse files Browse the repository at this point in the history
  • Loading branch information
cazala authored Nov 3, 2021
1 parent f81aaf9 commit a0ee63c
Show file tree
Hide file tree
Showing 12 changed files with 15,731 additions and 35,766 deletions.
51,251 changes: 15,541 additions & 35,710 deletions indexer/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions indexer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"deploy-studio:mainnet": "graph deploy --studio decentraland-marketplace-ethereum-mainnet"
},
"devDependencies": {
"@graphprotocol/graph-cli": "^0.21.1",
"@graphprotocol/graph-ts": "^0.16.0",
"@graphprotocol/graph-cli": "^0.19.0",
"@graphprotocol/graph-ts": "^0.19.0",
"ts-node": "^8.5.4",
"typescript": "^3.7.4"
},
Expand Down
44 changes: 38 additions & 6 deletions indexer/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@ type Count @entity {
orderEstate: Int!
orderWearable: Int!
orderENS: Int!

parcelTotal: Int!

estateTotal: Int!

wearableTotal: Int!

ensTotal: Int!

started: Int!
salesTotal: Int!
salesManaTotal: BigInt!
}

# ---------------------------------------------------------
Expand Down Expand Up @@ -154,6 +151,11 @@ type NFT @entity {

createdAt: BigInt!
updatedAt: BigInt!
soldAt: BigInt

# analytics
sales: Int!
volume: BigInt!

# search indexes
searchOrderStatus: OrderStatus
Expand Down Expand Up @@ -187,7 +189,11 @@ type Account @entity {
id: ID! # ETH addr
address: Bytes!
nfts: [NFT!] @derivedFrom(field: "owner")
mana: BigInt # Amount of mana owned
# analytics
sales: Int!
purchases: Int!
spent: BigInt!
earned: BigInt!
}

# ---------------------------------------------------------
Expand Down Expand Up @@ -239,3 +245,29 @@ enum WearableBodyShape @entity {
BaseFemale
BaseMale
}

# ---------------------------------------------------------
# Sales ---------------------------------------------------
# ---------------------------------------------------------

# We only track sales from Decentraland's smart contracts

enum SaleType @entity {
bid
order
}

type Sale @entity {
id: ID!
type: SaleType!
buyer: Bytes!
seller: Bytes!
price: BigInt!
nft: NFT!
timestamp: BigInt!
txHash: Bytes!

# search
searchTokenId: BigInt!
searchContractAddress: Bytes!
}
13 changes: 12 additions & 1 deletion indexer/src/handlers/bid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { BigInt, Address } from '@graphprotocol/graph-ts'
import {
BidCreated,
BidAccepted,
BidCancelled
BidCancelled,
} from '../entities/ERC721Bid/ERC721Bid'
import { Bid, NFT } from '../entities/schema'
import { getNFTId } from '../modules/nft'
import { getBidId } from '../modules/bid'
import { getCategory } from '../modules/category'
import * as status from '../modules/order/status'
import { BID_SALE_TYPE, trackSale } from '../modules/analytics'

export function handleBidCreated(event: BidCreated): void {
let category = getCategory(event.params._tokenAddress.toHexString())
Expand Down Expand Up @@ -74,6 +75,16 @@ export function handleBidAccepted(event: BidAccepted): void {

nft.updatedAt = event.block.timestamp
nft.save()

trackSale(
BID_SALE_TYPE,
event.params._bidder,
event.params._seller,
nft.id,
bid.price,
event.block.timestamp,
event.transaction.hash
)
}

export function handleBidCancelled(event: BidCancelled): void {
Expand Down
4 changes: 2 additions & 2 deletions indexer/src/handlers/ens.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NameRegistered } from '../entities/DCLRegistrar/DCLRegistrar'
import { ENS, NFT } from '../entities/schema'
import { createAccount } from '../modules/wallet'
import { getNFTId } from '../modules/nft'
import { getTokenIdFromLabelHash } from '../modules/ens'
import { createOrLoadAccount } from '../modules/account'
import { toLowerCase } from '../modules/utils'
import * as categories from '../modules/category/categories'
import * as addresses from '../data/addresses'
Expand All @@ -27,5 +27,5 @@ export function handleNameRegistered(event: NameRegistered): void {
nft.searchText = toLowerCase(ens.subdomain)
nft.save()

createAccount(event.params._caller)
createOrLoadAccount(event.params._caller)
}
11 changes: 7 additions & 4 deletions indexer/src/handlers/estate.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { ValueKind } from '@graphprotocol/graph-ts'
import { BigInt, ValueKind } from '@graphprotocol/graph-ts'
import {
CreateEstate,
AddLand,
RemoveLand,
Update
Update,
} from '../entities/EstateRegistry/EstateRegistry'
import { NFT, Parcel, Estate } from '../entities/schema'
import { getNFTId } from '../modules/nft'
import { decodeTokenId } from '../modules/parcel'
import { createAccount } from '../modules/wallet'
import { buildData, DataType } from '../modules/data'
import { createOrLoadAccount } from '../modules/account'
import { toLowerCase } from '../modules/utils'
import * as categories from '../modules/category/categories'
import * as addresses from '../data/addresses'
Expand Down Expand Up @@ -38,12 +38,15 @@ export function handleCreateEstate(event: CreateEstate): void {
nft.searchText = toLowerCase(estateData.name)
nft.createdAt = event.block.timestamp
nft.updatedAt = event.block.timestamp
nft.soldAt = null
nft.sales = 0
nft.volume = BigInt.fromI32(0)
nft.save()
}

estate.save()

createAccount(event.params._owner)
createOrLoadAccount(event.params._owner)
}

export function handleAddLand(event: AddLand): void {
Expand Down
47 changes: 32 additions & 15 deletions indexer/src/handlers/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ import { log } from '@graphprotocol/graph-ts'
import {
OrderCreated,
OrderSuccessful,
OrderCancelled
OrderCancelled,
} from '../entities/Marketplace/Marketplace'
import { Order, NFT } from '../entities/schema'
import {
getNFTId,
updateNFTOrderProperties,
cancelActiveOrder
cancelActiveOrder,
} from '../modules/nft'
import { getCategory } from '../modules/category'
import { buildCountFromOrder } from '../modules/count'
import * as status from '../modules/order/status'
import { ORDER_SALE_TYPE, trackSale } from '../modules/analytics'

export function handleOrderCreated(event: OrderCreated): void {
let category = getCategory(event.params.nftAddress.toHexString())
Expand Down Expand Up @@ -62,23 +63,39 @@ export function handleOrderSuccessful(event: OrderSuccessful): void {
)
let orderId = event.params.id.toHex()

let nft = NFT.load(nftId)
let order = Order.load(orderId)
if (order == null) {
return
}

if (nft != null && order != null) {
order.category = category
order.status = status.SOLD
order.buyer = event.params.buyer
order.price = event.params.totalPrice
order.blockNumber = event.block.number
order.updatedAt = event.block.timestamp
order.save()
order.category = category
order.status = status.SOLD
order.buyer = event.params.buyer
order.price = event.params.totalPrice
order.blockNumber = event.block.number
order.updatedAt = event.block.timestamp
order.save()

nft.owner = event.params.buyer.toHex()
nft.updatedAt = event.block.timestamp
nft = updateNFTOrderProperties(nft!, order!)
nft.save()
let nft = NFT.load(nftId)
if (nft == null) {
return
}

nft.owner = event.params.buyer.toHex()
nft.updatedAt = event.block.timestamp
nft = updateNFTOrderProperties(nft!, order!)
nft.save()

// analytics
trackSale(
ORDER_SALE_TYPE,
event.params.buyer,
event.params.seller,
nft.id,
order.price,
event.block.timestamp,
event.transaction.hash
)
}

export function handleOrderCancelled(event: OrderCancelled): void {
Expand Down
15 changes: 9 additions & 6 deletions indexer/src/handlers/nft.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { log } from '@graphprotocol/graph-ts'
import { BigInt } from '@graphprotocol/graph-ts'
import { Transfer } from '../entities/templates/ERC721/ERC721'
import { NFT, Parcel, Estate, Order, ENS, Wearable } from '../entities/schema'
import {
isMint,
getNFTId,
getTokenURI,
cancelActiveOrder,
clearNFTOrderProperties
clearNFTOrderProperties,
} from '../modules/nft'
import { getCategory } from '../modules/category'
import { buildEstateFromNFT, getEstateImage } from '../modules/estate'
Expand All @@ -15,19 +15,19 @@ import {
buildParcelFromNFT,
getParcelImage,
getParcelText,
isInBounds
isInBounds,
} from '../modules/parcel'
import {
buildWearableFromNFT,
getWearableImage,
isWearableHead,
isWearableAccessory
isWearableAccessory,
} from '../modules/wearable'
import { buildENSFromNFT } from '../modules/ens'
import { createAccount } from '../modules/wallet'
import { toLowerCase } from '../modules/utils'
import * as categories from '../modules/category/categories'
import * as addresses from '../data/addresses'
import { createOrLoadAccount } from '../modules/account'

export function handleTransfer(event: Transfer): void {
if (event.params.tokenId.toString() == '') {
Expand All @@ -49,6 +49,9 @@ export function handleTransfer(event: Transfer): void {
nft.contractAddress = event.address
nft.category = category
nft.updatedAt = event.block.timestamp
nft.soldAt = null
nft.sales = 0
nft.volume = BigInt.fromI32(0)

if (contractAddress != addresses.LANDRegistry) {
// The LANDRegistry contract does not have a tokenURI method
Expand Down Expand Up @@ -140,7 +143,7 @@ export function handleTransfer(event: Transfer): void {
ens.save()
}

createAccount(event.params.to)
createOrLoadAccount(event.params.to)

nft.save()
}
21 changes: 21 additions & 0 deletions indexer/src/modules/account/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BigInt, Address } from '@graphprotocol/graph-ts'
import { Account } from '../../entities/schema'

export let ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'

export function createOrLoadAccount(id: Address): Account {
let account = Account.load(id.toHex())

if (account == null) {
account = new Account(id.toHex())
account.address = id
account.sales = 0
account.purchases = 0
account.earned = BigInt.fromI32(0)
account.spent = BigInt.fromI32(0)
}

account.save()

return account!
}
57 changes: 57 additions & 0 deletions indexer/src/modules/analytics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Address, BigInt, Bytes, log } from '@graphprotocol/graph-ts'
import { NFT, Sale } from '../../entities/schema'
import { createOrLoadAccount } from '../account'
import { buildCountFromSale } from '../count'

export let BID_SALE_TYPE = 'bid'
export let ORDER_SALE_TYPE = 'order'

export function trackSale(
type: string,
buyer: Address,
seller: Address,
nftId: string,
price: BigInt,
timestamp: BigInt,
txHash: Bytes
): void {
// count sale
let count = buildCountFromSale(price)
count.save()

// load entities
let nft = NFT.load(nftId)

// save sale
let saleId = BigInt.fromI32(count.salesTotal).toString()
let sale = new Sale(saleId)
sale.type = type
sale.buyer = buyer
sale.seller = seller
sale.price = price
sale.nft = nftId
sale.timestamp = timestamp
sale.txHash = txHash
sale.searchTokenId = nft.tokenId
sale.searchContractAddress = nft.contractAddress
sale.save()

// update buyer account
let buyerAccount = createOrLoadAccount(buyer)
buyerAccount.purchases += 1
buyerAccount.spent = buyerAccount.spent.plus(price)
buyerAccount.save()

// update seller account
let sellerAccount = createOrLoadAccount(seller)
sellerAccount.sales += 1
sellerAccount.earned = sellerAccount.earned.plus(price)
sellerAccount.save()

// update nft
nft.soldAt = timestamp
nft.sales += 1
nft.volume = nft.volume.plus(price)
nft.updatedAt = timestamp
nft.save()
}
Loading

0 comments on commit a0ee63c

Please sign in to comment.