Parse ERC20 transfer from tx payload data
This commit is contained in:
parent
7edea80209
commit
2e9f87e39d
@ -30,7 +30,8 @@ var erc20abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"
|
|||||||
{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x54fd4d50"}]`
|
{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x54fd4d50"}]`
|
||||||
|
|
||||||
// doing the parsing/processing without using go-ethereum/accounts/abi library, it is simple to get data from Transfer event
|
// doing the parsing/processing without using go-ethereum/accounts/abi library, it is simple to get data from Transfer event
|
||||||
const erc20EventTransferSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
|
const erc20TransferMethodSignature = "0xa9059cbb"
|
||||||
|
const erc20TransferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
|
||||||
const erc20NameSignature = "0x06fdde03"
|
const erc20NameSignature = "0x06fdde03"
|
||||||
const erc20SymbolSignature = "0x95d89b41"
|
const erc20SymbolSignature = "0x95d89b41"
|
||||||
const erc20DecimalsSignature = "0x313ce567"
|
const erc20DecimalsSignature = "0x313ce567"
|
||||||
@ -49,7 +50,12 @@ var cachedContractsMux sync.Mutex
|
|||||||
|
|
||||||
func addressFromPaddedHex(s string) (string, error) {
|
func addressFromPaddedHex(s string) (string, error) {
|
||||||
var t big.Int
|
var t big.Int
|
||||||
_, ok := t.SetString(s, 0)
|
var ok bool
|
||||||
|
if has0xPrefix(s) {
|
||||||
|
_, ok = t.SetString(s[2:], 16)
|
||||||
|
} else {
|
||||||
|
_, ok = t.SetString(s, 16)
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("Data is not a number")
|
return "", errors.New("Data is not a number")
|
||||||
}
|
}
|
||||||
@ -60,7 +66,7 @@ func addressFromPaddedHex(s string) (string, error) {
|
|||||||
func erc20GetTransfersFromLog(logs []*rpcLog) ([]Erc20Transfer, error) {
|
func erc20GetTransfersFromLog(logs []*rpcLog) ([]Erc20Transfer, error) {
|
||||||
var r []Erc20Transfer
|
var r []Erc20Transfer
|
||||||
for _, l := range logs {
|
for _, l := range logs {
|
||||||
if len(l.Topics) == 3 && l.Topics[0] == erc20EventTransferSignature {
|
if len(l.Topics) == 3 && l.Topics[0] == erc20TransferEventSignature {
|
||||||
var t big.Int
|
var t big.Int
|
||||||
_, ok := t.SetString(l.Data, 0)
|
_, ok := t.SetString(l.Data, 0)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -85,6 +91,28 @@ func erc20GetTransfersFromLog(logs []*rpcLog) ([]Erc20Transfer, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func erc20GetTransfersFromTx(tx *rpcTransaction) ([]Erc20Transfer, error) {
|
||||||
|
var r []Erc20Transfer
|
||||||
|
if len(tx.Payload) == 128+len(erc20TransferMethodSignature) && strings.HasPrefix(tx.Payload, erc20TransferMethodSignature) {
|
||||||
|
to, err := addressFromPaddedHex(tx.Payload[len(erc20TransferMethodSignature) : 64+len(erc20TransferMethodSignature)])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var t big.Int
|
||||||
|
_, ok := t.SetString(tx.Payload[len(erc20TransferMethodSignature)+64:], 16)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Data is not a number")
|
||||||
|
}
|
||||||
|
r = append(r, Erc20Transfer{
|
||||||
|
Contract: strings.ToLower(tx.To),
|
||||||
|
From: strings.ToLower(tx.From),
|
||||||
|
To: strings.ToLower(to),
|
||||||
|
Tokens: t,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthereumRPC) ethCall(data, to string) (string, error) {
|
func (b *EthereumRPC) ethCall(data, to string) (string, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"blockbook/tests/dbtestdata"
|
||||||
fmt "fmt"
|
fmt "fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
@ -137,3 +138,45 @@ func TestErc20_parseErc20StringProperty(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestErc20_erc20GetTransfersFromTx(t *testing.T) {
|
||||||
|
p := NewEthereumParser(1)
|
||||||
|
b := dbtestdata.GetTestEthereumTypeBlock1(p)
|
||||||
|
bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16)
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args *rpcTransaction
|
||||||
|
want []Erc20Transfer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "0",
|
||||||
|
args: (b.Txs[0].CoinSpecificData.(completeTransaction)).Tx,
|
||||||
|
want: []Erc20Transfer{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1",
|
||||||
|
args: (b.Txs[1].CoinSpecificData.(completeTransaction)).Tx,
|
||||||
|
want: []Erc20Transfer{
|
||||||
|
{
|
||||||
|
Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2",
|
||||||
|
From: "0x20cd153de35d469ba46127a0c8f18626b59a256a",
|
||||||
|
To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f",
|
||||||
|
Tokens: *bn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := erc20GetTransfersFromTx(tt.args)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("erc20GetTransfersFromTx error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// the addresses could have different case
|
||||||
|
if strings.ToLower(fmt.Sprint(got)) != strings.ToLower(fmt.Sprint(tt.want)) {
|
||||||
|
t.Errorf("erc20GetTransfersFromTx = %+v, want %+v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -403,8 +403,12 @@ func GetErc20FromTx(tx *bchain.Tx) ([]Erc20Transfer, error) {
|
|||||||
var r []Erc20Transfer
|
var r []Erc20Transfer
|
||||||
var err error
|
var err error
|
||||||
csd, ok := tx.CoinSpecificData.(completeTransaction)
|
csd, ok := tx.CoinSpecificData.(completeTransaction)
|
||||||
if ok && csd.Receipt != nil {
|
if ok {
|
||||||
r, err = erc20GetTransfersFromLog(csd.Receipt.Logs)
|
if csd.Receipt != nil {
|
||||||
|
r, err = erc20GetTransfersFromLog(csd.Receipt.Logs)
|
||||||
|
} else {
|
||||||
|
r, err = erc20GetTransfersFromTx(csd.Tx)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -430,7 +430,7 @@ func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*
|
|||||||
err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{
|
err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{
|
||||||
"fromBlock": blockNumber,
|
"fromBlock": blockNumber,
|
||||||
"toBlock": blockNumber,
|
"toBlock": blockNumber,
|
||||||
"topics": []string{erc20EventTransferSignature},
|
"topics": []string{erc20TransferEventSignature},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotatef(err, "blockNumber %v", blockNumber)
|
return nil, errors.Annotatef(err, "blockNumber %v", blockNumber)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user