1
1
pragma solidity 0.4.21 ;
2
+ pragma experimental ABIEncoderV2;
2
3
3
4
4
5
import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol " ;
@@ -21,18 +22,23 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
21
22
uint [] public units;
22
23
mapping (address => bool ) internal isComponent;
23
24
24
-
25
- struct PartialRedeemStatus {
26
- uint unredeemedBalance;
25
+ struct unredeemedComponent {
26
+ uint balance;
27
27
bool isRedeemed;
28
28
}
29
29
30
- // Mapping of token address -> user address -> partialRedeemStatus
31
- mapping (address => mapping (address => PartialRedeemStatus )) public unredeemedComponents;
30
+ // Mapping of token address -> user address -> unredeemedComponent
31
+ mapping (address => mapping (address => unredeemedComponent )) public unredeemedComponents;
32
32
33
33
event LogPartialRedemption (
34
34
address indexed _sender ,
35
- uint indexed _quantity
35
+ uint indexed _quantity ,
36
+ address [] _excludedComponents
37
+ );
38
+
39
+ event LogRedeemExcluded (
40
+ address indexed _sender ,
41
+ address [] _components
36
42
);
37
43
38
44
modifier hasSufficientBalance (uint quantity ) {
@@ -43,9 +49,9 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
43
49
_;
44
50
}
45
51
46
- modifier preventRedeemReEntrancy (address sender , uint quantity ) {
52
+ modifier preventRedeemReEntrancy (uint quantity ) {
47
53
// To prevent re-entrancy attacks, decrement the user's Set balance
48
- balances[sender] = balances[sender].sub (quantity);
54
+ balances[msg . sender ] = balances[msg . sender ].sub (quantity);
49
55
50
56
// Decrement the total token supply
51
57
totalSupply = totalSupply.sub (quantity);
@@ -106,14 +112,7 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
106
112
address currentComponent = components[i];
107
113
uint currentUnits = units[i];
108
114
109
- // Transfer value is defined as the currentUnits (in GWei)
110
- // multiplied by quantity in Wei divided by the units of gWei.
111
- // We do this to allow fractional units to be defined
112
- uint transferValue = currentUnits.fxpMul (quantity, 10 ** 9 );
113
-
114
- // Protect against the case that the gWei divisor results in a value that is
115
- // 0 and the user is able to generate Sets without sending a balance
116
- assert (transferValue > 0 );
115
+ uint transferValue = calculateTransferValue (units[i], quantity);
117
116
118
117
assert (ERC20 (currentComponent).transferFrom (msg .sender , this , transferValue));
119
118
}
@@ -124,7 +123,7 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
124
123
// Increment the total token supply
125
124
totalSupply = totalSupply.add (quantity);
126
125
127
- LogIssuance (msg .sender , quantity);
126
+ emit LogIssuance (msg .sender , quantity);
128
127
129
128
return true ;
130
129
}
@@ -139,27 +138,20 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
139
138
function redeem (uint quantity )
140
139
public
141
140
hasSufficientBalance (quantity)
142
- preventRedeemReEntrancy (msg . sender , quantity)
141
+ preventRedeemReEntrancy (quantity)
143
142
returns (bool success )
144
143
{
145
144
for (uint i = 0 ; i < components.length ; i++ ) {
146
145
address currentComponent = components[i];
147
146
uint currentUnits = units[i];
148
147
149
- // Transfer value is defined as the currentUnits (in GWei)
150
- // multiplied by quantity in Wei divided by the units of gWei.
151
- // We do this to allow fractional units to be defined
152
- uint transferValue = currentUnits.fxpMul (quantity, 10 ** 9 );
153
-
154
- // Protect against the case that the gWei divisor results in a value that is
155
- // 0 and the user is able to generate Sets without sending a balance
156
- assert (transferValue > 0 );
148
+ uint transferValue = calculateTransferValue (units[i], quantity);
157
149
158
150
// The transaction will fail if any of the components fail to transfer
159
151
assert (ERC20 (currentComponent).transfer (msg .sender , transferValue));
160
152
}
161
153
162
- LogRedemption (msg .sender , quantity);
154
+ emit LogRedemption (msg .sender , quantity);
163
155
164
156
return true ;
165
157
}
@@ -177,7 +169,7 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
177
169
function partialRedeem (uint quantity , address [] excludedComponents )
178
170
public
179
171
hasSufficientBalance (quantity)
180
- preventRedeemReEntrancy (msg . sender , quantity)
172
+ preventRedeemReEntrancy (quantity)
181
173
returns (bool success )
182
174
{
183
175
// Excluded tokens should be less than the number of components
@@ -188,14 +180,7 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
188
180
for (uint i = 0 ; i < components.length ; i++ ) {
189
181
bool isExcluded = false ;
190
182
191
- // Transfer value is defined as the currentUnits (in GWei)
192
- // multiplied by quantity in Wei divided by the units of gWei.
193
- // We do this to allow fractional units to be defined
194
- uint transferValue = units[i].fxpMul (quantity, 10 ** 9 );
195
-
196
- // Protect against the case that the gWei divisor results in a value that is
197
- // 0 and the user is able to generate Sets without sending a balance
198
- assert (transferValue > 0 );
183
+ uint transferValue = calculateTransferValue (units[i], quantity);
199
184
200
185
// This is unideal to do a doubly nested loop, but the number of excludedComponents
201
186
// should generally be a small number
@@ -207,22 +192,21 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
207
192
208
193
// If the token is excluded, add to the user's unredeemed component value
209
194
if (components[i] == currentExcluded) {
210
- // Ensures there are no duplicates
195
+ // Check whether component is already redeemed; Ensures duplicate excludedComponents
196
+ // has not been inputted.
211
197
bool currentIsRedeemed = unredeemedComponents[components[i]][msg .sender ].isRedeemed;
212
198
assert (currentIsRedeemed == false );
213
199
214
- unredeemedComponents[components[i]][msg .sender ].unredeemedBalance += transferValue;
200
+ unredeemedComponents[components[i]][msg .sender ].balance += transferValue;
215
201
216
202
// Mark redeemed to ensure no duplicates
217
203
unredeemedComponents[components[i]][msg .sender ].isRedeemed = true ;
218
204
219
205
isExcluded = true ;
220
-
221
206
}
222
207
}
223
208
224
- if (isExcluded == false ) {
225
- // The transaction will fail if any of the components fail to transfer
209
+ if (! isExcluded) {
226
210
assert (ERC20 (components[i]).transfer (msg .sender , transferValue));
227
211
}
228
212
}
@@ -233,23 +217,44 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
233
217
unredeemedComponents[currentExcludedToUnredeem][msg .sender ].isRedeemed = false ;
234
218
}
235
219
236
- LogPartialRedemption (msg .sender , quantity);
220
+ emit LogPartialRedemption (msg .sender , quantity, excludedComponents );
237
221
238
222
return true ;
239
223
}
240
224
241
- function redeemExcluded (uint quantity , address excludedComponent )
225
+ /**
226
+ * @dev Function to withdraw tokens that have previously been excluded when calling
227
+ * the redeemExcluded method
228
+ *
229
+ * This function should be used to retrieve tokens that have previously excluded
230
+ * when calling the redeemExcluded function.
231
+ *
232
+ * @param componentsToRedeem address[] The list of tokens to redeem
233
+ * @param quantities uint[] The quantity of Sets desired to redeem in Wei
234
+ */
235
+ function redeemExcluded (address [] componentsToRedeem , uint [] quantities )
242
236
public
243
237
returns (bool success )
244
238
{
245
- // Check there is enough balance
246
- uint remainingBalance = unredeemedComponents[excludedComponent][ msg . sender ].unredeemedBalance ;
247
- require (remainingBalance >= quantity );
239
+ require (quantities. length > 0 );
240
+ require (componentsToRedeem. length > 0 ) ;
241
+ require (quantities. length == componentsToRedeem. length );
248
242
249
- // To prevent re-entrancy attacks, decrement the user's Set balance
250
- unredeemedComponents[excludedComponent][msg .sender ].unredeemedBalance = remainingBalance.sub (quantity);
243
+ for (uint i = 0 ; i < quantities.length ; i++ ) {
244
+ address currentComponent = componentsToRedeem[i];
245
+ uint currentQuantity = quantities[i];
246
+
247
+ // Check there is enough balance
248
+ uint remainingBalance = unredeemedComponents[currentComponent][msg .sender ].balance;
249
+ require (remainingBalance >= currentQuantity);
250
+
251
+ // To prevent re-entrancy attacks, decrement the user's Set balance
252
+ unredeemedComponents[currentComponent][msg .sender ].balance = remainingBalance.sub (currentQuantity);
251
253
252
- assert (ERC20 (excludedComponent).transfer (msg .sender , quantity));
254
+ assert (ERC20 (currentComponent).transfer (msg .sender , currentQuantity));
255
+ }
256
+
257
+ emit LogRedeemExcluded (msg .sender , componentsToRedeem);
253
258
254
259
return true ;
255
260
}
@@ -265,4 +270,16 @@ contract SetToken is StandardToken, DetailedERC20("", "", 18), Set {
265
270
function getUnits () public view returns (uint []) {
266
271
return units;
267
272
}
273
+
274
+ function calculateTransferValue (uint currentUnits , uint quantity ) internal returns (uint ) {
275
+ // Transfer value is defined as the currentUnits (in GWei)
276
+ // multiplied by quantity in Wei divided by the units of gWei.
277
+ // We do this to allow fractional units to be defined
278
+ uint transferValue = currentUnits.fxpMul (quantity, 10 ** 9 );
279
+
280
+ // Protect against the case that the gWei divisor results in a value that is
281
+ // 0 and the user is able to generate Sets without sending a balance
282
+ assert (transferValue > 0 );
283
+ return transferValue;
284
+ }
268
285
}
0 commit comments