Flop kick (#28)

* FlopKick transformer
This commit is contained in:
Elizabeth 2018-09-26 09:42:52 -05:00 committed by GitHub
parent a0ba6ca6bd
commit 31516ea87e
25 changed files with 1247 additions and 100 deletions

View File

@ -0,0 +1,3 @@
DROP TABLE maker.flop_kick;
ALTER TABLE public.checked_headers
DROP COLUMN flop_kick_checked;

View File

@ -0,0 +1,15 @@
CREATE TABLE maker.flop_kick (
id SERIAL PRIMARY KEY,
header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE,
bid_id NUMERIC NOT NULL,
lot NUMERIC NOT NULL,
bid NUMERIC NOT NULL,
gal VARCHAR,
"end" TIMESTAMP WITH TIME ZONE,
tx_idx INTEGER NOT NULL,
raw_log JSONB,
UNIQUE (header_id, tx_idx)
);
ALTER TABLE public.checked_headers
ADD COLUMN flop_kick_checked BOOLEAN NOT NULL DEFAULT FALSE;

View File

@ -340,6 +340,43 @@ CREATE SEQUENCE maker.flip_kick_id_seq
ALTER SEQUENCE maker.flip_kick_id_seq OWNED BY maker.flip_kick.id; ALTER SEQUENCE maker.flip_kick_id_seq OWNED BY maker.flip_kick.id;
--
-- Name: flop_kick; Type: TABLE; Schema: maker; Owner: -
--
CREATE TABLE maker.flop_kick (
id integer NOT NULL,
header_id integer NOT NULL,
bid_id numeric NOT NULL,
lot numeric NOT NULL,
bid numeric NOT NULL,
gal character varying,
"end" timestamp with time zone,
tx_idx integer NOT NULL,
raw_log jsonb
);
--
-- Name: flop_kick_id_seq; Type: SEQUENCE; Schema: maker; Owner: -
--
CREATE SEQUENCE maker.flop_kick_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: flop_kick_id_seq; Type: SEQUENCE OWNED BY; Schema: maker; Owner: -
--
ALTER SEQUENCE maker.flop_kick_id_seq OWNED BY maker.flop_kick.id;
-- --
-- Name: frob; Type: TABLE; Schema: maker; Owner: - -- Name: frob; Type: TABLE; Schema: maker; Owner: -
-- --
@ -670,6 +707,7 @@ CREATE TABLE public.checked_headers (
id integer NOT NULL, id integer NOT NULL,
header_id integer NOT NULL, header_id integer NOT NULL,
price_feeds_checked boolean DEFAULT false NOT NULL, price_feeds_checked boolean DEFAULT false NOT NULL,
flop_kick_checked boolean DEFAULT false NOT NULL,
deal_checked boolean DEFAULT false NOT NULL, deal_checked boolean DEFAULT false NOT NULL,
dent_checked boolean DEFAULT false NOT NULL, dent_checked boolean DEFAULT false NOT NULL,
flip_kick_checked boolean DEFAULT false NOT NULL, flip_kick_checked boolean DEFAULT false NOT NULL,
@ -1050,6 +1088,13 @@ ALTER TABLE ONLY maker.drip_file_vow ALTER COLUMN id SET DEFAULT nextval('maker.
ALTER TABLE ONLY maker.flip_kick ALTER COLUMN id SET DEFAULT nextval('maker.flip_kick_id_seq'::regclass); ALTER TABLE ONLY maker.flip_kick ALTER COLUMN id SET DEFAULT nextval('maker.flip_kick_id_seq'::regclass);
--
-- Name: flop_kick id; Type: DEFAULT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.flop_kick ALTER COLUMN id SET DEFAULT nextval('maker.flop_kick_id_seq'::regclass);
-- --
-- Name: frob id; Type: DEFAULT; Schema: maker; Owner: - -- Name: frob id; Type: DEFAULT; Schema: maker; Owner: -
-- --
@ -1305,6 +1350,22 @@ ALTER TABLE ONLY maker.flip_kick
ADD CONSTRAINT flip_kick_pkey PRIMARY KEY (id); ADD CONSTRAINT flip_kick_pkey PRIMARY KEY (id);
--
-- Name: flop_kick flop_kick_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.flop_kick
ADD CONSTRAINT flop_kick_header_id_tx_idx_key UNIQUE (header_id, tx_idx);
--
-- Name: flop_kick flop_kick_pkey; Type: CONSTRAINT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.flop_kick
ADD CONSTRAINT flop_kick_pkey PRIMARY KEY (id);
-- --
-- Name: frob frob_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: - -- Name: frob frob_header_id_tx_idx_key; Type: CONSTRAINT; Schema: maker; Owner: -
-- --
@ -1643,6 +1704,14 @@ ALTER TABLE ONLY maker.flip_kick
ADD CONSTRAINT flip_kick_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE; ADD CONSTRAINT flip_kick_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
--
-- Name: flop_kick flop_kick_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: -
--
ALTER TABLE ONLY maker.flop_kick
ADD CONSTRAINT flop_kick_header_id_fkey FOREIGN KEY (header_id) REFERENCES public.headers(id) ON DELETE CASCADE;
-- --
-- Name: frob frob_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: - -- Name: frob frob_header_id_fkey; Type: FK CONSTRAINT; Schema: maker; Owner: -
-- --

View File

@ -9,9 +9,9 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/core"
vulcCommon "github.com/vulcanize/vulcanizedb/pkg/geth/converters/common" vulcCommon "github.com/vulcanize/vulcanizedb/pkg/geth/converters/common"
"errors"
) )
var ErrEmptyHeader = errors.New("empty header returned over RPC") var ErrEmptyHeader = errors.New("empty header returned over RPC")

View File

@ -54,14 +54,14 @@ func (BiteConverter) ToEntity(contractAddress string, contractAbi string, ethLog
} }
func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) { func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) {
id := entity.Id.String() id := entity.Id
ilk := entity.Ilk[:] ilk := entity.Ilk[:]
lad := entity.Lad[:] lad := entity.Lad[:]
ink := entity.Ink.String() ink := entity.Ink
art := entity.Art.String() art := entity.Art
iArt := entity.IArt.String() iArt := entity.IArt
tab := entity.Tab.String() tab := entity.Tab
flip := entity.Flip.String() flip := entity.Flip
txIdx := entity.TransactionIndex txIdx := entity.TransactionIndex
rawLogJson, err := json.Marshal(entity.Raw) rawLogJson, err := json.Marshal(entity.Raw)
rawLogString := string(rawLogJson) rawLogString := string(rawLogJson)
@ -70,14 +70,14 @@ func (converter BiteConverter) ToModel(entity BiteEntity) (BiteModel, error) {
} }
return BiteModel{ return BiteModel{
Id: shared.ConvertNilToEmptyString(id), Id: shared.BigIntToString(id),
Ilk: ilk, Ilk: ilk,
Lad: lad, Lad: lad,
Ink: shared.ConvertNilToEmptyString(ink), Ink: shared.BigIntToString(ink),
Art: shared.ConvertNilToEmptyString(art), Art: shared.BigIntToString(art),
IArt: shared.ConvertNilToEmptyString(iArt), IArt: shared.BigIntToString(iArt),
Tab: shared.ConvertNilToEmptyString(tab), Tab: shared.BigIntToString(tab),
Flip: shared.ConvertNilToEmptyString(flip), Flip: shared.BigIntToString(flip),
TransactionIndex: txIdx, TransactionIndex: txIdx,
Raw: rawLogString, Raw: rawLogString,
}, nil }, nil

