第一个程序 solidity编写
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1; //指定solidity的版本
// 合约
contract hello {
string public name;
constructor(string memory _name) { //构造函数
name = _name;
}
}
solidity语法介绍
方法定义
solidity中使用function关键字定义一个方法,方法中包含传入参数以及返回值,方法体中实现业务方法
例如:实现从1加到100
function getSum() public view returns (uint256 ) {
uint256 sum = 0;
//for(init;cond;post)
for(uint256 i = 1; i <= 100; i ++) {
sum += i;
}
return sum;
}
function getSum2() public view returns (uint256 sum) {
//for(init;cond;post)
uint256 i = 0;
while(i <= 100) {
sum += i;
i ++;
}
//return sum;
}
数组
solidity定义数组的方式与go语言类似,使用[]来定义数组,并指定类型和长度,数组可以通过push方法添加数据。
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7; //solidity<=0.8.7 && >= 0.6.0;
contract array_demo {
string[5] public names;
uint8[] public ages;
constructor() {
names[0] = "zhangsan";
names[1] = "lisi";
names[2] = "wangwu";
// ages[0] = 20;
//ages[1] = 30;
ages.push(20);
}
function addAge(uint8 _age) public {
ages.push(_age);
}
function getLength() public view returns (uint256, uint256) {
return (names.length, ages.length);
}
function addName(string memory _name) public {
names.push(_name);
}
}
mapping定义
mapping(键值对)的定义,需要先指定键值的类型,比如:如果想用mapping存储学生的考试成绩,需要定义一个mapping,key表示学生姓名,score表示考试成绩,如下操作可以实现对数据的插入和查询
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
contract mapping_demo {
mapping(string=>uint256) maths;
function addScore(string memory _name, uint256 _score) public {
if (maths[_name] > 0) return;
maths[_name] = _score;
}
function getScore(string memory _name) public view returns (uint256) {
return maths[_name];
}
}
结构体
solidity使用struct来定义结构体,在方法中可以对结构体的各个字段进行赋值,合约部署后,直接调用setUser方法可以对人员信息进行赋值
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
struct User {
string name;
uint8 age;
string sex;
}
contract struct_demo {
User user;
function setUser(string memory _name, uint8 _age, string memory _sex) public {
user.name = _name;
user.age = _age;
user.sex = _sex;
}
function getUser() public view returns (User memory) {
return user;
}
}
变量的存储类型,memory,storage和calldata
memory存储位置同我们普通程序的内存类似。即分配,即使用,越过作用域即不可被访问,等待被回收。而对于storage的变量,数据将永远存在于区块链上。
1.默认的函数参数,包括返回的参数,他们是memory。而默认的局部变量是storage的。
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
struct User {
string name;
uint8 age;
string sex;
}
contract storage_demo {
User adminuser;
function setUser(string memory _name, uint8 _age, string memory _sex) public {
adminuser.name = _name;
adminuser.age = _age;
adminuser.sex = _sex;
}
function getUser() public view returns (User memory) {
return adminuser;
}
function setAge1(uint8 _age) public {
User memory user = adminuser;
user.age = _age;
}
function setAge2(uint8 _age) public {
User storage user = adminuser;
user.age = _age;
}
function setAge3(User storage _user, uint8 _age) internal {
_user.age = _age;
}
function callsetAge3(uint8 _age) public {
setAge3(adminuser, _age);
}
}
require的用法
require 函数用于确认条件有效性,例如输入变量,或合约状态变量是否满足条件,或验证外部合约调用返回的值
有两个参数:
- 第一个参数为条件判断表达式,必选
- 第二个参数为要返回的异常消息提醒,可选
比如在充值提现过程中会判断金额是否一致,余额是否足够之类的判断
require(_amount == msg.value, "amount must == msg.value");
完整代码:
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
contract money_demo {
address public admin;
address payable public user;
uint256 totalAmount;
constructor(address _owner) {
admin = _owner;
}
function deposit(uint256 _amount) public payable {
//if (_amount != msg.value) return;
require(_amount == msg.value, "amount must == msg.value");
assert(_amount > 0);
user = payable(msg.sender);
totalAmount = _amount;
//address(this).balance += _amount;
}
function getBalance() public view returns (uint256, uint256) {
//this dai biao he yue ben shen
// account's balance
return (address(this).balance, totalAmount);
}
function withdraw(uint256 _amount) public payable {
user.transfer(_amount);
}
}
函数修改器modifier
官方文档:modifier可以改变函数的行为。可以被继承和重写。
其实modifier被用于最多的是行为检查,这样可以使得减少检查代码的复用以及让代码看起来更简介易懂。比如,检查调用者是否有权限执行这个函数,传入的参数是否有错误等等。但是modifier不仅仅于此。通过一下一个例子来熟悉了解一下modifier的用法:
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
contract modifier_demo {
address public admin;
uint256 public amount;
constructor() {
admin = msg.sender;
amount = 101;
}
modifier onlyadmin() {
require(msg.sender == admin, "only admin can do");
require(amount > 100, "amount must > 100");
_;
}
function setCount(uint256 _amount) public onlyadmin {
amount = _amount;
}
}
上述例子中,我们通过关键字 modifier 后面接函数修改器名 onlyadmin 来定义一个modifier。在上述定义的modifier中如果调用者不是拥有者则会停止执行接下来的代码,并在控制台输出自定义的原因。如果是的话则执行到 _ 处,_ 代表使用该modifier的函数体,这里即为setCount 函数的函数体。在执行setCount 函数前先会使用onlyadmin进行检查,没有问题后才会执行。
receive函数和Fallback函数
Receive是一个接收以太币函数,一个合约中最多可以有一个 receive 函数。在对合约转账时会执行 receive 函数,例如通过 transfer()、send() 或 call()。如果 receive 函数不存在,那么 fallback 回退函数会被调用。receive 函数的
声明语法如下:
回退函数Fallback函数特点:
- 回退函数没有 function 关键字;
- 回退函数必须是 external 可见性,即允许被外部合约调用;
- 如果回退函数需要接收以太币,则必须标记为 payable 关键字。
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
contract receive_demo {
uint256 totalAmount;
address[] public addrs;
receive() external payable {
totalAmount += msg.value;
addrs.push(msg.sender);
}
function getBalance() public view returns (uint256, uint256) {
return (totalAmount, address(this).balance);
}
fallback() external payable {
totalAmount += msg.value;
addrs.push(msg.sender);
}
}
Fallback函数与Receive函数的区别是:Receive函数只在合约转账时调用,而Fallback函数除了可以在合约转账时调用外,在合约没有函数匹配或需要向合约发送附加数据时,也调用Fallback函数。
编写库Library
Solidity提供了Library的概念来实现代码重用,它可以被多个不同的智能合约调用。大家可以把library想象成在面向对象语言中的static类中的static函数。 Library在部署到区块链上时是很类似普通的智能合约的。这也允许大家可以使用别人部署的Library,但要十分小心的是使用非自己建立的、部署的library需要承担一定的安全风险。
比如定义比较2个字符串是否相等的库,如下
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
library libstring {
function isEqual(string memory a, string memory b) internal pure returns (bool) {
bytes32 hashA = keccak256(abi.encode(a));
bytes32 hashB = keccak256(abi.encode(b));
return hashA == hashB;
}
}
当我调用它时,需要讲它指定某个类型
// SPDX-License-Identifier: Apache-2.0
pragma solidity^0.8.7;
import "./libstring.sol";
contract library_demo {
using libstring for string;
function isMyEqual(string memory a, string memory b) public pure returns (bool) {
return a.isEqual(b);
}
}