solidity学习总结

1.状态变量是被永久地保存在合约中

contract Example {
  // 这个无符号整数将会永久的被保存在区块链中
  uint myUnsignedInteger = 100;
}

2.uint = uint256,一个东西。无符号整数类型。uint8, uint16, uint32 等……
3.两种数组: 静态数组动态数组

// 固定长度为5的string类型的静态数组:
string[5] stringArray;
// 动态数组,长度不固定,可以动态添加元素:
uint[] dynamicArray;

状态变量被永久保存在区块链中。所以创建动态数组来保存成结构的数据是非常有意义的,省gas。
公共数组:定义 public 数组, Solidity 会自动创建 getter 方法

Person[] public people;
//其它的合约可以从这个数组读取数据(但不能写入数据),所以这在合约中是一个有用的保存公共数据的模式。

4.函数里的变量都是以(_)开头 (但不是硬性规定) 以区别全局变量,好习惯。
5.Ethereum 内部有一个散列函数keccak256,它用了SHA3版本。一个散列函数基本上就是把一个字符串转换为一个256位的16进制数字。字符串的一个微小变化会引起散列数据极大变化。现在不能直接用了,要使用abi.encodePacked()

keccak256(abi.encodePacked(now, msg.sender, randNonce))

6.显性变换数据类型再操作,即数据类型一样才可以进行运算

uint8 a = 5;
uint b = 6;
// 将会抛出错误,因为 a * b 返回 uint, 而不是 uint8:
uint8 c = a * b;
// 我们需要将 b 转换为 uint8:
uint8 c = a * uint8(b);

7.事件 是合约和区块链通讯的一种机制。你的前端应用“监听”某些事件,并做出反应,很有用。

// 这里建立事件
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public {
  uint result = _x + _y;
  //触发事件,通知app
  emit IntegersAdded(_x, _y, result);//这里使用
  return result;
}

8.Mapping(映射是个好东西

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

//或者可以用来通过userId 存储/查找的用户名
mapping (uint => string) userIdToName;

映射本质上是存储和查找数据所用的键-值对。在第一个例子中,键是一个 address,值是一个uint,在第二个例子中,键是一个uint,值是一个 string
9.msg.sender也是个好东西
在 Solidity 中,有一些全局变量可以被所有函数调用。 其中一个就是 msg.sender,它指的是当前调用者(或智能合约)的 address
10.require使得函数在执行过程中,当不满足某些条件时抛出错误,并停止执行。好东西。
11.继承也好东西。常和Import一起玩,当然也可不跟它玩。

//A 继承 B,A便可用B里的想让你用的方法
contract A is B {

}

12.在 Solidity 中,有两个地方可以存储变量 —— storagememory
Storage 变量是指永久存储在区块链中的变量。 Memory 变量则是临时的,当外部函数对某合约调用完成时,内存型变量即被移除。
需要手动声明存储类型,主要用于处理函数内的 结构体数组 时,其他时候,编辑器会提示。
13.函数可见性-----重点!

public 公有 和 private 私有 属性
internal 和 private 类似,不过, 如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。
external 与public 类似,只不过这些函数只能在合约之外调用 - 它们不能被合约内的其他函数调用。稍后我们将讨论什么时候使用 external 和 public。

14.与其他合约的交互,要先定义交互合约的 interface(接口)
接口没函数体{},用;结束
15.solidity能返回多个值,没错,非常酷,不想要的值可以用,,,,,忽略,和go语言中的_有点像。如

(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);//一定要查好个数....

16.函数修饰符modifier,好东西啊
函数修饰符看起来跟函数没什么不同,不过关键字modifier 告诉编译器,这是个modifier(修饰符),而不是个function(函数)。它不能像函数那样被直接调用,只能被添加到函数定义的末尾,用以改变函数的行为。执行时先执行modifier内的判断,在_的位置正常执行被修饰的函数。
17.真金白银gas
以太坊就像一个巨大、缓慢、但非常安全的电脑。当你运行一个程序的时候,网络上的每一个节点都在进行相同的运算,以验证它的输出 —— 这就是所谓的去中心化 由于数以千计的节点同时在验证着每个功能的运行(POW),这可以确保它的数据不会被被监控,或者被刻意修改。
可能会有用户用无限循环堵塞网络,抑或用密集运算来占用大量的网络资源,为了防止这种事情的发生,以太坊的创建者为以太坊上的资源制定了价格,想要在以太坊上运算或者存储,你需要先付费
省gas的招数
如果一个 struct中有多个 uint,满足需求的情况下,要考虑安全,别溢出了,则尽可能使用较小的 uint, Solidity 会将这些 uint 打包在一起,从而占用较少的存储空间。
18.由于 struct的存储指针可以以参数的方式传递给一个 privateinternal 的函数,因此结构体可以在多个函数之间相互传递。
19.view 函数不花 gas
当玩家从外部调用一个view函数,是不需要支付一分gas的。
Solidity 使用 storage(存储)是相当昂贵的,”写入“操作尤其贵
在数组后面加上 memory 关键字, 表明这个数组是仅仅在内存中创建,不需要写入外部存储,并且在函数调用结束时它就解散了。与在程序结束时把数据保存进 storage 的做法相比,内存运算可以大大节省gas开销 ,再把这数组放在view里用,完全不用花钱。
20.for循环

function getEvens() pure external returns(uint[]) {
  uint[] memory evens = new uint[](5);
  // 在新数组中记录序列号
  uint counter = 0;
  // 在循环从1迭代到10:
  for (uint i = 1; i <= 10; i++) {
    // 如果 `i` 是偶数...
    if (i % 2 == 0) {
      // 把它加入偶数数组
      evens[counter] = i;
      //索引加一, 指向下一个空的‘even’
      counter++;
    }
  }
  return evens;
}

21.函数修饰符,,来个概览:

1、我们有决定函数何时和被谁调用的可见性修饰符: private 意味着它只能被合约内部调用; internal 就像 private 但是也能被继承的合约调用; external 只能从合约外部调用;最后 public 可以在任何地方调用,不管是内部还是外部。
2、我们也有状态修饰符, 告诉我们函数如何和区块链交互: view 告诉我们运行这个函数不会更改和保存任何数据; pure 告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。这两种在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)。
3、然后我们有了自定义的 modifiers, 对于这些修饰符我们可以自定义其对函数的约束逻辑。

