Combine price feed transformers

- fetches logs from all three price feeds in one query
- assumes eth/usd price feed will be updated to include LogValue event
- updates transformers to run separate from header sync
This commit is contained in:
Rob Mulholand 2018-08-14 16:59:41 -05:00
parent 9231d40369
commit 634604d0b5
68 changed files with 902 additions and 1384 deletions

View File

@ -29,8 +29,7 @@ Vulcanize DB is a set of tools that make it easier for developers to write appli
## Configuration ## Configuration
- To use a local Ethereum node, copy `environments/public.toml.example` to - To use a local Ethereum node, copy `environments/public.toml.example` to
`environments/public.toml` and update the `ipcPath`, `levelDbPath`, `environments/public.toml` and update the `ipcPath` and `levelDbPath`.
`pipContractAddress`, `pepContractAddress`, and `repContractAddress`.
- `ipcPath` should match the local node's IPC filepath: - `ipcPath` should match the local node's IPC filepath:
- when using geth: - when using geth:
- The IPC file is called `geth.ipc`. - The IPC file is called `geth.ipc`.
@ -52,10 +51,7 @@ Vulcanize DB is a set of tools that make it easier for developers to write appli
- Linux: `$HOME/.ethereum/geth/chaindata` - Linux: `$HOME/.ethereum/geth/chaindata`
- `levelDbPath` is irrelevant (and `coldImport` is currently unavailable) if only running parity. - `levelDbPath` is irrelevant (and `coldImport` is currently unavailable) if only running parity.
- `pepContractAddress`, `pipContractAddress`, and `repContractAddress` should match that medianizer - See `environments/infura.toml` to configure commands to run against infura, if a local node is unavailable.
addresses for each pair on the chain you're tracking. See https://makerdao.com/feeds/
- See `environments/infura.toml` to configure commands to run against infura, if a local node is unavailable
- Copy `environments/local.toml.example` to `environments/local.toml` to configure commands to run against a local node such as [Ganache](https://truffleframework.com/ganache) or [ganache-cli](https://github.com/trufflesuite/ganache-clihttps://github.com/trufflesuite/ganache-cli). - Copy `environments/local.toml.example` to `environments/local.toml` to configure commands to run against a local node such as [Ganache](https://truffleframework.com/ganache) or [ganache-cli](https://github.com/trufflesuite/ganache-clihttps://github.com/trufflesuite/ganache-cli).
## Start syncing with postgres ## Start syncing with postgres
@ -82,22 +78,17 @@ This command is useful when you want a minimal baseline from which to track targ
1. In a separate terminal start VulcanizeDB: 1. In a separate terminal start VulcanizeDB:
- `./vulcanizedb lightSync --config <config.toml> --starting-block-number <block-number>` - `./vulcanizedb lightSync --config <config.toml> --starting-block-number <block-number>`
## Backfill Auction event logs from light sync ## Backfill Maker event logs from light sync
Backfills auction event logs from the configured Ethereum node based on the populated block headers. Backfills Maker event logs from the configured Ethereum node based on the populated block headers.
This includes logs related to auctions, multi-collateral dai, and price feeds.
This command requires that a light sync (see command above) has previously been run. This command requires that a light sync (see command above) has previously been run.
_Since auction contracts have not yet been deployed, this command will need to be run a local blockchain at the moment. As such, a new environment file will need to be added. See `environments/local.toml.example`._ _Since auction/mcd contracts have not yet been deployed, this command will need to be run a local blockchain at the moment. As such, a new environment file will need to be added. See `environments/local.toml.example`._
1. Start Ethereum node 1. Start Ethereum node
1. In a separate terminal run the backfill command: 1. In a separate terminal run the backfill command:
- `./vulcanizedb backfillAuctionLogs --config <config.toml>` - `./vulcanizedb backfillMakerLogs --config <config.toml>`
## Sync in light mode with MakerDAO price feeds
Sync VulcanizeDB with the configured Ethereum node, populating block headers as well as price feeds for MKR/USD, ETH/USD, and REP/USD.
1. Start Ethereum node
1. In a separate terminal window start VulcanizeDB
- `./vulcanizedb syncPriceFeeds --config <config.toml> --starting-block-number <block-number>`
## Start full environment in docker by single command ## Start full environment in docker by single command
### Geth Rinkeby ### Geth Rinkeby

View File

@ -35,7 +35,8 @@ var backfillMakerLogsCmd = &cobra.Command{
Use: "backfillMakerLogs", Use: "backfillMakerLogs",
Short: "Backfill Maker event logs", Short: "Backfill Maker event logs",
Long: `Backfills Maker event logs based on previously populated block Header records. Long: `Backfills Maker event logs based on previously populated block Header records.
This currently includes logs related to Multi-collateral Dai (frob) and Auctions (flip-kick). This currently includes logs related to Multi-collateral Dai (frob), Auctions (flip-kick),
and Price Feeds (ETH/USD, MKR/USD, and REP/USD - LogValue).
vulcanize backfillMakerLogs --config environments/local.toml vulcanize backfillMakerLogs --config environments/local.toml

View File

@ -31,7 +31,6 @@ import (
vRpc "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc" vRpc "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
"github.com/vulcanize/vulcanizedb/pkg/geth/node" "github.com/vulcanize/vulcanizedb/pkg/geth/node"
"github.com/vulcanize/vulcanizedb/pkg/history" "github.com/vulcanize/vulcanizedb/pkg/history"
"github.com/vulcanize/vulcanizedb/pkg/transformers"
"github.com/vulcanize/vulcanizedb/utils" "github.com/vulcanize/vulcanizedb/utils"
) )
@ -65,8 +64,7 @@ func init() {
} }
func backFillAllHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, missingBlocksPopulated chan int, startingBlockNumber int64) { func backFillAllHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
emptyTransformers := []transformers.Transformer{} populated, err := history.PopulateMissingHeaders(blockchain, headerRepository, startingBlockNumber)
populated, err := history.PopulateMissingHeaders(blockchain, headerRepository, startingBlockNumber, emptyTransformers)
if err != nil { if err != nil {
log.Fatal("Error populating headers: ", err) log.Fatal("Error populating headers: ", err)
} }
@ -81,7 +79,7 @@ func lightSync() {
db := utils.LoadPostgres(databaseConfig, blockChain.Node()) db := utils.LoadPostgres(databaseConfig, blockChain.Node())
headerRepository := repositories.NewHeaderRepository(&db) headerRepository := repositories.NewHeaderRepository(&db)
validator := history.NewHeaderValidator(blockChain, headerRepository, validationWindow, []transformers.Transformer{}) validator := history.NewHeaderValidator(blockChain, headerRepository, validationWindow)
missingBlocksPopulated := make(chan int) missingBlocksPopulated := make(chan int)
go backFillAllHeaders(blockChain, headerRepository, missingBlocksPopulated, startingBlockNumber) go backFillAllHeaders(blockChain, headerRepository, missingBlocksPopulated, startingBlockNumber)

View File

@ -29,9 +29,6 @@ var (
databaseConfig config.Database databaseConfig config.Database
ipc string ipc string
levelDbPath string levelDbPath string
pepContractAddress string
pipContractAddress string
repContractAddress string
startingBlockNumber int64 startingBlockNumber int64
syncAll bool syncAll bool
endingBlockNumber int64 endingBlockNumber int64
@ -52,9 +49,6 @@ func Execute() {
func database(cmd *cobra.Command, args []string) { func database(cmd *cobra.Command, args []string) {
ipc = viper.GetString("client.ipcpath") ipc = viper.GetString("client.ipcpath")
levelDbPath = viper.GetString("client.leveldbpath") levelDbPath = viper.GetString("client.leveldbpath")
pepContractAddress = viper.GetString("client.pepcontractaddress")
pipContractAddress = viper.GetString("client.pipcontractaddress")
repContractAddress = viper.GetString("client.repcontractaddress")
databaseConfig = config.Database{ databaseConfig = config.Database{
Name: viper.GetString("database.name"), Name: viper.GetString("database.name"),
Hostname: viper.GetString("database.hostname"), Hostname: viper.GetString("database.hostname"),
@ -76,9 +70,6 @@ func init() {
rootCmd.PersistentFlags().String("database-password", "", "database password") rootCmd.PersistentFlags().String("database-password", "", "database password")
rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file") rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file")
rootCmd.PersistentFlags().String("client-levelDbPath", "", "location of levelDb chaindata") rootCmd.PersistentFlags().String("client-levelDbPath", "", "location of levelDb chaindata")
rootCmd.PersistentFlags().String("client-pepContractAddress", "", "mkr/usd price feed contract address")
rootCmd.PersistentFlags().String("client-pipContractAddress", "", "eth/usd price feed contract address")
rootCmd.PersistentFlags().String("client-repContractAddress", "", "rep/usd price feed contract address")
viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name"))
viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port")) viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port"))
@ -87,9 +78,6 @@ func init() {
viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password")) viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password"))
viper.BindPFlag("client.ipcPath", rootCmd.PersistentFlags().Lookup("client-ipcPath")) viper.BindPFlag("client.ipcPath", rootCmd.PersistentFlags().Lookup("client-ipcPath"))
viper.BindPFlag("client.levelDbPath", rootCmd.PersistentFlags().Lookup("client-levelDbPath")) viper.BindPFlag("client.levelDbPath", rootCmd.PersistentFlags().Lookup("client-levelDbPath"))
viper.BindPFlag("client.pepContractAddress", rootCmd.PersistentFlags().Lookup("client-pepContractAddress"))
viper.BindPFlag("client.pipContractAddress", rootCmd.PersistentFlags().Lookup("client-pipContractAddress"))
viper.BindPFlag("client.repContractAddress", rootCmd.PersistentFlags().Lookup("client-repContractAddress"))
} }
func initConfig() { func initConfig() {

View File

@ -1,88 +0,0 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"log"
"os"
"time"
"github.com/spf13/cobra"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/history"
"github.com/vulcanize/vulcanizedb/pkg/transformers"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pep"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pip"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/rep"
"github.com/vulcanize/vulcanizedb/utils"
)
// syncPriceFeedsCmd represents the syncPriceFeeds command
var syncPriceFeedsCmd = &cobra.Command{
Use: "syncPriceFeeds",
Short: "Sync block headers with price feed data",
Long: `Sync Ethereum block headers and price feed data. For example:
./vulcanizedb syncPriceFeeds --config <config.toml> --starting-block-number <block-number>
Price feed data will be updated when price feed contracts log value events.`,
Run: func(cmd *cobra.Command, args []string) {
syncPriceFeeds()
},
}
func init() {
rootCmd.AddCommand(syncPriceFeedsCmd)
syncPriceFeedsCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "block number at which to start tracking price feeds")
}
func backFillPriceFeeds(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, missingBlocksPopulated chan int, startingBlockNumber int64, transformers []transformers.Transformer) {
populated, err := history.PopulateMissingHeaders(blockchain, headerRepository, startingBlockNumber, transformers)
if err != nil {
log.Fatal("Error populating headers: ", err)
}
missingBlocksPopulated <- populated
}
func syncPriceFeeds() {
ticker := time.NewTicker(pollingInterval)
defer ticker.Stop()
blockChain := getBlockChain()
validateArgs(blockChain)
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
transformers := []transformers.Transformer{
pep.NewPepTransformer(blockChain, &db, pepContractAddress),
pip.NewPipTransformer(blockChain, &db, pipContractAddress),
rep.NewRepTransformer(blockChain, &db, repContractAddress),
}
headerRepository := repositories.NewHeaderRepository(&db)
missingBlocksPopulated := make(chan int)
validator := history.NewHeaderValidator(blockChain, headerRepository, validationWindow, transformers)
go backFillPriceFeeds(blockChain, headerRepository, missingBlocksPopulated, startingBlockNumber, transformers)
for {
select {
case <-ticker.C:
window := validator.ValidateHeaders()
window.Log(os.Stdout)
case <-missingBlocksPopulated:
go backFillPriceFeeds(blockChain, headerRepository, missingBlocksPopulated, startingBlockNumber, transformers)
}
}
}

View File

@ -1,2 +0,0 @@
DROP TABLE maker.peps;
DROP SCHEMA maker;

View File

@ -1,9 +0,0 @@
CREATE TABLE maker.peps (
id SERIAL PRIMARY KEY,
block_number BIGINT NOT NULL,
header_id INTEGER NOT NULL,
usd_value NUMERIC,
CONSTRAINT headers_fk FOREIGN KEY (header_id)
REFERENCES headers (id)
ON DELETE CASCADE
);

View File

@ -1 +0,0 @@
DROP TABLE maker.pips;

View File

@ -1,9 +0,0 @@
CREATE TABLE maker.pips (
id SERIAL PRIMARY KEY,
block_number BIGINT NOT NULL,
header_id INTEGER NOT NULL,
usd_value NUMERIC,
CONSTRAINT headers_fk FOREIGN KEY (header_id)
REFERENCES headers (id)
ON DELETE CASCADE
);

View File

@ -1 +0,0 @@
DROP TABLE maker.reps;

View File

@ -1,9 +0,0 @@
CREATE TABLE maker.reps (
id SERIAL PRIMARY KEY,
block_number BIGINT NOT NULL,
header_id INTEGER NOT NULL,
usd_value NUMERIC,
CONSTRAINT headers_fk FOREIGN KEY (header_id)
REFERENCES headers (id)
ON DELETE CASCADE
);

View File

@ -0,0 +1 @@
DROP TABLE maker.price_feeds;

View File

@ -0,0 +1,12 @@
CREATE TABLE maker.price_feeds (
id SERIAL PRIMARY KEY,
block_number BIGINT NOT NULL,
header_id INTEGER NOT NULL,
medianizer_address bytea,
tx_idx INTEGER NOT NULL,
usd_value NUMERIC,
UNIQUE (header_id, medianizer_address, tx_idx),
CONSTRAINT headers_fk FOREIGN KEY (header_id)
REFERENCES headers (id)
ON DELETE CASCADE
);

View File

@ -120,22 +120,24 @@ ALTER SEQUENCE maker.frob_id_seq OWNED BY maker.frob.id;
-- --
-- Name: peps; Type: TABLE; Schema: maker; Owner: - -- Name: price_feeds; Type: TABLE; Schema: maker; Owner: -
-- --
CREATE TABLE maker.peps ( CREATE TABLE maker.price_feeds (
id integer NOT NULL, id integer NOT NULL,
block_number bigint NOT NULL, block_number bigint NOT NULL,
header_id integer NOT NULL, header_id integer NOT NULL,
medianizer_address bytea,
tx_idx integer NOT NULL,
usd_value numeric usd_value numeric
); );
-- --
-- Name: peps_id_seq; Type: SEQUENCE; Schema: maker; Owner: - -- Name: price_feeds_id_seq; Type: SEQUENCE; Schema: maker; Owner: -
-- --
CREATE SEQUENCE maker.peps_id_seq CREATE SEQUENCE maker.price_feeds_id_seq
AS integer AS integer
START WITH 1 START WITH 1
INCREMENT BY 1 INCREMENT BY 1
@ -145,74 +147,10 @@ CREATE SEQUENCE maker.peps_id_seq
-- --
-- Name: peps_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: - -- Name: price_feeds_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: -
-- --
ALTER SEQUENCE maker.peps_id_seq OWNED BY maker.peps.id; ALTER SEQUENCE maker.price_feeds_id_seq OWNED BY maker.price_feeds.id;
--
-- Name: pips; Type: TABLE; Schema: maker; Owner: -
--
CREATE TABLE maker.pips (
id integer NOT NULL,
block_number bigint NOT NULL,
header_id integer NOT NULL,
usd_value numeric
);
--
-- Name: pips_id_seq; Type: SEQUENCE; Schema: maker; Owner: -
--
CREATE SEQUENCE maker.pips_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: pips_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: -
--
ALTER SEQUENCE maker.pips_id_seq OWNED BY maker.pips.id;
--
-- Name: reps; Type: TABLE; Schema: maker; Owner: -
--
CREATE TABLE maker.reps (
id integer NOT NULL,
block_number bigint NOT NULL,
header_id integer NOT NULL,
usd_value numeric
);
--
-- Name: reps_id_seq; Type: SEQUENCE; Schema: maker; Owner: -
--
CREATE SEQUENCE maker.reps_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: reps_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: -
--
ALTER SEQUENCE maker.reps_id_seq OWNED BY maker.reps.id;
-- --
@ -602,24 +540,10 @@ ALTER TABLE ONLY maker.frob ALTER COLUMN id SET DEFAULT nextval('maker.frob_id_s
-- --
-- Name: peps id; Type: DEFAULT; Schema: maker; Owner: - -- Name: price_feeds id; Type: DEFAULT; Schema: maker; Owner: -
-- --
ALTER TABLE ONLY maker.peps ALTER COLUMN id SET DEFAULT nextval('maker.peps_id_seq'::regclass); ALTER TABLE ONLY maker.price_feeds ALTER COLUMN id SET DEFAULT nextval('maker.price_feeds_id_seq'::regclass);
--
-- Name: pips id; Type: DEFAULT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.pips ALTER COLUMN id SET DEFAULT nextval('maker.pips_id_seq'::regclass);
--
-- Name: reps id; Type: DEFAULT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.reps ALTER COLUMN id SET DEFAULT nextval('maker.reps_id_seq'::regclass);
-- --
@ -718,27 +642,19 @@ ALTER TABLE ONLY maker.frob
-- --
-- Name: peps peps_pkey; Type: CONSTRAINT; Schema: maker; Owner: - -- Name: price_feeds price_feeds_header_id_medianizer_address_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: -
-- --
ALTER TABLE ONLY maker.peps ALTER TABLE ONLY maker.price_feeds
ADD CONSTRAINT peps_pkey PRIMARY KEY (id); ADD CONSTRAINT price_feeds_header_id_medianizer_address_tx_idx_key UNIQUE (header_id, medianizer_address, tx_idx);
-- --
-- Name: pips pips_pkey; Type: CONSTRAINT; Schema: maker; Owner: - -- Name: price_feeds price_feeds_pkey; Type: CONSTRAINT; Schema: maker; Owner: -
-- --
ALTER TABLE ONLY maker.pips ALTER TABLE ONLY maker.price_feeds
ADD CONSTRAINT pips_pkey PRIMARY KEY (id); ADD CONSTRAINT price_feeds_pkey PRIMARY KEY (id);
--
-- Name: reps reps_pkey; Type: CONSTRAINT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.reps
ADD CONSTRAINT reps_pkey PRIMARY KEY (id);
-- --
@ -889,26 +805,10 @@ ALTER TABLE ONLY maker.frob
-- --
-- Name: peps headers_fk; Type: FK CONSTRAINT; Schema: maker; Owner: - -- Name: price_feeds headers_fk; Type: FK CONSTRAINT; Schema: maker; Owner: -
-- --
ALTER TABLE ONLY maker.peps ALTER TABLE ONLY maker.price_feeds
ADD CONSTRAINT headers_fk FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
--
-- Name: pips headers_fk; Type: FK CONSTRAINT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.pips
ADD CONSTRAINT headers_fk FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
--
-- Name: reps headers_fk; Type: FK CONSTRAINT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.reps
ADD CONSTRAINT headers_fk FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; ADD CONSTRAINT headers_fk FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;

View File

@ -1,10 +1,7 @@
[database] [database]
name = "vulcanize_public" name = "vulcanize_private"
hostname = "localhost" hostname = "localhost"
port = 5432 port = 5432
[client] [client]
ipcPath = "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL" ipcPath = "https://mainnet.infura.io/J5Vd2fRtGsw0zZ0Ov3BL"
pepContractAddress = "0x99041F808D598B782D5a3e498681C2452A31da08"
pipContractAddress = "0x729D19f657BD0614b4985Cf1D82531c67569197B"
repContractAddress = "0xF5f94b7F9De14D43112e713835BCef2d55b76c1C"

View File

@ -5,6 +5,3 @@ port = 5432
[client] [client]
ipcPath = "http://127.0.0.1:7545" ipcPath = "http://127.0.0.1:7545"
pepContractAddress = "0x99041F808D598B782D5a3e498681C2452A31da08"
pipContractAddress = "0x729D19f657BD0614b4985Cf1D82531c67569197B"
repContractAddress = "0xF5f94b7F9De14D43112e713835BCef2d55b76c1C"

View File

@ -6,6 +6,3 @@ port = 5432
[client] [client]
ipcPath = <local node's IPC filepath> ipcPath = <local node's IPC filepath>
levelDbPath = <local node's LevelDB chaindata filepath> levelDbPath = <local node's LevelDB chaindata filepath>
pepContractAddress = <pep medianizer's address - see https://makerdao.com/feeds/ >
pipContractAddress = <pip medianizer's address - see https://makerdao.com/feeds/ >
repContractAddress = <rep medianizer's address - see https://makerdao.com/feeds/ >

View File

@ -3,6 +3,7 @@ package repositories
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
) )

View File

@ -1,44 +0,0 @@
package fakes
import (
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
)
type MockTransformer struct {
passedHeader core.Header
passedHeaderID int64
executeCalled bool
executeErr error
}
func NewMockTransformer() *MockTransformer {
return &MockTransformer{
passedHeader: core.Header{},
passedHeaderID: 0,
executeCalled: false,
executeErr: nil,
}
}
func (transformer *MockTransformer) SetExecuteErr(err error) {
transformer.executeErr = err
}
func (transformer *MockTransformer) Execute(header core.Header, headerID int64) error {
transformer.executeCalled = true
transformer.passedHeader = header
transformer.passedHeaderID = headerID
return transformer.executeErr
}
func (transformer *MockTransformer) AssertExecuteCalledWith(header core.Header, headerID int64) {
Expect(transformer.executeCalled).To(BeTrue())
Expect(header).To(Equal(transformer.passedHeader))
Expect(headerID).To(Equal(transformer.passedHeaderID))
}
func (tranformer *MockTransformer) AssertExecuteNotCalled() {
Expect(tranformer.executeCalled).To(BeFalse())
}

View File

@ -3,28 +3,25 @@ package history
import ( import (
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore" "github.com/vulcanize/vulcanizedb/pkg/datastore"
"github.com/vulcanize/vulcanizedb/pkg/transformers"
) )
type HeaderValidator struct { type HeaderValidator struct {
blockChain core.BlockChain blockChain core.BlockChain
headerRepository datastore.HeaderRepository headerRepository datastore.HeaderRepository
windowSize int windowSize int
transformers []transformers.Transformer
} }
func NewHeaderValidator(blockChain core.BlockChain, repository datastore.HeaderRepository, windowSize int, transformers []transformers.Transformer) HeaderValidator { func NewHeaderValidator(blockChain core.BlockChain, repository datastore.HeaderRepository, windowSize int) HeaderValidator {
return HeaderValidator{ return HeaderValidator{
blockChain: blockChain, blockChain: blockChain,
headerRepository: repository, headerRepository: repository,
windowSize: windowSize, windowSize: windowSize,
transformers: transformers,
} }
} }
func (validator HeaderValidator) ValidateHeaders() ValidationWindow { func (validator HeaderValidator) ValidateHeaders() ValidationWindow {
window := MakeValidationWindow(validator.blockChain, validator.windowSize) window := MakeValidationWindow(validator.blockChain, validator.windowSize)
blockNumbers := MakeRange(window.LowerBound, window.UpperBound) blockNumbers := MakeRange(window.LowerBound, window.UpperBound)
RetrieveAndUpdateHeaders(validator.blockChain, validator.headerRepository, blockNumbers, validator.transformers) RetrieveAndUpdateHeaders(validator.blockChain, validator.headerRepository, blockNumbers)
return window return window
} }

View File

@ -2,10 +2,8 @@ package history_test
import ( import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes" "github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/history" "github.com/vulcanize/vulcanizedb/pkg/history"
"github.com/vulcanize/vulcanizedb/pkg/transformers"
"math/big" "math/big"
) )
@ -15,24 +13,10 @@ var _ = Describe("Header validator", func() {
headerRepository.SetMissingBlockNumbers([]int64{}) headerRepository.SetMissingBlockNumbers([]int64{})
blockChain := fakes.NewMockBlockChain() blockChain := fakes.NewMockBlockChain()
blockChain.SetLastBlock(big.NewInt(3)) blockChain.SetLastBlock(big.NewInt(3))
validator := history.NewHeaderValidator(blockChain, headerRepository, 2, []transformers.Transformer{}) validator := history.NewHeaderValidator(blockChain, headerRepository, 2)
validator.ValidateHeaders() validator.ValidateHeaders()
headerRepository.AssertCreateOrUpdateHeaderCallCountAndPassedBlockNumbers(3, []int64{1, 2, 3}) headerRepository.AssertCreateOrUpdateHeaderCallCountAndPassedBlockNumbers(3, []int64{1, 2, 3})
}) })
It("passes transformers for execution on new blocks", func() {
headerRepository := fakes.NewMockHeaderRepository()
headerRepository.SetMissingBlockNumbers([]int64{})
blockChain := fakes.NewMockBlockChain()
blockChain.SetLastBlock(big.NewInt(3))
transformer := fakes.NewMockTransformer()
validator := history.NewHeaderValidator(blockChain, headerRepository, 1, []transformers.Transformer{transformer})
validator.ValidateHeaders()
headerRepository.AssertCreateOrUpdateHeaderCallCountAndPassedBlockNumbers(2, []int64{2, 3})
transformer.AssertExecuteCalledWith(core.Header{BlockNumber: 3}, 0)
})
}) })

