solidity 入门 1

创作人 Leo


编辑时间 Fri Jul 29,2022 at 19:47


环境搭建

推荐使用 truffle 套件

vscode 开发 https://github.com/ethereum/solc-bin 下载js编译器

包括:

  1. 合约开发部署框架 Truffle,一个轻量级基于命令行的 solidity 开发工具
  2. Ganache,本地开发链
  3. 选配一个 MetaMask 浏览器插件钱包,方便转账操作

https://www.trufflesuite.com/

快速开始

https://www.trufflesuite.com/docs/truffle/quickstart

部署

truffle migrate

RPC 查询

curl -X POST –data ‘{“jsonrpc”:“2.0”,“method”:“eth_getTransactionReceipt”,“params”:[“0xa5f225d77af5a484c03d242a09b4d339c1cd83a3080a2a81866c86874fe3c403”],“id”:1}’ ‘http://127.0.0.1:7545’

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": {
        "transactionHash": "0xa5f225d77af5a484c03d242a09b4d339c1cd83a3080a2a81866c86874fe3c403",
        "transactionIndex": "0x0",
        "blockHash": "0x1af8de1ce09e3ca5ec66f71740d1baf020a3b91d1bf58cf7b6b22f659685cf4d",
        "blockNumber": "0x54",
        "from": "0xbaea55dcb4e21b33197e2714493f8735d8c621dd",
        "to": "0xf6bc93faa92aca46cc1c25f63c4f6fd6eaf926ea",
        "gasUsed": "0xb7ee",
        "cumulativeGasUsed": "0xb7ee",
        "contractAddress": null,
        "logs": [
            {
                "logIndex": "0x0",
                "transactionIndex": "0x0",
                "transactionHash": "0xa5f225d77af5a484c03d242a09b4d339c1cd83a3080a2a81866c86874fe3c403",
                "blockHash": "0x1af8de1ce09e3ca5ec66f71740d1baf020a3b91d1bf58cf7b6b22f659685cf4d",
                "blockNumber": "0x54",
                "address": "0xf6bc93faa92aca46cc1c25f63c4f6fd6eaf926ea",
                "data": "0x000000000000000000000000baea55dcb4e21b33197e2714493f8735d8c621dd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000f4240",
                "topics": [
                    "0x7b821859205dbb4063d705c2fd11eca0e3de6c371e9d8967b1e7ff0fabd102a0"
                ],
                "type": "mined"
            }
        ],
        "status": "0x1",
        "logsBloom": "0x00000000000000000000000000000000000000000000000100000000000000000000000020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000"
    }
}

curl -X POST –data ‘{“jsonrpc”:“2.0”,“method”:“eth_getTransactionByHash”,“params”:[“0xa5f225d77af5a484c03d242a09b4d339c1cd83a3080a2a81866c86874fe3c403”],“id”:1}’ ‘http://127.0.0.1:7545’

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": {
        "hash": "0xa5f225d77af5a484c03d242a09b4d339c1cd83a3080a2a81866c86874fe3c403",
        "nonce": "0x50",
        "blockHash": "0x1af8de1ce09e3ca5ec66f71740d1baf020a3b91d1bf58cf7b6b22f659685cf4d",
        "blockNumber": "0x54",
        "transactionIndex": "0x0",
        "from": "0xbaea55dcb4e21b33197e2714493f8735d8c621dd",
        "to": "0xf6bc93faa92aca46cc1c25f63c4f6fd6eaf926ea",
        "value": "0x0",
        "gas": "0x6691b7",
        "gasPrice": "0x4a817c800",
        "input": "0xfa533a3c000000000000000000000000baea55dcb4e21b33197e2714493f8735d8c621dd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000f4240",
        "v": "0x26",
        "r": "0x5d6005b7decfba946c52cadbde9d77438a0b7baad50efdb020470827b39a972b",
        "s": "0x5934c4b0ec184b3f75e5efbd2eba4fc3c6b6f1a7cbc9e044bc62339a4da57611"
    }
}

curl -X POST –data ‘{“jsonrpc”:“2.0”,“method”:“eth_getBlockByNumber”,“params”:[“latest”, true],“id”:1}’ ‘http://127.0.0.1:7545’

