关于solidity知识总结

借此CSDN博客笔记来巩固一下对solidity知识的认识

1.helloworld

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {string public hello = "HelloWorld";
}

public 修饰符自动创建一个get()函数用于返回内容

类似于

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {string hello = "HelloWorld";function getHello() public view returns(string memory) {return hello;}
}

view:函数里的功能只查看了状态变量或者全局变量,不改变状态变量的内容,用view修饰。调用view修饰的函数,不会消耗gas,只有消耗gas的函数调用view修饰的函数,才会消耗gas。

memory:数据位置的修饰符,修饰数组,映射,字节,于calldata的区别是 memory 不能修改变量的内容,而calldata可以

returns:声明返回的数据类型,可以返回多个。

2.第一个应用程序

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {uint256 count;function get() public view returns(uint256) {return count;}function inc() public {count += 1;}function dec() public {count -= 1;}
}

功能描述:用于加减状态变量count

细节:0.8.0以下的版本减到零之后就会返回最大值,也就是常说的整数溢出问题 使用safemath库可以解决或者更换到0.8.0以上的版本。

状态变量:状态变量就是具体要上链的数据

3.数据类型

boolean

有两个值 true和false 默认为false

uint

无符号的整型,最大值为uint256,默认为0

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {uint256 i;function getMax() public pure returns(uint256) {return type(uint256).max;}function getMin() public pure returns(uint256) {return type(uint256).min;}
}

可以用type()函数查看最大值和最小值

pure:通常用pure修饰符的函数是工具类

int

有符号的整型,最大值为int256,默认为0

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {int256 i;function getMax() public pure returns(int256) {return type(int256).max;}function getMin() public pure returns(int256) {return type(int256).min;}
}

 也可以用type()函数查看最大值和最小值

address

地址类型,是solidity的特有的数据类型,以32位的十六进制表示,用于接受或发送以太币,默认为0x0000000000000000000000000000000000000000

4. 变量

solidity的变量有三种分别是:局部变量(local)、状态变量(state)、全局变量(global)。可以理解为solidity的状态变量是传统编程语言的全局变量,solidity的全局变量是提供有关区块链的信息的函数,比如(block.timestamp用于获取当前的时间戳、msg.sender获取当前账户的地址)

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {// 状态变量string text = "Hello";uint256 num = 123;function doSomething() public view returns(uint256,uint256,address){// 局部变量uint256 i = 456;// 全局变量uint256 timestamp = block.timestamp;address sender = msg.sender;return (i,timestamp,sender);}
}

5. 常量

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {address constant MY_ADDRESS = 0x0000000000000000000000000000000000000000;function get() public pure returns(address) {return MY_ADDRESS;}
}

这里解释一下 使用函数返回常量的值为什么使用pure修饰而不是view

因为constant修饰的是常量,常量的值是硬编码的,而pure修饰的函数就通常返回一些数学运算或者硬编码的值,view修饰的函数多用于查看状态变量或者全局变量。真因为常量的值是硬编码,所以可以节省gas费用。

6.不可变的

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {address immutable MY_ADDRESS;constructor() {MY_ADDRESS = msg.sender;}function get() public view returns(address) {return MY_ADDRESS;}
}

和常量很像,使用immutable修饰不可修改,多用于在部署合约时确定合约账户地址。

在定义常量和不可改变量时的标识符通常用大写来表示

7. 读取和改变状态变量

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {uint256 num;function set(uint256 _num) public {num = _num;}function get() public view returns(uint256) {return num;}
}

和我们第一个应用程序很像,读取变态变量不消耗gas,修改状态变量消耗gas

通常函数里的形参名称开头用_开头

8. ether与wei

ether和wei是以太坊用于交易的单位

1 ether = 1 * 10的18 次方 wei

