Solidity

solidity

版权声明

要不然会报错

 // SPDX-License-Identifier: MIT
 MIT 的版权协议
 // SPDX-License-Identifier:
 // SPDX-License-Identifier: SimPL-2.0

Hello World

 声明版本号 
 pragma solidity ^0.8.14;
 ​
 contract HelloWorld {
     string public myString  = "hello world";
 }

类型和值

 变量
 // SPDX-License-Identifier: MIT
 pragma solidity ^0.8.14;
 ​
 // Data types -values and references
 ​
 contract ValueTypes {
     bool public b = true;
     unit public u = 123; // uint = unit256 0 to 2**256  - 1   
                         //  uint = unit8 0 to 2**8  - 1 
                         //  uint = unit16 0 to 2**16  - 1   
                         
     int public i = -123; // int = int256 -2**255 to 2**255 - 1
                         // int = int128  -2**127 to 2**127 - 1
     int public minInt = type(int).min;  // int 有符号的整数的最小值 
     int public maxInt = type(int).max;  // int 有符号的整数的最大值 
     
     // 特殊的变量类型 地址 十六进制数字
     address public addr = 0x
 }
 ​
 ​
 bool:可以保存 true 或 false 作为其值的布尔值
 uint:这是无符号整数,只能保存0和正值
 int:这是可以保存负值和正值的有符号整数
 address:这表示以太坊环境中的账户地址
 byte:这表示固定大小的字节数组(byte1 到 bytes32)
 ​

函数

 external 外部函数 一个只能在外部读取的函数,内部是不能调用他的
 pure 纯函数 指的是这个函数 不能读不能写状态变量的值 只能拥有局部变量
 view 只读方法 会读取一些变量的值
 ​
 // SPDX-License-Identifier:MIT
 pragma solidity ^0.8.7;
 ​
 contract Function {
     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;
     }
 }
 ​
 ​
 ​

变量

 状态变量和局部变量
 // SPDX-License-Identifier:MIT
 pragma solidity ^0.8.7;
 ​
 contract StateVariables {
     uint public mjyUint = 123;  // 这是状态变量,永远记录在链上
     
     function foo() external {
         uint notState = 456; // 非状态变量 只能在调用foo这个函数时,才会产生
     }
 }
 ​
 全局变量 :
 pure 和 view  的区别
 pure纯函数,也就是无论何时何地何种情况,只要输入一样,输出就一样,pure是不能读取变量的,如全局变量和状态变量
 view  可以读取一些值,如状态变量或全局变量都可以通过view读取
 ​
 全局变量:
 msg.sender;
 block.timestamp;
 block.number;   当时的区块号
 ​
 // SPDX-License-Identifier:MIT
 pragma solidity ^0.8.7;
 contract GlobalVariables {
     function global() external view returns (address,uint,uint){
         address sender = msg.sender;
         uint timestamp = block.timestamp;
         uint blockNum = block.number;
         return (sender,timestamp,blockNum);
     }   
 }
 ​
 ​

只读函数

 ​
 // SPDX-License-Identifier: SimPL-2.0
 // SPDX-Lincense-Identifier:MIT
 ​
 pragma solidity ^0.8.7;
 contract ViewsAndPure{
     uint public num;
     
     funciton viewFunc() external view returns (unit){
         return num;
     }
      funciton prueFunc() external view returns (unit){
         return 1;
     }
     funciton addToNum(uint x) external [view] returns (unit){
         return num+x;  // 因为读取了num这个状态变量
     }
     funciton add(uint x,uint y) external [pure] returns (unit){
         return x+y;      //  因为他是个纯函数
     }
 }
 ​
 ​

计数器合约

 对以上学的进行总结
 ​
 ​
 // SPDX-License-Identifier: SimPL-2.0
 // SPDX-Lincense-Identifier:MIT
 ​
 pragma solidity ^0.8.7;
 contract Counter{
     uint public count;
     
     function inc() external {
         count +=1 ;
     }
     function dec() external {
         count -=1 ;
     }
 }
 ​

