2018-11-07 21:50:43 +00:00
// VulcanizeDB
// Copyright © 2018 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2018-05-05 20:25:54 +00:00
package every_block
import (
"fmt"
2018-08-31 19:48:43 +00:00
"log"
"math/big"
2018-08-22 19:11:15 +00:00
"github.com/ethereum/go-ethereum/common"
2018-08-31 19:48:43 +00:00
2018-09-19 17:22:05 +00:00
"github.com/vulcanize/vulcanizedb/examples/erc20_watcher"
2018-05-05 20:25:54 +00:00
"github.com/vulcanize/vulcanizedb/libraries/shared"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
)
2018-08-31 19:48:43 +00:00
type ERC20Transformer struct {
Moved fetcher to generic directory (methods have to remain public since it is in seperate package now), added FetchHash method, created ERC20 and generic getters which call the fetcher with specific contract methods (GetTotalSupply, GetBalance, GetAllowance for ERC20 getter, and GetOwner, GetStoppedStatus, GetStringName, GetHashName, GetStringSymbol, GetHashSymbol, and GetDecimals for generic getter). Getter tests cover all but GetBalance and GetAllowance, and also cover all of the Fetcher methods- but with only nil methodArgs. GetAllowance and GetBalance tests are not working against infura and these are the only contract method calls with arguments passed in so I suspect this might be where the issue lies. Have tested GetBalance using previous version of FetchContractData without the variadic input to the Pack method and it fails with the same error so I don’t think it is due to those changes.
2018-08-15 04:17:22 +00:00
Getter ERC20GetterInterface
2018-08-31 19:48:43 +00:00
Repository ERC20TokenDatastore
2018-09-19 17:22:05 +00:00
Retriever erc20_watcher . TokenHolderRetriever
Config shared . ContractConfig
2018-05-05 20:25:54 +00:00
}
2018-09-19 17:22:05 +00:00
func ( t * ERC20Transformer ) SetConfiguration ( config shared . ContractConfig ) {
2018-05-05 20:25:54 +00:00
t . Config = config
}
2018-09-19 17:22:05 +00:00
func NewERC20TokenTransformer ( db * postgres . DB , blockchain core . BlockChain , con shared . ContractConfig ) ( shared . Transformer , error ) {
2018-08-22 19:11:15 +00:00
getter := NewGetter ( blockchain )
2018-08-09 16:58:06 +00:00
repository := ERC20TokenRepository { DB : db }
2018-09-19 17:22:05 +00:00
retriever := erc20_watcher . NewTokenHolderRetriever ( db , con . Address )
2018-08-31 19:48:43 +00:00
transformer := ERC20Transformer {
Moved fetcher to generic directory (methods have to remain public since it is in seperate package now), added FetchHash method, created ERC20 and generic getters which call the fetcher with specific contract methods (GetTotalSupply, GetBalance, GetAllowance for ERC20 getter, and GetOwner, GetStoppedStatus, GetStringName, GetHashName, GetStringSymbol, GetHashSymbol, and GetDecimals for generic getter). Getter tests cover all but GetBalance and GetAllowance, and also cover all of the Fetcher methods- but with only nil methodArgs. GetAllowance and GetBalance tests are not working against infura and these are the only contract method calls with arguments passed in so I suspect this might be where the issue lies. Have tested GetBalance using previous version of FetchContractData without the variadic input to the Pack method and it fails with the same error so I don’t think it is due to those changes.
2018-08-15 04:17:22 +00:00
Getter : & getter ,
2018-05-05 20:25:54 +00:00
Repository : & repository ,
2018-08-22 19:11:15 +00:00
Retriever : retriever ,
2018-09-19 17:22:05 +00:00
Config : con ,
2018-05-05 20:25:54 +00:00
}
2018-09-19 17:22:05 +00:00
return transformer , nil
2018-05-05 20:25:54 +00:00
}
const (
2018-08-22 19:11:15 +00:00
FetchingBlocksError = "Error fetching missing blocks starting at block number %d: %s"
FetchingSupplyError = "Error fetching supply for block %d: %s"
CreateSupplyError = "Error inserting token_supply for block %d: %s"
FetchingTokenAddressesError = "Error fetching token holder addresses at block %d: %s"
FetchingBalanceError = "Error fetching balance at block %d: %s"
CreateBalanceError = "Error inserting token_balance at block %d: %s"
FetchingAllowanceError = "Error fetching allowance at block %d: %s"
CreateAllowanceError = "Error inserting allowance at block %d: %s"
2018-05-05 20:25:54 +00:00
)
type transformerError struct {
err string
blockNumber int64
msg string
}
func ( te * transformerError ) Error ( ) string {
return fmt . Sprintf ( te . msg , te . blockNumber , te . err )
}
func newTransformerError ( err error , blockNumber int64 , msg string ) error {
e := transformerError { err . Error ( ) , blockNumber , msg }
log . Println ( e . Error ( ) )
return & e
}
2018-08-31 19:48:43 +00:00
func ( t ERC20Transformer ) Execute ( ) error {
2018-05-05 20:25:54 +00:00
var upperBoundBlock int64
2018-08-22 19:11:15 +00:00
blockchain := t . Getter . GetBlockChain ( )
lastBlock := blockchain . LastBlock ( ) . Int64 ( )
2018-05-05 20:25:54 +00:00
if t . Config . LastBlock == - 1 {
upperBoundBlock = lastBlock
} else {
upperBoundBlock = t . Config . LastBlock
}
2018-08-09 16:58:06 +00:00
// Supply transformations:
// Fetch missing supply blocks
blocks , err := t . Repository . MissingSupplyBlocks ( t . Config . FirstBlock , upperBoundBlock , t . Config . Address )
2018-05-05 20:25:54 +00:00
if err != nil {
return newTransformerError ( err , t . Config . FirstBlock , FetchingBlocksError )
}
2018-08-09 16:58:06 +00:00
// Fetch supply for missing blocks
2018-08-22 19:11:15 +00:00
log . Printf ( "Fetching totalSupply for %d blocks" , len ( blocks ) )
2018-08-09 16:58:06 +00:00
// For each block missing total supply, create supply model and feed the missing data into the repository
2018-05-05 20:25:54 +00:00
for _ , blockNumber := range blocks {
Moved fetcher to generic directory (methods have to remain public since it is in seperate package now), added FetchHash method, created ERC20 and generic getters which call the fetcher with specific contract methods (GetTotalSupply, GetBalance, GetAllowance for ERC20 getter, and GetOwner, GetStoppedStatus, GetStringName, GetHashName, GetStringSymbol, GetHashSymbol, and GetDecimals for generic getter). Getter tests cover all but GetBalance and GetAllowance, and also cover all of the Fetcher methods- but with only nil methodArgs. GetAllowance and GetBalance tests are not working against infura and these are the only contract method calls with arguments passed in so I suspect this might be where the issue lies. Have tested GetBalance using previous version of FetchContractData without the variadic input to the Pack method and it fails with the same error so I don’t think it is due to those changes.
2018-08-15 04:17:22 +00:00
totalSupply , err := t . Getter . GetTotalSupply ( t . Config . Abi , t . Config . Address , blockNumber )
2018-05-05 20:25:54 +00:00
if err != nil {
2018-08-22 19:11:15 +00:00
return newTransformerError ( err , blockNumber , FetchingSupplyError )
2018-05-05 20:25:54 +00:00
}
2018-08-09 16:58:06 +00:00
// Create the supply model
2018-05-05 20:25:54 +00:00
model := createTokenSupplyModel ( totalSupply , t . Config . Address , blockNumber )
2018-08-09 16:58:06 +00:00
// Feed it into the repository
err = t . Repository . CreateSupply ( model )
2018-05-05 20:25:54 +00:00
if err != nil {
return newTransformerError ( err , blockNumber , CreateSupplyError )
}
}
2018-08-22 19:11:15 +00:00
// Balance and allowance transformations:
// Retrieve all token holder addresses for the given contract configuration
2018-08-28 17:50:53 +00:00
tokenHolderAddresses , err := t . Retriever . RetrieveTokenHolderAddresses ( )
2018-08-22 19:11:15 +00:00
if err != nil {
return newTransformerError ( err , t . Config . FirstBlock , FetchingTokenAddressesError )
}
// Iterate over the addresses and add their balances and allowances at each block height to the repository
for holderAddr := range tokenHolderAddresses {
// Balance transformations:
blocks , err := t . Repository . MissingBalanceBlocks ( t . Config . FirstBlock , upperBoundBlock , t . Config . Address , holderAddr . String ( ) )
if err != nil {
return newTransformerError ( err , t . Config . FirstBlock , FetchingBlocksError )
}
log . Printf ( "Fetching balances for %d blocks" , len ( blocks ) )
// For each block missing balances for the given address, create a balance model and feed the missing data into the repository
for _ , blockNumber := range blocks {
hashArgs := [ ] common . Address { holderAddr }
balanceOfArgs := make ( [ ] interface { } , len ( hashArgs ) )
for i , s := range hashArgs {
balanceOfArgs [ i ] = s
}
totalSupply , err := t . Getter . GetBalance ( t . Config . Abi , t . Config . Address , blockNumber , balanceOfArgs )
if err != nil {
return newTransformerError ( err , blockNumber , FetchingBalanceError )
}
model := createTokenBalanceModel ( totalSupply , t . Config . Address , blockNumber , holderAddr . String ( ) )
err = t . Repository . CreateBalance ( model )
if err != nil {
return newTransformerError ( err , blockNumber , CreateBalanceError )
}
}
// Allowance transformations:
for spenderAddr := range tokenHolderAddresses {
blocks , err := t . Repository . MissingAllowanceBlocks ( t . Config . FirstBlock , upperBoundBlock , t . Config . Address , holderAddr . String ( ) , spenderAddr . String ( ) )
if err != nil {
return newTransformerError ( err , t . Config . FirstBlock , FetchingBlocksError )
}
log . Printf ( "Fetching allowances for %d blocks" , len ( blocks ) )
// For each block missing allowances for the given holder and spender addresses, create a allowance model and feed the missing data into the repository
for _ , blockNumber := range blocks {
hashArgs := [ ] common . Address { holderAddr , spenderAddr }
allowanceArgs := make ( [ ] interface { } , len ( hashArgs ) )
for i , s := range hashArgs {
allowanceArgs [ i ] = s
}
totalSupply , err := t . Getter . GetAllowance ( t . Config . Abi , t . Config . Address , blockNumber , allowanceArgs )
if err != nil {
return newTransformerError ( err , blockNumber , FetchingAllowanceError )
}
model := createTokenAllowanceModel ( totalSupply , t . Config . Address , blockNumber , holderAddr . String ( ) , spenderAddr . String ( ) )
err = t . Repository . CreateAllowance ( model )
if err != nil {
return newTransformerError ( err , blockNumber , CreateAllowanceError )
}
}
}
}
2018-05-05 20:25:54 +00:00
return nil
}
func createTokenSupplyModel ( totalSupply big . Int , address string , blockNumber int64 ) TokenSupply {
return TokenSupply {
Value : totalSupply . String ( ) ,
TokenAddress : address ,
BlockNumber : blockNumber ,
}
}
2018-08-22 19:11:15 +00:00
func createTokenBalanceModel ( tokenBalance big . Int , tokenAddress string , blockNumber int64 , tokenHolderAddress string ) TokenBalance {
return TokenBalance {
Value : tokenBalance . String ( ) ,
TokenAddress : tokenAddress ,
BlockNumber : blockNumber ,
TokenHolderAddress : tokenHolderAddress ,
}
}
func createTokenAllowanceModel ( tokenBalance big . Int , tokenAddress string , blockNumber int64 , tokenHolderAddress , tokenSpenderAddress string ) TokenAllowance {
return TokenAllowance {
Value : tokenBalance . String ( ) ,
TokenAddress : tokenAddress ,
BlockNumber : blockNumber ,
TokenHolderAddress : tokenHolderAddress ,
TokenSpenderAddress : tokenSpenderAddress ,
}
}