1
1
// SPDX-License-Identifier: GPL-3.0-only
2
2
pragma solidity ^ 0.8.20 ;
3
3
4
+ import { Token, TokenType } from "../interfaces/IEnsoRouter.sol " ;
5
+
4
6
import { IERC4337CloneInitializer } from "./interfaces/IERC4337CloneInitializer.sol " ;
7
+ import { IERC1155 } from "openzeppelin-contracts/token/ERC1155/IERC1155.sol " ;
8
+ import { IERC20 , SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol " ;
9
+ import { IERC721 } from "openzeppelin-contracts/token/ERC721/IERC721.sol " ;
5
10
import { LibClone } from "solady/utils/LibClone.sol " ;
6
11
7
12
contract ERC4337CloneFactory {
8
13
using LibClone for address ;
14
+ using SafeERC20 for IERC20 ;
9
15
10
16
address public immutable implementation;
11
17
address public immutable entryPoint;
12
18
13
19
event CloneDeployed (address clone , address account , address signer );
14
20
21
+ error UnsupportedTokenType (TokenType tokenType );
22
+ error WrongMsgValue (uint256 value , uint256 expectedAmount );
23
+ error AlreadyDeployed ();
24
+
15
25
constructor (address implementation_ , address entryPoint_ ) {
16
26
implementation = implementation_;
17
27
entryPoint = entryPoint_;
@@ -21,10 +31,35 @@ contract ERC4337CloneFactory {
21
31
return _deploy (account, account);
22
32
}
23
33
34
+ function deployAndExecute (
35
+ address account ,
36
+ Token calldata tokenIn ,
37
+ bytes calldata data
38
+ )
39
+ external
40
+ payable
41
+ returns (address clone )
42
+ {
43
+ return _deployAndExecute (account, account, tokenIn, data);
44
+ }
45
+
24
46
function delegateDeploy (address account , address signer ) external returns (address clone ) {
25
47
return _deploy (account, signer);
26
48
}
27
49
50
+ function delegateDeployAndExecute (
51
+ address account ,
52
+ address signer ,
53
+ Token calldata tokenIn ,
54
+ bytes calldata data
55
+ )
56
+ external
57
+ payable
58
+ returns (address clone )
59
+ {
60
+ return _deployAndExecute (account, signer, tokenIn, data);
61
+ }
62
+
28
63
function getAddress (address account ) external view returns (address ) {
29
64
return _getAddress (account, account);
30
65
}
@@ -52,4 +87,47 @@ contract ERC4337CloneFactory {
52
87
IERC4337CloneInitializer (clone).initialize (account, signer, entryPoint);
53
88
emit CloneDeployed (clone, account, signer);
54
89
}
90
+
91
+ function _deployAndExecute (
92
+ address account ,
93
+ address signer ,
94
+ Token calldata tokenIn ,
95
+ bytes calldata data
96
+ )
97
+ private
98
+ returns (address clone )
99
+ {
100
+ bytes32 salt = _getSalt (account, signer);
101
+ address clonePredicted = implementation.predictDeterministicAddress (salt, address (this ));
102
+ if (clonePredicted.code.length > 0 ) {
103
+ revert AlreadyDeployed (); // factory cannot call EnsoReceiver if it's already deployed
104
+ }
105
+ clone = implementation.cloneDeterministic (salt);
106
+ bool isNativeAsset = _transfer (tokenIn, clone);
107
+ if (! isNativeAsset && msg .value != 0 ) revert WrongMsgValue (msg .value , 0 );
108
+ IERC4337CloneInitializer (clone).initializeAndExecuteShortcut { value: msg .value }(
109
+ account, signer, entryPoint, data
110
+ );
111
+ emit CloneDeployed (clone, account, signer);
112
+ }
113
+
114
+ function _transfer (Token calldata token , address receiver ) private returns (bool isNativeAsset ) {
115
+ TokenType tokenType = token.tokenType;
116
+
117
+ if (tokenType == TokenType.ERC20 ) {
118
+ (IERC20 erc20 , uint256 amount ) = abi.decode (token.data, (IERC20 , uint256 ));
119
+ erc20.safeTransferFrom (msg .sender , receiver, amount);
120
+ } else if (tokenType == TokenType.Native) {
121
+ // no need to get amount, it will come from msg.value
122
+ isNativeAsset = true ;
123
+ } else if (tokenType == TokenType.ERC721 ) {
124
+ (IERC721 erc721 , uint256 tokenId ) = abi.decode (token.data, (IERC721 , uint256 ));
125
+ erc721.safeTransferFrom (msg .sender , receiver, tokenId);
126
+ } else if (tokenType == TokenType.ERC1155 ) {
127
+ (IERC1155 erc1155 , uint256 tokenId , uint256 amount ) = abi.decode (token.data, (IERC1155 , uint256 , uint256 ));
128
+ erc1155.safeTransferFrom (msg .sender , receiver, tokenId, amount, "0x " );
129
+ } else {
130
+ revert UnsupportedTokenType (tokenType);
131
+ }
132
+ }
55
133
}
0 commit comments