diff --git a/api/types.go b/api/types.go
index dc6644d0..545e58ca 100644
--- a/api/types.go
+++ b/api/types.go
@@ -207,6 +207,7 @@ type EthereumSpecific struct {
GasUsed *big.Int `json:"gasUsed"`
GasPrice *Amount `json:"gasPrice"`
Data string `json:"data,omitempty"`
+ ParsedData *bchain.EthereumParsedInputData `json:"parsedData,omitempty"`
InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"`
}
diff --git a/api/worker.go b/api/worker.go
index c5f2e054..a2ec91ee 100644
--- a/api/worker.go
+++ b/api/worker.go
@@ -127,6 +127,23 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
return w.GetTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON)
}
+func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedInputData {
+ var err error
+ var signatures *[]bchain.FourByteSignature
+ fourBytes := eth.GetSignatureFromData(data)
+ if fourBytes != 0 {
+ signatures, err = w.db.GetFourByteSignatures(fourBytes)
+ if err != nil {
+ glog.Errorf("GetFourByteSignatures(%v) error %v", fourBytes, err)
+ return nil
+ }
+ if signatures == nil {
+ return nil
+ }
+ }
+ return eth.ParseInputData(signatures, data)
+}
+
// GetTransactionFromBchainTx reads transaction data from txid
func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool) (*Tx, error) {
var err error
@@ -270,6 +287,8 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
}
}
+ parsedInputData := w.getParsedEthereumInputData(ethTxData.Data)
+
// mempool txs do not have fees yet
if ethTxData.GasUsed != nil {
feesSat.Mul(ethTxData.GasPrice, ethTxData.GasUsed)
@@ -278,12 +297,13 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
valOutSat = bchainTx.Vout[0].ValueSat
}
ethSpecific = &EthereumSpecific{
- GasLimit: ethTxData.GasLimit,
- GasPrice: (*Amount)(ethTxData.GasPrice),
- GasUsed: ethTxData.GasUsed,
- Nonce: ethTxData.Nonce,
- Status: ethTxData.Status,
- Data: ethTxData.Data,
+ GasLimit: ethTxData.GasLimit,
+ GasPrice: (*Amount)(ethTxData.GasPrice),
+ GasUsed: ethTxData.GasUsed,
+ Nonce: ethTxData.Nonce,
+ Status: ethTxData.Status,
+ Data: ethTxData.Data,
+ ParsedData: parsedInputData,
}
if internalData != nil {
ethSpecific.Type = internalData.Type
@@ -674,7 +694,6 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
}
t := Token{
- Type: ERC20TokenType,
Contract: ci.Contract,
Name: ci.Name,
Symbol: ci.Symbol,
@@ -685,6 +704,7 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
// return contract balances/values only at or above AccountDetailsTokenBalances
if details >= AccountDetailsTokenBalances && validContract {
if c.Type == bchain.ERC20 {
+ t.Type = ERC20TokenType
// get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct
b, err := w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract)
if err != nil {
@@ -694,15 +714,20 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
t.BalanceSat = (*Amount)(b)
}
} else {
- if len(t.Ids) > 0 {
- ids := make([]Amount, len(t.Ids))
+ if c.Type == bchain.ERC721 {
+ t.Type = ERC771TokenType
+ } else {
+ t.Type = ERC1155TokenType
+ }
+ if len(c.Ids) > 0 {
+ ids := make([]Amount, len(c.Ids))
for j := range ids {
ids[j] = (Amount)(c.Ids[j])
}
t.Ids = ids
}
- if len(t.IdValues) > 0 {
- idValues := make([]TokenTransferValues, len(t.IdValues))
+ if len(c.IdValues) > 0 {
+ idValues := make([]TokenTransferValues, len(c.IdValues))
for j := range idValues {
idValues[j].Id = (*Amount)(&c.IdValues[j].Id)
idValues[j].Value = (*Amount)(&c.IdValues[j].Value)
diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go
index 55e632c3..88043a96 100644
--- a/bchain/coins/eth/dataparser.go
+++ b/bchain/coins/eth/dataparser.go
@@ -4,8 +4,15 @@ import (
"bytes"
"encoding/hex"
"math/big"
+ "runtime/debug"
+ "strconv"
+ "strings"
"unicode"
"unicode/utf8"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/golang/glog"
+ "github.com/trezor/blockbook/bchain"
)
func parseSimpleNumericProperty(data string) *big.Int {
@@ -58,15 +65,230 @@ func parseSimpleStringProperty(data string) string {
return ""
}
-func Decamel(s string) string {
+func decamel(s string) string {
var b bytes.Buffer
splittable := false
- for _, v := range s {
- if splittable && unicode.IsUpper(v) {
- b.WriteByte(' ')
+ for i, v := range s {
+ if i == 0 {
+ b.WriteRune(unicode.ToUpper(v))
+ } else {
+ if splittable && unicode.IsUpper(v) {
+ b.WriteByte(' ')
+ }
+ b.WriteRune(v)
+ splittable = unicode.IsLower(v) || unicode.IsNumber(v)
}
- b.WriteRune(v)
- splittable = unicode.IsLower(v) || unicode.IsNumber(v)
}
return b.String()
}
+
+func GetSignatureFromData(data string) uint32 {
+ if has0xPrefix(data) {
+ data = data[2:]
+ }
+ if len(data) < 8 {
+ return 0
+ }
+ sig, err := strconv.ParseUint(data[:8], 16, 32)
+ if err != nil {
+ return 0
+ }
+ return uint32(sig)
+}
+
+const ErrorTy byte = 255
+
+func processParam(data string, index int, t *abi.Type, processed []bool) ([]string, int, bool) {
+ var retval []string
+ d := index << 6
+ if d+64 > len(data) {
+ return nil, 0, false
+ }
+ block := data[d : d+64]
+ switch t.T {
+ // static types
+ case abi.IntTy, abi.UintTy, abi.BoolTy:
+ var n big.Int
+ _, ok := n.SetString(block, 16)
+ if !ok {
+ return nil, 0, false
+ }
+ if t.T == abi.BoolTy {
+ if n.Int64() != 0 {
+ retval = []string{"true"}
+ } else {
+ retval = []string{"false"}
+ }
+ } else {
+ retval = []string{n.String()}
+ }
+ processed[index] = true
+ index++
+ case abi.AddressTy:
+ b, err := hex.DecodeString(block[24:])
+ if err != nil {
+ return nil, 0, false
+ }
+ retval = []string{EIP55Address(b)}
+ processed[index] = true
+ index++
+ case abi.FixedBytesTy:
+ retval = []string{"0x" + block[:t.Size<<1]}
+ processed[index] = true
+ index++
+ case abi.ArrayTy:
+ for i := 0; i < t.Size; i++ {
+ var r []string
+ var ok bool
+ r, index, ok = processParam(data, index, t.Elem, processed)
+ if !ok {
+ return nil, 0, false
+ }
+ retval = append(retval, r...)
+ }
+ // dynamic types
+ case abi.StringTy, abi.BytesTy, abi.SliceTy:
+ // get offset of dynamic type
+ offset, err := strconv.ParseInt(block, 16, 64)
+ if err != nil {
+ return nil, 0, false
+ }
+ processed[index] = true
+ index++
+ offset <<= 1
+ d = int(offset)
+ dynIndex := d >> 6
+ if d+64 > len(data) || d < 0 {
+ return nil, 0, false
+ }
+ // get element count of dynamic type
+ c, err := strconv.ParseInt(data[d:d+64], 16, 64)
+ count := int(c)
+ if err != nil {
+ return nil, 0, false
+ }
+ processed[dynIndex] = true
+ dynIndex++
+ if t.T == abi.StringTy || t.T == abi.BytesTy {
+ d += 64
+ de := d + (count << 1)
+ if de > len(data) {
+ return nil, 0, false
+ }
+ if count == 0 {
+ retval = []string{""}
+ } else {
+ block = data[d:de]
+ if t.T == abi.StringTy {
+ b, err := hex.DecodeString(block)
+ if err != nil {
+ return nil, 0, false
+ }
+ retval = []string{string(b)}
+ } else {
+ retval = []string{"0x" + block}
+ }
+ count = ((count - 1) >> 5) + 1
+ for i := 0; i < count; i++ {
+ processed[dynIndex] = true
+ dynIndex++
+ }
+ }
+ } else {
+ for i := 0; i < count; i++ {
+ var r []string
+ var ok bool
+ r, dynIndex, ok = processParam(data, dynIndex, t.Elem, processed)
+ if !ok {
+ return nil, 0, false
+ }
+ retval = append(retval, r...)
+ }
+ }
+ // types not processed
+ case abi.HashTy, abi.FixedPointTy, abi.FunctionTy, abi.TupleTy:
+ fallthrough
+ default:
+ return nil, 0, false
+ }
+ return retval, index, true
+}
+
+func tryParseParams(data string, params []string, parsedParams []abi.Type) []bchain.EthereumParsedInputParam {
+ processed := make([]bool, len(data)/64)
+ parsed := make([]bchain.EthereumParsedInputParam, len(params))
+ index := 0
+ var values []string
+ var ok bool
+ for i := range params {
+ t := &parsedParams[i]
+ values, index, ok = processParam(data, index, t, processed)
+ if !ok {
+ return nil
+ }
+ parsed[i] = bchain.EthereumParsedInputParam{Type: params[i], Values: values}
+ }
+ // all data must be processed, otherwise wrong signature
+ for _, p := range processed {
+ if !p {
+ return nil
+ }
+ }
+ return parsed
+}
+
+// ParseInputData tries to parse transaction input data from known FourByteSignatures
+// as there may be multiple signatures for the same four bytes, it tries to match the input to the known parameters
+// it does not parse tuples for now
+func ParseInputData(signatures *[]bchain.FourByteSignature, data string) *bchain.EthereumParsedInputData {
+ if len(data) <= 2 { // data is empty or 0x
+ return &bchain.EthereumParsedInputData{Name: "Transfer"}
+ }
+ if len(data) < 10 || (len(data)-10)%64 != 0 {
+ return nil
+ }
+ parsed := bchain.EthereumParsedInputData{
+ MethodId: data[:10],
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ glog.Error("ParseInputData recovered from panic: ", r, ", ", data, ",signatures ", signatures)
+ debug.PrintStack()
+ }
+ }()
+ if signatures != nil {
+ data = data[10:]
+ for i := range *signatures {
+ s := &(*signatures)[i]
+ // if not yet done, set DecamelName and Function and parse parameter types from string to abi.Type
+ // the signatures are stored in cache
+ if s.DecamelName == "" {
+ s.DecamelName = decamel(s.Name)
+ s.Function = s.Name + "(" + strings.Join(s.Parameters, ", ") + ")"
+ s.ParsedParameters = make([]abi.Type, len(s.Parameters))
+ for j := range s.Parameters {
+ var t abi.Type
+ if len(s.Parameters[j]) > 0 && s.Parameters[j][0] == '(' {
+ // Tuple type is not supported for now
+ t = abi.Type{T: abi.TupleTy}
+ } else {
+ var err error
+ t, err = abi.NewType(s.Parameters[j], "", nil)
+ if err != nil {
+ t = abi.Type{T: ErrorTy}
+ }
+ }
+ s.ParsedParameters[j] = t
+ }
+ }
+ parsedParams := tryParseParams(data, s.Parameters, s.ParsedParameters)
+ if parsedParams != nil {
+ parsed.Name = s.DecamelName
+ parsed.Function = s.Function
+ parsed.Params = parsedParams
+ break
+ }
+ }
+ }
+ return &parsed
+}
diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go
index 9af84c1f..234dd8cd 100644
--- a/bchain/coins/eth/dataparser_test.go
+++ b/bchain/coins/eth/dataparser_test.go
@@ -2,7 +2,12 @@
package eth
-import "testing"
+import (
+ "reflect"
+ "testing"
+
+ "github.com/trezor/blockbook/bchain"
+)
func Test_parseSimpleStringProperty(t *testing.T) {
tests := []struct {
@@ -51,3 +56,314 @@ func Test_parseSimpleStringProperty(t *testing.T) {
})
}
}
+
+func TestGetSignatureFromData(t *testing.T) {
+ tests := []struct {
+ name string
+ data string
+ want uint32
+ }{
+ {
+ name: "0x9e53a69a",
+ data: "0x9e53a69a000000000000000000000000000000000000000000000",
+ want: 2656282266,
+ },
+ {
+ name: "9e53a69b",
+ data: "9e53a69b000000000000000000000000000000000000000000000",
+ want: 2656282267,
+ },
+ {
+ name: "0x9e53 short",
+ data: "0x9e53",
+ want: 0,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := GetSignatureFromData(tt.data); got != tt.want {
+ t.Errorf("GetSignatureFromData() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestParseInputData(t *testing.T) {
+ signatures := []bchain.FourByteSignature{
+ {
+ Name: "mintFighter",
+ Parameters: []string{},
+ },
+ {
+ Name: "cancelMultipleMakerOrders",
+ Parameters: []string{"uint256[]"},
+ },
+ {
+ Name: "mockRegisterFact",
+ Parameters: []string{"bytes32"},
+ },
+ {
+ Name: "vestingDeposits",
+ Parameters: []string{"address"},
+ },
+ {
+ Name: "addLiquidityETH",
+ Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "uint256"},
+ },
+ {
+ Name: "spread",
+ Parameters: []string{"uint256", "address[]"},
+ },
+ {
+ Name: "registerWithConfig",
+ Parameters: []string{"string", "address", "uint256", "bytes32", "address", "address"},
+ },
+ {
+ Name: "atomicMatch_",
+ Parameters: []string{"address[14]", "uint256[18]", "uint8[8]", "bytes", "bytes", "bytes", "bytes", "bytes", "bytes", "uint8[2]", "bytes32[5]"},
+ },
+ {
+ Name: "transmitAndSellTokenForEth",
+ Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "(uint8,bytes32,bytes32)", "bytes"},
+ },
+ }
+ tests := []struct {
+ name string
+ signatures *[]bchain.FourByteSignature
+ data string
+ want *bchain.EthereumParsedInputData
+ wantErr bool
+ }{
+ {
+ name: "transfer",
+ signatures: &signatures,
+ data: "",
+ want: &bchain.EthereumParsedInputData{
+ Name: "Transfer",
+ },
+ },
+ {
+ name: "mintFighter",
+ signatures: &signatures,
+ data: "0xa19b9082",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xa19b9082",
+ Name: "Mint Fighter",
+ Function: "mintFighter()",
+ Params: []bchain.EthereumParsedInputParam{},
+ },
+ },
+ {
+ name: "mockRegisterFact",
+ signatures: &signatures,
+ data: "0xf69507abdc8fa8fe57a22de66a1d5898496c524068cb04c31f72497b3ac9f3b449e58725",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xf69507ab",
+ Name: "Mock Register Fact",
+ Function: "mockRegisterFact(bytes32)",
+ Params: []bchain.EthereumParsedInputParam{
+ {
+ Type: "bytes32",
+ Values: []string{"0xdc8fa8fe57a22de66a1d5898496c524068cb04c31f72497b3ac9f3b449e58725"},
+ },
+ },
+ },
+ },
+ {
+ name: "cancelMultipleMakerOrders",
+ signatures: &signatures,
+ data: "0x9e53a69a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000017f62f8db30",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0x9e53a69a",
+ Name: "Cancel Multiple Maker Orders",
+ Function: "cancelMultipleMakerOrders(uint256[])",
+ Params: []bchain.EthereumParsedInputParam{
+ {
+ Type: "uint256[]",
+ Values: []string{"1646632950576"},
+ },
+ },
+ },
+ },
+ {
+ name: "addLiquidityETH",
+ signatures: &signatures,
+ data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f400000000000000000000000000000000000000000000000000000000623f56f8",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xf305d719",
+ Name: "Add Liquidity ETH",
+ Function: "addLiquidityETH(address, uint256, uint256, uint256, address, uint256)",
+ Params: []bchain.EthereumParsedInputParam{
+ {
+ Type: "address",
+ Values: []string{"0xB80e5AaA2131c07568128f68b8538eD3C8951234"},
+ },
+ {
+ Type: "uint256",
+ Values: []string{"10000000000000000000000000000000"},
+ },
+ {
+ Type: "uint256",
+ Values: []string{"10000000000000000000000000000000"},
+ },
+ {
+ Type: "uint256",
+ Values: []string{"1000000000000000000"},
+ },
+ {
+ Type: "address",
+ Values: []string{"0x9f64B014CA26F2DeF573246543DD1115b229e4F4"},
+ },
+ {
+ Type: "uint256",
+ Values: []string{"1648318200"},
+ },
+ },
+ },
+ },
+ {
+ name: "addLiquidityETH data don't match - too long",
+ signatures: &signatures,
+ data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f400000000000000000000000000000000000000000000000000000000623f56f800000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xf305d719",
+ },
+ },
+ {
+ name: "addLiquidityETH data don't match - too short",
+ signatures: &signatures,
+ data: "0xf305d719000000000000000000000000b80e5aaa2131c07568128f68b8538ed3c8951234000000000000000000000000000000000000007e37be2022c0914b2680000000000000000000000000000000000000000000007e37be2022c0914b26800000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000009f64b014ca26f2def573246543dd1115b229e4f4",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xf305d719",
+ },
+ },
+ {
+ name: "spread",
+ signatures: &signatures,
+ data: "0xcd51b093000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000048c999d9206fcf2a0ecde10049de6dc2d1704bb2000000000000000000000000d2dae6b2309ada5d4c983b4c7d2c942452adc759",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xcd51b093",
+ Name: "Spread",
+ Function: "spread(uint256, address[])",
+ Params: []bchain.EthereumParsedInputParam{
+ {
+ Type: "uint256",
+ Values: []string{"100000000000000000"},
+ },
+ {
+ Type: "address[]",
+ Values: []string{"0x48c999d9206fcf2A0ecdE10049de6Dc2d1704Bb2", "0xD2DAE6B2309aDa5d4c983B4c7D2c942452aDC759"},
+ },
+ },
+ },
+ },
+ {
+ name: "atomicMatch_", // mainnet tx 0x57aff22b0f812e05467fb73caec8ac0364a535382496e5f64eb9df9fb32bd85f
+ signatures: &signatures,
+ data: "0xab834bab0000000000000000000000007f268357a8c2552623316e2562d90e642bb538e50000000000000000000000001676b0ab0aeb83122c58abc3d6a50b6c4a9d376300000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f268357a8c2552623316e2562d90e642bb538e500000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b3256965e7c3cf26e11fcaf296dfc8807c01073000000000000000000000000baf2127b49fc93cbca6269fade0f7f31df4c88a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062531f6400000000000000000000000000000000000000000000000000000000000000000227db897c05fe6409bc72c6bee932b99a92ca45e155cf85e763424e7a3ee61500000000000000000000000000000000000000000000000000000000000002ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000625313f800000000000000000000000000000000000000000000000000000000627aa14b79166058af7dd96e2190730f926c56d6131af9d72b4dd2138b58c30e268c7f300000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000007c000000000000000000000000000000000000000000000000000000000000008e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b200000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c77e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd8960513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b10477e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd8960513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a59500000000000000000000000000000000000000000000000000000000000000000000000000000000000000001676b0ab0aeb83122c58abc3d6a50b6c4a9d3763000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4fb16a59500000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e400000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xab834bab",
+ Name: "Atomic Match_",
+ Function: "atomicMatch_(address[14], uint256[18], uint8[8], bytes, bytes, bytes, bytes, bytes, bytes, uint8[2], bytes32[5])",
+ Params: []bchain.EthereumParsedInputParam{
+ {
+ Type: "address[14]",
+ Values: []string{
+ "0x7f268357A8c2552623316e2562D90e642bB538E5", "0x1676b0AB0Aeb83122C58ABC3d6a50B6c4A9d3763", "0x24C57FBB5c260EDf158583818177Cfd5C2dec470", "0x0000000000000000000000000000000000000000",
+ "0xBAf2127B49fC93CbcA6269FAdE0F7F31dF4c88a7", "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", "0x7f268357A8c2552623316e2562D90e642bB538E5",
+ "0x24C57FBB5c260EDf158583818177Cfd5C2dec470", "0x0000000000000000000000000000000000000000", "0x5b3256965e7C3cF26E11FCAf296DfC8807C01073", "0xBAf2127B49fC93CbcA6269FAdE0F7F31dF4c88a7",
+ "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000"},
+ },
+ {
+ Type: "uint256[18]",
+ Values: []string{
+ "750", "0", "0", "0", "10000000000000000", "0", "1649614692", "0", "975047921716720136517384107537725863826800092678142650456874303300963329557",
+ "750", "0", "0", "0", "10000000000000000", "0", "1649611768", "1652203851", "54769390272606378508076535204478407261307419838517394120712398796227861053232"},
+ },
+ {
+ Type: "uint8[8]",
+ Values: []string{"1", "0", "0", "1", "1", "1", "0", "1"},
+ },
+ {
+ Type: "bytes",
+ Values: []string{"0xfb16a59500000000000000000000000000000000000000000000000000000000000000000000000000000000000000001676b0ab0aeb83122c58abc3d6a50b6c4a9d3763000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"},
+ },
+ {
+ Type: "bytes",
+ Values: []string{"0xfb16a59500000000000000000000000024c57fbb5c260edf158583818177cfd5c2dec4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f25f4f4f6517101dc947d1c0370571ebdd25f14a00000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"},
+ },
+ {
+ Type: "bytes",
+ Values: []string{"0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+ },
+ {
+ Type: "bytes",
+ Values: []string{"0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+ },
+ {
+ Type: "bytes",
+ Values: []string{""},
+ },
+ {
+ Type: "bytes",
+ Values: []string{""},
+ },
+ {
+ Type: "uint8[2]",
+ Values: []string{"28", "28"},
+ },
+ {
+ Type: "bytes32[5]",
+ Values: []string{"0x77e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd89", "0x60513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b104",
+ "0x77e6196859305642ea4751b9597a9507472acb04b9f1f4759aa0f27af41edd89", "0x60513f1649f58782cacce26b1341575b584594f940bba0614aff302d25b4b104",
+ "0x0000000000000000000000000000000000000000000000000000000000000000"},
+ },
+ },
+ },
+ },
+ {
+ name: "registerWithConfig",
+ signatures: &signatures,
+ data: "0xf7a1696300000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000769cbf44073741ccb4c39c945402130b46fa8a70000000000000000000000000000000000000000000000000000000012cf35707a8c22626793047f41a428e815e2bb12ced6d5db4246a8b0bda488c541647bef0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba410000000000000000000000000769cbf44073741ccb4c39c945402130b46fa8a700000000000000000000000000000000000000000000000000000000000000076d6f6e7369746100000000000000000000000000000000000000000000000000",
+ want: &bchain.EthereumParsedInputData{
+ MethodId: "0xf7a16963",
+ Name: "Register With Config",
+ Function: "registerWithConfig(string, address, uint256, bytes32, address, address)",
+ Params: []bchain.EthereumParsedInputParam{
+ {
+ Type: "string",
+ Values: []string{"monsita"},
+ },
+ {
+ Type: "address",
+ Values: []string{"0x0769cBf44073741cCb4C39c945402130B46fa8A7"},
+ },
+ {
+ Type: "uint256",
+ Values: []string{"315569520"},
+ },
+ {
+ Type: "bytes32",
+ Values: []string{"0x7a8c22626793047f41a428e815e2bb12ced6d5db4246a8b0bda488c541647bef"},
+ },
+ {
+ Type: "address",
+ Values: []string{"0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"},
+ },
+ {
+ Type: "address",
+ Values: []string{"0x0769cBf44073741cCb4C39c945402130B46fa8A7"},
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := ParseInputData(tt.signatures, tt.data)
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("ParseInputData() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go
index 4061eea7..a6315324 100644
--- a/bchain/types_ethereum_type.go
+++ b/bchain/types_ethereum_type.go
@@ -1,6 +1,10 @@
package bchain
-import "math/big"
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+)
// EthereumType specific
@@ -12,6 +16,27 @@ type EthereumInternalTransfer struct {
Value big.Int `json:"value"`
}
+type FourByteSignature struct {
+ // stored in DB
+ Name string
+ Parameters []string
+ // processed from DB data and stored only in cache
+ DecamelName string
+ Function string
+ ParsedParameters []abi.Type
+}
+
+type EthereumParsedInputParam struct {
+ Type string `json:"type"`
+ Values []string `json:"values,omitempty"`
+}
+type EthereumParsedInputData struct {
+ MethodId string `json:"methodId"`
+ Name string `json:"name"`
+ Function string `json:"function,omitempty"`
+ Params []EthereumParsedInputParam `json:"params,omitempty"`
+}
+
// EthereumInternalTransactionType - type of ethereum transaction from internal data
type EthereumInternalTransactionType int
diff --git a/db/rocksdb_ethereumtype.go b/db/rocksdb_ethereumtype.go
index 5bbc1c10..3310afe3 100644
--- a/db/rocksdb_ethereumtype.go
+++ b/db/rocksdb_ethereumtype.go
@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/hex"
"math/big"
+ "sync"
"github.com/flier/gorocksdb"
"github.com/golang/glog"
@@ -578,11 +579,8 @@ func (d *RocksDB) unpackEthInternalData(buf []byte) (*bchain.EthereumInternalDat
return &id, nil
}
-type FourByteSignature struct {
- Name string
- Parameters []string
-}
-
+// FourByteSignature contains 4byte signature of transaction value with parameters
+// and parsed parameters (that are not stored in DB)
func packFourByteKey(fourBytes uint32, id uint32) []byte {
key := make([]byte, 0, 8)
key = append(key, packUint(fourBytes)...)
@@ -590,7 +588,7 @@ func packFourByteKey(fourBytes uint32, id uint32) []byte {
return key
}
-func packFourByteSignature(signature *FourByteSignature) []byte {
+func packFourByteSignature(signature *bchain.FourByteSignature) []byte {
buf := packString(signature.Name)
for i := range signature.Parameters {
buf = append(buf, packString(signature.Parameters[i])...)
@@ -598,8 +596,8 @@ func packFourByteSignature(signature *FourByteSignature) []byte {
return buf
}
-func unpackFourByteSignature(buf []byte) (*FourByteSignature, error) {
- var signature FourByteSignature
+func unpackFourByteSignature(buf []byte) (*bchain.FourByteSignature, error) {
+ var signature bchain.FourByteSignature
var l int
signature.Name, l = unpackString(buf)
for l < len(buf) {
@@ -610,7 +608,8 @@ func unpackFourByteSignature(buf []byte) (*FourByteSignature, error) {
return &signature, nil
}
-func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*FourByteSignature, error) {
+// GetFourByteSignature gets all 4byte signature of given fourBytes and id
+func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*bchain.FourByteSignature, error) {
key := packFourByteKey(fourBytes, id)
val, err := d.db.GetCF(d.ro, d.cfh[cfFunctionSignatures], key)
if err != nil {
@@ -624,12 +623,51 @@ func (d *RocksDB) GetFourByteSignature(fourBytes uint32, id uint32) (*FourByteSi
return unpackFourByteSignature(buf)
}
-func (d *RocksDB) StoreFourByteSignature(wb *gorocksdb.WriteBatch, fourBytes uint32, id uint32, signature *FourByteSignature) error {
+var cachedByteSignatures = make(map[uint32]*[]bchain.FourByteSignature)
+var cachedByteSignaturesMux sync.Mutex
+
+// GetFourByteSignatures gets all 4byte signatures of given fourBytes
+// (there may be more than one signature starting with the same four bytes)
+func (d *RocksDB) GetFourByteSignatures(fourBytes uint32) (*[]bchain.FourByteSignature, error) {
+ cachedByteSignaturesMux.Lock()
+ signatures, found := cachedByteSignatures[fourBytes]
+ cachedByteSignaturesMux.Unlock()
+ if !found {
+ retval := []bchain.FourByteSignature{}
+ key := packUint(fourBytes)
+ it := d.db.NewIteratorCF(d.ro, d.cfh[cfFunctionSignatures])
+ defer it.Close()
+ for it.Seek(key); it.Valid(); it.Next() {
+ current := it.Key().Data()
+ if bytes.Compare(current[:4], key) > 0 {
+ break
+ }
+ val := it.Value().Data()
+ signature, err := unpackFourByteSignature(val)
+ if err != nil {
+ return nil, err
+ }
+ retval = append(retval, *signature)
+ }
+ cachedByteSignaturesMux.Lock()
+ cachedByteSignatures[fourBytes] = &retval
+ cachedByteSignaturesMux.Unlock()
+ return &retval, nil
+ }
+ return signatures, nil
+}
+
+// StoreFourByteSignature stores 4byte signature in DB
+func (d *RocksDB) StoreFourByteSignature(wb *gorocksdb.WriteBatch, fourBytes uint32, id uint32, signature *bchain.FourByteSignature) error {
key := packFourByteKey(fourBytes, id)
wb.PutCF(d.cfh[cfFunctionSignatures], key, packFourByteSignature(signature))
+ cachedByteSignaturesMux.Lock()
+ delete(cachedByteSignatures, fourBytes)
+ cachedByteSignaturesMux.Unlock()
return nil
}
+// GetEthereumInternalData gets transaction internal data from DB
func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternalData, error) {
btxID, err := d.chainParser.PackTxid(txid)
if err != nil {
@@ -902,7 +940,9 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain
glog.Warning("AddressContracts ", addrDesc, ", contract ", contractIndex, " Txs would be negative, tx ", hex.EncodeToString(btxID))
}
} else {
- glog.Warning("AddressContracts ", addrDesc, ", contract ", btxContract.contract, " not found, tx ", hex.EncodeToString(btxID))
+ if !isZeroAddress(addrDesc) {
+ glog.Warning("AddressContracts ", addrDesc, ", contract ", btxContract.contract, " not found, tx ", hex.EncodeToString(btxID))
+ }
}
}
} else {
diff --git a/db/rocksdb_ethereumtype_test.go b/db/rocksdb_ethereumtype_test.go
index 9d257c70..a61fc958 100644
--- a/db/rocksdb_ethereumtype_test.go
+++ b/db/rocksdb_ethereumtype_test.go
@@ -301,7 +301,7 @@ func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInterna
func testFourByteSignature(t *testing.T, d *RocksDB) {
fourBytes := uint32(1234123)
id := uint32(42313)
- signature := FourByteSignature{
+ signature := bchain.FourByteSignature{
Name: "xyz",
Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"},
}
@@ -320,6 +320,13 @@ func testFourByteSignature(t *testing.T, d *RocksDB) {
if !reflect.DeepEqual(*got, signature) {
t.Errorf("testFourByteSignature: got %+v, want %+v", got, signature)
}
+ gotSlice, err := d.GetFourByteSignatures(fourBytes)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(*gotSlice, []bchain.FourByteSignature{signature}) {
+ t.Errorf("testFourByteSignature: got %+v, want %+v", *gotSlice, []bchain.FourByteSignature{signature})
+ }
}
// TestRocksDB_Index_EthereumType is an integration test probing the whole indexing functionality for EthereumType chains
@@ -1165,24 +1172,24 @@ func Test_packUnpackBlockTx(t *testing.T) {
func Test_packUnpackFourByteSignature(t *testing.T) {
tests := []struct {
name string
- signature FourByteSignature
+ signature bchain.FourByteSignature
}{
{
name: "no params",
- signature: FourByteSignature{
+ signature: bchain.FourByteSignature{
Name: "abcdef",
},
},
{
name: "one param",
- signature: FourByteSignature{
+ signature: bchain.FourByteSignature{
Name: "opqr",
Parameters: []string{"uint16"},
},
},
{
name: "multiple params",
- signature: FourByteSignature{
+ signature: bchain.FourByteSignature{
Name: "xyz",
Parameters: []string{"address", "(bytes,uint256[],uint256)", "uint16"},
},
diff --git a/fourbyte/fourbyte.go b/fourbyte/fourbyte.go
index 76b56b3c..21d6fe12 100644
--- a/fourbyte/fourbyte.go
+++ b/fourbyte/fourbyte.go
@@ -11,6 +11,7 @@ import (
"github.com/flier/gorocksdb"
"github.com/golang/glog"
+ "github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/db"
)
@@ -98,13 +99,13 @@ func (fd *FourByteSignaturesDownloader) getPageWithRetry(url string) (*signature
return nil, errors.New("Too many retries to 4byte signatures")
}
-func parseSignatureFromText(t string) *db.FourByteSignature {
+func parseSignatureFromText(t string) *bchain.FourByteSignature {
s := strings.Index(t, "(")
e := strings.LastIndex(t, ")")
if s < 0 || e < 0 {
return nil
}
- var signature db.FourByteSignature
+ var signature bchain.FourByteSignature
signature.Name = t[:s]
params := t[s+1 : e]
if len(params) > 0 {
diff --git a/fourbyte/fourbyte_test.go b/fourbyte/fourbyte_test.go
index 95c7b3af..c64ddad5 100644
--- a/fourbyte/fourbyte_test.go
+++ b/fourbyte/fourbyte_test.go
@@ -4,26 +4,26 @@ import (
"reflect"
"testing"
- "github.com/trezor/blockbook/db"
+ "github.com/trezor/blockbook/bchain"
)
func Test_parseSignatureFromText(t *testing.T) {
tests := []struct {
name string
signature string
- want db.FourByteSignature
+ want bchain.FourByteSignature
}{
{
name: "_gonsPerFragment",
signature: "_gonsPerFragment()",
- want: db.FourByteSignature{
+ want: bchain.FourByteSignature{
Name: "_gonsPerFragment",
},
},
{
name: "vestingDeposits",
signature: "vestingDeposits(address)",
- want: db.FourByteSignature{
+ want: bchain.FourByteSignature{
Name: "vestingDeposits",
Parameters: []string{"address"},
},
@@ -31,7 +31,7 @@ func Test_parseSignatureFromText(t *testing.T) {
{
name: "batchTransferTokenB",
signature: "batchTransferTokenB(address[],uint256)",
- want: db.FourByteSignature{
+ want: bchain.FourByteSignature{
Name: "batchTransferTokenB",
Parameters: []string{"address[]", "uint256"},
},
@@ -39,7 +39,7 @@ func Test_parseSignatureFromText(t *testing.T) {
{
name: "transmitAndSellTokenForEth",
signature: "transmitAndSellTokenForEth(address,uint256,uint256,uint256,address,(uint8,bytes32,bytes32),bytes)",
- want: db.FourByteSignature{
+ want: bchain.FourByteSignature{
Name: "transmitAndSellTokenForEth",
Parameters: []string{"address", "uint256", "uint256", "uint256", "address", "(uint8,bytes32,bytes32)", "bytes"},
},
diff --git a/server/public.go b/server/public.go
index e778306b..be0dc402 100644
--- a/server/public.go
+++ b/server/public.go
@@ -457,6 +457,8 @@ func (s *PublicServer) parseTemplates() []*template.Template {
"isOwnAddress": isOwnAddress,
"toJSON": toJSON,
"tokenTransfersCount": tokenTransfersCount,
+ "tokenCount": tokenCount,
+ "hasPrefix": strings.HasPrefix,
}
var createTemplate func(filenames ...string) *template.Template
if s.debug {
@@ -559,7 +561,7 @@ func isOwnAddress(td *TemplateData, a string) bool {
return a == td.AddrStr
}
-// called from template, returns count of token transfers of given type
+// called from template, returns count of token transfers of given type in a tx
func tokenTransfersCount(tx *api.Tx, t api.TokenType) int {
count := 0
for i := range tx.TokenTransfers {
@@ -570,6 +572,17 @@ func tokenTransfersCount(tx *api.Tx, t api.TokenType) int {
return count
}
+// called from template, returns count of tokens in array of given type
+func tokenCount(tokens []api.Token, t api.TokenType) int {
+ count := 0
+ for i := range tokens {
+ if tokens[i].Type == t {
+ count++
+ }
+ }
+ return count
+}
+
func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var tx *api.Tx
var err error
diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go
index c6a02055..27a6009d 100644
--- a/server/public_ethereumtype_test.go
+++ b/server/public_ethereumtype_test.go
@@ -8,13 +8,43 @@ import (
"net/http/httptest"
"testing"
+ "github.com/flier/gorocksdb"
"github.com/golang/glog"
+ "github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/eth"
+ "github.com/trezor/blockbook/db"
"github.com/trezor/blockbook/tests/dbtestdata"
)
func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
tests := []httpTests{
+ {
+ name: "explorerAddress " + dbtestdata.EthAddr7b,
+ r: newGetRequest(ts.URL + "/address/" + dbtestdata.EthAddr7b),
+ status: http.StatusOK,
+ contentType: "text/html; charset=utf-8",
+ body: []string{
+ `
Trezor Fake Coin ExplorerContract Contract 123 (S123) 0.000000000123450123 FAKE
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
Confirmed
| Balance | 0.000000000123450123 FAKE |
| Transactions | 2 |
| Non-contract Transactions | 0 |
| Internal Transactions | 0 |
| Nonce | 123 |
| ERC20 Tokens | |
| ERC721 Tokens | |
Transactions
ERC721 Token Transfers
| 0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b |
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
| 0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b |
0.000871180000950184 S74
| 0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b |
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`,
+ },
+ },
+ {
+ name: "explorerAddress " + dbtestdata.EthAddr5d,
+ r: newGetRequest(ts.URL + "/address/" + dbtestdata.EthAddr5d),
+ status: http.StatusOK,
+ contentType: "text/html; charset=utf-8",
+ body: []string{
+ `Trezor Fake Coin ExplorerContract Contract 93 (S93) 0.000000000123450093 FAKE
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
Confirmed
| Balance | 0.000000000123450093 FAKE |
| Transactions | 1 |
| Non-contract Transactions | 1 |
| Internal Transactions | 0 |
| Nonce | 93 |
| ERC1155 Tokens | | Contract | Tokens | Transfers |
|---|
| Contract 111 | 1776:1 S111, 1898:10 S111 | 1 |
|
Transactions
| 0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e |
0 FAKE
ERC1155 Token Transfers
| 0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e |
1776:1 S1111898:10 S111
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`,
+ },
+ },
+ {
+ name: "explorerTx " + dbtestdata.EthTxidB1T2,
+ r: newGetRequest(ts.URL + "/tx/0x" + dbtestdata.EthTxidB1T2),
+ status: http.StatusOK,
+ contentType: "text/html; charset=utf-8",
+ body: []string{
+ `Trezor Fake Coin ExplorerTransaction
0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
Summary
| In Block | Unconfirmed |
| Status | Success |
| Value | 0 FAKE |
| Gas Used / Limit | 52025 / 78037 |
| Gas Price | 0.00000004 FAKE |
| Fees | 0.002081 FAKE |
| RBF | ON |
Details
Transfer
Method ID: 0xa9059cbb
ERC20 Token Transfers
Fee: 0.002081 FAKE
Unconfirmed Transaction!0 FAKE
Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
`,
+ },
+ },
{
name: "apiIndex",
r: newGetRequest(ts.URL + "/api"),
@@ -37,11 +67,42 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18,"balance":"1000075074"}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`,
},
},
+ {
+ name: "apiAddress EthAddr7b details=txs",
+ r: newGetRequest(ts.URL + "/api/v2/address/" + dbtestdata.EthAddr7b + "?details=txs"),
+ status: http.StatusOK,
+ contentType: "application/json; charset=utf-8",
+ body: []string{
+ `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","token":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":18,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"erc20Contract":{"contract":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","name":"Contract 123","symbol":"S123","decimals":18}}`,
+ },
+ },
+ {
+ name: "apiTx EthTxidB1T2",
+ r: newGetRequest(ts.URL + "/api/v2/tx/0x" + dbtestdata.EthTxidB1T2),
+ status: http.StatusOK,
+ contentType: "application/json; charset=utf-8",
+ body: []string{
+ `{"txid":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","vin":[{"n":0,"addresses":["0x20cD153de35D469BA46127A0C8F18626b59a256A"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x4af4114F73d1c1C903aC9E0361b379D1291808A2"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"2081000000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0xd0","gasPrice":"0x9502f9000","gas":"0x130d5","to":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","value":"0x0","input":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","hash":"0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101","blockNumber":"0x41eee8","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","transactionIndex":"0x0"},"internalData":{"type":0,"transfers":[{"type":1,"from":"9f4981531fda132e83c44680787dfa7ee31e4f8d","to":"4af4114f73d1c1c903ac9e0361b379d1291808a2","value":1000000},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000001},{"type":0,"from":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","to":"3e3a3d69dc66ba10737f531ed088954a9ec89d97","value":1000002}],"Error":""},"receipt":{"gasUsed":"0xcb39","status":"0x1","logs":[{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000020cd153de35d469ba46127a0c8f18626b59a256a","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x00000000000000000000000000000000000000000000021e19e0c9bab2400000"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x20cD153de35D469BA46127A0C8F18626b59a256A","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","token":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":18,"value":"10000000000000000000000"}],"ethereumSpecific":{"status":1,"nonce":208,"gasLimit":78037,"gasUsed":52025,"gasPrice":"40000000000","data":"0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000","parsedData":{"methodId":"0xa9059cbb","name":"Transfer","function":"transfer(address, uint256)","params":[{"type":"address","values":["0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"]},{"type":"uint256","values":["10000000000000000000000"]}]}}}`,
+ },
+ },
}
performHttpTests(tests, t, ts)
}
+func initEthereumTypeDB(d *db.RocksDB) error {
+ // add 0xa9059cbb transfer(address,uint256) signature
+ wb := gorocksdb.NewWriteBatch()
+ defer wb.Destroy()
+ if err := d.StoreFourByteSignature(wb, 2835717307, 145, &bchain.FourByteSignature{
+ Name: "transfer",
+ Parameters: []string{"address", "uint256"},
+ }); err != nil {
+ return err
+ }
+ return d.WriteBatch(wb)
+}
+
func Test_PublicServer_EthereumType(t *testing.T) {
parser := eth.NewEthereumParser(1)
chain, err := dbtestdata.NewFakeBlockChainEthereumType(parser)
diff --git a/server/public_test.go b/server/public_test.go
index f06d55d2..341a670d 100644
--- a/server/public_test.go
+++ b/server/public_test.go
@@ -74,10 +74,15 @@ func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *te
if err := d.ConnectBlock(block2); err != nil {
t.Fatal(err)
}
- if err := InitTestFiatRates(d); err != nil {
+ if err := initTestFiatRates(d); err != nil {
t.Fatal(err)
}
is.FinishedSync(block2.Height)
+ if parser.GetChainType() == bchain.ChainEthereumType {
+ if err := initEthereumTypeDB(d); err != nil {
+ t.Fatal(err)
+ }
+ }
return d, is, tmp
}
@@ -168,8 +173,8 @@ func insertFiatRate(date string, rates map[string]float64, d *db.RocksDB) error
return d.FiatRatesStoreTicker(ticker)
}
-// InitTestFiatRates initializes test data for /api/v2/tickers endpoint
-func InitTestFiatRates(d *db.RocksDB) error {
+// initTestFiatRates initializes test data for /api/v2/tickers endpoint
+func initTestFiatRates(d *db.RocksDB) error {
if err := insertFiatRate("20180320020000", map[string]float64{
"usd": 2000.0,
"eur": 1300.0,
@@ -235,7 +240,7 @@ func performHttpTests(tests []httpTests, t *testing.T, ts *httptest.Server) {
b := string(bb)
for _, c := range tt.body {
if !strings.Contains(b, c) {
- t.Errorf("got %v, want to contain %v", b, c)
+ t.Errorf("got\n%v\nwant to contain %v", b, c)
break
}
}
diff --git a/static/templates/address.html b/static/templates/address.html
index 7c661b78..730355ae 100644
--- a/static/templates/address.html
+++ b/static/templates/address.html
@@ -30,7 +30,7 @@
Nonce |
{{$addr.Nonce}} |
- {{- if $addr.Tokens -}}
+ {{if tokenCount $addr.Tokens "ERC20"}}
| ERC20 Tokens |
@@ -41,13 +41,75 @@
| Tokens |
Transfers |
- {{- range $t := $addr.Tokens -}}
+ {{range $t := $addr.Tokens}}
+ {{if eq $t.Type "ERC20"}}
| {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} |
{{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}} |
{{$t.Transfers}} |
- {{- end -}}
+ {{end}}
+ {{end}}
+
+
+
+
+ {{- end -}}
+ {{if tokenCount $addr.Tokens "ERC721"}}
+
+ | ERC721 Tokens |
+
+
+
+
+ | Contract |
+ Tokens |
+ Transfers |
+
+ {{range $t := $addr.Tokens}}
+ {{if eq $t.Type "ERC721"}}
+
+ | {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} |
+
+ {{range $i, $iv := $t.Ids}}
+ {{if $i}}, {{end}}
+ {{formatAmountWithDecimals $iv 0}}
+ {{end}}
+ |
+ {{$t.Transfers}} |
+
+ {{end}}
+ {{end}}
+
+
+ |
+
+ {{- end -}}
+ {{if tokenCount $addr.Tokens "ERC1155"}}
+
+ | ERC1155 Tokens |
+
+
+
+
+ | Contract |
+ Tokens |
+ Transfers |
+
+ {{range $t := $addr.Tokens}}
+ {{if eq $t.Type "ERC1155"}}
+
+ | {{if $t.Contract}}{{$t.Name}}{{else}}{{$t.Name}}{{end}} |
+
+ {{range $i, $iv := $t.IdValues}}
+ {{if $i}}, {{end}}
+ {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}
+ {{end}}
+ |
+ {{$t.Transfers}} |
+
+ {{end}}
+ {{end}}
|
diff --git a/static/templates/tx.html b/static/templates/tx.html
index 3f3f21d1..6a260a79 100644
--- a/static/templates/tx.html
+++ b/static/templates/tx.html
@@ -84,6 +84,46 @@
{{template "txdetail" .}}
+{{if eq .ChainType 1}}
+{{if $tx.EthereumSpecific.ParsedData}}
+{{if $tx.EthereumSpecific.ParsedData.Function }}
+
+
Input Data
+
+ {{if $tx.EthereumSpecific.ParsedData.Name}}
{{$tx.EthereumSpecific.ParsedData.Name}}
{{end}}{{if $tx.EthereumSpecific.ParsedData.MethodId}}
Method ID: {{$tx.EthereumSpecific.ParsedData.MethodId}}
{{end}}
+ {{if $tx.EthereumSpecific.ParsedData.Function}}
Function: {{$tx.EthereumSpecific.ParsedData.Function}}
{{end}}
+ {{if $tx.EthereumSpecific.ParsedData.Params}}
+
+
+
+
+ | # |
+ Type |
+ Data |
+
+
+
+ {{range $i,$p := $tx.EthereumSpecific.ParsedData.Params}}
+
+ | {{$i}} |
+ {{$p.Type}} |
+
+ {{range $j,$v := $p.Values}}
+ {{if $j}} {{end}}
+ {{if hasPrefix $p.Type "address"}}{{$v}}{{else}}{{$v}}{{end}}
+ {{end}}
+ |
+
+ {{end}}
+
+
+
+ {{end}}
+
+
+{{end}}
+{{end}}
+{{end}}
Raw Transaction
diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html
index aadb773e..d47d7f0c 100644
--- a/static/templates/txdetail_ethereumtype.html
+++ b/static/templates/txdetail_ethereumtype.html
@@ -6,6 +6,9 @@
{{if eq $tx.EthereumSpecific.Status 1}} ✔{{end}}{{if eq $tx.EthereumSpecific.Status 0}} ✘{{end}}
{{- if $tx.Blocktime}}
{{if $tx.Confirmations}}mined{{else}}first seen{{end}} {{formatUnixTime $tx.Blocktime}}
{{end -}}
+ {{if $tx.EthereumSpecific.ParsedData}}
+ {{if $tx.EthereumSpecific.ParsedData.Name}}
{{$tx.EthereumSpecific.ParsedData.Name}}
{{end}}{{if $tx.EthereumSpecific.ParsedData.MethodId}}
Method ID: {{$tx.EthereumSpecific.ParsedData.MethodId}}
{{end}}
+ {{end}}
{{if $tx.EthereumSpecific.Error}}
Error: {{$tx.EthereumSpecific.Error}}
{{end}}
@@ -265,7 +268,7 @@
{{- range $iv := $tt.Values -}}
- {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value $tt.Decimals}} {{$tt.Symbol}}
+ {{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$tt.Symbol}}
{{- end -}}