|
@@ -0,0 +1,198 @@
|
|
|
+import "helpers.spec"
|
|
|
+import "ERC20.spec"
|
|
|
+
|
|
|
+methods {
|
|
|
+ underlying() returns(address) envfree
|
|
|
+ underlyingTotalSupply() returns(uint256) envfree
|
|
|
+ underlyingBalanceOf(address) returns(uint256) envfree
|
|
|
+ underlyingAllowanceToThis(address) returns(uint256) envfree
|
|
|
+
|
|
|
+ depositFor(address, uint256) returns(bool)
|
|
|
+ withdrawTo(address, uint256) returns(bool)
|
|
|
+ recover(address) returns(uint256)
|
|
|
+}
|
|
|
+
|
|
|
+use invariant totalSupplyIsSumOfBalances
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Helper: consequence of `totalSupplyIsSumOfBalances` applied to underlying โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+function underlyingBalancesLowerThanUnderlyingSupply(address a) returns bool {
|
|
|
+ return underlyingBalanceOf(a) <= underlyingTotalSupply();
|
|
|
+}
|
|
|
+
|
|
|
+function sumOfUnderlyingBalancesLowerThanUnderlyingSupply(address a, address b) returns bool {
|
|
|
+ return a != b => underlyingBalanceOf(a) + underlyingBalanceOf(b) <= underlyingTotalSupply();
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Invariant: wrapped token can't be undercollateralized (solvency of the wrapper) โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+invariant totalSupplyIsSmallerThanUnderlyingBalance()
|
|
|
+ totalSupply() <= underlyingBalanceOf(currentContract) &&
|
|
|
+ underlyingBalanceOf(currentContract) <= underlyingTotalSupply() &&
|
|
|
+ underlyingTotalSupply() <= max_uint256
|
|
|
+ {
|
|
|
+ preserved {
|
|
|
+ requireInvariant totalSupplyIsSumOfBalances;
|
|
|
+ require underlyingBalancesLowerThanUnderlyingSupply(currentContract);
|
|
|
+ }
|
|
|
+ preserved depositFor(address account, uint256 amount) with (env e) {
|
|
|
+ require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(e.msg.sender, currentContract);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+invariant noSelfWrap()
|
|
|
+ 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
|
|
|
+ requireInvariant noSelfWrap;
|
|
|
+ requireInvariant totalSupplyIsSumOfBalances;
|
|
|
+ requireInvariant totalSupplyIsSmallerThanUnderlyingBalance;
|
|
|
+ require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(currentContract, sender);
|
|
|
+
|
|
|
+ uint256 balanceBefore = balanceOf(receiver);
|
|
|
+ uint256 supplyBefore = totalSupply();
|
|
|
+ uint256 senderUnderlyingBalanceBefore = underlyingBalanceOf(sender);
|
|
|
+ uint256 senderUnderlyingAllowanceBefore = underlyingAllowanceToThis(sender);
|
|
|
+ uint256 wrapperUnderlyingBalanceBefore = underlyingBalanceOf(currentContract);
|
|
|
+ uint256 underlyingSupplyBefore = underlyingTotalSupply();
|
|
|
+
|
|
|
+ uint256 otherBalanceBefore = balanceOf(other);
|
|
|
+ uint256 otherUnderlyingBalanceBefore = underlyingBalanceOf(other);
|
|
|
+
|
|
|
+ depositFor@withrevert(e, receiver, amount);
|
|
|
+ bool success = !lastReverted;
|
|
|
+
|
|
|
+ // liveness
|
|
|
+ assert success <=> (
|
|
|
+ sender != currentContract && // invalid sender
|
|
|
+ sender != 0 && // invalid sender
|
|
|
+ receiver != 0 && // invalid receiver
|
|
|
+ amount <= senderUnderlyingBalanceBefore && // deposit doesn't exceed balance
|
|
|
+ amount <= senderUnderlyingAllowanceBefore // deposit doesn't exceed allowance
|
|
|
+ );
|
|
|
+
|
|
|
+ // effects
|
|
|
+ assert success => (
|
|
|
+ balanceOf(receiver) == balanceBefore + amount &&
|
|
|
+ totalSupply() == supplyBefore + amount &&
|
|
|
+ underlyingBalanceOf(currentContract) == wrapperUnderlyingBalanceBefore + amount &&
|
|
|
+ underlyingBalanceOf(sender) == senderUnderlyingBalanceBefore - amount
|
|
|
+ );
|
|
|
+
|
|
|
+ // no side effect
|
|
|
+ assert underlyingTotalSupply() == underlyingSupplyBefore;
|
|
|
+ assert balanceOf(other) != otherBalanceBefore => other == receiver;
|
|
|
+ assert underlyingBalanceOf(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
|
|
|
+ requireInvariant noSelfWrap;
|
|
|
+ requireInvariant totalSupplyIsSumOfBalances;
|
|
|
+ requireInvariant totalSupplyIsSmallerThanUnderlyingBalance;
|
|
|
+ require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(currentContract, receiver);
|
|
|
+
|
|
|
+ uint256 balanceBefore = balanceOf(sender);
|
|
|
+ uint256 supplyBefore = totalSupply();
|
|
|
+ uint256 receiverUnderlyingBalanceBefore = underlyingBalanceOf(receiver);
|
|
|
+ uint256 wrapperUnderlyingBalanceBefore = underlyingBalanceOf(currentContract);
|
|
|
+ uint256 underlyingSupplyBefore = underlyingTotalSupply();
|
|
|
+
|
|
|
+ uint256 otherBalanceBefore = balanceOf(other);
|
|
|
+ uint256 otherUnderlyingBalanceBefore = underlyingBalanceOf(other);
|
|
|
+
|
|
|
+ withdrawTo@withrevert(e, receiver, amount);
|
|
|
+ bool success = !lastReverted;
|
|
|
+
|
|
|
+ // liveness
|
|
|
+ assert success <=> (
|
|
|
+ sender != 0 && // invalid sender
|
|
|
+ receiver != 0 && // invalid receiver
|
|
|
+ amount <= balanceBefore // withdraw doesn't exceed balance
|
|
|
+ );
|
|
|
+
|
|
|
+ // effects
|
|
|
+ assert success => (
|
|
|
+ balanceOf(sender) == balanceBefore - amount &&
|
|
|
+ totalSupply() == supplyBefore - amount &&
|
|
|
+ underlyingBalanceOf(currentContract) == wrapperUnderlyingBalanceBefore - (currentContract != receiver ? amount : 0) &&
|
|
|
+ underlyingBalanceOf(receiver) == receiverUnderlyingBalanceBefore + (currentContract != receiver ? amount : 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ // no side effect
|
|
|
+ assert underlyingTotalSupply() == underlyingSupplyBefore;
|
|
|
+ assert balanceOf(other) != otherBalanceBefore => other == sender;
|
|
|
+ assert underlyingBalanceOf(other) != otherUnderlyingBalanceBefore => (other == receiver || other == currentContract);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+โ Rule: recover liveness and effects โ
|
|
|
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
+*/
|
|
|
+rule recover(env e) {
|
|
|
+ require nonpayable(e);
|
|
|
+
|
|
|
+ address receiver;
|
|
|
+ address other;
|
|
|
+
|
|
|
+ // sanity
|
|
|
+ requireInvariant noSelfWrap;
|
|
|
+ requireInvariant totalSupplyIsSumOfBalances;
|
|
|
+ requireInvariant totalSupplyIsSmallerThanUnderlyingBalance;
|
|
|
+
|
|
|
+ uint256 value = underlyingBalanceOf(currentContract) - totalSupply();
|
|
|
+ uint256 supplyBefore = totalSupply();
|
|
|
+ uint256 balanceBefore = balanceOf(receiver);
|
|
|
+
|
|
|
+ uint256 otherBalanceBefore = balanceOf(other);
|
|
|
+ uint256 otherUnderlyingBalanceBefore = underlyingBalanceOf(other);
|
|
|
+
|
|
|
+ recover@withrevert(e, receiver);
|
|
|
+ bool success = !lastReverted;
|
|
|
+
|
|
|
+ // liveness
|
|
|
+ assert success <=> receiver != 0;
|
|
|
+
|
|
|
+ // effect
|
|
|
+ assert success => (
|
|
|
+ balanceOf(receiver) == balanceBefore + value &&
|
|
|
+ totalSupply() == supplyBefore + value &&
|
|
|
+ totalSupply() == underlyingBalanceOf(currentContract)
|
|
|
+ );
|
|
|
+
|
|
|
+ // no side effect
|
|
|
+ assert underlyingBalanceOf(other) == otherUnderlyingBalanceBefore;
|
|
|
+ assert balanceOf(other) != otherBalanceBefore => other == receiver;
|
|
|
+}
|