diff --git a/Eric.md b/Eric.md index e751fb53..d03ca480 100644 --- a/Eric.md +++ b/Eric.md @@ -57,6 +57,13 @@ day06: [WTF Academy Solidity 101 11-12 Note](/content/Eric/106.md) +### 2024.09.29 + +day07: + +[WTF Academy Solidity 101 13-14 Note](/content/Eric/107.md) + + diff --git a/content/Eric/109.md b/content/Eric/109.md new file mode 100644 index 00000000..6a47bd30 --- /dev/null +++ b/content/Eric/109.md @@ -0,0 +1,57 @@ +## 库合约 +### 库合约和普通合约的区别 +* 不能存在状态变量 +* 不能够继承和被继承 +* 不能接受以太币 +* 不可以被销毁 +* 库合约的可见效为public或者external时,为delegatecall调用 + +### 如何使用库合约 +1. 使用using for 指令 + ``` + // 利用using for指令 + using Strings for uint256; + function getString1(uint256 _number) public pure returns(string memory){ + // 库合约中的函数会自动添加为uint256型变量的成员 + return _number.toHexString(); + } + ``` +2. 通过库合约名称调用函数 + ``` + // 直接通过库合约名调用 + function getString2(uint256 _number) public pure returns(string memory){ + return Strings.toHexString(_number); + } + ``` +### 常用的库合约有 +* Strings:将uint256转换为String +* Address:判断某个地址是否为合约地址 +* Create2:更安全的使用Create2 EVM opcode +* Arrays:跟数组相关的库合约 + +## Import + +### 通过源文件相对位置导入 +``` +文件结构 +├── Import.sol +└── Yeye.sol + +// 通过文件相对位置import +import './Yeye.sol'; +``` +### 通过网址引入 +``` +// 通过网址引用 +import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol'; +``` +### 通过npm目录导入 +``` +import '@openzeppelin/contracts/access/Ownable.sol'; +``` +### 指定全局符号 +``` +import {Yeye} from './Yeye.sol'; +``` + + diff --git a/content/Eric/110.md b/content/Eric/110.md new file mode 100644 index 00000000..d56d870b --- /dev/null +++ b/content/Eric/110.md @@ -0,0 +1,78 @@ +## 接收ETH +### 接受ETH函数-receive +1. receive写法 +``` +event Received(address _add, uint256 value); + +receive() external payable{ + emit Received(msg.sender, msg.value); +} + +``` +2. 注意点 + * receive 函数不能写的太复杂,如果别人用send或transfer发送eth的话,因为这两个函数会限制gas费,就会造成“out of gas”发送eth失败 + +### 回退函数-fallback + +1. 写法 +``` +event fallbackCalled(address _add, uint256 value); + +fallback() external payable{ + emit fallbackCalled(msg.sender, msg.value); +} +``` + +### receive和fallback的区别 +receive和fallback函数可存在同一个合约,当合约接收ETH时,只会有其中一个函数被触发; +* 当msg.data为空且存在receive函数时会触发receive; +* 当msg.data不为空或者不存在receive函数时触发fallback; +* 如果这两个函数都不存在,则其他合约向该合约发送ETH时会报错; + + +## 发送ETH +三种方式发送ETH,send,transfer,call +### transfer +代码样例 +``` +// 用transfer发送eth +function transferETH(address payable _to, uint256 amount) external payable{ + _to.transfer(amount); +} +``` +* 用法:接收方.transfer(发送eth的金额) // 这个跟java用法不一样 +* 转帐限制gas费2300,所有接收的函数不能写的太复杂,通常就是打印一下日志 +* transfer如果失败,会自动revert // revert相当于java里面的throw 抛出异常 + +### send +用法跟transfer一样,唯一的区别的地方在于失败不会自动revert,需要通过接收bool,手动revert +例如: +``` +error SendFailed(); // 用send发送ETH失败error + +// send()发送ETH +function sendETH(address payable _to, uint256 amount) external payable{ + // 处理下send的返回值,如果失败,revert交易并发送error + bool success = _to.send(amount); + if(!success){ + revert SendFailed(); + } +} +``` + +### call +上代码 +``` +error CallETHFail(address _to, uint256 amount); //自定义异常 + +function callETH(address payable _to, uint256 amount) external payable{ + (bool success,) = _to.call{value:amount}(""); + if(!success){ + revert CallETHFail(_to,amount); + } +} +``` +* call函数没有gas费限制,是官方推荐的方式 +* 需要通过解构式的方式接受转账结果,手动revert + + diff --git a/content/Eric/111.md b/content/Eric/111.md new file mode 100644 index 00000000..37f69160 --- /dev/null +++ b/content/Eric/111.md @@ -0,0 +1,54 @@ +## 调用其他合约 +前提:已知其他合约代码(或接口)且已知其他合约地址 +### 传入合约地址 +上代码 +``` +function callSetX(address _add, uint256 x) external { + OtherContract(_add).set(x); +} +``` + +### 传入合约变量 +上代码 +``` +function callGetX(OtherContract _Address) external view returns(uint x){ + x = _Address.getX(); +} +``` + +### 调用合约并发送ETH +上代码 +``` +function setXTransferETH(address otherContract, uint256 x) payable external{ + OtherContract(otherContract).setX{value: msg.value}(x); +} +``` + +## Call +使用方式 +``` +目标合约地址.call{value:发送eth金额}(字节码); + +// 其中字节码利用结构化编码函数abi.encodeWithSignature获得: +abi.encodeWithSignature("函数签名", 逗号分隔的具体参数) +``` + +* call函数是官方推荐发送ETH的方法 +* call函数并不推荐用来调用其他合约的函数,但有个例外,是当你只知道对方合约地址,但是不知道合约的代码或者ABI(JSON格式的描述文件)时,可以用call来调用 + +### 利用call来调用其他函数 +``` +// 定义Response事件,输出call返回的结果success和data +event Response(bool success, bytes data); + +function callSetX(address payable _addr, uint256 x) public payable { + // call setX(),同时可以发送ETH + (bool success, bytes memory data) = _addr.call{value: msg.value}( + abi.encodeWithSignature("setX(uint256)", x) + ); + + emit Response(success, data); //释放事件 +} + +``` + diff --git a/content/Eric/112.md b/content/Eric/112.md new file mode 100644 index 00000000..7aa2cf50 --- /dev/null +++ b/content/Eric/112.md @@ -0,0 +1,53 @@ +## DelegateCall +### DelegateCall和Call的区别 +A合约委托调用B合约,执行的是B合约的函数,但是状态变量和上下文都是A的 + +### 什么情况下会用到DelegateCall +DelegateCall将代理合约和逻辑合约分开,状态变量和上下文都存储在代理合约中,逻辑合约只负责提供所有的函数, +这样当需要升级的时候,只需要将逻辑合约的地址替换掉就可以无缝升级了 + +### 例子 +``` +// 被调用的合约C(逻辑合约) +contract C { + uint public num; + address public sender; + + function setVars(uint _num) public payable { + num = _num; + sender = msg.sender; + } +} + +// 代理合约 +contract B { + uint public num; + address public sender; + + // 通过call来调用C的setVars()函数,将改变合约C里的状态变量 + function callSetVars(address _addr, uint _num) external payable{ + // call setVars() + (bool success, bytes memory data) = _addr.call( + abi.encodeWithSignature("setVars(uint256)", _num) + ); + } +} +``` + +## 在合约中创建新合约 +用户可以创建合约,合约同样也可以创建合约, +去中心化交易所uniswap就是通过工程合约(PairFactory)创建了无数个币对合约(Pair) +有两种方式可以创建合约,先讲Create +### Create +Create的用法很简单,就是new一个合约; +x是合约对象(地址),如果构造函数是payable,可以在创建时转入_value数量的ETH; +params是构造函数的参数 +``` +Contract x = new Contract{value:_value}(params); +``` +#### create如何计算出地址 +``` +新地址 = hash(调用者地址,nonce); +``` +对于用户来说,调用者地址是钱包地址,nonce是该钱包交易的次数; +对于合约来说,调用者地址是合约地址,nonce是该地址创建合约的次数; \ No newline at end of file diff --git a/content/Eric/113.md b/content/Eric/113.md new file mode 100644 index 00000000..847693d4 --- /dev/null +++ b/content/Eric/113.md @@ -0,0 +1,13 @@ +## Create2 + +// TODO + +## 删除合约 + +### 如何使用 +_addr是当合约被销毁时指定剩余ETH转移的地址,该地址不需要有receive/fallback函数也能接收ETH +``` +selfdestruct(_addr); +``` +### 同笔交易内实现合约创建和销毁 +// TODO \ No newline at end of file diff --git a/content/Eric/114.md b/content/Eric/114.md new file mode 100644 index 00000000..0da49377 --- /dev/null +++ b/content/Eric/114.md @@ -0,0 +1,3 @@ +## ABI编码解码 + +## Hash diff --git a/content/Eric/115.md b/content/Eric/115.md new file mode 100644 index 00000000..79024f1f --- /dev/null +++ b/content/Eric/115.md @@ -0,0 +1,16 @@ +## 选择器 +当我们调用合约时,本质上是向该合约发送了一段**calldata**,可以在input(输入)中看到此次交易的**calldata** +发送calldata的前4个字节是selector(函数选择器),后面32个字节是输入的参数,其实calldata就是告诉合约,我要 +调用哪个函数,以及参数是什么 + +### msg.data +msg.data = method id + 32个字节的参数 + +### method id,selector +method id 是函数签名哈希值的前4个字节 + +### 函数签名 + +函数签名 = 函数名(逗号分割的参数类型) + +## Try Catch \ No newline at end of file