变量的默认值

 ​
 // SPDX-License-Identifiter:MIT
 ​
 pragma solidity ^0.8.7;
 ​
 contract DefaultValues {
     bool public b; // false
     uint public u; // 0 
     int public i; // 0
     address public a; // 0地址 0x四十个0   二十位二进制的数
     bytes32 public b32; // 0x 64个0  32位
 ​
 }
 ​
 ​
 ​

常量

constant 可以把变量变成常量   这里的变量名称一般用大写中间用下划线做连接

注意:一般可以把不需要修改的值变成常量


pragma solidity ^0.8.7;

contract Constants {
	address public constant MY_ADDRESS  = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
	uint public constant MY_UINT  =123;
}

contract Var {
	address public MY_ADDRESS  = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
	
}

结构控制

if  -- else


pragma solidity ^0.8.7;

contract IFElse {
	function example(uint _x) external pure returns (uint) {
		if (_x<10){
			return 1;
		} else if (_x < 20){
			return 2;
		} else {
			return 3;
		}
	}
	
	function tenary(uint _x) external pure resturns(uint){
		// if (_x <10) {
		//	return 1;
		//}
		//return 2; 同下简单的三元运算
		return _x <10 ? 1 : 2;
	}

}

循环控制

for循环

pragma solidity ^0.8.7;

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) {
			j++
		}
	}
	
	// 累加器
	function sum(uint _n) external pure returns (uint){
		uint s;
		for (uint i = 1;i <= _n;i++){
			s +=  i;
		}
		return s; 
	}
	
	
}



报错控制

require 
revert   不能包含表达式
assert  断言的含义
// gas refund 
// custom 自定义错误 节约gas 

pragma solidity ^0.8.7;


contract Error {
	function testRequire(uint _i) public pure{
		require(_i <=10,"i>10");   //  只有前面的表达式为真才能执行后面的代码 满足条件才能正确运行,后面跟着报错信息 
	}
	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(uint _i) public {
		num +=1;
		require(_i <10);
	}
	
	error MyError(address caller,uint i);
	
	function testCustomError(uint _i) public view {
		// require(_i <=10," very long error message error error");
		if (_i>10){
			revert MyError(msg.sender,_i);
		}
	}
	
}



函数修改器

modifier --关键词

_就相当于把所修饰的函数代码段复制到这里了,代码段中有return,那么就会返回,不会执行_;后面的代码段。

pragma solidity ^0.8.7;

contract FunctionModifiter {
	bool public paused;
	uint public count;

	function setPaused(bool _paused) external {
		paused = _paused
	}
	
	modifiter whenPaused(){  //函数修改器
		require(!paused,"paused");
		_;
	}
	
	function inc() external whenPaused{  //先进入修改器中
		// require(!paused,"paused");
		count += 1;
	}
	
	function dec() external whenPaused{
		// 这一块就可以省略-require(!paused,"paused");
		count -= 1;
	}
	
	// 带参数的修改器
	codifier cap(uint _x){
		require(_x <100, "x>=100");
		_;  //下划线代表函数中的其他代码,让其他的代码在后面运行
	}
	
	function incBy(uint _x) external whenPaused cap(_x) {
		count += _x;
	}
	
	// 三明治类型
	modifier sandwith(){
		//code here
		count += 10;
		_;
		// more code here
		count *=2;
	}
	
	function foo() external sandwith {
		count += 1;
	}
	
	
}

构造函数

仅能在部署合约时被调用一次,一般用来初始化一些变量的值

关键字 constructor

pragma solidity  ^0.8.7;

contract Constructor {
	adress public owner;
	uint public x;
	
	constructor(uint _x) {
		owner = msg.sender;    //  合约的部署者就是msg.sender变量中的值
		x = _x;
	}
}

Ownable

// state varialbes
// globle variables
// function modifier
// function 
// error handling
// SPDX-License-Identifier:
pragma solidity ^0.8.7;

contract Ownable {
	address public owner;
	
	constructor(){
		owner = msg.sender;
	}
	
	modifier onlyOwner(){
		require(msg.sender == owner,"not owner"); // 管理员才能调用
		_;
	}
	
	function setOwner(address _newOwner) external onlyOwner { // 函数是外部可视的 必须只能只有onlyOwner才能调用它
	
    	require(_newOwner != address(0), "invalid address"); // 新的地址不等于地址零  "报错信息 = invalid address"
    	owner = _newOwner;		
	}
	
	function onlyOwnerCancallThisFunc() external onlyOwner{
		// code 
	}
	
	function anyOwnerCancall() external{
		// code 
	}
}

