CryptoZombies Solidity基础教程

51

前言

https://cryptozombies.io/ 是一个免费通过一步一步带着你写僵尸小游戏来教你学习Solidity的编程学校。免费的!而且教程以及中文指导(呜呜呜我还是好菜)做的真的很不错!

Part1 & Part2 基础语法

函数的修饰符

view,:只能读取数据不能更改数据:

function sayHello() public view returns (string) {

pure 函数, 表明这个函数甚至都不访问应用里的数据,例如:

function _multiply(uint a, uint b) private pure returns (uint) {
  return a * b;
}

Mapping(映射)

映射 是另一种在 Solidity 中存储有组织数据的方法。

//对于金融应用程序,将用户的余额保存在一个 uint类型的变量中:
mapping (address => uint) public accountBalance;

msg.sender

在 Solidity 中,有一些全局变量可以被所有函数调用。 其中一个就是 msg.sender,它指的是当前调用者(或智能合约)的 address

注意:在 Solidity 中,功能执行始终需要从外部调用者开始。 一个合约只会在区块链上什么也不做,除非有人调用其中的函数。所以 msg.sender总是存在的。

以下是使用 msg.sender 来更新 mapping 的例子:

mapping (address => uint) favoriteNumber;

function setMyNumber(uint _myNumber) public {
  // 更新我们的 `favoriteNumber` 映射来将 `_myNumber`存储在 `msg.sender`名下
  favoriteNumber[msg.sender] = _myNumber;
  // 存储数据至映射的方法和将数据存储在数组相似
}

function whatIsMyNumber() public view returns (uint) {
  // 拿到存储在调用者地址名下的值
  // 若调用者还没调用 setMyNumber, 则值为 `0`
  return favoriteNumber[msg.sender];
}

在这个小小的例子中,任何人都可以调用 setMyNumber 在我们的合约中存下一个 uint 并且与他们的地址相绑定。 然后,他们调用 whatIsMyNumber 就会返回他们存储的 uint

使用 msg.sender 很安全,因为它具有以太坊区块链的安全保障 —— 除非窃取与以太坊地址相关联的私钥,否则是没有办法修改其他人的数据的。

Require

require使得函数在执行过程中,当不满足某些条件时抛出错误,并停止执行:

function sayHiToVitalik(string _name) public returns (string) {
  // 比较 _name 是否等于 "Vitalik". 如果不成立,抛出异常并终止程序
  // (敲黑板: Solidity 并不支持原生的字符串比较, 我们只能通过比较
  // 两字符串的 keccak256 哈希值来进行判断)
  require(keccak256(_name) == keccak256("Vitalik"));
  // 如果返回 true, 运行如下语句
  return "Hi!";
}

如果你这样调用函数 sayHiToVitalik(“Vitalik”) ,它会返回“Hi!”。而如果调用的时候使用了其他参数,它则会抛出错误并停止执行。

因此,在调用一个函数之前,用 require 验证前置条件是非常有必要的。

Storage与Memory

在 Solidity 中,有两个地方可以存储变量 —— storage 或 memory

Storage 变量是指永久存储在区块链中的变量。 Memory 变量则是临时的。默认情况下 Solidity 会自动处理它们。 但主要用于处理函数内的 _ 结构体  和  数组  时,需要手动声明。

internal 和 external

除 public 和 private 属性之外,Solidity 还使用了另外两个描述函数可见性的修饰词:internal(内部) 和 external(外部)。

internal 和 private 类似,不过, 如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。(嘿,这听起来正是我们想要的那样!)。

external 与public 类似,只不过这些函数只能在合约之外调用 – 它们不能被合约内的其他函数调用。稍后我们将讨论什么时候使用 external 和 public

与其他合约的交互:接口

如果我们的合约需要和区块链上的其他的合约会话,则需先定义一个 interface (接口)。
我们定义 LuckyNumber 合约的 interface :
这个过程虽然看起来像在定义一个合约,但其实内里不同

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

 处理多返回值

getKitty 是我们所看到的第一个返回多个值的函数。我们来看看是如何处理的:

function multipleReturns() internal returns(uint a, uint b, uint c) {
  return (1, 2, 3);
}

function processMultipleReturns() external {
  uint a;
  uint b;
  uint c;
  // 这样来做批量赋值:
  (a, b, c) = multipleReturns();
}

// 或者如果我们只想返回其中一个变量:
function getLastReturnValue() external {
  uint c;
  // 可以对其他字段留空:
  (,,c) = multipleReturns();
}

Part3 高级一点的理论

Solidity特性

  • 在你把智能协议传上以太坊之后,它就变得**不可更改, 这种永固性意味着你的代码永远不能被调整或更新。 你编译的程序会一直,永久的,不可更改的,存在以太坊上。这就是 Solidity 代码的安全性如此重要的一个原因
  • 可以通过Ownable指定合约的“所有权”
`Ownable` 合约基本都会这么干:

1.  合约创建,构造函数先行,将其 `owner` 设置为`msg.sender`(其部署者)
2.  为它加上一个修饰符 `onlyOwner`,它会限制陌生人的访问,将访问某些函数的权限锁定在 `owner` 上。
3.  允许将合约所有权转让给他人。
`onlyOwner` 简直人见人爱,大多数人开发自己的 Solidity DApps,都是从复制/粘贴 `Ownable` 开始的,从它再继承出的子类,并在之上进行功能开发。
  • 在 Solidity 中,你的用户想要每次执行你的 DApp 都需要支付一定的 gas,gas 可以用以太币购买,因此,用户每次跑 DApp 都得花费以太币。
    • 省 gas 的招数:结构封装 (Struct packing),尽可能使用较小的 uint, Solidity 会将这些 uint 打包在一起

将结构体作为参数传入

由于结构体的存储指针可以以参数的方式传递给一个 private 或 internal 的函数,因此结构体可以在多个函数之间相互传递。

遵循这样的语法:

function _doStuff(Zombie storage _zombie) internal {
  // do stuff with _zombie
}

这样我们可以将某僵尸的引用直接传递给一个函数,而不用是通过参数传入僵尸ID后,函数再依据ID去查找。

在内存中声明数组

在数组后面加上 memory关键字, 表明这个数组是仅仅在内存中创建,不需要写入外部存储,并且在函数调用结束时它就解散了。与在程序结束时把数据保存进 storage 的做法相比,内存运算可以大大节省gas开销 — 把这数组放在view里用,完全不用花钱。

和以太的交互

  • 向合约发送以太
    • 用payable修饰符
  • 可以通过transfer向地址发送以太

Part4 和Web的交互

Infura

Infura 是一个服务,它维护了很多以太坊节点并提供了一个缓存层来实现高速读取。一般用Metamask,官方有提供示例代码

实例化

有了合约的地址和 ABI(ABI 意为应用二进制接口(Application Binary Interface)。 基本上,它是以 JSON 格式表示合约的方法,告诉 Web3.js 如何以合同理解的方式格式化函数调用。),可以实例化 Web3.js。

// 实例化 myContract
var myContract = new web3js.eth.Contract(myABI, myContractAddress);

调用

call

call 用来调用 view 和 pure 函数。它只运行在本地节点,不会在区块链上创建事务。

send

send 将创建一个事务并改变区块链上的数据。你需要用 send 来调用任何非 view 或者 pure 的函数。

Views: 27

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注