Skip to content

Commit beb9d29

Browse files
committed
enso receiver: deploy and execute flow
1 parent b421148 commit beb9d29

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

src/delegate/EnsoReceiver.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ contract EnsoReceiver is
3434

3535
error InvalidSender(address sender);
3636
error UnorderedNonceNotSupported();
37+
error InitializeExecuteFailed();
3738

3839
// for readability we use the same modifiers as the Ownable contract but this contract
3940
// does not allow the transferring of ownership, since the address is determinstically
@@ -59,6 +60,26 @@ contract EnsoReceiver is
5960
entryPoint = entryPoint_;
6061
}
6162

63+
function initializeAndExecuteShortcut(
64+
address owner_,
65+
address signer_,
66+
address entryPoint_,
67+
bytes calldata data
68+
)
69+
external
70+
payable
71+
initializer
72+
{
73+
_owner = owner_;
74+
signer = signer_;
75+
entryPoint = entryPoint_;
76+
77+
(bool success,) = address(this).call(data);
78+
if (!success) {
79+
revert InitializeExecuteFailed();
80+
}
81+
}
82+
6283
function safeExecute(IERC20 token, uint256 amount, bytes calldata data) external onlyOwnerOrEntryPoint {
6384
(bool success, bytes memory response) = address(this).call(data);
6485
if (success) {

src/factory/ERC4337CloneFactory.sol

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
// SPDX-License-Identifier: GPL-3.0-only
22
pragma solidity ^0.8.20;
33

4+
import { Token, TokenType } from "../interfaces/IEnsoRouter.sol";
5+
46
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";
510
import { LibClone } from "solady/utils/LibClone.sol";
611

712
contract ERC4337CloneFactory {
813
using LibClone for address;
14+
using SafeERC20 for IERC20;
915

1016
address public immutable implementation;
1117
address public immutable entryPoint;
1218

1319
event CloneDeployed(address clone, address account, address signer);
1420

21+
error UnsupportedTokenType(TokenType tokenType);
22+
error WrongMsgValue(uint256 value, uint256 expectedAmount);
23+
error AlreadyDeployed();
24+
1525
constructor(address implementation_, address entryPoint_) {
1626
implementation = implementation_;
1727
entryPoint = entryPoint_;
@@ -21,10 +31,35 @@ contract ERC4337CloneFactory {
2131
return _deploy(account, account);
2232
}
2333

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+
2446
function delegateDeploy(address account, address signer) external returns (address clone) {
2547
return _deploy(account, signer);
2648
}
2749

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+
2863
function getAddress(address account) external view returns (address) {
2964
return _getAddress(account, account);
3065
}
@@ -52,4 +87,47 @@ contract ERC4337CloneFactory {
5287
IERC4337CloneInitializer(clone).initialize(account, signer, entryPoint);
5388
emit CloneDeployed(clone, account, signer);
5489
}
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+
}
55133
}

src/factory/interfaces/IERC4337CloneInitializer.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@ pragma solidity ^0.8.20;
33

44
interface IERC4337CloneInitializer {
55
function initialize(address account, address signer, address entryPoint) external;
6+
function initializeAndExecuteShortcut(
7+
address account,
8+
address signer,
9+
address entryPoint,
10+
bytes calldata data
11+
)
12+
external
13+
payable;
614
}

0 commit comments

Comments
 (0)