函数的返回值

// return multiple outputs
// Named outputs
// Destructuring Assignment

contract FunctionOutputs {
	function returnMany() public pure returns (uint, bool){ //public 公开可视 外部内部都能调用函数 pure 纯函数
		
		return (1,true);
	} // 并不知道此函数的含义
	
	function named() public pure returns (uint x, bool b){ //public 公开可视 外部内部都能调用函数 pure 纯函数
		
		return (1,true);
	}
	
	function assigned() public pure returns (uint x, bool b){ //public 公开可视 外部内部都能调用函数 pure 纯函数
		x = 1;
		b = true;
	}
	
	function destructingAssigments() public prue{
		(uint x,bool b) = returnMany();
		(,bool b) = returnMany();  // 例如只需要bool类型的值
	}
}


数组

数组 有 动态数组和定长数组
数组的初始化
操作:插入 修改  删除  弹出 获取长度
在内存中创建数组
通过函数来返回数组

// SPDX-License-Identifier:SimPL-2.0
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;

contract Arry { 
	uint[3] public numFixed = [1,5,3];  // 定长数组
	
	function examples() external {
		nums.push();  //  动态数组用push的方法向数组的尾部推入一个数据 [1,2,3,4] 
		uint x = nums[1];
		
		nums[2] = 777; // [1,2,777,4]
		delect nums[1]; // [1,0,777,4]  不能删除数组的长度,会使值变成默认值0
		nums.pop();  // 弹出最后一个值,数组长度就会变短
		uint len = num.length; // 获得数组的长度
		
		// 在内存创建一个数组  是一个局部变量  关键词 memory
		uint[] memory  a  = new uint[](5); // 必须要定义长度,因为在内存中不能定义动态数组
		
		// 在内存中 不能使用a.弹出pop()和推入push()这种方法,因为会改变长度,改变长度的都不能使用
		
		a[1] = 123;  // 赋值可以
		
	}
	
	function returnArray() external view returns(uint[] memory) {  // 如果希望数组中所有内容全部返回 --内存的存储类型
		ruturn nums;
	}
	
}

删除数组元素通过替换

// 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


pragma solidity ^0.8.7;

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.length -1 数组中最后一个值
		arr.pop();
	}
	
	function text() 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}

pragma solidity ^0.8.7;

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类型的默认值 
		
		balances[msg.sender] += 456; //这里是在原有账号的基础上和余额进行计算 123+456=579
		
		delete balance[msg.sender]; // 删除数据, 恢复成默认值0
		
		isFriend[msg.sender][address(this)] = true
		// 第一个是调用者的地址,第二个是当前合约的地址 
	}
}

映射迭代

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract Mapping{
// 映射不能遍历

	mapping(address => uint) public  balances;
	mapping(address => bool) public  inserted;

	address[] public keys;
	// 实现可遍历式的这种映射	

	function Map(address _keys,uint num) external   {
		balances[_keys] = num;

		if (!inserted[_keys]){
			inserted[_keys] = true;
			keys.push(_keys);
		}
	}

	function getSize() external view returns (uint){
		return keys.length;
	}

	//  获得数组第一位的余额
	function frist() external view returns (uint){
		return balances[keys[0]];
	}

	//  获得数组最后一位的余额
	function last() external view returns (uint){
		return balances[keys[keys.length - 1]];
	}

	//  获得数组任意一个索引的余额
	function getInfo(uint _i) external view returns(uint){
		return balances[keys[_i]];
	}
	
}

结构体

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

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 toyata = Car("Toyata", 1998, msg.sender); // 这里必须按照顺序传入变量
        Car memory lambo = Car({model:"Lambogini",year:1980,owner: msg.sender});  // 顺序没影响
        Car memory tesla;
        tesla.model = "Tesla";
        tesla.year = 1896;
        tesla.owner = msg.sender;

        cars.push(toyata);
        cars.push(lambo);
        cars.push(tesla);

        cars.push(Car("Ferrari",2000,msg.sender));

        // 拿数据
        // Car memory _car = cars[0]; // 这是内存中 
        // _car.model;
        // _car.year;
        // _car.owner;

        Car storage _car = cars[0]; 
        // 如果是存储中,就可以对这个结构体的值进行修改和删除的操作了
        // 存储代表着我们把状态变量读取出来,而且是指针类型的读取
        _car.year = 1564;
        delete _car.owner; //删除后还是会恢复变成默认值,不会彻底删除
        
        delete cars[1];  // 整个结构体的三个值都会变成默认值


    }

}


