123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- import "helpers.spec"
- import "methods/IERC721.spec"
- methods {
- // exposed for FV
- mint(address,uint256)
- safeMint(address,uint256)
- safeMint(address,uint256,bytes)
- burn(uint256)
- tokenExists(uint256) returns (bool) envfree
- unsafeOwnerOf(uint256) returns (address) envfree
- unsafeGetApproved(uint256) returns (address) envfree
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Helpers โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- // Could be broken in theory, but not in practice
- function balanceLimited(address account) returns bool {
- return balanceOf(account) < max_uint256;
- }
- function helperTransferWithRevert(env e, method f, address from, address to, uint256 tokenId) {
- if (f.selector == transferFrom(address,address,uint256).selector) {
- transferFrom@withrevert(e, from, to, tokenId);
- } else if (f.selector == safeTransferFrom(address,address,uint256).selector) {
- safeTransferFrom@withrevert(e, from, to, tokenId);
- } else if (f.selector == safeTransferFrom(address,address,uint256,bytes).selector) {
- bytes params;
- require params.length < 0xffff;
- safeTransferFrom@withrevert(e, from, to, tokenId, params);
- } else {
- calldataarg args;
- f@withrevert(e, args);
- }
- }
- function helperMintWithRevert(env e, method f, address to, uint256 tokenId) {
- if (f.selector == mint(address,uint256).selector) {
- mint@withrevert(e, to, tokenId);
- } else if (f.selector == safeMint(address,uint256).selector) {
- safeMint@withrevert(e, to, tokenId);
- } else if (f.selector == safeMint(address,uint256,bytes).selector) {
- bytes params;
- require params.length < 0xffff;
- safeMint@withrevert(e, to, tokenId, params);
- } else {
- require false;
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Ghost & hooks: ownership count โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- ghost ownedTotal() returns uint256 {
- init_state axiom ownedTotal() == 0;
- }
- ghost mapping(address => uint256) ownedByUser {
- init_state axiom forall address a. ownedByUser[a] == 0;
- }
- hook Sstore _owners[KEY uint256 tokenId] address newOwner (address oldOwner) STORAGE {
- ownedByUser[newOwner] = ownedByUser[newOwner] + to_uint256(newOwner != 0 ? 1 : 0);
- ownedByUser[oldOwner] = ownedByUser[oldOwner] - to_uint256(oldOwner != 0 ? 1 : 0);
- havoc ownedTotal assuming ownedTotal@new() == ownedTotal@old()
- + to_uint256(newOwner != 0 ? 1 : 0)
- - to_uint256(oldOwner != 0 ? 1 : 0);
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Ghost & hooks: sum of all balances โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- ghost sumOfBalances() returns uint256 {
- init_state axiom sumOfBalances() == 0;
- }
- hook Sstore _balances[KEY address addr] uint256 newValue (uint256 oldValue) STORAGE {
- havoc sumOfBalances assuming sumOfBalances@new() == sumOfBalances@old() + newValue - oldValue;
- }
- ghost mapping(address => uint256) ghostBalanceOf {
- init_state axiom forall address a. ghostBalanceOf[a] == 0;
- }
- hook Sload uint256 value _balances[KEY address user] STORAGE {
- require ghostBalanceOf[user] == value;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: ownedTotal is the sum of all balances โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant ownedTotalIsSumOfBalances()
- ownedTotal() == sumOfBalances()
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: balanceOf is the number of tokens owned โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant balanceOfConsistency(address user)
- balanceOf(user) == ownedByUser[user] &&
- balanceOf(user) == ghostBalanceOf[user]
- {
- preserved {
- require balanceLimited(user);
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: owner of a token must have some balance โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant ownerHasBalance(uint256 tokenId)
- balanceOf(ownerOf(tokenId)) > 0
- {
- preserved {
- requireInvariant balanceOfConsistency(ownerOf(tokenId));
- require balanceLimited(ownerOf(tokenId));
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: tokens that do not exist are not owned and not approved โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant notMintedUnset(uint256 tokenId)
- (!tokenExists(tokenId) <=> unsafeOwnerOf(tokenId) == 0) &&
- (!tokenExists(tokenId) => unsafeGetApproved(tokenId) == 0)
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: ownerOf and getApproved revert if token does not exist โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule notMintedRevert(uint256 tokenId) {
- requireInvariant notMintedUnset(tokenId);
- bool e = tokenExists(tokenId);
- address owner = ownerOf@withrevert(tokenId);
- assert e <=> !lastReverted;
- assert e => owner == unsafeOwnerOf(tokenId); // notMintedUnset tells us this is non-zero
- address approved = getApproved@withrevert(tokenId);
- assert e <=> !lastReverted;
- assert e => approved == unsafeGetApproved(tokenId);
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: unsafeOwnerOf and unsafeGetApproved don't revert โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule unsafeDontRevert(uint256 tokenId) {
- unsafeOwnerOf@withrevert(tokenId);
- assert !lastReverted;
- unsafeGetApproved@withrevert(tokenId);
- assert !lastReverted;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: balance of address(0) is 0 โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule zeroAddressBalanceRevert() {
- balanceOf@withrevert(0);
- assert lastReverted;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: total supply can only change through mint and burn โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule supplyChange(env e) {
- uint256 supplyBefore = ownedTotal();
- method f; calldataarg args; f(e, args);
- uint256 supplyAfter = ownedTotal();
- assert supplyAfter > supplyBefore => (
- supplyAfter == supplyBefore + 1 &&
- (
- f.selector == mint(address,uint256).selector ||
- f.selector == safeMint(address,uint256).selector ||
- f.selector == safeMint(address,uint256,bytes).selector
- )
- );
- assert supplyAfter < supplyBefore => (
- supplyAfter == supplyBefore - 1 &&
- f.selector == burn(uint256).selector
- );
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: balanceOf can only change through mint, burn or transfers. balanceOf cannot change by more than 1. โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule balanceChange(env e, address account) {
- requireInvariant balanceOfConsistency(account);
- require balanceLimited(account);
- uint256 balanceBefore = balanceOf(account);
- method f; calldataarg args; f(e, args);
- uint256 balanceAfter = balanceOf(account);
- // balance can change by at most 1
- assert balanceBefore != balanceAfter => (
- balanceAfter == balanceBefore - 1 ||
- balanceAfter == balanceBefore + 1
- );
- // only selected function can change balances
- assert balanceBefore != balanceAfter => (
- f.selector == transferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256,bytes).selector ||
- f.selector == mint(address,uint256).selector ||
- f.selector == safeMint(address,uint256).selector ||
- f.selector == safeMint(address,uint256,bytes).selector ||
- f.selector == burn(uint256).selector
- );
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: ownership can only change through mint, burn or transfers. โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule ownershipChange(env e, uint256 tokenId) {
- address ownerBefore = unsafeOwnerOf(tokenId);
- method f; calldataarg args; f(e, args);
- address ownerAfter = unsafeOwnerOf(tokenId);
- assert ownerBefore == 0 && ownerAfter != 0 => (
- f.selector == mint(address,uint256).selector ||
- f.selector == safeMint(address,uint256).selector ||
- f.selector == safeMint(address,uint256,bytes).selector
- );
- assert ownerBefore != 0 && ownerAfter == 0 => (
- f.selector == burn(uint256).selector
- );
- assert (ownerBefore != ownerAfter && ownerBefore != 0 && ownerAfter != 0) => (
- f.selector == transferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256,bytes).selector
- );
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: token approval can only change through approve or transfers (implicitly). โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule approvalChange(env e, uint256 tokenId) {
- address approvalBefore = unsafeGetApproved(tokenId);
- method f; calldataarg args; f(e, args);
- address approvalAfter = unsafeGetApproved(tokenId);
- // approve can set any value, other functions reset
- assert approvalBefore != approvalAfter => (
- f.selector == approve(address,uint256).selector ||
- (
- (
- f.selector == transferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256,bytes).selector ||
- f.selector == burn(uint256).selector
- ) && approvalAfter == 0
- )
- );
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: approval for all tokens can only change through isApprovedForAll. โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule approvedForAllChange(env e, address owner, address spender) {
- bool approvedForAllBefore = isApprovedForAll(owner, spender);
- method f; calldataarg args; f(e, args);
- bool approvedForAllAfter = isApprovedForAll(owner, spender);
- assert approvedForAllBefore != approvedForAllAfter => f.selector == setApprovalForAll(address,bool).selector;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: transferFrom behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule transferFrom(env e, address from, address to, uint256 tokenId) {
- require nonpayable(e);
- address operator = e.msg.sender;
- uint256 otherTokenId;
- address otherAccount;
- requireInvariant ownerHasBalance(tokenId);
- require balanceLimited(to);
- uint256 balanceOfFromBefore = balanceOf(from);
- uint256 balanceOfToBefore = balanceOf(to);
- uint256 balanceOfOtherBefore = balanceOf(otherAccount);
- address ownerBefore = unsafeOwnerOf(tokenId);
- address otherOwnerBefore = unsafeOwnerOf(otherTokenId);
- address approvalBefore = unsafeGetApproved(tokenId);
- address otherApprovalBefore = unsafeGetApproved(otherTokenId);
- transferFrom@withrevert(e, from, to, tokenId);
- bool success = !lastReverted;
- // liveness
- assert success <=> (
- from == ownerBefore &&
- from != 0 &&
- to != 0 &&
- (operator == from || operator == approvalBefore || isApprovedForAll(ownerBefore, operator))
- );
- // effect
- assert success => (
- balanceOf(from) == balanceOfFromBefore - to_uint256(from != to ? 1 : 0) &&
- balanceOf(to) == balanceOfToBefore + to_uint256(from != to ? 1 : 0) &&
- unsafeOwnerOf(tokenId) == to &&
- unsafeGetApproved(tokenId) == 0
- );
- // no side effect
- assert balanceOf(otherAccount) != balanceOfOtherBefore => (otherAccount == from || otherAccount == to);
- assert unsafeOwnerOf(otherTokenId) != otherOwnerBefore => otherTokenId == tokenId;
- assert unsafeGetApproved(otherTokenId) != otherApprovalBefore => otherTokenId == tokenId;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: safeTransferFrom behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule safeTransferFrom(env e, method f, address from, address to, uint256 tokenId) filtered { f ->
- f.selector == safeTransferFrom(address,address,uint256).selector ||
- f.selector == safeTransferFrom(address,address,uint256,bytes).selector
- } {
- require nonpayable(e);
- address operator = e.msg.sender;
- uint256 otherTokenId;
- address otherAccount;
- requireInvariant ownerHasBalance(tokenId);
- require balanceLimited(to);
- uint256 balanceOfFromBefore = balanceOf(from);
- uint256 balanceOfToBefore = balanceOf(to);
- uint256 balanceOfOtherBefore = balanceOf(otherAccount);
- address ownerBefore = unsafeOwnerOf(tokenId);
- address otherOwnerBefore = unsafeOwnerOf(otherTokenId);
- address approvalBefore = unsafeGetApproved(tokenId);
- address otherApprovalBefore = unsafeGetApproved(otherTokenId);
- helperTransferWithRevert(e, f, from, to, tokenId);
- bool success = !lastReverted;
- assert success <=> (
- from == ownerBefore &&
- from != 0 &&
- to != 0 &&
- (operator == from || operator == approvalBefore || isApprovedForAll(ownerBefore, operator))
- );
- // effect
- assert success => (
- balanceOf(from) == balanceOfFromBefore - to_uint256(from != to ? 1: 0) &&
- balanceOf(to) == balanceOfToBefore + to_uint256(from != to ? 1: 0) &&
- unsafeOwnerOf(tokenId) == to &&
- unsafeGetApproved(tokenId) == 0
- );
- // no side effect
- assert balanceOf(otherAccount) != balanceOfOtherBefore => (otherAccount == from || otherAccount == to);
- assert unsafeOwnerOf(otherTokenId) != otherOwnerBefore => otherTokenId == tokenId;
- assert unsafeGetApproved(otherTokenId) != otherApprovalBefore => otherTokenId == tokenId;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: mint behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule mint(env e, address to, uint256 tokenId) {
- require nonpayable(e);
- requireInvariant notMintedUnset(tokenId);
- uint256 otherTokenId;
- address otherAccount;
- require balanceLimited(to);
- uint256 supplyBefore = ownedTotal();
- uint256 balanceOfToBefore = balanceOf(to);
- uint256 balanceOfOtherBefore = balanceOf(otherAccount);
- address ownerBefore = unsafeOwnerOf(tokenId);
- address otherOwnerBefore = unsafeOwnerOf(otherTokenId);
- mint@withrevert(e, to, tokenId);
- bool success = !lastReverted;
- // liveness
- assert success <=> (
- ownerBefore == 0 &&
- to != 0
- );
- // effect
- assert success => (
- ownedTotal() == supplyBefore + 1 &&
- balanceOf(to) == balanceOfToBefore + 1 &&
- unsafeOwnerOf(tokenId) == to
- );
- // no side effect
- assert balanceOf(otherAccount) != balanceOfOtherBefore => otherAccount == to;
- assert unsafeOwnerOf(otherTokenId) != otherOwnerBefore => otherTokenId == tokenId;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: safeMint behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule safeMint(env e, method f, address to, uint256 tokenId) filtered { f ->
- f.selector == safeMint(address,uint256).selector ||
- f.selector == safeMint(address,uint256,bytes).selector
- } {
- require nonpayable(e);
- requireInvariant notMintedUnset(tokenId);
- uint256 otherTokenId;
- address otherAccount;
- require balanceLimited(to);
- uint256 supplyBefore = ownedTotal();
- uint256 balanceOfToBefore = balanceOf(to);
- uint256 balanceOfOtherBefore = balanceOf(otherAccount);
- address ownerBefore = unsafeOwnerOf(tokenId);
- address otherOwnerBefore = unsafeOwnerOf(otherTokenId);
- helperMintWithRevert(e, f, to, tokenId);
- bool success = !lastReverted;
- assert success <=> (
- ownerBefore == 0 &&
- to != 0
- );
- // effect
- assert success => (
- ownedTotal() == supplyBefore + 1 &&
- balanceOf(to) == balanceOfToBefore + 1 &&
- unsafeOwnerOf(tokenId) == to
- );
- // no side effect
- assert balanceOf(otherAccount) != balanceOfOtherBefore => otherAccount == to;
- assert unsafeOwnerOf(otherTokenId) != otherOwnerBefore => otherTokenId == tokenId;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: burn behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule burn(env e, uint256 tokenId) {
- require nonpayable(e);
- address from = unsafeOwnerOf(tokenId);
- uint256 otherTokenId;
- address otherAccount;
- requireInvariant ownerHasBalance(tokenId);
- uint256 supplyBefore = ownedTotal();
- uint256 balanceOfFromBefore = balanceOf(from);
- uint256 balanceOfOtherBefore = balanceOf(otherAccount);
- address ownerBefore = unsafeOwnerOf(tokenId);
- address otherOwnerBefore = unsafeOwnerOf(otherTokenId);
- address otherApprovalBefore = unsafeGetApproved(otherTokenId);
- burn@withrevert(e, tokenId);
- bool success = !lastReverted;
- // liveness
- assert success <=> (
- ownerBefore != 0
- );
- // effect
- assert success => (
- ownedTotal() == supplyBefore - 1 &&
- balanceOf(from) == balanceOfFromBefore - 1 &&
- unsafeOwnerOf(tokenId) == 0 &&
- unsafeGetApproved(tokenId) == 0
- );
- // no side effect
- assert balanceOf(otherAccount) != balanceOfOtherBefore => otherAccount == from;
- assert unsafeOwnerOf(otherTokenId) != otherOwnerBefore => otherTokenId == tokenId;
- assert unsafeGetApproved(otherTokenId) != otherApprovalBefore => otherTokenId == tokenId;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: approve behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule approve(env e, address spender, uint256 tokenId) {
- require nonpayable(e);
- address caller = e.msg.sender;
- address owner = unsafeOwnerOf(tokenId);
- uint256 otherTokenId;
- address otherApprovalBefore = unsafeGetApproved(otherTokenId);
- approve@withrevert(e, spender, tokenId);
- bool success = !lastReverted;
- // liveness
- assert success <=> (
- owner != 0 &&
- owner != spender &&
- (owner == caller || isApprovedForAll(owner, caller))
- );
- // effect
- assert success => unsafeGetApproved(tokenId) == spender;
- // no side effect
- assert unsafeGetApproved(otherTokenId) != otherApprovalBefore => otherTokenId == tokenId;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: setApprovalForAll behavior and side effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule setApprovalForAll(env e, address operator, bool approved) {
- require nonpayable(e);
- address owner = e.msg.sender;
- address otherOwner;
- address otherOperator;
- bool otherIsApprovedForAllBefore = isApprovedForAll(otherOwner, otherOperator);
- setApprovalForAll@withrevert(e, operator, approved);
- bool success = !lastReverted;
- // liveness
- assert success <=> owner != operator;
- // effect
- assert success => isApprovedForAll(owner, operator) == approved;
- // no side effect
- assert isApprovedForAll(otherOwner, otherOperator) != otherIsApprovedForAllBefore => (
- otherOwner == owner &&
- otherOperator == operator
- );
- }
|