diff --git a/examples/erc20_watcher/every_block/fetcher.go b/examples/erc20_watcher/every_block/fetcher.go index e644b0c5..0d31fdf6 100644 --- a/examples/erc20_watcher/every_block/fetcher.go +++ b/examples/erc20_watcher/every_block/fetcher.go @@ -19,45 +19,54 @@ import ( "log" "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/vulcanize/vulcanizedb/pkg/core" ) +// Interface definition for a Fetcher type ERC20FetcherInterface interface { - FetchSupplyOf(contractAbi string, contractAddress string, blockNumber int64) (big.Int, error) + FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) + FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) + FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error) GetBlockChain() core.BlockChain } -func NewFetcher(blockchain core.BlockChain) Fetcher { +// Initializes and returns a Fetcher with the given blockchain +func NewFetcher(blockChain core.BlockChain) Fetcher { return Fetcher{ - Blockchain: blockchain, + BlockChain: blockChain, } } +// Fetcher struct type Fetcher struct { - Blockchain core.BlockChain + BlockChain core.BlockChain ContractAbi string ContractAddress string } +// Fetcher error type fetcherError struct { err string fetchMethod string } +// Fetcher error method func (fe *fetcherError) Error() string { return fmt.Sprintf("Error fetching %s: %s", fe.fetchMethod, fe.err) } +// Used to create a new Fetcher error for a given error and fetch method func newFetcherError(err error, fetchMethod string) *fetcherError { e := fetcherError{err.Error(), fetchMethod} log.Println(e.Error()) return &e } -func (f Fetcher) FetchSupplyOf(contractAbi string, contractAddress string, blockNumber int64) (big.Int, error) { - method := "totalSupply" +// Method used to fetch big.Int result from contract +func (f Fetcher) FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) { var result = new(big.Int) - err := f.Blockchain.FetchContractData(contractAbi, contractAddress, method, nil, &result, blockNumber) + err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) if err != nil { return *result, newFetcherError(err, method) @@ -66,6 +75,31 @@ func (f Fetcher) FetchSupplyOf(contractAbi string, contractAddress string, block return *result, nil } -func (f Fetcher) GetBlockChain() core.BlockChain { - return f.Blockchain +// Method used to fetch bool result from contract +func (f Fetcher) FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) { + var result = new(bool) + err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) + + if err != nil { + return *result, newFetcherError(err, method) + } + + return *result, nil +} + +// Method used to fetch address result from contract +func (f Fetcher) FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error) { + var result = new(common.Address) + err := f.BlockChain.FetchContractData(contractAbi, contractAddress, method, methodArgs, &result, blockNumber) + + if err != nil { + return *result, newFetcherError(err, method) + } + + return *result, nil +} + +// Getter method for Fetcher's blockchain +func (f Fetcher) GetBlockChain() core.BlockChain { + return f.BlockChain } diff --git a/examples/erc20_watcher/every_block/fetcher_test.go b/examples/erc20_watcher/every_block/fetcher_test.go index 9ca34486..1e2dfa30 100644 --- a/examples/erc20_watcher/every_block/fetcher_test.go +++ b/examples/erc20_watcher/every_block/fetcher_test.go @@ -34,22 +34,23 @@ import ( var _ = Describe("ERC20 Fetcher", func() { blockNumber := int64(5502914) - Describe("FetchSupplyOf", func() { - It("fetches data from the blockchain with the correct arguments", func() { - fakeBlockchain := fakes.NewMockBlockChain() - testFetcher := every_block.NewFetcher(fakeBlockchain) + Describe("totalSupply", func() { + It("fetches total supply data from the blockchain with the correct arguments", func() { + fakeBlockChain := fakes.NewMockBlockChain() + testFetcher := every_block.NewFetcher(fakeBlockChain) testAbi := "testAbi" testContractAddress := "testContractAddress" - _, err := testFetcher.FetchSupplyOf(testAbi, testContractAddress, blockNumber) + + _, err := testFetcher.FetchBigInt("totalSupply", testAbi, testContractAddress, blockNumber, nil) Expect(err).NotTo(HaveOccurred()) expectedResult := big.Int{} expected := &expectedResult - fakeBlockchain.AssertFetchContractDataCalledWith(testAbi, testContractAddress, "totalSupply", nil, &expected, blockNumber) + fakeBlockChain.AssertFetchContractDataCalledWith(testAbi, testContractAddress, "totalSupply", nil, &expected, blockNumber) }) - It("fetches a token's total supply at the given block height", func() { - infuraIPC := "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL" + It("fetches dai token's total supply at the given block height", func() { + infuraIPC := "https://mainnet.infura.io/v3/b09888c1113640cc9ab42750ce750c05" rawRpcClient, err := rpc.Dial(infuraIPC) Expect(err).NotTo(HaveOccurred()) rpcClient := client.NewRpcClient(rawRpcClient, infuraIPC) @@ -59,7 +60,7 @@ var _ = Describe("ERC20 Fetcher", func() { transactionConverter := rpc2.NewRpcTransactionConverter(ethClient) blockChain := geth.NewBlockChain(blockChainClient, node, transactionConverter) realFetcher := every_block.NewFetcher(blockChain) - result, err := realFetcher.FetchSupplyOf(constants.DaiAbiString, constants.DaiContractAddress, blockNumber) + result, err := realFetcher.FetchBigInt("totalSupply", constants.DaiAbiString, constants.DaiContractAddress, blockNumber, nil) Expect(err).NotTo(HaveOccurred()) expectedResult := big.Int{} @@ -71,7 +72,7 @@ var _ = Describe("ERC20 Fetcher", func() { blockChain := fakes.NewMockBlockChain() blockChain.SetFetchContractDataErr(fakes.FakeError) errorFetcher := every_block.NewFetcher(blockChain) - result, err := errorFetcher.FetchSupplyOf("", "", 0) + result, err := errorFetcher.FetchBigInt("totalSupply", "", "", 0, nil) Expect(result.String()).To(Equal("0")) Expect(err).To(HaveOccurred()) @@ -79,4 +80,4 @@ var _ = Describe("ERC20 Fetcher", func() { Expect(err.Error()).To(ContainSubstring(fakes.FakeError.Error())) }) }) -}) +}) \ No newline at end of file diff --git a/examples/erc20_watcher/every_block/model.go b/examples/erc20_watcher/every_block/model.go index fc973b9b..60492311 100644 --- a/examples/erc20_watcher/every_block/model.go +++ b/examples/erc20_watcher/every_block/model.go @@ -14,8 +14,26 @@ package every_block +// Struct to hold token supply data type TokenSupply struct { Value string TokenAddress string BlockNumber int64 } + +// Struct to hold token holder address balance data +type TokenBalance struct { + Value string + TokenAddress string + BlockNumber int64 + TokenHolderAddress string +} + +// Struct to hold token allowance data +type TokenAllowance struct { + Value string + TokenAddress string + BlockNumber int64 + TokenHolderAddress string + TokenSpenderAddress string +} diff --git a/examples/erc20_watcher/every_block/repository.go b/examples/erc20_watcher/every_block/repository.go index c0c4d79c..55501532 100644 --- a/examples/erc20_watcher/every_block/repository.go +++ b/examples/erc20_watcher/every_block/repository.go @@ -20,38 +20,46 @@ import ( "log" ) +// Interface definition for a generic ERC20 token repository type ERC20RepositoryInterface interface { - Create(supply TokenSupply) error - MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) + CreateSupply(supply TokenSupply) error + MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) } -type TokenSupplyRepository struct { +// Generic ERC20 token Repo struct +type ERC20TokenRepository struct { *postgres.DB } +// Repo error type repositoryError struct { err string msg string blockNumber int64 } +// Repo error method func (re *repositoryError) Error() string { return fmt.Sprintf(re.msg, re.blockNumber, re.err) } +// Used to create a new Repo error for a given error and fetch method func newRepositoryError(err error, msg string, blockNumber int64) error { e := repositoryError{err.Error(), msg, blockNumber} log.Println(e.Error()) return &e } +// Constant error definitions const ( GetBlockError = "Error fetching block number %d: %s" InsertTokenSupplyError = "Error inserting token_supply for block number %d: %s" MissingBlockError = "Error finding missing token_supply records starting at block %d: %s" ) -func (tsp *TokenSupplyRepository) Create(supply TokenSupply) error { +// Supply methods +// This method inserts the supply for a given token contract address at a given block height into the token_supply table +func (tsp *ERC20TokenRepository) CreateSupply(supply TokenSupply) error { var blockId int err := tsp.DB.Get(&blockId, `SELECT id FROM blocks WHERE number = $1 AND eth_node_id = $2`, supply.BlockNumber, tsp.NodeID) if err != nil { @@ -68,7 +76,8 @@ func (tsp *TokenSupplyRepository) Create(supply TokenSupply) error { return nil } -func (tsp *TokenSupplyRepository) MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) { +// This method returns an array of blocks that are missing a token_supply entry for a given tokenAddress +func (tsp *ERC20TokenRepository) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) { blockNumbers := make([]int64, 0) err := tsp.DB.Select( @@ -88,4 +97,4 @@ func (tsp *TokenSupplyRepository) MissingBlocks(startingBlock int64, highestBloc return []int64{}, newRepositoryError(err, MissingBlockError, startingBlock) } return blockNumbers, err -} +} \ No newline at end of file diff --git a/examples/erc20_watcher/every_block/repository_test.go b/examples/erc20_watcher/every_block/repository_test.go index 68372049..e3e7c13d 100644 --- a/examples/erc20_watcher/every_block/repository_test.go +++ b/examples/erc20_watcher/every_block/repository_test.go @@ -31,13 +31,13 @@ var _ = Describe("ERC20 Token Repository", func() { var db *postgres.DB var blockId int64 var blockNumber int64 - var repository every_block.TokenSupplyRepository + var repository every_block.ERC20TokenRepository var blockRepository repositories.BlockRepository testAddress := "abc" BeforeEach(func() { db = test_helpers.CreateNewDatabase() - repository = every_block.TokenSupplyRepository{DB: db} + repository = every_block.ERC20TokenRepository{DB: db} _, err := db.Query(`DELETE FROM token_supply`) Expect(err).NotTo(HaveOccurred()) @@ -49,7 +49,7 @@ var _ = Describe("ERC20 Token Repository", func() { Describe("Create", func() { It("creates a token supply record", func() { supply := supplyModel(blockNumber, testAddress, "100") - err := repository.Create(supply) + err := repository.CreateSupply(supply) Expect(err).NotTo(HaveOccurred()) dbResult := test_helpers.TokenSupplyDBRow{} @@ -73,7 +73,7 @@ var _ = Describe("ERC20 Token Repository", func() { It("returns an error if fetching the block's id from the database fails", func() { errorSupply := supplyModel(-1, "", "") - err := repository.Create(errorSupply) + err := repository.CreateSupply(errorSupply) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("sql")) @@ -82,7 +82,7 @@ var _ = Describe("ERC20 Token Repository", func() { It("returns an error if inserting the token_supply fails", func() { errorSupply := supplyModel(blockNumber, "", "") - err := repository.Create(errorSupply) + err := repository.CreateSupply(errorSupply) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("pq")) @@ -94,7 +94,7 @@ var _ = Describe("ERC20 Token Repository", func() { var node2DB *postgres.DB var node2BlockRepo *repositories.BlockRepository var node2BlockId int64 - var node2TokenSupplyRepo every_block.TokenSupplyRepository + var node2TokenSupplyRepo every_block.ERC20TokenRepository var tokenSupply every_block.TokenSupply BeforeEach(func() { @@ -105,11 +105,11 @@ var _ = Describe("ERC20 Token Repository", func() { node2BlockId = test_helpers.CreateBlock(blockNumber, *node2BlockRepo) tokenSupply = supplyModel(blockNumber, "abc", "100") - node2TokenSupplyRepo = every_block.TokenSupplyRepository{DB: node2DB} + node2TokenSupplyRepo = every_block.ERC20TokenRepository{DB: node2DB} }) It("only creates token_supply records for the current node (node2)", func() { - err := node2TokenSupplyRepo.Create(tokenSupply) + err := node2TokenSupplyRepo.CreateSupply(tokenSupply) Expect(err).NotTo(HaveOccurred()) var tokenSupplies []test_helpers.TokenSupplyDBRow @@ -121,14 +121,14 @@ var _ = Describe("ERC20 Token Repository", func() { It("only includes missing block numbers for the current node", func() { //create token_supply on original node - err := repository.Create(tokenSupply) + err := repository.CreateSupply(tokenSupply) Expect(err).NotTo(HaveOccurred()) - originalNodeMissingBlocks, err := repository.MissingBlocks(blockNumber, blockNumber) + originalNodeMissingBlocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(err).NotTo(HaveOccurred()) Expect(len(originalNodeMissingBlocks)).To(Equal(0)) - node2MissingBlocks, err := node2TokenSupplyRepo.MissingBlocks(blockNumber, blockNumber) + node2MissingBlocks, err := node2TokenSupplyRepo.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(err).NotTo(HaveOccurred()) Expect(len(node2MissingBlocks)).To(Equal(1)) }) @@ -140,7 +140,7 @@ var _ = Describe("ERC20 Token Repository", func() { newBlockNumber := blockNumber + 1 test_helpers.CreateBlock(newBlockNumber, blockRepository) - blocks, err := repository.MissingBlocks(blockNumber, newBlockNumber) + blocks, err := repository.MissingSupplyBlocks(blockNumber, newBlockNumber, testAddress) Expect(blocks).To(ConsistOf(newBlockNumber)) Expect(err).NotTo(HaveOccurred()) @@ -149,7 +149,7 @@ var _ = Describe("ERC20 Token Repository", func() { It("only returns blocks within the given range", func() { newBlockNumber := blockNumber + 1 test_helpers.CreateBlock(newBlockNumber, blockRepository) - blocks, err := repository.MissingBlocks(blockNumber, blockNumber) + blocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(blocks).NotTo(ConsistOf(newBlockNumber)) Expect(err).NotTo(HaveOccurred()) @@ -157,7 +157,7 @@ var _ = Describe("ERC20 Token Repository", func() { It("does not return numbers that already have an associated TokenSupply record", func() { createTokenSupplyFor(repository, blockNumber) - blocks, err := repository.MissingBlocks(blockNumber, blockNumber) + blocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(blocks).To(BeEmpty()) Expect(err).NotTo(HaveOccurred()) @@ -165,7 +165,7 @@ var _ = Describe("ERC20 Token Repository", func() { }) It("deletes the token supply record when the associated block is deleted", func() { - err := repository.Create(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) + err := repository.CreateSupply(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) Expect(err).NotTo(HaveOccurred()) var count int @@ -190,8 +190,8 @@ func supplyModel(blockNumber int64, tokenAddress string, supplyValue string) eve } } -func createTokenSupplyFor(repository every_block.TokenSupplyRepository, blockNumber int64) { - err := repository.Create(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) +func createTokenSupplyFor(repository every_block.ERC20TokenRepository, blockNumber int64) { + err := repository.CreateSupply(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) Expect(err).NotTo(HaveOccurred()) } diff --git a/examples/erc20_watcher/every_block/transformer.go b/examples/erc20_watcher/every_block/transformer.go index 6ca2718b..41a47c5c 100644 --- a/examples/erc20_watcher/every_block/transformer.go +++ b/examples/erc20_watcher/every_block/transformer.go @@ -40,7 +40,7 @@ type TokenSupplyTransformerInitializer struct { func (i TokenSupplyTransformerInitializer) NewTokenSupplyTransformer(db *postgres.DB, blockchain core.BlockChain) shared.Transformer { fetcher := NewFetcher(blockchain) - repository := TokenSupplyRepository{DB: db} + repository := ERC20TokenRepository{DB: db} transformer := Transformer{ Fetcher: &fetcher, Repository: &repository, @@ -83,21 +83,29 @@ func (t Transformer) Execute() error { upperBoundBlock = t.Config.LastBlock } - blocks, err := t.Repository.MissingBlocks(t.Config.FirstBlock, upperBoundBlock) + // Supply transformations: + + // Fetch missing supply blocks + blocks, err := t.Repository.MissingSupplyBlocks(t.Config.FirstBlock, upperBoundBlock, t.Config.Address) if err != nil { return newTransformerError(err, t.Config.FirstBlock, FetchingBlocksError) } + // Fetch supply for missing blocks log.Printf("Fetching totalSupply for %d blocks", len(blocks)) + + // For each block missing total supply, create supply model and feed the missing data into the repository for _, blockNumber := range blocks { - totalSupply, err := t.Fetcher.FetchSupplyOf(t.Config.Abi, t.Config.Address, blockNumber) + totalSupply, err := t.Fetcher.FetchBigInt("totalSupply", t.Config.Abi, t.Config.Address, blockNumber, nil) if err != nil { return newTransformerError(err, blockNumber, FetchingSupplyError) } + // Create the supply model model := createTokenSupplyModel(totalSupply, t.Config.Address, blockNumber) - err = t.Repository.Create(model) + // Feed it into the repository + err = t.Repository.CreateSupply(model) if err != nil { return newTransformerError(err, blockNumber, CreateSupplyError) diff --git a/examples/erc20_watcher/every_block/transformer_test.go b/examples/erc20_watcher/every_block/transformer_test.go index a89a5fcc..242c911f 100644 --- a/examples/erc20_watcher/every_block/transformer_test.go +++ b/examples/erc20_watcher/every_block/transformer_test.go @@ -39,7 +39,7 @@ var config = testContractConfig var _ = Describe("Everyblock transformer", func() { var fetcher mocks.Fetcher - var repository mocks.TotalSupplyRepository + var repository mocks.ERC20TokenRepository var transformer every_block.Transformer var blockChain *fakes.MockBlockChain var initialSupply = "27647235749155415536952630" @@ -53,8 +53,8 @@ var _ = Describe("Everyblock transformer", func() { blockChain.SetLastBlock(&defaultLastBlock) fetcher = mocks.Fetcher{BlockChain: blockChain} fetcher.SetSupply(initialSupply) - repository = mocks.TotalSupplyRepository{} - repository.SetMissingBlocks([]int64{config.FirstBlock}) + repository = mocks.ERC20TokenRepository{} + repository.SetMissingSupplyBlocks([]int64{config.FirstBlock}) //setting the mock repository to return the first block as the missing blocks transformer = every_block.Transformer{ @@ -89,8 +89,9 @@ var _ = Describe("Everyblock transformer", func() { config.FirstBlock + 1, config.FirstBlock + 2, } - repository.SetMissingBlocks(missingBlocks) - transformer.Execute() + repository.SetMissingSupplyBlocks(missingBlocks) + err := transformer.Execute() + Expect(err).NotTo(HaveOccurred()) Expect(len(fetcher.FetchedBlocks)).To(Equal(3)) Expect(fetcher.FetchedBlocks).To(ConsistOf(config.FirstBlock, config.FirstBlock+1, config.FirstBlock+2)) @@ -104,7 +105,7 @@ var _ = Describe("Everyblock transformer", func() { }) It("uses the set contract configuration", func() { - repository.SetMissingBlocks([]int64{testContractConfig.FirstBlock}) + repository.SetMissingSupplyBlocks([]int64{testContractConfig.FirstBlock}) transformer.SetConfiguration(testContractConfig) err := transformer.Execute() Expect(err).NotTo(HaveOccurred()) @@ -143,7 +144,7 @@ var _ = Describe("Everyblock transformer", func() { It("returns an error if the call to get missing blocks fails", func() { failureRepository := mocks.FailureRepository{} - failureRepository.SetMissingBlocksFail(true) + failureRepository.SetMissingSupplyBlocksFail(true) transformer = every_block.Transformer{ Fetcher: &fetcher, Repository: &failureRepository, @@ -171,8 +172,8 @@ var _ = Describe("Everyblock transformer", func() { It("returns an error if the call to save the token_supply fails", func() { failureRepository := mocks.FailureRepository{} - failureRepository.SetMissingBlocks([]int64{config.FirstBlock}) - failureRepository.SetCreateFail(true) + failureRepository.SetMissingSupplyBlocks([]int64{config.FirstBlock}) + failureRepository.SetCreateSupplyFail(true) transformer = every_block.Transformer{ Fetcher: &fetcher, diff --git a/examples/mocks/mocks.go b/examples/mocks/mocks.go index b2944170..03861de0 100644 --- a/examples/mocks/mocks.go +++ b/examples/mocks/mocks.go @@ -15,11 +15,14 @@ package mocks import ( + "errors" "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/vulcanize/vulcanizedb/examples/erc20_watcher/every_block" "github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/fakes" + "reflect" ) type Fetcher struct { @@ -28,6 +31,8 @@ type Fetcher struct { FetchedBlocks []int64 BlockChain core.BlockChain supply big.Int + balance map[string]*big.Int + allowance map[string]map[string]*big.Int } func (f *Fetcher) SetSupply(supply string) { @@ -38,69 +43,203 @@ func (f Fetcher) GetBlockChain() core.BlockChain { return f.BlockChain } -func (f *Fetcher) FetchSupplyOf(contractAbi string, contractAddress string, blockNumber int64) (big.Int, error) { +func (f *Fetcher) FetchBigInt(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (big.Int, error) { + f.Abi = contractAbi f.ContractAddress = contractAddress f.FetchedBlocks = append(f.FetchedBlocks, blockNumber) accumulator := big.NewInt(1) - f.supply.Add(&f.supply, accumulator) - return f.supply, nil + if method == "totalSupply" { + f.supply.Add(&f.supply, accumulator) + + return f.supply, nil + } + + if method == "balanceOf" { + rfl := reflect.ValueOf(methodArgs).Field(0) + tokenHolderAddr := rfl.Interface().(string) + pnt := f.balance[tokenHolderAddr] + f.balance[tokenHolderAddr].Add(pnt, accumulator) + + return *f.balance[tokenHolderAddr], nil + } + + if method == "allowance" { + rfl1 := reflect.ValueOf(methodArgs).Field(0) + rfl2 := reflect.ValueOf(methodArgs).Field(1) + tokenHolderAddr := rfl1.Interface().(string) + spenderAddr := rfl2.Interface().(string) + pnt := f.allowance[tokenHolderAddr][spenderAddr] + f.allowance[tokenHolderAddr][spenderAddr].Add(pnt, accumulator) + + return *f.allowance[tokenHolderAddr][spenderAddr], nil + } + + return *big.NewInt(0), errors.New("invalid method argument") + } -type TotalSupplyRepository struct { - TotalSuppliesCreated []every_block.TokenSupply - MissingBlockNumbers []int64 - StartingBlock int64 - EndingBlock int64 +func (f *Fetcher) FetchBool(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (bool, error) { + //TODO: this + return false, nil } -func (fr *TotalSupplyRepository) Create(supply every_block.TokenSupply) error { +func (f *Fetcher) FetchAddress(method, contractAbi, contractAddress string, blockNumber int64, methodArgs []interface{}) (common.Address, error) { + //TODO: this + var adr common.Address + return adr, nil +} + +type ERC20TokenRepository struct { + TotalSuppliesCreated []every_block.TokenSupply + MissingSupplyBlockNumbers []int64 + TotalBalancesCreated []every_block.TokenBalance + MissingBalanceBlockNumbers []int64 + TotalAllowancesCreated []every_block.TokenAllowance + MissingAllowanceBlockNumbers []int64 + StartingBlock int64 + EndingBlock int64 +} + +func (fr *ERC20TokenRepository) CreateSupply(supply every_block.TokenSupply) error { fr.TotalSuppliesCreated = append(fr.TotalSuppliesCreated, supply) return nil } -func (fr *TotalSupplyRepository) MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) { - fr.StartingBlock = startingBlock - fr.EndingBlock = highestBlock - return fr.MissingBlockNumbers, nil +func (fr *ERC20TokenRepository) CreateBalance(balance every_block.TokenBalance) error { + fr.TotalBalancesCreated = append(fr.TotalBalancesCreated, balance) + return nil } -func (fr *TotalSupplyRepository) SetMissingBlocks(missingBlocks []int64) { - fr.MissingBlockNumbers = missingBlocks +func (fr *ERC20TokenRepository) CreateAllowance(allowance every_block.TokenAllowance) error { + fr.TotalAllowancesCreated = append(fr.TotalAllowancesCreated, allowance) + return nil +} + +func (fr *ERC20TokenRepository) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) { + fr.StartingBlock = startingBlock + fr.EndingBlock = highestBlock + return fr.MissingSupplyBlockNumbers, nil +} + +func (fr *ERC20TokenRepository) MissingBalanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress string) ([]int64, error) { + fr.StartingBlock = startingBlock + fr.EndingBlock = highestBlock + return fr.MissingBalanceBlockNumbers, nil +} + +func (fr *ERC20TokenRepository) MissingAllowanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress, spenderAddress string) ([]int64, error) { + fr.StartingBlock = startingBlock + fr.EndingBlock = highestBlock + return fr.MissingAllowanceBlockNumbers, nil +} + +func (fr *ERC20TokenRepository) SetMissingSupplyBlocks(missingBlocks []int64) { + fr.MissingSupplyBlockNumbers = missingBlocks +} + +func (fr *ERC20TokenRepository) SetMissingBalanceBlocks(missingBlocks []int64) { + fr.MissingBalanceBlockNumbers = missingBlocks +} + +func (fr *ERC20TokenRepository) SetMissingAllowanceBlocks(missingBlocks []int64) { + fr.MissingAllowanceBlockNumbers = missingBlocks } type FailureRepository struct { - createFail bool - missingBlocksFail bool - missingBlocksNumbers []int64 + createSupplyFail bool + createBalanceFail bool + createAllowanceFail bool + missingSupplyBlocksFail bool + missingBalanceBlocksFail bool + missingAllowanceBlocksFail bool + missingSupplyBlocksNumbers []int64 + missingBalanceBlocksNumbers []int64 + missingAllowanceBlocksNumbers []int64 } -func (fr *FailureRepository) Create(supply every_block.TokenSupply) error { - if fr.createFail { +func (fr *FailureRepository) CreateSupply(supply every_block.TokenSupply) error { + if fr.createSupplyFail { return fakes.FakeError } else { return nil } } -func (fr *FailureRepository) MissingBlocks(startingBlock int64, highestBlock int64) ([]int64, error) { - if fr.missingBlocksFail { - return []int64{}, fakes.FakeError +func (fr *FailureRepository) CreateBalance(balance every_block.TokenBalance) error { + if fr.createBalanceFail { + return fakes.FakeError } else { - return fr.missingBlocksNumbers, nil + return nil } } -func (fr *FailureRepository) SetCreateFail(fail bool) { - fr.createFail = fail +func (fr *FailureRepository) CreateAllowance(allowance every_block.TokenAllowance) error { + if fr.createAllowanceFail { + return fakes.FakeError + } else { + return nil + } } -func (fr *FailureRepository) SetMissingBlocksFail(fail bool) { - fr.missingBlocksFail = fail +func (fr *FailureRepository) MissingSupplyBlocks(startingBlock, highestBlock int64, tokenAddress string) ([]int64, error) { + if fr.missingSupplyBlocksFail { + return []int64{}, fakes.FakeError + } else { + return fr.missingSupplyBlocksNumbers, nil + } } -func (fr *FailureRepository) SetMissingBlocks(missingBlocks []int64) { - fr.missingBlocksNumbers = missingBlocks +func (fr *FailureRepository) MissingBalanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress string) ([]int64, error) { + if fr.missingBalanceBlocksFail { + return []int64{}, fakes.FakeError + } else { + return fr.missingBalanceBlocksNumbers, nil + } +} + +func (fr *FailureRepository) MissingAllowanceBlocks(startingBlock, highestBlock int64, tokenAddress, holderAddress, spenderAddress string) ([]int64, error) { + if fr.missingAllowanceBlocksFail { + return []int64{}, fakes.FakeError + } else { + return fr.missingAllowanceBlocksNumbers, nil + } +} + +func (fr *FailureRepository) SetCreateSupplyFail(fail bool) { + fr.createSupplyFail = fail +} + +func (fr *FailureRepository) SetCreateBalanceFail(fail bool) { + fr.createBalanceFail = fail +} + +func (fr *FailureRepository) SetCreateAllowanceFail(fail bool) { + fr.createAllowanceFail = fail +} + +func (fr *FailureRepository) SetMissingSupplyBlocksFail(fail bool) { + fr.missingSupplyBlocksFail = fail +} + +func (fr *FailureRepository) SetMissingBalanceBlocksFail(fail bool) { + fr.missingBalanceBlocksFail = fail +} + +func (fr *FailureRepository) SetMissingAllowanceBlocksFail(fail bool) { + fr.missingAllowanceBlocksFail = fail +} + +func (fr *FailureRepository) SetMissingSupplyBlocks(missingBlocks []int64) { + fr.missingSupplyBlocksNumbers = missingBlocks +} + +func (fr *FailureRepository) SetMissingBalanceBlocks(missingBlocks []int64) { + fr.missingBalanceBlocksNumbers = missingBlocks +} + +func (fr *FailureRepository) SetMissingAllowanceBlocks(missingBlocks []int64) { + fr.missingAllowanceBlocksNumbers = missingBlocks } diff --git a/integration_test/contract_test.go b/integration_test/contract_test.go index ad204edb..275e4769 100644 --- a/integration_test/contract_test.go +++ b/integration_test/contract_test.go @@ -80,8 +80,15 @@ var _ = Describe("Reading contracts", func() { contract := testing.SampleContract() var balance = new(big.Int) - args := common.HexToHash("0xd26114cd6ee289accf82350c8d8487fedb8a0c07") - err = blockChain.FetchContractData(contract.Abi, "0xd26114cd6ee289accf82350c8d8487fedb8a0c07", "balanceOf", args, &balance, 5167471) + + arg := common.HexToHash("0xd26114cd6ee289accf82350c8d8487fedb8a0c07") + hashArgs := []common.Hash{arg} + balanceOfArgs := make([]interface{}, len(hashArgs)) + for i, s := range hashArgs { + balanceOfArgs[i] = s + } + + err = blockChain.FetchContractData(contract.Abi, "0xd26114cd6ee289accf82350c8d8487fedb8a0c07", "balanceOf", balanceOfArgs, &balance, 5167471) Expect(err).NotTo(HaveOccurred()) expected := new(big.Int) expected.SetString("10897295492887612977137", 10) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index fb56e96a..2c1aadab 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -12,5 +12,5 @@ type BlockChain interface { } type ContractDataFetcher interface { - FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error + FetchContractData(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error } diff --git a/pkg/fakes/mock_blockchain.go b/pkg/fakes/mock_blockchain.go index bd543106..b4538c57 100644 --- a/pkg/fakes/mock_blockchain.go +++ b/pkg/fakes/mock_blockchain.go @@ -13,7 +13,7 @@ type MockBlockChain struct { fetchContractDataPassedAbi string fetchContractDataPassedAddress string fetchContractDataPassedMethod string - fetchContractDataPassedMethodArg interface{} + fetchContractDataPassedMethodArgs []interface{} fetchContractDataPassedResult interface{} fetchContractDataPassedBlockNumber int64 getBlockByNumberErr error @@ -43,11 +43,11 @@ func (blockChain *MockBlockChain) GetHeaderByNumber(blockNumber int64) (core.Hea return core.Header{BlockNumber: blockNumber}, nil } -func (blockChain *MockBlockChain) FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error { +func (blockChain *MockBlockChain) FetchContractData(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error { blockChain.fetchContractDataPassedAbi = abiJSON blockChain.fetchContractDataPassedAddress = address blockChain.fetchContractDataPassedMethod = method - blockChain.fetchContractDataPassedMethodArg = methodArg + blockChain.fetchContractDataPassedMethodArgs = methodArgs blockChain.fetchContractDataPassedResult = result blockChain.fetchContractDataPassedBlockNumber = blockNumber return blockChain.fetchContractDataErr @@ -74,7 +74,7 @@ func (blockChain *MockBlockChain) GetBlockByNumber(blockNumber int64) (core.Bloc } // TODO: handle methodArg being nil (can't match nil to nil in Gomega) -func (blockChain *MockBlockChain) AssertFetchContractDataCalledWith(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) { +func (blockChain *MockBlockChain) AssertFetchContractDataCalledWith(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) { Expect(blockChain.fetchContractDataPassedAbi).To(Equal(abiJSON)) Expect(blockChain.fetchContractDataPassedAddress).To(Equal(address)) Expect(blockChain.fetchContractDataPassedMethod).To(Equal(method)) diff --git a/pkg/geth/contract.go b/pkg/geth/contract.go index f91b7ee4..a692f410 100644 --- a/pkg/geth/contract.go +++ b/pkg/geth/contract.go @@ -14,14 +14,14 @@ var ( ErrInvalidStateAttribute = errors.New("invalid state attribute") ) -func (blockChain *BlockChain) FetchContractData(abiJSON string, address string, method string, methodArg interface{}, result interface{}, blockNumber int64) error { +func (blockChain *BlockChain) FetchContractData(abiJSON string, address string, method string, methodArgs []interface{}, result interface{}, blockNumber int64) error { parsed, err := ParseAbi(abiJSON) if err != nil { return err } var input []byte - if methodArg != nil { - input, err = parsed.Pack(method, methodArg) + if methodArgs != nil { + input, err = parsed.Pack(method, methodArgs...) } else { input, err = parsed.Pack(method) }