枚举

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;


contract Enum {
    enum Status{
        //  枚举类型的值是以索引的形式存在的
        None, //  默认值,没有状态
        Pending,  //  处理中
        Shipped,          //  装载中
        Completed,   //  已装载的  
        Rejected,      //  已完成的
        canceled         // 已拒绝的,已取消的
    }

    Status public status;

    struct Order {
        address buyer;
        //  可以把枚举类型写进结构体内部
        Status status;
    }
    // 可以反复式的嵌套使用
    Order[] public order;

//  写一个只读函数,返回状态的类型就可以
    function getStatus() view external  returns (Status){
        return status;
    } 

    // 设置枚举类型的状态变量 
    function set(Status _status) external {
        status = _status;
    }

    // 设置枚举类型指定为想要的状态 例:装载中
    function ship() external  {
        status = Status.Shipped;
    }

    // 设置状态恢复到默认值
    function reset() external {
        // 可以使用删除的方法,让枚举类型恢复成默认值 --- 注意这里的默认值是设置Status的第一个字段
        delete status;
    }


}

部署合约

 

存储位置

storage   状态变量
memory    局部变量,只会在内存中生效,即使修改了值,也不会改变状态变量
calldata   和memory比较类似,但是只能用在输入的参数中 如果在智能合约的参数中使用calldata会节省gas

在函数的传参中,如果是数组,和字符串类型 必须要标记一下是memory内存中还是calldata这样的状态
其实字符串类型也是一个数组是 bytes类型的数组

内存中的数组必须是定长数组
动态数组只能用在状态变量

给数组赋值必须要带上索引,而不能使用push

简单存储

calldata

待办事项列表

 

事件

contract E {
    event Log(string message);
    function foo() public virtual {
    	emit Log("E.foo");
    }
    function bar() public virtual {
    	emit Log("E.bar");
    }

}

继承

关键字 vritual  来定义一个函数是可以被重写的
关键字 override  证明他是覆盖掉父合约中的函数
vritual  override 可以同时在一个函数使用

子函数定义继承于A
contract  A {	
	function foo() public pure vritual rerutns(String memory){
			return "A";
		}
	
}

contract B is A {	
	function foo() public pure vritual override rerutns(String memory){
			return "B ";
		}
	
}

contract C is B {	
	function foo() public pure  override rerutns(String memory){
			return " C";
		}
	
}

多线继承

要遵循一个原则是从基类到派生之间的关系 是一个顺序关系,继承最少的合约放在最前面,哪个合约更接近于基础

     X
   / |
Y    |
  \  |
  	 z

contract  X {	
	function foo() public pure vritual rerutns(String memory){
			return "X";
		}
		
		function X() public pure  rerutns(String memory){
			return "X";
		}
	
}

contract Y is X {	
	function foo() public pure vritual override rerutns(String memory){
			return "Y";
		}
		function Y() public pure  rerutns(String memory){
			return "Y";
		}
	
}

contract Z is X,Y {	
	 // override(X,Y) 同时覆盖两个合约的相同的函数,这里面的X,Y顺序没有影响
		function foo() public pure override(X,Y) rerutns(String memory){
			return "Z";
		}
}

运行父级合约构造函数

继承的父级合约中有构造函数
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

// 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("string"),T("string") {
 //  这里的参数是继承的父级的参数

}

contract V is S,T {
//  这里的参数是由部署时候传进来的
	constructor(string memory _name,string memory _text) S(_name) T(_text) {
	}
}

contract VV is S("string"),T {
 //  一个构造函数是由继承的时候传进去的,一个是由部署的时候传进去的
constructor(string memory _text) T(_text) {}
}



// 1.S
// 2.T
// 3.V0
contract V0 is S,T {
//  这里的参数是由部署的时候传进来的
	constructor(string memory _name,string memory _text) T(_text) S(_name)  {
	}
} 