View File

@ -6,40 +6,33 @@ import (
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore" "github.com/vulcanize/vulcanizedb/pkg/datastore"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers"
) )
func PopulateMissingHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, startingBlockNumber int64, transformers []transformers.Transformer) (int, error) { func PopulateMissingHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, startingBlockNumber int64) (int, error) {
lastBlock := blockchain.LastBlock().Int64() lastBlock := blockchain.LastBlock().Int64()
blockRange := headerRepository.MissingBlockNumbers(startingBlockNumber, lastBlock, blockchain.Node().ID) blockRange := headerRepository.MissingBlockNumbers(startingBlockNumber, lastBlock, blockchain.Node().ID)
log.SetPrefix("") log.SetPrefix("")
log.Printf("Backfilling %d blocks\n\n", len(blockRange)) log.Printf("Backfilling %d blocks\n\n", len(blockRange))
_, err := RetrieveAndUpdateHeaders(blockchain, headerRepository, blockRange, transformers) _, err := RetrieveAndUpdateHeaders(blockchain, headerRepository, blockRange)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return len(blockRange), nil return len(blockRange), nil
} }
func RetrieveAndUpdateHeaders(chain core.BlockChain, headerRepository datastore.HeaderRepository, blockNumbers []int64, transformers []transformers.Transformer) (int, error) { func RetrieveAndUpdateHeaders(chain core.BlockChain, headerRepository datastore.HeaderRepository, blockNumbers []int64) (int, error) {
for _, blockNumber := range blockNumbers { for _, blockNumber := range blockNumbers {
header, err := chain.GetHeaderByNumber(blockNumber) header, err := chain.GetHeaderByNumber(blockNumber)
if err != nil { if err != nil {
return 0, err return 0, err
} }
id, err := headerRepository.CreateOrUpdateHeader(header) _, err = headerRepository.CreateOrUpdateHeader(header)
if err != nil { if err != nil {
if err == repositories.ErrValidHeaderExists { if err == repositories.ErrValidHeaderExists {
continue continue
} }
return 0, err return 0, err
} }
for _, transformer := range transformers {
err := transformer.Execute(header, id)
if err != nil {
return 0, err
}
}
} }
return len(blockNumbers), nil return len(blockNumbers), nil
} }

