Skip to content

Commit

Permalink
contract: add data encode/decode; event decode
Browse files Browse the repository at this point in the history
  • Loading branch information
outprog committed Dec 28, 2020
1 parent 2e75386 commit 432142d
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 2 deletions.
81 changes: 79 additions & 2 deletions contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func NewContract(address common.Address, abiStr, rpc string, wallet *Wallet) (*C
// String "latest" - for the latest mined block
// String "pending" - for the pending state/transactions
func (c *Contract) CallMethod(methodName, tag string, args ...interface{}) (res string, err error) {
data, err := c.ABI.Pack(methodName, args...)
data, err := c.EncodeData(methodName, args...)
if err != nil {
return
}
Expand All @@ -59,7 +59,7 @@ func (c *Contract) ExecMethod(methodName string, opts *TxOpts, args ...interface
return
}

data, err := c.ABI.Pack(methodName, args...)
data, err := c.EncodeData(methodName, args...)
if err != nil {
return
}
Expand All @@ -70,3 +70,80 @@ func (c *Contract) ExecMethod(methodName string, opts *TxOpts, args ...interface
func (c *Contract) GetAddress() string {
return c.Address.String()
}

func (c *Contract) EncodeData(methodName string, args ...interface{}) ([]byte, error) {
return c.ABI.Pack(methodName, args...)
}

func (c *Contract) EncodeDataHex(methodName string, args ...interface{}) (hex string, err error) {
by, err := c.EncodeData(methodName, args...)
if err != nil {
return
}

return hexutil.Encode(by), nil
}

func (c *Contract) DecodeData(data []byte) (methodName string, params map[string]interface{}, err error) {
if len(data) < 4 {
err = errors.New("data is too short")
return
}

method, err := c.ABI.MethodById(data[:4])
if err != nil {
return
}
methodName = method.Name

params = make(map[string]interface{})
err = method.Inputs.UnpackIntoMap(params, data[4:])
return
}

func (c *Contract) DecodeDataHex(dataHex string) (methodName string, params map[string]interface{}, err error) {
data := common.FromHex(dataHex)
return c.DecodeData(data)
}

func (c *Contract) DecodeEvent(topics []common.Hash, data []byte) (eventName string, values map[string]interface{}, err error) {
if len(topics) < 1 {
err = errors.New("no topics found")
return
}

event, err := c.ABI.EventByID(topics[0])
if err != nil {
return
}
eventName = event.Name

values = make(map[string]interface{})
// parse topics
var indexed abi.Arguments
for _, arg := range event.Inputs {
if arg.Indexed {
indexed = append(indexed, arg)
}
}
indexTopics := []common.Hash{}
for _, topic := range topics[1:] {
indexTopics = append(indexTopics, topic)
}
if err = abi.ParseTopicsIntoMap(values, indexed, indexTopics); err != nil {
return
}

// parse data
err = event.Inputs.UnpackIntoMap(values, data)
return
}

func (c *Contract) DecodeEventHex(topicsHex []string, dataHex string) (eventName string, values map[string]interface{}, err error) {

topics := []common.Hash{}
for _, topicHex := range topicsHex {
topics = append(topics, common.HexToHash(topicHex))
}
return c.DecodeEvent(topics, common.FromHex(dataHex))
}
55 changes: 55 additions & 0 deletions contract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package goether

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/assert"
)

func TestDecodeDataHex(t *testing.T) {
abi := `[{"constant": true,"inputs": [{"name": "","type": "address"}],"name": "balanceOf","outputs": [{"name": "","type": "uint256"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "dst","type": "address"},{"name": "wad","type": "uint256"}],"name": "transfer","outputs": [{"name": "","type": "bool"}],"payable": false,"stateMutability": "nonpayable","type": "function"}]`
testContract, err := NewContract(common.HexToAddress("0x0"), abi, "", nil)
if err != nil {
panic(err)
}

inputData, err := testContract.EncodeData(
"transfer",
common.HexToAddress("0xab6c371B6c466BcF14d4003601951e5873dF2AcA"),
big.NewInt(100))
assert.NoError(t, err)

methodName, params, err := testContract.DecodeDataHex(hexutil.Encode(inputData))
assert.NoError(t, err)
assert.Equal(t, "transfer", methodName)
assert.Equal(t, common.HexToAddress("0xab6c371B6c466BcF14d4003601951e5873dF2AcA"), params["dst"])
assert.Equal(t, big.NewInt(100), params["wad"])

_, _, err = testContract.DecodeDataHex("0xa9059c")
assert.Equal(t, "data is too short", err.Error())
}

func TestDecodeEventHex(t *testing.T) {
abi := `[{"anonymous": false,"inputs": [{"indexed": true,"name": "from","type": "address"},{"indexed": true,"name": "to","type": "address"},{"indexed": false,"name": "value","type": "uint256"}],"name": "Transfer","type": "event"}]`
testContract, err := NewContract(common.HexToAddress("0x0"), abi, "", nil)
if err != nil {
panic(err)
}

eventName, values, err := testContract.DecodeEventHex(
[]string{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000a06b79e655db7d7c3b3e7b2cceeb068c3259d0c9",
"0x0000000000000000000000003dd22a3ad30df8acaf12def3b27e085525a98065",
},
"0x0000000000000000000000000000000000000000000000000000000000989680",
)
assert.NoError(t, err)
assert.Equal(t, "Transfer", eventName)
assert.Equal(t, common.HexToAddress("0xa06b79e655db7d7c3b3e7b2cceeb068c3259d0c9"), values["from"])
assert.Equal(t, common.HexToAddress("0x3dd22a3ad30df8acaf12def3b27e085525a98065"), values["to"])
assert.Equal(t, big.NewInt(10000000), values["value"])
}

0 comments on commit 432142d

Please sign in to comment.