其他分享
首页 > 其他分享> > 3.ERC20对接

3.ERC20对接

作者:互联网

本文写于2019

ERC20 签名

https://zhuanlan.zhihu.com/p/39656584

签名要点:

ABI 是什么

https://www.cnblogs.com/tinyxiong/p/9453563.html

ABI 全称是 Application Binary Interface,翻译过来就是:应用程序二进制接口,简单来说就是 以太坊的调用合约时的接口说明。还不是很理解,没关系。

自己发布ERC20代币

https://learnblockchain.cn/2018/01/12/create_token/

编译器选择 v0.4.17

我发行的 ERC20 Token 地址

https://rinkeby.etherscan.io/address/0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33


> eth.getTransactionReceipt("0x1a24c6110139e963a901d30937bf299753fd0574c3c796dcb0379b1ae4171841")
{
  blockHash: "0x971e6e1cd533d7332d3fb38be084c2199a589ea9d2dfb2e5e9a6e01379556fba",
  blockNumber: 4988294,
  contractAddress: null,
  cumulativeGasUsed: 338664,
  from: "0x954d1a58c7abd4ac8ebe05f59191cf718eb0cb89",
  gasUsed: 53418,
  logs: [{
      address: "0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33",
      blockHash: "0x971e6e1cd533d7332d3fb38be084c2199a589ea9d2dfb2e5e9a6e01379556fba",
      blockNumber: 4988294,
      data: "0x000000000000000000000000000000000000000000008a8ed1b5a15792880000",
      logIndex: 2,
      removed: false,
      topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000954d1a58c7abd4ac8ebe05f59191cf718eb0cb89", "0x00000000000000000000000056e5782c908f69bd46b7e77338349f961fbe55b1"],
      transactionHash: "0x1a24c6110139e963a901d30937bf299753fd0574c3c796dcb0379b1ae4171841",
      transactionIndex: 3
  }],
  logsBloom: "0x00000000000000000000000000040000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000050000000000000000000000000000000000000000000000400000000000000000000000000020000000000000000000000000000000000200000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000004000000000000000000000",
  status: "0x1",
  to: "0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33",
  transactionHash: "0x1a24c6110139e963a901d30937bf299753fd0574c3c796dcb0379b1ae4171841",
  transactionIndex: 3
}


ERC20转账

#!coding:utf8

#author:yqq
#date:2019/8/28 0028 14:22
#description:  ETH 普通转账  /  ERC20 代币转账

from ethereum.transactions import  Transaction
import rlp
from rlp.sedes import big_endian_int , binary
from decimal import  Decimal



#普通的ETH转账成功的案例: https://rinkeby.etherscan.io/address/0x56e5782c908f69bd46b7e77338349f961fbe55b1
#ERC20-Token的转账成功案例: https://rinkeby.etherscan.io/tx/0xfa8da3dda31d644a5f654fc332284cafc2367990a529a14fdb0085b2e89259aa
#ERC20-Token 假交易的案例: https://rinkeby.etherscan.io/tx/0xa5c51ead7f209ac97fb8d8685171e2a5fc50bfb98b987cb6e3342670e9919b3a
#

Mainnet=0
Rinkeby=4


def to_zero_padded_str(in_data, target_len=64):
    # print(in_data)
    return '0' * (target_len - len(in_data)) + in_data


def make_data(abi_transfer='a9059cbb', to='0x123423', value=0x123424):
    return abi_transfer + to_zero_padded_str(to[2:]) + to_zero_padded_str(hex(int(value))[2:].replace('L', ''))
    pass


def main():

    erc20_data =  make_data(to='0x56e5782c908f69bd46b7e77338349f961fbe55b1', value=Decimal('2019.08281809')*(10**18))
    print( "erc20_data:{}".format( erc20_data) )

    tx = Transaction(nonce=120,
                     gasprice=10*(10**9),
                     startgas=210000,
                     to='0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33',
                     value=1*(10**16),
                     # data=b''
                     data = erc20_data.decode('hex')
                     )
    tx.sender = '0x954d1a58c7abd4ac8ebe05f59191cf718eb0cb89'

    # print(tx.hash)
    signed_tx  =  tx.sign(key='DBBAD2A5682517E4FF095F948F721563231282CA4179AE0DFEA1C76143BA9607', network_id=Rinkeby)  # 4:Rinkeby
    print( signed_tx.hash.encode('hex'))
    rlp_data = rlp.encode(  rlp.infer_sedes(tx).serialize(tx) )
    print(rlp_data.encode('hex'))
    # print(signed_tx)

    pass


