Skip to content

Commit

Permalink
Merge pull request #485 from oasisprotocol/andrew7234/erc721-total-su…
Browse files Browse the repository at this point in the history
…pply

[analyzer] dead reckon erc721 total supply
  • Loading branch information
Andrew7234 authored Jul 17, 2023
2 parents a7f589b + 7d93b97 commit 141a7fa
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 17 deletions.
18 changes: 16 additions & 2 deletions analyzer/evmtokens/evm_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"time"

"github.com/jackc/pgx/v5/pgtype"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"

Expand Down Expand Up @@ -68,6 +69,7 @@ func NewMain(
type StaleToken struct {
Addr string
LastDownloadRound *uint64
TotalSupply common.BigInt
Type *evm.EVMTokenType
AddrContextIdentifier string
AddrContextVersion int
Expand All @@ -84,9 +86,11 @@ func (m main) getStaleTokens(ctx context.Context, limit int) ([]*StaleToken, err
defer rows.Close()
for rows.Next() {
var staleToken StaleToken
var totalSupply pgtype.Numeric
if err = rows.Scan(
&staleToken.Addr,
&staleToken.LastDownloadRound,
&totalSupply,
&staleToken.Type,
&staleToken.AddrContextIdentifier,
&staleToken.AddrContextVersion,
Expand All @@ -95,6 +99,10 @@ func (m main) getStaleTokens(ctx context.Context, limit int) ([]*StaleToken, err
); err != nil {
return nil, fmt.Errorf("scanning discovered token: %w", err)
}
staleToken.TotalSupply, err = common.NumericToBigInt(totalSupply)
if err != nil {
return nil, fmt.Errorf("unmarshalling token totalSupply: %w", err)
}
staleTokens = append(staleTokens, &staleToken)
}
return staleTokens, nil
Expand All @@ -120,10 +128,16 @@ func (m main) processStaleToken(ctx context.Context, batch *storage.QueryBatch,
if err != nil {
return fmt.Errorf("downloading new token %s: %w", staleToken.Addr, err)
}
// Use the totalSupply downloaded directly from the chain if it exists,
// else default to the dead-reckoned value in analysis.evm_tokens.
totalSupplyBytes, err := staleToken.TotalSupply.MarshalText()
if err != nil {
return fmt.Errorf("error converting totalSupply: %w", err)
}
totalSupply := string(totalSupplyBytes)
// If the returned token type is unsupported, it will have zero value token data.
var totalSupply *string
if tokenData.EVMTokenMutableData != nil && tokenData.TotalSupply != nil {
totalSupply = common.Ptr(tokenData.TotalSupply.String())
totalSupply = tokenData.TotalSupply.String()
}
batch.Queue(queries.RuntimeEVMTokenInsert,
m.runtime,
Expand Down
23 changes: 17 additions & 6 deletions analyzer/queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ var (
SELECT
token_analysis.token_address,
token_analysis.last_download_round,
token_analysis.total_supply,
evm_tokens.token_type,
address_preimages.context_identifier,
address_preimages.context_version,
Expand Down Expand Up @@ -466,15 +467,17 @@ var (
)`

RuntimeEVMTokenAnalysisInsert = `
INSERT INTO analysis.evm_tokens (runtime, token_address, last_mutate_round)
VALUES ($1, $2, $3)
INSERT INTO analysis.evm_tokens (runtime, token_address, total_supply, last_mutate_round)
VALUES ($1, $2, $3, $4)
ON CONFLICT (runtime, token_address) DO NOTHING`

RuntimeEVMTokenAnalysisMutateInsert = `
INSERT INTO analysis.evm_tokens (runtime, token_address, last_mutate_round)
VALUES ($1, $2, $3)
ON CONFLICT (runtime, token_address) DO UPDATE
SET last_mutate_round = excluded.last_mutate_round`
INSERT INTO analysis.evm_tokens (runtime, token_address, total_supply, last_mutate_round)
VALUES ($1, $2, $3, $4)
ON CONFLICT (runtime, token_address) DO
UPDATE SET
total_supply = analysis.evm_tokens.total_supply + $3,
last_mutate_round = excluded.last_mutate_round`

RuntimeEVMTokenAnalysisUpdate = `
UPDATE analysis.evm_tokens
Expand All @@ -496,6 +499,14 @@ var (
runtime = $1 AND
token_address = $2`

RuntimeEVMTokenTotalSupplyChangeUpdate = `
UPDATE chain.evm_tokens
SET
total_supply = total_supply + $3
WHERE
runtime = $1 AND
token_address = $2`

RuntimeEVMTokenBalanceAnalysisStale = fmt.Sprintf(`
WITH
max_processed_round AS (
Expand Down
3 changes: 2 additions & 1 deletion analyzer/runtime/evm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const (
const NativeRuntimeTokenAddress = "oasis1runt1menat1vet0ken0000000000000000000000"

type EVMPossibleToken struct {
Mutated bool
Mutated bool
TotalSupplyChange *big.Int
}

type EVMTokenData struct {
Expand Down
19 changes: 13 additions & 6 deletions analyzer/runtime/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,14 +751,23 @@ func extractEvents(blockData *BlockData, relatedAccountAddresses map[apiTypes.Ad
}
// TODO: Reckon ownership.
if _, ok := blockData.PossibleTokens[eventAddr]; !ok {
blockData.PossibleTokens[eventAddr] = &evm.EVMPossibleToken{}
blockData.PossibleTokens[eventAddr] = &evm.EVMPossibleToken{
TotalSupplyChange: &big.Int{},
}
}
// Mark as mutated if transfer is between zero address
// and nonzero address (either direction) and nonzero
// amount. These will change the total supply as mint/
// burn.
if fromZero != toZero && tokenID.Cmp(&big.Int{}) != 0 {
blockData.PossibleTokens[eventAddr].Mutated = true
if fromZero && !toZero && tokenID.Cmp(&big.Int{}) != 0 {
pt := blockData.PossibleTokens[eventAddr]
pt.TotalSupplyChange.Add(pt.TotalSupplyChange, big.NewInt(1))
pt.Mutated = true
}
if !fromZero && toZero && tokenID.Cmp(&big.Int{}) != 0 {
pt := blockData.PossibleTokens[eventAddr]
pt.TotalSupplyChange.Sub(pt.TotalSupplyChange, big.NewInt(1))
pt.Mutated = true
}
eventData.EvmLogName = evmabi.ERC721.Events["Transfer"].Name
eventData.EvmLogSignature = ethCommon.BytesToHash(event.Topics[0])
Expand Down Expand Up @@ -861,9 +870,7 @@ func extractEvents(blockData *BlockData, relatedAccountAddresses map[apiTypes.Ad
{
Name: "approved",
EvmType: "bool",
// JSON supports encoding big integers, but many clients (javascript, jq, etc.)
// will incorrectly parse them as floats. So we encode uint256 as a string instead.
Value: approved,
Value: approved,
},
}
return nil
Expand Down
19 changes: 17 additions & 2 deletions analyzer/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,25 @@ func (m *processor) queueDbUpdates(batch *storage.QueryBatch, data *BlockData) {

// Insert EVM token addresses.
for addr, possibleToken := range data.PossibleTokens {
totalSupply := "0"
if possibleToken.TotalSupplyChange != nil {
totalSupply = possibleToken.TotalSupplyChange.String()
}
if possibleToken.Mutated {
batch.Queue(queries.RuntimeEVMTokenAnalysisMutateInsert, m.runtime, addr, data.Header.Round)
batch.Queue(queries.RuntimeEVMTokenAnalysisMutateInsert, m.runtime, addr, totalSupply, data.Header.Round)
} else {
batch.Queue(queries.RuntimeEVMTokenAnalysisInsert, m.runtime, addr, data.Header.Round)
batch.Queue(queries.RuntimeEVMTokenAnalysisInsert, m.runtime, addr, totalSupply, data.Header.Round)
}
// Dead reckon total_supply because it's optional for ERC721 contracts.
// If the evm_tokens analyzer is able to fetch the total supply from the node,
// it will supersede this.
if possibleToken.TotalSupplyChange != nil && possibleToken.TotalSupplyChange.Cmp(&big.Int{}) != 0 {
batch.Queue(
queries.RuntimeEVMTokenTotalSupplyChangeUpdate,
m.runtime,
addr,
possibleToken.TotalSupplyChange.String(),
)
}
}

Expand Down
1 change: 1 addition & 0 deletions storage/migrations/02_runtimes.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ CREATE TABLE chain.evm_token_analysis -- Moved to analysis.evm_tokens in 06_ana
(
runtime runtime NOT NULL,
token_address oasis_addr NOT NULL,
total_supply uint_numeric, -- Dead reckons total_supply before token is downloaded.
PRIMARY KEY (runtime, token_address),
-- Block analyzer bumps this when it sees the mutable fields of the token
-- change (e.g. total supply) based on dead reckoning.
Expand Down

0 comments on commit 141a7fa

Please sign in to comment.