Skip to content

Commit

Permalink
Merge pull request #527 from oasisprotocol/pro-wh/feature/holders14
Browse files Browse the repository at this point in the history
NFT transfer tracking
  • Loading branch information
pro-wh authored Oct 13, 2023
2 parents 921d67b + 71599fa commit d1d6475
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 19 deletions.
2 changes: 1 addition & 1 deletion analyzer/consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func (m *processor) queueEntityEvents(batch *storage.QueryBatch, data *storage.R
node.String(),
)
}
batch.Queue(queries.ConsensusEntityInsert,
batch.Queue(queries.ConsensusEntityUpsert,
entityID,
staking.NewAddress(entityEvent.Entity.ID).String(),
)
Expand Down
17 changes: 10 additions & 7 deletions analyzer/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ var (
INSERT INTO chain.claimed_nodes (entity_id, node_id) VALUES ($1, $2)
ON CONFLICT (entity_id, node_id) DO NOTHING`

ConsensusEntityInsert = `
ConsensusEntityUpsert = `
INSERT INTO chain.entities (id, address) VALUES ($1, $2)
ON CONFLICT (id) DO
UPDATE SET
Expand Down Expand Up @@ -527,7 +527,7 @@ var (
ON CONFLICT (runtime, token_address, account_address) DO
UPDATE SET balance = chain.evm_token_balances.balance + $4`

RuntimeEVMTokenBalanceAnalysisInsert = `
RuntimeEVMTokenBalanceAnalysisUpsert = `
INSERT INTO analysis.evm_token_balances
(runtime, token_address, account_address, last_mutate_round)
VALUES
Expand Down Expand Up @@ -624,12 +624,15 @@ var (
token_address = $2 AND
nft_id = $3`

RuntimeEVMNFTInsert = `
INSERT INTO chain.evm_nfts
(runtime, token_address, nft_id, last_want_download_round)
RuntimeEVMNFTUpsert = `
INSERT INTO chain.evm_nfts AS old
(runtime, token_address, nft_id, owner, num_transfers, last_want_download_round)
VALUES
($1, $2, $3, $4)
ON CONFLICT (runtime, token_address, nft_id) DO NOTHING`
($1, $2, $3, $4, $5, $6)
ON CONFLICT (runtime, token_address, nft_id) DO UPDATE
SET
owner = COALESCE(excluded.owner, old.owner),
num_transfers = old.num_transfers + excluded.num_transfers`

RuntimeEVMNFTAnalysisStale = `
SELECT
Expand Down
38 changes: 30 additions & 8 deletions analyzer/runtime/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ type NFTKey struct {
TokenID *big.Int
}

type PossibleNFT struct {
NewOwner *apiTypes.Address
NumTransfers int
}

type BlockData struct {
Header nodeapi.RuntimeBlockHeader
NumTransactions int // Might be different from len(TransactionData) if some transactions are malformed.
Expand All @@ -130,7 +135,7 @@ type BlockData struct {
AddressPreimages map[apiTypes.Address]*AddressPreimageData
TokenBalanceChanges map[TokenChangeKey]*big.Int
PossibleTokens map[apiTypes.Address]*evm.EVMPossibleToken // key is oasis bech32 address
PossibleNFTs map[NFTKey]struct{}
PossibleNFTs map[NFTKey]*PossibleNFT
}

// Function naming conventions in this file:
Expand Down Expand Up @@ -248,11 +253,24 @@ func registerRelatedEthAddress(addressPreimages map[apiTypes.Address]*AddressPre
return addr, nil
}

func registerNFTExist(possibleNFTs map[NFTKey]struct{}, contractAddr apiTypes.Address, tokenID *big.Int) {
func findPossibleNFT(possibleNFTs map[NFTKey]*PossibleNFT, contractAddr apiTypes.Address, tokenID *big.Int) *PossibleNFT {
key := NFTKey{contractAddr, tokenID}
if _, ok := possibleNFTs[key]; !ok {
possibleNFTs[key] = struct{}{}
possibleNFT, ok := possibleNFTs[key]
if !ok {
possibleNFT = &PossibleNFT{}
possibleNFTs[key] = possibleNFT
}
return possibleNFT
}

func registerNFTExist(nftChanges map[NFTKey]*PossibleNFT, contractAddr apiTypes.Address, tokenID *big.Int) {
findPossibleNFT(nftChanges, contractAddr, tokenID)
}

func registerNFTTransfer(nftChanges map[NFTKey]*PossibleNFT, contractAddr apiTypes.Address, tokenID *big.Int, newOwner apiTypes.Address) {
possibleNFT := findPossibleNFT(nftChanges, contractAddr, tokenID)
possibleNFT.NumTransfers++
possibleNFT.NewOwner = &newOwner
}

func findTokenChange(tokenChanges map[TokenChangeKey]*big.Int, contractAddr apiTypes.Address, accountAddr apiTypes.Address) *big.Int {
Expand Down Expand Up @@ -284,7 +302,7 @@ func ExtractRound(blockHeader nodeapi.RuntimeBlockHeader, txrs []nodeapi.Runtime
AddressPreimages: map[apiTypes.Address]*AddressPreimageData{},
TokenBalanceChanges: map[TokenChangeKey]*big.Int{},
PossibleTokens: map[apiTypes.Address]*evm.EVMPossibleToken{},
PossibleNFTs: map[NFTKey]struct{}{},
PossibleNFTs: map[NFTKey]*PossibleNFT{},
}

// Extract info from non-tx events.
Expand Down Expand Up @@ -930,23 +948,25 @@ func extractEvents(blockData *BlockData, relatedAccountAddresses map[apiTypes.Ad
tokenID.SetBytes(tokenIDU256)
fromZero := bytes.Equal(fromEthAddr, uncategorized.ZeroEthAddr)
toZero := bytes.Equal(toEthAddr, uncategorized.ZeroEthAddr)
var fromAddr, toAddr apiTypes.Address
if !fromZero {
fromAddr, err2 := registerRelatedEthAddress(blockData.AddressPreimages, relatedAccountAddresses, fromEthAddr)
var err2 error
fromAddr, err2 = registerRelatedEthAddress(blockData.AddressPreimages, relatedAccountAddresses, fromEthAddr)
if err2 != nil {
return fmt.Errorf("from: %w", err2)
}
eventData.RelatedAddresses[fromAddr] = true
registerTokenDecrease(blockData.TokenBalanceChanges, eventAddr, fromAddr, big.NewInt(1))
}
if !toZero {
toAddr, err2 := registerRelatedEthAddress(blockData.AddressPreimages, relatedAccountAddresses, toEthAddr)
var err2 error
toAddr, err2 = registerRelatedEthAddress(blockData.AddressPreimages, relatedAccountAddresses, toEthAddr)
if err2 != nil {
return fmt.Errorf("to: %w", err2)
}
eventData.RelatedAddresses[toAddr] = true
registerTokenIncrease(blockData.TokenBalanceChanges, eventAddr, toAddr, big.NewInt(1))
}
// TODO: Reckon ownership.
if _, ok := blockData.PossibleTokens[eventAddr]; !ok {
blockData.PossibleTokens[eventAddr] = &evm.EVMPossibleToken{}
}
Expand All @@ -967,6 +987,8 @@ func extractEvents(blockData *BlockData, relatedAccountAddresses map[apiTypes.Ad
pt.Mutated = true
}
registerNFTExist(blockData.PossibleNFTs, eventAddr, tokenID)
// Mints, burns, and zero-value transfers all count as transfers.
registerNFTTransfer(blockData.PossibleNFTs, eventAddr, tokenID, toAddr)
eventData.EvmLogName = evmabi.ERC721.Events["Transfer"].Name
eventData.EvmLogSignature = ethCommon.BytesToHash(event.Topics[0])
eventData.EvmLogParams = []*apiTypes.EvmEventParam{
Expand Down
14 changes: 11 additions & 3 deletions analyzer/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,19 @@ func (m *processor) queueDbUpdates(batch *storage.QueryBatch, data *BlockData) {
}
// Even for a (suspected) non-change, notify the evm_token_balances analyzer to
// verify the correct balance by querying the EVM.
batch.Queue(queries.RuntimeEVMTokenBalanceAnalysisInsert, m.runtime, key.TokenAddress, key.AccountAddress, data.Header.Round)
batch.Queue(queries.RuntimeEVMTokenBalanceAnalysisUpsert, m.runtime, key.TokenAddress, key.AccountAddress, data.Header.Round)
}

// Insert NFTs.
for key := range data.PossibleNFTs {
batch.Queue(queries.RuntimeEVMNFTInsert, m.runtime, key.TokenAddress, key.TokenID, data.Header.Round)
for key, possibleNFT := range data.PossibleNFTs {
batch.Queue(
queries.RuntimeEVMNFTUpsert,
m.runtime,
key.TokenAddress,
key.TokenID,
possibleNFT.NewOwner,
possibleNFT.NumTransfers,
data.Header.Round,
)
}
}
4 changes: 4 additions & 0 deletions storage/migrations/21_evm_nfts.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ CREATE TABLE chain.evm_nfts (
nft_id uint_numeric NOT NULL,
PRIMARY KEY (runtime, token_address, nft_id),

-- Added in 22_evm_nfts_2.up.sql
-- owner oasis_addr,
-- num_transfers INT NOT NULL,

last_want_download_round UINT63 NOT NULL,
last_download_round UINT63,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
BEGIN;

ALTER TABLE chain.evm_nfts
ADD COLUMN owner oasis_addr,
ADD COLUMN num_transfers INT NOT NULL DEFAULT 0;

-- Grant others read-only use.
GRANT SELECT ON ALL TABLES IN SCHEMA chain TO PUBLIC;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA chain TO PUBLIC;
Expand Down

0 comments on commit d1d6475

Please sign in to comment.