@@ -23,6 +23,7 @@ import "./base/Managed.sol";
23
23
import "./storage/IGuardianStorage.sol " ;
24
24
import "./IModuleRegistry.sol " ;
25
25
import "../modules/common/IVersionManager.sol " ;
26
+ import "../modules/common/Utils.sol " ;
26
27
27
28
/**
28
29
* @title WalletFactory
@@ -31,56 +32,40 @@ import "../modules/common/IVersionManager.sol";
31
32
*/
32
33
contract WalletFactory is Owned , Managed {
33
34
35
+ address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ;
36
+
34
37
// The address of the module dregistry
35
38
address public moduleRegistry;
36
39
// The address of the base wallet implementation
37
40
address public walletImplementation;
38
41
// The address of the GuardianStorage
39
42
address public guardianStorage;
43
+ // The recipient of the refund
44
+ address public refundAddress;
40
45
41
46
// *************** Events *************************** //
42
47
43
48
event ModuleRegistryChanged (address addr );
44
- event WalletCreated (address indexed wallet , address indexed owner , address indexed guardian );
49
+ event RefundAddressChanged (address addr );
50
+ event WalletCreated (address indexed wallet , address indexed owner , address indexed guardian , address refundToken , uint256 refundAmount );
45
51
46
52
// *************** Constructor ********************** //
47
53
48
54
/**
49
55
* @notice Default constructor.
50
56
*/
51
- constructor (address _moduleRegistry , address _walletImplementation , address _guardianStorage ) public {
57
+ constructor (address _moduleRegistry , address _walletImplementation , address _guardianStorage , address _refundAddress ) public {
52
58
require (_moduleRegistry != address (0 ), "WF: ModuleRegistry address not defined " );
53
59
require (_walletImplementation != address (0 ), "WF: WalletImplementation address not defined " );
54
60
require (_guardianStorage != address (0 ), "WF: GuardianStorage address not defined " );
61
+ require (_refundAddress != address (0 ), "WF: refund address not defined " );
55
62
moduleRegistry = _moduleRegistry;
56
63
walletImplementation = _walletImplementation;
57
64
guardianStorage = _guardianStorage;
65
+ refundAddress = _refundAddress;
58
66
}
59
67
60
68
// *************** External Functions ********************* //
61
- /**
62
- * @notice Lets the manager create a wallet for an owner account.
63
- * The wallet is initialised with the version manager module, a version number and a first guardian.
64
- * The wallet is created using the CREATE opcode.
65
- * @param _owner The account address.
66
- * @param _versionManager The version manager module
67
- * @param _guardian The guardian address.
68
- * @param _version The version of the feature bundle.
69
- */
70
- function createWallet (
71
- address _owner ,
72
- address _versionManager ,
73
- address _guardian ,
74
- uint256 _version
75
- )
76
- external
77
- onlyManager
78
- {
79
- validateInputs (_owner, _versionManager, _guardian, _version);
80
- Proxy proxy = new Proxy (walletImplementation);
81
- address payable wallet = address (proxy);
82
- configureWallet (BaseWallet (wallet), _owner, _versionManager, _guardian, _version);
83
- }
84
69
85
70
/**
86
71
* @notice Lets the manager create a wallet for an owner account at a specific address.
@@ -97,7 +82,10 @@ contract WalletFactory is Owned, Managed {
97
82
address _versionManager ,
98
83
address _guardian ,
99
84
bytes32 _salt ,
100
- uint256 _version
85
+ uint256 _version ,
86
+ uint256 _refundAmount ,
87
+ address _refundToken ,
88
+ bytes calldata _ownerSignature
101
89
)
102
90
external
103
91
onlyManager
@@ -108,6 +96,15 @@ contract WalletFactory is Owned, Managed {
108
96
Proxy proxy = new Proxy {salt: newsalt}(walletImplementation);
109
97
address payable wallet = address (proxy);
110
98
configureWallet (BaseWallet (wallet), _owner, _versionManager, _guardian, _version);
99
+ if (_refundAmount > 0 && _ownerSignature.length == 65 ) {
100
+ validateAndRefund (wallet, _owner, _refundAmount, _refundToken, _ownerSignature);
101
+ }
102
+ // remove the factory from the authorised modules
103
+ BaseWallet (wallet).authoriseModule (address (this ), false );
104
+
105
+ // emit event
106
+ emit WalletCreated (wallet, _owner, _guardian, _refundToken, _refundAmount);
107
+
111
108
return wallet;
112
109
}
113
110
@@ -148,6 +145,16 @@ contract WalletFactory is Owned, Managed {
148
145
emit ModuleRegistryChanged (_moduleRegistry);
149
146
}
150
147
148
+ /**
149
+ * @notice Lets the owner change the refund address.
150
+ * @param _refundAddress The address to use for refunds.
151
+ */
152
+ function changeRefundAddress (address _refundAddress ) external onlyOwner {
153
+ require (_refundAddress != address (0 ), "WF: address cannot be null " );
154
+ refundAddress = _refundAddress;
155
+ emit RefundAddressChanged (_refundAddress);
156
+ }
157
+
151
158
/**
152
159
* @notice Inits the module for a wallet by doing nothing.
153
160
* The method can only be called by the wallet itself.
@@ -189,12 +196,6 @@ contract WalletFactory is Owned, Managed {
189
196
190
197
// upgrade the wallet
191
198
IVersionManager (_versionManager).upgradeWallet (address (_wallet), _version);
192
-
193
- // remove the factory from the authorised modules
194
- _wallet.authoriseModule (address (this ), false );
195
-
196
- // emit event
197
- emit WalletCreated (address (_wallet), _owner, _guardian);
198
199
}
199
200
200
201
/**
@@ -222,4 +223,68 @@ contract WalletFactory is Owned, Managed {
222
223
require (_guardian != (address (0 )), "WF: guardian cannot be null " );
223
224
require (_version > 0 , "WF: invalid _version " );
224
225
}
225
- }
226
+
227
+ /**
228
+ * @notice Refunds the creation of the wallet when provided with a valid signature from the wallet owner.
229
+ * @param _wallet The wallet created
230
+ * @param _owner The owner address
231
+ * @param _refundAmount The amount to refund
232
+ * @param _refundToken The token to use for the refund
233
+ * @param _ownerSignature A signature from the wallet owner approving the refund amount and token.
234
+ */
235
+ function validateAndRefund (
236
+ address _wallet ,
237
+ address _owner ,
238
+ uint256 _refundAmount ,
239
+ address _refundToken ,
240
+ bytes memory _ownerSignature
241
+ )
242
+ internal
243
+ {
244
+ bytes32 signedHash = keccak256 (abi.encodePacked (
245
+ "\x19Ethereum Signed Message:\n32 " ,
246
+ keccak256 (abi.encodePacked (_refundAmount, _refundToken))
247
+ ));
248
+ address signer = Utils.recoverSigner (signedHash, _ownerSignature, 0 );
249
+ if (signer == _owner) {
250
+ if (_refundToken == ETH_TOKEN) {
251
+ invokeWallet (_wallet, refundAddress, _refundAmount, "" );
252
+ } else {
253
+ bytes memory methodData = abi.encodeWithSignature ("transfer(address,uint256) " , refundAddress, _refundAmount);
254
+ bytes memory transferSuccessBytes = invokeWallet (_wallet, _refundToken, 0 , methodData);
255
+ if (transferSuccessBytes.length > 0 ) {
256
+ require (abi.decode (transferSuccessBytes, (bool )), "WF: Refund transfer failed " );
257
+ }
258
+ }
259
+ }
260
+ }
261
+
262
+ /**
263
+ * @notice Invoke the wallet to execute the refund transfer.
264
+ * @param _wallet The wallet
265
+ * @param _to The destination of the call
266
+ * @param _value The value of the call
267
+ * @param _data The data associated to the call
268
+ */
269
+ function invokeWallet (
270
+ address _wallet ,
271
+ address _to ,
272
+ uint256 _value ,
273
+ bytes memory _data
274
+ )
275
+ internal
276
+ returns (bytes memory _res )
277
+ {
278
+ bool success;
279
+ (success, _res) = _wallet.call (abi.encodeWithSignature ("invoke(address,uint256,bytes) " , _to, _value, _data));
280
+ if (success) {
281
+ (_res) = abi.decode (_res, (bytes ));
282
+ } else {
283
+ // solhint-disable-next-line no-inline-assembly
284
+ assembly {
285
+ returndatacopy (0 , 0 , returndatasize ())
286
+ revert (0 , returndatasize ())
287
+ }
288
+ }
289
+ }
290
+ }
0 commit comments