{
    "id": 1,
    "jsonrpc": "2.0",
    "result": {
        "number": "0x54",
        "hash": "0x1af8de1ce09e3ca5ec66f71740d1baf020a3b91d1bf58cf7b6b22f659685cf4d",
        "parentHash": "0xabfc052caf237d97c91cb910474191f66a20864bb3130e0f576b9bd7d3e6120f",
        "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "nonce": "0x0000000000000000",
        "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
        "logsBloom": "0x00000000000000000000000000000000000000000000000100000000000000000000000020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000",
        "transactionsRoot": "0x851c37ab94fd176876ef68359c50f5324b7b2176e72c37b64f59b87e6443d7d0",
        "stateRoot": "0xb80dbac714097feeae652dde706bfeb23b619faed4095eaeb98da21142a9a365",
        "receiptsRoot": "0x5f7d1033e3b280942cbe632309bfc4df0170e1223f31710010bbe26654208869",
        "miner": "0x0000000000000000000000000000000000000000",
        "difficulty": "0x0",
        "totalDifficulty": "0x0",
        "extraData": "0x",
        "size": "0x3e8",
        "gasLimit": "0x6691b7",
        "gasUsed": "0xb7ee",
        "timestamp": "0x604cd0d8",
        "transactions": [
            {
                "hash": "0xa5f225d77af5a484c03d242a09b4d339c1cd83a3080a2a81866c86874fe3c403",
                "nonce": "0x50",
                "blockHash": "0x1af8de1ce09e3ca5ec66f71740d1baf020a3b91d1bf58cf7b6b22f659685cf4d",
                "blockNumber": "0x54",
                "transactionIndex": "0x0",
                "from": "0xbaea55dcb4e21b33197e2714493f8735d8c621dd",
                "to": "0xf6bc93faa92aca46cc1c25f63c4f6fd6eaf926ea",
                "value": "0x0",
                "gas": "0x6691b7",
                "gasPrice": "0x4a817c800",
                "input": "0xfa533a3c000000000000000000000000baea55dcb4e21b33197e2714493f8735d8c621dd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000f4240",
                "v": "0x26",
                "r": "0x5d6005b7decfba946c52cadbde9d77438a0b7baad50efdb020470827b39a972b",
                "s": "0x5934c4b0ec184b3f75e5efbd2eba4fc3c6b6f1a7cbc9e044bc62339a4da57611"
            }
        ],
        "uncles": []
    }
}

基本知识

状态变量(State Variables)

变量值会永久存储在合约的存储空间

// simple store example

contract simpleStorage{

    uint valueStore; //state variable

}

visibility 含义:

external
External functions are part of the contract interface, which means they can be called from other contracts and via transactions. An external function f cannot be called internally (i.e. f() does not work, but this.f() works).
函数可以从外部合约通过事务调用
在内部调用不能直接使用 f(),需要使用 this.f()

public
Public functions are part of the contract interface and can be either called internally or via messages. For public state variables, an automatic getter function (see below) is generated.
public 函数可以在内部调用也可以通过消息在外部调用
public 状态变量会自动生成 getter 函数

internal
Those functions and state variables can only be accessed internally (i.e. from within the current contract or contracts deriving from it), without using this. This is the default visibility level for state variables. 函数和变量仅供内部使用(当前合约或者衍生合约)
这是状态变量的默认 visibility 值

private Private functions and state variables are only visible for the contract they are defined in and not in derived contracts. 函数或者状态变量只能被当前合约使用,外部或者衍生合约禁止使用

view

Functions can be declared view in which case they promise not to modify the state.
view 函数不改变状态变量

pure

Functions can be declared pure in which case they promise not to read from or modify the state.
纯函数不读取或者改变状态变量,也就是保存在区块链网络的数据(老版本能读取,但是保证不会修改)

注意:view 和 pure 都不是强制的

payable

如果一个函数可以接收外部转账,则必须标记为payable;比如出价竞拍函数需要发送者携带出价的eth
对于外部合约优先使用pull 而不是push 以太坊智能合约 —— 最佳安全开发指南

