Solidity 入门教程 第一小节(主题有字数限制)

2022-05-30 12:21:56 +08:00
 notot

导读

此教程适合至少了解过一点合约相关知识,对区块链有基本共识同学进行观看。

不管你是需要阅读合约源码,亦或是需要编写合约。这份指南都会对你有所帮助,这份为个人整理比不上官方文档,但是对特别需要注意的知识点做了补充说明,可用此文档进行过度。在后续有更加细节的知识点需要查询时,可到官方文档进行查阅。

Official Solidity Docs - EN

Community Solidity Docs - ZH

01. Tutorial

1.1 基础语法

pragma solidity >=0.4.0 <0.6.0;
contract SimpleStorage {
   uint storedData;
   function set(uint x) public {
      storedData = x;
   }
   function get() public view returns (uint) {
      return storedData;
   }
}

上面是一个最简单的合约

Pragma

第一行表示合约对应的 solidity 版本,大于等于 0.4.0 但是小于 0.6.0

也可以像下面这种写法

pragma solidity ^0.4.0;

Contract

合约本质上就是运行在区块链上的一段代码,如上方所示 unit storedData 代表了它是运行上区块链上的一个变量。我们可以通过下方的 get set 来对他进行修改。

Import

在 solidity 语言中,想要使用其他模块的方法或属性,则需要进行倒入,总共由两种方法

import "filename"

也可以使用

import * as symbolName from "filename";

1.2 第一个程序

注意:solidity 不像 JS JAVA 等语言,可以直接在本地运行,它更多的是需要像 EVM 这样的环境才能运行,所以在刚开始练习的时候,最好是用比较简单,能直观看到结果的编辑器。

打开页面 Remix IDE,运行我们的代码。

Step 1

将下方 Example 中的代码拷贝到 Remix 中

Example

pragma solidity ^0.5.0;
contract SolidityTest {
   constructor() public{
   }
   function getResult() public view returns(uint){
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Step 2

切换到 编译 Tab ,点击编译

Step 3

点击部署

Step 4

部署完成之后,下方就可以看到我们部署后的合约

然后将合约展开,展开之后我们就可以在这里面调用合约中代码。

点击 getResult

就能得到结果。这样我们就完成了第一个 solidity 程序的编写和部署测试。

1.3 注释

支持两种形式

function getResult() public view returns(uint){
   // This is a comment. It is similar to comments in C++

   /*
      * This is a multi-line comment in solidity
      * It is very similar to comments in C Programming
   */
   uint a = 1;
   uint b = 2;
   uint result = a + b;
   return result;
}

1.4 类型

types

地址 Address

address 保存代表以太坊地址大小的 20 字节值。 一个地址可以使用 .balance 方法获取余额,也可以使用 .transfer 方法将余额转移到另一个地址。

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

address 这里容易让人有点误解,明明存入的是 0x1341243243... 这样的一个东西,但是却可以直接点出方法 balancetransfer这里需要消化一下,不然后面容易懵

这里的 address 对象我们可以理解为是一个包装数据类型,可以直接理解 address 为一个类,他本身就具备很多方法。只不过在初始化的时候跟传统的 Struct 结构的数据不一样。只需要设置地址就可以了,程序会自动帮我们处理。

address 中所有的可用方法

<address>.balance (uint256):

以 Wei 为单位的 地址类型 的余额。

<address>.transfer(uint256 amount):

地址类型 发送数量为 amount 的 Wei ,失败时抛出异常,发送 2300 gas 的矿工费,不可调节。

<address>.send(uint256 amount) returns (bool):

地址类型 发送数量为 amount 的 Wei ,失败时返回 false,发送 2300 gas 的矿工费用,不可调节。

<address>.call(...) returns (bool):

发出低级函数 CALL,失败时返回 false,发送所有可用 gas ,可调节。

<address>.callcode(...) returns (bool)

发出低级函数 CALLCODE,失败时返回 false,发送所有可用 gas ,可调节。

<address>.delegatecall(...) returns (bool):

发出低级函数 DELEGATECALL,失败时返回 false,发送所有可用 gas ,可调节。

1.5 变量

此小节非常重要。

在 Solidity 中总共有 3 种变量

  • 状态变量
  • 本地变量
  • 全局变量

状态变量

表明存储在合约中的变量,什么叫存储在合约中呢,其实就相当于 Java 里面的成员变量,在 solidity 中的写法如下

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData;      // State variable
   constructor() public {
      storedData = 10;   // Using State variable
   }
}

本地变量

其值仅在定义它的函数内可用的变量。 函数参数始终是该函数的本地参数。其实说白了就是局部变量,就是写在方法里面的。

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result; //access the local variable
   }
}

全局变量 [重要‼️]