View File

@ -6,11 +6,8 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/fakes" "github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/history" "github.com/vulcanize/vulcanizedb/pkg/history"
"github.com/vulcanize/vulcanizedb/pkg/transformers"
) )
var _ = Describe("Populating headers", func() { var _ = Describe("Populating headers", func() {
@ -26,7 +23,7 @@ var _ = Describe("Populating headers", func() {
blockChain.SetLastBlock(big.NewInt(2)) blockChain.SetLastBlock(big.NewInt(2))
headerRepository.SetMissingBlockNumbers([]int64{2}) headerRepository.SetMissingBlockNumbers([]int64{2})
headersAdded, err := history.PopulateMissingHeaders(blockChain, headerRepository, 1, []transformers.Transformer{}) headersAdded, err := history.PopulateMissingHeaders(blockChain, headerRepository, 1)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(headersAdded).To(Equal(1)) Expect(headersAdded).To(Equal(1))
@ -37,54 +34,9 @@ var _ = Describe("Populating headers", func() {
blockChain.SetLastBlock(big.NewInt(2)) blockChain.SetLastBlock(big.NewInt(2))
headerRepository.SetMissingBlockNumbers([]int64{2}) headerRepository.SetMissingBlockNumbers([]int64{2})
_, err := history.PopulateMissingHeaders(blockChain, headerRepository, 1, []transformers.Transformer{}) _, err := history.PopulateMissingHeaders(blockChain, headerRepository, 1)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
headerRepository.AssertCreateOrUpdateHeaderCallCountAndPassedBlockNumbers(1, []int64{2}) headerRepository.AssertCreateOrUpdateHeaderCallCountAndPassedBlockNumbers(1, []int64{2})
}) })
It("executes passed transformers with created headers", func() {
blockNumber := int64(54321)
blockChain := fakes.NewMockBlockChain()
blockChain.SetLastBlock(big.NewInt(blockNumber))
headerRepository.SetMissingBlockNumbers([]int64{blockNumber})
headerID := int64(12345)
headerRepository.SetCreateOrUpdateHeaderReturnID(headerID)
transformer := fakes.NewMockTransformer()
_, err := history.PopulateMissingHeaders(blockChain, headerRepository, blockNumber, []transformers.Transformer{transformer})
Expect(err).NotTo(HaveOccurred())
transformer.AssertExecuteCalledWith(core.Header{BlockNumber: blockNumber}, headerID)
})
It("does not execute transformer if repository indicates header already exists", func() {
blockNumber := int64(54321)
blockChain := fakes.NewMockBlockChain()
blockChain.SetLastBlock(big.NewInt(blockNumber))
headerRepository.SetMissingBlockNumbers([]int64{blockNumber})
headerRepository.SetCreateOrUpdateHeaderReturnErr(repositories.ErrValidHeaderExists)
transformer := fakes.NewMockTransformer()
_, err := history.PopulateMissingHeaders(blockChain, headerRepository, blockNumber, []transformers.Transformer{transformer})
Expect(err).NotTo(HaveOccurred())
transformer.AssertExecuteNotCalled()
})
It("returns error if executing transformer fails", func() {
blockNumber := int64(54321)
blockChain := fakes.NewMockBlockChain()
blockChain.SetLastBlock(big.NewInt(blockNumber))
headerRepository.SetMissingBlockNumbers([]int64{blockNumber})
headerID := int64(12345)
headerRepository.SetCreateOrUpdateHeaderReturnID(headerID)
transformer := fakes.NewMockTransformer()
transformer.SetExecuteErr(fakes.FakeError)
_, err := history.PopulateMissingHeaders(blockChain, headerRepository, blockNumber, []transformers.Transformer{transformer})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
}) })

View File

@ -14,16 +14,10 @@
package flip_kick package flip_kick
type TransformerConfig struct { import "github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
ContractAddress string
ContractAbi string
Topics []string
StartingBlockNumber int64
EndingBlockNumber int64
}
var FlipKickConfig = TransformerConfig{ var FlipKickConfig = shared.TransformerConfig{
ContractAddress: "0x08cb6176addcca2e1d1ffe21bee464b72ee4cd8d", //this is a temporary address deployed locally ContractAddresses: "0x08cb6176addcca2e1d1ffe21bee464b72ee4cd8d", //this is a temporary address deployed locally
ContractAbi: FlipperABI, ContractAbi: FlipperABI,
Topics: []string{FlipKickSignature}, Topics: []string{FlipKickSignature},
StartingBlockNumber: 0, StartingBlockNumber: 0,

View File

@ -30,11 +30,11 @@ type FlipKickTransformer struct {
Fetcher shared.LogFetcher Fetcher shared.LogFetcher
Converter Converter Converter Converter
Repository Repository Repository Repository
Config TransformerConfig Config shared.TransformerConfig
} }
type FlipKickTransformerInitializer struct { type FlipKickTransformerInitializer struct {
Config TransformerConfig Config shared.TransformerConfig
} }
func (i FlipKickTransformerInitializer) NewFlipKickTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { func (i FlipKickTransformerInitializer) NewFlipKickTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer {
@ -50,7 +50,7 @@ func (i FlipKickTransformerInitializer) NewFlipKickTransformer(db *postgres.DB,
return transformer return transformer
} }
func (fkt *FlipKickTransformer) SetConfig(config TransformerConfig) { func (fkt *FlipKickTransformer) SetConfig(config shared.TransformerConfig) {
fkt.Config = config fkt.Config = config
} }
@ -91,13 +91,13 @@ func (fkt FlipKickTransformer) Execute() error {
log.Printf("Fetching event logs for %d headers \n", len(headers)) log.Printf("Fetching event logs for %d headers \n", len(headers))
var resultingErrors []error var resultingErrors []error
for _, header := range headers { for _, header := range headers {
ethLogs, err := fkt.Fetcher.FetchLogs(config.ContractAddress, topics, header.BlockNumber) ethLogs, err := fkt.Fetcher.FetchLogs(config.ContractAddresses, topics, header.BlockNumber)
if err != nil { if err != nil {
resultingErrors = append(resultingErrors, newTransformerError(err, header.BlockNumber, FetcherError)) resultingErrors = append(resultingErrors, newTransformerError(err, header.BlockNumber, FetcherError))
} }
for _, ethLog := range ethLogs { for _, ethLog := range ethLogs {
entity, err := fkt.Converter.ToEntity(config.ContractAddress, config.ContractAbi, ethLog) entity, err := fkt.Converter.ToEntity(config.ContractAddresses, config.ContractAbi, ethLog)
if err != nil { if err != nil {
resultingErrors = append(resultingErrors, newTransformerError(err, header.BlockNumber, LogToEntityError)) resultingErrors = append(resultingErrors, newTransformerError(err, header.BlockNumber, LogToEntityError))
} }

View File

@ -25,6 +25,7 @@ import (
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes" "github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick" "github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data" "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks" "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks"
flip_kick_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/flip_kick" flip_kick_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/flip_kick"
@ -35,7 +36,7 @@ var _ = Describe("FlipKick Transformer", func() {
var fetcher mocks.MockLogFetcher var fetcher mocks.MockLogFetcher
var converter flip_kick_mocks.MockFlipKickConverter var converter flip_kick_mocks.MockFlipKickConverter
var repository flip_kick_mocks.MockFlipKickRepository var repository flip_kick_mocks.MockFlipKickRepository
var testConfig flip_kick.TransformerConfig var testConfig shared.TransformerConfig
var blockNumber int64 var blockNumber int64
var headerId int64 var headerId int64
var headers []core.Header var headers []core.Header
@ -52,8 +53,8 @@ var _ = Describe("FlipKick Transformer", func() {
} }
startingBlockNumber := rand.Int63() startingBlockNumber := rand.Int63()
testConfig = flip_kick.TransformerConfig{ testConfig = shared.TransformerConfig{
ContractAddress: "0x12345", ContractAddresses: "0x12345",
ContractAbi: "test abi", ContractAbi: "test abi",
Topics: []string{flip_kick.FlipKickSignature}, Topics: []string{flip_kick.FlipKickSignature},
StartingBlockNumber: startingBlockNumber, StartingBlockNumber: startingBlockNumber,
@ -82,7 +83,7 @@ var _ = Describe("FlipKick Transformer", func() {
err := transformer.Execute() err := transformer.Execute()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(fetcher.FetchedContractAddress).To(Equal(testConfig.ContractAddress)) Expect(fetcher.FetchedContractAddress).To(Equal(testConfig.ContractAddresses))
Expect(fetcher.FetchedTopics).To(Equal(expectedTopics)) Expect(fetcher.FetchedTopics).To(Equal(expectedTopics))
Expect(fetcher.FetchedBlocks).To(Equal([]int64{blockNumber})) Expect(fetcher.FetchedBlocks).To(Equal([]int64{blockNumber}))
}) })
@ -99,7 +100,7 @@ var _ = Describe("FlipKick Transformer", func() {
err := transformer.Execute() err := transformer.Execute()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(converter.ConverterContract).To(Equal(testConfig.ContractAddress)) Expect(converter.ConverterContract).To(Equal(testConfig.ContractAddresses))
Expect(converter.ConverterAbi).To(Equal(testConfig.ContractAbi)) Expect(converter.ConverterAbi).To(Equal(testConfig.ContractAbi))
Expect(converter.LogsToConvert).To(Equal(logs)) Expect(converter.LogsToConvert).To(Equal(logs))
Expect(converter.EntitiesToConvert).To(Equal([]flip_kick.FlipKickEntity{test_data.FlipKickEntity})) Expect(converter.EntitiesToConvert).To(Equal([]flip_kick.FlipKickEntity{test_data.FlipKickEntity}))

View File

@ -14,16 +14,10 @@
package frob package frob
type TransformerConfig struct { import "github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
ContractAddress string
ContractAbi string
Topics []string
StartingBlockNumber int64
EndingBlockNumber int64
}
var FrobConfig = TransformerConfig{ var FrobConfig = shared.TransformerConfig{
ContractAddress: "0xff3f2400f1600f3f493a9a92704a29b96795af1a", //this is a temporary address deployed locally ContractAddresses: "0xff3f2400f1600f3f493a9a92704a29b96795af1a", //this is a temporary address deployed locally
ContractAbi: FrobABI, ContractAbi: FrobABI,
Topics: []string{FrobEventSignature}, Topics: []string{FrobEventSignature},
StartingBlockNumber: 0, StartingBlockNumber: 0,

View File

@ -23,13 +23,14 @@ import (
) )
type FrobTransformer struct { type FrobTransformer struct {
Config shared.TransformerConfig
Converter Converter Converter Converter
Fetcher shared.LogFetcher Fetcher shared.LogFetcher
Repository Repository Repository Repository
} }
type FrobTransformerInitializer struct { type FrobTransformerInitializer struct {
Config TransformerConfig Config shared.TransformerConfig
} }
func (initializer FrobTransformerInitializer) NewFrobTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer { func (initializer FrobTransformerInitializer) NewFrobTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer {
@ -37,6 +38,7 @@ func (initializer FrobTransformerInitializer) NewFrobTransformer(db *postgres.DB
fetcher := shared.NewFetcher(blockChain) fetcher := shared.NewFetcher(blockChain)
repository := NewFrobRepository(db) repository := NewFrobRepository(db)
return FrobTransformer{ return FrobTransformer{
Config: initializer.Config,
Converter: converter, Converter: converter,
Fetcher: fetcher, Fetcher: fetcher,
Repository: repository, Repository: repository,
@ -44,18 +46,18 @@ func (initializer FrobTransformerInitializer) NewFrobTransformer(db *postgres.DB
} }
func (transformer FrobTransformer) Execute() error { func (transformer FrobTransformer) Execute() error {
missingHeaders, err := transformer.Repository.MissingHeaders(FrobConfig.StartingBlockNumber, FrobConfig.EndingBlockNumber) missingHeaders, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber)
if err != nil { if err != nil {
return err return err
} }
for _, header := range missingHeaders { for _, header := range missingHeaders {
topics := [][]common.Hash{{common.HexToHash(FrobEventSignature)}} topics := [][]common.Hash{{common.HexToHash(FrobEventSignature)}}
matchingLogs, err := transformer.Fetcher.FetchLogs(FrobConfig.ContractAddress, topics, header.BlockNumber) matchingLogs, err := transformer.Fetcher.FetchLogs(FrobConfig.ContractAddresses, topics, header.BlockNumber)
if err != nil { if err != nil {
return err return err
} }
for _, log := range matchingLogs { for _, log := range matchingLogs {
entity, err := transformer.Converter.ToEntity(FrobConfig.ContractAddress, FrobConfig.ContractAbi, log) entity, err := transformer.Converter.ToEntity(FrobConfig.ContractAddresses, FrobConfig.ContractAbi, log)
if err != nil { if err != nil {
return err return err
} }

View File

@ -32,6 +32,7 @@ var _ = Describe("Frob transformer", func() {
It("gets missing headers for block numbers specified in config", func() { It("gets missing headers for block numbers specified in config", func() {
repository := &frob_mocks.MockFrobRepository{} repository := &frob_mocks.MockFrobRepository{}
transformer := frob.FrobTransformer{ transformer := frob.FrobTransformer{
Config: frob.FrobConfig,
Fetcher: &mocks.MockLogFetcher{}, Fetcher: &mocks.MockLogFetcher{},
Converter: &frob_mocks.MockFrobConverter{}, Converter: &frob_mocks.MockFrobConverter{},
Repository: repository, Repository: repository,
@ -73,7 +74,7 @@ var _ = Describe("Frob transformer", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(fetcher.FetchedBlocks).To(Equal([]int64{1, 2})) Expect(fetcher.FetchedBlocks).To(Equal([]int64{1, 2}))
Expect(fetcher.FetchedContractAddress).To(Equal(frob.FrobConfig.ContractAddress)) Expect(fetcher.FetchedContractAddress).To(Equal(frob.FrobConfig.ContractAddresses))
Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(frob.FrobEventSignature)}})) Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(frob.FrobEventSignature)}}))
}) })
@ -109,7 +110,7 @@ var _ = Describe("Frob transformer", func() {
err := transformer.Execute() err := transformer.Execute()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(converter.PassedContractAddress).To(Equal(frob.FrobConfig.ContractAddress)) Expect(converter.PassedContractAddress).To(Equal(frob.FrobConfig.ContractAddresses))
Expect(converter.PassedContractABI).To(Equal(frob.FrobConfig.ContractAbi)) Expect(converter.PassedContractABI).To(Equal(frob.FrobConfig.ContractAbi))
Expect(converter.PassedLog).To(Equal(test_data.EthFrobLog)) Expect(converter.PassedLog).To(Equal(test_data.EthFrobLog))
Expect(converter.PassedEntity).To(Equal(test_data.FrobEntity)) Expect(converter.PassedEntity).To(Equal(test_data.FrobEntity))

View File

@ -0,0 +1,37 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds
var (
PepAddress = "0x99041F808D598B782D5a3e498681C2452A31da08"
PipAddress = "0x729D19f657BD0614b4985Cf1D82531c67569197B"
RepAddress = "0xF5f94b7F9De14D43112e713835BCef2d55b76c1C"
)
type IPriceFeedConfig struct {
ContractAddresses []string
StartingBlockNumber int64
EndingBlockNumber int64
}
var PriceFeedConfig = IPriceFeedConfig{
ContractAddresses: []string{
PepAddress,
PipAddress,
RepAddress,
},
StartingBlockNumber: 0,
EndingBlockNumber: 100,
}

View File

@ -1,3 +1,17 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds package price_feeds
import ( import (
@ -6,13 +20,9 @@ import (
) )
var ( var (
ErrMultipleLogs = errors.New("multiple matching logs found in block")
ErrNoMatchingLog = errors.New("no matching log") ErrNoMatchingLog = errors.New("no matching log")
Ether = big.NewFloat(1e18) Ether = big.NewFloat(1e18)
PeekMethodName = "peek" LogValueTopic0 = "0x296ba4ca62c6c21c95e828080cb8aec7481b71390585605300a8a76f9e95b527"
PepLogTopic0 = "0x296ba4ca62c6c21c95e828080cb8aec7481b71390585605300a8a76f9e95b527" MedianizerABI = `[{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"poke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"poke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"compute","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wat","type":"address"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wat","type":"address"}],"name":"unset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"indexes","outputs":[{"name":"","type":"bytes12"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"next","outputs":[{"name":"","type":"bytes12"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"read","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"peek","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes12"}],"name":"values","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"min_","type":"uint96"}],"name":"setMin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"void","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"pos","type":"bytes12"},{"name":"wat","type":"address"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"pos","type":"bytes12"}],"name":"unset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"next_","type":"bytes12"}],"name":"setNext","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"min","outputs":[{"name":"","type":"uint96"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"val","type":"bytes32"}],"name":"LogValue","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]]`
PipLogTopic0 = "0x1817835800000000000000000000000000000000000000000000000000000000"
PipMedianizerABI = `[{"constant":false,"inputs":[{"name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"","type":"bytes32"}],"name":"poke","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"poke","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"compute","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wat","type":"address"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"wat","type":"address"}],"name":"unset","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"indexes","outputs":[{"name":"","type":"bytes12"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"next","outputs":[{"name":"","type":"bytes12"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"read","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"peek","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes12"}],"name":"values","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"min_","type":"uint96"}],"name":"setMin","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"void","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"pos","type":"bytes12"},{"name":"wat","type":"address"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"authority","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"pos","type":"bytes12"}],"name":"unset","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"next_","type":"bytes12"}],"name":"setNext","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"min","outputs":[{"name":"","type":"uint96"}],"payable":false,"type":"function"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"guy","type":"address"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":false,"name":"wad","type":"uint256"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"}]`
Ray = big.NewFloat(1e27) Ray = big.NewFloat(1e27)
RepLogTopic0 = "0x296ba4ca62c6c21c95e828080cb8aec7481b71390585605300a8a76f9e95b527"
) )

View File

@ -0,0 +1,32 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds
import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
type PriceFeedConverter struct{}
func (converter PriceFeedConverter) ToModel(log types.Log, headerID int64) PriceFeedModel {
return PriceFeedModel{
BlockNumber: log.BlockNumber,
HeaderID: headerID,
MedianizerAddress: log.Address.Bytes(),
UsdValue: Convert("wad", hexutil.Encode(log.Data), 15),
TransactionIndex: log.TxIndex,
}
}

View File

@ -0,0 +1,57 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds_test
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
var _ = Describe("Price feed converter", func() {
It("converts a log to a price feed model", func() {
medianizerAddress := common.HexToAddress("0x99041f808d598b782d5a3e498681c2452a31da08")
blockNumber := uint64(6147230)
txIndex := uint(119)
// https://etherscan.io/tx/0xa51a50a2adbfba4e2ab3d72dfd67a21c769f1bc8d2b180663a15500a56cde58f
log := types.Log{
Address: medianizerAddress,
Topics: []common.Hash{common.HexToHash(price_feeds.LogValueTopic0)},
Data: common.FromHex("00000000000000000000000000000000000000000000001486f658319fb0c100"),
BlockNumber: blockNumber,
TxHash: common.HexToHash("0xa51a50a2adbfba4e2ab3d72dfd67a21c769f1bc8d2b180663a15500a56cde58f"),
TxIndex: txIndex,
BlockHash: common.HexToHash("0x27ecebbf69eefa3bb3cf65f472322a80ff4946653a50a2171dc605f49829467d"),
Index: 0,
Removed: false,
}
converter := price_feeds.PriceFeedConverter{}
headerID := int64(123)
model := converter.ToModel(log, headerID)
expectedModel := price_feeds.PriceFeedModel{
BlockNumber: blockNumber,
HeaderID: headerID,
MedianizerAddress: medianizerAddress[:],
UsdValue: "378.6599388897",
TransactionIndex: txIndex,
}
Expect(model).To(Equal(expectedModel))
})
})

View File

@ -0,0 +1,54 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds
import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"math/big"
)
type IPriceFeedFetcher interface {
FetchLogValues(blockNumber int64) ([]types.Log, error)
}
type PriceFeedFetcher struct {
blockChain core.BlockChain
contractAddresses []string
}
func NewPriceFeedFetcher(blockChain core.BlockChain, contractAddresses []string) PriceFeedFetcher {
return PriceFeedFetcher{
blockChain: blockChain,
contractAddresses: contractAddresses,
}
}
func (fetcher PriceFeedFetcher) FetchLogValues(blockNumber int64) ([]types.Log, error) {
var addresses []common.Address
for _, addr := range fetcher.contractAddresses {
addresses = append(addresses, common.HexToAddress(addr))
}
n := big.NewInt(blockNumber)
query := ethereum.FilterQuery{
FromBlock: n,
ToBlock: n,
Addresses: addresses,
Topics: [][]common.Hash{{common.HexToHash(LogValueTopic0)}},
}
return fetcher.blockChain.GetEthLogsWithCustomQuery(query)
}

View File

@ -0,0 +1,64 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds_test
import (
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
var _ = Describe("Price fetcher", func() {
It("gets log value events from price feed medianizers", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}})
contractAddresses := []string{"pep-contract-address", "pip-contract-address", "rep-contract-address"}
fetcher := price_feeds.NewPriceFeedFetcher(mockBlockChain, contractAddresses)
blockNumber := int64(100)
_, err := fetcher.FetchLogValues(blockNumber)
Expect(err).NotTo(HaveOccurred())
var expectedAddresses []common.Address
for _, address := range contractAddresses {
expectedAddresses = append(expectedAddresses, common.HexToAddress(address))
}
expectedQuery := ethereum.FilterQuery{
FromBlock: big.NewInt(blockNumber),
ToBlock: big.NewInt(blockNumber),
Addresses: expectedAddresses,
Topics: [][]common.Hash{{common.HexToHash(price_feeds.LogValueTopic0)}},
}
mockBlockChain.AssertGetEthLogsWithCustomQueryCalledWith(expectedQuery)
})
It("returns error if getting logs fails", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
fetcher := price_feeds.NewPriceFeedFetcher(mockBlockChain, []string{"contract-address"})
_, err := fetcher.FetchLogValues(100)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
})

View File

@ -1,53 +0,0 @@
package pep
import (
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type IPepFetcher interface {
FetchPepValue(header core.Header) (string, error)
}
type PepFetcher struct {
blockChain core.BlockChain
contractAddress string
}
func NewPepFetcher(chain core.BlockChain, contractAddress string) PepFetcher {
return PepFetcher{
blockChain: chain,
contractAddress: contractAddress,
}
}
func (fetcher PepFetcher) FetchPepValue(header core.Header) (string, error) {
blockNumber := big.NewInt(header.BlockNumber)
query := ethereum.FilterQuery{
FromBlock: blockNumber,
ToBlock: blockNumber,
Addresses: []common.Address{common.HexToAddress(fetcher.contractAddress)},
Topics: [][]common.Hash{{common.HexToHash(price_feeds.PepLogTopic0)}},
}
logs, err := fetcher.blockChain.GetEthLogsWithCustomQuery(query)
return fetcher.getLogValue(logs, err)
}
func (fetcher PepFetcher) getLogValue(logs []types.Log, err error) (string, error) {
if err != nil {
return "", err
}
if len(logs) < 1 {
return "", price_feeds.ErrNoMatchingLog
}
if len(logs) > 1 {
return "", price_feeds.ErrMultipleLogs
}
return string(logs[0].Data), nil
}

View File

@ -1,74 +0,0 @@
package pep_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pep"
"math/big"
)
var _ = Describe("Pep fetcher", func() {
It("gets logs describing updated mkr/usd value", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}})
contractAddress := "pep-contract-address"
fetcher := pep.NewPepFetcher(mockBlockChain, contractAddress)
blockNumber := int64(12345)
header := core.Header{
BlockNumber: blockNumber,
Hash: "",
Raw: nil,
}
_, err := fetcher.FetchPepValue(header)
Expect(err).NotTo(HaveOccurred())
expectedQuery := ethereum.FilterQuery{
FromBlock: big.NewInt(blockNumber),
ToBlock: big.NewInt(blockNumber),
Addresses: []common.Address{common.HexToAddress(contractAddress)},
Topics: [][]common.Hash{{common.HexToHash(price_feeds.PepLogTopic0)}},
}
mockBlockChain.AssertGetEthLogsWithCustomQueryCalledWith(expectedQuery)
})
It("returns error if getting logs fails", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}})
mockBlockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
fetcher := pep.NewPepFetcher(mockBlockChain, "pep-contract-address")
_, err := fetcher.FetchPepValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("returns no matching logs error if no logs returned", func() {
mockBlockChain := fakes.NewMockBlockChain()
fetcher := pep.NewPepFetcher(mockBlockChain, "pep-contract-address")
_, err := fetcher.FetchPepValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(price_feeds.ErrNoMatchingLog))
})
It("returns error if more than one matching logs returned", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}, {}})
fetcher := pep.NewPepFetcher(mockBlockChain, "pep-contract-address")
_, err := fetcher.FetchPepValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(price_feeds.ErrMultipleLogs))
})
})

View File

@ -1,13 +0,0 @@
package pep
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestPep(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Pep Suite")
}

View File

@ -1,25 +0,0 @@
package pep
import (
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type IPepRepository interface {
CreatePep(pep price_feeds.PriceUpdate) error
}
type PepRepository struct {
db *postgres.DB
}
func NewPepRepository(db *postgres.DB) PepRepository {
return PepRepository{
db: db,
}
}
func (repository PepRepository) CreatePep(pep price_feeds.PriceUpdate) error {
_, err := repository.db.Exec(`INSERT INTO maker.peps (block_number, header_id, usd_value) VALUES ($1, $2, $3::NUMERIC)`, pep.BlockNumber, pep.HeaderID, pep.UsdValue)
return err
}

View File

@ -1,51 +0,0 @@
package pep_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pep"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Pep repository", func() {
It("returns header if matching header does not exist", func() {
db := test_config.NewTestDB(core.Node{})
repository := pep.NewPepRepository(db)
pepToAdd := price_feeds.PriceUpdate{
BlockNumber: 0,
HeaderID: 0,
UsdValue: "123.456",
}
err := repository.CreatePep(pepToAdd)
Expect(err).To(HaveOccurred())
})
It("creates a pep when matching header exists", func() {
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
repository := pep.NewPepRepository(db)
header := core.Header{BlockNumber: 12345}
headerRepository := repositories.NewHeaderRepository(db)
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
pepToAdd := price_feeds.PriceUpdate{
BlockNumber: header.BlockNumber,
HeaderID: headerID,
UsdValue: "123.456",
}
err = repository.CreatePep(pepToAdd)
Expect(err).NotTo(HaveOccurred())
var dbPep price_feeds.PriceUpdate
err = db.Get(&dbPep, `SELECT block_number, header_id, usd_value FROM maker.peps WHERE header_id = $1`, pepToAdd.HeaderID)
Expect(err).NotTo(HaveOccurred())
Expect(dbPep).To(Equal(pepToAdd))
})
})

View File

@ -1,43 +0,0 @@
package pep
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type PepTransformer struct {
fetcher IPepFetcher
repository IPepRepository
}
func NewPepTransformer(chain core.BlockChain, db *postgres.DB, contractAddress string) PepTransformer {
fetcher := NewPepFetcher(chain, contractAddress)
repository := NewPepRepository(db)
return PepTransformer{
fetcher: fetcher,
repository: repository,
}
}
func (transformer PepTransformer) Execute(header core.Header, headerID int64) error {
logValue, err := transformer.fetcher.FetchPepValue(header)
if err != nil {
if err == price_feeds.ErrNoMatchingLog {
return nil
}
return err
}
pep := getPep(logValue, header, headerID)
return transformer.repository.CreatePep(pep)
}
func getPep(logValue string, header core.Header, headerID int64) price_feeds.PriceUpdate {
valueInUSD := price_feeds.Convert("wad", logValue, 15)
pep := price_feeds.PriceUpdate{
BlockNumber: header.BlockNumber,
HeaderID: headerID,
UsdValue: valueInUSD,
}
return pep
}

View File

@ -1,46 +0,0 @@
package pep_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pep"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Pep transformer", func() {
It("returns nil if no logs found", func() {
chain := fakes.NewMockBlockChain()
db := test_config.NewTestDB(core.Node{})
transformer := pep.NewPepTransformer(chain, db, "pep-contract-address")
err := transformer.Execute(core.Header{}, 123)
Expect(err).NotTo(HaveOccurred())
})
It("creates pep row for found log", func() {
chain := fakes.NewMockBlockChain()
chain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{Data: []byte{1, 2, 3, 4, 5}}})
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
headerRepository := repositories.NewHeaderRepository(db)
header := core.Header{BlockNumber: 12345}
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
transformer := pep.NewPepTransformer(chain, db, "pep-contract-address")
err = transformer.Execute(header, headerID)
Expect(err).NotTo(HaveOccurred())
var dbPep price_feeds.PriceUpdate
err = db.Get(&dbPep, `SELECT block_number, header_id, usd_value FROM maker.peps WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(dbPep.BlockNumber).To(Equal(header.BlockNumber))
})
})

View File

@ -1,77 +0,0 @@
package pip
import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"math/big"
)
type IPipFetcher interface {
FetchPipValue(header core.Header) (string, error)
}
type PipFetcher struct {
blockChain core.BlockChain
contractAddress string
}
func NewPipFetcher(chain core.BlockChain, contractAddress string) PipFetcher {
return PipFetcher{
blockChain: chain,
contractAddress: contractAddress,
}
}
func (fetcher PipFetcher) FetchPipValue(header core.Header) (string, error) {
blockNumber := big.NewInt(header.BlockNumber)
query := ethereum.FilterQuery{
FromBlock: blockNumber,
ToBlock: blockNumber,
Addresses: []common.Address{common.HexToAddress(fetcher.contractAddress)},
Topics: [][]common.Hash{{common.HexToHash(price_feeds.PipLogTopic0)}},
}
logs, err := fetcher.blockChain.GetEthLogsWithCustomQuery(query)
if err != nil {
return "", err
}
if len(logs) > 0 {
return fetcher.getLogValue(logs, err)
}
return "", price_feeds.ErrNoMatchingLog
}
func (fetcher PipFetcher) getLogValue(logs []types.Log, err error) (string, error) {
var (
ret0 = new([32]byte)
ret1 = new(bool)
)
var r = &[]interface{}{
ret0,
ret1,
}
err = fetcher.blockChain.FetchContractData(price_feeds.PipMedianizerABI, fetcher.contractAddress, price_feeds.PeekMethodName, nil, r, int64(logs[0].BlockNumber))
if err != nil {
return "", err
}
result := newResult(*ret0, *ret1)
return result.Value.String(), nil
}
type Value [32]byte
type Peek struct {
Value
OK bool
}
func (value Value) String() string {
bi := big.NewInt(0).SetBytes(value[:])
return bi.String()
}
func newResult(value [32]byte, ok bool) *Peek {
return &Peek{Value: value, OK: ok}
}

View File

@ -1,61 +0,0 @@
package pip_test
import (
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pip"
)
var _ = Describe("Pip fetcher", func() {
It("returns error if fetching logs fails", func() {
chain := fakes.NewMockBlockChain()
chain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
fetcher := pip.NewPipFetcher(chain, "pip-contract-address")
_, err := fetcher.FetchPipValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("returns no matching logs error if no logs returned", func() {
chain := fakes.NewMockBlockChain()
fetcher := pip.NewPipFetcher(chain, "pip-contract-address")
_, err := fetcher.FetchPipValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(price_feeds.ErrNoMatchingLog))
})
Describe("when matching log found", func() {
It("calls contract to peek current eth/usd value", func() {
blockNumber := uint64(12345)
chain := fakes.NewMockBlockChain()
chain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{BlockNumber: blockNumber}})
contractAddress := "pip-contract-address"
fetcher := pip.NewPipFetcher(chain, contractAddress)
_, err := fetcher.FetchPipValue(core.Header{})
Expect(err).NotTo(HaveOccurred())
chain.AssertFetchContractDataCalledWith(price_feeds.PipMedianizerABI, contractAddress, price_feeds.PeekMethodName, nil, &[]interface{}{[32]byte{}, false}, int64(blockNumber))
})
It("returns error if contract call fails", func() {
chain := fakes.NewMockBlockChain()
chain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{BlockNumber: uint64(12345)}})
chain.SetFetchContractDataErr(fakes.FakeError)
fetcher := pip.NewPipFetcher(chain, "pip-contract-address")
_, err := fetcher.FetchPipValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
})
})

View File

@ -1,13 +0,0 @@
package pip
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestPip(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Pip Suite")
}

View File

@ -1,25 +0,0 @@
package pip
import (
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type IPipRepository interface {
CreatePip(pip price_feeds.PriceUpdate) error
}
type PipRepository struct {
db *postgres.DB
}
func NewPipRepository(db *postgres.DB) PipRepository {
return PipRepository{
db: db,
}
}
func (repository PipRepository) CreatePip(pip price_feeds.PriceUpdate) error {
_, err := repository.db.Exec(`INSERT INTO maker.pips (block_number, header_id, usd_value) VALUES ($1, $2, $3::NUMERIC)`, pip.BlockNumber, pip.HeaderID, pip.UsdValue)
return err
}

View File

@ -1,51 +0,0 @@
package pip_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pip"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Pip repository", func() {
It("does not create a pip if no matching header", func() {
db := test_config.NewTestDB(core.Node{})
repository := pip.NewPipRepository(db)
priceUpdate := price_feeds.PriceUpdate{
BlockNumber: 0,
HeaderID: 0,
UsdValue: "123",
}
err := repository.CreatePip(priceUpdate)
Expect(err).To(HaveOccurred())
})
It("creates a pip when header exists", func() {
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
repository := pip.NewPipRepository(db)
headerRepository := repositories.NewHeaderRepository(db)
header := core.Header{BlockNumber: 12345}
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
priceUpdate := price_feeds.PriceUpdate{
BlockNumber: header.BlockNumber,
HeaderID: headerID,
UsdValue: "777.777",
}
err = repository.CreatePip(priceUpdate)
Expect(err).NotTo(HaveOccurred())
var dbPip price_feeds.PriceUpdate
err = db.Get(&dbPip, `SELECT block_number, header_id, usd_value FROM maker.pips WHERE block_number = $1`, header.BlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(dbPip).To(Equal(priceUpdate))
})
})

View File

@ -1,43 +0,0 @@
package pip
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type PipTransformer struct {
fetcher IPipFetcher
repository IPipRepository
}
func NewPipTransformer(chain core.BlockChain, db *postgres.DB, contractAddress string) PipTransformer {
fetcher := NewPipFetcher(chain, contractAddress)
repository := NewPipRepository(db)
return PipTransformer{
fetcher: fetcher,
repository: repository,
}
}
func (transformer PipTransformer) Execute(header core.Header, headerID int64) error {
value, err := transformer.fetcher.FetchPipValue(header)
if err != nil {
if err == price_feeds.ErrNoMatchingLog {
return nil
}
return err
}
pip := getPip(value, header, headerID)
return transformer.repository.CreatePip(pip)
}
func getPip(logValue string, header core.Header, headerID int64) price_feeds.PriceUpdate {
valueInUSD := price_feeds.Convert("wad", logValue, 15)
pep := price_feeds.PriceUpdate{
BlockNumber: header.BlockNumber,
HeaderID: headerID,
UsdValue: valueInUSD,
}
return pep
}

View File

@ -1,45 +0,0 @@
package pip_test
import (
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/pip"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Pip transformer", func() {
It("returns nil if no logs found", func() {
chain := fakes.NewMockBlockChain()
db := test_config.NewTestDB(core.Node{})
transformer := pip.NewPipTransformer(chain, db, "pip-contract-address")
err := transformer.Execute(core.Header{}, 123)
Expect(err).NotTo(HaveOccurred())
})
It("creates pip row for found log", func() {
chain := fakes.NewMockBlockChain()
chain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{Data: []byte{1, 2, 3, 4, 5}}})
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
headerRepository := repositories.NewHeaderRepository(db)
header := core.Header{BlockNumber: 12345}
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
transformer := pip.NewPipTransformer(chain, db, "pip-contract-address")
err = transformer.Execute(header, headerID)
Expect(err).NotTo(HaveOccurred())
var dbPip price_feeds.PriceUpdate
err = db.Get(&dbPip, `SELECT block_number, header_id, usd_value FROM maker.pips WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(dbPip.BlockNumber).To(Equal(header.BlockNumber))
})
})

View File

@ -0,0 +1,27 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestPriceFeeds(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "PriceFeeds Suite")
}

View File

@ -1,11 +1,34 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds package price_feeds
import "math/big" import (
"github.com/ethereum/go-ethereum/common"
"math/big"
)
type PriceUpdate struct { type LogValueEntity struct {
BlockNumber int64 `db:"block_number"` Val common.Address
HeaderID int64 `db:"header_id"` }
UsdValue string `db:"usd_value"`
type PriceFeedModel struct {
BlockNumber uint64 `db:"block_number"`
HeaderID int64 `db:"header_id"`
MedianizerAddress []byte `db:"medianizer_address"`
UsdValue string `db:"usd_value"`
TransactionIndex uint `db:"tx_idx"`
} }
func Convert(conversion string, value string, prec int) string { func Convert(conversion string, value string, prec int) string {

View File

@ -1,51 +0,0 @@
package rep
import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"math/big"
)
type IRepFetcher interface {
FetchRepValue(header core.Header) (string, error)
}
type RepFetcher struct {
chain core.BlockChain
contractAddress string
}
func NewRepFetcher(chain core.BlockChain, contractAddress string) RepFetcher {
return RepFetcher{
chain: chain,
contractAddress: contractAddress,
}
}
func (fetcher RepFetcher) FetchRepValue(header core.Header) (string, error) {
blockNumber := big.NewInt(header.BlockNumber)
query := ethereum.FilterQuery{
FromBlock: blockNumber,
ToBlock: blockNumber,
Addresses: []common.Address{common.HexToAddress(fetcher.contractAddress)},
Topics: [][]common.Hash{{common.HexToHash(price_feeds.RepLogTopic0)}},
}
logs, err := fetcher.chain.GetEthLogsWithCustomQuery(query)
return fetcher.getLogValue(logs, err)
}
func (fetcher RepFetcher) getLogValue(logs []types.Log, err error) (string, error) {
if err != nil {
return "", err
}
if len(logs) < 1 {
return "", price_feeds.ErrNoMatchingLog
}
if len(logs) > 1 {
return "", price_feeds.ErrMultipleLogs
}
return string(logs[0].Data), nil
}

View File

@ -1,72 +0,0 @@
package rep_test
import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/rep"
"math/big"
)
var _ = Describe("Rep fetcher", func() {
It("gets logs describing updated rep/usd value", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}})
contractAddress := "rep-contract-address"
fetcher := rep.NewRepFetcher(mockBlockChain, contractAddress)
blockNumber := int64(100)
header := core.Header{
BlockNumber: blockNumber,
Hash: "",
Raw: nil,
}
_, err := fetcher.FetchRepValue(header)
Expect(err).NotTo(HaveOccurred())
expectedQuery := ethereum.FilterQuery{
FromBlock: big.NewInt(blockNumber),
ToBlock: big.NewInt(blockNumber),
Addresses: []common.Address{common.HexToAddress(contractAddress)},
Topics: [][]common.Hash{{common.HexToHash(price_feeds.RepLogTopic0)}},
}
mockBlockChain.AssertGetEthLogsWithCustomQueryCalledWith(expectedQuery)
})
It("returns error if getting logs fails", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
fetcher := rep.NewRepFetcher(mockBlockChain, "rep-contract-address")
_, err := fetcher.FetchRepValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("returns no matching logs error if no logs returned", func() {
mockBlockChain := fakes.NewMockBlockChain()
fetcher := rep.NewRepFetcher(mockBlockChain, "rep-contract-address")
_, err := fetcher.FetchRepValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(price_feeds.ErrNoMatchingLog))
})
It("returns error if more than one matching logs returned", func() {
mockBlockChain := fakes.NewMockBlockChain()
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}, {}})
fetcher := rep.NewRepFetcher(mockBlockChain, "rep-contract-address")
_, err := fetcher.FetchRepValue(core.Header{})
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(price_feeds.ErrMultipleLogs))
})
})