def main2():

    erc20_data =  make_data(to='0xdf88522B56B85d4F0Bb08a7494b97E017BC6CB31', value=Decimal('656340.20627410')*(10**18))
    print( "erc20_data:{}".format( erc20_data) )

    tx = Transaction(nonce=51,
                     gasprice=10*(10**9),
                     startgas=210000,
                     to='0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33',
                     value=1*(10**16),
                     # data=b''
                     data = erc20_data.decode('hex')
                     )
    tx.sender = '0x56e5782c908f69bd46b7e77338349f961fbe55b1'

    # print(tx.hash)
    signed_tx  =  tx.sign(key='63C08FABC252B53B9471E520CE8199971DB8884B5B569CBBBD17BC714E6BB39F', network_id=Rinkeby)  # 4:Rinkeby
    print( signed_tx.hash.encode('hex'))
    rlp_data = rlp.encode(  rlp.infer_sedes(tx).serialize(tx) )
    print(rlp_data.encode('hex'))
    # print(signed_tx)

    pass

# 
# import ethereum.utils
# def main3():
# 
#     utils.sha3('')
# 
# 
# 
# 
#     pass



if __name__ == '__main__':

    # main()
    main2()
    # main3()


成功的ERC20 Token转账

> eth.getTransactionReceipt("0xa5c51ead7f209ac97fb8d8685171e2a5fc50bfb98b987cb6e3342670e9919b3a")
{
  blockHash: "0xc789baa019defb946c10679782a93f1a333a9c89f3e21bb05faca250b0cc9c3d",
  blockNumber: 4989987,
  contractAddress: null,
  cumulativeGasUsed: 24409,
  from: "0x56e5782c908f69bd46b7e77338349f961fbe55b1",
  gasUsed: 24409,
  logs: [],  //日志为空
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x0", //状态为0
  to: "0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33",
  transactionHash: "0xa5c51ead7f209ac97fb8d8685171e2a5fc50bfb98b987cb6e3342670e9919b3a",
  transactionIndex: 0
}
> 

> eth.getTransactionReceipt("0xfa8da3dda31d644a5f654fc332284cafc2367990a529a14fdb0085b2e89259aa")
{
  blockHash: "0xe639442fc8b9201e674dfffe2738ced535034324ebc533d429be7305c2407a28",
  blockNumber: 4989937,
  contractAddress: null,
  cumulativeGasUsed: 614873,
  from: "0x954d1a58c7abd4ac8ebe05f59191cf718eb0cb89",
  gasUsed: 38418,
  logs: [{
      address: "0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33",
      blockHash: "0xe639442fc8b9201e674dfffe2738ced535034324ebc533d429be7305c2407a28",
      blockNumber: 4989937,
      data: "0x00000000000000000000000000000000000000000000006d7467257e5d192400",
      logIndex: 7,
      removed: false,
      topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000954d1a58c7abd4ac8ebe05f59191cf718eb0cb89", "0x00000000000000000000000056e5782c908f69bd46b7e77338349f961fbe55b1"],
      transactionHash: "0xfa8da3dda31d644a5f654fc332284cafc2367990a529a14fdb0085b2e89259aa",
      transactionIndex: 10
  }],
  logsBloom: "0x00000000000000000000000000040000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000050000000000000000000000000000000000000000000000400000000000000000000000000020000000000000000000000000000000000200000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000004000000000000000000000",
  status: "0x1",
  to: "0x130fc2749d35fe026f32427f60dd3f2ecb6c2f33",
  transactionHash: "0xfa8da3dda31d644a5f654fc332284cafc2367990a529a14fdb0085b2e89259aa",
  transactionIndex: 10
}

事件日志 和 logsBloom分析

