ERC20Wrapper.spec 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import "ERC20.spec"
  2. methods {
  3. underlying() returns(address) envfree
  4. underlyingTotalSupply() returns(uint256) envfree
  5. underlyingBalanceOf(address) returns(uint256) envfree
  6. depositFor(address, uint256) returns(bool)
  7. withdrawTo(address, uint256) returns(bool)
  8. _recover(address) returns(uint256)
  9. }
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  11. // Invariants //
  12. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  13. use invariant totalSupplyIsSumOfBalances
  14. // totalsupply of wrapped should be less than or equal to underlying (assuming no external transfer) - solvency
  15. invariant whatAboutTotal(env e)
  16. totalSupply() <= underlyingTotalSupply()
  17. filtered { f -> f.selector != certorafallback_0().selector && !f.isView }
  18. {
  19. preserved with (env e2) {
  20. require underlyingBalanceOf(currentContract) <= underlyingTotalSupply();
  21. requireInvariant totalSupplyIsSumOfBalances;
  22. require e.msg.sender == e2.msg.sender;
  23. }
  24. preserved depositFor(address account, uint256 amount) with (env e3){
  25. require totalSupply() + amount <= underlyingTotalSupply();
  26. }
  27. preserved _mint(address account, uint256 amount) with (env e4){
  28. require totalSupply() + amount <= underlyingTotalSupply();
  29. }
  30. preserved _burn(address account, uint256 amount) with (env e5){
  31. require balanceOf(account) >= amount;
  32. requireInvariant totalSupplyIsSumOfBalances;
  33. }
  34. }
  35. // totalsupply of wrapped should be less than or equal to the underlying balanceOf contract (assuming no external transfer) - solvency
  36. invariant underTotalAndContractBalanceOfCorrelation(env e)
  37. totalSupply() <= underlyingBalanceOf(currentContract)
  38. {
  39. preserved with (env e2) {
  40. require underlying() != currentContract;
  41. require e.msg.sender != currentContract;
  42. require e.msg.sender == e2.msg.sender;
  43. requireInvariant totalSupplyIsSumOfBalances;
  44. }
  45. preserved _mint(address account, uint256 amount) with (env e4){
  46. require totalSupply() + amount <= underlyingBalanceOf(currentContract);
  47. require underlying() != currentContract;
  48. }
  49. preserved _burn(address account, uint256 amount) with (env e5){
  50. require balanceOf(account) >= amount;
  51. requireInvariant totalSupplyIsSumOfBalances;
  52. }
  53. }
  54. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  55. // Rules //
  56. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  57. // Check that values are updated correctly by `depositFor()`
  58. rule depositForSpecBasic(env e) {
  59. address account;
  60. uint256 amount;
  61. require e.msg.sender != currentContract;
  62. require underlying() != currentContract;
  63. uint256 wrapperTotalBefore = totalSupply();
  64. uint256 underlyingTotalBefore = underlyingTotalSupply();
  65. uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract);
  66. depositFor(e, account, amount);
  67. uint256 wrapperTotalAfter = totalSupply();
  68. uint256 underlyingTotalAfter = underlyingTotalSupply();
  69. uint256 underlyingThisBalanceAfter = underlyingBalanceOf(currentContract);
  70. assert wrapperTotalBefore == to_uint256(wrapperTotalAfter - amount), "wrapper total wrong update";
  71. assert underlyingTotalBefore == underlyingTotalAfter, "underlying total was updated";
  72. assert underlyingThisBalanceBefore == to_uint256(underlyingThisBalanceAfter - amount), "underlying this balance wrong update";
  73. }
  74. // Check that values are updated correctly by `depositFor()`
  75. rule depositForSpecWrapper(env e) {
  76. address account;
  77. uint256 amount;
  78. require underlying() != currentContract;
  79. uint256 wrapperUserBalanceBefore = balanceOf(account);
  80. uint256 wrapperSenderBalanceBefore = balanceOf(e.msg.sender);
  81. depositFor(e, account, amount);
  82. uint256 wrapperUserBalanceAfter = balanceOf(account);
  83. uint256 wrapperSenderBalanceAfter = balanceOf(e.msg.sender);
  84. assert account == e.msg.sender => wrapperUserBalanceBefore == wrapperSenderBalanceBefore
  85. && wrapperUserBalanceAfter == wrapperSenderBalanceAfter
  86. && wrapperUserBalanceBefore == to_uint256(wrapperUserBalanceAfter - amount)
  87. , "wrapper balances wrong update";
  88. assert account != e.msg.sender => wrapperUserBalanceBefore == to_uint256(wrapperUserBalanceAfter - amount)
  89. && wrapperSenderBalanceBefore == wrapperSenderBalanceAfter
  90. , "wrapper balances wrong update";
  91. }
  92. // Check that values are updated correctly by `depositFor()`
  93. rule depositForSpecUnderlying(env e) {
  94. address account;
  95. uint256 amount;
  96. require e.msg.sender != currentContract;
  97. require underlying() != currentContract;
  98. uint256 underlyingSenderBalanceBefore = underlyingBalanceOf(e.msg.sender);
  99. uint256 underlyingUserBalanceBefore = underlyingBalanceOf(account);
  100. depositFor(e, account, amount);
  101. uint256 underlyingSenderBalanceAfter = underlyingBalanceOf(e.msg.sender);
  102. uint256 underlyingUserBalanceAfter = underlyingBalanceOf(account);
  103. assert account == e.msg.sender => underlyingSenderBalanceBefore == underlyingUserBalanceBefore
  104. && underlyingSenderBalanceAfter == underlyingUserBalanceAfter
  105. && underlyingSenderBalanceBefore == to_uint256(underlyingSenderBalanceAfter + amount)
  106. , "underlying balances wrong update";
  107. assert account != e.msg.sender
  108. && account == currentContract => underlyingSenderBalanceBefore == to_uint256(underlyingSenderBalanceAfter + amount)
  109. && underlyingUserBalanceBefore == to_uint256(underlyingUserBalanceAfter - amount)
  110. , "underlying balances wrong update";
  111. assert account != e.msg.sender
  112. && account != currentContract => underlyingSenderBalanceBefore == to_uint256(underlyingSenderBalanceAfter + amount)
  113. && underlyingUserBalanceBefore == to_uint256(underlyingUserBalanceAfter)
  114. , "underlying balances wrong update";
  115. }
  116. // Check that values are updated correctly by `withdrawTo()`
  117. rule withdrawToSpecBasic(env e) {
  118. address account;
  119. uint256 amount;
  120. require underlying() != currentContract;
  121. uint256 wrapperTotalBefore = totalSupply();
  122. uint256 underlyingTotalBefore = underlyingTotalSupply();
  123. withdrawTo(e, account, amount);
  124. uint256 wrapperTotalAfter = totalSupply();
  125. uint256 underlyingTotalAfter = underlyingTotalSupply();
  126. assert wrapperTotalBefore == to_uint256(wrapperTotalAfter + amount), "wrapper total wrong update";
  127. assert underlyingTotalBefore == underlyingTotalAfter, "underlying total was updated";
  128. }
  129. // Check that values are updated correctly by `withdrawTo()`
  130. rule withdrawToSpecWrapper(env e) {
  131. address account; uint256 amount;
  132. require underlying() != currentContract;
  133. uint256 wrapperUserBalanceBefore = balanceOf(account);
  134. uint256 wrapperSenderBalanceBefore = balanceOf(e.msg.sender);
  135. withdrawTo(e, account, amount);
  136. uint256 wrapperUserBalanceAfter = balanceOf(account);
  137. uint256 wrapperSenderBalanceAfter = balanceOf(e.msg.sender);
  138. assert account == e.msg.sender => wrapperUserBalanceBefore == wrapperSenderBalanceBefore
  139. && wrapperUserBalanceAfter == wrapperSenderBalanceAfter
  140. && wrapperUserBalanceBefore == to_uint256(wrapperUserBalanceAfter + amount)
  141. , "wrapper user balance wrong update";
  142. assert account != e.msg.sender => wrapperSenderBalanceBefore == to_uint256(wrapperSenderBalanceAfter + amount)
  143. && wrapperUserBalanceBefore == wrapperUserBalanceAfter
  144. , "wrapper user balance wrong update";
  145. }
  146. // STATUS - verified
  147. // Check that values are updated correctly by `withdrawTo()`
  148. rule withdrawToSpecUnderlying(env e) {
  149. address account; uint256 amount;
  150. require e.msg.sender != currentContract;
  151. require underlying() != currentContract;
  152. uint256 underlyingSenderBalanceBefore = underlyingBalanceOf(e.msg.sender);
  153. uint256 underlyingUserBalanceBefore = underlyingBalanceOf(account);
  154. uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract);
  155. withdrawTo(e, account, amount);
  156. uint256 underlyingSenderBalanceAfter = underlyingBalanceOf(e.msg.sender);
  157. uint256 underlyingUserBalanceAfter = underlyingBalanceOf(account);
  158. uint256 underlyingThisBalanceAfter = underlyingBalanceOf(currentContract);
  159. assert account == e.msg.sender => underlyingSenderBalanceBefore == underlyingUserBalanceBefore
  160. && underlyingSenderBalanceAfter == underlyingUserBalanceAfter
  161. && underlyingUserBalanceBefore == to_uint256(underlyingUserBalanceAfter - amount)
  162. , "underlying balances wrong update (acc == sender)";
  163. assert account != e.msg.sender && account == currentContract => underlyingUserBalanceBefore == underlyingUserBalanceAfter
  164. && underlyingSenderBalanceBefore == underlyingSenderBalanceAfter
  165. , "underlying balances wrong update (acc == contract)";
  166. assert account != e.msg.sender && account != currentContract => underlyingUserBalanceBefore == to_uint256(underlyingUserBalanceAfter - amount)
  167. && underlyingSenderBalanceBefore == underlyingSenderBalanceAfter
  168. && underlyingThisBalanceBefore == to_uint256(underlyingThisBalanceAfter + amount)
  169. , "underlying balances wrong update (acc != contract)";
  170. }
  171. // Check that values are updated correctly by `_recover()`
  172. rule recoverSpec(env e) {
  173. address account;
  174. uint256 amount;
  175. uint256 wrapperTotalBefore = totalSupply();
  176. uint256 wrapperUserBalanceBefore = balanceOf(account);
  177. uint256 wrapperSenderBalanceBefore = balanceOf(e.msg.sender);
  178. uint256 underlyingThisBalanceBefore = underlyingBalanceOf(currentContract);
  179. mathint value = underlyingThisBalanceBefore - wrapperTotalBefore;
  180. _recover(e, account);
  181. uint256 wrapperTotalAfter = totalSupply();
  182. uint256 wrapperUserBalanceAfter = balanceOf(account);
  183. uint256 wrapperSenderBalanceAfter = balanceOf(e.msg.sender);
  184. assert wrapperTotalBefore == to_uint256(wrapperTotalAfter - value), "wrapper total wrong update";
  185. assert e.msg.sender == account => wrapperUserBalanceBefore == wrapperSenderBalanceBefore
  186. && wrapperUserBalanceAfter == wrapperSenderBalanceAfter
  187. && wrapperUserBalanceBefore == to_uint256(wrapperUserBalanceAfter - value)
  188. , "wrapper balances wrong update";
  189. assert e.msg.sender != account => wrapperUserBalanceBefore == to_uint256(wrapperUserBalanceAfter - value)
  190. && wrapperSenderBalanceBefore == wrapperSenderBalanceAfter
  191. , "wrapper balances wrong update";
  192. }