全局变量是存在于全局工作空间中的特殊变量,它们提供关于区块链和交易属性的信息。说白了就是不需要声明,有点类似于 Node 里面的 Process ,浏览器里面的 Windows ,可以直接拿来使用。

Solidity 中的全局变量有下面几种。

  • block.blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希——仅可用于最新的 256 个区块且不包括当前区块;而 blocks 从 0.4.22 版本开始已经不推荐使用,由 blockhash(uint blockNumber) 代替
  • block.coinbase (address): 挖出当前区块的矿工地址
  • block.difficulty (uint): 当前区块难度
  • block.gaslimit (uint): 当前区块 gas 限额
  • block.number (uint): 当前区块号
  • block.timestamp (uint): 自 unix epoch 起始当前区块以秒计的时间戳
  • gasleft() returns (uint256):剩余的 gas
  • msg.data (bytes): 完整的 calldata
  • msg.gas (uint): 剩余 gas - 自 0.4.21 版本开始已经不推荐使用,由 gesleft() 代替
  • msg.sender (address): 消息发送者(当前调用)
  • msg.sig (bytes4): calldata 的前 4 字节(也就是函数标识符)
  • msg.value (uint): 随消息发送的 wei 的数量
  • now (uint): 目前区块时间戳(block.timestamp
  • tx.gasprice (uint): 交易的 gas 价格
  • tx.origin (address): 交易发起者(完全的调用链)

看一下在合约中如何使用 全局变量

pragma solidity ^0.5.0;
contract SolidityTest {

   function getNowTime() public view returns(uint) {
       return now;
   }

   function getSender() public view returns (address payable) {
       return msg.sender;
   }

   function getGaslimit() public view returns (uint) {
       return block.gaslimit;
   }
}

然后在从新点击 部署,按照上面讲过的步骤

然后运行部署后的合约。

变量命名规范

  • 您不应使用任何 Solidity 保留关键字作为变量名。 这些关键字将在下一节中提到。 例如,break, bool 。

  • Solidity 变量名称不应以数字 (0-9) 开头。 它们必须以字母或下划线字符开头。 例如,123test 是一个无效的变量名,但 _123test 是一个有效的变量名。

  • Solidity 变量名称区分大小写。 例如,name 和 Name 是两个不同的变量。

1.6 变量作用域

在 1.5 小节中讲到 Solidity 中有三种类型的变量

  • 状态变量
  • 本地变量
  • 全局变量

本地变量的作用于只存在于方法中,但是状态变量有三种作用域。

  • Public

    公共状态变量可以在内部访问,也可以通过消息访问。 对于公共状态变量,会生成一个自动 getter 函数。

  • Internal

    内部状态变量只能从当前合约或从它派生的合约内部访问,而不使用 this 。就类似于 Java 里面的 protected

  • Private

    私有状态变量只能在当前合约内部访问,它们不是在派生合约中定义的。

  • ? External

pragma solidity ^0.5.0;
contract C {
   uint public data = 30;
   uint internal iData= 10;
   
   function x() public returns (uint) {
      data = 3; // internal access
      return data;
   }
}
contract Caller {
   C c = new C();
   function f() public view returns (uint) {
      return c.data(); //external access
   }
}
contract D is C {
   function y() public returns (uint) {
      iData = 3; // internal access
      return iData;
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

上方 Example 的合约关系为

  • 定义了一个 合约 C ,并在合约 C 种定义了一个 public 状态变量和一个 interval 状态变量
  • 定义了一个合约 Caller ,并在内部 New 了合约 C
  • 定义了一个合约 D 并继承 C

1.7 省略章节

  • 循环
  • if
  • 操作符
  • array
  • enum

以上内容都比较常规,和平常使用的编程语言,语法差异不大。大家有兴趣可以查查官方文档,这里就不在赘述。

1.8 struct

struct struct_name { 
   type1 type_name_1;
   type2 type_name_2;
   type3 type_name_3;
}

struct 可以理解为多种类型的一个包装,就和包装数据类型是一样的。

Eample

pragma solidity ^0.5.0;

contract test {
   struct Book { 
      string title;
      string author;
      uint book_id;
   }
   Book book;

   function setBook() public {
      book = Book('Learn Java', 'TP', 1);
   }
   function getBookId() public view returns (uint) {
      return book.book_id;
   }
}

1.9 Mapping

mapping(_KeyType => _ValueType)

  • _KeyType: 可以是任何内置类型加上字节和字符串。不允许引用类型或复杂对象。
  • _ValueType: 任何类型都可以

可以理解为 Hash 类型

pragma solidity ^0.5.0;

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

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}
1654 次点击
所在节点    Solidity
1 条回复
NK007
2023-02-19 17:38:29 +08:00
牛的,不过我去 udemy 上面找课程了~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/856187

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX