-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathLayerZeroModule.sol
484 lines (437 loc) · 17.6 KB
/
LayerZeroModule.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
// SPDX-License-Identifier: UNLICENSED
/*
┌───────────┐
│ HOLOGRAPH │
└───────────┘
╔═════════════════════════════════════════════════════════════╗
║ ║
║ / ^ \ ║
║ ~~*~~ ¸ ║
║ [ '<>:<>' ] │░░░ ║
║ ╔╗ _/"\_ ╔╣ ║
║ ┌─╬╬─┐ """ ┌─╬╬─┐ ║
║ ┌─┬┘ ╠╣ └┬─┐ \_/ ┌─┬┘ ╠╣ └┬─┐ ║
║ ┌─┬┘ │ ╠╣ │ └┬─┐ ┌─┬┘ │ ╠╣ │ └┬─┐ ║
║ ┌─┬┘ │ │ ╠╣ │ │ └┬─┐ ┌─┬┘ │ │ ╠╣ │ │ └┬─┐ ║
║ ┌─┬┘ │ │ │ ╠╣ │ │ │ └┬┐ ┌┬┘ │ │ │ ╠╣ │ │ │ └┬─┐ ║
╠┬┘ │ │ │ │ ╠╣ │ │ │ │└¤┘│ │ │ │ ╠╣ │ │ │ │ └┬╣
║│ │ │ │ │ ╠╣ │ │ │ │ │ │ │ │ ╠╣ │ │ │ │ │║
╠╩══╩══╩══╩══╩══╬╬══╩══╩══╩══╩═══╩══╩══╩══╩══╬╬══╩══╩══╩══╩══╩╣
╠┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴╬╬┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴╬╬┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴╣
║ ╠╣ ╠╣ ║
║ ╠╣ ╠╣ ║
║ , ╠╣ , ,' * ╠╣ ║
║~~~~~^~~~~~~~~┌╬╬┐~~~^~~~~~~~~^^~~~~~~~~^~~┌╬╬┐~~~~~~~^~~~~~~║
╚══════════════╩╩╩╩═════════════════════════╩╩╩╩══════════════╝
- one protocol, one bridge = infinite possibilities -
***************************************************************
DISCLAIMER: U.S Patent Pending
LICENSE: Holograph Limited Public License (H-LPL)
https://holograph.xyz/licenses/h-lpl/1.0.0
This license governs use of the accompanying software. If you
use the software, you accept this license. If you do not accept
the license, you are not permitted to use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and
"distribution" have the same meaning here as under U.S.
copyright law. A "contribution" is the original software, or
any additions or changes to the software. A "contributor" is
any person that distributes its contribution under this
license. "Licensed patents" are a contributor’s patent claims
that read directly on its contribution.
2. Grant of Rights
A) Copyright Grant- Subject to the terms of this license,
including the license conditions and limitations in sections 3
and 4, each contributor grants you a non-exclusive, worldwide,
royalty-free copyright license to reproduce its contribution,
prepare derivative works of its contribution, and distribute
its contribution or any derivative works that you create.
B) Patent Grant- Subject to the terms of this license,
including the license conditions and limitations in section 3,
each contributor grants you a non-exclusive, worldwide,
royalty-free license under its licensed patents to make, have
made, use, sell, offer for sale, import, and/or otherwise
dispose of its contribution in the software or derivative works
of the contribution in the software.
3. Conditions and Limitations
A) No Trademark License- This license does not grant you rights
to use any contributors’ name, logo, or trademarks.
B) If you bring a patent claim against any contributor over
patents that you claim are infringed by the software, your
patent license from such contributor is terminated with
immediate effect.
C) If you distribute any portion of the software, you must
retain all copyright, patent, trademark, and attribution
notices that are present in the software.
D) If you distribute any portion of the software in source code
form, you may do so only under this license by including a
complete copy of this license with your distribution. If you
distribute any portion of the software in compiled or object
code form, you may only do so under a license that complies
with this license.
E) The software is licensed “as-is.” You bear all risks of
using it. The contributors give no express warranties,
guarantees, or conditions. You may have additional consumer
rights under your local laws which this license cannot change.
To the extent permitted under your local laws, the contributors
exclude all implied warranties, including those of
merchantability, fitness for a particular purpose and
non-infringement.
4. (F) Platform Limitation- The licenses granted in sections
2.A & 2.B extend only to the software or derivative works that
you create that run on a Holograph system product.
***************************************************************
*/
pragma solidity 0.8.13;
import "../abstract/Admin.sol";
import "../abstract/Initializable.sol";
import "../enum/ChainIdType.sol";
import "../interface/CrossChainMessageInterface.sol";
import "../interface/HolographOperatorInterface.sol";
import "../interface/InitializableInterface.sol";
import "../interface/HolographInterfacesInterface.sol";
import "../interface/LayerZeroModuleInterface.sol";
import "../interface/LayerZeroOverrides.sol";
/**
* @title Holograph LayerZero Module
* @author https://github.com/holographxyz
* @notice Holograph module for enabling LayerZero cross-chain messaging
* @dev This contract abstracts all of the LayerZero specific logic into an isolated module
*/
contract LayerZeroModule is Admin, Initializable, CrossChainMessageInterface, LayerZeroModuleInterface {
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.bridge')) - 1)
*/
bytes32 constant _bridgeSlot = 0xeb87cbb21687feb327e3d58c6c16d552231d12c7a0e8115042a4165fac8a77f9;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.interfaces')) - 1)
*/
bytes32 constant _interfacesSlot = 0xbd3084b8c09da87ad159c247a60e209784196be2530cecbbd8f337fdd1848827;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.lZEndpoint')) - 1)
*/
bytes32 constant _lZEndpointSlot = 0x56825e447adf54cdde5f04815fcf9b1dd26ef9d5c053625147c18b7c13091686;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.operator')) - 1)
*/
bytes32 constant _operatorSlot = 0x7caba557ad34138fa3b7e43fb574e0e6cc10481c3073e0dffbc560db81b5c60f;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.operator')) - 1)
*/
bytes32 constant _baseGasSlot = 0x1eaa99919d5563fbfdd75d9d906ff8de8cf52beab1ed73875294c8a0c9e9d83a;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.operator')) - 1)
*/
bytes32 constant _gasPerByteSlot = 0x99d8b07d37c89d4c4f4fa0fd9b7396caeb5d1d4e58b41c61c71e3cf7d424a625;
/**
* @dev Constructor is left empty and init is used instead
*/
constructor() {}
/**
* @notice Used internally to initialize the contract instead of through a constructor
* @dev This function is called by the deployer/factory when creating a contract
* @param initPayload abi encoded payload to use for contract initilaization
*/
function init(bytes memory initPayload) external override returns (bytes4) {
require(!_isInitialized(), "HOLOGRAPH: already initialized");
(address bridge, address interfaces, address operator, uint256 baseGas, uint256 gasPerByte) = abi.decode(
initPayload,
(address, address, address, uint256, uint256)
);
assembly {
sstore(_adminSlot, origin())
sstore(_bridgeSlot, bridge)
sstore(_interfacesSlot, interfaces)
sstore(_operatorSlot, operator)
sstore(_baseGasSlot, baseGas)
sstore(_gasPerByteSlot, gasPerByte)
}
_setInitialized();
return InitializableInterface.init.selector;
}
/**
* @notice Receive cross-chain message from LayerZero
* @dev This function only allows calls from the configured LayerZero endpoint address
*/
function lzReceive(
uint16, /* _srcChainId*/
bytes calldata _srcAddress,
uint64, /* _nonce*/
bytes calldata _payload
) external payable {
assembly {
/**
* @dev check if msg.sender is LayerZero Endpoint
*/
switch eq(sload(_lZEndpointSlot), caller())
case 0 {
/**
* @dev this is the assembly version of -> revert("HOLOGRAPH: LZ only endpoint");
*/
mstore(0x80, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(0xa0, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(0xc0, 0x0000001b484f4c4f47524150483a204c5a206f6e6c7920656e64706f696e7400)
mstore(0xe0, 0x0000000000000000000000000000000000000000000000000000000000000000)
revert(0x80, 0xc4)
}
let ptr := mload(0x40)
calldatacopy(add(ptr, 0x0c), _srcAddress.offset, _srcAddress.length)
/**
* @dev check if LZ from address is same as address(this)
*/
switch eq(mload(ptr), address())
case 0 {
/**
* @dev this is the assembly version of -> revert("HOLOGRAPH: unauthorized sender");
*/
mstore(0x80, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(0xa0, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(0xc0, 0x0000001e484f4c4f47524150483a20756e617574686f72697a65642073656e64)
mstore(0xe0, 0x6572000000000000000000000000000000000000000000000000000000000000)
revert(0x80, 0xc4)
}
}
/**
* @dev if validation has passed, submit payload to Holograph Operator for converting into an operator job
*/
_operator().crossChainMessage(_payload);
}
/**
* @dev Need to add an extra function to get LZ gas amount needed for their internal cross-chain message verification
*/
function send(
uint256, /* gasLimit*/
uint256, /* gasPrice*/
uint32 toChain,
address msgSender,
uint256 msgValue,
bytes calldata crossChainPayload
) external payable {
require(msg.sender == address(_operator()), "HOLOGRAPH: operator only call");
LayerZeroOverrides lZEndpoint;
assembly {
lZEndpoint := sload(_lZEndpointSlot)
}
// need to recalculate the gas amounts for LZ to deliver message
lZEndpoint.send{value: msgValue}(
uint16(_interfaces().getChainId(ChainIdType.HOLOGRAPH, uint256(toChain), ChainIdType.LAYERZERO)),
abi.encodePacked(address(this), address(this)),
crossChainPayload,
payable(msgSender),
address(this),
abi.encodePacked(uint16(1), uint256(_baseGas() + (crossChainPayload.length * _gasPerByte())))
);
}
function getMessageFee(
uint32 toChain,
uint256 gasLimit,
uint256 gasPrice,
bytes calldata crossChainPayload
) external view returns (uint256 hlgFee, uint256 msgFee) {
uint16 lzDestChain = uint16(
_interfaces().getChainId(ChainIdType.HOLOGRAPH, uint256(toChain), ChainIdType.LAYERZERO)
);
LayerZeroOverrides lz;
assembly {
lz := sload(_lZEndpointSlot)
}
// convert holograph chain id to lz chain id
(uint128 dstPriceRatio, uint128 dstGasPriceInWei) = _getPricing(lz, lzDestChain);
if (gasPrice == 0) {
gasPrice = dstGasPriceInWei;
}
bytes memory adapterParams = abi.encodePacked(
uint16(1),
uint256(_baseGas() + (crossChainPayload.length * _gasPerByte()))
);
(uint256 nativeFee, ) = lz.estimateFees(lzDestChain, address(this), crossChainPayload, false, adapterParams);
return (((gasPrice * (gasLimit + (gasLimit / 10))) * dstPriceRatio) / (10**10), nativeFee);
}
function getHlgFee(
uint32 toChain,
uint256 gasLimit,
uint256 gasPrice
) external view returns (uint256 hlgFee) {
LayerZeroOverrides lz;
assembly {
lz := sload(_lZEndpointSlot)
}
uint16 lzDestChain = uint16(
_interfaces().getChainId(ChainIdType.HOLOGRAPH, uint256(toChain), ChainIdType.LAYERZERO)
);
(uint128 dstPriceRatio, uint128 dstGasPriceInWei) = _getPricing(lz, lzDestChain);
if (gasPrice == 0) {
gasPrice = dstGasPriceInWei;
}
return ((gasPrice * (gasLimit + (gasLimit / 10))) * dstPriceRatio) / (10**10);
}
function _getPricing(LayerZeroOverrides lz, uint16 lzDestChain)
private
view
returns (uint128 dstPriceRatio, uint128 dstGasPriceInWei)
{
return
LayerZeroOverrides(LayerZeroOverrides(lz.defaultSendLibrary()).getAppConfig(lzDestChain, address(this)).relayer)
.dstPriceLookup(lzDestChain);
}
/**
* @notice Get the address of the Holograph Bridge module
* @dev Used for beaming holographable assets cross-chain
*/
function getBridge() external view returns (address bridge) {
assembly {
bridge := sload(_bridgeSlot)
}
}
/**
* @notice Update the Holograph Bridge module address
* @param bridge address of the Holograph Bridge smart contract to use
*/
function setBridge(address bridge) external onlyAdmin {
assembly {
sstore(_bridgeSlot, bridge)
}
}
/**
* @notice Get the address of the Holograph Interfaces module
* @dev Holograph uses this contract to store data that needs to be accessed by a large portion of the modules
*/
function getInterfaces() external view returns (address interfaces) {
assembly {
interfaces := sload(_interfacesSlot)
}
}
/**
* @notice Update the Holograph Interfaces module address
* @param interfaces address of the Holograph Interfaces smart contract to use
*/
function setInterfaces(address interfaces) external onlyAdmin {
assembly {
sstore(_interfacesSlot, interfaces)
}
}
/**
* @notice Get the address of the approved LayerZero Endpoint
* @dev All lzReceive function calls allow only requests from this address
*/
function getLZEndpoint() external view returns (address lZEndpoint) {
assembly {
lZEndpoint := sload(_lZEndpointSlot)
}
}
/**
* @notice Update the approved LayerZero Endpoint address
* @param lZEndpoint address of the LayerZero Endpoint to use
*/
function setLZEndpoint(address lZEndpoint) external onlyAdmin {
assembly {
sstore(_lZEndpointSlot, lZEndpoint)
}
}
/**
* @notice Get the address of the Holograph Operator module
* @dev All cross-chain Holograph Bridge beams are handled by the Holograph Operator module
*/
function getOperator() external view returns (address operator) {
assembly {
operator := sload(_operatorSlot)
}
}
/**
* @notice Update the Holograph Operator module address
* @param operator address of the Holograph Operator smart contract to use
*/
function setOperator(address operator) external onlyAdmin {
assembly {
sstore(_operatorSlot, operator)
}
}
/**
* @dev Internal function used for getting the Holograph Bridge Interface
*/
function _bridge() private view returns (address bridge) {
assembly {
bridge := sload(_bridgeSlot)
}
}
/**
* @dev Internal function used for getting the Holograph Interfaces Interface
*/
function _interfaces() private view returns (HolographInterfacesInterface interfaces) {
assembly {
interfaces := sload(_interfacesSlot)
}
}
/**
* @dev Internal function used for getting the Holograph Operator Interface
*/
function _operator() private view returns (HolographOperatorInterface operator) {
assembly {
operator := sload(_operatorSlot)
}
}
/**
* @dev Purposefully reverts to prevent having any type of ether transfered into the contract
*/
receive() external payable {
revert();
}
/**
* @dev Purposefully reverts to prevent any calls to undefined functions
*/
fallback() external payable {
revert();
}
/**
* @notice Get the baseGas value
* @dev Cross-chain messages require at least this much gas
*/
function getBaseGas() external view returns (uint256 baseGas) {
assembly {
baseGas := sload(_baseGasSlot)
}
}
/**
* @notice Update the baseGas value
* @param baseGas minimum gas amount that a message requires
*/
function setBaseGas(uint256 baseGas) external onlyAdmin {
assembly {
sstore(_baseGasSlot, baseGas)
}
}
/**
* @dev Internal function used for getting the baseGas value
*/
function _baseGas() private view returns (uint256 baseGas) {
assembly {
baseGas := sload(_baseGasSlot)
}
}
/**
* @notice Get the gasPerByte value
* @dev Cross-chain messages require at least this much gas (per payload byte)
*/
function getGasPerByte() external view returns (uint256 gasPerByte) {
assembly {
gasPerByte := sload(_gasPerByteSlot)
}
}
/**
* @notice Update the gasPerByte value
* @param gasPerByte minimum gas amount (per payload byte) that a message requires
*/
function setGasPerByte(uint256 gasPerByte) external onlyAdmin {
assembly {
sstore(_gasPerByteSlot, gasPerByte)
}
}
/**
* @dev Internal function used for getting the gasPerByte value
*/
function _gasPerByte() private view returns (uint256 gasPerByte) {
assembly {
gasPerByte := sload(_gasPerByteSlot)
}
}
}