View File

@ -1,13 +0,0 @@
package rep_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestRep(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Rep Suite")
}

View File

@ -1,25 +0,0 @@
package rep
import (
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type IRepRepository interface {
CreateRep(rep price_feeds.PriceUpdate) error
}
type RepRepository struct {
db *postgres.DB
}
func NewRepRepository(db *postgres.DB) RepRepository {
return RepRepository{
db: db,
}
}
func (repository RepRepository) CreateRep(rep price_feeds.PriceUpdate) error {
_, err := repository.db.Exec(`INSERT INTO maker.reps (block_number, header_id, usd_value) VALUES ($1, $2, $3::NUMERIC)`, rep.BlockNumber, rep.HeaderID, rep.UsdValue)
return err
}

View File

@ -1,50 +0,0 @@
package rep_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/rep"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Rep repository", func() {
It("returns header if matching header does not exist", func() {
db := test_config.NewTestDB(core.Node{})
repository := rep.NewRepRepository(db)
pepToAdd := price_feeds.PriceUpdate{
BlockNumber: 0,
HeaderID: 0,
UsdValue: "123.456",
}
err := repository.CreateRep(pepToAdd)
Expect(err).To(HaveOccurred())
})
It("creates a rep when matching header exists", func() {
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
repository := rep.NewRepRepository(db)
header := core.Header{BlockNumber: 12345}
headerRepository := repositories.NewHeaderRepository(db)
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
pepToAdd := price_feeds.PriceUpdate{
BlockNumber: header.BlockNumber,
HeaderID: headerID,
UsdValue: "123.456",
}
err = repository.CreateRep(pepToAdd)
Expect(err).NotTo(HaveOccurred())
var dbRep price_feeds.PriceUpdate
err = db.Get(&dbRep, `SELECT block_number, header_id, usd_value FROM maker.reps WHERE header_id = $1`, pepToAdd.HeaderID)
Expect(err).NotTo(HaveOccurred())
Expect(dbRep).To(Equal(pepToAdd))
})
})

