当前位置:   article > 正文

区块链以太坊 - Go调用合约_golang调用已部署合约功能

golang调用已部署合约功能

在这里插入图片描述

从之前的文章,我们一直都是使用Nodejs以及使用web3js或者ethers与合约进行交互,在这篇文章中,我们讲讲如何使用go语言来与合约进行交互。由于go没有web3js或者ethers这样的第三方工具库来调用,所以go与合约的交互使用起来就比较复杂一些,需要自己去创建示例对象来调用合约接口。我们接下来看看,如何使用go来调用合约的方法。
在开始之前,我们需要下载go的环境,这就需要大家自己去安装了,我们就跳过安装go环境的教学了。好了,我们开始正式的学习步骤。
我们需要安装下以太坊源码并使用make命令编译得到solc和abigen可执行命令工具。

go get -u github.com/ethereum/go-ethereum

make
  • 1
  • 2
  • 3

编写一个示例合约,文件名称叫做Sar.sol。

pragma solidity ^0.4.26; //编译器版本要求

// 父合约
contract ERC20Interface {

    // 代币名称
    string public constant name = "SAR Token";
    // 代币符号
    string public constant symbol = "SAR";
    // 精度。使用的小数点后几位。比如如果设置为3,就是支持0.001表示。默认为18,之所以需要有小数位字段是因为EVM不支持小数点运算,需要在做计算的时候先转成整型,最后根据小数位把运算结果转换会对应的小数位
    uint8 public constant decimals = 18;
    // 总发行量
    function totalSupply() public constant returns (uint);
    // 余额。根据账户地址查询该地址的余额。返回某个地址(账户)的账户余额
    function balanceOf(address tokenOwner) public constant returns (uint balance);  //返回某个地址(账户)的账户余额
    // 自己转账给别人。表示合约的调用者往_to账户转token
    function transfer(address to, uint tokens) public returns (bool success);

    /*
       approve、transferFrom及allowance解释:
       账户A有1000个ETH,想允许B账户随意调用100个ETH。A账户按照以下形式调用approve函数approve(B,100)。
       当B账户想用这100个ETH中的10个ETH给C账户时,则调用transferFrom(A, C, 10)。
       这时调用allowance(A, B)可以查看B账户还能够调用A账户多少个token。
    */

    // 批准。限定spender能从自己账户中转出多少token
    function approve(address spender, uint tokens) public returns (bool success);  //授权第三方(比如某个合约)从发送者账户转移代币
    // 让spender代替自己给别人转账,前提是得到了approve批准,此函数与approve搭配使用,approve批准之后,调用transferFrom函数来转移token
    function transferFrom(address from, address to, uint tokens) public returns (bool success);   //执行具体的转移操作
    // 限额。返回spender还有多少token可以花费。返回_spender还能提取token的个数
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);  //返回_spender仍然被允许从_owner提取的金额

    // 从代币合约的调用者地址上转移_value的数量token到的地址_to,并且必须触发Transfer事件,代币被转移时触发
    event Transfer(address indexed from, address indexed to, uint tokens);
    // 当调用approval函数成功时,一定要触发Approval事件
    // 允许_spender多次取回您的帐户,最高达_value金额。 如果再次调用此函数,它将以_value覆盖当前的余量,调用approve方法时触发
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
    // 事件,用来通知客户端代币被消费
    event Burn(address indexed from, uint256 value);

    address public owner;


}

