-
-
Notifications
You must be signed in to change notification settings - Fork 103
Expand file tree
/
Copy pathNativeTokenMultiOperationIncreaseBalanceEnforcer.sol
More file actions
155 lines (130 loc) · 6.24 KB
/
NativeTokenMultiOperationIncreaseBalanceEnforcer.sol
File metadata and controls
155 lines (130 loc) · 6.24 KB
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: MIT AND Apache-2.0
pragma solidity ^0.8.23;
import { CaveatEnforcer } from "./CaveatEnforcer.sol";
import { ModeCode } from "../utils/Types.sol";
/**
* @title NativeTokenMultiOperationIncreaseBalanceEnforcer
* @notice Enforces that a recipient's token balance increases by at least the expected amount across multiple delegations.
* Tracks balance changes from the first beforeAllHook call to the last afterAllHook call within a redemption.
*
* @dev This enforcer operates in delegation chains where multiple delegations may affect the same recipient.
* State is shared between enforcers watching the same recipient pair and is cleared after transaction execution.
*
* @dev Only operates in default execution mode.
*
* @dev Security considerations:
* - State is shared between enforcers watching the same recipient.
* - Balance changes are tracked by comparing first beforeAll/last afterAll balances in batch delegations.
* - If the delegate is an EOA and not a DeleGator in multi-delegation scenarios, use an adapter contract
* like DelegationMetaSwapAdapter.sol to redeem delegations.
* - If there are multiple instances of this enforcer tracking the same recipient inside a redemption the
* balance increase will be aggregated.
*/
contract NativeTokenMultiOperationIncreaseBalanceEnforcer is CaveatEnforcer {
////////////////////////////// Events //////////////////////////////
event TrackedBalance(address indexed delegationManager, address indexed recipient, uint256 balance);
event UpdatedExpectedBalance(address indexed delegationManager, address indexed recipient, uint256 expected);
event ValidatedBalance(address indexed delegationManager, address indexed recipient, uint256 expected);
////////////////////////////// State //////////////////////////////
struct BalanceTracker {
uint256 balanceBefore;
uint256 expectedIncrease;
uint256 validationRemaining;
}
mapping(bytes32 hashKey => BalanceTracker balance) public balanceTracker;
////////////////////////////// External Methods //////////////////////////////
/**
* @notice Generates the key that identifies the run, produced by hashing the provided values.
* @param _caller Address of the sender calling the enforcer.
* @param _recipient Address of the recipient.
* @return The hash to be used as key of the mapping.
*/
function getHashKey(address _caller, address _recipient) external pure returns (bytes32) {
return _getHashKey(_caller, _recipient);
}
////////////////////////////// Public Methods //////////////////////////////
/**
* @notice This function caches the delegator's native token balance before the delegation is executed.
* @param _terms 52 packed bytes where:
* - first 20 bytes: address of the recipient
* - next 32 bytes: balance change guardrail amount
* @param _mode The execution mode. (Must be Default execType)
*/
function beforeAllHook(
bytes calldata _terms,
bytes calldata,
ModeCode _mode,
bytes calldata,
bytes32,
address,
address
)
public
override
onlyDefaultExecutionMode(_mode)
{
(address recipient_, uint256 amount_) = getTermsInfo(_terms);
require(amount_ > 0, "NativeTokenMultiOperationIncreaseBalanceEnforcer:zero-expected-change-amount");
bytes32 hashKey_ = _getHashKey(msg.sender, recipient_);
BalanceTracker memory balanceTracker_ = balanceTracker[hashKey_];
if (balanceTracker_.expectedIncrease == 0) {
balanceTracker_.balanceBefore = recipient_.balance;
emit TrackedBalance(msg.sender, recipient_, recipient_.balance);
}
balanceTracker_.expectedIncrease += amount_;
balanceTracker_.validationRemaining++;
balanceTracker[hashKey_] = balanceTracker_;
emit UpdatedExpectedBalance(msg.sender, recipient_, amount_);
}
/**
* @notice This function validates that the recipient's token balance has changed within expected limits.
* @param _terms 52 packed bytes where:
* - first 20 bytes: address of the recipient
* - next 32 bytes: balance change guardrail amount
*/
function afterAllHook(
bytes calldata _terms,
bytes calldata,
ModeCode,
bytes calldata,
bytes32,
address,
address
)
public
override
{
(address recipient_,) = getTermsInfo(_terms);
bytes32 hashKey_ = _getHashKey(msg.sender, recipient_);
balanceTracker[hashKey_].validationRemaining--;
// Only validate on the last afterAllHook if there are multiple enforcers tracking the same recipient pair
if (balanceTracker[hashKey_].validationRemaining > 0) return;
BalanceTracker memory balanceTracker_ = balanceTracker[hashKey_];
require(
recipient_.balance >= balanceTracker_.balanceBefore + balanceTracker_.expectedIncrease,
"NativeTokenMultiOperationIncreaseBalanceEnforcer:insufficient-balance-increase"
);
emit ValidatedBalance(msg.sender, recipient_, balanceTracker_.expectedIncrease);
delete balanceTracker[hashKey_];
}
/**
* @notice Decodes the terms used in this CaveatEnforcer.
* @param _terms 52 packed bytes where:
* - first 20 bytes: address of the recipient
* - next 32 bytes: balance change guardrail amount
* @return recipient_ The address of the recipient whose balance will change.
* @return amount_ Balance change guardrail amount (i.e., minimum increase)
*/
function getTermsInfo(bytes calldata _terms) public pure returns (address recipient_, uint256 amount_) {
require(_terms.length == 52, "NativeTokenMultiOperationIncreaseBalanceEnforcer:invalid-terms-length");
recipient_ = address(bytes20(_terms[0:20]));
amount_ = uint256(bytes32(_terms[20:]));
}
////////////////////////////// Internal Methods //////////////////////////////
/**
* @notice Generates the key that identifies the run, produced by hashing the provided values.
*/
function _getHashKey(address _caller, address _recipient) private pure returns (bytes32) {
return keccak256(abi.encode(_caller, _recipient));
}
}