123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- import "helpers/helpers.spec";
- import "ERC20.spec";
- using ERC20PermitHarness as underlying;
- methods {
- function underlying() external returns(address) envfree;
- function depositFor(address, uint256) external returns(bool);
- function withdrawTo(address, uint256) external returns(bool);
- function recover(address) external returns(uint256);
- function underlying.totalSupply() external returns (uint256) envfree;
- function underlying.balanceOf(address) external returns (uint256) envfree;
- function underlying.allowance(address,address) external returns (uint256) envfree;
- unresolved external in _._ => DISPATCH(optimistic=true) [
- underlying.transferFrom(address, address, uint256),
- underlying.transfer(address, uint256)
- ];
- }
- use invariant totalSupplyIsSumOfBalances;
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Helper: consequence of `totalSupplyIsSumOfBalances` applied to underlying โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- definition sumOfUnderlyingBalancesLowerThanUnderlyingSupply(address a, address b) returns bool =
- a != b => underlying.balanceOf(a) + underlying.balanceOf(b) <= to_mathint(underlying.totalSupply());
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: wrapped token should not allow any third party to spend its tokens โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant noAllowance(address user)
- underlying.allowance(currentContract, user) == 0
- {
- preserved ERC20PermitHarness.approve(address spender, uint256 value) with (env e) {
- require e.msg.sender != currentContract;
- }
- preserved ERC20PermitHarness.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) with (env e) {
- require owner != currentContract;
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: wrapped token can't be undercollateralized (solvency of the wrapper) โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant totalSupplyIsSmallerThanUnderlyingBalance()
- totalSupply() <= underlying.balanceOf(currentContract) &&
- underlying.balanceOf(currentContract) <= underlying.totalSupply() &&
- underlying.totalSupply() <= max_uint256
- {
- preserved with (env e) {
- requireInvariant totalSupplyIsSumOfBalances;
- require e.msg.sender != currentContract;
- require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(e.msg.sender, currentContract);
- }
- preserved ERC20PermitHarness.transferFrom(address from, address to, uint256 amount) with (env e) {
- requireInvariant noAllowance(e.msg.sender);
- require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(from, to);
- }
- preserved ERC20PermitHarness.burn(address from, uint256 amount) with (env e) {
- // If someone can burn from the wrapper, than the invariant obviously doesn't hold.
- require from != currentContract;
- require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(from, currentContract);
- }
- }
- rule noSelfWrap() {
- assert currentContract != underlying();
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: depositFor liveness and effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule depositFor(env e) {
- require nonpayable(e);
- address sender = e.msg.sender;
- address receiver;
- address other;
- uint256 amount;
- // sanity
- require currentContract != underlying();
- requireInvariant totalSupplyIsSumOfBalances;
- requireInvariant totalSupplyIsSmallerThanUnderlyingBalance;
- require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(currentContract, sender);
- uint256 balanceBefore = balanceOf(receiver);
- uint256 supplyBefore = totalSupply();
- uint256 senderUnderlyingBalanceBefore = underlying.balanceOf(sender);
- uint256 senderUnderlyingAllowanceBefore = underlying.allowance(sender, currentContract);
- uint256 wrapperUnderlyingBalanceBefore = underlying.balanceOf(currentContract);
- uint256 underlyingSupplyBefore = underlying.totalSupply();
- uint256 otherBalanceBefore = balanceOf(other);
- uint256 otherUnderlyingBalanceBefore = underlying.balanceOf(other);
- depositFor@withrevert(e, receiver, amount);
- bool success = !lastReverted;
- // liveness
- assert success <=> (
- sender != currentContract && // invalid sender
- sender != 0 && // invalid sender
- receiver != currentContract && // invalid receiver
- receiver != 0 && // invalid receiver
- amount <= senderUnderlyingBalanceBefore && // deposit doesn't exceed balance
- amount <= senderUnderlyingAllowanceBefore // deposit doesn't exceed allowance
- );
- // effects
- assert success => (
- to_mathint(balanceOf(receiver)) == balanceBefore + amount &&
- to_mathint(totalSupply()) == supplyBefore + amount &&
- to_mathint(underlying.balanceOf(currentContract)) == wrapperUnderlyingBalanceBefore + amount &&
- to_mathint(underlying.balanceOf(sender)) == senderUnderlyingBalanceBefore - amount
- );
- // no side effect
- assert underlying.totalSupply() == underlyingSupplyBefore;
- assert balanceOf(other) != otherBalanceBefore => other == receiver;
- assert underlying.balanceOf(other) != otherUnderlyingBalanceBefore => (other == sender || other == currentContract);
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: withdrawTo liveness and effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule withdrawTo(env e) {
- require nonpayable(e);
- address sender = e.msg.sender;
- address receiver;
- address other;
- uint256 amount;
- // sanity
- require currentContract != underlying();
- requireInvariant totalSupplyIsSumOfBalances;
- requireInvariant totalSupplyIsSmallerThanUnderlyingBalance;
- require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(currentContract, receiver);
- uint256 balanceBefore = balanceOf(sender);
- uint256 supplyBefore = totalSupply();
- uint256 receiverUnderlyingBalanceBefore = underlying.balanceOf(receiver);
- uint256 wrapperUnderlyingBalanceBefore = underlying.balanceOf(currentContract);
- uint256 underlyingSupplyBefore = underlying.totalSupply();
- uint256 otherBalanceBefore = balanceOf(other);
- uint256 otherUnderlyingBalanceBefore = underlying.balanceOf(other);
- withdrawTo@withrevert(e, receiver, amount);
- bool success = !lastReverted;
- // liveness
- assert success <=> (
- sender != 0 && // invalid sender
- receiver != currentContract && // invalid receiver
- receiver != 0 && // invalid receiver
- amount <= balanceBefore // withdraw doesn't exceed balance
- );
- // effects
- assert success => (
- to_mathint(balanceOf(sender)) == balanceBefore - amount &&
- to_mathint(totalSupply()) == supplyBefore - amount &&
- to_mathint(underlying.balanceOf(currentContract)) == wrapperUnderlyingBalanceBefore - (currentContract != receiver ? amount : 0) &&
- to_mathint(underlying.balanceOf(receiver)) == receiverUnderlyingBalanceBefore + (currentContract != receiver ? amount : 0)
- );
- // no side effect
- assert underlying.totalSupply() == underlyingSupplyBefore;
- assert balanceOf(other) != otherBalanceBefore => other == sender;
- assert underlying.balanceOf(other) != otherUnderlyingBalanceBefore => (other == receiver || other == currentContract);
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rule: recover liveness and effects โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule recover(env e) {
- require nonpayable(e);
- address receiver;
- address other;
- // sanity
- require currentContract != underlying();
- requireInvariant totalSupplyIsSumOfBalances;
- requireInvariant totalSupplyIsSmallerThanUnderlyingBalance;
- mathint value = underlying.balanceOf(currentContract) - totalSupply();
- uint256 supplyBefore = totalSupply();
- uint256 balanceBefore = balanceOf(receiver);
- uint256 otherBalanceBefore = balanceOf(other);
- uint256 otherUnderlyingBalanceBefore = underlying.balanceOf(other);
- recover@withrevert(e, receiver);
- bool success = !lastReverted;
- // liveness
- assert success <=> receiver != 0;
- // effect
- assert success => (
- to_mathint(balanceOf(receiver)) == balanceBefore + value &&
- to_mathint(totalSupply()) == supplyBefore + value &&
- totalSupply() == underlying.balanceOf(currentContract)
- );
- // no side effect
- assert underlying.balanceOf(other) == otherUnderlyingBalanceBefore;
- assert balanceOf(other) != otherBalanceBefore => other == receiver;
- }
|