ERC1155Burnable.spec 6.5 KB

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