View File

@ -64,13 +64,13 @@ func (FlipKickConverter) ToModels(flipKicks []FlipKickEntity) (results []FlipKic
} }
id := flipKick.Id.String() id := flipKick.Id.String()
lot := shared.ConvertNilToEmptyString(flipKick.Lot.String()) lot := shared.BigIntToString(flipKick.Lot)
bid := shared.ConvertNilToEmptyString(flipKick.Bid.String()) bid := shared.BigIntToString(flipKick.Bid)
gal := flipKick.Gal.String() gal := flipKick.Gal.String()
endValue := shared.ConvertNilToZeroTimeValue(flipKick.End) endValue := shared.BigIntToInt64(flipKick.End)
end := time.Unix(endValue, 0) end := time.Unix(endValue, 0)
urn := common.BytesToAddress(flipKick.Urn[:common.AddressLength]).String() urn := common.BytesToAddress(flipKick.Urn[:common.AddressLength]).String()
tab := shared.ConvertNilToEmptyString(flipKick.Tab.String()) tab := shared.BigIntToString(flipKick.Tab)
rawLogJson, err := json.Marshal(flipKick.Raw) rawLogJson, err := json.Marshal(flipKick.Raw)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,25 @@
// 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 flop_kick
import "github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
var Config = shared.TransformerConfig{
ContractAddress: shared.FlopperContractAddress,
ContractAbi: shared.FlopperABI,
Topics: []string{shared.FlopKickSignature},
StartingBlockNumber: 0,
EndingBlockNumber: 10000000,
}

View File

@ -0,0 +1,81 @@
// 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 flop_kick
import (
"encoding/json"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/geth"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
)
type Converter interface {
ToEntities(contractAddress, contractAbi string, ethLogs []types.Log) ([]Entity, error)
ToModels(entities []Entity) ([]Model, error)
}
type FlopKickConverter struct{}
func (FlopKickConverter) ToEntities(contractAddress, contractAbi string, ethLogs []types.Log) ([]Entity, error) {
var results []Entity
for _, ethLog := range ethLogs {
entity := Entity{}
address := common.HexToAddress(contractAddress)
abi, err := geth.ParseAbi(contractAbi)
if err != nil {
return nil, err
}
contract := bind.NewBoundContract(address, abi, nil, nil, nil)
err = contract.UnpackLog(&entity, "Kick", ethLog)
if err != nil {
return nil, err
}
entity.Raw = ethLog
entity.TransactionIndex = ethLog.TxIndex
results = append(results, entity)
}
return results, nil
}
func (FlopKickConverter) ToModels(entities []Entity) ([]Model, error) {
var results []Model
for _, entity := range entities {
endValue := shared.BigIntToInt64(entity.End)
rawLogJson, err := json.Marshal(entity.Raw)
if err != nil {
return nil, err
}
model := Model{
BidId: shared.BigIntToString(entity.Id),
Lot: shared.BigIntToString(entity.Lot),
Bid: shared.BigIntToString(entity.Bid),
Gal: entity.Gal.String(),
End: time.Unix(endValue, 0),
TransactionIndex: entity.TransactionIndex,
Raw: rawLogJson,
}
results = append(results, model)
}
return results, nil
}

View File

@ -0,0 +1,82 @@
// 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 flop_kick_test
import (
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/ethereum/go-ethereum/core/types"
)
var _ = Describe("FlopKick Converter", func() {
Describe("ToEntities", func() {
It("converts a log to a FlopKick entity", func() {
converter := flop_kick.FlopKickConverter{}
entities, err := converter.ToEntities(shared.FlopperContractAddress, shared.FlopperABI, []types.Log{test_data.FlopKickLog})
Expect(err).NotTo(HaveOccurred())
entity := entities[0]
Expect(entity.Id).To(Equal(test_data.FlopKickEntity.Id))
Expect(entity.Lot).To(Equal(test_data.FlopKickEntity.Lot))
Expect(entity.Bid).To(Equal(test_data.FlopKickEntity.Bid))
Expect(entity.Gal).To(Equal(test_data.FlopKickEntity.Gal))
Expect(entity.End).To(Equal(test_data.FlopKickEntity.End))
Expect(entity.TransactionIndex).To(Equal(test_data.FlopKickEntity.TransactionIndex))
Expect(entity.Raw).To(Equal(test_data.FlopKickEntity.Raw))
})
It("returns an error if converting the log to an entity fails", func() {
converter := flop_kick.FlopKickConverter{}
entities, err := converter.ToEntities(shared.FlopperContractAddress, "error abi", []types.Log{test_data.FlopKickLog})
Expect(err).To(HaveOccurred())
Expect(entities).To(BeNil())
})
})
Describe("ToModels", func() {
var emptyAddressHex = "0x0000000000000000000000000000000000000000"
var emptyString = ""
var emptyTime = time.Unix(0, 0)
var emptyEntities = []flop_kick.Entity{flop_kick.Entity{}}
It("converts an Entity to a Model", func() {
converter := flop_kick.FlopKickConverter{}
models, err := converter.ToModels([]flop_kick.Entity{test_data.FlopKickEntity})
Expect(err).NotTo(HaveOccurred())
Expect(models[0]).To(Equal(test_data.FlopKickModel))
})
It("handles nil values", func() {
converter := flop_kick.FlopKickConverter{}
models, err := converter.ToModels(emptyEntities)
model := models[0]
Expect(err).NotTo(HaveOccurred())
Expect(model.BidId).To(Equal(emptyString))
Expect(model.Lot).To(Equal(emptyString))
Expect(model.Bid).To(Equal(emptyString))
Expect(model.Gal).To(Equal(emptyAddressHex))
Expect(model.End).To(Equal(emptyTime))
})
})
})

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 flop_kick
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type Entity struct {
Id *big.Int
Lot *big.Int
Bid *big.Int
Gal common.Address
End *big.Int
TransactionIndex uint
Raw types.Log
}

View File

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

View File

@ -0,0 +1,50 @@
// 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 flop_kick_test
import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/geth"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
)
var _ = Describe("Integration tests", func() {
It("unpacks an flop kick event log", func() {
address := common.HexToAddress(shared.FlopperContractAddress)
abi, err := geth.ParseAbi(shared.FlopperABI)
Expect(err).NotTo(HaveOccurred())
contract := bind.NewBoundContract(address, abi, nil, nil, nil)
entity := &flop_kick.Entity{}
var eventLog = test_data.FlopKickLog
err = contract.UnpackLog(entity, "Kick", eventLog)
Expect(err).NotTo(HaveOccurred())
expectedEntity := test_data.FlopKickEntity
Expect(entity.Id).To(Equal(expectedEntity.Id))
Expect(entity.Lot).To(Equal(expectedEntity.Lot))
Expect(entity.Bid).To(Equal(expectedEntity.Bid))
Expect(entity.Gal).To(Equal(expectedEntity.Gal))
Expect(entity.End).To(Equal(expectedEntity.End))
})
})

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 flop_kick
import "time"
type Model struct {
BidId string `db:"bid_id"`
Lot string
Bid string
Gal string
End time.Time
TransactionIndex uint `db:"tx_idx"`
Raw []byte `db:"raw_log"`
}

