Skip to content

Commit

Permalink
update abigen
Browse files Browse the repository at this point in the history
  • Loading branch information
wincenteam committed Jul 29, 2020
1 parent 7350e93 commit db8b64b
Show file tree
Hide file tree
Showing 19 changed files with 1,384 additions and 568 deletions.
130 changes: 106 additions & 24 deletions accounts/abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import (
"fmt"
"io"

"github.com/pkg/errors"
"github.com/sero-cash/go-czero-import/c_type"
"github.com/sero-cash/go-sero/common"
"github.com/sero-cash/go-sero/crypto"
)

// The ABI holds information about a contract's context and available
Expand All @@ -32,6 +35,12 @@ type ABI struct {
Constructor Method
Methods map[string]Method
Events map[string]Event

// Additional "special" functions introduced in solidity v0.6.0.
// It's separated from the original default fallback. Each contract
// can only define one fallback and receive function.
Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
Receive Method
}

// JSON returns a parsed ABI interface and error if it failed.
Expand Down Expand Up @@ -101,7 +110,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return nil, err
}
// Pack up the method ID too if not a constructor and return
return append(method.Id(), arguments...), nil
return append(method.ID, arguments...), nil
}

// Unpack output in v according to the abi specification
Expand Down Expand Up @@ -144,53 +153,126 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
// UnmarshalJSON implements json.Unmarshaler interface
func (abi *ABI) UnmarshalJSON(data []byte) error {
var fields []struct {
Type string
Name string
Constant bool
Type string
Name string
Inputs []Argument
Outputs []Argument

// Status indicator which can be: "pure", "view",
// "nonpayable" or "payable".
StateMutability string

// Deprecated Status indicators, but removed in v0.6.0.
Constant bool // True if function is either pure or view
Payable bool // True if function is payable

// Event relevant indicator represents the event is
// declared as anonymous.
Anonymous bool
Inputs []Argument
Outputs []Argument
}

if err := json.Unmarshal(data, &fields); err != nil {
return err
}

abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event)
for _, field := range fields {
switch field.Type {
case "constructor":
abi.Constructor = Method{
Inputs: field.Inputs,
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
case "function":
name := abi.overloadedMethodName(field.Name)
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
case "fallback":
// New introduced function type in v0.6.0, check more detail
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
if abi.HasFallback() {
return errors.New("only single fallback is allowed")
}
// empty defaults to function according to the abi spec
case "function", "":
abi.Methods[field.Name] = Method{
Name: field.Name,
Const: field.Constant,
Inputs: field.Inputs,
Outputs: field.Outputs,
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "receive":
// New introduced function type in v0.6.0, check more detail
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
if abi.HasReceive() {
return errors.New("only single receive is allowed")
}
case "event":
abi.Events[field.Name] = Event{
Name: field.Name,
Anonymous: field.Anonymous,
Inputs: field.Inputs,
if field.StateMutability != "payable" {
return errors.New("the statemutability of receive can only be payable")
}
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "event":
name := abi.overloadedEventName(field.Name)
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
default:
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
}
}

return nil
}

// overloadedMethodName returns the next available name for a given function.
// Needed since solidity allows for function overload.
//
// e.g. if the abi contains Methods send, send1
// overloadedMethodName would return send2 for input send.
func (abi *ABI) overloadedMethodName(rawName string) string {
name := rawName
_, ok := abi.Methods[name]
for idx := 0; ok; idx++ {
name = fmt.Sprintf("%s%d", rawName, idx)
_, ok = abi.Methods[name]
}
return name
}

// overloadedEventName returns the next available name for a given event.
// Needed since solidity allows for event overload.
//
// e.g. if the abi contains events received, received1
// overloadedEventName would return received2 for input received.
func (abi *ABI) overloadedEventName(rawName string) string {
name := rawName
_, ok := abi.Events[name]
for idx := 0; ok; idx++ {
name = fmt.Sprintf("%s%d", rawName, idx)
_, ok = abi.Events[name]
}
return name
}

// MethodById looks up a method by the 4-byte id
// returns nil if none found
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
if len(sigdata) < 4 {
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
}
for _, method := range abi.Methods {
if bytes.Equal(method.Id(), sigdata[:4]) {
if bytes.Equal(method.ID, sigdata[:4]) {
return &method, nil
}
}
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
}

// EventByID looks an event up by its topic hash in the
// ABI and returns nil if none found.
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
for _, event := range abi.Events {
if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
return &event, nil
}
}
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
}

// HasFallback returns an indicator whether a fallback function is included.
func (abi *ABI) HasFallback() bool {
return abi.Fallback.Type == Fallback
}

// HasReceive returns an indicator whether a receive function is included.
func (abi *ABI) HasReceive() bool {
return abi.Receive.Type == Receive
}