在solidity中定义的uint或int默认的单位都是Wei

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {uint256 oneWei = 1 wei;uint256 oneEther = 1 ether; function fun1() public view returns(bool) {bool isOneWei = oneWei == 1;return isOneWei;// 返回true}function fun2() public view returns(bool) {bool isOneEther = oneEther == 1e18;return isOneEther;// 返回true}
}

9. Gas

在以太坊网络上进行交易,都需要支付一定的gas费用,举例子说明一下

设置Gas Limit。假设小明在以太坊网络上要部署合约,就是相当于一次交易。在部署合约时需要设置一个交易的Gas Limit这是小明愿意为这个交易使用的最大Gas量。假设设置的Gas Limit为300000Gas

设置Gas价格。接下来,小明需要设置一个Gas价格,这表示他愿意为每单位Gas支付的以太币数量。假设设置的Gas价格是50Gwei

计算交易费用。当小明的交易被执行时,实际上可能并不需要消耗全部的300000Gas,假设部署合约这个交易消耗了80000Gas。那么小明实际需要支付的Gas费用是:"消耗的Gas * Gas价格 = 80000Gas * 50Gwei = 4000000Gwei或0.004ETH" 。

未消耗的Gas退还。由于小明设置的Gas Limit是300000,但实际消耗了80000,剩余的220000Gas会回滚。

区块的Gas Limit。区块的Gas Limit是由以太坊网络决定的,限制一个区块内所有交易可以消耗的总Gas量。假设当前的区块Gas Limit是15,000,000 Gas,这意味着所有包含在这个区块中的交易,它们消耗的Gas总和不能超过这个限制。如果一个单独的交易超出区块的Gas Limit,这意味着当前交易太大,不能被任何矿工在单个区块中处理,所以矿工会忽略这样的交易。直到它被发送方取消或替换,或者网络的条件改变(例如区块的Gas Limit增加)。即便交易的Gas消耗没有超过区块的Gas Limit,但如果区块内已经包含的其他交易的Gas总和加上这个交易的Gas超过了区块的Gas Limit,那么这个交易将不会被包含在当前区块中。它会留在交易池中,等待下一个或未来某个区块有足够的空间。

10.if else

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract First {function fun(uint256 _num) public pure returns(uint256) {if(_num < 10) {return 0;} else if (_num < 20) {return 1;} else {return 2;}}function fun1(uint256 _x) public pure returns(uint256) {// if(_x < 10) {//     return 1;// } else {//     return 2;// }return _x < 10 ? 1 : 2; // 相当于上段代码}
}

solidity支持if,else if,else的写法,不过在区块链中进行判断多用于require(),因为可以进行回滚操作

11. for、while、dowhile

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;contract First {// 100以内的偶数和function fun() public pure returns (uint256) {uint256 sum = 0;for (uint256 i = 0; i <= 100; i++) {if (i % 2 == 0) {sum += i;}}return sum;}// 100以内的奇数和function fun1() public pure returns (uint256) {uint256 sum = 0;uint256 i = 0;while (i <= 100) {if (i % 2 != 0) {sum += i;}i++;}return sum;}// 100以内的数字之和function fun2() public pure returns(uint256) {uint256 sum = 0;uint256 i = 0;do {sum += i;i++;} while(i <= 100);return sum;}
}

同样solidity支持for、while、dowhile的写法。这个例子也很好的诠释了pure修饰符的用处

12. mapping

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;contract First {mapping(address => uint256) myMap;mapping(address => mapping(uint256 => bool)) nested;function get(address _addr) public view returns(uint256) {return myMap[_addr];}function set(address _addr,uint256 _i) public {myMap[_addr] = _i;}function remove(address _addr) public {delete myMap[_addr];}function get1(address _addr,uint256 _i) public view returns(bool) {return nested[_addr][_i];}function set1(address _addr,uint256 _i,bool _boo) public{nested[_addr][_i] = _boo;}function remove1(address _addr,uint256 _i) public {delete nested[_addr][_i];}}

mapping映射的key可以是任何值类型、字节、字符串、或者合约类型

                        value可以是任何类型,包括另一个映射或数组

13.数组

solidity的数组分为动态数组、静态数组和内存数组。

动态数组

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract Second {// 动态数组uint256[] arr;// 添加数组元素function push(uint256 _num) public {arr.push(_num);}// 根据索引获取数组元素function getIndexArr(uint256 _i) public view returns(uint256) {return arr[_i];}// 获取数组function getArr() public view returns(uint256[] memory) {return arr;}// 获取数组的长度function getArrLength() public view returns(uint256) {return arr.length;}// 删除数组中最后一个元素,会使数组的长度 -1function pop() public {arr.pop();}// 根据索引重置元素为默认值,不会删除数组的长度function remove(uint256 index) public {delete arr[index];}}

动态数组是solidity中常用的形式

静态数组

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;contract Second {// 静态数组uint256[10] arr = [1, 2, 3, 4, 5];// 根据索引获取数组元素function getIndexArr(uint256 _i) public view returns (uint256) {return arr[_i];}// 获取数组function getArr() public view returns (uint256[10] memory) {return arr;}// 获取数组的长度function getArrLength() public view returns (uint256) {return arr.length;}// 静态数组添加元素function addArr() public {for (uint256 i = 0; i < 5; i++) {arr[i + 5] = i;}}
}

在静态数组中,不可以使用push pop函数等

内存数组

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;contract Second {function fun() external pure returns(uint256[] memory){uint256[] memory arr = new uint256[](5);return arr;}
}

这里的external代表着只能从合约外部调用,不能被合约外部的其他函数调用

小案例

我们使用solidity想要删除数组,无非是使用 delete 和pop函数,可它们的缺点也很明显,delete函数只能根据索引重置数组内容为默认值,pop函数只能删除数组中的最后一个。但是我们想要的效果是根据索引删除元素,该如何实现呢?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract Frist {uint256[] arr;function remove(uint256 index) public {for(uint256 i = index;i< arr.length - 1;i++) {arr[i] = arr[i + 1];// 将想要被删除的数替换为想要被删除的数之后的一位}arr.pop();}function getArr() public view returns(uint256[] memory) {return arr;}function test() public {arr = [1, 2, 3, 4, 5, 6];remove(1);// [1, 3, 4, 5, 6]assert(arr.length == 5);assert(arr[0] == 1);assert(arr[1] == 3);assert(arr[2] == 4);assert(arr[3] == 5);assert(arr[4] == 6);}
}

这里为了测试用到了断言语句,assert如果值相等才会往下执行

14. 枚举

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract Frist {enum Status {Pending,Shipped,Accepted,Rejected,Canceled}Status status;function get() public view returns(Status) {return status;// 默认值为枚举类型的第一个值}function set(Status _status) public {status = _status;}function cancel() public {status = Status.Canceled;}function reset() public {delete status;// 重置枚举类型为默认值}
}

15. 结构体

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract Frist {struct Todo {string text;bool completed;}Todo[] todos;// 结构体数组function create(string calldata _text,bool _completed) public{// 添加结构体数组的三种方式// 第一种todos.push(Todo(_text,_completed));// 第二种/**todos.push(Todo({text: _text,completed: _completed}));**/// 第三种/**Todo memory todo;todo.text = _text;todo.completed = _completed;todos.push(todo);**/}// 根据数组索引找到对应的结构体数据function get(uint256 _index) public view returns(string memory text,bool completed) {Todo memory todo = todos[_index];return (todo.text,todo.completed);}// 返回整个结构体数组function getTodos() public view returns(Todo[] memory) {return todos;}// 更新结构体数据function update(uint256 _index,string memory _text) public {Todo storage todo = todos[_index];todo.text = _text;todo.completed = !todo.completed;}
}

结构体常常和数组结合用来存储某一类的数据。

我们常说区块链是透明,不可更改等等,都只是狭义上的理解,这些概念都需要一个精确的解释。我们知道在solidity中状态变量就相当于上链的数据。为什么在这段代码里就可以修改状态变量status里的值呢?区块链不可更改就意味着一旦数据被记录到区块链上,就无法删除或篡改,每一个新区块都包含前一个区块的哈希值,在逻辑上形成了链。如果尝试更改链中的任何信息,都会使后续所有的区块哈希值无效。然而,不可更改并不意味着区块链上的应用状态不能变化。在solidity中状态变量的值可以随着新的交易执行而改变,创建一个新的区块,更新的状态也就在记录在链上了。所以说区块链上的记录是一个不可更改的操作历史,但数据的当前状态是可以通过智能合约的逻辑来改变。这个例子也很好的证明了区块链是公开透明的,因为每次状态的改变都是可查看、可验证的。

16.  数据存储位置

数据存储位置有Storage,Memory,Calldata

Storage

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Frist {struct MyStruct {uint256 num;string name;}mapping(uint256 => MyStruct) myStructs;function getMyStruct1() public view returns(MyStruct memory) {return myStructs[0];}function fun1() public {MyStruct storage myStruct = myStructs[0];myStruct.num = 666;myStruct.name = "Hello";}
}

让我们来解释一下这段代码的意思。首先定义了一个MyStruct结构体和map映射。在函数fun1()里,定义了一个MyStruct实例,使用storage修饰,并引用了map键为0的映射。这样在修改MyStruct实例的时候,实际会改变状态变量myStruct键为0的映射。getMyStruct1()是为了测试查看map映射的结果

Memory

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Frist {struct MyStruct {uint256 num;string name;}function fun1() public pure returns(MyStruct memory){MyStruct memory myStruct = MyStruct({num: 666,name:"hello" });return myStruct;}
}

memory声明的MyStruct实例,只有在函数运行期间有效,是一个临时的存储。

特别说明一下当结构体里的数据只有一个参数时,可以使用以下代码来初始化一个结构体

struct MyStruct {uint256 num;}
MyStruct memory myMemStruct = MyStruct(0);

Calldata

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract First {struct MyStruct {uint256 num;string name;}function fun1(MyStruct calldata myStruct) external pure returns(MyStruct calldata){return myStruct;}
}

calldata修饰的变量,是只读的,不能修改,通常和external搭配,使用外部的数据来给calldata修饰的变量赋值。

注意:在solidity0.8.0以后的版本才支持上述写法,在remix传入参数的值为数组形式[666,"hello"]

之后写的代码全是以0.5.0以上0.6.0以下版本书写

17. 函数

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;contract First {// 函数可以返回多个值function returnMany() public pure returns(uint256,bool,uint256) {return(1,true,2);}// 也可以给函数的返回值命名function named() public pure returns(uint256 x,bool b,uint256 y) {return(1,true,2);}// 可以将返回值赋给他的名称,这种情况下可以省略returnfunction assigned() public pure returns(uint256 x,bool b,uint256 y) {x = 1;b = true;y = 3;}// 调用另一个函数时,也可以使用解构赋值function destructuringAssignments() public pure returns(uint256,bool,uint256,uint256,uint256) {// 用解构赋值的语法接收函数返回的值(uint256 i,bool b,uint256 j) = returnMany();// 返回的值也可以省略(uint256 x,,uint256 y) = returnMany();return(i,b,j,x,y);}// 不能使用map作为函数的输入值和输出值,但可以使用数组function arrayInput(uint256[] memory _arr) public {}}

关于函数的细节还是挺多的,知道不用和不知道也完全时两码事

17. pure和view 

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;contract First {uint256 public x = 1;function addToX(uint256 y) public view returns(uint256) {return x + y;}function add(uint256 i,uint256 j) public pure returns(uint256) {return i + j;}}

这两个修饰符也提到过很多次了

view修饰的函数只能查看状态变量或者全局变量

pure修饰的函数不能查看、修改状态变量或者全局变量

什么都不写代表着要更新状态变量

18.Error

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;contract First {uint256 x = 1;function getX() public view returns(uint256) {return x;}function testRequire(uint256 _i) public{x = 2;// 条件不成立时,会回滚之前的操作require(_i > 10, "输入的数字必须大于10");}// 这段代码和testRequire函数的作用完全一样function testRevert(uint256 _i) public {x = 3;if(_i <= 10) {revert("输入的数字必须大于10");}}// assert多用于测试内部的错误function testAssert() public view {assert(x == 1);}
}

上述代码说明了require、revert、assert的作用,在高版本的solidity出现了自定义的错误,这种自定义错误可以节省gas费。以小案例来演示以下自定义错误的语法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract First {error InsufficientBalance(uint256 balance,uint256 withdrawAmount,string msg);function testCustomError(uint256 _withdrawAmount) public view returns(uint256){uint256 bal = address(this).balance;// 返回当前合约的账户余额if(bal < _withdrawAmount) {revert InsufficientBalance({balance:bal,withdrawAmount:_withdrawAmount,msg:unicode"当前的账户余额小于输入值"});}return 0;}// 返回当前的账户余额,用于测试,因为当前账户没有余额所以返回的是默认值0,function getBalance() public view returns(uint256) {return address(this).balance;}
}

这个小案例用到了address(this).balance,这就之前一直提到的全局变量,这条语句的作用是将当前合约实例转化为地址类型,从而可以调用balance属性来获取当前合约账户的余额。

这里要特别理清三个概念。智能合约账户,智能合约地址,外部账户。这三个概念在区块链世界中容易混淆。在实际使用中,当我们说到“智能合约地址”,我们可能是在讨论与之交互的方法,例如发送以太币到这个地址或是调用它的函数。而当我们提及“智能合约账户”,则更多地在关注合约的状态和行为——例如它的余额、它能执行哪些操作,以及它存储了哪些数据。而外部账户用于部署合约,发送交易。

19. Modifier

// SPDX-License-Identifier:MIT
pragma solidity ^0.5.0;
contract Second {address public owner;uint256 public x = 10;bool public locked;// 构造函数,在合约部署的时候自动调用constructor() public{owner = msg.sender;}modifier onlyOwner() {require(msg.sender == owner,"你不是合约部署者");// 执行完了require再执行剩余用onlyOwner修饰的函数代码_;}modifier validAddress(address _addr) {require(_addr != address(0),"无效的地址值");_;}// 只有合约拥有者且地址值有效才可以修改合约拥有者function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner){owner = _newOwner;}// 防止用noReentrancy修饰的函数在执行的过程中再次调用modifier noReentrancy() {require(!locked,"不可重入");// 确保当前没有其他操作锁定了合约locked = true;// 在执行操作前锁定合约_;// 执行被修饰的函数的体locked = false;// 执行完毕后解锁合约,允许其他操作}function decrement(uint256 _i) public noReentrancy returns(uint256){x -= _i;if(_i > 1) {decrement(_i - 1);}return x;}
}

Modifier修饰符是可以在函数调用之前和之后运行的代码,多用于限制访问验证和防止重入攻击

19. Events事件

// SPDX-License-Identifier:MIT
pragma solidity ^0.5.0;
contract Second {event Log(address indexed sender,string message);event AnotherLog();function test() public {emit Log(msg.sender,"Hello World");emit Log(msg.sender,"Hello EVM");emit AnotherLog();}
}

事件是一种智能合约内部记录和触发日志信息的机制,在执行操作时向外部发送信息,在外部就可以被应用程序监听和处理。

indexed表示为地址添加索引,而动态类型的参数(stringbytes),由于其数据大小的不确定性,不能被设置为indexed。

20. constructor

// SPDX-License-Identifier:MIT
pragma solidity ^0.5.0;
contract X {string public name;constructor (string memory _name) public {name = _name;}
}
contract Y {string public text;constructor(string memory _text) public{text = _text;}
}
contract B is X("继承X"),Y("继承Y"){}contract C is X,Y {constructor(string memory _name,string memory _text) X(_name) Y(_text) public{}
}
contract D is X,Y{constructor() X("继承X") Y("继承Y") public {}
}
contract E is X,Y{constructor() Y("继承Y") X("继承X") public {}
}

注意

is关键字表示继承。构造函数 在部署合约的时候自动调用,注意就是在低版本的solidity中需要在constructor后面加上public修饰符,在高版本中就不需要加了。

21. 继承

继承函数

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract A {function foo() public pure virtual returns(string memory) {return "A";}
}
contract B is A {function foo() public pure virtual override returns(string memory) {return "B";}
}
contract C is A {function foo() public pure virtual override returns(string memory) {return "C";}
}
contract D is B,C {function foo() public pure override(B,C) returns(string memory) {return super.foo();}
}
contract E is C, B {function foo() public pure override(C, B) returns (string memory) {return super.foo();}
}
contract F is A, B {function foo() public pure override(A, B) returns (string memory) {return super.foo();}
}/* A/ \B   C/ \ /
F  D,E*/

在solidity中支持多重继承,在高版本中父合约需要被重写的函数要加上virtual修饰,子合约中需要重写父合约函数要加上override修饰。调用父合约的函数需要super

继承状态变量 

// SPDX-License-Identifier:MIT
pragma solidity ^0.5.0;
contract A {string public name = "Contract A";function getName() public view returns(string memory) {return name;}
}
contract B is A {function setName() public returns(string memory) {name = "Contract B";}
}

继承下来的状态变量可以直接修改

调用父合约

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.0;
contract A {// 方便追踪函数event Log(string message);function foo() public virtual {emit Log("A.foo called");}function bar() public virtual {emit Log("A.bar called");}
}
contract B is A {function foo() public virtual override {emit Log("B.foo called");A.foo();}function bar() public virtual override {emit Log("B.bar called");super.bar();}
}
contract C is A {function foo() public virtual override {emit Log("C.foo called");A.foo();}function bar() public virtual override {emit Log("C.bar called");super.bar();}
}
contract D is B, C {function foo() public override(B, C) {super.foo();}function bar() public override(B, C) {super.bar();}
}

调用父合约可以使用合约名或者super关键字

22. 可见性

函数

// SPDX-License-Identifier:MIT
pragma solidity ^0.5.0;
contract A {// 私有函数只能被合约内部调用,继承后不能被调用function privateFunc() private pure returns(string memory) {return "私有函数被调用了";}function testPrivateFunc() public pure returns(string memory) {return privateFunc();}// 内部函数可以被合约内部调用,也可以被继承调用function internalFunc() internal pure returns(string memory) {return "内部函数被调用了";}function testInternalFunc() public pure returns(string memory) {return internalFunc();}// 公共函数可以被外部的账户或合约调用function publicFunc() public pure returns(string memory) {return "公共函数被调用了";}// 外部函数只能被外部的账户或者合约调用,合约内部不可以调用function externalFunc() external pure returns(string memory) {return "外部函数被调用了";}
}

之前的操作中或多或少都遇到过,现在把它们总结一下

函数的修饰符有public、private、internal、external

public:任何合约或者账户都可以调用

private:只有在合约内部才可以调用

internal:只有在合约内部或者继承的合约才可以调用

external:只有其他外部合约或者外部账户才可以调用

状态变量

// SPDX-License-Identifier:MIT
pragma solidity ^0.5.0;
contract A {string private privateVar = "私有状态变量";string internal internalVar = "内部状态变量";string public publicVar = "公共变量";
}

状态变量的修饰符只有三种分别是public,private,internal,没有external修饰符,作用和函数类似。

23. Interface

24.案例

合约投票

// SPDX-License-Identifier: MIT
pragma solidity ^0.5.0;contract First {// 表示一个投票人struct Voter {uint256 weight; // 通过代理积累的权重bool voted; // 表示是否已经投过票uint256 vote; // 选择的提案编号}// 表示一个提案struct Proposal {bytes32 name; // 提案名称uint256 voteCount; // 积累的投票数量}address public chairperson; // 主席// 保存从地址到投票人数据的映射mapping(address => Voter) public voters;// 将提案保存在数组里Proposal[] public proposals;// 基于一组提案,构建一个投票协约function Ballot(bytes32[] memory proposalNames) public {chairperson = msg.sender; // 投票协约的创建人是主席voters[chairperson].weight = 1; // 主席的投票权重是1// 针对每一个提案名,创建一个对应的提案,并且保存在Proposals数组中for (uint256 i = 0; i < proposalNames.length; i++) {proposals.push(Proposal({name: proposalNames[i], voteCount: 0}));}}// 主席给予一个人的投票权利function giveRightToVote(address voter) public{require(msg.sender == chairperson,"只有主席才能给予一个人有投票的权力");require(!voters[voter].voted,"选民已经投过票了");require(voters[voter].weight == 0);voters[voter].weight = 1;}// 进行投票function vote(uint proposal) public {// 使用当前账户进行投票Voter storage sender = voters[msg.sender];require(!sender.voted,"选民已经投过票了");sender.voted = true;sender.vote = proposal;proposals[proposal].voteCount += sender.weight;}// 计算出胜者的提案function winningProposal() public view returns(uint256 winningProposal_) {uint winningVoteCount = 0;for(uint256 p = 0; p< proposals.length;p++) {if(proposals[p].voteCount > winningVoteCount) {winningVoteCount = proposals[p].voteCount;winningProposal_ = p;}}return winningProposal_;}// 返回胜出提案的名称function winnerName() public view returns(bytes32 winnerName_) {winnerName_ = proposals[winningProposal()].name;return winnerName_;}
}

这个案例从github上找到的,写的很详细,很适合新手来钻研其中的逻辑,这里我去除了代理人的逻辑,因为比较难,后续有时间再加上。

这个案例有三个角色,分别是提案,投票者,主席。基于这三个角色的互动实现了基础的投票

 还有很多优化的地方比如使用构造器来确定主席的地址等等,后续再改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/408787.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

策略产品 ①算法逻辑

目录 一、机器学习与AI的关系 二、机器学习全流程 1. 问题定义 2. 数据处理 3. 特征工程 4. 模型训练 5. 模型评估 6. 模型应用 机器学习是AI的关键技术之一&#xff0c;是指机器从历史数据中学习规律&#xff0c;从而提升系统某个性能度量的过程。这篇文章&#xff0c;我们在作…

78 Linux libusb库USB HID应用编程笔记

1 前言 这几天搞另外一个项目&#xff0c;基于Ubuntu开发一个小的应用程序&#xff0c;就是通过USB HID与设备通信。因此需要在Linux环境编写对应USB HID通信应用。 目前libusb库已经很好的支持USB相关应用的开发&#xff0c;库中提供了丰富的USB接口&#xff0c;用户可以直接调…

做空股指期货一手多少钱?

股指期货的保证金比例是12%-15%不等&#xff0c;所以做空一手股指期货的保证金最少是要十几万元&#xff0c;部分平台两万。关于做空一手股指期货的具体金额&#xff0c;这并非固定不变&#xff0c;而是会根据市场的实时价格、合约的乘数以及交易所的规定等因素而有所变动。 股…

如何使用ssm实现开放式教学评价管理系统+vue

TOC ssm121开放式教学评价管理系统vue 第1章 绪论 1.1 背景及意义 系统管理也都将通过计算机进行整体智能化操作&#xff0c;对于开放式教学评价管理系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如个人中心、教师管理、学生管理、游客管理、评价信息管理、综合评…

HCIP第五次作业

一、实验拓扑 二、实验要求 1.如图连接网络&#xff0c;合理规格IP地址&#xff0c;AS200内IGP协议为OSPF 2.R1属于AS 100:R2-R3-R4小AS 234 R5-R6-R7/AS567&#xff0c;同时声明大AS 200&#xff0c;R8属于AS300 3.R2-R5 R4-R7之间为联邦EBGP邻居关系 4.R1-R8之间通信 三、实…

第133天:内网安全-横向移动域控提权NetLogonADCSPACKDC永恒之蓝

案例一&#xff1a;横向移动-系统漏洞-CVE-2017-0146 这个漏洞就是大家熟悉的ms17-010&#xff0c;这里主要学习cs发送到msf&#xff0c;并且msf正向连接后续 原因是cs只能支持漏洞检测&#xff0c;而msf上有很多exp可以利用 注意msf不能使用4.5版本的有bug 这里还是反弹权…

基于STM32开发的智能风扇控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化温度与湿度监测风扇控制与状态显示Wi-Fi通信与远程控制应用场景 家庭与办公室的温控风扇管理工业环境的智能通风控制常见问题及解决方案 常见问题解决方案结论 1. 引言 智能风扇控…

react笔记(React18)

以下笔记可能毫无章法&#xff0c;仅供个人学习记录使用。 关于状态提升&#xff1a; 状态提升适用于兄弟组件之间传递数据&#xff0c;共享状态&#xff0c;其实就是把两个兄弟组件要共同使用的数据存放到共同的父组件中&#xff0c;称状态提升。 关于context跨层级组件通信…

Vodafone 推出了与 Wi-Fi 竞争的基于树莓派私人5G技术

随着全球5G网络的逐步推出&#xff0c;在其过程中遇到了可预见的起起伏伏&#xff0c;并且蜂窝技术也开始进入另一个无线技术 Wi-Fi &#xff0c;并且已经占据的市场。私有5G网络&#xff08;即个人或公司建立自己的全设施蜂窝网络&#xff09;如今正在寻找曾经属于Wi-Fi的唯一…

openai whisper使用

whisper使用 介绍 Whisper是一种通用的语音识别模型。它是在大量不同音频数据集上训练的&#xff0c;也是一个多任务模型&#xff0c;可以执行多语言语音识别、语音翻译和语言识别。 GitHub&#xff1a;https://github.com/openai/whisper 论文链接&#xff1a;https://arx…

SQL Server 2017上服务端设置强制加密启用SSL

在数据库服务端设置&#xff0c;强制所有客户端使用 SSL&#xff0c;设置完后&#xff0c;后续客户端所有连接&#xff0c;都将以密文传送&#xff0c;不论客户端是否指定安全连接&#xff08;即EncryptTrue/False&#xff09; 一、服务端强制加密使用 SSL 1.在数据库服务器上…

微服务事务管理

目录 一、分布式事务问题 1、本地事务 2、分布式事务 3、分布式事务问题 二、理论基础 1、CAP定理 &#xff08;1&#xff09;一致性 &#xff08;2&#xff09;可用性 &#xff08;3&#xff09;分区容错 &#xff08;4&#xff09;矛盾 2、BASE理论 3、解决分布式…

Java学习_21_多线程JUC

文章目录 前言多线程并发与并行多线程的实现方式Thread类Runnable接口Callable接口和Future接口 Thread类的相关方法线程对象线程优先级守护线程出让线程/礼让线程插入线程/插队线程 线程的相关问题生命周期安全问题Lock锁死锁等待唤醒机制&#xff08;生产者和消费者&#xff…

Flex的基本使用+综合案例

组成 弹性盒子没有设置高&#xff0c;就会自动拉伸 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&q…

GCB | 首次揭示!气候变化对常绿和落叶植物物候差异化影响的机制

气候变化引起的植物物候改变已对全球范围内生物多样性和生态系统产生重大影响&#xff08;Nature高引文章 | 北京大学朴世龙院士等&#xff1a;全球变暖对植被物候的影响及其机制&#xff1b;Nature Ecology & Evolution | 南京大学张永光教授团队揭示延长的植被物候期受CO…

【日记】狗尾巴草与暗恋(1519 字)

写在前面 消极内容注意 正文 好想吃火龙果。 下周会变得异常艰难。因为事情已经垒到天上去了&#xff0c;还要来检查。 上午&#xff0c;同事送了一点水果&#xff0c;我从来没见过。问了一下别人&#xff0c;有的说是灯笼果&#xff0c;有的说是菇凉果、姑娘果。搜了一下&am…

go const(常量)

常量介绍 示例 package mainimport ("fmt" )func main() {const name "tom"fmt.Println(name)const tax float64 0.8fmt.Println(tax) }go run const.go tom 0.8package mainimport ("fmt" )func main() {const a intfmt.Println(a) }go run…

【Excal】OR 函数

语法&#xff1a; OR&#xff08;判断条件1&#xff0c;判断条件2&#xff0c;判断体件3&#xff0c;****&#xff09; 评优条件&#xff1a; 语文成绩高于90 数学成绩高于90 英语成绩高于85 物理成绩高于85 点击回车键 选中填充 回车 选中填充

SpringBootWeb 篇-深入了解 SpringBoot + Vue 的前后端分离项目部署上线与 Nginx 配置文件结构

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 云服务器的准备 2.0 Xshell 和 Xftp 软件 2.1 Xshell 介绍 2.2 Xftp 介绍 3.0 在云服务器进行环境配置 3.1 安装 JDK 3.2 安装 MySQL 3.3 安装 Nginx 4.0 SpringB…

论文降重,Kimi如何助你一臂之力?

在学术研究的浪潮中&#xff0c;原创性和学术诚信是每位研究者必须坚守的灯塔。然而&#xff0c;随着研究领域的不断扩展和深化&#xff0c;论文写作过程中难免会遇到内容重复的问题&#xff0c;这不仅影响论文的独创性&#xff0c;也对学术声誉构成挑战。本文将介绍Kimi的核心…