View File

@ -0,0 +1,89 @@
// 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 flop_kick
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
)
type Repository interface {
Create(headerId int64, flopKick []Model) error
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
MarkHeaderChecked(headerId int64) error
}
type FlopKickRepository struct {
DB *postgres.DB
}
func NewFlopKickRepository(db *postgres.DB) FlopKickRepository {
return FlopKickRepository{DB: db}
}
func (r FlopKickRepository) Create(headerId int64, flopKicks []Model) error {
tx, err := r.DB.Begin()
if err != nil {
return err
}
for _, flopKick := range flopKicks {
_, err = tx.Exec(
`INSERT into maker.flop_kick (header_id, bid_id, lot, bid, gal, "end", tx_idx, raw_log)
VALUES($1, $2, $3, $4, $5, $6, $7, $8)`,
headerId, flopKick.BidId, flopKick.Lot, flopKick.Bid, flopKick.Gal, flopKick.End, flopKick.TransactionIndex, flopKick.Raw,
)
if err != nil {
tx.Rollback()
return err
}
}
_, err = tx.Exec(`INSERT INTO public.checked_headers (header_id, flop_kick_checked)
VALUES ($1, $2)
ON CONFLICT (header_id) DO
UPDATE SET flop_kick_checked = $2`, headerId, true)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
func (r FlopKickRepository) MarkHeaderChecked(headerId int64) error {
_, err := r.DB.Exec(`INSERT INTO public.checked_headers (header_id, flop_kick_checked)
VALUES ($1, $2)
ON CONFLICT (header_id) DO
UPDATE SET flop_kick_checked = $2`, headerId, true)
return err
}
func (r FlopKickRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
var result []core.Header
err := r.DB.Select(
&result,
`SELECT headers.id, headers.block_number FROM headers
LEFT JOIN checked_headers on headers.id = header_id
WHERE (header_id ISNULL OR flop_kick_checked IS FALSE)
AND headers.block_number >= $1
AND headers.block_number <= $2
AND headers.eth_node_fingerprint = $3`,
startingBlockNumber,
endingBlockNumber,
r.DB.Node.ID,
)
return result, err
}

View File

@ -0,0 +1,200 @@
// 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 flop_kick_test
import (
"math/rand"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("FlopRepository", func() {
var db *postgres.DB
var repository flop_kick.FlopKickRepository
var headerRepository repositories.HeaderRepository
var headerId int64
var err error
var dbResult test_data.FlopKickDBResult
BeforeEach(func() {
node := test_config.NewTestNode()
db = test_config.NewTestDB(node)
test_config.CleanTestDB(db)
repository = flop_kick.NewFlopKickRepository(db)
headerRepository = repositories.NewHeaderRepository(db)
headerId, err = headerRepository.CreateOrUpdateHeader(core.Header{})
Expect(err).NotTo(HaveOccurred())
dbResult = test_data.FlopKickDBResult{}
})
Describe("Create", func() {
It("creates FlopKick records", func() {
err := repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel})
Expect(err).NotTo(HaveOccurred())
err = db.QueryRowx(`SELECT * FROM maker.flop_kick WHERE header_id = $1`, headerId).StructScan(&dbResult)
Expect(err).NotTo(HaveOccurred())
Expect(dbResult.HeaderId).To(Equal(headerId))
Expect(dbResult.BidId).To(Equal(test_data.FlopKickModel.BidId))
Expect(dbResult.Lot).To(Equal(test_data.FlopKickModel.Lot))
Expect(dbResult.Bid).To(Equal(test_data.FlopKickModel.Bid))
Expect(dbResult.Gal).To(Equal(test_data.FlopKickModel.Gal))
Expect(dbResult.End.Equal(test_data.FlopKickModel.End)).To(BeTrue())
Expect(dbResult.TransactionIndex).To(Equal(test_data.FlopKickModel.TransactionIndex))
Expect(dbResult.Raw).To(MatchJSON(test_data.FlopKickModel.Raw))
})
It("marks headerId as checked for flop kick logs", func() {
err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel})
Expect(err).NotTo(HaveOccurred())
var headerChecked bool
err = db.Get(&headerChecked, `SELECT flop_kick_checked FROM public.checked_headers WHERE header_id = $1`, headerId)
Expect(err).NotTo(HaveOccurred())
Expect(headerChecked).To(BeTrue())
})
It("returns an error if inserting the flop_kick record fails", func() {
err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel})
Expect(err).NotTo(HaveOccurred())
err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("pq: duplicate key value violates unique constraint"))
})
It("deletes the flop_kick records if its corresponding header record is deleted", func() {
err = repository.Create(headerId, []flop_kick.Model{test_data.FlopKickModel})
Expect(err).NotTo(HaveOccurred())
var flopKickCount int
err = db.QueryRow(`SELECT count(*) FROM maker.flop_kick`).Scan(&flopKickCount)
Expect(err).NotTo(HaveOccurred())
Expect(flopKickCount).To(Equal(1))
_, err = db.Exec(`DELETE FROM headers where id = $1`, headerId)
Expect(err).NotTo(HaveOccurred())
err = db.QueryRow(`SELECT count(*) FROM maker.flop_kick`).Scan(&flopKickCount)
Expect(err).NotTo(HaveOccurred())
Expect(flopKickCount).To(Equal(0))
})
})
Describe("MarkedHeadersChecked", func() {
It("creates a row for a new headerId", func() {
err := repository.MarkHeaderChecked(headerId)
Expect(err).NotTo(HaveOccurred())
var headerChecked bool
err = db.Get(&headerChecked, `SELECT flop_kick_checked FROM public.checked_headers WHERE header_id = $1`, headerId)
Expect(err).NotTo(HaveOccurred())
Expect(headerChecked).To(BeTrue())
})
It("updates row when headerId already exists", func() {
_, err = db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerId)
err = repository.MarkHeaderChecked(headerId)
Expect(err).NotTo(HaveOccurred())
var headerChecked bool
err = db.Get(&headerChecked, `SELECT flop_kick_checked FROM public.checked_headers WHERE header_id = $1`, headerId)
Expect(err).NotTo(HaveOccurred())
Expect(headerChecked).To(BeTrue())
})
})
Describe("MissingHeaders", func() {
var flopKickBlockNumber = rand.Int63()
var startingBlockNumber = flopKickBlockNumber - 1
var endingBlockNumber = flopKickBlockNumber + 1
var outOfRangeBlockNumber = flopKickBlockNumber + 2
It("returns headers haven't been checked", func() {
var headerIds []int64
for _, number := range []int64{startingBlockNumber, flopKickBlockNumber, endingBlockNumber, outOfRangeBlockNumber} {
headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number})
Expect(err).NotTo(HaveOccurred())
headerIds = append(headerIds, headerId)
}
err = repository.MarkHeaderChecked(headerIds[1])
Expect(err).NotTo(HaveOccurred())
headers, err := repository.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 treats headers as checked if flop_kicks have been checked", func() {
var headerIds []int64
for _, number := range []int64{startingBlockNumber, flopKickBlockNumber, endingBlockNumber, outOfRangeBlockNumber} {
headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number})
Expect(err).NotTo(HaveOccurred())
headerIds = append(headerIds, headerId)
}
_, err := db.Exec(`INSERT INTO public.checked_headers (header_id) VALUES ($1)`, headerIds[1])
Expect(err).NotTo(HaveOccurred())
headers, err := repository.MissingHeaders(startingBlockNumber, endingBlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(len(headers)).To(Equal(3))
Expect(headers[0].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(flopKickBlockNumber)))
Expect(headers[1].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(flopKickBlockNumber)))
Expect(headers[2].BlockNumber).To(Or(Equal(startingBlockNumber), Equal(endingBlockNumber), Equal(flopKickBlockNumber)))
})
It("only returns missing headers for the current node", func() {
var headerIds []int64
node2 := core.Node{}
db2 := test_config.NewTestDB(node2)
headerRepository2 := repositories.NewHeaderRepository(db2)
flopKickRepository2 := flop_kick.NewFlopKickRepository(db2)
for _, number := range []int64{startingBlockNumber, flopKickBlockNumber, endingBlockNumber} {
headerId, err := headerRepository.CreateOrUpdateHeader(core.Header{BlockNumber: number})
Expect(err).NotTo(HaveOccurred())
headerIds = append(headerIds, headerId)
headerRepository2.CreateOrUpdateHeader(core.Header{BlockNumber: number})
Expect(err).NotTo(HaveOccurred())
}
err = repository.MarkHeaderChecked(headerIds[1])
Expect(err).NotTo(HaveOccurred())
node1MissingHeaders, err := repository.MissingHeaders(startingBlockNumber, endingBlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(len(node1MissingHeaders)).To(Equal(2))
node2MissingHeaders, err := flopKickRepository2.MissingHeaders(startingBlockNumber, endingBlockNumber)
Expect(err).NotTo(HaveOccurred())
Expect(len(node2MissingHeaders)).To(Equal(3))
})
})
})