// SafeMath 是一个安全数字运算的合约
contract SafeMath {

    // @dev Multiplies two numbers, throws on overflow.
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c)
    {
        if (a == 0) {
            return 0;
        }
        c = a * b;
        assert(c / a == b);
        return c;
    }

    // @dev Integer division of two numbers, truncating the quotient.
    function div(uint256 a, uint256 b) internal pure returns (uint256)
    {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        // uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return a / b;
    }

    // @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
    function sub(uint256 a, uint256 b) internal pure returns (uint256)
    {
        assert(b <= a);
        return a - b;
    }

    // @dev Adds two numbers, throws on overflow.
    function add(uint256 a, uint256 b) internal pure returns (uint256 c)
    {
        c = a + b;
        assert(c >= a);
        return c;
    }
}
// 子合约。继承合约接口
contract SAR_Token is ERC20Interface, SafeMath {

    // 代币名称
    string public name;
    // 代币符号
    string public symbol;
    // 代币小数点位数,代币的最小单位, 如3表示我们可以拥有 0.001单位个代币
    uint8 public decimals;
    // 发行代币总量
    uint256 public totalSupply;
    // 用mapping保存每个地址对应的余额
    mapping(address => uint256) public balanceOf;
    // allowanceOf保存每个地址(第一个address) 授权给其他地址(第二个address)的额度(uint256)也就是存取被授权人的额度
    mapping(address => mapping(address => uint256)) public allowanceOf;

    // 构造函数
    constructor() public {

        owner = msg.sender;

        name = "SAR Token";
        symbol = "SAR";
        decimals = 18;
        // 20个0,metamask显示的是100.000。18个0,metamask显示的是1.000。
        //        totalSupply = 1000000000;
        // totalSupply = 1000000000000000000000000000; // 10亿
        //        totalSupply = 100000000 * (10 ** unit256(decimals));
        totalSupply = 10000000000000000000000000000;
        // 100亿


        balanceOf[msg.sender] = totalSupply;

    }

    /* modifier是修改标志 */
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    // 代币交易转移的内部实现
    function _transfer(address _from, address _to, uint _value) internal {
        // 确保目标地址不为0x0,因为0x0地址代表销毁
        // require(_to != 0x0);
        // 检查发送者余额
        require(balanceOf[_from] >= _value);
        // 溢出检查
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // 以下用来检查交易
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        emit Transfer(_from, _to, _value);
        // 用assert来检查代码逻辑。
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    // 合约调用者转账
    function transfer(address _to, uint256 _value) public returns (bool success) {
        _transfer(msg.sender, _to, _value);
        return true;
    }

    // 批准spender从合约调用者那里花费多少value
    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowanceOf[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    // spender代替合约调用者转账
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(allowanceOf[_from][msg.sender] >= _value);
        allowanceOf[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    // spender余额
    function allowance(address _owner, address _spender) view public returns (uint remaining){
        return allowanceOf[_owner][_spender];
    }

    // 发行代币总量
    function totalSupply() public constant returns (uint totalsupply){
        return totalSupply;
    }

    // 查看对应账号的代币余额
    function balanceOf(address tokenOwner) public constant returns (uint balance){
        return balanceOf[tokenOwner];
    }

    // 销毁创建者账户中指定个代币
    function burn(uint256 _value) public returns (bool success) {
        // Check if the sender has enough
        require(balanceOf[msg.sender] >= _value);
        // Subtract from the sender
        balanceOf[msg.sender] -= _value;
        // Updates totalSupply
        totalSupply -= _value;
        // 监听Burn事件
        emit Burn(msg.sender, _value);
        return true;
    }

    // 销毁用户账户中指定个代币
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        // Check if the targeted balance is enough
        require(balanceOf[_from] >= _value);
        // Check allowance
        require(_value <= allowanceOf[_from][msg.sender]);
        // Subtract from the targeted balance
        balanceOf[_from] -= _value;
        // Subtract from the sender's allowance
        allowanceOf[_from][msg.sender] -= _value;
        // Update totalSupply
        totalSupply -= _value;
        // 监听Burn事件
        emit Burn(_from, _value);
        return true;
    }


    /// 向指定账户增发资金,此时totalSupply就会增加数量,不会自动在后面补0
    // 只有owner能分发,如果transferOwnership的newOwner发生改变那只能用newOwner来调用
    function mintToken(address target, uint256 mintedAmount) public {
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;
        emit Transfer(address(0), address(this), mintedAmount);
        emit Transfer(address(this), target, mintedAmount);
    }
}
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218

我们将合约进行编译获取到abi和bin文件。

solc --abi Sar.sol
solc --bin Sar.sol
  • 1
  • 2

现在让我们用abigen将abi和bin转换为我们可以导入的Go文件。 这个abi文件将包含我们可以用来与Go应用程序中的智能合约进行交互的所有可用方法,bin文件可以让我们部署合约。

abigen --abi token.abi --bin token.bin --pkg=token --out=token.go

--abi token.abi	指定abi文件来源
--pkg token	指定输出文件的包名
--out token.go	指定合约交互文件名称
  • 1
  • 2
  • 3
  • 4
  • 5

有了合约的接口之后,我们现在就可以开始调用合约的方法了。

package api

import (
	"ccmWeb/token"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func Name(c *gin.Context) {
	conn, err := ethclient.Dial(Url)
	if err != nil {
		log.Fatal(err)
	}

	// contractAddress
	tokenAddress := common.HexToAddress("0xe0f8e3108109ac2cb2ed812b4fd91a38b11f1446")
	instance, err := token.NewToken(tokenAddress, conn)
	if err != nil {
		log.Fatal(err)
	}

	// call token name
	name, err := instance.Name(&bind.CallOpts{})
	if err != nil {
		log.Fatal(err)
	}
	c.JSON(http.StatusOK, gin.H{
		"name": name,
	})
}
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

对于详细的学习步骤,推荐大家学习这个教程。https://goethereumbook.org/zh/smart-contract-deploy/。

总结:本人在编写go调用合约的过程中,遇到的最多的问题就是关于拉取国外依赖,go的依赖包,以及调用合约的写入方法时会遇到比较多。当然了,go语言就是依赖包比较多的一种语言,要使用起来也不难。但是在用合约的开发调用过程中,我还是倾向于使用nodejs语言,web3js的使用让我们编写起来会更加顺畅一些,如果您是go开发出身,当然用go也是比较适合的。

关于以太坊的合约方面的入门学习,我们就讲到这里了,后面的课程中,我们再来讲讲其它的一些知识体系。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/877807
推荐阅读
相关标签
  

闽ICP备14008679号