MHuiG

「看庭前花开花落,望天上云卷云舒」

# 部署第一个智能合约

MHuiG

字数：3.4k 字

时长：15 分钟

注意访问本文的引用链接可能需要魔法

对区块链最好的描述是将其描述为一个公共数据库，它由网络中的许多计算机更新和共享。

## 小试身手

我猜您和我们一样会很兴奋在以太坊区块链上部署智能合约并与之交互。

别担心，作为我们的第一个智能合约，我们会将其部署在本地测试网络上，因此您不需要任何开销就可以随意部署和运行它。

### 编写合约

第一步访问 Remix 并创建一个新文件。 在 Remix 界面的左上角添加一个新文件，并输入所需的文件名。

在这个新文件中，我们将粘贴如下代码：

    // SPDX-License-Identifier: MIT
    pragma solidity >=0.5.17;

    contract Counter {

        // Public variable of type unsigned int to keep the number of counts
        uint256 public count = 0;

        // Function that increments our counter
        function increment() public {
            count += 1;
        }

        // Not necessary getter to get the count value
        function getCount() public view returns (uint256) {
            return count;
        }

    }

如果您曾经写过程序，应该可以轻松猜到这个程序是做什么的。 下面按行解释：

- 第 3 行：定义了一个名为 `Counter` 的合约。
- 第 6 行：我们的合约存储了一个无符号整型 `count`，从 0 开始。
- 第 9 行：第一个函数将修改合约的状态并且 `increment()` 变量 `count`。
- 第 14 行，第二个函数是一个 getter 函数，能够从智能合约外部读取 `count` 变量的值。 请注意，因为我们将 `count` 变量定义为公共变量，所以这个函数是不必要的，但它可以作为一个例子展示。

第一个简单的智能合约到此结束。 正如您所知，它看上去像是 Java、C++ 这样的面向对象编程语言中的一个类。 现在可以运行我们的合约了。

### 部署合约

当我们写了第一个智能合约后，我们现在可以将它部署在区块链中并运行它。

在区块链上部署智能合约实际上只是发送了一个包含已编译智能合约代码的交易，并且没有指定任何收件人。

我们首先点击左侧的编译图标来编译合约：

然后点击编译按钮：

您可以选择 “自动编译” 选项，这样当您在文本编辑器中保存内容时，合约始终会自动编译。

然后切换到部署和运行交易屏幕：

在 “部署和运行交易” 屏幕上，仔细检查显示的合约名称并点击 “部署”。 在页面顶部可以看到，当前环境为 “Javascript VM”，这意味着当前我们在本地测试区块链上部署智能合约并交互，这样测试可以更快，也不需要任何费用。

点击 “部署” 按钮后，您可以看到合约在底部显示出来。 点击左侧的箭头展开，可以看到合约的内容。 这里有我们的变量 `counter`、函数 `increment()` 和 getter `getCounter()`。

如果您点击 `count` 或 `getCount` 按钮，它将实际检索合约的 `count` 变量的内容，并显示出来。 因为我们尚未调用 `increment` 函数，它应该显示 0。

现在点击按钮来调用 `increment` 函数。 您可以在窗口底部看到交易产生的日志。 当按下检索数据按钮而非 `increment` 按钮时，您看到的日志有所不同。 这是因为读取区块链的数据不需要任何交易（写入）或费用。 因为只有修改区块链的状态需要进行交易。

在按下 increment 按钮后，将产生一个交易来调用我们的 `increment()` 函数，如果我们点击 count 或 getCount 按钮，将读取我们的智能合约的最新状态，count 变量大于 0。

### 使用事件记录智能合约中的数据

在 solidity 中，事件是智能合约可触发的调度信号。 去中心化应用或其他任何连接到以太坊 JSON-PRC API 的程序，都可以监听这些事件，并执行相应操作。 可以建立事件的索引，以便稍后可以搜索到事件历史记录。

在撰写这篇文章之时，以太坊区块链上最常见的事件是由 ERC20 代币转账时触发的 Transfer 事件。

    event Transfer(address indexed from, address indexed to, uint256 value);

事件签名在合约代码内声明，并且可以使用 emit 关键字来触发。 例如，transfer 事件记录了谁发起了转账 (from)，转账给谁 (to)，以及转账的代币数转账 (value)。