logsBloom: The Bloom filter composed from indexable information (logger address and log topics) contained in each log entry from the receipt of each transaction in the transactions list.

// go-ethereum/core/types/bloom9.go

// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package types

import (
	"fmt"
	"math/big"

	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/crypto"
)

type bytesBacked interface {
	Bytes() []byte
}

const (
	// BloomByteLength represents the number of bytes used in a header log bloom.
	BloomByteLength = 256

	// BloomBitLength represents the number of bits used in a header log bloom.
	BloomBitLength = 8 * BloomByteLength
)

// Bloom represents a 2048 bit bloom filter.
type Bloom [BloomByteLength]byte

// BytesToBloom converts a byte slice to a bloom filter.
// It panics if b is not of suitable size.
func BytesToBloom(b []byte) Bloom {
	var bloom Bloom
	bloom.SetBytes(b)
	return bloom
}

// SetBytes sets the content of b to the given bytes.
// It panics if d is not of suitable size.
func (b *Bloom) SetBytes(d []byte) {
	if len(b) < len(d) {
		panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
	}
	copy(b[BloomByteLength-len(d):], d)
}

// Add adds d to the filter. Future calls of Test(d) will return true.
func (b *Bloom) Add(d *big.Int) {
	bin := new(big.Int).SetBytes(b[:])
	bin.Or(bin, bloom9(d.Bytes()))
	b.SetBytes(bin.Bytes())
}

// Big converts b to a big integer.
func (b Bloom) Big() *big.Int {
	return new(big.Int).SetBytes(b[:])
}

func (b Bloom) Bytes() []byte {
	return b[:]
}

func (b Bloom) Test(test *big.Int) bool {
	return BloomLookup(b, test)
}

func (b Bloom) TestBytes(test []byte) bool {
	return b.Test(new(big.Int).SetBytes(test))

}

// MarshalText encodes b as a hex string with 0x prefix.
func (b Bloom) MarshalText() ([]byte, error) {
	return hexutil.Bytes(b[:]).MarshalText()
}

// UnmarshalText b as a hex string with 0x prefix.
func (b *Bloom) UnmarshalText(input []byte) error {
	return hexutil.UnmarshalFixedText("Bloom", input, b[:])
}

//生成过滤器
func CreateBloom(receipts Receipts) Bloom {
	bin := new(big.Int)
	for _, receipt := range receipts {
		bin.Or(bin, LogsBloom(receipt.Logs))
	}

	return BytesToBloom(bin.Bytes())
}

func LogsBloom(logs []*Log) *big.Int {
	bin := new(big.Int)
	for _, log := range logs {
		bin.Or(bin, bloom9(log.Address.Bytes()))   //将地址加入过滤器
		for _, b := range log.Topics {
			bin.Or(bin, bloom9(b[:]))  //将topics的元素加入过滤器
		}
	}

	return bin
}

//计算过滤器
func bloom9(b []byte) *big.Int {
	b = crypto.Keccak256(b)

	r := new(big.Int)

	for i := 0; i < 6; i += 2 {
		t := big.NewInt(1)
		b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047
		r.Or(r, t.Lsh(t, b))
	}

	return r
}

var Bloom9 = bloom9

func BloomLookup(bin Bloom, topic bytesBacked) bool {
	bloom := bin.Big()
	cmp := bloom9(topic.Bytes())

	return bloom.And(bloom, cmp).Cmp(cmp) == 0  //验证 topic是否在过滤中
}