View File

@ -1,43 +0,0 @@
package rep
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type RepTransformer struct {
fetcher IRepFetcher
repository IRepRepository
}
func NewRepTransformer(chain core.BlockChain, db *postgres.DB, contractAddress string) RepTransformer {
fetcher := NewRepFetcher(chain, contractAddress)
repository := NewRepRepository(db)
return RepTransformer{
fetcher: fetcher,
repository: repository,
}
}
func (transformer RepTransformer) Execute(header core.Header, headerID int64) error {
logValue, err := transformer.fetcher.FetchRepValue(header)
if err != nil {
if err == price_feeds.ErrNoMatchingLog {
return nil
}
return err
}
rep := getRep(logValue, header, headerID)
return transformer.repository.CreateRep(rep)
}
func getRep(logValue string, header core.Header, headerID int64) price_feeds.PriceUpdate {
valueInUSD := price_feeds.Convert("wad", logValue, 15)
rep := price_feeds.PriceUpdate{
BlockNumber: header.BlockNumber,
HeaderID: headerID,
UsdValue: valueInUSD,
}
return rep
}

View File

@ -1,46 +0,0 @@
package rep_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds/rep"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Rep transformer", func() {
It("returns nil if no logs found", func() {
chain := fakes.NewMockBlockChain()
db := test_config.NewTestDB(core.Node{})
transformer := rep.NewRepTransformer(chain, db, "rep-contract-address")
err := transformer.Execute(core.Header{}, 123)
Expect(err).NotTo(HaveOccurred())
})
It("creates rep row for found log", func() {
chain := fakes.NewMockBlockChain()
chain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{Data: []byte{1, 2, 3, 4, 5}}})
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
headerRepository := repositories.NewHeaderRepository(db)
header := core.Header{BlockNumber: 12345}
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
transformer := rep.NewRepTransformer(chain, db, "rep-contract-address")
err = transformer.Execute(header, headerID)
Expect(err).NotTo(HaveOccurred())
var dbRep price_feeds.PriceUpdate
err = db.Get(&dbRep, `SELECT block_number, header_id, usd_value FROM maker.reps WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(dbRep.BlockNumber).To(Equal(header.BlockNumber))
})
})

View File

@ -0,0 +1,56 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
)
type IPriceFeedRepository interface {
Create(model PriceFeedModel) error
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
}
type PriceFeedRepository struct {
db *postgres.DB
}
func NewPriceFeedRepository(db *postgres.DB) PriceFeedRepository {
return PriceFeedRepository{db: db}
}
func (repository PriceFeedRepository) Create(model PriceFeedModel) error {
_, err := repository.db.Exec(`INSERT INTO maker.price_feeds (block_number, header_id, medianizer_address, usd_value, tx_idx)
VALUES ($1, $2, $3, $4::NUMERIC, $5)`, model.BlockNumber, model.HeaderID, model.MedianizerAddress, model.UsdValue, model.TransactionIndex)
return err
}
func (repository PriceFeedRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
var result []core.Header
err := repository.db.Select(
&result,
`SELECT headers.id, headers.block_number FROM headers
LEFT JOIN maker.price_feeds on headers.id = header_id
WHERE header_id ISNULL
AND headers.block_number >= $1
AND headers.block_number <= $2
AND headers.eth_node_fingerprint = $3`,
startingBlockNumber,
endingBlockNumber,
repository.db.Node.ID,
)
return result, err
}

View File

@ -0,0 +1,147 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Price feeds repository", func() {
Describe("Create", func() {
It("persists a price feed update", func() {
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
headerRepository := repositories.NewHeaderRepository(db)
blockNumber := uint64(12345)
header := core.Header{BlockNumber: int64(blockNumber)}
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
priceFeedUpdate := price_feeds.PriceFeedModel{
BlockNumber: blockNumber,
HeaderID: headerID,
MedianizerAddress: []byte{1, 2, 3, 4, 5},
UsdValue: "123.45",
TransactionIndex: 1,
}
priceFeedRepository := price_feeds.NewPriceFeedRepository(db)
err = priceFeedRepository.Create(priceFeedUpdate)
Expect(err).NotTo(HaveOccurred())
var dbPriceFeedUpdate price_feeds.PriceFeedModel
err = db.Get(&dbPriceFeedUpdate, `SELECT block_number, header_id, medianizer_address, usd_value, tx_idx FROM maker.price_feeds WHERE header_id = $1`, headerID)
Expect(err).NotTo(HaveOccurred())
Expect(dbPriceFeedUpdate).To(Equal(priceFeedUpdate))
})
It("does not duplicate price feed updates", func() {
db := test_config.NewTestDB(core.Node{})
test_config.CleanTestDB(db)
headerRepository := repositories.NewHeaderRepository(db)
blockNumber := uint64(12345)
header := core.Header{BlockNumber: int64(blockNumber)}
headerID, err := headerRepository.CreateOrUpdateHeader(header)
Expect(err).NotTo(HaveOccurred())
priceFeedUpdate := price_feeds.PriceFeedModel{
BlockNumber: blockNumber,
HeaderID: headerID,
MedianizerAddress: []byte{1, 2, 3, 4, 5},
UsdValue: "123.45",
TransactionIndex: 1,
}
priceFeedRepository := price_feeds.NewPriceFeedRepository(db)
err = priceFeedRepository.Create(priceFeedUpdate)
Expect(err).NotTo(HaveOccurred())
err = priceFeedRepository.Create(priceFeedUpdate)
Expect(err).To(HaveOccurred())
})
})
Describe("MissingHeaders", func() {
It("returns headers with no associated price feed event", func() {
node := core.Node{}
db := test_config.NewTestDB(node)
test_config.CleanTestDB(db)
headerRepository := repositories.NewHeaderRepository(db)
startingBlockNumber := int64(1)
priceFeedBlockNumber := int64(2)
endingBlockNumber := int64(3)
blockNumbers := []int64{startingBlockNumber, priceFeedBlockNumber, endingBlockNumber, endingBlockNumber + 1}
var headerIDs []int64
for _, n := range blockNumbers {
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n})
headerIDs = append(headerIDs, headerID)
Expect(err).NotTo(HaveOccurred())
}
priceFeedRepository := price_feeds.NewPriceFeedRepository(db)
priceFeedUpdate := price_feeds.PriceFeedModel{
BlockNumber: uint64(blockNumbers[1]),
HeaderID: headerIDs[1],
UsdValue: "123.45",
}
err := priceFeedRepository.Create(priceFeedUpdate)
Expect(err).NotTo(HaveOccurred())
headers, err := priceFeedRepository.MissingHeaders(startingBlockNumber, endingBlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(len(headers)).To(Equal(2))
Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber)))
Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber)))
})
It("only returns headers associated with the current node", func() {
nodeOne := core.Node{}
db := test_config.NewTestDB(nodeOne)
test_config.CleanTestDB(db)
blockNumbers := []int64{1, 2, 3}
headerRepository := repositories.NewHeaderRepository(db)
nodeTwo := core.Node{ID: "second"}
dbTwo := test_config.NewTestDB(nodeTwo)
headerRepositoryTwo := repositories.NewHeaderRepository(dbTwo)
var headerIDs []int64
for _, n := range blockNumbers {
headerID, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: n})
Expect(err).NotTo(HaveOccurred())
headerIDs = append(headerIDs, headerID)
_, err = headerRepositoryTwo.CreateOrUpdateHeader(core.Header{BlockNumber: n})
Expect(err).NotTo(HaveOccurred())
}
priceFeedRepository := price_feeds.NewPriceFeedRepository(db)
priceFeedRepositoryTwo := price_feeds.NewPriceFeedRepository(dbTwo)
err := priceFeedRepository.Create(price_feeds.PriceFeedModel{
HeaderID: headerIDs[0],
UsdValue: "123.45",
})
Expect(err).NotTo(HaveOccurred())
nodeOneMissingHeaders, err := priceFeedRepository.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1])
Expect(err).NotTo(HaveOccurred())
Expect(len(nodeOneMissingHeaders)).To(Equal(len(blockNumbers) - 1))
nodeTwoMissingHeaders, err := priceFeedRepositoryTwo.MissingHeaders(blockNumbers[0], blockNumbers[len(blockNumbers)-1])
Expect(err).NotTo(HaveOccurred())
Expect(len(nodeTwoMissingHeaders)).To(Equal(len(blockNumbers)))
})
})
})

View File

@ -0,0 +1,65 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
)
type PriceFeedTransformerInitializer struct {
Config IPriceFeedConfig
}
func (initializer PriceFeedTransformerInitializer) NewPriceFeedTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer {
converter := PriceFeedConverter{}
fetcher := NewPriceFeedFetcher(blockChain, initializer.Config.ContractAddresses)
repository := NewPriceFeedRepository(db)
return PriceFeedTransformer{
Config: initializer.Config,
converter: converter,
Fetcher: fetcher,
Repository: repository,
}
}
type PriceFeedTransformer struct {
Config IPriceFeedConfig
converter PriceFeedConverter
Fetcher IPriceFeedFetcher
Repository IPriceFeedRepository
}
func (transformer PriceFeedTransformer) Execute() error {
headers, err := transformer.Repository.MissingHeaders(transformer.Config.StartingBlockNumber, transformer.Config.EndingBlockNumber)
if err != nil {
return err
}
for _, header := range headers {
logs, err := transformer.Fetcher.FetchLogValues(header.BlockNumber)
if err != nil {
return err
}
for _, log := range logs {
model := transformer.converter.ToModel(log, header.Id)
err = transformer.Repository.Create(model)
if err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,150 @@
// Copyright © 2018 Vulcanize
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package price_feeds_test
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
price_feeds2 "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/price_feeds"
"math/big"
)
var _ = Describe("Price feed transformer", func() {
It("gets missing headers for price feeds", func() {
mockRepository := &price_feeds2.MockPriceFeedRepository{}
transformer := price_feeds.PriceFeedTransformer{
Config: price_feeds.PriceFeedConfig,
Fetcher: &price_feeds2.MockPriceFeedFetcher{},
Repository: mockRepository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
mockRepository.AssertMissingHeadersCalledwith(price_feeds.PriceFeedConfig.StartingBlockNumber, price_feeds.PriceFeedConfig.EndingBlockNumber)
})
It("returns error is missing headers call returns err", func() {
mockRepository := &price_feeds2.MockPriceFeedRepository{}
mockRepository.SetMissingHeadersErr(fakes.FakeError)
transformer := price_feeds.PriceFeedTransformer{
Fetcher: &price_feeds2.MockPriceFeedFetcher{},
Repository: mockRepository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("fetches logs for missing headers", func() {
mockRepository := &price_feeds2.MockPriceFeedRepository{}
blockNumberOne := int64(1)
blockNumberTwo := int64(2)
mockRepository.SetMissingHeaders([]core.Header{{BlockNumber: blockNumberOne}, {BlockNumber: blockNumberTwo}})
mockFetcher := &price_feeds2.MockPriceFeedFetcher{}
transformer := price_feeds.PriceFeedTransformer{
Fetcher: mockFetcher,
Repository: mockRepository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
mockFetcher.AssertFetchLogValuesCalledWith([]int64{blockNumberOne, blockNumberTwo})
})
It("returns err if fetcher returns err", func() {
mockRepository := &price_feeds2.MockPriceFeedRepository{}
mockRepository.SetMissingHeaders([]core.Header{{BlockNumber: 1}})
mockFetcher := &price_feeds2.MockPriceFeedFetcher{}
mockFetcher.SetReturnErr(fakes.FakeError)
transformer := price_feeds.PriceFeedTransformer{
Fetcher: mockFetcher,
Repository: mockRepository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("persists model converted from log", func() {
mockRepository := &price_feeds2.MockPriceFeedRepository{}
headerID := int64(11111)
mockRepository.SetMissingHeaders([]core.Header{{BlockNumber: 1, Id: headerID}})
mockFetcher := &price_feeds2.MockPriceFeedFetcher{}
blockNumber := uint64(22222)
txIndex := uint(33333)
usdValue := int64(44444)
etherMultiplier, _ := price_feeds.Ether.Int64()
rawUsdValue := big.NewInt(0)
rawUsdValue = rawUsdValue.Mul(big.NewInt(usdValue), big.NewInt(etherMultiplier))
address := common.BytesToAddress([]byte{1, 2, 3, 4, 5})
fakeLog := types.Log{
Address: address,
Topics: nil,
Data: rawUsdValue.Bytes(),
BlockNumber: blockNumber,
TxHash: common.Hash{},
TxIndex: txIndex,
BlockHash: common.Hash{},
Index: 0,
Removed: false,
}
mockFetcher.SetReturnLogs([]types.Log{fakeLog})
transformer := price_feeds.PriceFeedTransformer{
Fetcher: mockFetcher,
Repository: mockRepository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
expectedModel := price_feeds.PriceFeedModel{
BlockNumber: blockNumber,
HeaderID: headerID,
MedianizerAddress: address.Bytes(),
UsdValue: fmt.Sprintf("%d", usdValue),
TransactionIndex: txIndex,
}
mockRepository.AssertCreateCalledWith(expectedModel)
})
It("returns error if creating price feed update returns error", func() {
mockRepository := &price_feeds2.MockPriceFeedRepository{}
mockRepository.SetMissingHeaders([]core.Header{{BlockNumber: 1, Id: 2}})
mockRepository.SetCreateErr(fakes.FakeError)
mockFetcher := &price_feeds2.MockPriceFeedFetcher{}
mockFetcher.SetReturnLogs([]types.Log{{}})
transformer := price_feeds.PriceFeedTransformer{
Fetcher: mockFetcher,
Repository: mockRepository,
}
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
})

View File

@ -27,6 +27,14 @@ type Transformer interface {
type TransformerInitializer func(db *postgres.DB, blockChain core.BlockChain) Transformer type TransformerInitializer func(db *postgres.DB, blockChain core.BlockChain) Transformer
type TransformerConfig struct {
ContractAddresses string
ContractAbi string
Topics []string
StartingBlockNumber int64
EndingBlockNumber int64
}
func HexToInt64(byteString string) int64 { func HexToInt64(byteString string) int64 {
value := common.HexToHash(byteString) value := common.HexToHash(byteString)
return value.Big().Int64() return value.Big().Int64()

View File

@ -0,0 +1,29 @@
package price_feeds
import (
"github.com/ethereum/go-ethereum/core/types"
. "github.com/onsi/gomega"
)
type MockPriceFeedFetcher struct {
passedBlockNumbers []int64
returnErr error
returnLogs []types.Log
}
func (fetcher *MockPriceFeedFetcher) SetReturnErr(err error) {
fetcher.returnErr = err
}
func (fetcher *MockPriceFeedFetcher) SetReturnLogs(logs []types.Log) {
fetcher.returnLogs = logs
}
func (fetcher *MockPriceFeedFetcher) FetchLogValues(blockNumber int64) ([]types.Log, error) {
fetcher.passedBlockNumbers = append(fetcher.passedBlockNumbers, blockNumber)
return fetcher.returnLogs, fetcher.returnErr
}
func (fetcher *MockPriceFeedFetcher) AssertFetchLogValuesCalledWith(blockNumbers []int64) {
Expect(fetcher.passedBlockNumbers).To(Equal(blockNumbers))
}

View File

@ -0,0 +1,49 @@
package price_feeds
import (
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
)
type MockPriceFeedRepository struct {
createErr error
missingHeaders []core.Header
missingHeadersErr error
passedEndingBlockNumber int64
passedModel price_feeds.PriceFeedModel
passedStartingBlockNumber int64
}
func (repository *MockPriceFeedRepository) SetCreateErr(err error) {
repository.createErr = err
}
func (repository *MockPriceFeedRepository) SetMissingHeadersErr(err error) {
repository.missingHeadersErr = err
}
func (repository *MockPriceFeedRepository) SetMissingHeaders(headers []core.Header) {
repository.missingHeaders = headers
}
func (repository *MockPriceFeedRepository) Create(model price_feeds.PriceFeedModel) error {
repository.passedModel = model
return repository.createErr
}
func (repository *MockPriceFeedRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
repository.passedStartingBlockNumber = startingBlockNumber
repository.passedEndingBlockNumber = endingBlockNumber
return repository.missingHeaders, repository.missingHeadersErr
}
func (repository *MockPriceFeedRepository) AssertCreateCalledWith(model price_feeds.PriceFeedModel) {
Expect(repository.passedModel).To(Equal(model))
}
func (repository *MockPriceFeedRepository) AssertMissingHeadersCalledwith(startingBlockNumber, endingBlockNumber int64) {
Expect(repository.passedStartingBlockNumber).To(Equal(startingBlockNumber))
Expect(repository.passedEndingBlockNumber).To(Equal(endingBlockNumber))
}

View File

@ -1,9 +0,0 @@
package transformers
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
)
type Transformer interface {
Execute(header core.Header, headerID int64) error
}

View File

@ -17,6 +17,7 @@ package transformers
import ( import (
"github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick" "github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/frob" "github.com/vulcanize/vulcanizedb/pkg/transformers/frob"
"github.com/vulcanize/vulcanizedb/pkg/transformers/price_feeds"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared" "github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
) )
@ -25,8 +26,11 @@ func TransformerInitializers() []shared.TransformerInitializer {
flipKickTransformerInitializer := flip_kick.FlipKickTransformerInitializer{Config: flipKickConfig} flipKickTransformerInitializer := flip_kick.FlipKickTransformerInitializer{Config: flipKickConfig}
frobConfig := frob.FrobConfig frobConfig := frob.FrobConfig
frobTransformerInitializer := frob.FrobTransformerInitializer{Config: frobConfig} frobTransformerInitializer := frob.FrobTransformerInitializer{Config: frobConfig}
priceFeedConfig := price_feeds.PriceFeedConfig
priceFeedTransformerInitializer := price_feeds.PriceFeedTransformerInitializer{Config: priceFeedConfig}
return []shared.TransformerInitializer{ return []shared.TransformerInitializer{
flipKickTransformerInitializer.NewFlipKickTransformer, flipKickTransformerInitializer.NewFlipKickTransformer,
frobTransformerInitializer.NewFrobTransformer, frobTransformerInitializer.NewFrobTransformer,
priceFeedTransformerInitializer.NewPriceFeedTransformer,
} }
} }

View File

@ -77,9 +77,6 @@ func CleanTestDB(db *postgres.DB) {
db.MustExec("DELETE FROM headers") db.MustExec("DELETE FROM headers")
db.MustExec("DELETE FROM log_filters") db.MustExec("DELETE FROM log_filters")
db.MustExec("DELETE FROM logs") db.MustExec("DELETE FROM logs")
db.MustExec("DELETE FROM maker.peps")
db.MustExec("DELETE FROM maker.pips")
db.MustExec("DELETE FROM maker.reps")
db.MustExec("DELETE FROM receipts") db.MustExec("DELETE FROM receipts")
db.MustExec("DELETE FROM transactions") db.MustExec("DELETE FROM transactions")
db.MustExec("DELETE FROM watched_contracts") db.MustExec("DELETE FROM watched_contracts")