Add Pep price feed
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package pep
|
||||
|
||||
var (
|
||||
LogValueTopic0 = "0x296ba4ca62c6c21c95e828080cb8aec7481b71390585605300a8a76f9e95b527"
|
||||
PepAddress = "0x99041f808d598b782d5a3e498681c2452a31da08"
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
package pep
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMultipleLogs = errors.New("multiple matching logs found in block")
|
||||
ErrNoMatchingLog = errors.New("no matching log")
|
||||
)
|
||||
|
||||
type IPepFetcher interface {
|
||||
FetchPepValue(header core.Header) (string, error)
|
||||
}
|
||||
|
||||
type PepFetcher struct {
|
||||
blockChain core.BlockChain
|
||||
}
|
||||
|
||||
func NewPepFetcher(chain core.BlockChain) PepFetcher {
|
||||
return PepFetcher{
|
||||
blockChain: chain,
|
||||
}
|
||||
}
|
||||
|
||||
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(PepAddress)},
|
||||
Topics: [][]common.Hash{{common.HexToHash(LogValueTopic0)}},
|
||||
}
|
||||
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 "", ErrNoMatchingLog
|
||||
}
|
||||
if len(logs) > 1 {
|
||||
return "", ErrMultipleLogs
|
||||
}
|
||||
return string(logs[0].Data), nil
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
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/pep"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var _ = Describe("Pep fetcher", func() {
|
||||
It("calls contract to peek mkr/usd value", func() {
|
||||
mockBlockChain := fakes.NewMockBlockChain()
|
||||
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}})
|
||||
fetcher := pep.NewPepFetcher(mockBlockChain)
|
||||
blockNumber := int64(100)
|
||||
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(pep.PepAddress)},
|
||||
Topics: [][]common.Hash{{common.HexToHash(pep.LogValueTopic0)}},
|
||||
}
|
||||
mockBlockChain.AssertGetEthLogsWithCustomQueryCalledWith(expectedQuery)
|
||||
})
|
||||
|
||||
It("returns error if contract call fails", func() {
|
||||
mockBlockChain := fakes.NewMockBlockChain()
|
||||
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}})
|
||||
mockBlockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
|
||||
fetcher := pep.NewPepFetcher(mockBlockChain)
|
||||
|
||||
_, 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)
|
||||
|
||||
_, err := fetcher.FetchPepValue(core.Header{})
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(pep.ErrNoMatchingLog))
|
||||
})
|
||||
|
||||
It("returns error if more than one matching logs returned", func() {
|
||||
mockBlockChain := fakes.NewMockBlockChain()
|
||||
mockBlockChain.SetGetEthLogsWithCustomQueryReturnLogs([]types.Log{{}, {}})
|
||||
fetcher := pep.NewPepFetcher(mockBlockChain)
|
||||
|
||||
_, err := fetcher.FetchPepValue(core.Header{})
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(pep.ErrMultipleLogs))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
package pep
|
||||
|
||||
type Pep struct {
|
||||
BlockNumber int64 `db:"block_number"`
|
||||
HeaderID int64 `db:"header_id"`
|
||||
UsdValue string `db:"usd_value"`
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package pep_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestPep(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Pep Suite")
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package pep
|
||||
|
||||
import (
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
)
|
||||
|
||||
type IPepRepository interface {
|
||||
CreatePep(pep Pep) error
|
||||
}
|
||||
|
||||
type PepRepository struct {
|
||||
db *postgres.DB
|
||||
}
|
||||
|
||||
func NewPepRepository(db *postgres.DB) PepRepository {
|
||||
return PepRepository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (repository PepRepository) CreatePep(pep Pep) 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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/pep"
|
||||
"github.com/vulcanize/vulcanizedb/test_config"
|
||||
)
|
||||
|
||||
var _ = Describe("Pep repository", func() {
|
||||
It("creates a pep", 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 := pep.Pep{
|
||||
BlockNumber: header.BlockNumber,
|
||||
HeaderID: headerID,
|
||||
UsdValue: "123.456",
|
||||
}
|
||||
|
||||
err = repository.CreatePep(pepToAdd)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
var dbPep pep.Pep
|
||||
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))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
package pep
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
)
|
||||
|
||||
var Ether = big.NewFloat(1e18)
|
||||
var Ray = big.NewFloat(1e27)
|
||||
|
||||
type PepTransformer struct {
|
||||
fetcher IPepFetcher
|
||||
repository IPepRepository
|
||||
}
|
||||
|
||||
func NewPepTransformer(chain core.BlockChain, db *postgres.DB) PepTransformer {
|
||||
fetcher := NewPepFetcher(chain)
|
||||
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 == ErrNoMatchingLog {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
pep := getPep(logValue, header, headerID)
|
||||
return transformer.repository.CreatePep(pep)
|
||||
}
|
||||
|
||||
func getPep(logValue string, header core.Header, headerID int64) Pep {
|
||||
valueInUSD := convert("wad", logValue, 15)
|
||||
pep := Pep{
|
||||
BlockNumber: header.BlockNumber,
|
||||
HeaderID: headerID,
|
||||
UsdValue: valueInUSD,
|
||||
}
|
||||
return pep
|
||||
}
|
||||
|
||||
func convert(conversion string, value string, prec int) string {
|
||||
var bgflt = big.NewFloat(0.0)
|
||||
bgflt.SetString(value)
|
||||
switch conversion {
|
||||
case "ray":
|
||||
bgflt.Quo(bgflt, Ray)
|
||||
case "wad":
|
||||
bgflt.Quo(bgflt, Ether)
|
||||
}
|
||||
return bgflt.Text('g', prec)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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/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)
|
||||
|
||||
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{})
|
||||
headerRepository := repositories.NewHeaderRepository(db)
|
||||
header := core.Header{BlockNumber: 12345}
|
||||
headerID, err := headerRepository.CreateOrUpdateHeader(header)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
transformer := pep.NewPepTransformer(chain, db)
|
||||
|
||||
err = transformer.Execute(header, headerID)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
var dbPep pep.Pep
|
||||
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))
|
||||
})
|
||||
})
|
||||
@@ -51,7 +51,7 @@ var _ = Describe("Fetcher", func() {
|
||||
|
||||
It("returns an error if fetching the logs fails", func() {
|
||||
blockChain := fakes.NewMockBlockChain()
|
||||
blockChain.SetGetLogsErr(fakes.FakeError)
|
||||
blockChain.SetGetEthLogsWithCustomQueryErr(fakes.FakeError)
|
||||
fetcher := shared.NewFetcher(blockChain)
|
||||
|
||||
_, err := fetcher.FetchLogs("", [][]common.Hash{}, int64(1))
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
)
|
||||
|
||||
type Transformer interface {
|
||||
Execute(header core.Header, headerID int64) error
|
||||
}
|
||||
Reference in New Issue
Block a user