60、solidity编程语言了解、实践-1——2020年07月12日12:01:12

2020年07月12日12:04:35

2019年09月26日10:01:43

solidity编程语言了解、实践-1

1.pragma

版本标识指令,用来启动编译器检查。

使用规范:

pragma solidity ^0.5.2;

这样的话,源文件不允许低于0.5.2版本的编译器编译,也不允许高于0.6.0版本的编译器编译。(第二个条件因使用^被添加)

2.合约结构

2.1 介绍

每个合约可以包括状态变量,函数,事件Event,结构体,枚举类型,合约可以从其他合约继承。

2.2 状态变量

指永久地存储在合约中的值。

合约内——方法外

pragma solidity >=0.4.0 <0.7.0;

contract TinyStorage {
    uint storedXlbData; // 状态变量
    // ...
}

2.3 函数

函数是合约中代码的可执行单元。

pragma solidity >=0.4.0 <0.7.0;

contract TinyAuction {
    function Mybid() public payable { // 定义函数
        // ...
    }
}

函数接受参数和返回值。

2.4 函数修饰器 modifier

修饰符可以用来以声明的方式来修改函数语义。

pragma solidity >=0.4.22 <0.7.0;

contract MyPurchase {
    address public seller;

    modifier onlySeller() { // 修饰器
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _;
    }

    function abort() public onlySeller { // 修饰器用法
        // ...
    }
}

2.4 事件Event

事件是能方便地调用以太坊虚拟机日志功能的接口。

pragma solidity >=0.4.21 <0.7.0;
contract TinyAuction {
    event HighestBidIncreased(address bidder, uint amount); // 事件

    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // 触发事件
    }
}

事件编写:

pragma solidity  >=0.4.21 <0.7.0;

contract ClientReceipt {
    event Deposit(
        address indexed _from,
        bytes32 indexed _id,
        uint _value
    );

    function deposit(bytes32 _id) public payable {
        // 事件使用 emit 触发事件。
        // 我们可以过滤对 `Deposit` 的调用,从而用 Javascript API 来查明对这个函数的任何调用(甚至是深度嵌套调用)。
        emit Deposit(msg.sender, _id, msg.value);
    }
}

使用 JavaScript API 调用事件的用法如下:

var abi = /* abi 由编译器产生 */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...xlb67" /* 地址 */);

var event = clientReceipt.Deposit();

// 监听变化
event.watch(function(error, result) {
    // 结果包含 非索引参数 以及 主题 topic
    if (!error)
        console.log(result);
});

// 或者通过传入回调函数,立即开始听监
var event = clientReceipt.Deposit(function(error, result) {
    if (!error)
        console.log(result);
});

上面的输出如下所示(有删减):

{
   "returnValues": {
       "_from": "0x1111…FFFFCCCC",
       "_id": "0x50…sd5adb20",
       "_value": "0x420042"
   },
   "raw": {
       "data": "0x7f…91385",
       "topics": ["0xfd4…b4ead7", "0x7f…1a91385"]
   }
}

2.5 结构体

结构体是可以将几个变量分组的自定义类型。

pragma solidity >=0.4.0 <0.7.0;

contract TinyBallot {
    struct Voter { // 结构体
        uint weight;
        bool voted;
        address delegate;
        uint vote;
    }
}

2.6 枚举类型

枚举可用来创建一定数量的“常量值”构成的自定义类型。

pragma solidity >=0.4.0 <0.7.0;

contract Upchain {
    enum State { Created, Locked, InValid } // 枚举
}

3.类型

“undefined”或“null”值的概念在Solidity中不存在,但是新声明的变量总是有一个默认值,具体的默认值跟类型相关。

3.1 值类型

3.1.1 bool :true或者false
3.1.2 整形

int / uint :分别表示有符号和无符号的不同位数的整型变量。 支持关键字 uint8 到 uint256 (无符号,从 8 位到 256 位)以及 int8 到 int256,以 8 位为步长递增。uint 和 int 分别是 uint256 和 int256 的别名。

3.2 地址类型

