refer to:
https://github.com/RareSkills/Solidity-Exercises
不错的练习题,下载后, forge test 就可以直接看结果。
使用方式:
1. 进入到对应的目录
2. forge test
3. 看源代码中的注释,说明和代码结构
4. 修改代码
5. forge test,通过即可。
我的进度
ok AccessModifer
ok Add
BasicBank
BasicBankV2
BasicStorage OK. 设置 getter, setter
BlockNumber OK。 让对应方法不要在同一个block内访问两次
CodeSize OK . 多定义一些乱七八糟的方法即可。我定义了7个。 https://ethereum.org/en/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/ 这个文章写了如何优化contract code size
CrossContract OK. 寻找两个contract中,price()返回值 低的那个值。 知识点:三元表达式,contract(address) 获得实例。
Decoder OK abi.decode(bytes_data, (type1, type2));
DeployContract
Deployer
Distribute OK。 for 循环, to_address.call {value: xx } (""); 发送eth
DistributeV2 TODO 没弄明白。。。
Divide OK solidity中不支持float. 都是整数操作。所以需要调整一下各个数的计算先后顺序。
Donations: OK. 使用mapping (跟js一样的)
Emitter OK. 实现emit简单,难点在于理解 em.expectEmit, 该方法最多测试4个emit的参数,而且需要: 1. expectEmit, 2. 调用写在Test中的emit 3. 调用真实的contract.some_method_which_triggered_event. 太绕了。
Encoder OK. abi.encode(var1, var2);
Enum OK 考察 enum与uint的类型转换。 要明确转换,例如 MyEnum(1) == MyOne 或者 1 == int(MyOne)
EverythingWorks ... 这个似乎不是题目。直接跳过。
Exponent OK 返回N次方。 写个for循环就可以了。
Fibonacci OK fibonacci 数组是 : 0, 1, 1, 2, 3, 5, 8, 13 ... 前两位是固定的0,1 , 后面开始 第n位 = (n - 1)位的值 + (n - 2)位的值 . 这里需要使用数组。另外,console2 不能直接打印数组,需要做一个循环才能打印里面的元素.
FilterOddNumbers OK. 操作数组。odd奇数,even 偶数。这个题目要求去掉某个数组中的奇数,并且返回一个数组。
FizzBuzz OK
IdiotBetting
IfStatement OK 。 太简单了吧,应该叫三元表达式
Immutable OK. 考察immutable关键字。只能在constructor中被修改,不能在其他方法中修改。否则编译器报错。
InheritanceOverride 考察继承
InsertInArray OK. 特别简单。 array[1] = 3; 然后 myContract.array(1) 就是调用。记得: 为array生成的getter是需要参数的,该参数就是element index. 例如 myContract.myArray(3) 表示获得myContract的 public member myArray 的 index = 3 的元素。 具体参考:
https://ethereum.stackexchange.com/questions/60489/why-is-there-a-difference-between-a-public-array-and-a-function-which-returns-it
IsPrime OK. 实现质数,考察for 循环, continue/break
IsSorted OK. 判断某个数组是否是升序排序
Keccak 没搞明白。
ListOfNumbers OK. 自己写一个对数组进行操作(排序的)小函数
Mean OK
MultiInheritance OK
NestedArray OK
NestedMapping OK。 用自己idea方式实现的
NotEnough OK. 使用require
OneWeekLockup OK. 使用require, block.timestamp, 1 weeks, 7 days 作为时间戳的计算,uint256 作为最终计算单位。to.call { value: xx }(""); 作为发送ETH的方法
OriginVsSender 基本OK,不过没明白UT的机制。
1. tx.origin 永远不变
2. msg.sender 会在tx chain 中发生变化。
3. vm.startPrank(address1, address2) 表示,从现在开始往后,所有的tx的 tx.origin都是address2, (address1) 自然就是msg.sender)
Owner OK. 考察modifier 的声明和使用。
PriceIsRight OK. 秒过。
PublicFunction OK . 考察public function的使用。其实改成external一样可以通过UT的。
PublicVariable OK. 记得variable只有public , 没有external这一说。
PureVsView OK. 特别简单。让代码编译通过即可。查看pure 与 view的区别(pure跟链上状态无关,读都不读)
Receive OK. 每个可以接收ETH的contract都要求有个receive.
调用方式: receive(): address(myContract).call { value: 1 ether } ("");
普通payable函数: myContract.theMethodName { value: 1 ether } ();
查看balance的方式: address(this).balance;
ReducingPayout
SelfDestroyer
SpecialNumbers OK. mapping的使用。
Stack OK。 实现一个stack.
StudentDB OK. 考察使用struct ( 就是个hash, 不能用new )
SumArray OK 计算array中的元素的和
Super OK 使用super关键字。 还可以看到virtual
TicTacToe OK. 考察循环。逻辑就是画圈圈。横,竖,斜 只要有3个连在一起的,就算赢
TimelockEscrow 基本OK。
escrow: 托管。
buyer: 充值后,3天内可以随时取。 seller 3天后才能取。 owner是seller.
同时只能存在一个托管。
基本OK了。重点在于require blocktime的使用. 细节没看明白。感觉题目过于啰嗦。
prank, startPrank 见这个 cheatcode:
https://book.getfoundry.sh/cheatcodes/prank
TripleNestedMapping OK. 关键点在于:mapping(string => mapping(uint256 => mapping(uint256 => bool))) users;
另外,需要知道在普通的function中无法创建动态mapping ,只能在function中操作contract member的mapping.
Tupledore 没弄明白,应该是需要struct + assembly 的知识。我看到assembly就贵了。。。555
Typecast 没弄明白如何调用。 从uint160 可以转换到 address, 例如 address(uint160(msg.value)) , 但是uint256, uint128就不可以
unchecked : OK 为了节省gas, 从而不检查 overflow , underflow . 例如 :
function sub(uint a, uint b )returns (uint) ,该函数要做减法,返回的都是unsigned int (正数),那么正常的时候,solidity都会去判断的(是否是负数), 例如:
Ran 1 test for test/Unchecked.t.sol:UncheckedTest
[FAIL. Reason: panic: arithmetic underflow or overflow (0x11)] testSub() (gas: 6281)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 249.50µs (27.70µs CPU time)
那么使用了uncheck, 运行时就不会报这个错误。
WhoCalledMe OK. msg.sender的使用
Withdraw ok