// revertSelector is a special function selector for revert reason unpacking.
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
130 changes: 61 additions & 69 deletions accounts/abi/argument.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,28 @@ type Argument struct {

type Arguments []Argument

type ArgumentMarshaling struct {
Name string
Type string
InternalType string
Components []ArgumentMarshaling
Indexed bool
}

// UnmarshalJSON implements json.Unmarshaler interface
func (argument *Argument) UnmarshalJSON(data []byte) error {
var extarg struct {
Name string
Type string
Indexed bool
}
err := json.Unmarshal(data, &extarg)
var arg ArgumentMarshaling
err := json.Unmarshal(data, &arg)
if err != nil {
return fmt.Errorf("argument json err: %v", err)
}

argument.Type, err = NewType(extarg.Type)
argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components)
if err != nil {
return err
}
argument.Name = extarg.Name
argument.Indexed = extarg.Indexed
argument.Name = arg.Name
argument.Indexed = arg.Indexed

return nil
}
Expand Down Expand Up @@ -99,10 +103,15 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if err != nil {
return err
}

if len(marshalledValues) == 0 {
return fmt.Errorf("abi: Unpack(no-values unmarshalled %T)", v)
}

if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues)
}
return arguments.unpackAtomic(v, marshalledValues)
return arguments.unpackAtomic(v, marshalledValues[0])
}

// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
Expand All @@ -128,83 +137,55 @@ func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledVal
return nil
}

// unpackTuple unpacks ( hexdata -> go ) a batch of values.
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {

var (
value = reflect.ValueOf(v).Elem()
typ = value.Type()
kind = value.Kind()
)

if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
return err
}

// If the interface is a struct, get of abi->struct_field mapping

var abi2struct map[string]string
if kind == reflect.Struct {
value := reflect.ValueOf(v).Elem()
nonIndexedArgs := arguments.NonIndexed()

switch value.Kind() {
case reflect.Struct:
argNames := make([]string, len(nonIndexedArgs))
for i, arg := range nonIndexedArgs {
argNames[i] = arg.Name
}
var err error
abi2struct, err = mapAbiToStructFields(arguments, value)
abi2struct, err := mapArgNamesToStructFields(argNames, value)
if err != nil {
return err
}
}
for i, arg := range arguments.NonIndexed() {

reflectValue := reflect.ValueOf(marshalledValues[i])

switch kind {
case reflect.Struct:
if structField, ok := abi2struct[arg.Name]; ok {
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil {
return err
}
for i, arg := range nonIndexedArgs {
field := value.FieldByName(abi2struct[arg.Name])
if !field.IsValid() {
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
}
case reflect.Slice, reflect.Array:
if value.Len() < i {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
}
v := value.Index(i)
if err := requireAssignable(v, reflectValue); err != nil {
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
return err
}

if err := set(v.Elem(), reflectValue, arg); err != nil {
}
case reflect.Slice, reflect.Array:
if value.Len() < len(marshalledValues) {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
}
for i := range nonIndexedArgs {
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
return err
}
default:
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
}
default:
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
}
return nil
}

// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
if len(marshalledValues) != 1 {
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
}

elem := reflect.ValueOf(v).Elem()
kind := elem.Kind()
reflectValue := reflect.ValueOf(marshalledValues[0])
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
dst := reflect.ValueOf(v).Elem()
src := reflect.ValueOf(marshalledValues)

var abi2struct map[string]string
if kind == reflect.Struct {
var err error
if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
return err
}
arg := arguments.NonIndexed()[0]
if structField, ok := abi2struct[arg.Name]; ok {
return set(elem.FieldByName(structField), reflectValue, arg)
}
return nil
if dst.Kind() == reflect.Struct && src.Kind() != reflect.Struct {
return set(dst.Field(0), src)
}

return set(elem, reflectValue, arguments.NonIndexed()[0])

return set(dst, src)
}

// Computes the full size of an array;
Expand Down Expand Up @@ -341,3 +322,14 @@ func capitalise(input string) string {
}
return strings.ToUpper(input[:1]) + input[1:]
}

// ToCamelCase converts an under-score string to a camel-case string
func ToCamelCase(input string) string {
parts := strings.Split(input, "_")
for i, s := range parts {
if len(s) > 0 {
parts[i] = strings.ToUpper(s[:1]) + s[1:]
}
}
return strings.Join(parts, "")
}
2 changes: 1 addition & 1 deletion accounts/abi/bind/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (
"errors"
"math/big"

sero "github.com/sero-cash/go-sero"
"github.com/sero-cash/go-sero/zero/txtool"

sero "github.com/sero-cash/go-sero"
"github.com/sero-cash/go-sero/common"
"github.com/sero-cash/go-sero/core/types"
)
Expand Down
4 changes: 2 additions & 2 deletions accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
opts = new(FilterOpts)
}
// Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)

topics, err := makeTopics(query...)
if err != nil {
Expand Down Expand Up @@ -318,7 +318,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
opts = new(WatchOpts)
}
// Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)

topics, err := makeTopics(query...)
if err != nil {
Expand Down
Loading

0 comments on commit db8b64b

Please sign in to comment.