ipld-eth-server/pkg/config/contract.go
2019-12-02 13:24:50 -06:00

219 lines
7.6 KiB
Go

// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import (
"strings"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/vulcanize/vulcanizedb/pkg/eth"
)
// Config struct for generic contract transformer
type ContractConfig struct {
// Name for the transformer
Name string
// Ethereum network name; default "" is mainnet
Network string
// List of contract addresses (map to ensure no duplicates)
Addresses map[string]bool
// Map of contract address to abi
// If an address has no associated abi the parser will attempt to fetch one from etherscan
Abis map[string]string
// Map of contract address to slice of events
// Used to set which addresses to watch
// If any events are listed in the slice only those will be watched
// Otherwise all events in the contract ABI are watched
Events map[string][]string
// Map of contract address to slice of methods
// If any methods are listed in the slice only those will be polled
// Otherwise no methods will be polled
Methods map[string][]string
// Map of contract address to slice of event arguments to filter for
// If arguments are provided then only events which emit those arguments are watched
// Otherwise arguments are not filtered on events
EventArgs map[string][]string
// Map of contract address to slice of method arguments to limit polling to
// If arguments are provided then only those arguments are allowed as arguments in method polling
// Otherwise any argument of the right type seen emitted from events at that contract will be used in method polling
MethodArgs map[string][]string
// Map of contract address to their starting block
StartingBlocks map[string]int64
// Map of contract address to whether or not to pipe method polling results forward into subsequent method calls
Piping map[string]bool
}
func (contractConfig *ContractConfig) PrepConfig() {
addrs := viper.GetStringSlice("contract.addresses")
contractConfig.Network = viper.GetString("contract.network")
contractConfig.Addresses = make(map[string]bool, len(addrs))
contractConfig.Abis = make(map[string]string, len(addrs))
contractConfig.Methods = make(map[string][]string, len(addrs))
contractConfig.Events = make(map[string][]string, len(addrs))
contractConfig.MethodArgs = make(map[string][]string, len(addrs))
contractConfig.EventArgs = make(map[string][]string, len(addrs))
contractConfig.StartingBlocks = make(map[string]int64, len(addrs))
contractConfig.Piping = make(map[string]bool, len(addrs))
// De-dupe addresses
for _, addr := range addrs {
contractConfig.Addresses[strings.ToLower(addr)] = true
}
// Iterate over addresses to pull out config info for each contract
for _, addr := range addrs {
transformer := viper.GetStringMap("contract." + addr)
// Get and check abi
var abi string
abiInterface, abiOK := transformer["abi"]
if !abiOK {
log.Warnf("contract %s not configured with an ABI, will attempt to fetch it from Etherscan\r\n", addr)
} else {
abi, abiOK = abiInterface.(string)
if !abiOK {
log.Fatal(addr, "transformer `abi` not of type []string")
}
}
if abi != "" {
if _, abiErr := eth.ParseAbi(abi); abiErr != nil {
log.Fatal(addr, "transformer `abi` not valid JSON")
}
}
contractConfig.Abis[strings.ToLower(addr)] = abi
// Get and check events
events := make([]string, 0)
eventsInterface, eventsOK := transformer["events"]
if !eventsOK {
log.Warnf("contract %s not configured with a list of events to watch, will watch all events\r\n", addr)
events = []string{}
} else {
eventsI, eventsOK := eventsInterface.([]interface{})
if !eventsOK {
log.Fatal(addr, "transformer `events` not of type []string\r\n")
}
for _, strI := range eventsI {
str, strOK := strI.(string)
if !strOK {
log.Fatal(addr, "transformer `events` not of type []string\r\n")
}
events = append(events, str)
}
}
contractConfig.Events[strings.ToLower(addr)] = events
// Get and check methods
methods := make([]string, 0)
methodsInterface, methodsOK := transformer["methods"]
if !methodsOK {
log.Warnf("contract %s not configured with a list of methods to poll, will not poll any methods\r\n", addr)
methods = []string{}
} else {
methodsI, methodsOK := methodsInterface.([]interface{})
if !methodsOK {
log.Fatal(addr, "transformer `methods` not of type []string\r\n")
}
for _, strI := range methodsI {
str, strOK := strI.(string)
if !strOK {
log.Fatal(addr, "transformer `methods` not of type []string\r\n")
}
methods = append(methods, str)
}
}
contractConfig.Methods[strings.ToLower(addr)] = methods
// Get and check eventArgs
eventArgs := make([]string, 0)
eventArgsInterface, eventArgsOK := transformer["eventArgs"]
if !eventArgsOK {
log.Warnf("contract %s not configured with a list of event arguments to filter for, will not filter events for specific emitted values\r\n", addr)
eventArgs = []string{}
} else {
eventArgsI, eventArgsOK := eventArgsInterface.([]interface{})
if !eventArgsOK {
log.Fatal(addr, "transformer `eventArgs` not of type []string\r\n")
}
for _, strI := range eventArgsI {
str, strOK := strI.(string)
if !strOK {
log.Fatal(addr, "transformer `eventArgs` not of type []string\r\n")
}
eventArgs = append(eventArgs, str)
}
}
contractConfig.EventArgs[strings.ToLower(addr)] = eventArgs
// Get and check methodArgs
methodArgs := make([]string, 0)
methodArgsInterface, methodArgsOK := transformer["methodArgs"]
if !methodArgsOK {
log.Warnf("contract %s not configured with a list of method argument values to poll with, will poll methods with all available arguments\r\n", addr)
methodArgs = []string{}
} else {
methodArgsI, methodArgsOK := methodArgsInterface.([]interface{})
if !methodArgsOK {
log.Fatal(addr, "transformer `methodArgs` not of type []string\r\n")
}
for _, strI := range methodArgsI {
str, strOK := strI.(string)
if !strOK {
log.Fatal(addr, "transformer `methodArgs` not of type []string\r\n")
}
methodArgs = append(methodArgs, str)
}
}
contractConfig.MethodArgs[strings.ToLower(addr)] = methodArgs
// Get and check startingBlock
startInterface, startOK := transformer["startingblock"]
if !startOK {
log.Fatal(addr, "transformer config is missing `startingBlock` value\r\n")
}
start, startOK := startInterface.(int64)
if !startOK {
log.Fatal(addr, "transformer `startingBlock` not of type int\r\n")
}
contractConfig.StartingBlocks[strings.ToLower(addr)] = start
// Get pipping
var piping bool
_, pipeOK := transformer["piping"]
if !pipeOK {
log.Warnf("contract %s does not have its `piping` set, by default piping is turned off\r\n", addr)
piping = false
} else {
pipingInterface := transformer["piping"]
piping, pipeOK = pipingInterface.(bool)
if !pipeOK {
log.Fatal(addr, "transformer `piping` not of type bool\r\n")
}
}
contractConfig.Piping[strings.ToLower(addr)] = piping
}
}