View File

@ -0,0 +1,81 @@
// 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 flop_kick
import (
"github.com/ethereum/go-ethereum/common"
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
)
type Transformer struct {
Config shared.TransformerConfig
Converter Converter
Fetcher shared.LogFetcher
Repository Repository
}
type FlopKickTransformerInitializer struct {
Config shared.TransformerConfig
}
func (i FlopKickTransformerInitializer) NewFlopKickTransformer(db *postgres.DB, blockChain core.BlockChain) shared.Transformer {
return Transformer{
Config: i.Config,
Converter: FlopKickConverter{},
Fetcher: shared.NewFetcher(blockChain),
Repository: NewFlopKickRepository(db),
}
}
func (t Transformer) Execute() error {
config := t.Config
headers, err := t.Repository.MissingHeaders(config.StartingBlockNumber, config.EndingBlockNumber)
if err != nil {
return err
}
for _, header := range headers {
topics := [][]common.Hash{{common.HexToHash(shared.FlopKickSignature)}}
matchingLogs, err := t.Fetcher.FetchLogs(config.ContractAddress, topics, header.BlockNumber)
if err != nil {
return err
}
if len(matchingLogs) < 1 {
err := t.Repository.MarkHeaderChecked(header.Id)
if err != nil {
return err
}
}
entities, err := t.Converter.ToEntities(config.ContractAddress, config.ContractAbi, matchingLogs)
if err != nil {
return err
}
models, err := t.Converter.ToModels(entities)
if err != nil {
return err
}
err = t.Repository.Create(header.Id, models)
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,246 @@
// 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 flop_kick_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/core"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks"
flop_kick_mocks "github.com/vulcanize/vulcanizedb/pkg/transformers/test_data/mocks/flop_kick"
)
var _ = Describe("FlopKick Transformer", func() {
var fetcher mocks.MockLogFetcher
var converter flop_kick_mocks.MockConverter
var repository flop_kick_mocks.MockRepository
var config = flop_kick.Config
var headerOne core.Header
var headerTwo core.Header
BeforeEach(func() {
fetcher = mocks.MockLogFetcher{}
converter = flop_kick_mocks.MockConverter{}
repository = flop_kick_mocks.MockRepository{}
headerOne = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()}
headerTwo = core.Header{Id: GinkgoRandomSeed(), BlockNumber: GinkgoRandomSeed()}
})
It("gets missing headers for specified block numbers", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(repository.PassedStartingBlockNumber).To(Equal(flop_kick.Config.StartingBlockNumber))
Expect(repository.PassedEndingBlockNumber).To(Equal(flop_kick.Config.EndingBlockNumber))
})
It("returns error if getting missing headers fails", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeadersError(fakes.FakeError)
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("fetches logs for each missing header", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(fetcher.FetchedBlocks).To(Equal([]int64{headerOne.BlockNumber, headerTwo.BlockNumber}))
Expect(fetcher.FetchedContractAddress).To(Equal(flop_kick.Config.ContractAddress))
Expect(fetcher.FetchedTopics).To(Equal([][]common.Hash{{common.HexToHash(shared.FlopKickSignature)}}))
})
It("returns error if fetcher returns error", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
fetcher.SetFetcherError(fakes.FakeError)
repository.SetMissingHeaders([]core.Header{{}})
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("marks header as checked even if no logs were returned", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
fetcher.SetFetchedLogs([]types.Log{})
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(repository.CheckedHeaderIds).To(ContainElement(headerOne.Id))
Expect(repository.CheckedHeaderIds).To(ContainElement(headerTwo.Id))
})
It("returns error if marking header checked returns err", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
repository.SetCheckedHeaderError(fakes.FakeError)
fetcher.SetFetchedLogs([]types.Log{})
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("converts matching logs to entity", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{{BlockNumber: GinkgoRandomSeed()}})
fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog})
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(converter.PassedContractAddress).To(Equal(flop_kick.Config.ContractAddress))
Expect(converter.PassedContractABI).To(Equal(flop_kick.Config.ContractAbi))
Expect(converter.PassedLogs).To(Equal([]types.Log{test_data.FlopKickLog}))
})
It("returns an error if converting logs to entity fails", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{{BlockNumber: GinkgoRandomSeed()}})
fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog})
converter.SetToEntityConverterError(fakes.FakeError)
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("converts flop_kick entity to model", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{{}})
fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog})
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(converter.PassedEntities).To(Equal([]flop_kick.Entity{test_data.FlopKickEntity}))
})
It("returns an error if there's a failure in converting to model", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{{}})
fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog})
converter.SetToModelConverterError(fakes.FakeError)
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
It("persists the flop_kick model", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{headerOne, headerTwo})
fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog})
err := transformer.Execute()
Expect(err).NotTo(HaveOccurred())
Expect(repository.CreatedHeaderIds).To(ContainElement(headerOne.Id))
Expect(repository.CreatedHeaderIds).To(ContainElement(headerTwo.Id))
Expect(repository.CreatedModels).To(ContainElement(test_data.FlopKickModel))
})
It("returns error if repository returns error for create", func() {
transformer := flop_kick.Transformer{
Config: config,
Converter: &converter,
Fetcher: &fetcher,
Repository: &repository,
}
repository.SetMissingHeaders([]core.Header{{}})
repository.SetCreateError(fakes.FakeError)
fetcher.SetFetchedLogs([]types.Log{test_data.FlopKickLog})
err := transformer.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(fakes.FakeError))
})
})

