solidity基础语法与简单案例20221201
1、回退函数
fallback():当调用的函数不存在或者直接向合约发送主币的时候调用fallback函数。
receive():不能接受数据,仅向合约发送主币时触发
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract Fallback {
event Log(string func,address sender , uint value ,bytes data);
fallback() external payable {
emit Log("fallback", msg.sender, msg.value,msg.data);
}
//receive 不接受数据
receive() external payable {
emit Log("receive", msg.sender, msg.value, "");
}
}
2、发送ETH
可以通过send、transfer和call进行主笔发送。
send返回true和false来确定是否发送失败;
transfer返回revert来确定是否发送失败;
call返回ture和false,如果接收地址有信息时可以返回信息data
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract SendETH {
constructor() payable{}
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 failde");
}
//发送所有的gas
function sendViaCall(address payable _to) external payable{
(bool success,) = _to.call{value: 123}("");
require(success, "send failed")
}
}
3、小案例:建立简单钱包合约
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract SimpleWallet{
address payable public owner;
constructor(){
owner = payable(msg.sender);
}
receive() external payable{}
function withdraw(uint amount) public{
require(msg.sender == owner);
owner.transfer(amount);
}
function getBalance() public view returns (uint){
return address(this).balance;
}
}
4、调用其他合约
输入其他合约的地址,之后可以直接把其他合约当作类型进行使用。还可以把类型当作直接输入的变量的类型进行传入。
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract CallTestContract {
//为调用函数传参
function setX(address _test, uint _x) external {
TestContract(_test).setX(_x);
}
//获取从方法得到的参数
function getX(address _test) external view returns(uint x){
return x = TestContract(_test).getX();
}
//传参并发送主币
function setXandReceiveEther(address _test, uint _x) external payable {
TestContract(_test).setXandReceiveEther(_x);
}
//获取两个参数
function getXandValue(address _test) external returns(uint x,uint value) {
(x,value) = TestContract(_test).getXandValue();
}
}
contract TestContract {
uint public x;
uint public value = 123;
function setX(uint _x) external {
x = _x;
}
function getX() external view returns (uint) {
return x;
}
function setXandReceiveEther(uint _x) external payable {
x = _x;
value = msg.value;
}
function getXandValue() external view returns (uint,uint) {
return(x,value);
}
}
5、接口合约
不知道另一个合约的源代码或者代码量特别大时,可以使用接口合约。
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract Counter {
uint public count;
function inc() external {
count += 1;
}
function dec() external {
count -= 1;
}
}
//在接口合约中调用了两个合约的函数
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();
}
}
6、低级调用 Call
使用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 {
//使用call调用目标地址的函数,返回是否调用成功,以及一个返回值,该返回值转载了所有返回的数据
//可以携带一定数量的主币和gas进行调用
(bool success, bytes memory data) = _test.call{value: 111, gas:5000}(abi.encodeWithSignature(
"foo(string,uint256)","call foo",123
));
require(success,"call failed");
}
function callDoesNotExit(address _test) external{
(bool success,) = _test.call(abi.encodeWithSignature("DONTEXIST(string,uint)"));
require(success,"call failed");
}
}
7、委托调用:
用于观测其他合约的交互。
例如:A向B发送100wei,B委托调用C,站在C的视角能够看到A向B发送100wei,但C自身的状态变量不发生改变。
委托调用可以用来实现合约升级。
委托调用执行函数后的值在委托调用合约中修改。
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
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{
//使用签名进行编码
// _test.delegatecall(
// abi.encodeWithSignature("setVars(uint256)",_num)
// );
//使用selector进行编码
(bool success,bytes memory data) = _test.delegatecall(
abi.encodeWithSelector(TestDelegateCall.setVars.selector, _num)
);
require(success,"delegatecall failed");
}
}
8、工厂合约
用于生成新的合约
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract Account {
address public bank;
address public owner;
constructor(address _owner) payable{
bank = msg.sender;
owner = _owner;
}
}
contract AccountFactory {
Account[] public accounts;
address public owner;
constructor() payable{
owner = msg.sender;
}
function createAccount(address _owner,uint amount) external {
//account 变量记录了新建立的账户的地址,通过增加{value}来向新创建的合约中发送主币。
//在构建包含主币的合约前应当向账户工厂发送一定数量的主币
Account account = new Account{value:amount} (_owner);
//构建accounts以记录所有由该合约生成的合约
accounts.push(account);
}
function Val() external view returns(uint val){
return address(this).balance;
}
}
9、库合约
将一些常用的算法抽象出来,形成数据库,以后可以直接调用,从而避免代码的重复使用。
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
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 x;
}
}
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);
}
}
10、哈希算法
通常用于签名运算和获取特定ID
在对数据进行打包时,如果使用api.encodePacked方法,可能会造成哈希碰撞(加密内容不相同但哈希运算结果相同)的问题。
两种打包方法:
abi.encodePacked()
abi.encode()
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract HashFunc{
function hash1(string memory text ,uint num ,address addr) external pure returns(bytes32){
return keccak256(abi.encodePacked(text,num,addr));
}
function hash2(string memory text ,uint num ,address addr) external pure returns(bytes32){
return keccak256(abi.encode(text,num,addr));
}
}