初探Solidity

初探Solidity

基础概念

没有浮点数运算

为了弥补这个缺陷,如何表示带小数的数字呢?以太坊将自身的数切分为最小18位,我们称为wei,任何数字都是wei的整数倍

44A06DF3-DABB-4BD0-86EE-86A0726C014C
44A06DF3-DABB-4BD0-86EE-86A0726C014C

变量类型

智能合约的变量分为两种,存在区块链上的和不存在区块链上的。存在区块链上的我们称为状态变量(state variable)。这类变量将永久记录在区块链上,写入读取它们就仿佛操作一个数据库一样,修改和赋值都会造成巨额的开销。不存在于区块链上的变量则是程序中的内存变量(memory variable),程序运行完毕就从内存中释放,相对开销较小

internal/external修饰符

  • internal修饰符可以让合约继承后子合约访问该函数
  • external 修饰符让该函数只能被外部调用者调用

Solidity的函数也有修饰符,称为modifiers,这标明了函数可能对区块链状态有无修改/读取的标记。一般都会标记该值让编译器帮我们执行代码的优化

672641FC-3F0D-45C1-921E-05293A6D99FD
672641FC-3F0D-45C1-921E-05293A6D99FD

内置函数

  • keccak256 散列函数算法,可以根据任意长度的明文产生固定长度256位的哈希值

数据结构:Map

映射数据结构:mapping

例如我们可以存储账号地址以及它对应的合约内的token数量的关系(假设是一个代币合约)

1CC08B0E-986A-4DDA-9732-C7A7DAB23454
1CC08B0E-986A-4DDA-9732-C7A7DAB23454

环境变量:msg.sender

当前合约调用者的地址

FD0D5090-B4DB-40B3-AE34-36C17DB7A106
FD0D5090-B4DB-40B3-AE34-36C17DB7A106

require / assert

在合约中进行权限检查或者条件满足检查

  • require 条件检查语句如果不通过,则扣除运行到当前语句时,程序执行所话费的gas,终止程序执行,并返回
  • assert 条件检查语句如果不通过,则视为严重错误,扣除所有的gas,终止程序执行,并返回
6CFC6B7B-5EAF-4AE4-8D89-4514B8C65B9E
6CFC6B7B-5EAF-4AE4-8D89-4514B8C65B9E

继承和引入

智能合约的代码可以来源于自身项目内,也可以来源于外部早已部署完毕的链上合约

CDE78801-35DE-4599-BBA1-7F536A4F5C6A
CDE78801-35DE-4599-BBA1-7F536A4F5C6A
C77EB3A9-9CD3-463A-9A1A-787056410F6F
C77EB3A9-9CD3-463A-9A1A-787056410F6F

内存变量

不同的存储类型,花费的gas数额不同。它们的最终存储地方也不同。有时候为了省钱,我们会把临时变量留在内存里,而不是保存在区块链上。随着程序执行,内存里的变量会消亡,而区块链上的会永存。由于没有改变区块链状态,内存变量(memory)的花费会比状态变量(storage)的花费少很多

9047DF90-02CA-48D2-B48D-CE2B597A5BD9
9047DF90-02CA-48D2-B48D-CE2B597A5BD9

接口与合约调用

合约的接口就是合约的抽象。我们可以通过定义合约接口,并指定合约地址,来调用另外一个在以太坊上早已经部署好的合约

ECC942AC-4156-445B-9542-F6379B490E37
ECC942AC-4156-445B-9542-F6379B490E37
8CDE7C2D-CB8D-41E3-833F-1446E07D5FDE
8CDE7C2D-CB8D-41E3-833F-1446E07D5FDE

多返回值

09363FC1-2333-472A-8116-1ABDB5BF929F
09363FC1-2333-472A-8116-1ABDB5BF929F

Ownable控制

753431E0-28EA-4C7B-9C26-DDE534A6B4AE
753431E0-28EA-4C7B-9C26-DDE534A6B4AE

Pausable控制

367FDA31-4F30-4E7F-8E41-BF0F9190CF36
367FDA31-4F30-4E7F-8E41-BF0F9190CF36

时间单位表示

86C34667-D059-4B79-8AB4-6F6ABA3AAD58
86C34667-D059-4B79-8AB4-6F6ABA3AAD58

这里now和5 minutes都属于时间表示,我们甚至可以直接将时间单位赋值给uint类型的变量。以下的语句是完全合法的。

