golang学习笔记19 用Golang实现以太坊代币转账
golang学习笔记19 用Golang实现以太坊代币转账
- 在以太坊区块链中,我们称代币为Token,是以太坊区块链中每个人都可以任意发行的数字资产。并且它必须是遵循erc20标准的,至于erc20标准,大家可以参考这篇文章 https://theethereum.wiki/w/index.php/ERC20_Token_Standard
- 它实际上一段智能合约代码,智能合约代码中必须要有以下的一些function 和 event。
contract ERC20 { function totalSupply() constant returns (uint totalSupply); function balanceOf(address _owner) constant returns (uint balance); function transfer(address _to, uint _value) returns (bool success); function transferFrom(address _from, address _to, uint _value) returns (bool success); function approve(address _spender, uint _value) returns (bool success); function allowance(address _owner, address _spender) constant returns (uint remaining); event Transfer(address indexed _from, address indexed _to, uint _value); event Approval(address indexed _owner, address indexed _spender, uint _value); }
-
能合约代码是运行在以太坊智能合约虚拟机中的。文档:https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source
-
我们看到上面那段类似golang中interface的代码,里面分别包含了总量、余额、转账等方法。我们今天重点讲的其实就是用golang来实现transfer、transferFrom方法。
连接以太坊RPC节点
- 目前广泛使用的是go-ethereum,他的客户端名是geth。你可以通过编译、安装等方式把节点搭建在你的电脑或者服务器中,并开启rpc服务。本文省略这一步骤,网上有很文章供你了解。
- 附上github:https://github.com/ethereum/go-ethereum
- geth默认的rpc端口是
8545
,我使用默认端口,后面我们都用http://127.0.0.1:8545
作为我们的rpc连接。
首先获取go-ethereum代码
go get github.com/ethereum/go-ethereum
- 然后我们go-ethereum目录,如果你的golang环境没有问题,那么应该是这个路径。
cd $GOPATH/src/github.com/ethereum/go-ethereum
- 当你进入目录,看到代码已经完整拉取下来,那么我们就可以进行下一步了。
连接RPC节点
1 2 3 4 5 6 7 8 9 | import ( "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/ethclient" ) rpcDial, err := rpc.Dial( "http://127.0.0.1:8545" ) if err != nil { panic(err); } client := ethclient.NewClient(rpcDial) |
如果没有panic,那么我们已经成功连接了
创建测试账户
- 要进行转账测试,那么我们需要两个以太坊账户。我们用golang来生成,我们知道以太坊的账户私钥是放在keystore文件中的,是一段json,并且创建的时候可以设置密码。跟比特币的wallet.dat文件是一样的意思,不见哪一样,你的资产就永远留在区块链网络中,再也无法找回。
- 下面我们用代码创建两个以太坊账户。
1 2 3 4 5 6 7 8 9 | import ( "github.com/ethereum/go-ethereum/accounts/keystore" ) ks := keystore.NewKeyStore( "/" , keystore.StandardScryptN, keystore.StandardScryptP) address, _ := ks.NewAccount( "password" ) account, err := ks.Export(address, "password" , "password" ) if err != nil { panic(err) } |
- 从上面的代码我们可以看到,创建了一个以太坊的账户,并且密码设置为
password
,并导出。最终account
变量就是账户的私钥,是一段json文本。 - 通过
address
变量,我们可以获得账户的地址。如address.Address.Hex()
生成代币文件
- 打开
cd $GOPATH/src/github.com/ethereum/go-ethereum/cmd/abigen
你能看到main.go文件 -
执行
go build main.go
,会在目录下生成一个main
的二进制文件。 -
将以下的json 保存为token.abi,并放在当前目录下。
1 | [{ "constant" :true, "inputs" :[], "name" : "name" , "outputs" :[{ "name" : "" , "type" : "bytes32" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[], "name" : "stop" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "guy" , "type" : "address" },{ "name" : "wad" , "type" : "uint256" }], "name" : "approve" , "outputs" :[{ "name" : "" , "type" : "bool" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "owner_" , "type" : "address" }], "name" : "setOwner" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[], "name" : "totalSupply" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "src" , "type" : "address" },{ "name" : "dst" , "type" : "address" },{ "name" : "wad" , "type" : "uint256" }], "name" : "transferFrom" , "outputs" :[{ "name" : "" , "type" : "bool" }], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[], "name" : "decimals" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "dst" , "type" : "address" },{ "name" : "wad" , "type" : "uint128" }], "name" : "push" , "outputs" :[{ "name" : "" , "type" : "bool" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "name_" , "type" : "bytes32" }], "name" : "setName" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "wad" , "type" : "uint128" }], "name" : "mint" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[{ "name" : "src" , "type" : "address" }], "name" : "balanceOf" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[], "name" : "stopped" , "outputs" :[{ "name" : "" , "type" : "bool" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "authority_" , "type" : "address" }], "name" : "setAuthority" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "src" , "type" : "address" },{ "name" : "wad" , "type" : "uint128" }], "name" : "pull" , "outputs" :[{ "name" : "" , "type" : "bool" }], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[], "name" : "owner" , "outputs" :[{ "name" : "" , "type" : "address" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "wad" , "type" : "uint128" }], "name" : "burn" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[], "name" : "symbol" , "outputs" :[{ "name" : "" , "type" : "bytes32" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[{ "name" : "dst" , "type" : "address" },{ "name" : "wad" , "type" : "uint256" }], "name" : "transfer" , "outputs" :[{ "name" : "" , "type" : "bool" }], "payable" :false, "type" : "function" },{ "constant" :false, "inputs" :[], "name" : "start" , "outputs" :[], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[], "name" : "authority" , "outputs" :[{ "name" : "" , "type" : "address" }], "payable" :false, "type" : "function" },{ "constant" :true, "inputs" :[{ "name" : "src" , "type" : "address" },{ "name" : "guy" , "type" : "address" }], "name" : "allowance" , "outputs" :[{ "name" : "" , "type" : "uint256" }], "payable" :false, "type" : "function" },{ "inputs" :[{ "name" : "symbol_" , "type" : "bytes32" }], "payable" :false, "type" : "constructor" },{ "anonymous" :true, "inputs" :[{ "indexed" :true, "name" : "sig" , "type" : "bytes4" },{ "indexed" :true, "name" : "guy" , "type" : "address" },{ "indexed" :true, "name" : "foo" , "type" : "bytes32" },{ "indexed" :true, "name" : "bar" , "type" : "bytes32" },{ "indexed" :false, "name" : "wad" , "type" : "uint256" },{ "indexed" :false, "name" : "fax" , "type" : "bytes" }], "name" : "LogNote" , "type" : "event" },{ "anonymous" :false, "inputs" :[{ "indexed" :true, "name" : "authority" , "type" : "address" }], "name" : "LogSetAuthority" , "type" : "event" },{ "anonymous" :false, "inputs" :[{ "indexed" :true, "name" : "owner" , "type" : "address" }], "name" : "LogSetOwner" , "type" : "event" },{ "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" },{ "anonymous" :false, "inputs" :[{ "indexed" :true, "name" : "owner" , "type" : "address" },{ "indexed" :true, "name" : "spender" , "type" : "address" },{ "indexed" :false, "name" : "value" , "type" : "uint256" }], "name" : "Approval" , "type" : "event" }] |
-
执行命令
./main --abi token.abi --pkg main --type Token --out token.go
-
我们可以看到目录下生成了一个
token.go
文件。大功告成。
开始转账
- 首先就把上一步生成的
token.go
拖入项目中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" ) //首先导入上面生成的账户密钥(json)和密码 auth, err := bind.NewTransactor(strings.NewReader( "json" ), "password" ) //这句用的是生成的token.go里面的方法 //client变量是我们第一步连接以太坊rpc节点的时候创建的 //contractAddress 是代币地址,比如eos 的地址是0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 //那么我们转账针对的就是账户里的eos代币 //具体看这里 https://etherscan.io/token/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 token, err := NewToken(common.HexToAddress( "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0" ), client) if err != nil { panic(err) } //每个代币都会有相应的位数,例如eos是18位,那么我们转账的时候,需要在金额后面加18个0 decimal, err := token.Decimals(nil) if err != nil { panic(err) } //这是处理位数的代码段 tenDecimal := big.NewFloat(math.Pow(10, float64(decimal))) convertAmount, _ := new(big.Float).Mul(tenDecimal, amount).Int(&big.Int{}) //然后就可以转账到你需要接受的账户上了 //toAddress 是接受eos的账户地址 txs, err := token.Transfer(auth, common.HexToAddress(toAddress), convertAmount) |
到此转账就成功了。
https://golang.org/src/go/token/token.go
里面还有很多其他的方法
大自然,飘然的风,QQ群: python技术交流群:453879716,人工智能深度学习群:251088643
golang技术交流群:316397059,vuejs技术交流群:458915921 囤币一族:621258209,有兴趣的可以加入
微信公众号: 心禅道(xinchandao)投资论道
golang技术交流群:316397059,vuejs技术交流群:458915921 囤币一族:621258209,有兴趣的可以加入
微信公众号: 心禅道(xinchandao)投资论道
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2017-09-03 普通程序员转型AI免费教程整合,零基础也可自学
2017-09-03 深度学习之循环神经网络RNN概述,双向LSTM实现字符识别
2017-09-03 想学习深度学习需要什么样的基础?
2012-09-03 eclipse开启和去掉代码上面的快速导航栏(Toggle Breadcrumb)的方法