solidity快速入门
从实战角度讲解solidity
--- 学无止尽,本篇文章会持续更新
在变量前使用public修饰等于自动帮他创建一个get函数
不可变类型
在 Solidity 中,constant
是一个修饰符,用于声明函数的状态。它指示函数不会修改或访问合约的状态。具体来说,constant
表示一个函数是只读的,不会修改合约中的数据。在变量前加一个constant表示变量不可被修改
string constant str001 = "hell";
//constant修饰的不可变量
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract Constants {
string public constant addr1 = "0x1"; // 定义常量不可更改
// string public immutable addr2 = "0x2"; //string无法使用immutable
address public constant addr3 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
function setAddr1() public {
// addr1 = "0x111"; // 这行代码会报错,因为 addr1 是 constant 的
}
}
//immutable修饰的不可变量
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract Immutable{
bytes32 public immutable STR;
address public address1 ;
// 合约部署时自动初始化的值 ,immutable只能在constructor初始化时才能修改/定义
constructor(){
STR = "hi";
address1 = msg.sender;
}
}
函数处理变量
在 Solidity 中,有几种不同的数据位置用于处理函数参数和变量。这些位置包括 memory
、storage
和 calldata
。
memory
:表示数据将被存储在临时内存中。这适用于函数内部创建的变量或函数参数,因为它们只在函数执行期间存在,并在函数执行后被清除。storage
:表示数据将被永久存储在区块链的状态中。这适用于合约的状态变量,它们在合约被部署后一直存在。calldata
:表示数据位于交易的输入数据中,它在函数调用期间被读取。这适用于函数的参数,特别是在从外部调用合约时,参数通常会出现在事务数据中。
在 Solidity 函数中,参数的数据位置默认情况下是 calldata
。但是对于字符串类型,必须显式指定数据位置为 memory
或 calldata
,以便在函数内部使用它们。
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract getandset{
string public name1 ;
function set(string memory _name) public{
name1 = _name;
}
function get()view public returns(string memory greeting){
return name1;
}
}
function get()view public returns(string memory a){
a = "11"; //直接复制returns的参数,不需要写return可以直接进行赋值返回
}
在有多个变量的时候可以更好的展示,用前缀greeting(自定义的),在获取实际数据的时候不会展示前缀,也可以理解为更清晰的让你看懂这个代码返回的参数是什么意思
function函数返回值
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Function {
// Functions can return multiple values.
function returnMany() public pure returns (uint, bool, uint) {
return (1, true, 2);
}
// Return values can be named.
function named() public pure returns (uint x, bool b, uint y) {
return (1, true, 2);
}
// Return values can be assigned to their name.
// In this case the return statement can be omitted.
function assigned() public pure returns (uint x, bool b, uint y) {
x = 1;
b = true;
y = 2;
}
// Use destructuring assignment when calling another
// function that returns multiple values.
function destructuringAssignments()
public
pure
returns (uint, bool, uint, uint, uint)
{
(uint i, bool b, uint j) = returnMany(); //调用上面的函数进行返回值传参
// Values can be left out.
(uint x, , uint y) = (4, 5, 6); //相当于x=4 y=6
return (i, b, j, x, y);
}
// Cannot use map for either input or output
// Can use array for input
function arrayInput(uint[] memory _arr) public {}
// Can use array for output
uint[] public arr;
function arrayOutput() public view returns (uint[] memory) {
return arr;
}
}
// Call function with key-value inputs
contract XYZ {
function someFuncWithManyInputs(
uint x,
uint y,
uint z,
address a,
bool b,
string memory c
) public pure returns (uint) {}
function callFunc() external pure returns (uint) {
return someFuncWithManyInputs(1, 2, 3, address(0), true, "c");
}
function callFuncWithKeyValue() external pure returns (uint) {
return
someFuncWithManyInputs({a: address(0), b: true, c: "c", x: 1, y: 2, z: 3});
}
}
modifier自定义修饰器
修饰符可以传参
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract modifiertest{
address public owner;
string public name;
constructor(){
owner = msg.sender;
name = "nbsb";
}
modifier OnlyOwner(){
require(msg.sender == owner,"you are not contract owner");
_; //要在require后面 继续下面的函数主体的意思
}
modifier NotGtZero(string memory _name){ //modifier可以接受参数
require(bytes(_name).length >0 , "invalid Name");
_;
}
modifier VaildAddress(address _address){
require(_address != address(0) , "invalid address"); //可以判断账户地址是否有效,需要存在才可以
_;
}
function setName(string memory _name) public OnlyOwner NotGtZero(_name){ //使用修饰器这样就会先判断调用者是否符合条件(是否是拥有者)/ 还可以判断name是否为0 使用了2个modifier
name = _name;
}
// // 等同于这样
// function setName(string memory _name) public OnlyOwner NotGtZero(_name){
// require(msg.sender == owner,"you are not contract owner");
// require(bytes(_name).length >0 , "invalid Name");
// name = _name;
// }
function getName() public view returns(string memory) {
return name;
}
}
函数修饰符可以在函数主题之前条用 也可以在函数主题之后甚至中间被调用
//例如这样可以防止重注攻击
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
pragma solidity ^0.8.17;
contract ExampleContract {
address public owner;
uint public balance;
bool public locked;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function withdraw(uint _amount) public onlyOwner noReentrancy {
require(balance >= _amount, "Insufficient balance");
// 假设在取款时,需要执行一些复杂的逻辑或调用其他合约。
// 在这个例子中,我们调用了一个名为`reentrantFunction`的函数,这个函数可能会尝试重入`withdraw`函数。
// 重入比如就是 在调用一次 withdraw 函数
// 正常的取款逻辑
balance -= _amount;
// 调用可能尝试重入的函数
reentrantFunction(_amount);
}
function reentrantFunction(uint _amount) private {
require(balance >= _amount, "Insufficient balance");
// 在这里进行重入攻击的尝试
// 假设攻击者尝试进行转账操作,从该合约再次调用`withdraw`函数
// 没有使用修饰器的情况下,重入攻击可能会导致合约的余额被重复转出。
// 使用了`noReentrancy`修饰器后,重入攻击会被检测并阻止。
// 注意:此处为了演示目的,没有进行实际的转账操作。
// 实际应用中,应该根据业务逻辑来处理资金转移。
balance -= _amount;
}
}
修饰器noReentrancy
用来防止重入攻击。它通过一个名为lock
的布尔变量来管理函数的执行权限。当一个函数被调用时,首先要检查lock
变量的值,如果为false
,则将lock
设置为true
,表示函数正在执行。然后执行函数的代码(通过_
占位符),最后将lock
设置为false
,表示函数执行完成。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract modifiertest{
bool public locked;
int public x;
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function decrement(int i) public noReentrancy { //因为加入了noReentranct
x -= i;
if (i > 1) {
decrement(i - 1); //这里重新调用此函数的时候会抛出错误
}
}
}
Events 进行记录
可以进行监听事件
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Event{
//使用 indexed 关键字可以为事件参数创建索引,从而提高事件过滤和搜索的效率
//只能通过列入js等进行过滤索引
event Log(address indexed sender , string message);
event AnotherLog();
function test() public{
emit Log(msg.sender,"hhh");
emit Log(msg.sender,"nihao");
emit Log(msg.sender,"hello world");
emit Log(msg.sender,"nbsb is me");
emit AnotherLog(); //只触发了这个事件,但是并没有传递任何参数
}
}
pure和view的相同点和不同点
pure
和 view
都是 Solidity 中的函数修饰符,用于声明一个函数不会修改状态。它们之间的共同点和不同点如下:
共同点:
- 都用于标识函数不会对区块链状态产生改变。
- 都仅能读取数据而不能修改数据。
不同点:
pure
修饰的函数不能读取或修改除函数参数以外的任何数据。它是用于执行纯粹的计算,不涉及任何外部信息的函数。例如,计算两个数的乘积而不涉及合约状态或外部调用的函数,可以使用pure
修饰。view
修饰的函数可以读取合约内的数据,但不能修改任何状态。它用于函数在执行期间只读取数据的情况。例如,读取合约的状态变量或调用其他view
函数的函数,可以使用view
修饰。
在选择使用哪个修饰符时,根据你的需求来确定:
如果函数内部只执行计算操作而不涉及合约状态和其他外部信息,可选用 pure
,这样会使函数更明确且限制了函数的行为范围。
如果函数仅读取数据而不修改状态,而且可能需要访问合约内的状态变量或调用其他 view
函数,可选用 view
。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract ifElse {
//不涉及合约状态和其他外部信息,直接使用pure
function iforno(uint x) public pure returns(string memory) {
if(x < 10){
return "lt 10";
} else if(x < 20){
return "lt 20";
} else {
return "gt 20";
}
}
function ternary(uint _x) public pure returns(string memory){
return _x >20 ? "gt 20" :"lt 20"; //三元运算符
}
}
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract getandset{
string public name1 ;
function set(string memory _name) public{
name1 = _name;
}
//设计合约状态和需要用合约中的参数可以用view
function get()view public returns(string memory greeting,int numberNB){
return (name1,1);
}
}
mapping
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract mpas {
mapping(int=>address) public mapss ;
string public aa ;
function addmap(int x,string memory y) public {
mapss[x] = msg.sender;
aa = y;
}
function get(int x2) public view returns(address){
return mapss[x2];
}
}
Array
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Array013{
uint[] public arr1 = [1,2,3,4,5]; //动态数组初始长度
uint[10] public arr2 ; //固定长度默认每个元素都是0 / 不支持push
function getArray1() view public returns(uint[] memory) {
return arr1;
}
function getArray2() view public returns(uint[10] memory){
return arr2;
}
function pushArr1(uint x) public {
arr1.push(x);
}
function pushArr2(uint x , uint y ) public {
arr2[x] = y;
}
function popArray1() public {
arr1.pop(); //删除最后一个数字
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Array013{
uint[] public arr1 = [1,2,3,4,5]; //动态数组初始长度
uint[10] public arr2 ; //固定长度默认每个元素都是0 / 不支持push
function getArray1() view public returns(uint[] memory) {
return arr1;
}
function getArray2() view public returns(uint[10] memory){
return arr2;
}
function pushArr1(uint x) public {
arr1.push(x);
}
function pushArr2(uint x , uint y ) public {
arr2[x] = y;
}
function popArray1() public {
arr1.pop(); //删除最后一个数字
}
function getArray1length() public view returns(uint){
return arr1.length; //返回长度
}
function deleteArray1(uint index ) public {
delete arr1[index]; //并不是删除,是将这个索引的值转为初始值也就是0(使用pop可以删除最后一个元素)
}
function examples() external{
uint[] memory a = new uint[](5); //创建一个只有5个数组的array
}
}
删除数组中的指定索引的元素,实际上就是替换数组指定索引依次替换为后一位,然后使用pop将最后一个元素进行删除,即可完成替换
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract removeArray{
// [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]
// [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
// [1] -- remove(0) --> [1] --> []
uint[] public arr= [1,2,3,4,5,6,7,8];
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 remove001(uint _index) public {
require(_index < arr.length, "index out of bound");
arr[_index] = arr[arr.length -1];
arr.pop();
}
function getArray() view public returns(uint[] memory){
return arr;
}
}
枚举体Enum
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Enum{
enum Status{Pening,shiipped,nbsb}
Status public status;
function get() public view returns(Status){
return status; //获取的是这个枚举体的索引
}
function getStatus() public view returns (string memory) {
if (status == Status.Pening) {
return "Pening";
} else if (status == Status.shiipped) {
return "shiipped";
} else if (status == Status.nbsb) {
return "nbsb";
} else {
revert("Invalid status");
}
}
function set(Status _status) public { //就算设置也只能设置上面枚举的索引,不然会报错
status = _status;
}
function setPening() public {
status = Status.Pening;//设置这个枚举具体数的索引(这个函数表示为设置为Pening的索引)
}
// delete resets the enum to its first value, 0
function reset() public {
delete status; //将这个值还原
}
}
这个也是可以直接将枚举体放到一个文件中,在另一个文件种引用这个枚举体enum的文件夹
结构体struct
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Struct{
struct Person{
string name;
int age;
int id;
}
Person[] public persons;
function add(string memory _name, int _age, int _id) public{
require(bytes(_name).length > 0 ,"invild _name");
require(_age != 0 ,"invild age");
require(_id != 0 ,"invild id");
Person memory people = Person(_name,_age,_id);
// Person memory people = Person({name:_name,id:_id,age:_age}); //使用参数传入,不需要位置一一对应就可以进行传参
//还有一个方法就是新建一个Person的对象然后将对象的每个值分别复制后在push到这里,推荐直接使用第一种方法简洁明了
persons.push(people);
}
function getPersonName(uint _index) view public returns(string memory){
require(_index < persons.length ,"please input lt persons index");
return persons[_index].name ;
}
function getPersonN(uint _index) view public returns(Person memory){
require(_index < persons.length ,"please input lt persons index");
return persons[_index] ;
}
//修改这个值
function updata(uint x , string memory _name) public {
persons[x].name = _name;
}
}
这个也是可以直接将结构体放到一个文件中,在另一个文件种引用这个结构体struct的文件夹
修饰符
pure
:该修饰符用于声明函数不会读取或修改合约的状态,并且不会调用其他的非pure
或非view
函数。这意味着pure
函数不会有副作用,只返回根据输入参数计算得出的结果。使用pure
修饰符可以让编译器优化函数的执行。const
:该修饰符是pure
的同义词,在Solidity 0.6.0之前的版本中使用。在Solidity 0.6.0及更高版本中,建议使用pure
而不是const
。view
:该修饰符用于声明函数只读取合约的状态,而不会修改合约的状态。view
函数可以调用其他的view
和pure
函数,但不可以调用其他类型的函数,包括non-payable
和payable
函数。与pure
函数类似,view
函数也可以让编译器进行优化。
在Solidity中,可以使用pure
修饰符来声明函数时不会访问或修改合约状态的纯函数。纯函数只依赖于输入参数,并根据输入参数计算并返回结果,对合约状态没有任何影响。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract MathOperations {
function add(uint a, uint b) public pure returns (uint) {
return a + b;
}
function multiply(uint a, uint b) public pure returns (uint) {
return a * b;
}
function exponentiation(uint base, uint exponent) public pure returns (uint) {
return base ** exponent;
}
}
将函数映射到一个foods中,用的时候可以直接实例化出来
mapping (uint256 => address) foods;
FoodInfoItem food = new FoodInfoItem(name, traceName, quality, msg.sender);
foods[traceNumber] = food;
FoodInfoItem(foods[traceNumber]) //可以直接映射出来使用
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.18;
contract Variablez{
//使用type函数获取变量最大值最小值
int32 public a = type(int32).max;
function dosomthing() public view returns(uint) {
uint number = 99;
uint time = block.timestamp;
address sender = msg.sender;
return time;
}
}
合约继承
想要在子合约中继承可以使用 is 进行继承
重写父合约方法可以在子合约函数上用override修饰符同时父合约要加 virtual修饰符
//子合约中
function dosomthing() public pure override returns(uint) {
uint number = 88;
return number;
}
//父合约中
function dosomthing() public pure virtual returns(uint) {
uint number = 99;
return number;
}
余额转移
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract DappTest{
uint public aa ;
uint public bb = 100;
function found() payable public{
require(msg.value > 100 wei,"not great than 100wei");
}
function withdraw() public{
aa = address(this).balance ;
bb = 100;
//取钱三种方法: send transfer call //send和transfer显示2300gas call不限制
// msg.sender 是接收方地址 address(this)是合约地址
// payable(msg.sender).transfer(address(this).balance);
// bool senSuccess = payable(msg.sender).send(address(this).balance);
// require(senSuccess,"send failed");
// 这个bytes直接设置为空的,所以可以直接写不接受也可以
(bool callSuccess ,bytes memory a) = payable(msg.sender).call{value:aa}("");
require(callSuccess,"failed");
}
}
library
using的前提是需要直接或者间接的导入某个library.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
// 定义一个库合约
library MathUtils {
function add(uint a, uint b) public pure returns (uint) {
return a + b;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "./useing1.sol";
// 使用库合约
contract MyContract {
// 引入库合约
using MathUtils for uint; //只要是uint类型的都可以调用MathUtils中的函数
function myFunction(uint a, uint b) public pure returns (uint) {
// 调用库合约中的函数
return a.add(b);
}
}
这样说更容易理解: using A for B,这里A通常是某个library,B是某种数据类型,这句话是把A中的方法绑定到B类型上,相当于给B类型附加了A库中的方法。(也有翻译为附着库的) 比如 using SafeMath for uint256,SafeMath是库,for uint256,表示,SafeMath库中的方法绑定到uint256这个类型,那么uint256类型的实例可以直接调用SafeMath库中的所有方法; 然后库SafeMath里面有定义一个add(uint256 a,uint256 b)方法。 我们有一个uint256 c;那么可以这样调用c.add(d),执行结果相当于c+d, add方法在定义的时候,第一个参数会是一个uint256类型的变量,表示调用者。
实战用例:(将类型赋值为library中的struct变量,这样就可以只能使用这个赋值的变量进行调用了)
为什么这里使用的storage而不使用memory?
使用 storage
主要是因为需要修改存储位置上的数据或读取最新的存储位置。当需要直接访问或修改合约的状态变量时,就需要使用 storage
。而使用 memory
是为了临时存储和操作数据,在函数执行结束后会被清除。
uint[0]和uint[](0)的区别
uint[0]
和 uint[](0)
在 Solidity 中有不同的含义:
uint[0]
:表示一个固定大小为 0 的无符号整数数组。这个数组没有任何元素,因此它没有分配任何存储空间。这种数组在编译时大小就确定了,不能增加或减少元素。uint[](0)
:表示一个动态大小的无符号整数数组,并且该数组没有任何元素。这个数组是通过动态分配的,在运行时可以根据需要增加或减少元素。
总结来说,uint[0]
是固定大小的数组,且大小为 0,而 uint[](0)
是动态大小的数组,且初始时没有元素。
在实际使用中,通常使用 uint[]
表示动态大小的数组,而不是 uint[0]
。因为 uint[0]
没有任何实际存储空间,而动态数组可以根据需要进行大小调整。
uint[](0)创建有0个参数的动态数组
在直接对struct结构体进行实例化时,uint[](0)需要使用new进行创建,否则会报错Explicit type conversion not allowed from "int_const 0" to "uint256[] memory".
Company memory newCompany = Company(name,companyAddress,uint(0),new uint[](0),new uint[](0) );
在solidity8版本之前声明uint[0]会直接报错
keccak256函数比较空字符串
keccak256()
函数可以传递可变数量的参数,你可以将任意数量的值作为参数传递给该函数。这些值将在进行哈希计算之前被编码和混合在一起。例如,你可以使用以下方式传递多个参数给 keccak256()
函数:keccak256(value1, value2, value3, ...)
在函数内部,所有传递的参数将会被编码和混合,然后进行 Keccak-256 哈希计算,最终返回一个 256 位的哈希值。需要注意的是,传递给 keccak256()
函数的参数的类型必须是 Solidity 支持的合法类型,例如整数、字符串、字节数组等。如果传递的参数类型不正确,编译器将会报错。
这段代码使用了 Solidity 中的 keccak256 函数来比较 bank.bankName
是否等于一个空字符串。
在 Solidity 中,keccak256
是一个哈希函数,它会接收一个字节数组作为参数,并返回该字节数组的 keccak256 哈希值。
在这段代码中,keccak256(bank.bankName)
和 keccak256("")
分别计算了 bank.bankName
和空字符串的 keccak256 哈希值。然后使用 ==
运算符来比较这两个哈希值是否相等。
如果 bank.bankName
和空字符串的 keccak256 哈希值相等,说明 bank.bankName
是一个空字符串,这表示银行的名称没有被设置。在这种情况下,代码返回 404001。否则,返回值将取决于代码的其他部分,这段代码没有提供更多的信息。
需要注意的是,keccak256 是一种哈希算法,它确保即使输入只有微小的变化,哈希值都会有显著的不同。这使得 keccak256 适用于许多加密和完整性校验的应用场景。
if (keccak256(name) == keccak256(""))
{ return 404001; }
引入外部合约并实例化进行调用他的方法
调用的地址可以是合约地址
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0 <=0.8.20;
pragma experimental ABIEncoderV2;
import "./test88.sol";
contract test888{
address public tt;
address public testadd;
function test() public returns(address){
test88 adddr = new test88();
tt = address(adddr);
testadd = adddr.get();
return testadd;
}
// test88 adddr = (new test88){value:msg.value}(nbsb,1123); //如果有需要msg.value值的话就需要使用这样方法示例创建,constructor中需要传入两个值进行创建的话
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0 <=0.8.20;
pragma experimental ABIEncoderV2;
contract test88{
address public owner;
constructor(){
owner = msg.sender;
}
function get() view returns(address){
return owner; //返回的是上面的那个合约 的合约地址
}
}
可以进行限制这个合约只能在那个合约中使用
require(_owner == msg.sender, "only trace contract can invoke");
//必须是来自trace合约的地址调用的才可以,在trace函数中,使用new实例化这个内容在进行调用,则的调用地址就是trace合约的地址
接口和抽象合约有什么不同?
interface abstract
pragma solidity ^0.6.0;
//请在此处定义接口:
interface Calculator{
function getResult() external view returns(uint);
}
contract Test is Calculator{
//请在下方一行写出合约继承接口的语句:
//请在下方一行写出继承的函数定义及其内容:
function getResult() external view returns(uint){
uint a = 1;
uint b = 2;
uint result = a+b;
return result;
}
pragma solidity ^0.6.0;
//请在下方定义抽象合约:
abstract contract Calculator{
function getResult() public view virtual returns(uint); //使用virtual注解意思是可以进行重写
}
//请在下方一行定义继承合约的语句:
contract Test is Calculator{
//请在下方一行写出定义函数的语句:
function getResult() public view override returns(uint)
{
uint a = 1;
uint b = 2;
uint result = a + b;
return result;
}
}
solidity中的错误处理
pragma solidity ^0.6.0;
contract Account {
uint public balance;
//下方一行没有实际用途,仅作为提示:整形变量的最大数值,超过就会上溢。
uint public constant MAX_UINT = 2 ** 256 - 1;
function deposit(uint _amount) public {
uint oldBalance = balance;
uint newBalance = balance + _amount;
// 请在下方一行写出第一个 require 语句:
require(newBalance>= oldBalance,"上溢了哦");
balance = newBalance;
// 请在下方一行写出第一个 assert 语句:
assert(balance>=oldBalance);
}
function withdraw(uint _amount) public {
uint oldBalance = balance;
// 请在下方一行写出第二个 require 语句:
require(balance>=_amount,"下溢了哦");
// 请在下方写出 if 条件语句:
if(balance< _amount){
revert("下溢了哦");
}
balance -= _amount;
// 请在下方一行写出第二个 assert 语句:
assert(balance<=oldBalance);
}
}
委托调用
委托这个函数代码进行调用,但是实际更新的是自己函数的值,也是代码复用的一种
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract TestDelegateCall {
uint public num;
address public sender;
uint public value;
function setVars(uint _num) external payable{
num = _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{
(bool success , bytes memory data ) = _test.delegatecall(abi.encodeWithSelector(TestDelegateCall.setVars.selector,_num));
require(success,"fail ");
}
}
solidityTable的使用
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: MIT
pragma solidity ^0.4.25;
pragma experimental ABIEncoderV2;
import "./Table.sol";
contract Test3 {
TableFactory tf;
string constant TABLE_NAME = "tb_user33";
// uint count ;
constructor() {
tf = TableFactory(0x1001);
tf.createTable(TABLE_NAME, "id", "username,password");
}
function insertTest(string memory _id,string memory _username,string memory _password) public returns(int) {
Table table = tf.openTable(TABLE_NAME);
//Entry类似于映射实体类对象
Entry entry = table.newEntry(); // Entry 相当于设置了一个类似于java中的bean
// count +=1;
entry.set("id",_id);
entry.set("username", _username);
entry.set("password", _password);
int res = table.insert(_id, entry);
return res;
}
function selectById(string memory _id,string memory _username) public view returns(string memory,string memory){
Table table = tf.openTable(TABLE_NAME);
Condition condition = table.newCondition(); // Condition 就相当于mybatis plus中的wapper,用于设置查询条件
condition.EQ("username",_username); // 将 _id 参数传递给条件对象
Entries entries = table.select(_id, condition); //Entries获取到的是一个数据集,获取数据集中第一个即可得到这个映射的数据
// 检查是否有匹配的记录
Entry entry = entries.get(0);
string memory user_name = entry.getString("username");
string memory password_ = entry.getString("password");
return (user_name , password_);
}
function selectAll(string memory _id) public view returns(string[] memory, string[] memory, string[] memory){
Table table = tf.openTable(TABLE_NAME);
Condition condition = table.newCondition();
Entries entries = table.select(_id,condition);
string[] memory ids = new string[](uint256(entries.size()));
string[] memory usernames = new string[](uint256(entries.size()));
string[] memory passwords = new string[](uint256(entries.size()));
for(int256 i = 0; i < entries.size(); ++i){
Entry entry = entries.get(i);
ids[uint256(i)] = entry.getString("id");
usernames[uint256(i)] = entry.getString("username");
passwords[uint256(i)] = entry.getString("password");
}
return (ids , usernames , passwords);
}
function update(string memory _id , string memory _username , string memory _newUsername) public returns(int256){
Table table = tf.openTable(TABLE_NAME);
Entry entry = table.newEntry();
entry.set("username",_newUsername);
Condition condition = table.newCondition();
condition.EQ("username",_username);
int256 count = table.update(_id, entry, condition); //返回修改的行数
return count;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.4.25;
contract TableFactory {
/**
* brief 打开表,返回Table合约地址
* param tableName 表的名称
* return 返回Table的地址,当表不存在时,将会返回空地址即address(0x0)
*/
function openTable(string) public constant returns (Table); // 打开表
/**
* brief 创建表,返回是否成功
* param tableName 表的名称
* param key 表的主键名
* param valueFields 表的字段名,多个字段名以英文逗号分隔
* return 返回错误码,成功为0,错误则为负数
*/
function createTable(string tableName,string key,string valueFields) public returns(int);
}
// 查询条件
contract Condition {
//等于
function EQ(string, int) public;
function EQ(string, string) public;
//不等于
function NE(string, int) public;
function NE(string, string) public;
//大于
function GT(string, int) public;
//大于或等于
function GE(string, int) public;
//小于
function LT(string, int) public;
//小于或等于
function LE(string, int) public;
//限制返回记录条数
function limit(int) public;
function limit(int, int) public;
}
// 单条数据记录
contract Entry {
function getInt(string) public constant returns(int);
function getAddress(string) public constant returns(address);
function getBytes64(string) public constant returns(byte[64]);
function getBytes32(string) public constant returns(bytes32);
function getString(string) public constant returns(string);
function set(string, int) public;
function set(string, string) public;
function set(string, address) public;
}
// 数据记录集
contract Entries {
function get(int) public constant returns(Entry);
function size() public constant returns(int);
}
// Table主类
contract Table {
/**
* brief 查询接口
* param key 查询主键值
* param cond 查询条件
* return Entries合约地址,合约地址一定存在
*/
function select(string key, Condition cond) public constant returns(Entries);
/**
* brief 插入接口
* param key 插入主键值
* param entry 插入字段值
* return 插入影响的行数
*/
function insert(string key, Entry entry) public returns(int);
/**
* brief 更新接口
* param key 更新主键值
* param entry 更新字段值
*param cond 更新条件
* return 更新影响的行数
*/
function update(string key, Entry entry, Condition cond) public returns(int);
/**
* brief 删除接口
* param key 删除的主键值
* param cond 删除条件
* return 删除影响的行数
*/
function remove(string key, Condition cond) public returns(int);
function newEntry() public constant returns(Entry);
function newCondition() public constant returns(Condition);
}
版权属于:戏人看戏博客网
本文链接:https://day.nb.sb/archives/1186.html
若无注明均为戏人看戏原创,转载请注明出处,感谢您的支持!
solidity so easy
123