using ERC20A as Token; methods { function Token.balanceOf(address) external returns (uint256) envfree; function Token.totalSupply() external returns (uint256) envfree; function publicPeriodEnd(Periods.Period) external returns (uint256) envfree; } /*-------------------------------------------- | Ghosts and hooks | --------------------------------------------*/ ghost mathint sumOfBalances { init_state axiom sumOfBalances == 0; } hook Sload uint256 balance Token._balances[KEY address addr] { require sumOfBalances >= to_mathint(balance); } hook Sstore Token._balances[KEY address addr] uint256 newValue (uint256 oldValue) { sumOfBalances = sumOfBalances - oldValue + newValue; } ghost mathint totalReceived; hook Sload uint256 defaultValue currentContract._marketplaceTotals.received { require totalReceived >= to_mathint(defaultValue); } hook Sstore currentContract._marketplaceTotals.received uint256 defaultValue (uint256 defaultValue_old) { totalReceived = totalReceived + defaultValue - defaultValue_old; } ghost mathint totalSent; hook Sload uint256 defaultValue currentContract._marketplaceTotals.sent { require totalSent >= to_mathint(defaultValue); } hook Sstore currentContract._marketplaceTotals.sent uint256 defaultValue (uint256 defaultValue_old) { totalSent = totalSent + defaultValue - defaultValue_old; } ghost uint256 lastBlockTimestampGhost; hook TIMESTAMP uint v { require lastBlockTimestampGhost <= v; lastBlockTimestampGhost = v; } ghost mapping(MarketplaceHarness.SlotId => mapping(Periods.Period => bool)) _missingMirror { init_state axiom forall MarketplaceHarness.SlotId a. forall Periods.Period b. _missingMirror[a][b] == false; } hook Sload bool defaultValue _missing[KEY MarketplaceHarness.SlotId slotId][KEY Periods.Period period] { require _missingMirror[slotId][period] == defaultValue; } hook Sstore _missing[KEY MarketplaceHarness.SlotId slotId][KEY Periods.Period period] bool defaultValue { _missingMirror[slotId][period] = defaultValue; } /*-------------------------------------------- | Helper functions | --------------------------------------------*/ function canCancelRequest(method f) returns bool { return f.selector == sig:withdrawFunds(Marketplace.RequestId).selector; } function canStartRequest(method f) returns bool { return f.selector == sig:fillSlot(Marketplace.RequestId, uint256, Marketplace.Groth16Proof).selector; } function canFinishRequest(method f) returns bool { return f.selector == sig:freeSlot(Marketplace.SlotId).selector; } function canFailRequest(method f) returns bool { return f.selector == sig:markProofAsMissing(Marketplace.SlotId, Periods.Period).selector || f.selector == sig:freeSlot(Marketplace.SlotId).selector; } /*-------------------------------------------- | Invariants | --------------------------------------------*/ invariant totalSupplyIsSumOfBalances() to_mathint(Token.totalSupply()) == sumOfBalances; invariant requestStartedWhenSlotsFilled(env e, Marketplace.RequestId requestId, Marketplace.SlotId slotId) to_mathint(currentContract.requestContext(e, requestId).slotsFilled) == to_mathint(currentContract.getRequest(e, requestId).ask.slots) => currentContract.requestState(e, requestId) == Marketplace.RequestState.Started; // STATUS - verified // can set missing if period was passed // https://prover.certora.com/output/3106/026b36c118e44ad0824a51c50647c497/?anonymousKey=29879706f3d343555bb6122d071c9409d4e9876d invariant cantBeMissedIfInPeriod(MarketplaceHarness.SlotId slotId, Periods.Period period) lastBlockTimestampGhost <= publicPeriodEnd(period) => !_missingMirror[slotId][period]; /*-------------------------------------------- | Properties | --------------------------------------------*/ rule sanity(env e, method f) { calldataarg args; f(e, args); assert true; satisfy true; } rule totalReceivedCannotDecrease(env e, method f) { mathint total_before = totalReceived; calldataarg args; f(e, args); mathint total_after = totalReceived; assert total_after >= total_before; } rule totalSentCannotDecrease(env e, method f) { mathint total_before = totalSent; calldataarg args; f(e, args); mathint total_after = totalSent; assert total_after >= total_before; } rule allowedRequestStateChanges(env e, method f) { calldataarg args; Marketplace.RequestId requestId; Marketplace.RequestState requestStateBefore = currentContract.requestState(e, requestId); f(e, args); Marketplace.RequestState requestStateAfter = currentContract.requestState(e, requestId); assert requestStateBefore != requestStateAfter && requestStateAfter == Marketplace.RequestState.Started => requestStateBefore == Marketplace.RequestState.New; assert requestStateBefore != requestStateAfter && requestStateAfter == Marketplace.RequestState.Finished => requestStateBefore == Marketplace.RequestState.Started; assert requestStateBefore != requestStateAfter && requestStateAfter == Marketplace.RequestState.Failed => requestStateBefore == Marketplace.RequestState.Started; assert requestStateBefore != requestStateAfter && requestStateAfter == Marketplace.RequestState.Cancelled => requestStateBefore == Marketplace.RequestState.New; } rule functionsCausingRequestStateChanges(env e, method f) { calldataarg args; Marketplace.RequestId requestId; Marketplace.RequestState requestStateBefore = currentContract.requestState(e, requestId); f(e, args); Marketplace.RequestState requestStateAfter = currentContract.requestState(e, requestId); // RequestState.New -> RequestState.Started assert requestStateBefore == Marketplace.RequestState.New && requestStateAfter == Marketplace.RequestState.Started => canStartRequest(f); // RequestState.Started -> RequestState.Finished assert requestStateBefore == Marketplace.RequestState.Started && requestStateAfter == Marketplace.RequestState.Finished => canFinishRequest(f); // RequestState.Started -> RequestState.Failed assert requestStateBefore == Marketplace.RequestState.Started && requestStateAfter == Marketplace.RequestState.Failed => canFailRequest(f); // RequestState.New -> RequestState.Cancelled assert requestStateBefore == Marketplace.RequestState.New && requestStateAfter == Marketplace.RequestState.Cancelled => canCancelRequest(f); } rule finishedRequestCannotBeStartedAgain(env e, method f) { calldataarg args; Marketplace.RequestId requestId; Marketplace.RequestState requestStateBefore = currentContract.requestState(e, requestId); require requestStateBefore == Marketplace.RequestState.Finished; f(e, args); Marketplace.RequestState requestStateAfter = currentContract.requestState(e, requestId); assert requestStateBefore == requestStateAfter; }