//      ======= 在这里运行的初始化顺序是会按照继承的顺序去初始化 ======
// 1.S
// 2.T
// 3.V1
contract V1 is S,T {
//  这里的参数是由部署的时候传进来的
	constructor(string memory _name,string memory _text) S(_name) T(_text) {
	}
} 

// 1.T
// 2.S
// 3.V2
contract V2 is T,S {
//  这里的参数是由部署的时候传进来的
	constructor(string memory _name,string memory _text) S(_name) T(_text) {
	}
} 

// 1.T
// 2.S
// 3.V3
contract V3 is  T,S {
//  这里的参数是由部署的时候传进来的
	constructor(string memory _name,string memory _text) T(_text) S(_name) {
	}
} 

调用父级合约函数

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

/*
调用父级合约函数
 - 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的关键词,会自动的寻找父级合约,然后调用父级合约的函数
    	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的关键词,会自动的寻找父级合约,然后调用父级合约的函数
    	super.bar();
    }
}


contract H is F,G {
	function foo() public  override(F,G){
    	F.foo(); // 两个合约的事件都会被触发
    }
    function bar() public  override(F,G){
    	super.bar();
    }
}

可视范围

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;


// visibility
// private-  私有 -- 只在合约内部可见
// internal - 内部 -- 是在合约的内部和被继承的子合约中都可以看得见
// public -  公开  在内部和被继承的和外部都可见
// externali- 外部  仅在函数外部调用的可见,在内部不可见

可以用在状态变量和函数中

A合约:
private pri()
internal inter()      C 合约在外部调用了A合约:
public pub()					pub()和ext()
external ext()

B is A:
inter()
pub()



小技巧 -- 浪费gas,没必要用
this.external  -- 意思是先去外部然后在调用外部函数
就可以拿到仅外部调用的函数

不可变量

关键词 immutable 设置成常量 节省gas
但是必须在部署合约的时候给他赋值,才能像常量一样工作

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

contract Immutable {
	
// 1、 address public owner = msg sender;

 // 2、
 	address public immutable owner;
 	
    constructor() {
    	owner = ms.sender;
    }
    
    uint public x;
    
    function foo() external {
    	require(msg.sender == owner);
    	x += 1;
    }
}

接口合约

Interface.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
// 新的接口合约 以interface开始 接口的名称习惯以I为开头来定义 Counter这个接口合约的名字
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();
    }
	
}


编译这个合约
Counter.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract Counter {
    uint public count;
    
    function inc() external {
		count += 1;
	}
    function dec() external {
    	count -=1;
	}
}

库合约

 避免重复代码的编写


创建一个数学的库合约  一般都是内部可视,或者想要外部调用的话设置成公开可视
library Math{
	// 寻找最大值的函数
	
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
library Math {
function max(uint x, uint y) internal pure returns (uint) {
	return x >= y ? x : y;
}
	
}

contract Test {
	function testMax(uint x, uint y) external pure returns (uint) {
	// 调用数学库中的函数
	return Math.max(x,y);
}

//  寻找数组中对应值
library ArrayLib {
	function find(uint[] storage arr, uint x) internal view returns (uint) {
	for (uint i =0;i < arr.length; i++) {
        if (arr[i] == x) {
        	return i;
     	}
	}
	revert("not found");
}

contract TestArray {
   // 相当于把库应用到了数组的类型中,这个类型中就拥有了这个库的所有函数功能
	using ArrayLib for uint[];  // 推荐这种
	
	uint[] public arr = [3,2,1];
	
	function testFind() external view returns (uint i) [
		  // return ArrayLib.find(arr,2);
		  return arr.find(2);
	}
}
    
利用using for指令

指令using A for B;可用于附加库函数(从库 A)到任何类型(B)。添加完指令后,库A中的函数会自动添加为B类型变量的成员,可以直接调用。注意:在调用的时候,这个变量会被当作第一个参数传递给函数:

低级调用Call

 

函数签名

是用来标识函数的唯一标识符,它由函数的**名称**和**参数类型**组成。例如,函数 foo(uint256 a, string memory b) 的签名是 foo(uint256,string)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值