@across-protocol/contracts 5.0.18 → 5.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/interfaces/ICounterfactualBeacon.sol +91 -0
- package/contracts/interfaces/ICounterfactualDeposit.sol +21 -0
- package/contracts/libraries/TronClones.sol +13 -0
- package/contracts/periphery/counterfactual/CounterfactualBeacon.sol +115 -0
- package/contracts/periphery/counterfactual/CounterfactualBeaconBase.sol +121 -0
- package/contracts/periphery/counterfactual/CounterfactualBeaconBootstrap.sol +39 -0
- package/contracts/periphery/counterfactual/CounterfactualDeposit.sol +109 -28
- package/contracts/periphery/counterfactual/CounterfactualDepositCCTP.sol +102 -57
- package/contracts/periphery/counterfactual/CounterfactualDepositFactory.sol +61 -83
- package/contracts/periphery/counterfactual/CounterfactualDepositFactoryTron.sol +14 -24
- package/contracts/periphery/counterfactual/CounterfactualDepositOFT.sol +119 -58
- package/contracts/periphery/counterfactual/CounterfactualDepositSpokePool.sol +122 -90
- package/contracts/periphery/counterfactual/CounterfactualDepositSpokePoolTr.sol +10 -21
- package/contracts/periphery/counterfactual/CounterfactualDepositVanillaCCTP.sol +170 -0
- package/contracts/periphery/counterfactual/CounterfactualImplementationBase.sol +51 -0
- package/dist/broadcast/deployed-addresses.json +6 -6
- package/dist/evm/artifacts/AdminWithdrawManager.sol/AdminWithdrawManager.json +1 -1
- package/dist/evm/artifacts/CounterfactualBeacon.sol/CounterfactualBeacon.json +1 -0
- package/dist/evm/artifacts/CounterfactualBeaconBase.sol/CounterfactualBeaconBase.json +1 -0
- package/dist/evm/artifacts/CounterfactualBeaconBase.sol/IBeaconTarget.json +1 -0
- package/dist/evm/artifacts/CounterfactualBeaconBootstrap.sol/CounterfactualBeaconBootstrap.json +1 -0
- package/dist/evm/artifacts/CounterfactualDeposit.sol/CounterfactualDeposit.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositCCTP.sol/CounterfactualDepositCCTP.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositFactory.sol/CounterfactualDepositFactory.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositFactoryTron.sol/CounterfactualDepositFactoryTron.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositOFT.sol/CounterfactualDepositOFT.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositOFT.sol/ISponsoredOFTSrcPeriphery.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositSpokePool.sol/CounterfactualDepositSpokePool.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositSpokePoolTr.sol/CounterfactualDepositSpokePoolTr.json +1 -1
- package/dist/evm/artifacts/CounterfactualDepositVanillaCCTP.sol/CounterfactualDepositVanillaCCTP.json +1 -0
- package/dist/evm/artifacts/CounterfactualImplementationBase.sol/CounterfactualImplementationBase.json +1 -0
- package/dist/evm/artifacts/CounterfactualTestBase.sol/CounterfactualTestBase.json +1 -0
- package/dist/evm/artifacts/ICounterfactualBeacon.sol/ICounterfactualBeacon.json +1 -0
- package/dist/evm/artifacts/ICounterfactualDeposit.sol/ICounterfactualDeposit.json +1 -1
- package/dist/evm/artifacts/Ownable2StepUpgradeable.sol/Ownable2StepUpgradeable.json +1 -0
- package/dist/evm/artifacts/OwnableUpgradeable.sol/OwnableUpgradeable.json +1 -1
- package/dist/evm/artifacts/UUPSUpgradeable.sol/UUPSUpgradeable.json +1 -1
- package/package.json +1 -1
- package/dist/evm/artifacts/Clones.sol/Clones.json +0 -1
|
@@ -3,25 +3,26 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
5
5
|
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
7
|
+
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
6
8
|
import { SponsoredCCTPInterface } from "../../interfaces/SponsoredCCTPInterface.sol";
|
|
7
9
|
import { ICounterfactualImplementation } from "../../interfaces/ICounterfactualImplementation.sol";
|
|
10
|
+
import { CounterfactualImplementationBase } from "./CounterfactualImplementationBase.sol";
|
|
8
11
|
import { BPS_SCALAR } from "./CounterfactualConstants.sol";
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
* @notice Minimal interface for calling depositForBurn on SponsoredCCTPSrcPeriphery
|
|
12
|
-
* @custom:security-contact bugs@across.to
|
|
13
|
-
*/
|
|
13
|
+
/// @notice Minimal interface for `depositForBurn` on SponsoredCCTPSrcPeriphery.
|
|
14
14
|
interface ISponsoredCCTPSrcPeriphery {
|
|
15
15
|
function depositForBurn(SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature) external;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* @notice Route parameters committed to in the merkle leaf.
|
|
19
|
+
* @notice Route parameters committed to in the merkle leaf (chain-agnostic: no source chain, no token).
|
|
20
|
+
* @dev Burn token is always USDC (`beacon.usdc()`); source domain and periphery come from
|
|
21
|
+
* `beacon.cctpSourceDomain()` / `beacon.cctpSrcPeriphery()`.
|
|
20
22
|
*/
|
|
21
|
-
struct
|
|
23
|
+
struct CCTPRouteParams {
|
|
22
24
|
uint32 destinationDomain;
|
|
23
25
|
bytes32 mintRecipient;
|
|
24
|
-
bytes32 burnToken;
|
|
25
26
|
bytes32 destinationCaller;
|
|
26
27
|
uint256 cctpMaxFeeBps;
|
|
27
28
|
uint32 minFinalityThreshold;
|
|
@@ -33,7 +34,9 @@ struct CCTPDepositParams {
|
|
|
33
34
|
uint8 accountCreationMode;
|
|
34
35
|
uint8 executionMode;
|
|
35
36
|
bytes actionData;
|
|
36
|
-
|
|
37
|
+
/// @dev Selector of the beacon getter for this route's per-chain execution-fee cap (e.g.
|
|
38
|
+
/// `beacon.usdcCctpMaxExecutionFee.selector`).
|
|
39
|
+
bytes4 maxExecutionFeeGetter;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
/**
|
|
@@ -44,16 +47,20 @@ struct CCTPSubmitterData {
|
|
|
44
47
|
address executionFeeRecipient;
|
|
45
48
|
bytes32 nonce;
|
|
46
49
|
uint256 cctpDeadline;
|
|
47
|
-
|
|
50
|
+
uint256 executionFee;
|
|
51
|
+
uint32 signatureDeadline;
|
|
52
|
+
bytes peripherySignature;
|
|
53
|
+
bytes counterfactualSignature;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
/**
|
|
51
57
|
* @title CounterfactualDepositCCTP
|
|
52
|
-
* @notice
|
|
53
|
-
* @dev
|
|
58
|
+
* @notice Counterfactual deposit via SponsoredCCTP.
|
|
59
|
+
* @dev Delegatecalled by the dispatcher. Periphery, source domain, burn token (USDC) and fee signer come
|
|
60
|
+
* from the beacon, so the impl holds no chain-specific values and has one address per chain.
|
|
54
61
|
* @custom:security-contact bugs@across.to
|
|
55
62
|
*/
|
|
56
|
-
contract CounterfactualDepositCCTP is
|
|
63
|
+
contract CounterfactualDepositCCTP is CounterfactualImplementationBase, EIP712 {
|
|
57
64
|
using SafeERC20 for IERC20;
|
|
58
65
|
|
|
59
66
|
/**
|
|
@@ -62,77 +69,115 @@ contract CounterfactualDepositCCTP is ICounterfactualImplementation {
|
|
|
62
69
|
* @param executionFeeRecipient Address that received the execution fee.
|
|
63
70
|
* @param nonce CCTP nonce used for the deposit.
|
|
64
71
|
* @param cctpDeadline Deadline timestamp for the CCTP quote.
|
|
72
|
+
* @param executionFee Execution fee paid to the executor (in input token).
|
|
65
73
|
*/
|
|
66
74
|
event CCTPDepositExecuted(
|
|
67
75
|
uint256 amount,
|
|
68
76
|
address indexed executionFeeRecipient,
|
|
69
77
|
bytes32 nonce,
|
|
70
|
-
uint256 cctpDeadline
|
|
78
|
+
uint256 cctpDeadline,
|
|
79
|
+
uint256 executionFee
|
|
71
80
|
);
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
error InvalidSignature();
|
|
83
|
+
error SignatureExpired();
|
|
84
|
+
error MaxExecutionFee();
|
|
75
85
|
|
|
76
|
-
/// @notice
|
|
77
|
-
|
|
86
|
+
/// @notice EIP-712 typehash binding the local fee signature to the route, nonce, runtime fee, and deadline.
|
|
87
|
+
bytes32 public constant EXECUTE_CCTP_TYPEHASH =
|
|
88
|
+
keccak256("ExecuteCCTP(bytes32 routeParamsHash,bytes32 nonce,uint256 executionFee,uint32 signatureDeadline)");
|
|
78
89
|
|
|
79
|
-
constructor(
|
|
80
|
-
srcPeriphery = _srcPeriphery;
|
|
81
|
-
sourceDomain = _sourceDomain;
|
|
82
|
-
}
|
|
90
|
+
constructor() EIP712("CounterfactualDepositCCTP", "v2.0.0") {}
|
|
83
91
|
|
|
84
92
|
/**
|
|
85
93
|
* @inheritdoc ICounterfactualImplementation
|
|
86
|
-
* @dev Bridges tokens via SponsoredCCTP. `
|
|
87
|
-
* `
|
|
88
|
-
* ERC-20
|
|
94
|
+
* @dev Bridges tokens via SponsoredCCTP. `routeParamsEncoded` is ABI-encoded as `CCTPRouteParams`;
|
|
95
|
+
* `submitterDataEncoded` as `CCTPSubmitterData` (includes a signature forwarded to the CCTP periphery).
|
|
96
|
+
* ERC-20 (USDC) only. The local fee signature binds the route (`routeParamsHash`); `amount` is
|
|
97
|
+
* bound transitively via the periphery quote signature forwarded to `srcPeriphery`.
|
|
89
98
|
*/
|
|
90
|
-
function execute(bytes calldata
|
|
91
|
-
|
|
92
|
-
CCTPSubmitterData memory
|
|
99
|
+
function execute(bytes calldata routeParamsEncoded, bytes calldata submitterDataEncoded) external payable {
|
|
100
|
+
CCTPRouteParams memory routeParams = abi.decode(routeParamsEncoded, (CCTPRouteParams));
|
|
101
|
+
CCTPSubmitterData memory submitterData = abi.decode(submitterDataEncoded, (CCTPSubmitterData));
|
|
102
|
+
|
|
103
|
+
_verifySignature(keccak256(routeParamsEncoded), submitterData);
|
|
104
|
+
if (submitterData.executionFee > _resolveBeaconUint(routeParams.maxExecutionFeeGetter))
|
|
105
|
+
revert MaxExecutionFee();
|
|
93
106
|
|
|
94
|
-
address
|
|
107
|
+
address srcPeriphery = _requireConfigured(_beacon().cctpSrcPeriphery());
|
|
108
|
+
address inputToken = _requireConfigured(_beacon().usdc());
|
|
95
109
|
|
|
96
|
-
|
|
110
|
+
// Fee paid before the periphery call (load-bearing): the local signature binds the route and
|
|
111
|
+
// (nonce, fee, deadline) but not `amount`, so amount-replay protection is the periphery's nonce
|
|
112
|
+
// check — a replayed fee reverts at `depositForBurn` and rolls back this transfer.
|
|
113
|
+
if (submitterData.executionFee > 0)
|
|
114
|
+
IERC20(inputToken).safeTransfer(submitterData.executionFeeRecipient, submitterData.executionFee);
|
|
97
115
|
|
|
98
|
-
uint256 depositAmount =
|
|
116
|
+
uint256 depositAmount = submitterData.amount - submitterData.executionFee;
|
|
99
117
|
|
|
100
118
|
IERC20(inputToken).forceApprove(srcPeriphery, depositAmount);
|
|
101
119
|
|
|
102
|
-
_depositForBurn(
|
|
120
|
+
_depositForBurn(srcPeriphery, inputToken, routeParams, submitterData, depositAmount);
|
|
103
121
|
|
|
104
|
-
emit CCTPDepositExecuted(
|
|
122
|
+
emit CCTPDepositExecuted(
|
|
123
|
+
submitterData.amount,
|
|
124
|
+
submitterData.executionFeeRecipient,
|
|
125
|
+
submitterData.nonce,
|
|
126
|
+
submitterData.cctpDeadline,
|
|
127
|
+
submitterData.executionFee
|
|
128
|
+
);
|
|
105
129
|
}
|
|
106
130
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
function _verifySignature(bytes32 routeParamsHash, CCTPSubmitterData memory submitterData) private view {
|
|
132
|
+
if (block.timestamp > submitterData.signatureDeadline) revert SignatureExpired();
|
|
133
|
+
bytes32 structHash = keccak256(
|
|
134
|
+
abi.encode(
|
|
135
|
+
EXECUTE_CCTP_TYPEHASH,
|
|
136
|
+
routeParamsHash,
|
|
137
|
+
submitterData.nonce,
|
|
138
|
+
submitterData.executionFee,
|
|
139
|
+
submitterData.signatureDeadline
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
if (ECDSA.recover(_hashTypedDataV4(structHash), submitterData.counterfactualSignature) != _beacon().signer())
|
|
143
|
+
revert InvalidSignature();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// @notice Calls `depositForBurn` on the SponsoredCCTPSrcPeriphery with the constructed quote.
|
|
147
|
+
/// @param srcPeriphery The CCTP source periphery (from the beacon).
|
|
148
|
+
/// @param inputToken The burn token, USDC (from the beacon).
|
|
149
|
+
/// @param routeParams Route parameters from the merkle leaf.
|
|
150
|
+
/// @param submitterData Submitter-provided execution data.
|
|
151
|
+
/// @param depositAmount Amount to deposit after deducting the execution fee.
|
|
152
|
+
function _depositForBurn(
|
|
153
|
+
address srcPeriphery,
|
|
154
|
+
address inputToken,
|
|
155
|
+
CCTPRouteParams memory routeParams,
|
|
156
|
+
CCTPSubmitterData memory submitterData,
|
|
157
|
+
uint256 depositAmount
|
|
158
|
+
) private {
|
|
114
159
|
ISponsoredCCTPSrcPeriphery(srcPeriphery).depositForBurn(
|
|
115
160
|
SponsoredCCTPInterface.SponsoredCCTPQuote({
|
|
116
|
-
sourceDomain:
|
|
117
|
-
destinationDomain:
|
|
118
|
-
mintRecipient:
|
|
161
|
+
sourceDomain: _beacon().cctpSourceDomain(),
|
|
162
|
+
destinationDomain: routeParams.destinationDomain,
|
|
163
|
+
mintRecipient: routeParams.mintRecipient,
|
|
119
164
|
amount: depositAmount,
|
|
120
|
-
burnToken:
|
|
121
|
-
destinationCaller:
|
|
122
|
-
maxFee: (depositAmount *
|
|
123
|
-
minFinalityThreshold:
|
|
124
|
-
nonce:
|
|
125
|
-
deadline:
|
|
126
|
-
maxBpsToSponsor:
|
|
127
|
-
maxUserSlippageBps:
|
|
128
|
-
finalRecipient:
|
|
129
|
-
finalToken:
|
|
130
|
-
destinationDex:
|
|
131
|
-
accountCreationMode:
|
|
132
|
-
executionMode:
|
|
133
|
-
actionData:
|
|
165
|
+
burnToken: bytes32(uint256(uint160(inputToken))),
|
|
166
|
+
destinationCaller: routeParams.destinationCaller,
|
|
167
|
+
maxFee: (depositAmount * routeParams.cctpMaxFeeBps) / BPS_SCALAR,
|
|
168
|
+
minFinalityThreshold: routeParams.minFinalityThreshold,
|
|
169
|
+
nonce: submitterData.nonce,
|
|
170
|
+
deadline: submitterData.cctpDeadline,
|
|
171
|
+
maxBpsToSponsor: routeParams.maxBpsToSponsor,
|
|
172
|
+
maxUserSlippageBps: routeParams.maxUserSlippageBps,
|
|
173
|
+
finalRecipient: routeParams.finalRecipient,
|
|
174
|
+
finalToken: routeParams.finalToken,
|
|
175
|
+
destinationDex: routeParams.destinationDex,
|
|
176
|
+
accountCreationMode: routeParams.accountCreationMode,
|
|
177
|
+
executionMode: routeParams.executionMode,
|
|
178
|
+
actionData: routeParams.actionData
|
|
134
179
|
}),
|
|
135
|
-
|
|
180
|
+
submitterData.peripherySignature
|
|
136
181
|
);
|
|
137
182
|
}
|
|
138
183
|
}
|
|
@@ -1,112 +1,90 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BUSL-1.1
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
|
|
5
|
+
import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
|
|
6
|
+
import { CounterfactualDeposit } from "./CounterfactualDeposit.sol";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @title CounterfactualDepositFactory
|
|
9
|
-
* @notice
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* @notice Deterministically deploys counterfactual `BeaconProxy` instances. Each proxy uses the global
|
|
11
|
+
* `CounterfactualBeacon` as its beacon (so it always runs the registry's current implementation) and
|
|
12
|
+
* is initialized with its route root in the constructor `data`.
|
|
13
|
+
* @dev The `initialize(initialRoot)` call data is part of the proxy's init code, so for a fixed `salt`
|
|
14
|
+
* the address is `f(salt, initialRoot)`. Callers wanting one canonical address per destination
|
|
15
|
+
* identity (and automatic cross-chain parity) should pass `salt = 0`; a non-zero `salt` yields
|
|
16
|
+
* additional addresses for the same `initialRoot` and requires the caller to reuse the same `salt`
|
|
17
|
+
* on every chain for parity. The factory and the beacon must be deployed deterministically at
|
|
18
|
+
* identical addresses across chains for cross-chain address parity.
|
|
19
|
+
* `predictAddress` / `_initCode` / `_computeProxyAddress` are `virtual`/`internal` so chain-specific
|
|
20
|
+
* variants (e.g. Tron's 0x41 CREATE2 prefix) can override prediction.
|
|
12
21
|
* @custom:security-contact bugs@across.to
|
|
13
22
|
*/
|
|
14
|
-
contract CounterfactualDepositFactory
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
) public returns (address
|
|
27
|
-
|
|
28
|
-
counterfactualDepositImplementation,
|
|
29
|
-
abi.encode(paramsHash),
|
|
30
|
-
salt
|
|
31
|
-
);
|
|
32
|
-
emit DepositAddressCreated(depositAddress, counterfactualDepositImplementation, paramsHash, salt);
|
|
23
|
+
contract CounterfactualDepositFactory {
|
|
24
|
+
/// @notice The beacon (the `CounterfactualBeacon`) every deployed proxy points at.
|
|
25
|
+
address public immutable BEACON;
|
|
26
|
+
|
|
27
|
+
/// @notice Emitted when a counterfactual proxy is deployed.
|
|
28
|
+
event CounterfactualDeployed(address indexed counterfactual, bytes32 initialRoot);
|
|
29
|
+
|
|
30
|
+
constructor(address beacon) {
|
|
31
|
+
BEACON = beacon;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @notice Predict the proxy address for a given `salt` and `initialRoot`.
|
|
35
|
+
function predictAddress(bytes32 salt, bytes32 initialRoot) public view virtual returns (address) {
|
|
36
|
+
return _computeProxyAddress(salt, keccak256(_initCode(initialRoot)));
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
/// @notice Deploy the proxy for `salt` and `initialRoot` (already initialized + always-current via
|
|
40
|
+
/// the beacon). Reverts if already deployed.
|
|
41
|
+
function deploy(bytes32 salt, bytes32 initialRoot) public returns (address counterfactual) {
|
|
42
|
+
counterfactual = address(
|
|
43
|
+
new BeaconProxy{ salt: salt }(BEACON, abi.encodeCall(CounterfactualDeposit.initialize, (initialRoot)))
|
|
44
|
+
);
|
|
45
|
+
emit CounterfactualDeployed(counterfactual, initialRoot);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
* @notice Deploys and executes a deposit in one transaction
|
|
46
|
-
* @dev Reverts if the clone is already deployed. Use deployIfNeededAndExecute for idempotent behavior.
|
|
47
|
-
* @param counterfactualDepositImplementation Implementation contract address
|
|
48
|
-
* @param paramsHash keccak256 hash of the ABI-encoded route parameters
|
|
49
|
-
* @param salt Unique salt for address generation
|
|
50
|
-
* @param executeCalldata Calldata to forward to the clone (e.g. abi.encodeCall of executeDeposit)
|
|
51
|
-
* @return depositAddress Address of deposit contract
|
|
52
|
-
*/
|
|
48
|
+
/// @notice Deploy, then forward `executeCalldata` to the proxy. Reverts if already deployed.
|
|
53
49
|
function deployAndExecute(
|
|
54
|
-
address counterfactualDepositImplementation,
|
|
55
|
-
bytes32 paramsHash,
|
|
56
50
|
bytes32 salt,
|
|
51
|
+
bytes32 initialRoot,
|
|
57
52
|
bytes calldata executeCalldata
|
|
58
|
-
) external payable returns (address
|
|
59
|
-
|
|
60
|
-
_execute(
|
|
53
|
+
) external payable returns (address counterfactual) {
|
|
54
|
+
counterfactual = deploy(salt, initialRoot);
|
|
55
|
+
_execute(counterfactual, executeCalldata);
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
* @notice Deploys (if not already deployed) and executes a deposit in one transaction
|
|
65
|
-
* @dev Unlike deployAndExecute, this does not revert if the clone already exists.
|
|
66
|
-
* @param counterfactualDepositImplementation Implementation contract address
|
|
67
|
-
* @param paramsHash keccak256 hash of the ABI-encoded route parameters
|
|
68
|
-
* @param salt Unique salt for address generation
|
|
69
|
-
* @param executeCalldata Calldata to forward to the clone (e.g. abi.encodeCall of executeDeposit)
|
|
70
|
-
* @return depositAddress Address of deposit contract
|
|
71
|
-
*/
|
|
58
|
+
/// @notice Deploy if needed (idempotent), then forward `executeCalldata` to the proxy.
|
|
72
59
|
function deployIfNeededAndExecute(
|
|
73
|
-
address counterfactualDepositImplementation,
|
|
74
|
-
bytes32 paramsHash,
|
|
75
60
|
bytes32 salt,
|
|
61
|
+
bytes32 initialRoot,
|
|
76
62
|
bytes calldata executeCalldata
|
|
77
|
-
) external payable returns (address
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
_execute(
|
|
63
|
+
) external payable returns (address counterfactual) {
|
|
64
|
+
counterfactual = predictAddress(salt, initialRoot);
|
|
65
|
+
if (counterfactual.code.length == 0) deploy(salt, initialRoot);
|
|
66
|
+
_execute(counterfactual, executeCalldata);
|
|
81
67
|
}
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
* @param counterfactualDepositImplementation Implementation contract address
|
|
86
|
-
* @param paramsHash keccak256 hash of the ABI-encoded route parameters
|
|
87
|
-
* @param salt Unique salt for address generation
|
|
88
|
-
* @return Predicted address
|
|
89
|
-
*/
|
|
90
|
-
function predictDepositAddress(
|
|
91
|
-
address counterfactualDepositImplementation,
|
|
92
|
-
bytes32 paramsHash,
|
|
93
|
-
bytes32 salt
|
|
94
|
-
) public view virtual returns (address) {
|
|
69
|
+
/// @dev The proxy creation code (creation bytecode + constructor args) — `virtual` for Tron etc.
|
|
70
|
+
function _initCode(bytes32 initialRoot) internal view virtual returns (bytes memory) {
|
|
95
71
|
return
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
abi.encode(
|
|
99
|
-
salt
|
|
72
|
+
abi.encodePacked(
|
|
73
|
+
type(BeaconProxy).creationCode,
|
|
74
|
+
abi.encode(BEACON, abi.encodeCall(CounterfactualDeposit.initialize, (initialRoot)))
|
|
100
75
|
);
|
|
101
76
|
}
|
|
102
77
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
78
|
+
/// @dev CREATE2 address derivation for a `salt` and the proxy init-code hash. `virtual` so
|
|
79
|
+
/// chain-specific variants (e.g. Tron's 0x41 prefix) can override the derivation. Deployment
|
|
80
|
+
/// via `deploy()` uses the `create2` opcode directly, which is correct on every chain; this
|
|
81
|
+
/// hook only governs off-chain-style prediction.
|
|
82
|
+
function _computeProxyAddress(bytes32 salt, bytes32 initCodeHash) internal view virtual returns (address) {
|
|
83
|
+
return Create2.computeAddress(salt, initCodeHash, address(this));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _execute(address counterfactual, bytes calldata executeCalldata) private {
|
|
87
|
+
(bool success, bytes memory returnData) = counterfactual.call{ value: msg.value }(executeCalldata);
|
|
110
88
|
if (!success) {
|
|
111
89
|
assembly {
|
|
112
90
|
revert(add(returnData, 32), mload(returnData))
|
|
@@ -6,31 +6,21 @@ import { CounterfactualDepositFactory } from "./CounterfactualDepositFactory.sol
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @title CounterfactualDepositFactoryTron
|
|
9
|
-
* @notice Tron-compatible
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* @notice Tron-compatible variant of `CounterfactualDepositFactory` for deploying counterfactual
|
|
10
|
+
* proxies via CREATE2 on the TVM.
|
|
11
|
+
* @dev Tron's TVM uses 0x41 instead of EVM's 0xff as the CREATE2 address derivation prefix. The
|
|
12
|
+
* `create2` opcode itself uses 0x41 natively, so deployment via the inherited `deploy(...)`
|
|
13
|
+
* (which uses `new BeaconProxy{ salt: 0 }(...)`) produces the correct address on Tron. Only
|
|
14
|
+
* OZ's `Create2.computeAddress` — used for off-chain-style prediction — hardcodes 0xff and would
|
|
15
|
+
* return the wrong address. This variant overrides the `_computeProxyAddress` hook to predict
|
|
16
|
+
* with the 0x41 prefix; all other logic is inherited unchanged.
|
|
17
|
+
* @custom:security-contact bugs@across.to
|
|
14
18
|
*/
|
|
15
19
|
contract CounterfactualDepositFactoryTron is CounterfactualDepositFactory {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @param salt Unique salt for address generation.
|
|
22
|
-
* @return Predicted address of the clone on Tron.
|
|
23
|
-
*/
|
|
24
|
-
function predictDepositAddress(
|
|
25
|
-
address counterfactualDepositImplementation,
|
|
26
|
-
bytes32 paramsHash,
|
|
27
|
-
bytes32 salt
|
|
28
|
-
) public view override returns (address) {
|
|
29
|
-
return
|
|
30
|
-
TronClones.predictDeterministicAddressWithImmutableArgs(
|
|
31
|
-
counterfactualDepositImplementation,
|
|
32
|
-
abi.encode(paramsHash),
|
|
33
|
-
salt
|
|
34
|
-
);
|
|
20
|
+
constructor(address beacon) CounterfactualDepositFactory(beacon) {} // solhint-disable-line no-empty-blocks
|
|
21
|
+
|
|
22
|
+
/// @dev TRON OVERRIDE: predict the `BeaconProxy` CREATE2 address using the 0x41 prefix.
|
|
23
|
+
function _computeProxyAddress(bytes32 salt, bytes32 initCodeHash) internal view override returns (address) {
|
|
24
|
+
return TronClones.computeAddress(salt, initCodeHash, address(this));
|
|
35
25
|
}
|
|
36
26
|
}
|