> eth.getBlockByNumber("0x7f6414", false)
{
  difficulty: "0x80ef294c2069a",
  extraData: "0x5050594520737061726b706f6f6c2d6574682d636e2d687a",
  gasLimit: "0x7a2310",
  gasUsed: "0x7a1c34",
  hash: "0x6eabf54758b981adf0736ec5521593199b64d1af668120eacb9dea665c85a5f9",
  logsBloom: "0xd82011ccca1160c828c342000bcad811448c8290041c342a08c284a8001c106e601401400889090808965c029248094602600e8e0bc0149f03b001ce0039a8b0103862600001a05349849c2e518854b0b7016d00e27408e02560823a1910105003b290090cd00a02034a553f2cc80809d7141220a204006194281ff1088d7504300b88140044241400d2147ab06a00d080a5c885c14212c645250ce480320c346a2c2c0e204e796a8487339332829d0c22105470a0a8b031f9161124d40d05400003084246088d8c009a0420c6204ca48ac1c4559a18471c34e6b8e1108213d53010e22144a000c3850ac9c8c450f0c655c23d0a801804c888125784c2804265",
  miner: "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c",
  mixHash: "0x2c08ca89b804f373aefb8157afc12a4e44df58a53719e1a05564d62867be2fac",
  nonce: "0x228a1e700639d330",
 //....其他信息省略....

class Log(rlp.Serializable):

    # TODO: original version used zpad (here replaced by int32.serialize); had
    # comment "why zpad"?
    fields = [
        ('address', utils.address),
        ('topics', CountableList(utils.int32)),
        ('data', binary)
    ]

    def __init__(self, address, topics, data):
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20
        super(Log, self).__init__(address, topics, data)

    def bloomables(self):
        return [self.address] + [utils.int32.serialize(x) for x in self.topics]

    def to_dict(self):
        return {
            "bloom": encode_hex(bloom.b64(bloom.bloom_from_list(self.bloomables()))),
            "address": encode_hex(self.address),
            "data": b'0x' + encode_hex(self.data),
            "topics": [encode_hex(utils.int32.serialize(t))
                       for t in self.topics]
        }

    def __repr__(self):
        return '<Log(address=%r, topics=%r, data=%r)>' %  \
            (encode_hex(self.address), self.topics, self.data)

https://etherscan.io/tx/0x516fd3e91718d5576f4d1b9dc4de6ef9f597615675e69705f90d88b64d4b4fe2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cEvliFuH-1620963035689)(./img/erc20_failed.png)]


> eth.getBlockByNumber("0x802e65", false)
{
  difficulty: "0x7e4b2262a729f",
  extraData: "0x505059452d65746865726d696e652d6575312d37",
  gasLimit: "0x7a2135",
  gasUsed: "0x7a156d",
  hash: "0xb84959b878c9e6a753637d0d8ded53fc3e404dd763cbc85351e1bf60c35cd2f4",
  logsBloom: "0x040e14c44940903a951a060d0311308f80408c11876cd83d141983460a42111a8e40004820581411b8a803024a1c8142044956101862a893020047380030f03ae564892be0024abe451a758849f8c8a88728650002c24143219160d894024409482018028b60a60006842670e4001c4107931214226a439a195069177022e0c702400b04804a940d118ec411801609e68240fc2d401a951d6049b110e150c9a212505c851e264028101146840806186620122700b80bf04145522020bdbd6a322610d07600440369091c2010ad0304050a5558552010421e2b801a50944ba928d311c5153612518860830412944820849610bc4007d4a4e80a253251418502c5",
  miner: "0xea674fdde714fd979de3edf0f56aa9716b898ec8",
  mixHash: "0x18e5df8b0941134b4a02f33679400ee52d6e8ebe7a22076c173ad63ee0f398ef",
  nonce: "0x1d6b49c20db8d89a",
  number: "0x802e65",
.....其他省略.....

>>> bx = 0x040e14c44940903a951a060d0311308f80408c11876cd83d141983460a42111a8e40004820581411b8a803024a1c8142044956101862a893020047380030f03ae564892be0024abe451a758849f8c8a88728650002c24143219160d894024409482018028b60a60006842670e4001c4107931214226a439a195069177022e0c702400b04804a940d118ec411801609e68240fc2d401a951d6049b110e150c9a212505c851e264028101146840806186620122700b80bf04145522020bdbd6a322610d07600440369091c2010ad0304050a5558552010421e2b801a50944ba928d311c5153612518860830412944820849610bc4007d4a4e80a253251418502c5
>>> 
>>> bloom_query(bx, 'cfe4b035f17209df73198bed33793718b277981b'.decode('hex'))
False

标签:bloom,tx,hex,对接,address,x00,data,ERC20
来源: https://blog.csdn.net/yqq1997/article/details/116784838