Solidity学习记录
Soldity0.8新特性·
- 安全数学
- 自定义错误
- 函数在合约之外
- 引用,引用合约的名称可以起别名
- create 同方法由原来的内联汇编变成了一个
//safe math
contract SafeMath{
function testUnderflow() public pure returns (uint){
uint x = 0;
x--;
return x;
}
function testUncheckedUnderflow() public pure returns (uint){
uint x = 0;
unchecked { x--;}
return x;
}
//custom error
error Unauthorized(address caller);
contract VendingMachine{
address payable owner = payable(msg.sender);
//error Unauthorized();
function withdraw() public{
if(msg.sender != owner)
//23642
//revert("error");
revert Unauthorized(msg.sender);
owner.transfer(address(this).balance);
}
}
//function outside contract
//使用类似于库--------》这种可以
function helper(uint x) view returns (uint){
return x * 2;//在本函数中不允许有函数变量
}
contract TestHelper{
function test() external view returns (uint){
return helper(123);
}
}
//function outside contract
//使用类似于库——————》这种报错
function helper(uint x) view returns (uint){
return foo+x * 2;//在本函数中不允许有函数变量
}
contract TestHelper{
uint foo;
function test() external view returns (uint){
return helper(123);
}
}
//* import {sysboll as alias,symbol2} from "filename";
import{Unauthorized,helper as h1} from "./Sol.sol";
function helper(uint x) view returns (uint){
}
contract Import{
}
HelloWorld
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract HelloWorld{
string public myString = "Hello World!";
}
类型和值
//Data types - values and references 类型和值
contract ValueTypes{
bool public b = true;
uint public u = 123;// uint = uint256 0 to 2**256 - 1
// uint8 0 to 2** 8 - 1
// uint16 0 to 2** 16 - 1
int public i = 123; // int = int256 -2**255 to 2**255 -1
// int128 -2**127 to 2**127 -1
int public minInt = type(int).min;
int public maxInt = type(int).max;
address public addr = ...;//十六进制数字,私钥公钥算出
bytes public b32 = 。。。//32位;
函数简介
//函数简介
//external 外部函数,只能在外部读取的函数
//pure 纯函数 不能读也不能写状态变量,只能够拥有局部变量
contract FunctionIntro{
function add(uint x,uint y) external pure returns (uint){
return x+y;
}
function sub(uint x,uint y) external pure returns (uint){
return x-y;
}
}
状态变量·
//状态变量
//变量分为三种:状态变量,局部变量,全局变量
//状态变量:就是相当于把一个数据写入区块链上,只要不写修改的方法就将永远保存在链上(值固定)
contract StateVariable{
uint public myUint = 123;//状态变量
function foo() external {
uint notSateVariable = 456;//局部变量:只在函数调用的时候才会产生,在函数内部
}
}
局部变量·
//状态变量和局部变量
contract LocalVariables{
uint public i;
bool public b;
address public myAddress;//状态变量//方法调用后,定义值//会让链上的数据改变
function foo() external {
uint x = 123;
bool f = false;//uint和bool 局部变量,只在函数内部生效//不会让链上的数据改变
//more code
x +=456;
f = true;
i = 123;
b =true;
myAddress = address(1);//定义了状态变量的值
}
}
全局变量·
//全局变量
//不用定义就能显示内容的变量,这些变量往往记录了链上和账户的信息
contract GlobalVariables{
//最常用的全局变量
//view只读方法,view会读取一些变量的值,如状态和全局变量(pure不能读取状态和全局变量,只可以局部变量)
function globalvars() external view returns (address,uint,uint){
//全局变量
address sender = msg.sender;//调用这个函数的地址是什么。调用这个函数的地址指的是上一个调用它的,可能是人也可能调用这个合约的合约
uint timestamp = block.timestamp;//时间戳,是区块的时间戳。只读的--->当前按下按钮的时间戳。如果是写入方法--->出块的时间,不是真实的时间
uint blockNum = block.number;//当前的区块号
return (sender,timestamp,blockNum);
}
}
只读函数·
//只读函数
//pure和view
contract ViewAndPureFunctions{
uint public num;//状态变量
function ViewFunc1() external view returns(uint){
return num;
}
/*报错 function ViewFunc2() external pure returns (uint){
return num;
}
*/
//区别:
function addToNum(uint x) external view returns (uint){//读取了number这个变量
return num + x;
}
function add(uint x,uint y) external pure returns (uint){//只把参数进行相加的运算,并不会读取number
return x + y;
}
}
计合器合约
//对状态变量更改不能有view或pure,否则不能更改,冲突
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Counter{
uint public count;
function inc() external {
count +=1;
}
function dec() external {
count -=1;
}
}
//inc增加
//+=就是count=count+1,自封1
//dec减少
//外部可视的含义是合约内部的其他的函数是不能调用的
默认值·
//默认值
contract DefaultValues{
bool public b;//false
uint public u;//0
int public i;//0
address public a;//零地址 40个零,20位十六进制数字0x0000000000000000000000000000000000000000
bytes public b32;//32位十六进制数字,64个零
//mapping,structs,enums,fixed,sized,arrays
}
常量·
//常量
contract Constants{
//address public myAddr = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
address public constant MY_ADDRE = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
uint public constant MY_UINT = 123;
//变量--->常量,只需constant。常量变量名称要改成大写,中间用下划线做连接
}
contract Var{
address public MY_ADDRE = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
}
//在写入函数中读取常量就要按照它是否定义常量来消耗gas,所以要把不需要修改的值改为常量
结构控制·
//结构控制
//三元控制的简化语法
contract IfElse{
function example(uint _x) external pure returns (uint){
if (_x<10){
return 1;
}else if (_x <20){
return 2;
}else {
return 3;
}
/* if (_x<10){
return = 1;
}else if (_x <20){
return 2;
} 可以简化掉
return 3;
*/
}
//三元控制符
function ternary(uint _x) external pure returns (uint){
/* if (_x<10){
return 1;
}return 2;
*/
//简化为:
return _x<10 ? 1:2;//?则,:else
}
}
循环控制·
//循环控制
contract ForAndWhileLoops{
function loops() external pure{
for(uint i = 0;i< 10;i++){
//code
if(i==3){
continue ;
}
//more code
if(i == 5){
break ;//彻底
}
}
uint j = 0;
while (j < 10){//condition==true=>无限循环
//code
j++;
}
}
function sum(uint _n) external pure returns (uint){
uint s;
for (uint i=1;i<= _n;i++){
s += i;//s=s+i
}
return s;
//javaSricp无妨,但是在合约中n不能太大,gas浪费
}
}
报错控制
//报错控制
//require。revert,assert
//-gas refund,state updates are reverted gas退还,状态变量回滚
//custom error - save gas 自定义错误-节约
contract Error {
function testRequire(uint _i) public pure{
require(_i <= 10,"i>10");
//code
}
function testRevert(uint _i) public pure{
if(_i>10){
revert("i>10");
}
}
uint public num = 123;
function testAssert() public view{
assert(num == 123);
}
/* function foo() public {
//accidentally update num
num += 1;
require();
}
*/
//revert不能包含表达式。可以多曾嵌套
//require和revert的表达式方向是相反的
//assert 断言 用智能合约去写测试的方法
function foo(uint _i) public{
num += 1;
require(_i<10);
}
error MyError(address caller,uint _i);
function testCustomError(uint _i) public view{//view
if(_i>10){
revert MyError(msg.sender,_i);//revert
}
//code
}
}
函数修改器
//函数修改器
//是一种使服用的代码简化的语法
//Functionmodifier - reuse code before and /or after function
//Basic,inputs,sandwich 有三种:基本类型、输入参数、三明治式的
contract FunctionModifier{
bool public paused;
uint public count;
//paused状态变量
//_;使用修改器的其他代码在哪儿
//1、
function setPause(bool _paused) external{
paused = _paused;
}
modifier whenNotPaused(){
require(!paused,"paused");
_;
}
function inc() external whenNotPaused{
count += 1;
}
function dec() external whenNotPaused{
// require(!paused,"paused");
count -= 1;
}
//2、
modifier cap(uint _x){
require(_x < 100,"x>= 100");
_;
}
function invBy(uint _x) external whenNotPaused cap(_x){
// require(_x < 100,"x>= 100");
count += _x;
}
//3、
modifier sandwich(){
//code here
count += 10;
_;
//more code here
count *= 2;
}
function foo() external sandwich{
count += 1;
}
//单独的下划线“_”代表其他代码
}
构造函数
//构造函数
//是一个特殊函数,仅能在合约部署的时候,被调用一次,之后再也不能被调用
//一般用于初始化变量
contract Constructor{
address public owner;
uint public x;
constructor(uint _x){
owner = msg.sender;
x = _x;//把输入的变量x赋值到状态变量中
}
}
Ownable合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//state variables 状态变量
//global variables 全局变量
//function modifier 函数修改器
//function 函数
//error handling 错误的控制
//权限管理的Owanavle合约 (经常遇到)
contract Ownable{
address public owner;
constructor(){
owner = msg.sender;
}
modifier onlyOwner(){
require(msg.sender == owner,"not owner");
_;
}
function setOwner(address _newOwner) external onlyOwner{
require(_newOwner != address(0),"invalid address");//不能是零地址,否则合约所有权锁死
owner = _newOwner;
}
function onlyOwnerCallThisFunc() external onlyOwner{
//code
}
function anyOneCanCall() external{
//code
}
}
//因为构造函数值在部署的时候执行一次,而且是自动执行。
//之后都不会再执行和调用,可以确保ower是部署者
函数返回值
//函数返回值
//Return multiple outputs
//Named outputs
//Destructuring Assignments
contract FunctionOutputs{
function returnMany() public pure returns (uint,bool){
return (1,true);
}
function named() public pure returns (uint x, bool b){
return (1,true);
}
function assi gned() public pure returns (uint x,bool b){
// return (1,true);
x = 1;
b = true;
}
function destructingAssigments() public pure{
(uint x,bool b) = returnMany();
(,bool _b) = returnMany();
}
}
数组
//数组
//Array - dynamic or fixed size ||数组-动态或固定大小
//Initialization ||初始化
//Insert(push),get,update,delete,pop,length ||插入(推送)、获取、更新、删除、弹出、长度
//Creating array in memory ||在内存中创建数组
//Returning array from function || 从函数返回数组
contract Array{
uint[] public nums =[1.2.3] ;//动态数组。长度可变
uint[3] public numsFixed = [4,5,6];//固定数组的定长数组,长度固定
function examples() external{
nums.push(4);//push:推
uint x = nums[1];
nums[2] = 6666;
delete nums[1];//不会修改数组长度,会将索引值修改为默认值
nums.pop();//pop,弹出数组最后一个值,减少数组长度
uint len = nums.length;//数组长度
//create aaray in memory--->局部变量
uint[] memory a = new uint[](5);//在内存中不能创建动态数组,所以必须定义长度
a[0]=888;
}
function return Array() external view returns (uint [] memory){
returns nums;
}
}
//在内存中,局部变量只能够定义定长数组
//动态数组只能够存在于状态变量中
数组删除元素通过移动位置
//数组删除元素通过移动位置
contract ArrayShift {
uint[] public arr;
function example() public{
delete arr[1];
}
//[1,2,3] ..remove(1) -->[1,3,3] -->[1,3]
//[1,2,3,4,5,6] -- remove(2) -->[1,2,4,5,6,6]-->[1,2,4,5,6]
function remove(uint _index) public{
require(_index < arr.length,"index out of bound");
for(uint i= _index;i < arr.length - 1;i++){
arr[i] = arr[i + 1];
}
arr.pop();
}
function test() external{
arr = [1,2,3,4,5];
remove(2);
//[1,2,4,5]
assert(arr[0] == 1);
assert(arr[1] == 2);
assert(arr[2] == 4);
assert(arr[3] == 5);
assert(arr.length == 4);
arr = [1];
remove(0);
//[]
assert(arr.length == 0);
}
}
//由于pop只是删除最后一个元素,所以需要把remove下标的元素开始,
//后面元素全部左移,再最后pop
删除数组元素通过替换
//删除数组元素通过替换
//remove array element by shifting elements to left
//[1,2,3,4,5,6] -- remove(2) -->[1,2,4,5,6,6] -->[1,2,4,5,6]
//特点:保持数组原有的顺序,gas浪费
contract ArrayReplaceLast {
uint[] public arr;
// [1,2,3,4] -- remove(1) -->[1,4,3]
// [1,4,3] -- remove(2) -->[1,4]
//节约gas,但数组顺序会打乱
function remove (uint _index) public{
arr[_index] = arr[arr.length - 1];
arr.pop();
}
function test() external {
arr = [1,2,3,4];
remove(1);
//[1,4,3]
assert(arr.length == 3);
assert(arr[0] == 1);
assert(arr[1] == 4);
assert(arr[2] == 3);
remove(2);
//[1,4]
assert(arr.length == 2);
assert(arr[0] == 1);
assert(arr[1] == 4);
}
}
映射
//映射
//Mapping
//How to declare a mapping (simple and nested) ||如何声明映射(简单和嵌套)
//Set,get,delete
//["alice","bob","charlie"]
//{"alice":true,"bob":true,"charlie":true}
contract Mapping{
mapping (address => uint) public balances;
mapping (address =>mapping (address => bool)) public isFriend;
function examples() external {
balances[msg.sender] = 123;
uint bal = balances[msg.sender];
uint bal2 = balances[address(1)];//0
// 映射中不存在的元素,默认为 uint的默认值===0
balances[msg.sender] += 456;//123+456=579
delete balances[msg.sender];//删除之后返回默认值,0
isFriend[msg.sender] [address(this)] = true;
//嵌套:有两个主键,设两个值,前是调用者后当前合约的地址
}
}
映射迭代
//映射迭代
contract IterableMapping {
mapping (address => uint) public balances;
mapping (address => bool) public inserted;
address[] public keys;
function set (address _key,uint _val ) external {
balances [_key] = _val;//_key是不是已插入
if(!inserted[_key]){
inserted[_key] = true;
keys.push(_key);
}
}
function getSize() external view returns (uint){
return keys.length;
}
function first() external view returns (uint){
return balances[keys[0]];
}
function last() external view returns (uint){
return balances[keys[keys.length - 1]];
}
function get(uint _i) external view returns (uint){
return balances[keys[_i]];
}
}
结构体
//结构体
contract Structs{
struct Car{
string model;
uint year;
address owner;
}
Car public car;
Car[] public cars;
mapping (address =>Car[]) public carsByOwner;
function examples() external {
Car memory toyota = Car("Toyota",1990,msg.sender);
Car memory lambo = Car({model:"Lamborhini",year:1980,owner:msg.sender});
Car memory tesla;
tesla.model = "Tesla";
tesla.year = 2010;
tesla.owner = msg.sender;
cars.push(toyota);
cars.push(lambo);
cars.push(tesla);
cars.push(Car("Ferrari",2020,msg.sender));
// Car memory _car = cars[0];
// _car.model;
// _car.year;
Car storage _car = cars[0];
_car.year = 1999;
delete _car.owner;
delete cars[1];
}
}
枚举
//枚举
contract Enum{
//可以使一个变量拥有多种状态
//六种状态
//枚举和结构体都是一种类型
//类型用大写字母开头,变量用小写
//可以用类型定义数组
enum Status{
None,
Pending,
Shipped,
Completed,
Rejected,
Canceled
}
Status public status;
struct Order{
address buyer;
Status status;
}
Order[] public orders;
function get() view external returns (Status){
return status;//不会返回字符串,返回所在的索引
}
function set(Status _status) external {
status = _status;
}
function ship()external {
status = Status.Shipped;
}
function reset()external {
delete status;
}
}
部署合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract TestContract1{
address public owner = msg.sender;//外部可见的方法
function setOwner(address _owner)public {
require(msg.sender == owner,"not owner");
owner = _owner;
}
}
contract TestContract2{
address public owner = msg.sender;
uint public value = msg.value;
uint public x;
uint public y;
constructor(uint _x,uint _y)payable{//构造
x=_x;
y=_y;
}
}
contract Proxy{
event Deploy(address);
function deploy(bytes memory _code) external payable returns (address addr){
// new TestContract1();//code机器码
address addr;
assembly{//内联汇编
//create(v,p,n)
//v = amount of ETH to send 部署合约发送以太坊主币的数量
//p = pointer in memory to start of code 内存中机器码开始的位置
//n = size of code 内存中机器码存在的大小
addr := create(callvalue(),add(_code,0x20),mload(_code))
//:=就是快捷声明一个变量然后给他赋值,go里面也是这么写的
}
require(addr != address(0),"deploy failed");
emit Deploy(addr);
}
function execute(address _target,bytes memory _data)
external
payable{
(bool success,) = _target.call{value: msg.value}(_data);
require(success,"failed");
}
}
contract Helper {
function getByecode1() external pure returns (bytes memory){
bytes memory bytecode = type (TestContract1).creationCode;
return bytecode;
}
function getBytecode2(uint _x,uint _y)external pure returns (bytes memory){
bytes memory bytecode = type(TestContract2).creationCode;
return abi.encodePacked(bytecode,abi.encode(_x,_y));
}
function getCalldata(address _owner) external pure returns (bytes memory){
return abi.encodeWithSignature("setowner(address)", _owner);
}
}
存储位置
//存储位置
//Data locations - storage,memory and calldata 数据位置-存储、内存和通话数据
//storage:状态变量
//memory:局部变量只在内存中生效,即使修改值也不会修改掉状态变量
//calldata:用在输入参数中,节约gas
contract DataLocation{
struct MyStruct{
uint foo;
string text;
}
mapping (address =>MyStruct) public myStructs;
function examples (uint[] calldata y,string calldata s ) external returns (uint[] memory){
myStructs[msg.sender] = MyStruct({foo:123,text:"bar"});
MyStruct storage myStruct = myStructs[msg.sender];
myStruct.text = "foo";//状态变量改变
MyStruct memory readOnly = myStructs[msg.sender];
readOnly.foo = 123;//局部变量的状态,不能修改链上的状态
_internal(y);
uint[] memory memArr = new uint [](3);
memArr[0] = 234;
return memArr;
}
function _internal (uint[] calldata y)private{
uint x = y[0];
}
}
//字符串的本质也是数组(bytes类型的)
简单存储
//简单存储
contract SimpleStorage{
string public text;
function set(string calldata _text) external {
text = _text;
}
function get() external view returns (string memory){
return text;
}
}
待办事项列表
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
//待办事项列表
//Insert,updata,read from array of structs 插入、更新、从结构数组读取
contract TodoList {
struct Todo{
string text;
bool completed;
}
Todo[] public todos;
function create(string calldata _text)external{
todos.push(Todo({
text: _text,
completed:false
}));
}
function updataText(uint _index,string calldata _text) external{
todos[_index].text = _text;
/* Todo storage todo = todos[_index];
todo.text = _index;
todo.text = _index;
todo.text = _index;
todo.text = _index;
*/
}
function get(uint _index) external view returns (string memory,bool){
Todo storage todo = todos[_index];
return (todo.text,todo.completed);
}
//改变待办完成的状态
function toggleCompleted(uint _index) external {
todos[_index].completed = ! todos[_index].completed;
}
}
事件
//事件
//记录当前智能合约运行状态的一种方法,不记录在状态变量中,而是区块链浏览器/交易记录中的LOgs
//声明事件,名称以大写字母开头
contract Event {
event Log(string message,uint val);
//up to 3 index
event IndexedLog(address indexed sender,uint val);
function example() external {
emit Log("foo",689);
emit IndexedLog(msg.sender,986);
}
event Message(address indexed _from,address indexed _to,string message);
function sendMessage(address _to,string calldata message) external{
emit Message(msg.sender,_to,message);
}
}
//emit:触发
//在一个事件中,可以汇报多个变量,但是有索引的变量最多只能有三个
//写消息调用可以用状态变量和事件,事件更节约gas
继承
//继承-virtual
contract A{
function foo() public pure virtual returns (string memory){
return "A";
}
function bar() public pure virtual returns (string memory){
return "A";
}
//more code here
function baz() public pure returns (string memory){
return "A";
}
}
contract B is A{
function foo() public pure override returns (string memory){
return "B";
}
function bar() public pure virtual override returns (string memory){
return "B";
}
}
contract C is B{
function bar() public pure override returns (string memory){
return "C";
}
}
//一、复制
//二、virtual is |override覆盖重写
多线继承
//多线继承
//Order of inheritance - most base - lke to derived
//继承顺序-最基本-从左到右
/*
X
/ |
Y |
\ |
Z
//order of most base like to derived
//X,Y,Z
X
/ \
Y A
| |
| B
\ /
Z
X,Y,A,B,Z
*/
contract X{
function foo() public pure virtual returns (string memory){
return "X";
}
function bar() public pure virtual returns (string memory){
return "X";
}
function x() public pure returns (string memory){
return "X";
}
}
contract Y is X{
function foo() public pure virtual override returns (string memory){
return "Y";
}
function bar() public pure virtual override returns (string memory){
return "Y";
}
function y() public pure returns (string memory){
return "Y";
}
}
contract Z is X,Y{
function foo() public pure override(X,Y) returns (string memory){//必按顺序
return "Z";
}
function bar() public pure override(X,Y) returns (string memory){//无所谓
return "Z";
}
}
运行父级合约构造函数
//运行父级合约构造函数
//2 ways to call parent constructors |//调用父构造函数的两种方法
//order of initialization |//初始化顺序
contract S {
string public name;
constructor(string memory _name){
name = _name;
}
}
contract T {
string public text;
constructor(string memory _text){
text = _text;
}
}
contract U is S("s"),T("t"){//已知参数内容的情况下,可以使用
}
contract V is S,T{//
constructor(string memory _name,string memory _text) S(_name) T(_text){
}
}
contract VV is S("s"),T{//以上两种混合使用
constructor(string memory _text) T(_text){
}
}
//Oreder of excution
//1.S
//2.T
//3.V0
contract V0 is S,T{
constructor(string memory _name,string memory _text) S(_text) T(_name){
}
}
//Order of execution
//1.S
//2.T
//3.V1
contract V1 is S,T{
constructor(string memory _name,string memory _text) T(_text) S(_name){
}
}
//Order of execution
//1.T
//2.S
//3.V2
contract V2 is T,S{
constructor(string memory _name,string memory _text) T(_text) S(_name){
}
}
//Order of execution
//1.T
//2.S
//3.V3
contract V3 is T,S{
constructor(string memory _name,string memory _text) S(_name) T(_text){
}
}
调用父级合约构造函数
/*
Calling parent functions
- direct
- super
E
/ \
F G
\ /
H
*/
contract E {
event Log(string message);
function foo() public virtual {
emit Log("E.foo");
}
function bar() public virtual {
emit Log("E.bar");
}
}
contract F is E{
function foo()public virtual override {
emit Log("F.foo");
E.foo();
}
function bar() public virtual override {
emit Log("F.bar");
super.bar();
}
}
contract G is E {
function foo() public virtual override {
emit Log("G.foo");
E.foo();
}
function bar() public virtual override {
emit Log("G.bar");
super.bar();
}
}
contract H is F, G {
function foo() public override (F,G){
F.foo();
}
function bar() public override (F,G){
super.bar();
}
}
//多重继承很容易踩坑,大部分语言都不支持
可视范围
//visibility
//private - only inside contract
//internal - only inside contract and child contracts
//public - inside and outside contract
//external - only from outside contract
//能见度
//私人-仅限内部合同
//内部-仅内部合同和子合同
//公共内外契约
//外部-仅来自外部合同
/*
______________________
| A |
| private pri() |
| internal inter() |
| public pub() |<----------C
| external ext() | pub() and ext()
|_____________________|
_______________________
| B is A |
| inter() |
| pub() |
|_____________________|
*/
contract VisibilityBase{
uint private x = 0;
uint internal y = 1'
uint public z = 2;
function privateFunc() private pure returns (uint){
return 0;
}
function internalFunc() private pure returns (uint){
return 100;
}
function publicFunc() private pure returns (uint){
return 200;
}
function externalFunc() private pure returns (uint){
return 300;
}
function examples() public view {
x + y + z;
privateFunc();
internalFunc();
publicFunc();
// externalFunc();外部函数不能在合约内部访问,只能够用调用这个合约的其他合约/个人账户去访问
//external内部可以通过this去调用
this.externalFunc();
}
}
contract VisibilityChild is VissibilityBase {
function examples2() external view{
//私有变量不能被继承 私有变量、外部函数不能访问
y+z;
internalFunc();
publicFunc();
}
}
不可变量
contract Immutable {
// address public owner = msg.sender;
/* address public immutable owner = msg.sender;//像常量一样节约gas。在合约部署的时候才定义值
//more code here
uint public x;
function foo() external {
require(msg.sender == owner);
x += 1;
}
*/
address public immutable owner;
constructor (){
owner = msg.sender;
}
uint public x;
function foo() external {
require(msg.sender == owner);
x += 1;
}
}
//两种写法
//immutable 必须在部署合约时赋值,才会像常量一样
支付ETH
contract Payable {
address payable public owner;
constructor (){
owner = payable (msg.sender);
}
function deposit() external payable{
}
function getBalance() external view returns (uint){
return address(this).balance;
}
}
回退函数
/*
Fallbsck executed when Fallbsck执行时
- function doesn't exist -函数不存在
- directly send ETH -直接发送ETH
fallback() or receive()?
Ether is sent to contract
|
is msg.data empty?
/ \
yes no
/ \
receive() exists? fallback()
/ \
yes no
/ \
receive() fallback()
*/
contract Fallback{
event Log(string func,address sender,uint value ,bytes data);
fallback() external payable {//可以不加payable
emit Log("fallback",msg.sender,msg.value,msg.data);
}
/* receive() external payable {//必须加payable
emit Log("receive", msg.sender, msg.value, "");
}
*/
}
//1、定义一个fallback,不需要加function
//2、直接接收主币
//receive函数是不接收任何数据的,不接收数据才会调用receive
//fallback:有数据调用/receive不存在
发送ETH
// 3 ways to send ETH 发送ETH的三种方式
//transfer - 2300 gas,reverts 传输-2300气体,返回
//send - 2300 gas ,returns bool send-2300 gas,返回bool
//call - all gas,returns bool and data call-all gas,返回bool和data
contract SendEther{
constructor() payable { //存入主币两种方法:1、构造函数中传入 2、回退函数接收主币
}
receive() external payable{
}
function sendViaTransfer(address payable _to) external payable{
_to.transfer(123);
}
function sendViaSend (address payable _to)external payable{
bool sent = _to.send(123);
require(sent,"send failed");
}
function sendViaCall(address payable _to) external payable{
(bool success, ) = _to.call{value:123}("");
require(success,"call failed");
}
}
//接受主币发送的地址
contract EthReceiver{
event Log(uint amount,uint gas);
receive() external payable {
emit Log(msg.value,gasleft());
}
}
//transfer 和send都会消耗2300gas
//call会消耗剩余的所有gas,会返回两个值,bool 和data
//transfer不返回值,直接会报错
//send返回一个bool值
钱包合约
contract EtherWallet{
address payable public owner;//规定管理员
constructor(){//在构造函数中将部署者的身份传入owner,实现管理员定义
owner = payable (msg.sender);
}
receive() external payable { }//定义回退函数,接受主币发送
function withdraw (uint _amount) external{//取款方法
require(msg.sender == owner,"caller is not owner");//判断
payable (msg.sender).transfer(_amount); //发送主币
// (bool senet,) = msg.sender.call{value:_amount}("");
// require(sent,"Failed to send Ether");
}
function getBalance() external view returns (uint){//获取余额
return address(this).balance;
}
}
调用其他合约
接口合约
只要知道要调用的函数的名称和参数类型等信息就可以调用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Counter{
uint public count;
function inc() external{
count += 1;
}
function dedc() external{
count -= 1;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
interface ICounter {
function count() external view returns (uint);
function inc() external ;
}
contract CallInterface{
uint public count;
function examples(address _counter) external {
ICounter(_counter).inc();
count = ICounter(_counter).count();
}
}
低级call
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract TestCall{
string public message;
uint public x;
event Log(string message);
fallback() external payable{
emit Log("fallback was called");
}
function foo(string memory _message,uint _x) external payable returns (bool,uint){
message = _message;
x= _x;
return (true,999);
}
}
contract Call{
bytes public data;
function callFoo(address _test) external payable {
(bool success,bytes memory _data) = _test.call{value:666,gas:6000}(abi.encodeWithSignature(
"foo(string,uint256)","call foo",123));
require(success,"call failed");
data = _data;
}
function callDoesNotExit(address _test)external{
(bool success,) = _test.call(abi.encodeWithSignature("doesNotExist()"));
require(success,"call failed");
}
}
委托调用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/*
A calls B,sends 100wei
B call C,sends 50 wei
A--->B--->C
msg.sender = B
msg.value = 50
execute code on C's state variables
use ETH in C
委托调用
A call B,sends 100 wei
B delegatecall C
A -->B -->C
msg.sender = A
msg.value = 100
execute code on B's state variables
use ETH in B B是可以改变状态变量的值的
委托调用不能改变自己(C)所有的值
*/
contract TestDelegateCall{
uint public num;
address public sender;
uint public value;
address public owner;
function setVars(uint _num) external payable{
num =2 *_num;
sender= msg.sender;
value = msg.value;
}
}
//被调用合约中的变量的顺序、名称、类型必须和委托合约完全一致。否则,会出现错误
//修改变量位置的方法:可以在被调用合约的三个变量后面增加变量,在后面增加就不会修改前面的位置
contract DelegateCall{
uint public num;
address public sender;
uint public value;
function setVars(address _test,uint _num)external payable {
//_test.delegatecall(
// abi.encodeWithSignature("setVars(uint256)",_num) //签名编码|低级call
// );
(bool success,bytes memory data) = _test.delegatecall(abi.encodeWithSelector(TestDelegateCall.setVars.selector,_num)
);
require(success,"delegatecall failed");
}
}
//slector进行编码,也可以应用在低级call中
//使用上一个合约的名字+函数名字+.selector
//好处:避免写函数签名时写错--->大小写/标点/类型
//被调用的合约的值不能够被改变,只能使用被调用合约的逻辑来改变当前委托合约中的状态变量的值
contract Counter{
uint public counter;
constructor(){
counter = 0;
}
function count () public {
counter = counter + 1;
}
function get() public view returns (uint){
return counter;
}
}
/*
1.委托调用(委托智能合约功能执行):
第一个合约“TestDelegateCall”有一个函数“setVars”,它接受两个参数:一个无符号整数“_num”和随交易发送的Ether值。
该函数将“num”加1,并将“sender”设置为消息发送者(拨打电话的人或合同)的地址。
第二个合约“DelegateCall”具有类似的功能,但使用DelegateCall而不是常规调用。
Delegatecall进行一个不改变代码的外部低级函数调用,即它在调用合约的上下文中执行被调用合约的字节码(它不创建新事务)。
2.功能签名:
在delegatecall中,我们需要手动对函数签名进行编码,因为Solidity没有提供用于编码和解码这些签名的内置函数。
这里使用函数`abi.encodeWithSignature`,但重要的是要注意,如果您更改参数的顺序或添加新的参数而不递增abi版本,这可能会导致问题,
因为不同的版本可能在同一位置编码了不同的类型。
相反,我们使用内置的`abi.encodeWithSelector `函数,该函数将对任何给定函数签名的选择器(前四个字节)进行正确编码。
它使用起来更坚固、更安全。在第二个合约中使用此函数的代码行被注释掉了,但如果你想看到它的实际操作,可以取消注释。
3.反合同:
第三个合约“Counter”演示了一个具有两个功能的简单计数器。一个将计数增加一个,另一个返回当前计数值。
这可用于跟踪投票或应用程序中的类似功能。当部署合约的新实例时,构造函数将初始计数设置为零。
*/
工厂合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//通过new新语句来新建合约
contract Account{
address public bank;
address public owner;
constructor (address _owner)payable {
bank = msg.sender;
owner = _owner;
}
}
contract AccountFactory {
Account[] public accounts;
function creatAccount(address _owner) external payable{
Account account = new Account{value:689}(_owner);
accounts.push(account);
}
}
//两个合约是引用关系,如果不在一个文件中,可以用import导入
库合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//把算法抽象出来--->库合约:避免重复代码的编写
//库合约以libaray开头,合约名称以大写字母开头,一般在合约内部使用,
//若希望外部可以读取,可以使用public。external会使当前合约内部无法读取
library Math{
function max(uint x,uint y)internal pure returns (uint){
return x >= y ? x:y; //三元运算符:x如果大于等于y则返回x否则返回y;
}
}
contract Test{
function testMax(uint x,uint y)external pure returns (uint){
return Math.max(x,y);
}
}
//创建一个数组,寻找数组中某一个特定的值,返回特定值对应的索引
//查找数组中对应值的索引的算法---->库合约,作一个函数,这个函数会应用到当前合约中所有使用此算法的函数中
library ArrayLib {//数组存储在内存,这里必须用storage,传入数组是状态变量
function find(uint[] storage arr,uint x ) internal view returns (uint){//必须view,读取了状态变量的值
for(uint i =0;i<arr.length;i++){
if(arr[i] == x){
return i;
}
}
revert("not found");
}
}
contract TestArray{//两种写法,使用using更加简洁
using ArrayLib for uint[];//把库应用到数组这个类型中,这个类型拥有了库的函数的功能
uint [] public arr = [3,2,1];
function testFinded()external view returns (uint i){
// return ArrayLib.fund(arr,2);
return arr.find(2);
}
}
哈希运算
//哈希算法
//有两个特性:输入值相同,输出值一定相同。
//不管输入值多大,输出值是定长的。并且哈希算法是不可逆向运算的
//应用:签名运算,获取特定的id
//哈希值特定的格式bytes32字节类型的返回值
//有一个特定的内部函数keccak256
//打包:abi.encode(补零)/abi.encodePacked(有一定的压缩.不会补零易有漏洞)
//打包形式不同会发生哈希碰撞:输入值不同,输出值也相同
//不定长bytes都要加上memory
contract HashFunc{
function hash(string memory text,uint num,address addr) external pure returns(bytes32) {
return keccak256(abi.encodePacked(text,num,addr));
}
function encode(string memory text0,string memory text1)external pure returns (bytes memory){
return abi.encode(text0,text1);
}
function encodePacked(string memory text0,string memory text1)external pure returns (bytes memory){
return abi.encodePacked(text0,text1);
}
//不同的输入参数会产生相同的打包结果,相同的打包结果在运算哈希值的时候也会得到相同的哈希结果--->哈希运算碰撞
//哈希碰撞实验
function collision(string memory text0,uint x,string memory text1)external pure returns (bytes32){
return keccak256(abi.encodePacked(text0,text1));
}
//两种方案:使用encode,把输入参数隔开
}
验证签名
权限控制合约
自毁合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//selfdestruct --->智能合约自带的函数
//两个功能:删除合约/强制发送主币到一个地址
//-delete contract
//-force send Ether to any address
//自毁装置
//-删除合同
//-强制将Ether发送到任何地址
contract Kill{
//合约存储主币
constructor() payable{}
function kill() external{
selfdestruct(payable(msg.sender));//msg.sender默认没有payable
}
//测试函数:合约是否真正自毁成功
function testCall() external pure returns (uint){
return 689;
}
}
//测试强制发送主币---不存在回退函数---不能够接收主币
contract Helper{
function getBalance() external view returns (uint){
return address(this).balance;//返回当前合约的主币余额
}
//让助手合约调用kill合约的自毁功能---->把剩余的主币发送给助手合约
function kill(Kill _kill) external {
_kill.kill();
}
}
小猪存钱罐
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//可以通过任何人的地址向合约发送以太坊主币
//存钱罐的拥有者才可以从存钱罐中取出别人或者自己存储的主币
//取出之后会被打碎,合约自毁
contract PiggyBank{
//收款事件:向链外汇报收到的款项
event Deposit(uint amount);
//取款事件:取出了多少的数量
event Withdraw(uint amount);
//定义合约部署者(和在构造函数中给owner赋值效果一样)
address public owner = msg.sender;
//创建收款方法--->回退函数
receive() external payable{
emit Deposit(msg.value);//触发事件
}
//取款方法--->必须由合约的部署者调用
function withdraw() external {
//确认当前消息调用者和合约拥有者owner是否一致
require(msg.sender == owner,"not owner");
//汇报取款事件
emit Withdraw(address(this).balance);
//msg.sender代替合约拥有者。如果使用状态变量这里将多消耗gas
selfdestruct(payable(msg.sender));//将合约上的主币发送给合约的拥有者;合约销毁
}
}
ERC20合约
多签钱包
函数签名
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//函数签名:也叫函数的选择器。用来代表智能合约中虚拟机是如何找到函数的
contract FunctionSelector{
//获取函数选择器的合约
function getSelector(string calldata _func) external pure returns (bytes4){
return bytes4(keccak256(bytes(_func)));//将输入参数->字符串进行哈希运算,字符串不能够直接使用,需要转换
}
}
//0x75159a78
contract Receiver{
event Log(bytes data);
function transfer(address _to,uint _amount) external{
emit Log(msg.data);
//0xa9059cbb000000000000000000000000b27a31f1b0af2946b7f582768f03239b1ec07c2c0000000000000000000000000000000000000000000000000000000000000058
//0xa9059cbb 四字节的bytes类型,8个字符,占四个字节
//两部分输入参数
//000000000000000000000000b27a31f1b0af2946b7f582768f03239b1ec07c2c地址
//0000000000000000000000000000000000000000000000000000000000000058输入的数字类型
//呼叫一个函数的数据由两部分组成:1、函数的选择器/函数的签名 2、参数
//智能合约的虚拟机如何知道这个签名对应着的函数?
//这个函数签名通过将函数的名称和他的参数类型打包在一起进行哈希值
//然后取哈希值的前4位十六进制数字得到结果
}
}
//在智能合约中的虚拟机中,调用一个函数,要通过函数选择器去区分函数
//可以出现同名函数,只要参数类型不同,名称可以相同
荷兰拍卖

浙公网安备 33010602011771号