我们再次回到 Counter 智能合约，决定在每次值发生变化时进行记录。 由于这个合约不是为了部署，而是作为基础，通过扩展来构建另一个合约：因此它被称为抽象合约。 在我们 counter 示例中，它将类似于如下：

    pragma solidity 0.5.17;

    contract Counter {

        event ValueChanged(uint oldValue, uint256 newValue);

        // Private variable of type unsigned int to keep the number of counts
        uint256 private count = 0;

        // Function that increments our counter
        function increment() public {
            count += 1;
            emit ValueChanged(count - 1, count);
        }

        // Getter to get the count value
        function getCount() public view returns (uint256) {
            return count;
        }

    }

注意：

- 第 5 行：我们声明了事件及其包含的内容、旧值以及新值。

- 第 13 行：当我们增加 count 变量的值时，我们会触发事件。

如果我们现在部署合约并调用 increment 函数，如果您在名为 logs 的数组内单击新交易，我们将看到 Remix 会自动显示它。

日志在调试智能合约时非常有用，另一方面，如果您构建一个不同人使用的应用，并且使分析更容易跟踪和了解您的智能合约的使用情况，那么日志也是非常重要的手段。 交易生成的日志会显示常见的区块浏览器中，并且，举例来说，您也可以使用它们来创建链外脚本，用于侦听特定的事件，并且这些事件发生时采取相应操作。

## 注册钱包

### 安装 Chrome MetaMask 插件

https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn

注册绑定你的账号。

### 添加 BSC Testnet （测试网络）

添加自定义网络

- 网络名称：BSC Testnet

- RPC URL: https://data-seed-prebsc-1-s1.binance.org:8545/

- 链 ID: 0x61

- 区块浏览器：https://testnet.bscscan.com/

- 货币符号: BNB

### 每日领取测试 BNB

进入 https://testnet.binance.org/faucet-smart , 填入账户 Address 领取测试链的 BNB。

BNB 用于支付测试网络的燃料费用.

在 metamask 钱包中也可以看到这笔 BNB 到账了。

## 发行 MHGC (MHuiGCoin) 代币

进入 Remix 环境 中

### FILE EXPLORERS

依次创建三个文件: `EIP20.sol`、`EIP20Factory.sol`、`EIP20Interface.sol`。

EIP20.sol

    /*
    Implements EIP20 token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
    .*/

    pragma solidity ^0.4.21;

    import "./EIP20Interface.sol";

    contract EIP20 is EIP20Interface {

        uint256 constant private MAX_UINT256 = 2**256 - 1;
        mapping (address => uint256) public balances;
        mapping (address => mapping (address => uint256)) public allowed;
        /*
        NOTE:
        The following variables are OPTIONAL vanities. One does not have to include them.
        They allow one to customise the token contract & in no way influences the core functionality.
        Some wallets/interfaces might not even bother to look at this information.
        */
        string public name;                   //fancy name: eg Simon Bucks
        uint8 public decimals;                //How many decimals to show.
        string public symbol;                 //An identifier: eg SBX

        function EIP20(
            uint256 _initialAmount,
            string _tokenName,
            uint8 _decimalUnits,
            string _tokenSymbol
        ) public {
            balances[msg.sender] = _initialAmount;               // Give the creator all initial tokens
            totalSupply = _initialAmount;                        // Update total supply
            name = _tokenName;                                   // Set the name for display purposes
            decimals = _decimalUnits;                            // Amount of decimals for display purposes
            symbol = _tokenSymbol;                               // Set the symbol for display purposes
        }

        function transfer(address _to, uint256 _value) public returns (bool success) {
            require(balances[msg.sender] >= _value);
            balances[msg.sender] -= _value;
            balances[_to] += _value;
            emit Transfer(msg.sender, _to, _value); //solhint-disable-line indent, no-unused-vars
            return true;
        }

        function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
            uint256 allowance = allowed[_from][msg.sender];
            require(balances[_from] >= _value && allowance >= _value);
            balances[_to] += _value;
            balances[_from] -= _value;
            if (allowance < MAX_UINT256) {
                allowed[_from][msg.sender] -= _value;
            }
            emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars
            return true;
        }

        function balanceOf(address _owner) public view returns (uint256 balance) {
            return balances[_owner];
        }

        function approve(address _spender, uint256 _value) public returns (bool success) {
            allowed[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars
            return true;
        }

        function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
            return allowed[_owner][_spender];
        }
    }

