导读
此教程适合至少了解过一点合约相关知识,对区块链有基本共识同学进行观看。
不管你是需要阅读合约源码,亦或是需要编写合约。这份指南都会对你有所帮助,这份为个人整理比不上官方文档,但是对特别需要注意的知识点做了补充说明,可用此文档进行过度。在后续有更加细节的知识点需要查询时,可到官方文档进行查阅。
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;
}
}
上面是一个最简单的合约
第一行表示合约对应的 solidity 版本,大于等于 0.4.0 但是小于 0.6.0
也可以像下面这种写法
pragma solidity ^0.4.0;
合约本质上就是运行在区块链上的一段代码,如上方所示 unit storedData
代表了它是运行上区块链上的一个变量。我们可以通过下方的 get set 来对他进行修改。
在 solidity 语言中,想要使用其他模块的方法或属性,则需要进行倒入,总共由两种方法
import "filename"
也可以使用
import * as symbolName from "filename";
注意:solidity 不像 JS JAVA 等语言,可以直接在本地运行,它更多的是需要像 EVM 这样的环境才能运行,所以在刚开始练习的时候,最好是用比较简单,能直观看到结果的编辑器。
打开页面 Remix IDE,运行我们的代码。
将下方 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;
}
}
切换到 编译 Tab ,点击编译
点击部署
部署完成之后,下方就可以看到我们部署后的合约
然后将合约展开,展开之后我们就可以在这里面调用合约中代码。
点击 getResult
就能得到结果。这样我们就完成了第一个 solidity 程序的编写和部署测试。
支持两种形式
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;
}
address 保存代表以太坊地址大小的 20 字节值。 一个地址可以使用 .balance 方法获取余额,也可以使用 .transfer 方法将余额转移到另一个地址。
address x = 0x212;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
address 这里容易让人有点误解,明明存入的是 0x1341243243... 这样的一个东西,但是却可以直接点出方法 balance 和 transfer。这里需要消化一下,不然后面容易懵。
这里的 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 ,可调节。
此小节非常重要。
在 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)
:剩余的 gasmsg.data
(bytes
): 完整的 calldatamsg.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.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 的合约关系为
以上内容都比较常规,和平常使用的编程语言,语法差异不大。大家有兴趣可以查查官方文档,这里就不在赘述。
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;
}
}
mapping(_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));
}
}
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.