地址类型有两种形式,他们大致相同:

  • address:保存一个20字节的值(以太坊地址的大小)。
  • ddress payable :可支付地址,与 address 相同,不过有成员函数 transfer 和 send 。

这种区别背后的思想是 address payable 可以接受以太币的地址,而一个普通的 address 则不能。

类型转换:

允许从 address payable 到 address 的隐式转换,而从 address 到 address payable 的转换是不可以的( 执行这种转换的唯一方法是使用中间类型,先转换为 uint160 )

大部分情况下你不需要关心 address 与 address payable 之间的区别,并且到处都使用 address 。 例如,如果你在使用取款模式, 你可以(也应该)保存地址为 address 类型, 因为可以在 msg.sender 对象上调用 transfer 函数, 因为 msg.sender 是 address payable。

address 和 address payable 的区别是在 0.5.0 版本引入的,同样从这个版本开始,合约类型不再继承自地址类型,不过如果合约有可支付的回退( payable fallback )函数,合约类型仍然可以显示转换为 address 或 address payable 。

3.2.1 balance tranfer

可以使用 balance 属性来查询一个地址的余额, 也可以使用 transfer 函数向一个可支付地址(payable address)发送 Ether (以 wei 为单位)

address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

如果当前合约的余额不够多,则 transfer 函数会执行失败,或者如果以太转移被接收帐户拒绝, transfer 函数同样会失败而进行回退。

3.2.2 send

send 是 transfer 的低级版本。如果执行失败,当前的合约不会因为异常而终止,但 send 会返回 false。

3.2.3 call, delegatecall 和 staticcall

为了与不符合应用二进制接口Application Binary Interface(ABI) 的合约交互,或者要更直接地控制编码,提供了函数 call,delegatecall 和 staticcall 。 它们都带有一个bytes memory参数和返回执行成功状态(bool)和数据(bytes memory)。

所有三个函数 call ,delegatecall 和 staticcall 都是非常低级的函数,应该只把它们当作 最后一招 来使用,因为它们破坏了 Solidity 的类型安全性。

3.3 合约类型

每一个contract定义都有他自己的类型。

您可以隐式地将合约转换为从他们继承的合约。 合约可以显式转换为 address 类型。

只有当合约具有可支付回退函数时,才能显式和 address payable 类型相互转换 转换仍然使用 address(x) 执行,而不是使用 address payable(x) 。

在版本0.5.0之前,合约直接从地址类型派生的, 并且 address 和 address payable 之间没有区别。

3.4 字符串字面常量及类型

字符串字面常量是指由双引号或单引号引起来的字符串("foo" 或者 'bar')。 不像在 C 语言中那样带有结束符;"foo" 相当于 3 个字节而不是 4 个。 和整数字面常量一样,字符串字面常量的类型也可以发生改变,但它们可以隐式地转换成 bytes1,……,bytes32,如果合适的话,还可以转换成 bytes 以及 string。

3.5 映射

映射类型在声明时的形式为 mapping(_KeyType => _ValueType)。 其中 _KeyType 可以是任何基本类型,即可以是任何的内建类型加上bytes and string。 而用户定义的类型或复杂的类型如:合约类型、枚举、映射、结构体、即除bytes and string 之外的数组类型 是不可以作为 _KeyType 的类型的。

_ValueType 可以是包括映射类型在内的任何类型。

映射可以视作 哈希表 ,它们在实际的初始化过程中创建每个可能的 key, 并将其映射到字节形式全是零的值:一个类型的 默认值。然而下面是映射与哈希表不同的地方: 在映射中,实际上并不存储 key,而是存储它的 keccak256 哈希值,从而便于查询实际的值。

正因为如此,映射是没有长度的,也没有 key 的集合或 value 的集合的概念。

pragma solidity >=0.4.0 <0.7.0;

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}

contract MappingLBC {
    function f() public returns (uint) {
        MappingExample m = new MappingExample();
        m.update(100);
        return m.balances(this);
    }
}
posted @ 2020-07-12 12:01  一颗小苹果  阅读(511)  评论(0)    收藏  举报