EIP20Factory.sol

    import "./EIP20.sol";

    pragma solidity ^0.4.21;

    contract EIP20Factory {

        mapping(address => address[]) public created;
        mapping(address => bool) public isEIP20; //verify without having to do a bytecode check.
        bytes public EIP20ByteCode; // solhint-disable-line var-name-mixedcase

        function EIP20Factory() public {
            //upon creation of the factory, deploy a EIP20 (parameters are meaningless) and store the bytecode provably.
            address verifiedToken = createEIP20(10000, "Verify Token", 3, "VTX");
            EIP20ByteCode = codeAt(verifiedToken);
        }

        //verifies if a contract that has been deployed is a Human Standard Token.
        //NOTE: This is a very expensive function, and should only be used in an eth_call. ~800k gas
        function verifyEIP20(address _tokenContract) public view returns (bool) {
            bytes memory fetchedTokenByteCode = codeAt(_tokenContract);

            if (fetchedTokenByteCode.length != EIP20ByteCode.length) {
                return false; //clear mismatch
            }

          //starting iterating through it if lengths match
            for (uint i = 0; i < fetchedTokenByteCode.length; i++) {
                if (fetchedTokenByteCode[i] != EIP20ByteCode[i]) {
                    return false;
                }
            }
            return true;
        }

        function createEIP20(uint256 _initialAmount, string _name, uint8 _decimals, string _symbol)
            public
        returns (address) {

            EIP20 newToken = (new EIP20(_initialAmount, _name, _decimals, _symbol));
            created[msg.sender].push(address(newToken));
            isEIP20[address(newToken)] = true;
            //the factory will own the created tokens. You must transfer them.
            newToken.transfer(msg.sender, _initialAmount);
            return address(newToken);
        }

        //for now, keeping this internal. Ideally there should also be a live version of this that
        // any contract can use, lib-style.
        //retrieves the bytecode at a specific address.
        function codeAt(address _addr) internal view returns (bytes outputCode) {
            assembly { // solhint-disable-line no-inline-assembly
                // retrieve the size of the code, this needs assembly
                let size := extcodesize(_addr)
                // allocate output byte array - this could also be done without assembly
                // by using outputCode = new bytes(size)
                outputCode := mload(0x40)
                // new "memory end" including padding
                mstore(0x40, add(outputCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
                // store length in memory
                mstore(outputCode, size)
                // actually retrieve the code, this needs assembly
                extcodecopy(_addr, add(outputCode, 0x20), 0, size)
            }
        }
    }

EIP20Interface.sol

    // Abstract contract for the full ERC 20 Token standard
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
    pragma solidity ^0.4.21;

    contract EIP20Interface {
        /* This is a slight change to the ERC20 base standard.
        function totalSupply() constant returns (uint256 supply);
        is replaced with:
        uint256 public totalSupply;
        This automatically creates a getter function for the totalSupply.
        This is moved to the base contract since public getter functions are not
        currently recognised as an implementation of the matching abstract
        function by the compiler.
        */
        /// total amount of tokens
        uint256 public totalSupply;

        /// @param _owner The address from which the balance will be retrieved
        /// @return The balance
        function balanceOf(address _owner) public view returns (uint256 balance);

        /// @notice send `_value` token to `_to` from `msg.sender`
        /// @param _to The address of the recipient
        /// @param _value The amount of token to be transferred
        /// @return Whether the transfer was successful or not
        function transfer(address _to, uint256 _value) public returns (bool success);

        /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
        /// @param _from The address of the sender
        /// @param _to The address of the recipient
        /// @param _value The amount of token to be transferred
        /// @return Whether the transfer was successful or not
        function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);

        /// @notice `msg.sender` approves `_spender` to spend `_value` tokens
        /// @param _spender The address of the account able to transfer the tokens
        /// @param _value The amount of tokens to be approved for transfer
        /// @return Whether the approval was successful or not
        function approve(address _spender, uint256 _value) public returns (bool success);

        /// @param _owner The address of the account owning tokens
        /// @param _spender The address of the account able to transfer the tokens
        /// @return Amount of remaining tokens allowed to spent
        function allowance(address _owner, address _spender) public view returns (uint256 remaining);

        // solhint-disable-next-line no-simple-event-func-name
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    }

### SOLIDITY COMPILER

我们选择 0.4.21 版本的编译环境.

点击编译.

在这里你可以将源码发布到 `IPFS`.

    Metadata of "eip20" was published successfully.
    token/EIP20.sol :
    dweb:/ipfs/QmZemboWkhVyhYMRyCJmjwhw4caenvXS5Mw7Uc5tdE77jP
    token/EIP20Interface.sol :
    dweb:/ipfs/QmWQEUAdy5QnnCTGsEtRfedZrVG2xZ77YfKiPE5MVvQiSS
    metadata.json :
    dweb:/ipfs/QmWFrJHPJLEWJGAoXpq8MD1pGJsvdcfxhPiW5SDXiwEvm8

### DEPLOY & RUN TRANSACTIONS

运行环境选择 `Injected Web3`

因为我们用的是 metamask 钱包；Account 账户填写 metamask 钱包账户；此时浏览器插件会弹出，我们选择连接账户。点击下一步，点击连接.

在 `DEPLOY` 中输入部署信息，合约构造函数的输入参数.

    _INITIALAMOUNT: "21000000000000000000000000"
    _TOKENNAME: "MHuiGCoin"
    _DECIMALUNITS: "18"
    _TOKENSYMBOL: "MHGC"

发币数量 `21000000`, 和比特币一样，向中本聪致敬。

货币名称 `MHuiGCoin`，最小货币单位 `18`（decimaUnits），货币简称 `MHGC`。

点击 `transact`.

点击确认支付燃料费用.

从 `Deployed Contracts` 复制部署的合约地址: `0xeb86A66E2d4A51F8de3d4d0F509f18A15Cde68F6`.

### 导入代币合约

进入部署合约的 MetaMask 账户，点击导入代币.

输入代币合约信息

- 代币合约地址：0xeb86A66E2d4A51F8de3d4d0F509f18A15Cde68F6

- 代币符号：MHGC

- 小数精度：18

可以看到我的账户中有 `21000000 MHGC (MHuiGCoin)`

### 代币转账测试

我们创建一个新的账户，并导入上面的代币合约.

我们向目标账户发送 `1 MHGC` 测试

需要支付测试网络的燃料费用.

转账发送成功.

### 测试网络资源管理器

在资源管理器中查看 资产:

https://testnet.bscscan.com/token/0xeb86A66E2d4A51F8de3d4d0F509f18A15Cde68F6

可以看到资源信息和公开的转账记录.

如你所见这些在区块链中已经发生的历史信息是无法更改的.

## 安全风险提示

任何人都可以创建代币，包括创建现有代币的假版本。了解更多关于 欺诈和安全风险.

2016 年 6 月，The DAOEther 的漏洞造成损失 5000 万美元，而开发者试图达成共识的解决方案。DAO 的程序在黑客删除资金之前有一段时间的延迟。以太坊软件的一个硬分叉在时限到期之前完成了攻击者的资金回收工作。

2021 年 9 月 24 日，中国人民银行发布进一步防范和处置虚拟货币交易炒作风险的通知。通知指出，虚拟货币不具有与法定货币等同的法律地位。比特币、以太币、泰达币等虚拟货币具有非货币当局发行、使用加密技术及分布式账户或类似技术、以数字化形式存在等主要特点，不具有法偿性，不应且不能作为货币在市场上流通使用 。

## 最后

### MHGC 获取方式

#### 测试网络

- 网络名称：BSC Testnet

- RPC URL: https://data-seed-prebsc-1-s1.binance.org:8545/

- 链 ID: 0x61

- 区块浏览器：https://testnet.bscscan.com/

- 货币符号: BNB

#### 代币合约

- 代币合约地址：0xeb86A66E2d4A51F8de3d4d0F509f18A15Cde68F6

- 代币符号：MHGC

- 小数精度：18

### 尾巴

联系我，发我地址，在 BSC Testnet 下可领取 `100 MHGC`.

共发行 `21000000 MHGC`, 集齐 `22000000 MHGC` 可领取精美礼品一份.

Just For Fun.

### 参考资料

- Welcome to Ethereum

以太坊智能合约 以太坊 智能合约

以太坊

智能合约

区块链

以太坊 智能合约 区块链

发布于：2022年3月1日

更新于：2022年3月1日

CoPoKo 用户文档

.fa-secondary{opacity:.4} CoPoKo CoPoKo 开始阅读

唯物辩证法

唯物辩证法是关于自然界、人类社会以及人类思维领域发展最一般规律的科学，它在坚持唯物论观点的基础上，研究世界的运行状况、形态和发展规律，进一步回答客观世界究竟 “怎么样” 的问题。 唯物辩证法...

留言区

本文目录
<pjax>
</pjax>
<pjax></pjax>
<pjax> Are you Robot?</pjax>