-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathCCIPAdapter.sol
162 lines (137 loc) · 5.65 KB
/
CCIPAdapter.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {SafeCast} from 'solidity-utils/contracts/oz-common/SafeCast.sol';
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import {BaseAdapter, IBaseAdapter} from '../BaseAdapter.sol';
import {ICCIPAdapter, IRouterClient} from './ICCIPAdapter.sol';
import {IAny2EVMMessageReceiver, Client} from './interfaces/IAny2EVMMessageReceiver.sol';
import {IERC165} from './interfaces/IERC165.sol';
import {Errors} from '../../libs/Errors.sol';
import {ChainIds} from 'solidity-utils/contracts/utils/ChainHelpers.sol';
/**
* @title CCIPAdapter
* @author BGD Labs
* @notice CCIP bridge adapter. Used to send and receive messages cross chain
* @dev it uses the eth balance of CrossChainController contract to pay for message bridging as the method to bridge
is called via delegate call
*/
contract CCIPAdapter is ICCIPAdapter, BaseAdapter, IAny2EVMMessageReceiver, IERC165 {
using SafeERC20 for IERC20;
/// @inheritdoc ICCIPAdapter
IRouterClient public immutable CCIP_ROUTER;
/// @inheritdoc ICCIPAdapter
IERC20 public immutable LINK_TOKEN;
/**
* @notice only calls from the set router are accepted.
*/
modifier onlyRouter() {
require(msg.sender == address(CCIP_ROUTER), Errors.CALLER_NOT_CCIP_ROUTER);
_;
}
/**
* @param crossChainController address of the cross chain controller that will use this bridge adapter
* @param ccipRouter ccip entry point address
* @param providerGasLimit base gas limit used by the bridge adapter
* @param trustedRemotes list of remote configurations to set as trusted
* @param linkToken address of the erc20 LINK token
*/
constructor(
address crossChainController,
address ccipRouter,
uint256 providerGasLimit,
TrustedRemotesConfig[] memory trustedRemotes,
address linkToken
) BaseAdapter(crossChainController, providerGasLimit, 'CCIP adapter', trustedRemotes) {
require(ccipRouter != address(0), Errors.CCIP_ROUTER_CANT_BE_ADDRESS_0);
require(linkToken != address(0), Errors.LINK_TOKEN_CANT_BE_ADDRESS_0);
CCIP_ROUTER = IRouterClient(ccipRouter);
LINK_TOKEN = IERC20(linkToken);
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return
interfaceId == type(IAny2EVMMessageReceiver).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
/// @inheritdoc IBaseAdapter
function forwardMessage(
address receiver,
uint256 executionGasLimit,
uint256 destinationChainId,
bytes calldata message
) external returns (address, uint256) {
uint64 nativeChainId = SafeCast.toUint64(infraToNativeChainId(destinationChainId));
require(CCIP_ROUTER.isChainSupported(nativeChainId), Errors.DESTINATION_CHAIN_ID_NOT_SUPPORTED);
require(receiver != address(0), Errors.RECEIVER_NOT_SET);
uint256 totalGasLimit = executionGasLimit + BASE_GAS_LIMIT;
Client.EVMExtraArgsV1 memory evmExtraArgs = Client.EVMExtraArgsV1({
gasLimit: totalGasLimit,
strict: false
});
bytes memory extraArgs = Client._argsToBytes(evmExtraArgs);
Client.EVM2AnyMessage memory ccipMessage = Client.EVM2AnyMessage({
receiver: abi.encode(receiver),
data: message,
tokenAmounts: new Client.EVMTokenAmount[](0),
feeToken: address(LINK_TOKEN),
extraArgs: extraArgs
});
uint256 clFee = CCIP_ROUTER.getFee(nativeChainId, ccipMessage);
require(clFee != 0, Errors.CCIP_MESSAGE_IS_INVALID);
require(
LINK_TOKEN.balanceOf(address(this)) >= clFee,
Errors.NOT_ENOUGH_VALUE_TO_PAY_BRIDGE_FEES
);
bytes32 messageId = CCIP_ROUTER.ccipSend(nativeChainId, ccipMessage);
return (address(CCIP_ROUTER), uint256(messageId));
}
/// @inheritdoc IAny2EVMMessageReceiver
function ccipReceive(Client.Any2EVMMessage calldata message) external onlyRouter {
address srcAddress = abi.decode(message.sender, (address));
uint256 originChainId = nativeToInfraChainId(message.sourceChainSelector);
require(
_trustedRemotes[originChainId] == srcAddress && srcAddress != address(0),
Errors.REMOTE_NOT_TRUSTED
);
_registerReceivedMessage(message.data, originChainId);
}
/// @inheritdoc IBaseAdapter
function setupPayments() external override {
LINK_TOKEN.forceApprove(address(CCIP_ROUTER), type(uint256).max);
}
/// @inheritdoc IBaseAdapter
function nativeToInfraChainId(
uint256 nativeChainId
) public pure virtual override returns (uint256) {
if (nativeChainId == uint64(5009297550715157269)) {
return ChainIds.ETHEREUM;
} else if (nativeChainId == uint64(6433500567565415381)) {
return ChainIds.AVALANCHE;
} else if (nativeChainId == uint64(4051577828743386545)) {
return ChainIds.POLYGON;
} else if (nativeChainId == uint64(11344663589394136015)) {
return ChainIds.BNB;
} else if (nativeChainId == uint64(1346049177634351622)) {
return ChainIds.CELO;
}
return nativeChainId;
}
/// @inheritdoc IBaseAdapter
function infraToNativeChainId(
uint256 infraChainId
) public pure virtual override returns (uint256) {
if (infraChainId == ChainIds.ETHEREUM) {
return uint64(5009297550715157269);
} else if (infraChainId == ChainIds.AVALANCHE) {
return uint64(6433500567565415381);
} else if (infraChainId == ChainIds.POLYGON) {
return uint64(4051577828743386545);
} else if (infraChainId == ChainIds.BNB) {
return uint64(11344663589394136015);
} else if (infraChainId == ChainIds.CELO) {
return uint64(1346049177634351622);
}
return infraChainId;
}
}