forked from crytic/properties
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRedeemUsingApprovalProps.sol
139 lines (124 loc) · 5.38 KB
/
RedeemUsingApprovalProps.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;
import {CryticERC4626PropertyBase} from "../util/ERC4626PropertyTestBase.sol";
import {CryticERC4626VaultProxy} from "./VaultProxy.sol";
contract CryticERC4626RedeemUsingApproval is
CryticERC4626PropertyBase,
CryticERC4626VaultProxy
{
/// @notice verifies `redeem()` must allow proxies to redeem shares on behalf of the owner using share token approvals
/// verifies third party `redeem()` calls must update the msg.sender's allowance
function verify_redeemViaApprovalProxy(
uint256 receiverId,
uint256 shares
) public returns (uint256 tokensWithdrawn) {
address owner = address(this);
address receiver = restrictAddressToThirdParties(receiverId);
shares = requireValidRedeemAmount(owner, shares);
vault.approve(address(redemptionProxy), shares);
measureAddressHoldings(address(this), "vault", "before redeemption");
try redemptionProxy.redeemOnBehalf(shares, receiver, owner) returns (
uint256 _tokensWithdrawn
) {
tokensWithdrawn = _tokensWithdrawn;
} catch {
assertWithMsg(
false,
"vault.redeem() reverted during redeem via approval"
);
}
// verify allowance is updated
uint256 newAllowance = vault.allowance(owner, address(redemptionProxy));
assertEq(
newAllowance,
0,
"The vault failed to update the redemption proxy's share allowance"
);
}
/// @notice verifies `withdraw()` must allow proxies to withdraw shares on behalf of the owner using share token approvals
/// verifies third party `withdraw()` calls must update the msg.sender's allowance
function verify_withdrawViaApprovalProxy(
uint256 receiverId,
uint256 tokens
) public returns (uint256 sharesBurned) {
address owner = address(this);
address receiver = restrictAddressToThirdParties(receiverId);
tokens = requireValidWithdrawAmount(owner, tokens);
uint256 expectedSharesConsumed = vault.previewWithdraw(tokens);
vault.approve(address(redemptionProxy), expectedSharesConsumed);
measureAddressHoldings(address(this), "vault", "before withdraw");
try redemptionProxy.withdrawOnBehalf(tokens, receiver, owner) returns (
uint256 _sharesBurned
) {
sharesBurned = _sharesBurned;
} catch {
assertWithMsg(
false,
"vault.withdraw() reverted during withdraw via approval"
);
}
emit LogUint256("withdraw consumed this many shares:", sharesBurned);
// verify allowance is updated
uint256 newAllowance = vault.allowance(owner, address(redemptionProxy));
uint256 expectedAllowance = expectedSharesConsumed - sharesBurned;
emit LogUint256("Expecting allowance to now be:", expectedAllowance);
assertEq(
expectedAllowance,
newAllowance,
"The vault failed to update the redemption proxy's share allowance"
);
}
/// @notice verifies third parties must not be able to `withdraw()` tokens on an owner's behalf without a token approval
function verify_withdrawRequiresTokenApproval(
uint256 receiverId,
uint256 tokens,
uint256 sharesApproved
) public {
address owner = address(this);
address receiver = restrictAddressToThirdParties(receiverId);
tokens = requireValidWithdrawAmount(owner, tokens);
uint256 expectedSharesConsumed = vault.previewWithdraw(tokens);
emit LogUint256(
"Will attempt to proxy withdraw this many shares:",
expectedSharesConsumed
);
require(sharesApproved < expectedSharesConsumed);
emit LogUint256("Approving spend of this many shares:", sharesApproved);
vault.approve(address(redemptionProxy), sharesApproved);
try redemptionProxy.withdrawOnBehalf(tokens, receiver, owner) returns (
uint256 _sharesBurned
) {
assertLte(
_sharesBurned,
sharesApproved,
"Redemption proxy must not be able to withdraw more shares than it was approved"
);
} catch {}
}
/// @notice verifies third parties must not be able to `redeem()` shares on an owner's behalf without a token approval
function verify_redeemRequiresTokenApproval(
uint256 receiverId,
uint256 shares,
uint256 sharesApproved
) public {
address owner = address(this);
address receiver = restrictAddressToThirdParties(receiverId);
shares = requireValidRedeemAmount(owner, shares);
emit LogUint256(
"Will attempt to proxy redeem this many shares:",
shares
);
require(sharesApproved < shares);
emit LogUint256("Approving spend of this many shares:", sharesApproved);
vault.approve(address(redemptionProxy), sharesApproved);
try redemptionProxy.redeemOnBehalf(shares, receiver, owner) returns (
uint256 _sharesBurned
) {
assertLte(
_sharesBurned,
sharesApproved,
"Redemption proxy must not be able to redeem more shares than it was approved"
);
} catch {}
}
}