solidity 教程链接

1. 源文件结构

源文件中可以包含任意多个 合约定义 、导入源文件指令 、 版本标识 指令、 结构体 、 枚举 和 函数 定义.

SPDX许可标识
SPDX:The Software Package Data Exchange
常见开源:
// SPDX-License-Identifier: MIT
私有或者无授权:
// SPDX-License-Identifier: UNLICENSED
版本标识
pragma solidity ^0.8.4;或者 pragma solidity >=0.4.16 <0.9.0;
ABICoder Pragma
Solidity 0.7.4 之前:pragma experimental ABIEncoderV2
Solidity 0.7.4 之后:pragma abicoder v2

导入文件

import "filename";
示例:
import "./helper.sol";

注释

// This is a single-line comment./*
This is a
multi-line comment.
*/

2. 合约结构

状态变量

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;contract SimpleStorage {uint storedData; // State variable// ...
}

函数

/ SPDX-License-Identifier: GPL-3.0
pragma solidity >0.7.0 <0.9.0;contract TinyAuction {function Mybid() public payable { // 定义函数// ...}
}// Helper function defined outside of a contract
function helper(uint x) pure returns (uint) {return x * 2;
}

函数修饰器

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;contract Purchase {address public seller;modifier onlySeller() { // Modifierrequire(msg.sender == seller,"Only seller can call this.");_;}function abort() public view onlySeller { // Modifier usage// ...}
}

事件

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.21 <0.9.0;contract SimpleAuction {event HighestBidIncreased(address bidder, uint amount); // Eventfunction bid() public payable {// ...emit HighestBidIncreased(msg.sender, msg.value); // Triggering event}
}

异常处理

使用revert或者require(推荐)

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;/// Not enough funds for transfer. Requested `requested`,
/// but only `available` available.
error NotEnoughFunds(uint requested, uint available);contract Token {mapping(address => uint) balances;function transfer(address to, uint amount) public {uint balance = balances[msg.sender];if (balance < amount)revert NotEnoughFunds(amount, balance);balances[msg.sender] -= amount;balances[to] += amount;// ...}function transfer2(address to, uint amount) public {uint balance = balances[msg.sender];require(balance > amount," balance must be greater than amount");balances[msg.sender] -= amount;balances[to] += amount;// ...}
}

结构

pragma solidity >=0.4.0 <0.9.0;contract Ballot {struct Voter { // 结构体uint weight;bool voted;address delegate;uint vote;}
}

枚举

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.25 <0.9.0;contract Purchase {enum State { Created, Locked } // Enum
}

3. 常用信息

数据类型

  • 值类型:Booleans(true&false)、Integers(int8 to int256,uint8 to uint256)、Address(长度40位,20字节)、byte(byte1 to byte32)。
  • 引用类型:struct、bytes(byte[])、string、mapping。
    引用类型可以通过多个不同的名称修改它的值,而值类型的变量,每次都有独立的副本。因此,必须比值类型更谨慎地处理引用类型。 目前,引用类型包括结构,数组和映射,如果使用引用类型,则必须明确指明数据存储哪种类型的位置(空间)里:
    a. memory :数据在内存中,因此数据仅在其生命周期内(函数调用期间)有效。不能用于外部调用。
    b. storage :状态变量保存的位置,只要合约存在就一直存储.
    c. calldata:调用数据时用来保存函数参数的特殊数据位置,是一个只读位置,无需copy变量值到其他存储位置。
  • 类型修饰符:私有(private)、内部(internal)、公共(public)、外部(external);函数默认为internal,状态变量默认为private。另外函数还包含view(读取账本)和pure(不读取也不更改账本)。
    详解:
    private:仅内部访问。
    internal: 仅内部以及派生类访问。
    external:仅外部访问。
    public:内部和外部都可以访问。

特殊变量和函数

abi.encode(...) returns (bytes): ABI - 对给定参数进行编码
abi.encodePacked(...) returns (bytes):对给定参数执行 紧打包编码
abi.encodeWithSelector(bytes4 selector, ...) returns (bytes): ABI- 对给定参数进行编码,并以给定的函数选择器作为起始的 4 字节数据一起返回
abi.encodeWithSignature(string signature, ...) returns (bytes):等价于 abi.encodeWithSelector(bytes4(keccak256(signature), ...)
block.blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希——仅可用于最新的 256 个区块且不包括当前区块
block.chainid (uint): 当前链 id
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.sender ( address ): 消息发送者(当前调用)
msg.sig ( bytes4 ): calldata 的前 4 字节(也就是函数标识符)
msg.value ( uint ): 随消息发送的 wei 的数量
tx.gasprice (uint): 交易的 gas 价格
tx.origin (address payable): 交易发起者(完全的调用链)
address(this):当前合约的地址

内联汇编

通常我们通过库代码,来增强语言我,实现一些精细化的控制,Solidity为我们提供了一种接近于EVM底层的语言,内联汇编,允许与Solidity结合使用。由于EVM是栈式的,所以有时定位栈比较麻烦,Solidty的内联汇编为我们提供了下述的特性,来解决手写底层代码带来的各种问题:允许函数风格的操作码:mul(1, add(2, 3))等同于push1 3 push1 2 add push1 1 mul
内联局部变量:let x := add(2, 3) let y := mload(0x40) x := add(x, y)
可访问外部变量:function f(uint x) { assembly { x := sub(x, 1) } }
标签:let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))
循环:for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }
switch语句:switch x case 0 { y := mul(x, 2) } default { y := 0 }
函数调用:function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }
pragma solidity ^0.4.0;library GetCode {function at(address _addr) returns (bytes o_code) {assembly {// retrieve the size of the code, this needs assemblylet size := extcodesize(_addr)// allocate output byte array - this could also be done without assembly// by using o_code = new bytes(size)o_code := mload(0x40)// new "memory end" including paddingmstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))// store length in memorymstore(o_code, size)// actually retrieve the code, this needs assemblyextcodecopy(_addr, add(o_code, 0x20), 0, size)}}
}

合约

Solidity中合约有点类似面向对象语言中的类。合约中有用于数据持久化的状态变量(state variables),和可以操作他们的函数。调用另一个合约实例的函数时,会执行一个EVM函数调用,这个操作会切换执行时的上下文,这样,前一个合约的状态变量(state variables)就不能访问了。

创建合约

合约可以通过Solidity,或不通过Solidity创建。当合约创建时,一个和合约同名的函数(构造器函数)会调用一次,用于初始化。
构造器函数是可选的。仅能有一个构造器,所以不支持重载。
如果不通过Solidity,我们可以通过web3.js,使用JavaScript的API来完成合约创建:

pragma solidity ^0.4.0;contract OwnedToken {// TokenCreator is a contract type that is defined below.// It is fine to reference it as long as it is not used// to create a new contract.TokenCreator creator;address owner;bytes32 name;// This is the constructor which registers the// creator and the assigned name.function OwnedToken(bytes32 _name) {// State variables are accessed via their name// and not via e.g. this.owner. This also applies// to functions and especially in the constructors,// you can only call them like that ("internall"),// because the contract itself does not exist yet.owner = msg.sender;// We do an explicit type conversion from `address`// to `TokenCreator` and assume that the type of// the calling contract is TokenCreator, there is// no real way to check that.creator = TokenCreator(msg.sender);name = _name;}function changeName(bytes32 newName) {// Only the creator can alter the name --// the comparison is possible since contracts// are implicitly convertible to addresses.if (msg.sender == address(creator))name = newName;}function transfer(address newOwner) {// Only the current owner can transfer the token.if (msg.sender != owner) return;// We also want to ask the creator if the transfer// is fine. Note that this calls a function of the// contract defined below. If the call fails (e.g.// due to out-of-gas), the execution here stops// immediately.if (creator.isTokenTransferOK(owner, newOwner))owner = newOwner;}
}contract TokenCreator {function createToken(bytes32 name)returns (OwnedToken tokenAddress){// Create a new Token contract and return its address.// From the JavaScript side, the return type is simply// "address", as this is the closest type available in// the ABI.return new OwnedToken(name);}function changeName(OwnedToken tokenAddress, bytes32 name) {// Again, the external type of "tokenAddress" is// simply "address".tokenAddress.changeName(name);}function isTokenTransferOK(address currentOwner,address newOwner) returns (bool ok) {// Check some arbitrary condition.address tokenAddress = msg.sender;return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);}
}

库(Libraries)

库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过EVM的特性DELEGATECALL(Homestead之前是用CALLCODE)来复用代码。这意味着库函数调用时,它的代码是在调用合约的上下文中执行。使用this将会指向到调用合约,而且可以访问调用合约的存储(storage)。因为一个合约是一个独立的代码块,它仅可以访问调用合约明确提供的状态变量(state variables),否则除此之外,没有任何方法去知道这些状态变量。

使用库合约的合约,可以将库合约视为隐式的父合约(base contracts),当然它们不会显式的出现在继承关系中。但调用库函数的方式非常类似,如库L有函数f(),使用L.f()即可访问。此外,internal的库函数对所有合约可见,如果把库想像成一个父合约就能说得通了。当然调用内部函数使用的是internal的调用惯例,这意味着所有internal类型可以传进去,memory类型则通过引用传递,而不是拷贝的方式。为了在EVM中实现这一点,internal的库函数的代码和从其中调用的所有函数将被拉取(pull into)到调用合约中,然后执行一个普通的JUMP来代替DELEGATECALL。

pragma solidity ^0.4.0;library Set {// We define a new struct datatype that will be used to// hold its data in the calling contract.struct Data { mapping(uint => bool) flags; }// Note that the first parameter is of type "storage// reference" and thus only its storage address and not// its contents is passed as part of the call.  This is a// special feature of library functions.  It is idiomatic// to call the first parameter 'self', if the function can// be seen as a method of that object.function insert(Data storage self, uint value)returns (bool){if (self.flags[value])return false; // already thereself.flags[value] = true;return true;}function remove(Data storage self, uint value)returns (bool){if (!self.flags[value])return false; // not thereself.flags[value] = false;return true;}function contains(Data storage self, uint value)returns (bool){return self.flags[value];}
}contract C {Set.Data knownValues;function register(uint value) {// The library functions can be called without a// specific instance of the library, since the// "instance" will be the current contract.if (!Set.insert(knownValues, value))throw;}// In this contract, we can also directly access knownValues.flags, if we want.
}

接口

/*接口与抽象合约类似,与之不同的是,接口内没有任何函数是已实现的,同时还有如下限制:
不能继承其它合约,或接口。
不能定义构造器
不能定义变量
不能定义结构体
不能定义枚举类*/
interface Token {function transfer(address recipient, uint amount);
}

抽象(Abstract Contracts)

pragma solidity ^0.4.0;contract Feline {function utterance() returns (bytes32);function getContractName() returns (string){return "Feline";}
}contract Cat is Feline {function utterance() returns (bytes32) { return "miaow"; }
}

4. 示例

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;// pragma experimental ABIEncoderV2;import "./helper.sol";/*** @title Storage* @dev Store & retrieve value in a variable*/
contract Storage {address public seller;constructor(){seller = msg.sender;}modifier onlySeller() { // Modifierrequire(msg.sender == seller,"Only seller can call this.");_;}uint256 number;event storeEvent(string name,uint256 value);/*** @dev Store value in variable* @param num value to store*/function store(uint256 num) public {number = num;emit storeEvent("number",number);}/*** @dev Return value * @return value of 'number'*/function retrieve() public view returns (uint256){return number;}// only call by sellerfunction storeOnlySeller(uint256 num) public onlySeller {// number = num;number = Helper.add(num,1);emit storeEvent("number",number);}// destory the contractfunction destory() public onlySeller {selfdestruct(payable(msg.sender));}}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;library Helper {function add(uint256 a,uint256 b) public pure returns(uint256) {return (a+b);}}

5.合约编写

pragma solidity >=0.4.22 <0.5.0;//ERC20协议接口
contract ERC20Interface{function totalSupply() public constant returns(uint);function balanceOf(address tokenOwner) public constant returns(uint balance) ;function allowance(address tokenOwner,address spender) public constant returns(uint remainIng);function transfer(address to,uint tokens) public returns(bool success);function approve(address spender,uint tokens) public returns(bool success);function transferFrom(address from,address to,uint tokens) public returns(bool success);event Transfer(address indexed from,address indexed to, uint tokens);event Approval(address indexed tokenOwner,address indexed spender,uint tokens);}/*** @title SafeMath 安全计算,避免溢出* @dev Math operations with safety checks that throw on error*/
library SafeMath {/*** @dev Multiplies two numbers, throws on overflow.*/function mul(uint256 a, uint256 b) internal pure returns (uint256) {if (a == 0) {return 0;}uint256 c = a * b;assert(c / a == b);return c;}/*** @dev Integer division of two numbers, truncating the quotient.*/function div(uint256 a, uint256 b) internal pure returns (uint256) {// assert(b > 0); // Solidity automatically throws when dividing by 0uint256 c = a / b;// assert(a == b * c + a % b); // There is no case in which this doesn't holdreturn c;}/*** @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).*/function sub(uint256 a, uint256 b) internal pure returns (uint256) {assert(b <= a);return a - b;}/*** @dev Adds two numbers, throws on overflow.*/function add(uint256 a, uint256 b) internal pure returns (uint256) {uint256 c = a + b;assert(c >= a);return c;}
}/*** @title Ownable 权限控制* @dev The Ownable contract has an owner address, and provides basic authorization control* functions, this simplifies the implementation of "user permissions".*/
contract Ownable {address public owner;//这里是个事件,供前端监听event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);/*** @dev The Ownable constructor sets the original `owner` of the contract to the sender* account.这是一个构造函数,在合约启动时只运行一次,将合约的地址赋给地址owner*/function Ownable() public {owner = msg.sender;}/*** @dev Throws if called by any account other than the owner。这里就是modifier onlyOwner的修饰符,用来判定是否是合约的发布者* 修饰器,通常用于校验函数权限只有合约的拥有者才能执行函数*/modifier onlyOwner() {require(msg.sender == owner);_;}/*** @dev Allows the current owner to transfer control of the contract to a newOwner.* @param newOwner The address to transfer ownership to.* 让合约拥有者修改指定新的合约拥有者,并调用事件来监听*/function transferOwnership(address newOwner) public onlyOwner {require(newOwner != address(0));OwnershipTransferred(owner, newOwner);owner = newOwner;}}contract bmtToken is ERC20Interface , Ownable{using SafeMath for uint;//代币名称string public name;//代币符号 ,比如 BTCstring public symbol;//代币精度,比如 ETC 有18位uint8 public decimals;//发行总额uint public totalSupply;//地址 => 余额 映射关系mapping(address => uint) balances;//授权 地址A 授权给 地址B  多少个代币mapping(address => mapping(address => uint)) allowed;constructor() public {name = "BMT Token";symbol = "BMT";decimals = 18;totalSupply = 1000000000000000000;balances[msg.sender] = totalSupply;Transfer(address(0),msg.sender,totalSupply);}function totalSupply() public constant returns(uint){return totalSupply;}//获取账户余额function balanceOf(address tokenOwner) public constant returns(uint){return balances[tokenOwner];}//转账给地址tofunction transfer(address to, uint tokens) public returns (bool){balances[msg.sender] = balances[msg.sender].sub(tokens);balances[to] = balances[to].add(tokens);//触发事件Transfer(msg.sender,to,tokens);return true;}//被授权账户余额function allowance (address tokenOwner,address spender) public constant returns(uint){return allowed[tokenOwner][spender];}//授权给账户address tokens个代币function approve(address spender,uint tokens) public returns(bool){allowed[msg.sender][spender] = tokens;//触发事件Approval(msg.sender,spender,tokens);return true;}//from 转账给 to function transferFrom(address from,address to,uint tokens) public returns(bool){allowed[from][to] = allowed[from][to].sub(tokens);balances[from] = balances[from].sub(tokens);balances[to] = balances[to].add(tokens);//触发事件Transfer(from,to,tokens);return true;}//niming匿名 huigun回滚 hanshu函数function () payable{//hanshu函数buneng不能 zhixing执行,hui会 huigun回滚 revert();}//合约 与 合约 之间交互,代币 与 代币 交易 function transferAnyERC20Token(address tokenAddr,uint tokens)public onlyOwner returns(bool) {//nenggou能够 jieshou接收接收renhe接收 shix实现ERCjiekou接口 d的 daibi代币 zhuangzhang转账 ERC20Interface(tokenAddr).transfer(msg.sender,tokens);}}

solidity开发讲解相关推荐

  1. 以太坊+IPFS+WEB 电商平台开发讲解

    以太坊+IPFS+WEB 电商平台开发讲解 作者: AlexTan CSDN: http://blog.csdn.net/alextan_ Github: https://github.com/Ale ...

  2. 【0基础】学习solidity开发智能合约-初识solidity

    本篇课程开始,我们来学习一下如何使用solidity开发智能合约,由于博主对于solidity的学习,也是自学的,所以一些不足或有纰漏之处还望指出,大家共同进步,本系列课程会分很多节课讲述,从入门到进 ...

  3. JEECG_3.7 权限开发讲解-张代浩-专题视频课程

    JEECG_3.7 权限开发讲解-743人已学习 课程介绍         系统性讲解jeecg权限设计.真实案例讲解各权限规则使用方法 课程收益     通过视频教程,掌握jeecg权限开发 讲师介 ...

  4. WebFlux响应式编程基础之 5 webflux服务端开发讲解

    https://blog.csdn.net/qq_27093465/article/details/64124330 debug技巧 第5章 webflux服务端开发讲解 Spring5 非组塞的开发 ...

  5. WebFlux响应式编程基础之 6 webflux客户端声明式restclient框架开发讲解

    第6章 webflux客户端声明式 restclient框架开发讲解 看不懂,为什么看不懂? 写方法最主要考虑输入与输出 Feign Retrofit 框架 6-1 框架效果介绍 6-2 设计思路 6 ...

  6. Yocto开发讲解系列 - 总目录

    Yocto开发讲解系列总纲 Yocto开发专栏前言 Yocto开发专栏总目录 快速上手 Yocto理论篇 Metadata Layer BitBake工具 Toolchain或交叉编译器 Linux内 ...

  7. Android studio百度地图SDK开发 2020最新超详细的Android 百度地图开发讲解(3) 路线规划步行骑行驾车路线规划

    2020最新超详细的Android 百度地图开发讲解(3) 路线规划步行骑行驾车路线规划 开发前配置,显示基本地图,实时定位等查看之前代码,此博客紧接上一博客:https://blog.csdn.ne ...

  8. Unity初学者课堂—助手游戏局部功能开发讲解之开始游戏倒计时

    Unity初学者课堂-助手游戏局部功能开发讲解之开始游戏倒计时 学习闲言 游戏开始倒计时 地址栏qq后面号码非本人号码,避免误解已搬家至https://blog.csdn.net/weixin_403 ...

  9. S3C2440上LCD驱动 (FrameBuffer)实例开发讲解

    1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其中的代码也可直接参考:drivers/video/s3c2410fb.c 以下为转载文章,文章原地址:http://blog. ...

最新文章

  1. GreenDao存储自定义类型对象解决方案(转)
  2. WebStorm添加多个项目到当前工程目录
  3. 强烈推荐的TensorFlow、Pytorch和Keras的样例资源(深度学习初学者必须收藏)
  4. 计算机多文件管理,电脑文件管理几条小技巧
  5. 驱动编程class_create说明
  6. 做好前端的话HTML和CSS基础必须夯实!
  7. linux命令echo的实现,Linux echo命令的使用及三种实现方式
  8. python爬取网页实时数据_使用 Python 爬取网页数据
  9. 梳理的关于mongodb的基础使用命令:----查询记录点--推荐使用:
  10. 程序员眼中的中国传统文化-王阳明《传习录》21
  11. potato chat 怎么用不了 土豆聊天软件 登陆不上 连接中 无法接收验证码 一直转圈...
  12. 用c语言编写一个日期计算器
  13. 安装程序无法打开注册表项 UNKNOWN\Components\…解决办法
  14. ISP中的Lens shading整理不易
  15. 简述计算机五种常见动画及特点。【可拆成多个简答】
  16. android 取消蓝牙配对框 实现自动配对,Android 取消蓝牙配对框实现自动配对功能...
  17. 现有论文和作者两个实体,论文实体的属性包括题目、期刊名称、年份、期刊号;作者实体的属性包括姓名、单位、地址;一篇论文可以有多个作者,且每一位作者写过多篇论文,在每一篇论文中有作者的顺序号。请完成以下操
  18. pacemaker+corosync+pcs实验
  19. Python语言程序设计 第一周习题
  20. Android怎么保证广播安全,Android 广播机制安全性研究.PDF

热门文章

  1. android百度地图集成,android 百度地图集成之 导航
  2. 名片识别信息分类python_基于PYTHON的名片识别接口调用代码实例
  3. Putty和8uftp的使用
  4. el-drawer抽屉打开遮住目录
  5. echarts显示纵坐标轴上的箭头
  6. Linux的软件安装
  7. android 4.0 编译,编译Android 4.0.4(打包出问题)
  8. C#分割字符串。歌词
  9. html修改字体华文彩云,CSS里怎么修改字体样式?需要修改字体和居中显示
  10. Flash转换为mp4等等格式