Skip to content

Commit a3f9730

Browse files
committed
fix ERC20Wrapper specs
1 parent c8d3e0f commit a3f9730

File tree

2 files changed

+38
-18
lines changed

2 files changed

+38
-18
lines changed

certora/harnesses/ERC20WrapperHarness.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ contract ERC20WrapperHarness is ERC20Permit, ERC20Wrapper {
2020
return underlying().balanceOf(account);
2121
}
2222

23-
function underlyingAllowanceToThis(address account) public view returns (uint256) {
24-
return underlying().allowance(account, address(this));
23+
function underlyingAllowance(address spender, address receiver) public view returns (uint256) {
24+
return underlying().allowance(spender, receiver);
2525
}
2626

2727
function recover(address account) public returns (uint256) {

certora/specs/ERC20Wrapper.spec

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import "helpers/helpers.spec";
22
import "ERC20.spec";
33

44
methods {
5-
function underlying() external returns(address) envfree;
6-
function underlyingTotalSupply() external returns(uint256) envfree;
7-
function underlyingBalanceOf(address) external returns(uint256) envfree;
8-
function underlyingAllowanceToThis(address) external returns(uint256) envfree;
9-
10-
function depositFor(address, uint256) external returns(bool);
11-
function withdrawTo(address, uint256) external returns(bool);
12-
function recover(address) external returns(uint256);
5+
function underlying() external returns(address) envfree;
6+
function underlyingTotalSupply() external returns(uint256) envfree;
7+
function underlyingBalanceOf(address) external returns(uint256) envfree;
8+
function underlyingAllowance(address, address) external returns(uint256) envfree;
9+
10+
function depositFor(address, uint256) external returns(bool);
11+
function withdrawTo(address, uint256) external returns(bool);
12+
function recover(address) external returns(uint256);
1313
}
1414

1515
use invariant totalSupplyIsSumOfBalances;
@@ -19,12 +19,25 @@ use invariant totalSupplyIsSumOfBalances;
1919
Helper: consequence of `totalSupplyIsSumOfBalances` applied to underlying
2020
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
2121
*/
22-
definition underlyingBalancesLowerThanUnderlyingSupply(address a) returns bool =
23-
underlyingBalanceOf(a) <= underlyingTotalSupply();
24-
2522
definition sumOfUnderlyingBalancesLowerThanUnderlyingSupply(address a, address b) returns bool =
2623
a != b => underlyingBalanceOf(a) + underlyingBalanceOf(b) <= to_mathint(underlyingTotalSupply());
2724

25+
/*
26+
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
27+
Invariant: wrapped token should not allow any third party to spend its tokens
28+
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
29+
*/
30+
invariant noAllowance(address user)
31+
underlyingAllowance(currentContract, user) == 0
32+
{
33+
preserved ERC20PermitHarness.approve(address spender, uint256 value) with (env e) {
34+
require e.msg.sender != currentContract;
35+
}
36+
preserved ERC20PermitHarness.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) with (env e) {
37+
require owner != currentContract;
38+
}
39+
}
40+
2841
/*
2942
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
3043
Invariant: wrapped token can't be undercollateralized (solvency of the wrapper) │
@@ -35,13 +48,20 @@ invariant totalSupplyIsSmallerThanUnderlyingBalance()
3548
underlyingBalanceOf(currentContract) <= underlyingTotalSupply() &&
3649
underlyingTotalSupply() <= max_uint256
3750
{
38-
preserved {
51+
preserved with (env e) {
3952
requireInvariant totalSupplyIsSumOfBalances;
40-
require underlyingBalancesLowerThanUnderlyingSupply(currentContract);
41-
}
42-
preserved depositFor(address account, uint256 amount) with (env e) {
53+
require e.msg.sender != currentContract;
4354
require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(e.msg.sender, currentContract);
4455
}
56+
preserved ERC20PermitHarness.transferFrom(address from, address to, uint256 amount) with (env e) {
57+
requireInvariant noAllowance(e.msg.sender);
58+
require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(from, to);
59+
}
60+
preserved ERC20PermitHarness.burn(address from, uint256 amount) with (env e) {
61+
// If someone can burn from the wrapper, than the invariant obviously doesn't hold.
62+
require from != currentContract;
63+
require sumOfUnderlyingBalancesLowerThanUnderlyingSupply(from, currentContract);
64+
}
4565
}
4666

4767
invariant noSelfWrap()
@@ -69,7 +89,7 @@ rule depositFor(env e) {
6989
uint256 balanceBefore = balanceOf(receiver);
7090
uint256 supplyBefore = totalSupply();
7191
uint256 senderUnderlyingBalanceBefore = underlyingBalanceOf(sender);
72-
uint256 senderUnderlyingAllowanceBefore = underlyingAllowanceToThis(sender);
92+
uint256 senderUnderlyingAllowanceBefore = underlyingAllowance(sender, currentContract);
7393
uint256 wrapperUnderlyingBalanceBefore = underlyingBalanceOf(currentContract);
7494
uint256 underlyingSupplyBefore = underlyingTotalSupply();
7595

0 commit comments

Comments
 (0)