ERC1155.spec 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. methods {
  2. isApprovedForAll(address, address) returns(bool) envfree
  3. balanceOf(address, uint256) returns(uint256) envfree
  4. balanceOfBatch(address[], uint256[]) returns(uint256[]) envfree
  5. _doSafeBatchTransferAcceptanceCheck(address, address, address, uint256[], uint256[], bytes) envfree
  6. _doSafeTransferAcceptanceCheck(address, address, address, uint256, uint256, bytes) envfree
  7. setApprovalForAll(address, bool)
  8. safeTransferFrom(address, address, uint256, uint256, bytes)
  9. safeBatchTransferFrom(address, address, uint256[], uint256[], bytes)
  10. mint(address, uint256, uint256, bytes)
  11. mintBatch(address, uint256[], uint256[], bytes)
  12. burn(address, uint256, uint256)
  13. burnBatch(address, uint256[], uint256[])
  14. }
  15. /////////////////////////////////////////////////
  16. // Approval (4/4)
  17. /////////////////////////////////////////////////
  18. // STATUS - verified
  19. // Function $f, which is not setApprovalForAll, should not change approval
  20. rule unexpectedAllowanceChange(method f, env e) filtered { f -> f.selector != setApprovalForAll(address, bool).selector } {
  21. address account; address operator;
  22. bool approveBefore = isApprovedForAll(account, operator);
  23. calldataarg args;
  24. f(e, args);
  25. bool approveAfter = isApprovedForAll(account, operator);
  26. assert approveBefore == approveAfter, "You couldn't get king's approval this way!";
  27. }
  28. // STATUS - verified
  29. // approval can be changed only by owner
  30. rule onlyOwnerCanApprove(env e){
  31. address owner; address operator; bool approved;
  32. bool aprovalBefore = isApprovedForAll(owner, operator);
  33. setApprovalForAll(e, operator, approved);
  34. bool aprovalAfter = isApprovedForAll(owner, operator);
  35. assert aprovalBefore != aprovalAfter => owner == e.msg.sender, "There should be only one owner";
  36. }
  37. // STATUS - verified
  38. // Check that isApprovedForAll() revertes in planned scenarios and no more.
  39. rule approvalRevertCases(env e){
  40. address account; address operator;
  41. isApprovedForAll@withrevert(account, operator);
  42. assert !lastReverted, "Houston, we have a problem";
  43. }
  44. // STATUS - verified
  45. // setApproval changes only one approval
  46. rule onlyOneAllowanceChange(method f, env e) {
  47. address owner; address operator; address user;
  48. bool approved;
  49. bool userApproveBefore = isApprovedForAll(owner, user);
  50. setApprovalForAll(e, operator, approved);
  51. bool userApproveAfter = isApprovedForAll(owner, user);
  52. assert userApproveBefore != userApproveAfter => (e.msg.sender == owner && operator == user), "Imposter!";
  53. }
  54. /////////////////////////////////////////////////
  55. // Balance (3/3)
  56. /////////////////////////////////////////////////
  57. // STATUS - verified
  58. // Function $f, which is not one of transfers, mints and burns, should not change balanceOf of a user
  59. rule unexpectedBalanceChange(method f, env e)
  60. filtered { f -> f.selector != safeTransferFrom(address, address, uint256, uint256, bytes).selector
  61. && f.selector != safeBatchTransferFrom(address, address, uint256[], uint256[], bytes).selector
  62. && f.selector != mint(address, uint256, uint256, bytes).selector
  63. && f.selector != mintBatch(address, uint256[], uint256[], bytes).selector
  64. && f.selector != burn(address, uint256, uint256).selector
  65. && f.selector != burnBatch(address, uint256[], uint256[]).selector } {
  66. address from; uint256 id;
  67. uint256 balanceBefore = balanceOf(from, id);
  68. calldataarg args;
  69. f(e, args);
  70. uint256 balanceAfter = balanceOf(from, id);
  71. assert balanceBefore == balanceAfter, "How you dare to take my money?";
  72. }
  73. // STATUS - verified
  74. // Check that `balanceOf()` revertes in planned scenarios and no more (only if `account` is 0)
  75. rule balanceOfRevertCases(env e){
  76. address account; uint256 id;
  77. balanceOf@withrevert(account, id);
  78. assert lastReverted => account == 0, "Houston, we have a problem";
  79. }
  80. // STATUS - verified
  81. // Check that `balanceOfBatch()` revertes in planned scenarios and no more (only if at least one of `account`s is 0)
  82. rule balanceOfBatchRevertCases(env e){
  83. address[] accounts; uint256[] ids;
  84. address account1; address account2; address account3;
  85. uint256 id1; uint256 id2; uint256 id3;
  86. require accounts.length == 3;
  87. require ids.length == 3;
  88. require accounts[0] == account1; require accounts[1] == account2; require accounts[2] == account3;
  89. balanceOfBatch@withrevert(accounts, ids);
  90. assert lastReverted => (account1 == 0 || account2 == 0 || account3 == 0), "Houston, we have a problem";
  91. }
  92. /////////////////////////////////////////////////
  93. // Transfer (13/13)
  94. /////////////////////////////////////////////////
  95. // STATUS - verified
  96. // transfer additivity
  97. rule transferAdditivity(env e){
  98. address from; address to; uint256 id; bytes data;
  99. uint256 amount; uint256 amount1; uint256 amount2;
  100. require amount == amount1 + amount2;
  101. storage initialStorage = lastStorage;
  102. safeTransferFrom(e, from, to, id, amount, data);
  103. uint256 balanceAfterSingleTransaction = balanceOf(to, id);
  104. safeTransferFrom(e, from, to, id, amount1, data) at initialStorage;
  105. safeTransferFrom(e, from, to, id, amount2, data);
  106. uint256 balanceAfterDoubleTransaction = balanceOf(to, id);
  107. assert balanceAfterSingleTransaction == balanceAfterDoubleTransaction, "Not additive";
  108. }
  109. // STATUS - verified
  110. // safeTransferFrom updates `from` and `to` balances
  111. rule transferCorrectness(env e){
  112. address from; address to; uint256 id; uint256 amount; bytes data;
  113. require to != from;
  114. uint256 fromBalanceBefore = balanceOf(from, id);
  115. uint256 toBalanceBefore = balanceOf(to, id);
  116. safeTransferFrom(e, from, to, id, amount, data);
  117. uint256 fromBalanceAfter = balanceOf(from, id);
  118. uint256 toBalanceAfter = balanceOf(to, id);
  119. assert fromBalanceBefore == fromBalanceAfter + amount, "Something wet wrong";
  120. assert toBalanceBefore == toBalanceAfter - amount, "Something wet wrong";
  121. }
  122. // STATUS - verified
  123. // safeBatchTransferFrom updates `from` and `to` balances)
  124. rule transferBatchCorrectness(env e){
  125. address from; address to; uint256[] ids; uint256[] amounts; bytes data;
  126. uint256 idToCheck1; uint256 amountToCheck1;
  127. uint256 idToCheck2; uint256 amountToCheck2;
  128. uint256 idToCheck3; uint256 amountToCheck3;
  129. require to != from;
  130. require idToCheck1 != idToCheck2 && idToCheck3 != idToCheck2 && idToCheck1 != idToCheck3;
  131. require ids.length == 3;
  132. require amounts.length == 3;
  133. require ids[0] == idToCheck1; require amounts[0] == amountToCheck1;
  134. require ids[1] == idToCheck2; require amounts[1] == amountToCheck2;
  135. require ids[2] == idToCheck3; require amounts[2] == amountToCheck3;
  136. uint256 fromBalanceBefore1 = balanceOf(from, idToCheck1);
  137. uint256 fromBalanceBefore2 = balanceOf(from, idToCheck2);
  138. uint256 fromBalanceBefore3 = balanceOf(from, idToCheck3);
  139. uint256 toBalanceBefore1 = balanceOf(to, idToCheck1);
  140. uint256 toBalanceBefore2 = balanceOf(to, idToCheck2);
  141. uint256 toBalanceBefore3 = balanceOf(to, idToCheck3);
  142. safeBatchTransferFrom(e, from, to, ids, amounts, data);
  143. uint256 fromBalanceAfter1 = balanceOf(from, idToCheck1);
  144. uint256 fromBalanceAfter2 = balanceOf(from, idToCheck2);
  145. uint256 fromBalanceAfter3 = balanceOf(from, idToCheck3);
  146. uint256 toBalanceAfter1 = balanceOf(to, idToCheck1);
  147. uint256 toBalanceAfter2 = balanceOf(to, idToCheck2);
  148. uint256 toBalanceAfter3 = balanceOf(to, idToCheck3);
  149. assert (fromBalanceBefore1 == fromBalanceAfter1 + amountToCheck1)
  150. && (fromBalanceBefore2 == fromBalanceAfter2 + amountToCheck2)
  151. && (fromBalanceBefore3 == fromBalanceAfter3 + amountToCheck3), "Something wet wrong";
  152. assert (toBalanceBefore1 == toBalanceAfter1 - amountToCheck1)
  153. && (toBalanceBefore2 == toBalanceAfter2 - amountToCheck2)
  154. && (toBalanceBefore3 == toBalanceAfter3 - amountToCheck3), "Something wet wrong";
  155. }
  156. // STATUS - verified
  157. // cannot transfer more than `from` balance (safeTransferFrom version)
  158. rule cannotTransferMoreSingle(env e){
  159. address from; address to; uint256 id; uint256 amount; bytes data;
  160. uint256 balanceBefore = balanceOf(from, id);
  161. safeTransferFrom@withrevert(e, from, to, id, amount, data);
  162. assert amount > balanceBefore => lastReverted, "Achtung! Scammer!";
  163. }
  164. // STATUS - verified
  165. // cannot transfer more than allowed (safeBatchTransferFrom version)
  166. rule cannotTransferMoreBatch(env e){
  167. address from; address to; uint256[] ids; uint256[] amounts; bytes data;
  168. uint256 idToCheck1; uint256 amountToCheck1;
  169. uint256 idToCheck2; uint256 amountToCheck2;
  170. uint256 idToCheck3; uint256 amountToCheck3;
  171. uint256 balanceBefore1 = balanceOf(from, idToCheck1);
  172. uint256 balanceBefore2 = balanceOf(from, idToCheck2);
  173. uint256 balanceBefore3 = balanceOf(from, idToCheck3);
  174. require ids.length == 3;
  175. require amounts.length == 3;
  176. require ids[0] == idToCheck1; require amounts[0] == amountToCheck1;
  177. require ids[1] == idToCheck2; require amounts[1] == amountToCheck2;
  178. require ids[2] == idToCheck3; require amounts[2] == amountToCheck3;
  179. safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data);
  180. assert (amountToCheck1 > balanceBefore1 || amountToCheck2 > balanceBefore2 || amountToCheck3 > balanceBefore3) => lastReverted, "Achtung! Scammer!";
  181. }
  182. // STATUS - verified
  183. // Sender calling safeTransferFrom should only reduce 'from' balance and not other's if sending amount is greater than 0
  184. rule transferBalanceReduceEffect(env e){
  185. address from; address to; address other;
  186. uint256 id; uint256 amount;
  187. bytes data;
  188. require other != to;
  189. uint256 otherBalanceBefore = balanceOf(other, id);
  190. safeTransferFrom(e, from, to, id, amount, data);
  191. uint256 otherBalanceAfter = balanceOf(other, id);
  192. assert from != other => otherBalanceBefore == otherBalanceAfter, "Don't touch my money!";
  193. }
  194. // STATUS - verified
  195. // Sender calling safeTransferFrom should only increase 'to' balance and not other's if sending amount is greater than 0
  196. rule transferBalanceIncreaseEffect(env e){
  197. address from; address to; address other;
  198. uint256 id; uint256 amount;
  199. bytes data;
  200. require from != other;
  201. uint256 otherBalanceBefore = balanceOf(other, id);
  202. safeTransferFrom(e, from, to, id, amount, data);
  203. uint256 otherBalanceAfter = balanceOf(other, id);
  204. assert other != to => otherBalanceBefore == otherBalanceAfter, "Don't touch my money!";
  205. }
  206. // STATUS - verified
  207. // Sender calling safeTransferFrom should only reduce 'from' balance and not other's if sending amount is greater than 0
  208. rule transferBatchBalanceFromEffect(env e){
  209. address from; address to; address other;
  210. uint256[] ids; uint256[] amounts;
  211. uint256 id1; uint256 amount1; uint256 id2; uint256 amount2; uint256 id3; uint256 amount3;
  212. bytes data;
  213. require other != to;
  214. uint256 otherBalanceBefore1 = balanceOf(other, id1);
  215. uint256 otherBalanceBefore2 = balanceOf(other, id2);
  216. uint256 otherBalanceBefore3 = balanceOf(other, id3);
  217. safeBatchTransferFrom(e, from, to, ids, amounts, data);
  218. uint256 otherBalanceAfter1 = balanceOf(other, id1);
  219. uint256 otherBalanceAfter2 = balanceOf(other, id2);
  220. uint256 otherBalanceAfter3 = balanceOf(other, id3);
  221. assert from != other => (otherBalanceBefore1 == otherBalanceAfter1
  222. && otherBalanceBefore2 == otherBalanceAfter2
  223. && otherBalanceBefore3 == otherBalanceAfter3), "Don't touch my money!";
  224. }
  225. // STATUS - verified
  226. // Sender calling safeBatchTransferFrom should only increase 'to' balance and not other's if sending amount is greater than 0
  227. rule transferBatchBalanceToEffect(env e){
  228. address from; address to; address other;
  229. uint256[] ids; uint256[] amounts;
  230. uint256 id1; uint256 amount1; uint256 id2; uint256 amount2; uint256 id3; uint256 amount3;
  231. bytes data;
  232. require other != from;
  233. uint256 otherBalanceBefore1 = balanceOf(other, id1);
  234. uint256 otherBalanceBefore2 = balanceOf(other, id2);
  235. uint256 otherBalanceBefore3 = balanceOf(other, id3);
  236. safeBatchTransferFrom(e, from, to, ids, amounts, data);
  237. uint256 otherBalanceAfter1 = balanceOf(other, id1);
  238. uint256 otherBalanceAfter2 = balanceOf(other, id2);
  239. uint256 otherBalanceAfter3 = balanceOf(other, id3);
  240. assert other != to => (otherBalanceBefore1 == otherBalanceAfter1
  241. && otherBalanceBefore2 == otherBalanceAfter2
  242. && otherBalanceBefore3 == otherBalanceAfter3), "Don't touch my money!";
  243. }
  244. // STATUS - verified
  245. // cannot transfer without approval (safeTransferFrom version)
  246. rule noTransferForNotApproved(env e) {
  247. address from; address operator;
  248. address to; uint256 id; uint256 amount; bytes data;
  249. require from != e.msg.sender;
  250. bool approve = isApprovedForAll(from, e.msg.sender);
  251. safeTransferFrom@withrevert(e, from, to, id, amount, data);
  252. assert !approve => lastReverted, "You don't have king's approval!";
  253. }
  254. // STATUS - verified
  255. // cannot transfer without approval (safeBatchTransferFrom version)
  256. rule noTransferBatchForNotApproved(env e) {
  257. address from; address operator; address to;
  258. bytes data;
  259. uint256[] ids; uint256[] amounts;
  260. require from != e.msg.sender;
  261. bool approve = isApprovedForAll(from, e.msg.sender);
  262. safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data);
  263. assert !approve => lastReverted, "You don't have king's approval!";
  264. }
  265. // STATUS - verified
  266. // safeTransferFrom doesn't affect any approval
  267. rule noTransferEffectOnApproval(env e){
  268. address from; address to;
  269. address owner; address operator;
  270. uint256 id; uint256 amount;
  271. bytes data;
  272. bool approveBefore = isApprovedForAll(owner, operator);
  273. safeTransferFrom(e, from, to, id, amount, data);
  274. bool approveAfter = isApprovedForAll(owner, operator);
  275. assert approveBefore == approveAfter, "Something was effected";
  276. }
  277. // STATUS - verified
  278. // safeTransferFrom doesn't affect any approval
  279. rule noTransferBatchEffectOnApproval(env e){
  280. address from; address to;
  281. address owner; address operator;
  282. uint256[] ids; uint256[] amounts;
  283. bytes data;
  284. bool approveBefore = isApprovedForAll(owner, operator);
  285. safeBatchTransferFrom(e, from, to, ids, amounts, data);
  286. bool approveAfter = isApprovedForAll(owner, operator);
  287. assert approveBefore == approveAfter, "Something was effected";
  288. }
  289. /////////////////////////////////////////////////
  290. // Mint (7/9)
  291. /////////////////////////////////////////////////
  292. // STATUS - verified
  293. // Additivity of mint: mint(a); mint(b) has same effect as mint(a+b)
  294. rule mintAdditivity(env e){
  295. address to; uint256 id; uint256 amount; uint256 amount1; uint256 amount2; bytes data;
  296. require amount == amount1 + amount2;
  297. storage initialStorage = lastStorage;
  298. mint(e, to, id, amount, data);
  299. uint256 balanceAfterSingleTransaction = balanceOf(to, id);
  300. mint(e, to, id, amount1, data) at initialStorage;
  301. mint(e, to, id, amount2, data);
  302. uint256 balanceAfterDoubleTransaction = balanceOf(to, id);
  303. assert balanceAfterSingleTransaction == balanceAfterDoubleTransaction, "Not additive";
  304. }
  305. // STATUS - verified
  306. // Check that `mint()` revertes in planned scenario(s) (only if `to` is 0)
  307. rule mintRevertCases(env e){
  308. address to; uint256 id; uint256 amount; bytes data;
  309. mint@withrevert(e, to, id, amount, data);
  310. assert to == 0 => lastReverted, "Should revert";
  311. }
  312. // STATUS - verified
  313. // Check that `mintBatch()` revertes in planned scenario(s) (only if `to` is 0 or arrays have different length)
  314. rule mintBatchRevertCases(env e){
  315. address to; uint256[] ids; uint256[] amounts; bytes data;
  316. require ids.length < 1000000000;
  317. require amounts.length < 1000000000;
  318. mintBatch@withrevert(e, to, ids, amounts, data);
  319. assert (ids.length != amounts.length || to == 0) => lastReverted, "Should revert";
  320. }
  321. // STATUS - verified
  322. // Check that mint updates `to` balance correctly
  323. rule mintCorrectWork(env e){
  324. address to; uint256 id; uint256 amount; bytes data;
  325. uint256 otherBalanceBefore = balanceOf(to, id);
  326. mint(e, to, id, amount, data);
  327. uint256 otherBalanceAfter = balanceOf(to, id);
  328. assert otherBalanceBefore == otherBalanceAfter - amount, "Something is wrong";
  329. }
  330. // STATUS - verified
  331. // check that mintBatch updates `bootcamp participantsfrom` balance correctly
  332. rule mintBatchCorrectWork(env e){
  333. address to;
  334. uint256 id1; uint256 id2; uint256 id3;
  335. uint256 amount1; uint256 amount2; uint256 amount3;
  336. uint256[] ids; uint256[] amounts;
  337. bytes data;
  338. require ids.length == 3;
  339. require amounts.length == 3;
  340. require id1 != id2 && id2 != id3 && id3 != id1;
  341. require ids[0] == id1; require ids[1] == id2; require ids[2] == id3;
  342. require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3;
  343. uint256 otherBalanceBefore1 = balanceOf(to, id1);
  344. uint256 otherBalanceBefore2 = balanceOf(to, id2);
  345. uint256 otherBalanceBefore3 = balanceOf(to, id3);
  346. mintBatch(e, to, ids, amounts, data);
  347. uint256 otherBalanceAfter1 = balanceOf(to, id1);
  348. uint256 otherBalanceAfter2 = balanceOf(to, id2);
  349. uint256 otherBalanceAfter3 = balanceOf(to, id3);
  350. assert otherBalanceBefore1 == otherBalanceAfter1 - amount1
  351. && otherBalanceBefore2 == otherBalanceAfter2 - amount2
  352. && otherBalanceBefore3 == otherBalanceAfter3 - amount3
  353. , "Something is wrong";
  354. }
  355. // STATUS - verified
  356. // The user cannot mint more than max_uint256
  357. rule cantMintMoreSingle(env e){
  358. address to; uint256 id; uint256 amount; bytes data;
  359. require to_mathint(balanceOf(to, id) + amount) > max_uint256;
  360. mint@withrevert(e, to, id, amount, data);
  361. assert lastReverted, "Don't be too greedy!";
  362. }
  363. // STATUS - verified
  364. // the user cannot mint more than max_uint256 (batch version)
  365. rule cantMintMoreBatch(env e){
  366. address to; bytes data;
  367. uint256 id1; uint256 id2; uint256 id3;
  368. uint256 amount1; uint256 amount2; uint256 amount3;
  369. uint256[] ids; uint256[] amounts;
  370. require ids.length == 3;
  371. require amounts.length == 3;
  372. require ids[0] == id1; require ids[1] == id2; require ids[2] == id3;
  373. require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3;
  374. require to_mathint(balanceOf(to, id1) + amount1) > max_uint256
  375. || to_mathint(balanceOf(to, id2) + amount2) > max_uint256
  376. || to_mathint(balanceOf(to, id3) + amount3) > max_uint256;
  377. mintBatch@withrevert(e, to, ids, amounts, data);
  378. assert lastReverted, "Don't be too greedy!";
  379. }
  380. // STATUS - verified
  381. // `mint()` changes only `to` balance
  382. rule cantMintOtherBalances(env e){
  383. address to; uint256 id; uint256 amount; bytes data;
  384. address other;
  385. uint256 otherBalanceBefore = balanceOf(other, id);
  386. mint(e, to, id, amount, data);
  387. uint256 otherBalanceAfter = balanceOf(other, id);
  388. assert other != to => otherBalanceBefore == otherBalanceAfter, "I like to see your money disappearing";
  389. }
  390. // STATUS - verified
  391. // mintBatch changes only `to` balance
  392. rule cantMintBatchOtherBalances(env e){
  393. address to;
  394. uint256 id1; uint256 id2; uint256 id3;
  395. uint256[] ids; uint256[] amounts;
  396. address other;
  397. bytes data;
  398. uint256 otherBalanceBefore1 = balanceOf(other, id1);
  399. uint256 otherBalanceBefore2 = balanceOf(other, id2);
  400. uint256 otherBalanceBefore3 = balanceOf(other, id3);
  401. mintBatch(e, to, ids, amounts, data);
  402. uint256 otherBalanceAfter1 = balanceOf(other, id1);
  403. uint256 otherBalanceAfter2 = balanceOf(other, id2);
  404. uint256 otherBalanceAfter3 = balanceOf(other, id3);
  405. assert other != to => (otherBalanceBefore1 == otherBalanceAfter1
  406. && otherBalanceBefore2 == otherBalanceAfter2
  407. && otherBalanceBefore3 == otherBalanceAfter3)
  408. , "I like to see your money disappearing";
  409. }
  410. /////////////////////////////////////////////////
  411. // Burn (9/9)
  412. /////////////////////////////////////////////////
  413. // STATUS - verified
  414. // Additivity of burn: burn(a); burn(b) has same effect as burn(a+b)
  415. rule burnAdditivity(env e){
  416. address from; uint256 id; uint256 amount; uint256 amount1; uint256 amount2;
  417. require amount == amount1 + amount2;
  418. storage initialStorage = lastStorage;
  419. burn(e, from, id, amount);
  420. uint256 balanceAfterSingleTransaction = balanceOf(from, id);
  421. burn(e, from, id, amount1) at initialStorage;
  422. burn(e, from, id, amount2);
  423. uint256 balanceAfterDoubleTransaction = balanceOf(from, id);
  424. assert balanceAfterSingleTransaction == balanceAfterDoubleTransaction, "Not additive";
  425. }
  426. // STATUS - verified
  427. // Check that `burn()` revertes in planned scenario(s) (if `from` is 0)
  428. rule burnRevertCases(env e){
  429. address from; uint256 id; uint256 amount;
  430. burn@withrevert(e, from, id, amount);
  431. assert from == 0 => lastReverted, "Should revert";
  432. }
  433. // STATUS - verified
  434. // Check that `balanceOf()` revertes in planned scenario(s) (if `from` is 0 or arrays have different length)
  435. rule burnBatchRevertCases(env e){
  436. address from; uint256[] ids; uint256[] amounts;
  437. require ids.length < 1000000000;
  438. require amounts.length < 1000000000;
  439. burnBatch@withrevert(e, from, ids, amounts);
  440. assert (from == 0 || ids.length != amounts.length) => lastReverted, "Should revert";
  441. }
  442. // STATUS - verified
  443. // check that burn updates `from` balance correctly
  444. rule burnCorrectWork(env e){
  445. address from; uint256 id; uint256 amount;
  446. uint256 otherBalanceBefore = balanceOf(from, id);
  447. burn(e, from, id, amount);
  448. uint256 otherBalanceAfter = balanceOf(from, id);
  449. assert otherBalanceBefore == otherBalanceAfter + amount, "Something is wrong";
  450. }
  451. // STATUS - verified
  452. // check that burnBatch updates `from` balance correctly
  453. rule burnBatchCorrectWork(env e){
  454. address from;
  455. uint256 id1; uint256 id2; uint256 id3;
  456. uint256 amount1; uint256 amount2; uint256 amount3;
  457. uint256[] ids; uint256[] amounts;
  458. require ids.length == 3;
  459. require id1 != id2 && id2 != id3 && id3 != id1;
  460. require ids[0] == id1; require ids[1] == id2; require ids[2] == id3;
  461. require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3;
  462. uint256 otherBalanceBefore1 = balanceOf(from, id1);
  463. uint256 otherBalanceBefore2 = balanceOf(from, id2);
  464. uint256 otherBalanceBefore3 = balanceOf(from, id3);
  465. burnBatch(e, from, ids, amounts);
  466. uint256 otherBalanceAfter1 = balanceOf(from, id1);
  467. uint256 otherBalanceAfter2 = balanceOf(from, id2);
  468. uint256 otherBalanceAfter3 = balanceOf(from, id3);
  469. assert otherBalanceBefore1 == otherBalanceAfter1 + amount1
  470. && otherBalanceBefore2 == otherBalanceAfter2 + amount2
  471. && otherBalanceBefore3 == otherBalanceAfter3 + amount3
  472. , "Something is wrong";
  473. }
  474. // STATUS - verified
  475. // the user cannot burn more than they have
  476. rule cantBurnMoreSingle(env e){
  477. address from; uint256 id; uint256 amount;
  478. require to_mathint(balanceOf(from, id) - amount) < 0;
  479. burn@withrevert(e, from, id, amount);
  480. assert lastReverted, "Don't be too greedy!";
  481. }
  482. // STATUS - verified
  483. // the user cannot burn more than they have (batch version)
  484. rule cantBurnMoreBatch(env e){
  485. address from;
  486. uint256 id1; uint256 id2; uint256 id3;
  487. uint256 amount1; uint256 amount2; uint256 amount3;
  488. uint256[] ids; uint256[] amounts;
  489. require ids.length == 3;
  490. require ids[0] == id1; require ids[1] == id2; require ids[2] == id3;
  491. require amounts[0] == amount1; require amounts[1] == amount2; require amounts[2] == amount3;
  492. require to_mathint(balanceOf(from, id1) - amount1) < 0
  493. || to_mathint(balanceOf(from, id2) - amount2) < 0
  494. || to_mathint(balanceOf(from, id3) - amount3) < 0 ;
  495. burnBatch@withrevert(e, from, ids, amounts);
  496. assert lastReverted, "Don't be too greedy!";
  497. }
  498. // STATUS - verified
  499. // burn changes only `from` balance
  500. rule cantBurnOtherBalances(env e){
  501. address from; uint256 id; uint256 amount;
  502. address other;
  503. uint256 otherBalanceBefore = balanceOf(other, id);
  504. burn(e, from, id, amount);
  505. uint256 otherBalanceAfter = balanceOf(other, id);
  506. assert other != from => otherBalanceBefore == otherBalanceAfter, "I like to see your money disappearing";
  507. }
  508. // STATUS - verified
  509. // burnBatch changes only `from` balance
  510. rule cantBurnBatchOtherBalances(env e){
  511. address from;
  512. uint256 id1; uint256 id2; uint256 id3;
  513. uint256 amount1; uint256 amount2; uint256 amount3;
  514. uint256[] ids; uint256[] amounts;
  515. address other;
  516. uint256 otherBalanceBefore1 = balanceOf(other, id1);
  517. uint256 otherBalanceBefore2 = balanceOf(other, id2);
  518. uint256 otherBalanceBefore3 = balanceOf(other, id3);
  519. burnBatch(e, from, ids, amounts);
  520. uint256 otherBalanceAfter1 = balanceOf(other, id1);
  521. uint256 otherBalanceAfter2 = balanceOf(other, id2);
  522. uint256 otherBalanceAfter3 = balanceOf(other, id3);
  523. assert other != from => (otherBalanceBefore1 == otherBalanceAfter1
  524. && otherBalanceBefore2 == otherBalanceAfter2
  525. && otherBalanceBefore3 == otherBalanceAfter3)
  526. , "I like to see your money disappearing";
  527. }
  528. /////////////////////////////////////////////////
  529. // The rules below were added to this base ERC1155 spec as part of a later
  530. // project with OpenZeppelin covering various ERC1155 extensions.
  531. /////////////////////////////////////////////////
  532. /// The result of transferring a single token must be equivalent whether done
  533. /// via safeTransferFrom or safeBatchTransferFrom.
  534. rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence {
  535. storage beforeTransfer = lastStorage;
  536. env e;
  537. address holder; address recipient;
  538. uint256 token; uint256 transferAmount; bytes data;
  539. uint256[] tokens; uint256[] transferAmounts;
  540. mathint holderStartingBalance = balanceOf(holder, token);
  541. mathint recipientStartingBalance = balanceOf(recipient, token);
  542. require tokens.length == 1; require transferAmounts.length == 1;
  543. require tokens[0] == token; require transferAmounts[0] == transferAmount;
  544. // transferring via safeTransferFrom
  545. safeTransferFrom(e, holder, recipient, token, transferAmount, data) at beforeTransfer;
  546. mathint holderSafeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token);
  547. mathint recipientSafeTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance;
  548. // transferring via safeBatchTransferFrom
  549. safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer;
  550. mathint holderSafeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token);
  551. mathint recipientSafeBatchTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance;
  552. assert holderSafeTransferFromBalanceChange == holderSafeBatchTransferFromBalanceChange
  553. && recipientSafeTransferFromBalanceChange == recipientSafeBatchTransferFromBalanceChange,
  554. "Transferring a single token via safeTransferFrom or safeBatchTransferFrom must be equivalent";
  555. }
  556. /// The results of transferring multiple tokens must be equivalent whether done
  557. /// separately via safeTransferFrom or together via safeBatchTransferFrom.
  558. rule multipleTokenSafeTransferFromSafeBatchTransferFromEquivalence {
  559. storage beforeTransfers = lastStorage;
  560. env e;
  561. address holder; address recipient; bytes data;
  562. uint256 tokenA; uint256 tokenB; uint256 tokenC;
  563. uint256 transferAmountA; uint256 transferAmountB; uint256 transferAmountC;
  564. uint256[] tokens; uint256[] transferAmounts;
  565. mathint holderStartingBalanceA = balanceOf(holder, tokenA);
  566. mathint holderStartingBalanceB = balanceOf(holder, tokenB);
  567. mathint holderStartingBalanceC = balanceOf(holder, tokenC);
  568. mathint recipientStartingBalanceA = balanceOf(recipient, tokenA);
  569. mathint recipientStartingBalanceB = balanceOf(recipient, tokenB);
  570. mathint recipientStartingBalanceC = balanceOf(recipient, tokenC);
  571. require tokens.length == 3; require transferAmounts.length == 3;
  572. require tokens[0] == tokenA; require transferAmounts[0] == transferAmountA;
  573. require tokens[1] == tokenB; require transferAmounts[1] == transferAmountB;
  574. require tokens[2] == tokenC; require transferAmounts[2] == transferAmountC;
  575. // transferring via safeTransferFrom
  576. safeTransferFrom(e, holder, recipient, tokenA, transferAmountA, data) at beforeTransfers;
  577. safeTransferFrom(e, holder, recipient, tokenB, transferAmountB, data);
  578. safeTransferFrom(e, holder, recipient, tokenC, transferAmountC, data);
  579. mathint holderSafeTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA);
  580. mathint holderSafeTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB);
  581. mathint holderSafeTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC);
  582. mathint recipientSafeTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA;
  583. mathint recipientSafeTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB;
  584. mathint recipientSafeTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC;
  585. // transferring via safeBatchTransferFrom
  586. safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfers;
  587. mathint holderSafeBatchTransferFromBalanceChangeA = holderStartingBalanceA - balanceOf(holder, tokenA);
  588. mathint holderSafeBatchTransferFromBalanceChangeB = holderStartingBalanceB - balanceOf(holder, tokenB);
  589. mathint holderSafeBatchTransferFromBalanceChangeC = holderStartingBalanceC - balanceOf(holder, tokenC);
  590. mathint recipientSafeBatchTransferFromBalanceChangeA = balanceOf(recipient, tokenA) - recipientStartingBalanceA;
  591. mathint recipientSafeBatchTransferFromBalanceChangeB = balanceOf(recipient, tokenB) - recipientStartingBalanceB;
  592. mathint recipientSafeBatchTransferFromBalanceChangeC = balanceOf(recipient, tokenC) - recipientStartingBalanceC;
  593. assert holderSafeTransferFromBalanceChangeA == holderSafeBatchTransferFromBalanceChangeA
  594. && holderSafeTransferFromBalanceChangeB == holderSafeBatchTransferFromBalanceChangeB
  595. && holderSafeTransferFromBalanceChangeC == holderSafeBatchTransferFromBalanceChangeC
  596. && recipientSafeTransferFromBalanceChangeA == recipientSafeBatchTransferFromBalanceChangeA
  597. && recipientSafeTransferFromBalanceChangeB == recipientSafeBatchTransferFromBalanceChangeB
  598. && recipientSafeTransferFromBalanceChangeC == recipientSafeBatchTransferFromBalanceChangeC,
  599. "Transferring multiple tokens via safeTransferFrom or safeBatchTransferFrom must be equivalent";
  600. }
  601. /// If transfer methods do not revert, the input arrays must be the same length.
  602. rule transfersHaveSameLengthInputArrays {
  603. env e;
  604. address recipient; bytes data;
  605. uint256[] tokens; uint256[] transferAmounts;
  606. uint max_int = 0xffffffffffffffffffffffffffffffff;
  607. require tokens.length >= 0 && tokens.length <= max_int;
  608. require transferAmounts.length >= 0 && transferAmounts.length <= max_int;
  609. safeBatchTransferFrom(e, _, recipient, tokens, transferAmounts, data);
  610. uint256 tokensLength = tokens.length;
  611. uint256 transferAmountsLength = transferAmounts.length;
  612. assert tokens.length == transferAmounts.length,
  613. "If transfer methods do not revert, the input arrays must be the same length";
  614. }