源文件中可以包含任意多个 合约定义 、导入源文件指令 、 版本标识 指令、 结构体 、 枚举 和 函数 定义.
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.
*/
状态变量
// 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() { // Modifier
require(
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); // Event
function 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
}
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 assembly
let 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 padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_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);
}
}
库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过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 there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.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);
}
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"; }
}
// 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() { // Modifier
require(
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 seller
function storeOnlySeller(uint256 num) public onlySeller {
// number = num;
number = Helper.add(num,1);
emit storeEvent("number",number);
}
// destory the contract
function 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);
}
}
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 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return 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;
//代币符号 ,比如 BTC
string 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];
}
//转账给地址to
function 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);
}
}