ERC1155Burnable.spec 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. //// ## Verification of `ERC1155Burnable`
  2. ////
  3. //// `ERC1155Burnable` extends the `ERC1155` functionality by wrapping the internal
  4. //// methods `_burn` and `_burnBatch` in the public methods `burn` and `burnBatch`,
  5. //// adding a requirement that the caller of either method be the account holding
  6. //// the tokens or approved to act on that account's behalf.
  7. ////
  8. //// ### Assumptions and Simplifications
  9. ////
  10. //// - No changes made using the harness
  11. ////
  12. //// ### Properties
  13. methods {
  14. balanceOf(address, uint256) returns uint256 envfree
  15. isApprovedForAll(address,address) returns bool envfree
  16. }
  17. /// If a method call reduces account balances, the caller must be either the
  18. /// holder of the account or approved to act on the holder's behalf.
  19. rule onlyHolderOrApprovedCanReduceBalance(method f)
  20. {
  21. address holder; uint256 token; uint256 amount;
  22. uint256 balanceBefore = balanceOf(holder, token);
  23. env e; calldataarg args;
  24. f(e, args);
  25. uint256 balanceAfter = balanceOf(holder, token);
  26. assert balanceAfter < balanceBefore => e.msg.sender == holder || isApprovedForAll(holder, e.msg.sender),
  27. "An account balance may only be reduced by the holder or a holder-approved agent";
  28. }
  29. /// Burning a larger amount of a token must reduce that token's balance more
  30. /// than burning a smaller amount.
  31. /// n.b. This rule holds for `burnBatch` as well due to rules establishing
  32. /// appropriate equivalence between `burn` and `burnBatch` methods.
  33. rule burnAmountProportionalToBalanceReduction {
  34. storage beforeBurn = lastStorage;
  35. env e;
  36. address holder; uint256 token;
  37. mathint startingBalance = balanceOf(holder, token);
  38. uint256 smallBurn; uint256 largeBurn;
  39. require smallBurn < largeBurn;
  40. // smaller burn amount
  41. burn(e, holder, token, smallBurn) at beforeBurn;
  42. mathint smallBurnBalanceChange = startingBalance - balanceOf(holder, token);
  43. // larger burn amount
  44. burn(e, holder, token, largeBurn) at beforeBurn;
  45. mathint largeBurnBalanceChange = startingBalance - balanceOf(holder, token);
  46. assert smallBurnBalanceChange < largeBurnBalanceChange,
  47. "A larger burn must lead to a larger decrease in balance";
  48. }
  49. /// Two sequential burns must be equivalent to a single burn of the sum of their
  50. /// amounts.
  51. /// This rule holds for `burnBatch` as well due to rules establishing
  52. /// appropriate equivalence between `burn` and `burnBatch` methods.
  53. rule sequentialBurnsEquivalentToSingleBurnOfSum {
  54. storage beforeBurns = lastStorage;
  55. env e;
  56. address holder; uint256 token;
  57. mathint startingBalance = balanceOf(holder, token);
  58. uint256 firstBurn; uint256 secondBurn; uint256 sumBurn;
  59. require sumBurn == firstBurn + secondBurn;
  60. // sequential burns
  61. burn(e, holder, token, firstBurn) at beforeBurns;
  62. burn(e, holder, token, secondBurn);
  63. mathint sequentialBurnsBalanceChange = startingBalance - balanceOf(holder, token);
  64. // burn of sum of sequential burns
  65. burn(e, holder, token, sumBurn) at beforeBurns;
  66. mathint sumBurnBalanceChange = startingBalance - balanceOf(holder, token);
  67. assert sequentialBurnsBalanceChange == sumBurnBalanceChange,
  68. "Sequential burns must be equivalent to a burn of their sum";
  69. }
  70. /// The result of burning a single token must be equivalent whether done via
  71. /// burn or burnBatch.
  72. /// @title Single token `burn` / `burnBatch` equivalence
  73. rule singleTokenBurnBurnBatchEquivalence {
  74. storage beforeBurn = lastStorage;
  75. env e;
  76. address holder;
  77. uint256 token; uint256 burnAmount;
  78. uint256[] tokens; uint256[] burnAmounts;
  79. mathint startingBalance = balanceOf(holder, token);
  80. require tokens.length == 1; require burnAmounts.length == 1;
  81. require tokens[0] == token; require burnAmounts[0] == burnAmount;
  82. // burning via burn
  83. burn(e, holder, token, burnAmount) at beforeBurn;
  84. mathint burnBalanceChange = startingBalance - balanceOf(holder, token);
  85. // burning via burnBatch
  86. burnBatch(e, holder, tokens, burnAmounts) at beforeBurn;
  87. mathint burnBatchBalanceChange = startingBalance - balanceOf(holder, token);
  88. assert burnBalanceChange == burnBatchBalanceChange,
  89. "Burning a single token via burn or burnBatch must be equivalent";
  90. }
  91. /// The results of burning multiple tokens must be equivalent whether done
  92. /// separately via `burn` or together via `burnBatch`.
  93. /// @title Multiple token `burn` / `burnBatch` equivalence
  94. rule multipleTokenBurnBurnBatchEquivalence {
  95. storage beforeBurns = lastStorage;
  96. env e;
  97. address holder;
  98. uint256 tokenA; uint256 tokenB; uint256 tokenC;
  99. uint256 burnAmountA; uint256 burnAmountB; uint256 burnAmountC;
  100. uint256[] tokens; uint256[] burnAmounts;
  101. mathint startingBalanceA = balanceOf(holder, tokenA);
  102. mathint startingBalanceB = balanceOf(holder, tokenB);
  103. mathint startingBalanceC = balanceOf(holder, tokenC);
  104. require tokens.length == 3; require burnAmounts.length == 3;
  105. require tokens[0] == tokenA; require burnAmounts[0] == burnAmountA;
  106. require tokens[1] == tokenB; require burnAmounts[1] == burnAmountB;
  107. require tokens[2] == tokenC; require burnAmounts[2] == burnAmountC;
  108. // burning via burn
  109. burn(e, holder, tokenA, burnAmountA) at beforeBurns;
  110. burn(e, holder, tokenB, burnAmountB);
  111. burn(e, holder, tokenC, burnAmountC);
  112. mathint burnBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA);
  113. mathint burnBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB);
  114. mathint burnBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC);
  115. // burning via burnBatch
  116. burnBatch(e, holder, tokens, burnAmounts) at beforeBurns;
  117. mathint burnBatchBalanceChangeA = startingBalanceA - balanceOf(holder, tokenA);
  118. mathint burnBatchBalanceChangeB = startingBalanceB - balanceOf(holder, tokenB);
  119. mathint burnBatchBalanceChangeC = startingBalanceC - balanceOf(holder, tokenC);
  120. assert burnBalanceChangeA == burnBatchBalanceChangeA
  121. && burnBalanceChangeB == burnBatchBalanceChangeB
  122. && burnBalanceChangeC == burnBatchBalanceChangeC,
  123. "Burning multiple tokens via burn or burnBatch must be equivalent";
  124. }
  125. /// If passed empty token and burn amount arrays, `burnBatch` must not change
  126. /// token balances or address permissions.
  127. rule burnBatchOnEmptyArraysChangesNothing {
  128. uint256 token; address nonHolderA; address nonHolderB;
  129. uint256 startingBalance = balanceOf(nonHolderA, token);
  130. bool startingPermission = isApprovedForAll(nonHolderA, nonHolderB);
  131. env e; address holder; uint256[] noTokens; uint256[] noBurnAmounts;
  132. require noTokens.length == 0; require noBurnAmounts.length == 0;
  133. burnBatch(e, holder, noTokens, noBurnAmounts);
  134. uint256 endingBalance = balanceOf(nonHolderA, token);
  135. bool endingPermission = isApprovedForAll(nonHolderA, nonHolderB);
  136. assert startingBalance == endingBalance,
  137. "burnBatch must not change token balances if passed empty arrays";
  138. assert startingPermission == endingPermission,
  139. "burnBatch must not change account permissions if passed empty arrays";
  140. }
  141. /*
  142. /// This rule should always fail.
  143. rule sanity {
  144. method f; env e; calldataarg args;
  145. f(e, args);
  146. assert false,
  147. "This rule should always fail";
  148. }
  149. */