View File

@ -1,79 +0,0 @@
The main goal of creating a transformer is to fetch specific log events from Ethereum, convert/decode them into usable data and then persist them to VulcanizeDB. For Maker there are two main types of log events that we're tracking: custom events that are defined in the contract solidity code, and LogNote events which utilize the [DSNote library](https://github.com/dapphub/ds-note). The transformer process for each of these different log types is the same, except for the converting process, as denoted below.
## Creating a Transformer for custom events (i.e. FlopKick)
To illustrate how to create a custom log event transformer we'll use the Kick event defined in [flop.sol](https://github.com/makerdao/dss/blob/master/src/flop.sol) as an example.
1. Get an example FlopKick log event either from mainnet (if the
contract has already been deployed), from the Kovan testnet, or by
deploying the contract to a local chain and emitting the event manually.
We will use the example log event to test drive converting the log to a
FlopKick database model.
1. Fetch the appropriate logs from the chain.
- Most transformers use `shared.LogFetcher`
- Price Feeds
1. Convert the raw log into a database model.
- For Custom Events, such as FlopKick
1. Create a converter to convert the raw log into a Go structure.
- We've been using [go-ethereum's abigen tool](https://github.com/ethereum/go-ethereum/tree/master/cmd/abigen) to get the contract's ABI, and a Go struct that represents the event log. We will unpack the raw logs into this struct.
- To use abigen: `abigen --sol flip.sol --pkg flip --out {/path/to/output_file}`
- sol: this is the path to the solidity contract
- pkg: a package name for the generated Go code
- out: the file path for the generated Go code (optional)
- the output for `flop.sol` will include the FlopperAbi and the FlopperKick struct:
```go
type FlopperKick struct {
Id *big.Int
Lot *big.Int
Bid *big.Int
Gal common.Address
End *big.Int
Raw types.Log
}
```
- Using go-ethereum's `contract.UnpackLog` method we can unpack the raw log into the FlopperKick struct (which we're referring to as the `entity`).
- See the `ToEntity` method in `pkg/transformers/flop_kick/converter`.
- The unpack method will not add the `Raw` or `TransactionIndex` values to the entity struct - both of these values are accessible from the entity.
1. Then convert the entity into a database model. See the `ToModel` method in `pkg/transformers/flop_kick/converter`.
- For LogNote Events, such as tend.
- Since LogNote events are a generic structure, they depend on the
method signature of the method that is calling them. For example,
when the `tend` method is called on the
[flip.sol contract](https://github.com/makerdao/dss/blob/master/src/flip.sol#L117), the method signature looks like this: `tend(uint id, uint lot, uint bid)`
- the LogNote event will take the first
1. Persist the log record to VulcanizeDB.
- Each event log has it's own table in the database, as well as it's
own column in the `checked_headers` table.
- The `checked_headers` table alllows us to keep track of which
headers have been queried for a given log type.
- To create a new migration file: `migrate create -ext sql -dir ./db/migrations/ create_flop_kick`
- See `db/migrations/1536942529_create_flop_kick.up.sql`.
- The specific log event tables are all created in the `maker`
schema.
- There is a one-many association between `headers` and the log
event tables. This is so that if a header is removed due to a
reorg, the associated log event records are also removed.
- We have been following a repository pattern to interact with the
database for each table, see the `Create` method in `pkg/transformers/flop_kick/repository.go`.
1. Get all MissingHeaders. //TODO//
- The repository is also responsible for querying for all header
records that have not yet been.
1. MarkHeaderChecked//TODO//
1. Wire each component up in the transformer.
- Each transformer's `Execute` method iterates through all of the
MissingHeaders
- Currently each transformer's `Execute` method is responsible for
fetching the missing headers, meaning the headers that haven't yet
been checked for a given log type
#### For LogNote events

View File

@ -20,6 +20,7 @@ var (
CatABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"flips\",\"outputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"},{\"name\":\"ink\",\"type\":\"uint256\"},{\"name\":\"tab\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"nflip\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"name\":\"flip\",\"type\":\"address\"},{\"name\":\"chop\",\"type\":\"uint256\"},{\"name\":\"lump\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pit\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"vat_\",\"type\":\"address\"},{\"name\":\"pit_\",\"type\":\"address\"},{\"name\":\"vow_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"lad\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"flip\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"iArt\",\"type\":\"uint256\"}],\"name\":\"Bite\",\"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\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"flip\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"}],\"name\":\"bite\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"uint256\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flip\",\"outputs\":[{\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" CatABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"vat\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vow\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"flips\",\"outputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"},{\"name\":\"ink\",\"type\":\"uint256\"},{\"name\":\"tab\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"nflip\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"live\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"ilks\",\"outputs\":[{\"name\":\"flip\",\"type\":\"address\"},{\"name\":\"chop\",\"type\":\"uint256\"},{\"name\":\"lump\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"pit\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"vat_\",\"type\":\"address\"},{\"name\":\"pit_\",\"type\":\"address\"},{\"name\":\"vow_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"ilk\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"lad\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ink\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"art\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"tab\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"flip\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"iArt\",\"type\":\"uint256\"}],\"name\":\"Bite\",\"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\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"what\",\"type\":\"bytes32\"},{\"name\":\"flip\",\"type\":\"address\"}],\"name\":\"file\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"ilk\",\"type\":\"bytes32\"},{\"name\":\"lad\",\"type\":\"bytes32\"}],\"name\":\"bite\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"uint256\"},{\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"flip\",\"outputs\":[{\"name\":\"id\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
DripABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"repo","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x56ff3122"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"vow","type":"bytes32"},{"name":"tax","type":"uint256"},{"name":"rho","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"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","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x143e55e0"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"vow","type":"bytes32"},{"name":"tax","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1a0b287e"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x29ae8114"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"}],"name":"drip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x44e2a5a8"}]` DripABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"repo","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x56ff3122"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"vow","type":"bytes32"},{"name":"tax","type":"uint256"},{"name":"rho","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"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","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x143e55e0"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"vow","type":"bytes32"},{"name":"tax","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1a0b287e"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x29ae8114"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"}],"name":"drip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x44e2a5a8"}]`
FlipperABI = `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"bids","outputs":[{"name":"bid","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"guy","type":"address"},{"name":"tic","type":"uint48"},{"name":"end","type":"uint48"},{"name":"urn","type":"bytes32"},{"name":"gal","type":"address"},{"name":"tab","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4423c5f1"},{"constant":true,"inputs":[],"name":"ttl","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4e8b1dd5"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7bd2bea7"},{"constant":true,"inputs":[],"name":"beg","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7d780d82"},{"constant":true,"inputs":[],"name":"tau","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcfc4af55"},{"constant":true,"inputs":[],"name":"kicks","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcfdd3302"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf4b9fa75"},{"inputs":[{"name":"dai_","type":"address"},{"name":"gem_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"id","type":"uint256"},{"indexed":false,"name":"lot","type":"uint256"},{"indexed":false,"name":"bid","type":"uint256"},{"indexed":false,"name":"gal","type":"address"},{"indexed":false,"name":"end","type":"uint48"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"tab","type":"uint256"}],"name":"Kick","type":"event","signature":"0xbac86238bdba81d21995024470425ecb370078fa62b7271b90cf28cbd1e3e87e"},{"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","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x143e55e0"},{"constant":false,"inputs":[{"name":"urn","type":"bytes32"},{"name":"gal","type":"address"},{"name":"tab","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"kick","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xeae19d9e"},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"tick","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfc7b6aee"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"tend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4b43ed12"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"dent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5ff3a382"},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"deal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc959c42b"}]` FlipperABI = `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"bids","outputs":[{"name":"bid","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"guy","type":"address"},{"name":"tic","type":"uint48"},{"name":"end","type":"uint48"},{"name":"urn","type":"bytes32"},{"name":"gal","type":"address"},{"name":"tab","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4423c5f1"},{"constant":true,"inputs":[],"name":"ttl","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4e8b1dd5"},{"constant":true,"inputs":[],"name":"gem","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7bd2bea7"},{"constant":true,"inputs":[],"name":"beg","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x7d780d82"},{"constant":true,"inputs":[],"name":"tau","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcfc4af55"},{"constant":true,"inputs":[],"name":"kicks","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xcfdd3302"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf4b9fa75"},{"inputs":[{"name":"dai_","type":"address"},{"name":"gem_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"id","type":"uint256"},{"indexed":false,"name":"lot","type":"uint256"},{"indexed":false,"name":"bid","type":"uint256"},{"indexed":false,"name":"gal","type":"address"},{"indexed":false,"name":"end","type":"uint48"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"tab","type":"uint256"}],"name":"Kick","type":"event","signature":"0xbac86238bdba81d21995024470425ecb370078fa62b7271b90cf28cbd1e3e87e"},{"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","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":true,"inputs":[],"name":"era","outputs":[{"name":"","type":"uint48"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x143e55e0"},{"constant":false,"inputs":[{"name":"urn","type":"bytes32"},{"name":"gal","type":"address"},{"name":"tab","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"kick","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xeae19d9e"},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"tick","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xfc7b6aee"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"tend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4b43ed12"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"lot","type":"uint256"},{"name":"bid","type":"uint256"}],"name":"dent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5ff3a382"},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"deal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xc959c42b"}]`
FlopperABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"era\",\"outputs\":[{\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"bids\",\"outputs\":[{\"name\":\"bid\",\"type\":\"uint256\"},{\"name\":\"lot\",\"type\":\"uint256\"},{\"name\":\"guy\",\"type\":\"address\"},{\"name\":\"tic\",\"type\":\"uint48\"},{\"name\":\"end\",\"type\":\"uint48\"},{\"name\":\"vow\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"ttl\",\"outputs\":[{\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"id\",\"type\":\"uint256\"},{\"name\":\"lot\",\"type\":\"uint256\"},{\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"dent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"gem\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"beg\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"guy\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"gal\",\"type\":\"address\"},{\"name\":\"lot\",\"type\":\"uint256\"},{\"name\":\"bid\",\"type\":\"uint256\"}],\"name\":\"kick\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"deal\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tau\",\"outputs\":[{\"name\":\"\",\"type\":\"uint48\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"kicks\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"dai\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"dai_\",\"type\":\"address\"},{\"name\":\"gem_\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"lot\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"bid\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"gal\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"end\",\"type\":\"uint48\"}],\"name\":\"Kick\",\"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\"}]"
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"}]]` 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"}]]`
PitABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"live","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x957aa58c"},{"constant":true,"inputs":[],"name":"drip","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x9f678cca"},{"constant":true,"inputs":[],"name":"Line","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbabe8a3f"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"spot","type":"uint256"},{"name":"line","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"ilk","type":"bytes32"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"ink","type":"uint256"},{"indexed":false,"name":"art","type":"uint256"},{"indexed":false,"name":"dink","type":"int256"},{"indexed":false,"name":"dart","type":"int256"},{"indexed":false,"name":"iArt","type":"uint256"}],"name":"Frob","type":"event","signature":"0xb2afa28318bcc689926b52835d844de174ef8de97e982a85c0199d584920791b"},{"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","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1a0b287e"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x29ae8114"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xd4e8be83"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"frob","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5a984ded"}]` PitABI = `[{"constant":true,"inputs":[],"name":"vat","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x36569e77"},{"constant":true,"inputs":[],"name":"live","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x957aa58c"},{"constant":true,"inputs":[],"name":"drip","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x9f678cca"},{"constant":true,"inputs":[],"name":"Line","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbabe8a3f"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"spot","type":"uint256"},{"name":"line","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"inputs":[{"name":"vat_","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"ilk","type":"bytes32"},{"indexed":true,"name":"urn","type":"bytes32"},{"indexed":false,"name":"ink","type":"uint256"},{"indexed":false,"name":"art","type":"uint256"},{"indexed":false,"name":"dink","type":"int256"},{"indexed":false,"name":"dart","type":"int256"},{"indexed":false,"name":"iArt","type":"uint256"}],"name":"Frob","type":"event","signature":"0xb2afa28318bcc689926b52835d844de174ef8de97e982a85c0199d584920791b"},{"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","signature":"0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x1a0b287e"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"uint256"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x29ae8114"},{"constant":false,"inputs":[{"name":"what","type":"bytes32"},{"name":"data","type":"address"}],"name":"file","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xd4e8be83"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"frob","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5a984ded"}]`
VatABI = `[{"constant":true,"inputs":[],"name":"debt","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x0dca59c1"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes32"}],"name":"urns","outputs":[{"name":"ink","type":"uint256"},{"name":"art","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26e27482"},{"constant":true,"inputs":[],"name":"vice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x2d61a355"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"sin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xa60f1d3e"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes32"}],"name":"gem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc0912683"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"take","type":"uint256"},{"name":"rate","type":"uint256"},{"name":"Ink","type":"uint256"},{"name":"Art","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"dai","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf53e4e69"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":true,"name":"too","type":"bytes32"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"Note","type":"event","signature":"0x8c2dbbc2b33ffaa77c104b777e574a8a4ff79829dfee8b66f4dc63e3f8067152"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"}],"name":"init","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3b663195"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"guy","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"slip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x42066cbb"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"flux","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6e41821"},{"constant":false,"inputs":[{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x78f19470"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"w","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"tune","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5dd6471a"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"w","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"grab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3690ae4c"},{"constant":false,"inputs":[{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"heal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x990a5f63"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"rate","type":"int256"}],"name":"fold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe6a6a64d"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"take","type":"int256"}],"name":"toll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x09b7a0b5"}]` VatABI = `[{"constant":true,"inputs":[],"name":"debt","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x0dca59c1"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes32"}],"name":"urns","outputs":[{"name":"ink","type":"uint256"},{"name":"art","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x26e27482"},{"constant":true,"inputs":[],"name":"vice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x2d61a355"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"sin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xa60f1d3e"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"wards","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xbf353dbb"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes32"}],"name":"gem","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc0912683"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ilks","outputs":[{"name":"take","type":"uint256"},{"name":"rate","type":"uint256"},{"name":"Ink","type":"uint256"},{"name":"Art","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xd9638d36"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"dai","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xf53e4e69"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"name":"sig","type":"bytes4"},{"indexed":true,"name":"foo","type":"bytes32"},{"indexed":true,"name":"bar","type":"bytes32"},{"indexed":true,"name":"too","type":"bytes32"},{"indexed":false,"name":"fax","type":"bytes"}],"name":"Note","type":"event","signature":"0x8c2dbbc2b33ffaa77c104b777e574a8a4ff79829dfee8b66f4dc63e3f8067152"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x65fae35e"},{"constant":false,"inputs":[{"name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9c52a7f1"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"}],"name":"init","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3b663195"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"guy","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"slip","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x42066cbb"},{"constant":false,"inputs":[{"name":"ilk","type":"bytes32"},{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"flux","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa6e41821"},{"constant":false,"inputs":[{"name":"src","type":"bytes32"},{"name":"dst","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x78f19470"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"w","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"tune","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x5dd6471a"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"w","type":"bytes32"},{"name":"dink","type":"int256"},{"name":"dart","type":"int256"}],"name":"grab","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x3690ae4c"},{"constant":false,"inputs":[{"name":"u","type":"bytes32"},{"name":"v","type":"bytes32"},{"name":"rad","type":"int256"}],"name":"heal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x990a5f63"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"rate","type":"int256"}],"name":"fold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xe6a6a64d"},{"constant":false,"inputs":[{"name":"i","type":"bytes32"},{"name":"u","type":"bytes32"},{"name":"take","type":"int256"}],"name":"toll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x09b7a0b5"}]`
@ -28,6 +29,7 @@ var (
CatContractAddress = "0x2f34f22a00ee4b7a8f8bbc4eaee1658774c624e0" CatContractAddress = "0x2f34f22a00ee4b7a8f8bbc4eaee1658774c624e0"
DripContractAddress = "0x891c04639a5edcae088e546fa125b5d7fb6a2b9d" DripContractAddress = "0x891c04639a5edcae088e546fa125b5d7fb6a2b9d"
FlipperContractAddress = "0x32d496ad866d110060866b7125981c73642cc509" // ETH FLIP Contract FlipperContractAddress = "0x32d496ad866d110060866b7125981c73642cc509" // ETH FLIP Contract
FlopperContractAddress = "0x6191c9b0086c2ebf92300cc507009b53996fbffa" // MCD FLOP Contract
PepContractAddress = "0xB1997239Cfc3d15578A3a09730f7f84A90BB4975" PepContractAddress = "0xB1997239Cfc3d15578A3a09730f7f84A90BB4975"
PipContractAddress = "0x9FfFE440258B79c5d6604001674A4722FfC0f7Bc" PipContractAddress = "0x9FfFE440258B79c5d6604001674A4722FfC0f7Bc"
PitContractAddress = "0xe7cf3198787c9a4daac73371a38f29aaeeced87e" PitContractAddress = "0xe7cf3198787c9a4daac73371a38f29aaeeced87e"
@ -43,6 +45,7 @@ var (
dripFileRepoMethod = GetSolidityMethodSignature(DripABI, "file") dripFileRepoMethod = GetSolidityMethodSignature(DripABI, "file")
dripFileVowMethod = "file(bytes32,bytes32)" dripFileVowMethod = "file(bytes32,bytes32)"
flipKickMethod = GetSolidityMethodSignature(FlipperABI, "Kick") flipKickMethod = GetSolidityMethodSignature(FlipperABI, "Kick")
flopKickMethod = GetSolidityMethodSignature(FlopperABI, "Kick")
frobMethod = GetSolidityMethodSignature(PitABI, "Frob") frobMethod = GetSolidityMethodSignature(PitABI, "Frob")
logValueMethod = GetSolidityMethodSignature(MedianizerABI, "LogValue") logValueMethod = GetSolidityMethodSignature(MedianizerABI, "LogValue")
pitFileDebtCeilingMethod = "file(bytes32,uint256)" pitFileDebtCeilingMethod = "file(bytes32,uint256)"
@ -59,6 +62,7 @@ var (
DripFileRepoSignature = GetLogNoteSignature(dripFileRepoMethod) DripFileRepoSignature = GetLogNoteSignature(dripFileRepoMethod)
DripFileVowSignature = GetLogNoteSignature(dripFileVowMethod) DripFileVowSignature = GetLogNoteSignature(dripFileVowMethod)
FlipKickSignature = GetEventSignature(flipKickMethod) FlipKickSignature = GetEventSignature(flipKickMethod)
FlopKickSignature = GetEventSignature(flopKickMethod)
FrobSignature = GetEventSignature(frobMethod) FrobSignature = GetEventSignature(frobMethod)
LogValueSignature = GetEventSignature(logValueMethod) LogValueSignature = GetEventSignature(logValueMethod)
PitFileDebtCeilingSignature = GetLogNoteSignature(pitFileDebtCeilingMethod) PitFileDebtCeilingSignature = GetLogNoteSignature(pitFileDebtCeilingMethod)

View File

@ -135,6 +135,13 @@ var _ = Describe("Event signature generator", func() {
Expect(expected).To(Equal(actual)) Expect(expected).To(Equal(actual))
}) })
It("gets the flop Kick event signature", func() {
expected := "Kick(uint256,uint256,uint256,address,uint48)"
actual := shared.GetSolidityMethodSignature(shared.FlopperABI, "Kick")
Expect(expected).To(Equal(actual))
})
It("gets the pit frob event signature", func() { It("gets the pit frob event signature", func() {
expected := "Frob(bytes32,bytes32,uint256,uint256,int256,int256,uint256)" expected := "Frob(bytes32,bytes32,uint256,uint256,int256,int256,uint256)"
actual := shared.GetSolidityMethodSignature(shared.PitABI, "Frob") actual := shared.GetSolidityMethodSignature(shared.PitABI, "Frob")

View File

@ -16,7 +16,7 @@ package shared
import "math/big" import "math/big"
func ConvertNilToZeroTimeValue(value *big.Int) int64 { func BigIntToInt64(value *big.Int) int64 {
if value == nil { if value == nil {
return int64(0) return int64(0)
} else { } else {
@ -24,10 +24,11 @@ func ConvertNilToZeroTimeValue(value *big.Int) int64 {
} }
} }
func ConvertNilToEmptyString(value string) string { func BigIntToString(value *big.Int) string {
if value == "<nil>" { result := value.String()
if result == "<nil>" {
return "" return ""
} else { } else {
return value return result
} }
} }

View File

@ -0,0 +1,78 @@
// 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 test_data
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/shared"
"math/big"
"strconv"
"time"
)
var (
FlopKickLog = types.Log{
Address: common.HexToAddress(shared.FlopperContractAddress),
Topics: []common.Hash{
common.HexToHash("0xefa52d9342a199cb30efd2692463f2c2bef63cd7186b50382d4fb94ad207880e"),
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000005"),
},
Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000020000000000000000000000007d7bee5fcfd8028cf7b00876c5b1421c800561a6000000000000000000000000000000000000000000000000000000000002a300"),
BlockNumber: 19,
TxHash: common.HexToHash("0xd8fd67b37a6aa64a3cef4937204765183b180d8dc92eecd0d233f445526d31b5"),
TxIndex: flopTxIndex,
BlockHash: common.HexToHash("0x7891fe725691abc5b94113dc2bfa8c3509e1e1d0e0bb74289e3b996c30d00c8c"),
Index: 32,
Removed: false,
}
flopTxIndex = uint(33)
flopBidId = int64(5)
flopLot = int64(15)
flopBid = int64(2)
flopGal = "0x7d7bEe5fCfD8028cf7b00876C5b1421c800561A6"
rawFlopLogJson, _ = json.Marshal(FlopKickLog)
flopEnd = int64(172800)
FlopKickEntity = flop_kick.Entity{
Id: big.NewInt(flopBidId),
Lot: big.NewInt(flopLot),
Bid: big.NewInt(flopBid),
Gal: common.HexToAddress(flopGal),
End: big.NewInt(flopEnd),
TransactionIndex: flopTxIndex,
Raw: FlopKickLog,
}
FlopKickModel = flop_kick.Model{
BidId: strconv.FormatInt(flopBidId, 10),
Lot: strconv.FormatInt(flopLot, 10),
Bid: strconv.FormatInt(flopBid, 10),
Gal: flopGal,
End: time.Unix(flopEnd, 0),
TransactionIndex: flopTxIndex,
Raw: rawFlopLogJson,
}
)
type FlopKickDBResult struct {
Id int64
HeaderId int64 `db:"header_id"`
flop_kick.Model
}

View File

@ -0,0 +1,51 @@
// 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 flop_kick
import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/test_data"
)
type MockConverter struct {
PassedContractAddress string
PassedContractABI string
PassedLogs []types.Log
PassedEntities []flop_kick.Entity
entityConverterError error
modelConverterError error
}
func (c *MockConverter) ToEntities(contractAddress, contractAbi string, ethLogs []types.Log) ([]flop_kick.Entity, error) {
c.PassedContractAddress = contractAddress
c.PassedContractABI = contractAbi
c.PassedLogs = ethLogs
return []flop_kick.Entity{test_data.FlopKickEntity}, c.entityConverterError
}
func (c *MockConverter) ToModels(entities []flop_kick.Entity) ([]flop_kick.Model, error) {
c.PassedEntities = entities
return []flop_kick.Model{test_data.FlopKickModel}, c.modelConverterError
}
func (c *MockConverter) SetToEntityConverterError(err error) {
c.entityConverterError = err
}
func (c *MockConverter) SetToModelConverterError(err error) {
c.modelConverterError = err
}

View File

@ -0,0 +1,68 @@
// 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 flop_kick
import (
"github.com/vulcanize/vulcanizedb/pkg/core"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
)
type MockRepository struct {
PassedStartingBlockNumber int64
PassedEndingBlockNumber int64
CreatedHeaderIds []int64
CreatedModels []flop_kick.Model
CheckedHeaderIds []int64
missingHeaders []core.Header
missingHeadersError error
createError error
checkedHeaderError error
}
func (r *MockRepository) Create(headerId int64, flopKicks []flop_kick.Model) error {
r.CreatedHeaderIds = append(r.CreatedHeaderIds, headerId)
r.CreatedModels = flopKicks
return r.createError
}
func (r *MockRepository) MarkHeaderChecked(headerId int64) error {
r.CheckedHeaderIds = append(r.CheckedHeaderIds, headerId)
return r.checkedHeaderError
}
func (r *MockRepository) MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error) {
r.PassedStartingBlockNumber = startingBlockNumber
r.PassedEndingBlockNumber = endingBlockNumber
return r.missingHeaders, r.missingHeadersError
}
func (r *MockRepository) SetMissingHeaders(headers []core.Header) {
r.missingHeaders = headers
}
func (r *MockRepository) SetMissingHeadersError(err error) {
r.missingHeadersError = err
}
func (r *MockRepository) SetCreateError(err error) {
r.createError = err
}
func (r *MockRepository) SetCheckedHeaderError(err error) {
r.checkedHeaderError = err
}

View File

@ -24,6 +24,7 @@ import (
"github.com/vulcanize/vulcanizedb/pkg/transformers/drip_file/repo" "github.com/vulcanize/vulcanizedb/pkg/transformers/drip_file/repo"
"github.com/vulcanize/vulcanizedb/pkg/transformers/drip_file/vow" "github.com/vulcanize/vulcanizedb/pkg/transformers/drip_file/vow"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick" "github.com/vulcanize/vulcanizedb/pkg/transformers/flip_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/flop_kick"
"github.com/vulcanize/vulcanizedb/pkg/transformers/frob" "github.com/vulcanize/vulcanizedb/pkg/transformers/frob"
"github.com/vulcanize/vulcanizedb/pkg/transformers/pit_file" "github.com/vulcanize/vulcanizedb/pkg/transformers/pit_file"
"github.com/vulcanize/vulcanizedb/pkg/transformers/pit_file/debt_ceiling" "github.com/vulcanize/vulcanizedb/pkg/transformers/pit_file/debt_ceiling"
@ -45,6 +46,7 @@ var (
DripFileRepoTransformerInitializer = repo.DripFileRepoTransformerInitializer{Config: dripFileConfig}.NewDripFileRepoTransformer DripFileRepoTransformerInitializer = repo.DripFileRepoTransformerInitializer{Config: dripFileConfig}.NewDripFileRepoTransformer
DripFileVowTransfromerInitializer = vow.DripFileVowTransformerInitializer{Config: dripFileConfig}.NewDripFileVowTransformer DripFileVowTransfromerInitializer = vow.DripFileVowTransformerInitializer{Config: dripFileConfig}.NewDripFileVowTransformer
FlipKickTransformerInitializer = flip_kick.FlipKickTransformerInitializer{Config: flip_kick.FlipKickConfig}.NewFlipKickTransformer FlipKickTransformerInitializer = flip_kick.FlipKickTransformerInitializer{Config: flip_kick.FlipKickConfig}.NewFlipKickTransformer
FlopKickTransformerInitializer = flop_kick.FlopKickTransformerInitializer{Config: flop_kick.Config}.NewFlopKickTransformer
FrobTransformerInitializer = frob.FrobTransformerInitializer{Config: frob.FrobConfig}.NewFrobTransformer FrobTransformerInitializer = frob.FrobTransformerInitializer{Config: frob.FrobConfig}.NewFrobTransformer
pitFileConfig = pit_file.PitFileConfig pitFileConfig = pit_file.PitFileConfig
PitFileDebtCeilingTransformerInitializer = debt_ceiling.PitFileDebtCeilingTransformerInitializer{Config: pitFileConfig}.NewPitFileDebtCeilingTransformer PitFileDebtCeilingTransformerInitializer = debt_ceiling.PitFileDebtCeilingTransformerInitializer{Config: pitFileConfig}.NewPitFileDebtCeilingTransformer
@ -62,8 +64,10 @@ func TransformerInitializers() []shared.TransformerInitializer {
DentTransformerInitializer, DentTransformerInitializer,
DripDripTransformerInitializer, DripDripTransformerInitializer,
DripFileIlkTransformerInitializer, DripFileIlkTransformerInitializer,
DripFileVowTransfromerInitializer,
DripFileRepoTransformerInitializer, DripFileRepoTransformerInitializer,
FlipKickTransformerInitializer, FlipKickTransformerInitializer,
FlopKickTransformerInitializer,
FrobTransformerInitializer, FrobTransformerInitializer,
PitFileDebtCeilingTransformerInitializer, PitFileDebtCeilingTransformerInitializer,
PitFileIlkTransformerInitializer, PitFileIlkTransformerInitializer,