ERC1155Burnable.spec 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. methods {
  2. balanceOf(address, uint256) returns uint256 envfree
  3. isApprovedForAll(address,address) returns bool envfree
  4. }
  5. /// If a method call reduces account balances, the caller must be either the
  6. /// holder of the account or approved to act on the holder's behalf.
  7. rule onlyHolderOrApprovedCanReduceBalance {
  8. address holder; uint256 token; uint256 amount;
  9. uint256 balanceBefore = balanceOf(holder, token);
  10. method f; env e; calldataarg args;
  11. f(e, args);
  12. uint256 balanceAfter = balanceOf(holder, token);
  13. assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender),
  14. "An account balance may only be reduced by the holder or a holder-approved agent";
  15. }
  16. /// Burning a larger amount of a token must reduce that token's balance more
  17. /// than burning a smaller amount.
  18. rule burnAmountProportionalToBalanceReduction {
  19. storage beforeBurn = lastStorage;
  20. env e;
  21. address holder; uint256 token;
  22. mathint startingBalance = balanceOf(holder, token);
  23. uint256 smallBurn; uint256 largeBurn;
  24. require smallBurn < largeBurn;
  25. // smaller burn amount
  26. burn(e, holder, token, smallBurn) at beforeBurn;
  27. mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token);
  28. // larger burn amount
  29. burn(e, holder, token, largeBurn) at beforeBurn;
  30. mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token);
  31. assert smallBurnBalanceChange < largeBurnBalanceChange,
  32. "A larger burn must lead to a larger decrease in balance";
  33. }
  34. /// This rule not needed (because multipleTokenBurnBurnBatchEquivalence)
  35. /// Unimplemented rule to verify monotonicity of burnBatch.
  36. /// Using only burnBatch, possible approach:
  37. /// Token with smaller and larger burn amounts
  38. /// Round one smaller burn
  39. /// Round two larger burn
  40. rule burnBatchAmountProportionalToBalanceReduction { // TODO implement rule or remove
  41. storage beforeBurn = lastStorage;
  42. env e;
  43. address holder; uint256 token;
  44. mathint startingBalance = balanceOf(holder, token);
  45. uint256 smallBurn; uint256 largeBurn;
  46. require smallBurn < largeBurn;
  47. uint256[] tokens; uint256[] smallBurnAmounts; uint256[] largeBurnAmounts;
  48. require tokens.length == 1; require smallBurnAmounts.length == 1; require largeBurnAmounts.length == 1;
  49. require tokens[0] == token; require smallBurnAmounts[0] == smallBurn; require largeBurnAmounts[0] == largeBurn;
  50. // smaller burn amount
  51. burnBatch(e, holder, tokens, smallBurnAmounts) at beforeBurn;
  52. mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token);
  53. // larger burn amount
  54. burnBatch(e, holder, tokens, largeBurnAmounts) at beforeBurn;
  55. mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token);
  56. assert smallBurnBalanceChange < largeBurnBalanceChange,
  57. "A larger burn must lead to a larger decrease in balance";
  58. }
  59. /// Two sequential burns must be equivalent to a single burn of the sum of their
  60. /// amounts.
  61. rule sequentialBurnsEquivalentToSingleBurnOfSum {
  62. storage beforeBurns = lastStorage;
  63. env e;
  64. address holder; uint256 token;
  65. mathint startingBalance = balanceOf(holder, token);
  66. uint256 firstBurn; uint256 secondBurn; uint256 sumBurn;
  67. require sumBurn == firstBurn + secondBurn;
  68. // sequential burns
  69. burn(e, holder, token, firstBurn) at beforeBurns;
  70. burn(e, holder, token, secondBurn);
  71. mathint sequentialBurnsBalanceChange = startingBalance - balanceOf(holder, token);
  72. // burn of sum of sequential burns
  73. burn(e, holder, token, sumBurn) at beforeBurns;
  74. mathint sumBurnBalanceChange = startingBalance - balanceOf(holder, token);
  75. assert sequentialBurnsBalanceChange == sumBurnBalanceChange,
  76. "Sequential burns must be equivalent to a burn of their sum";
  77. }
  78. /// This rule not needed (because multipleTokenBurnBurnBatchEquivalence)
  79. /// Unimplemented rule to verify additivty of burnBatch.
  80. /// Using only burnBatch, possible approach:
  81. /// Token with first and second burn amounts
  82. /// Round one two sequential burns in separate transactions
  83. /// Round two two sequential burns in the same transaction
  84. /// Round three one burn of sum
  85. rule sequentialBatchBurnsEquivalentToSingleBurnBatchOfSum { // TODO implement rule or remove
  86. assert false,
  87. "TODO just a placeholder that should always show up until rule is implemented";
  88. }
  89. /// The result of burning a single token must be equivalent whether done via
  90. /// burn or burnBatch.
  91. rule singleTokenBurnBurnBatchEquivalence {
  92. storage beforeBurn = lastStorage;
  93. env e;
  94. address holder;
  95. uint256 token; uint256 burnAmount;
  96. uint256[] tokens; uint256[] burnAmounts;
  97. mathint startingBalance = balanceOf(holder, token);
  98. require tokens.length == 1; require burnAmounts.length == 1;
  99. require tokens[0] == token; require burnAmounts[0] == burnAmount;
  100. // burning via burn
  101. burn(e, holder, token, burnAmount) at beforeBurn;
  102. mathint burnBalanceChange = startingBalance - balanceOf(holder, token);
  103. // burning via burnBatch
  104. burnBatch(e, holder, tokens, burnAmounts) at beforeBurn;
  105. mathint burnBatchBalanceChange = startingBalance - balanceOf(holder, token);
  106. assert burnBalanceChange == burnBatchBalanceChange,
  107. "Burning a single token via burn or burnBatch must be equivalent";
  108. }
  109. /// The results of burning multiple tokens must be equivalent whether done
  110. /// separately via burn or together via burnBatch.
  111. rule multipleTokenBurnBurnBatchEquivalence {
  112. storage beforeBurns = lastStorage;
  113. env e;
  114. address holder;
  115. uint256 tokenA; uint256 tokenB; uint256 tokenC;
  116. uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC;
  117. uint256[] tokens; uint256[] burnAmounts;
  118. // require tokenA != tokenB; require tokenB != tokenC; require tokenC != tokenA;
  119. mathint startingBalanceA = balanceOf(holder, tokenA);
  120. mathint startingBalanceB = balanceOf(holder, tokenB);
  121. mathint startingBalanceC = balanceOf(holder, tokenC);
  122. require tokens.length == 3; require burnAmounts.length == 3;
  123. require tokens[0] == tokenA; require burnAmounts[0] == burnAmountA;
  124. require tokens[1] == tokenB; require burnAmounts[1] == burnAmountB;
  125. require tokens[2] == tokenC; require burnAmounts[2] == burnAmountC;
  126. // burning via burn
  127. burn(e, holder, tokenA, burnAmountA) at beforeBurns;
  128. burn(e, holder, tokenB, burnAmountB);
  129. burn(e, holder, tokenC, burnAmountC);
  130. mathint burnBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA);
  131. mathint burnBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB);
  132. mathint burnBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC);
  133. // burning via burnBatch
  134. burnBatch(e, holder, tokens, burnAmounts) at beforeBurns;
  135. mathint burnBatchBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA);
  136. mathint burnBatchBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB);
  137. mathint burnBatchBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC);
  138. assert burnBalanceChangeA == burnBatchBalanceChangeA
  139. && burnBalanceChangeB == burnBatchBalanceChangeB
  140. && burnBalanceChangeC == burnBatchBalanceChangeC,
  141. "Burning multiple tokens via burn or burnBatch must be equivalent";
  142. }
  143. /// possible rule:
  144. /// like singleTokenBurnBurnBatchEquivalence but for no operation
  145. /// i.e. burnBatch on empty arrays does nothing
  146. /// skip frontrunning because
  147. /// (1) needing to filter a ton of rules for f
  148. /// (2) frontrunning before burning isn't a likely issue
  149. /// This rule should always fail.
  150. rule sanity {
  151. method f; env e; calldataarg args;
  152. f(e, args);
  153. assert false,
  154. "This rule should always fail";
  155. }