payable修饰符
payable 方法是让 Solidity 和以太坊变得如此酷的一部分 —— 它们是一种可以接收以太的特殊函数。
22.用keccak256来制造随机数
Solidity 中最好的随机数生成器是 keccak256 哈希函数,不安全,但一般时候够用,只要攻击远远得不偿失就没事。安全的就得用第三方随机函数了。
23.以太坊上的代币
一个 代币 在以太坊基本上就是一个遵循一些共同规则的智能合约——即它实现了所有其他代币合约共享的一组标准函数,例如transfer(address _to, uint256 _value)balanceOf(address _owner)
在智能合约内部,通常有一个映射, mapping(address => uint256) balances,用于追踪每个地址还有多少余额。
所以基本上一个代币只是一个追踪谁拥有多少该代币的合约,和一些可以让那些用户将他们的代币转移到其他地址的函数。
代币分ERC20和ERC721
具体实现参考:ERC20、ERC721
24.合约安全是很重要很重要非常非常重要
防止溢出
比如uint8, 只能存储8 bit数据。这意味着我们能存储的最大数字就是二进制 11111111 (或者说十进制的 2^8 - 1 = 255).

uint8 number = 255;
number++;     // number 等于 0了。给二进制 11111111 加1, 它将被重置为 00000000

使用SafeMath,OpenZeppelin 建立了一个库(library),默认情况下可以防止这些问题。
25.assertrequire区别
assert 和 require 相似,若结果为否它就会抛出错误。 assert 和 require 区别在于,require 若失败则会返还给用户剩下的 gas, assert 则不会。所以大部分情况下,你写代码的时候会比较喜欢 require,assert 只在代码可能出现严重错误的时候使用,比如 uint 溢出。
26.事件
当定义的事件触发时,我们可以将事件存储到EVM的交易日志中,日志是区块链中的一种特殊数据结构。日志与合约关联,与合约的存储合并存入区块链中。只要某个区块可以访问,其相关的日志就可以访问。但在合约中,我们不能直接访问日志和事件数据(即便是创建日志的合约)。
Indexed属性
可以在事件参数上增加indexed属性,最多可以对三个参数增加这样的属性。加上这个属性,可以允许你在web3.js中通过对加了这个属性的参数进行值过滤

event transfer(address indexed _from, address indexed _to, uint indexed value);
var event = myContract.transfer({value: "100"});//实现的是对value值为100的日志,过滤后的返回
//同时匹配多个值,还可以传入一个要匹配的数组
var event = myContract.transfer({value: ["99","100","101"]});

增加了indexed的参数值会存到日志结构的Topic部分,便于快速查找。而未加indexed的参数值会存在data部分,成为原始日志。需要注意的是,如果增加indexed属性的是数组类型(包括stringbytes),那么只会在Topic存储对应的数据的web3.sha3哈希值,将不会再存原始数据。因为Topic是用于快速查找的,不能存任意长度的数据,所以通过Topic实际存的是数组这种非固定长度数据哈希结果。要查找时,是将要查找内容哈希后与Topic内容进行匹配,但我们不能反推哈希结果,从而得不到原始值

事件还支持传入其它参数对象来限定可检索的范围,支持fromBlocktoBlock等过滤条件

var event = myContract.transfer({value: "100"}, {fromBlock: 0, toBlock: 'latest'});

事件可以被继承

你可能感兴趣的:(solidity,区块链)