// good
contract auction {
    address highestBidder;
    uint highestBid;
    mapping(address => uint) refunds;

    function bid() payable external {
        if (msg.value < highestBid) throw;

        if (highestBidder != 0) {
            refunds[highestBidder] += highestBid; // record the refund that this user can claim
        }

        highestBidder = msg.sender;
        highestBid = msg.value;
    }

    function withdrawRefund() external {
        uint refund = refunds[msg.sender];
        refunds[msg.sender] = 0;
        if (!msg.sender.send(refund)) {
            refunds[msg.sender] = refund; // reverting state because send failed
        }
    }
}

receive Ether 函数

合约需要接受 ether 转账,必须包含一个 receive Ether 函数或者声明为 payable 的 fallback 函数

receive() external payable { ... }

fallback 函数

fallback () external [payable] 
或者
fallback (bytes calldata _input) external [payable] returns (bytes memory _output)
不需要 function 关键字声明

fallback 会在当前合约找不到调用的函数,或者接收到了没有data的tx时被调用

如果 fallback 函数接受 ether 转账,则需要 payable 声明

数据作用域:

memory storage 数据声明

map 的判断:

不同于其他语言,solidity 没有提供 key 是否存在的判断语句,但是可以通过在值结构中设置一个 bool 的 flag 处理

function newHero(bytes32 name, uint32 atk, uint32 def, uint32 amount, uint128 coinId, uint256 price) public payable returns(bool) {
    require(msg.sender==godAddr && coins[coinId].isValid);

    heros[heroIdIncr] = Hero(true, heroIdIncr, name, atk, def, amount, coinId, price);
    heroIdIncr = heroIdIncr+1;

    return true;
} // coins[coinId].isValid 判断该 coin 是否存在
````

返回值:

payable:   
不能从被称为交易一部分的智能合约上返回方法的值(被标记为 payable)。如果希望在交易期间读取值,则必须使用事件event。  
不过交易相关提供的返回值可以被合约的调用获取。  

```plain
function buyHero(uint128 heroId) public payable returns(bool) {
    require(heros[heroId].isValid 
        && heros[heroId].amount>0
        && userBalance[msg.sender][heros[heroId].coinId]>=heros[heroId].price);

    userBalance[msg.sender][heros[heroId].coinId] = userBalance[msg.sender][heros[heroId].coinId] - heros[heroId].price;
    heros[heroId].amount = heros[heroId].amount -1;
    userHeros[msg.sender][heroId] = userHeros[msg.sender][heroId]+1;

    emit BuyHero(msg.sender, heroId, userHeros[msg.sender][heroId]);
    return true;
}
```    


要从智能合约中查询值,必须使用与交易分开的调用。这些方法应该标记为常量函数(view)。由web3j创建的solidity智能合约封装包为你处理这些差异。

```plain
function getHeros(address addr, uint128 heroId) public view returns(uint32) {
    if (userHeros[addr][heroId]>0) {
        return userHeros[addr][heroId];
    }
    return 0;
}

调用外部合约函数:

调用外部合约需要在当前合约声明该合约,并且将需要用到的函数声明
然后通过实例化时指定地址即可实现

EIP20Interface.sol

pragma solidity ^0.5.2;

contract EIP20Interface {
    ...
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
    ...
}

Trade.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.2;

import "./EIP20Interface.sol";

contract Trader {

    EIP20Interface HETAddr = EIP20Interface(0x4Ff144DC970ECEe34B56D6c51a4606199d320293);

    ...

    function recharge(uint128 coinId, uint256 amount) public payable returns(bool) {
        bool ok = HETAddr.transferFrom(msg.sender, godAddr, amount);
        require(ok);
        address to = msg.sender;
        uint256 balance = 0;
        if (userBalance[to][coinId]>0){
            balance = userBalance[to][coinId];
        }
        userBalance[to][coinId] = balance+amount;
        emit Recharge(to, coinId, amount, userBalance[to][coinId]) ;
        return true;
    }

}

阅读:68
搜索
  • Linux 高性能网络编程库 Libevent 简介和示例 2288
  • Mac系统编译PHP7【20190929更新】 2186
  • Hadoop 高可用集群搭建 (Hadoop HA) 1852
  • Linux 常用命令 1837
  • zksync 和 layer2 1832
  • 安徽黄山游 1817
  • Windows 安装Swoole 1794
  • Hadoop 高可用YARN 配置 1750
  • 小白鼠问题 1707
  • 使用 Java+Thrift 实现异步事件处理服务 1627
简介
不定期分享软件开发经验,生活经验