2
2
pragma solidity ^ 0.8.0 ;
3
3
4
4
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol ' ;
5
-
6
5
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol ' ;
7
6
import '@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol ' ;
8
7
import '@openzeppelin/contracts/interfaces/IERC20.sol ' ;
@@ -67,13 +66,12 @@ contract DonationHandler is OwnableUpgradeable, ReentrancyGuardUpgradeable {
67
66
/// @param tokenAddress The address of the token being donated
68
67
/// @param requiredAmount The required amount of the allowance
69
68
/// @param owner The owner of the token
70
- modifier checkERC20Allowance (address tokenAddress , uint256 requiredAmount , address owner ) {
69
+ function _checkERC20Allowance (address tokenAddress , uint256 requiredAmount , address owner ) internal view {
71
70
require (tokenAddress != ETH_TOKEN_ADDRESS, 'Invalid token address ' );
72
71
uint256 allowance = IERC20 (tokenAddress).allowance (owner, address (this ));
73
72
if (allowance < requiredAmount) {
74
73
revert InsufficientAllowance ();
75
74
}
76
- _;
77
75
}
78
76
// ETH Donations
79
77
// Donate multiple ETH donations at once
@@ -112,6 +110,22 @@ contract DonationHandler is OwnableUpgradeable, ReentrancyGuardUpgradeable {
112
110
_handleETH (amount, recipientAddress, data);
113
111
}
114
112
113
+ // Donate a single ERC20 donation
114
+ /// @notice Donate a single ERC20 donation
115
+ /// @param tokenAddress The address of the token being donated
116
+ /// @param recipientAddress The address of the recipient of the donation
117
+ /// @param amount The amount of the donation
118
+ /// @param data The data of the donation
119
+ function donateERC20 (
120
+ address tokenAddress ,
121
+ address recipientAddress ,
122
+ uint256 amount ,
123
+ bytes calldata data
124
+ ) external nonReentrant {
125
+ _checkERC20Allowance (tokenAddress, amount, msg .sender );
126
+ _handleERC20 (tokenAddress, amount, recipientAddress, data);
127
+ }
128
+
115
129
// ERC20 Donations
116
130
// Donate multiple ERC20 donations at once
117
131
/// @notice Donate multiple ERC20 donations at once
@@ -126,12 +140,8 @@ contract DonationHandler is OwnableUpgradeable, ReentrancyGuardUpgradeable {
126
140
address [] calldata recipientAddresses ,
127
141
uint256 [] calldata amounts ,
128
142
bytes [] calldata data
129
- )
130
- external
131
- nonReentrant
132
- validateInputLengths (recipientAddresses, amounts, data)
133
- checkERC20Allowance (tokenAddress, totalAmount, msg .sender )
134
- {
143
+ ) external nonReentrant validateInputLengths (recipientAddresses, amounts, data) {
144
+ _checkERC20Allowance (tokenAddress, totalAmount, msg .sender );
135
145
Allocations memory allocations = Allocations (tokenAddress, totalAmount, recipientAddresses, amounts, data);
136
146
require (allocations.tokenAddress != ETH_TOKEN_ADDRESS, 'Invalid token address ' );
137
147
@@ -145,20 +155,50 @@ contract DonationHandler is OwnableUpgradeable, ReentrancyGuardUpgradeable {
145
155
}
146
156
}
147
157
}
158
+ // Donate multiple ERC20 & ETH donations at once
159
+ /// @notice Donate multiple ERC20 & ETH donations at once
160
+ /// @param tokenAddresses The addresses of the tokens being donated
161
+ /// @param amounts The amounts of the donation to each recipient
162
+ /// @param recipientAddresses The addresses of the recipients of the donation
163
+ /// @param data The data of the donation to each recipient
148
164
149
- // Donate a single ERC20 donation
150
- /// @notice Donate a single ERC20 donation
151
- /// @param tokenAddress The address of the token being donated
152
- /// @param recipientAddress The address of the recipient of the donation
153
- /// @param amount The amount of the donation
154
- /// @param data The data of the donation
155
- function donateERC20 (
156
- address tokenAddress ,
157
- address recipientAddress ,
158
- uint256 amount ,
159
- bytes calldata data
160
- ) external nonReentrant checkERC20Allowance (tokenAddress, amount, msg .sender ) {
161
- _handleERC20 (tokenAddress, amount, recipientAddress, data);
165
+ function donateManyMultipleTokens (
166
+ address [] calldata tokenAddresses ,
167
+ uint256 [] calldata amounts ,
168
+ address [] calldata recipientAddresses ,
169
+ bytes [] calldata data
170
+ ) external payable nonReentrant validateInputLengths (recipientAddresses, amounts, data) {
171
+ uint256 len = recipientAddresses.length ;
172
+ if (len != tokenAddresses.length ) revert InvalidInput ();
173
+ uint256 totalETHNeeded = 0 ;
174
+
175
+ for (uint256 i = 0 ; i < len;) {
176
+ if (tokenAddresses[i] == ETH_TOKEN_ADDRESS) {
177
+ totalETHNeeded += amounts[i];
178
+ }
179
+ unchecked {
180
+ ++ i;
181
+ }
182
+ }
183
+
184
+ require (msg .value == totalETHNeeded, 'Incorrect ETH amount sent ' );
185
+
186
+ for (uint256 i = 0 ; i < len;) {
187
+ address token = tokenAddresses[i];
188
+ uint256 amount = amounts[i];
189
+ address recipient = recipientAddresses[i];
190
+ bytes memory donationData = data[i];
191
+
192
+ if (token == ETH_TOKEN_ADDRESS) {
193
+ _handleETH (amount, recipient, donationData);
194
+ } else {
195
+ _checkERC20Allowance (token, amount, msg .sender );
196
+ _handleERC20 (token, amount, recipient, donationData);
197
+ }
198
+ unchecked {
199
+ ++ i;
200
+ }
201
+ }
162
202
}
163
203
164
204
// Internal functions
0 commit comments