uint lastUpdated = now;
  uint one_day = 1 days;
  uint five_minutes = 5 minutes;
  uint now_time = now;
  uint two_hours = 2 hours;

带参数的函数修饰符

// ID 和年龄的映射
mapping (uint => uint) public age;

// 一个函数修饰
modifier olderThan(uint _age, uint _userId) {
  require(age[_userId] >= _age);
  _;
}

// 限制年龄大于16岁才能开车
function driveCar(uint _userId) public olderThan(16, _userId) {

}

for循环

环在智能合约中使用范围不是很明显。因为循环消耗大量的计算步骤,智能合约编纂者都尽量减少循环次数,甚至从根本上避免循环的产生

function getOdds() pure external returns(uint[]) {
  uint[] memory odds = new uint[](5);

  uint counter = 0;

  for (uint i = 1; i <= 10; i++) {
    if (i % 2 != 0) {
      odds[counter] = i;
      counter++;
    }
  }
  return odds;
}

合约收款:payable修饰符

contract OnlineStore {
  function buySomething() external payable {
    // 查看付款金额
    require(msg.value == 0.001 ether);
    // 将对应金额的物品转移给调用方
    transferSomething(msg.sender);
  }
}

上面多了一个关键字payable和两个环境变量:msg.value和msg.sender。payable表示调用者可以在调用该方法时附上想发送的以太币数量;msg.value则表示实际使用中调用者支付的以太币,这里要和gas费用相区别,gas费用是付给矿工来执行合约的,而 msg.value则是直接付给智能合约的费用。在智能合约收到以太币后,可以直接调用其他函数进行等价交换。这种函数经常在ICO的代币发行中使用,让广大用户可以直接通过合约交换以太坊为代币,无需人工干预,公平、公正、公开

支付费用:transfer方法

智能合约的作者往往能获得报酬。怎么获得呢?就是通过用户不断向智能合约打入以太币,并积累在智能合约的balance余额中。当智能合约所有者想提取的时候,调用一个函数就可以执行

A984B0D7-FBB5-4CA0-9FC7-DB47FB2BC256
A984B0D7-FBB5-4CA0-9FC7-DB47FB2BC256

Truffle

Truffle内置了一个默认的测试底层网络实现 Ganache。Ganache 原名 Test-RPC,是以太坊开源项目中采用JavaScript编写的模拟区块链运行的节点。它本身并不联网产生区块,而是一个测试环境中的高度仿真模拟器。它的Web3命令支持丰富,紧跟社区的最新提案和开发进度,因为没有实际挖矿(仅仅模拟挖矿行为),所以它的运行速度是惊人的,非常利于测试环境下的高速使用,不需要等实际区块链的出块时间间隔

EFD042A4-D6FD-4DD6-A83C-15F52C7E3FF8.png
EFD042A4-D6FD-4DD6-A83C-15F52C7E3FF8.png

命令

91F22119-838F-4434-9836-83983AAB103D
91F22119-838F-4434-9836-83983AAB103D
$ mkdir truffle-project
$ cd truffle-project
$ truffle unbox metacoin
2E0AF910-243F-4C8F-8013-A6376F777513
2E0AF910-243F-4C8F-8013-A6376F777513
453E6230-9C76-4E60-9078-95117039817D
453E6230-9C76-4E60-9078-95117039817D

每个目录具体解释如下:

  • contracts目录包含了主要的合约代码,示例中包含了MetaCoin.sol(主要合约),ConvertLib.sol (库文件)和部署时记录地址的合约 Migration.sol
  • migrations目录包含了两个部署文件,先会部署Migration.sol, 再会链接ConvertLib.sol与MetaCoin.sol部署到链上,两者顺序不可颠倒。代码中aritfacts.require()函数相当于node.js中的require()函数,指明了我们希望引用哪个合约的抽象。这个引用名必须与合约中的类名相同
  • test目录包含了两种不同的测试文件,智能合约可以通过sol结尾的合约进行测试写作,也可以通过js文件写作,两者功能几乎一致,笔者在开发组多时,考虑到语言的通用性与灵活程度,平时一直使用js的格式书写测试。Truffle的测试都是Mocha测试框架格式的语法
  • truffle-config.js和truffle.js都保存了控制truffle行为定义的全局变量,当想调换测试网络时,可以编辑truffle.js指定网络和端口

ERC20合约

3C20E549-C0C0-4A14-9142-6E565F46A871
3C20E549-C0C0-4A14-9142-6E565F46A871
Subscribe to MarkTang's Blog
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.