-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathHolographBridge.sol
587 lines (543 loc) · 22.7 KB
/
HolographBridge.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
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
// 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 "./interface/HolographERC20Interface.sol";
import "./interface/Holographable.sol";
import "./interface/HolographInterface.sol";
import "./interface/HolographBridgeInterface.sol";
import "./interface/HolographFactoryInterface.sol";
import "./interface/HolographOperatorInterface.sol";
import "./interface/HolographRegistryInterface.sol";
import "./interface/InitializableInterface.sol";
/**
* @title Holograph Bridge
* @author https://github.com/holographxyz
* @notice Beam any holographable contracts and assets across blockchains
* @dev The contract abstracts all the complexities of making bridge requests and uses a universal interface to bridge any type of holographable assets
*/
contract HolographBridge is Admin, Initializable, HolographBridgeInterface {
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.factory')) - 1)
*/
bytes32 constant _factorySlot = 0xa49f20855ba576e09d13c8041c8039fa655356ea27f6c40f1ec46a4301cd5b23;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.holograph')) - 1)
*/
bytes32 constant _holographSlot = 0xb4107f746e9496e8452accc7de63d1c5e14c19f510932daa04077cd49e8bd77a;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.jobNonce')) - 1)
*/
bytes32 constant _jobNonceSlot = 0x1cda64803f3b43503042e00863791e8d996666552d5855a78d53ee1dd4b3286d;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.operator')) - 1)
*/
bytes32 constant _operatorSlot = 0x7caba557ad34138fa3b7e43fb574e0e6cc10481c3073e0dffbc560db81b5c60f;
/**
* @dev bytes32(uint256(keccak256('eip1967.Holograph.registry')) - 1)
*/
bytes32 constant _registrySlot = 0xce8e75d5c5227ce29a4ee170160bb296e5dea6934b80a9bd723f7ef1e7c850e7;
/**
* @dev Allow calls only from Holograph Operator contract
*/
modifier onlyOperator() {
require(msg.sender == address(_operator()), "HOLOGRAPH: operator only call");
_;
}
/**
* @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 factory, address holograph, address operator, address registry) = abi.decode(
initPayload,
(address, address, address, address)
);
assembly {
sstore(_adminSlot, origin())
sstore(_factorySlot, factory)
sstore(_holographSlot, holograph)
sstore(_operatorSlot, operator)
sstore(_registrySlot, registry)
}
_setInitialized();
return InitializableInterface.init.selector;
}
/**
* @notice Receive a beam from another chain
* @dev This function can only be called by the Holograph Operator module
* @param fromChain Holograph Chain ID where the brigeOutRequest was created
* @param holographableContract address of the destination contract that the bridgeInRequest is targeted for
* @param hToken address of the hToken contract that wrapped the origin chain native gas token
* @param hTokenRecipient address of recipient for the hToken reward
* @param hTokenValue exact amount of hToken reward in wei
* @param doNotRevert boolean used to specify if the call should revert
* @param bridgeInPayload actual abi encoded bytes of the data that the holographable contract bridgeIn function will receive
*/
function bridgeInRequest(
uint256, /* nonce*/
uint32 fromChain,
address holographableContract,
address hToken,
address hTokenRecipient,
uint256 hTokenValue,
bool doNotRevert,
bytes calldata bridgeInPayload
) external payable onlyOperator {
/**
* @dev check that the target contract is either Holograph Factory or a deployed holographable contract
*/
require(
_registry().isHolographedContract(holographableContract) || address(_factory()) == holographableContract,
"HOLOGRAPH: not holographed"
);
/**
* @dev make a bridgeIn function call to the holographable contract
*/
bytes4 selector = Holographable(holographableContract).bridgeIn(fromChain, bridgeInPayload);
/**
* @dev ensure returned selector is bridgeIn function signature, to guarantee that the function was called and succeeded
*/
require(selector == Holographable.bridgeIn.selector, "HOLOGRAPH: bridge in failed");
/**
* @dev check if a specific reward amount was assigned to this request
*/
if (hTokenValue > 0) {
/**
* @dev mint the specific hToken amount for hToken recipient
* this value is equivalent to amount that is deposited on origin chain's hToken contract
* recipient can beam the asset to origin chain and unwrap for native gas token at any time
*/
require(
HolographERC20Interface(hToken).holographBridgeMint(hTokenRecipient, hTokenValue) ==
HolographERC20Interface.holographBridgeMint.selector,
"HOLOGRAPH: hToken mint failed"
);
}
/**
* @dev allow the call to revert on demand, for example use case, look into the Holograph Operator's jobEstimator function
*/
require(doNotRevert, "HOLOGRAPH: reverted");
}
/**
* @notice Create a beam request for a destination chain
* @dev This function works for deploying contracts and beaming supported holographable assets across chains
* @param toChain Holograph Chain ID where the beam is being sent to
* @param holographableContract address of the contract for which the bridge request is being made
* @param gasLimit maximum amount of gas to spend for executing the beam on destination chain
* @param gasPrice maximum amount of gas price (in destination chain native gas token) to pay on destination chain
* @param bridgeOutPayload actual abi encoded bytes of the data that the holographable contract bridgeOut function will receive
*/
function bridgeOutRequest(
uint32 toChain,
address holographableContract,
uint256 gasLimit,
uint256 gasPrice,
bytes calldata bridgeOutPayload
) external payable {
/**
* @dev check that the target contract is either Holograph Factory or a deployed holographable contract
*/
require(
_registry().isHolographedContract(holographableContract) || address(_factory()) == holographableContract,
"HOLOGRAPH: not holographed"
);
/**
* @dev make a bridgeOut function call to the holographable contract
*/
(bytes4 selector, bytes memory returnedPayload) = Holographable(holographableContract).bridgeOut(
toChain,
msg.sender,
bridgeOutPayload
);
/**
* @dev ensure returned selector is bridgeOut function signature, to guarantee that the function was called and succeeded
*/
require(selector == Holographable.bridgeOut.selector, "HOLOGRAPH: bridge out failed");
/**
* @dev pass the request, along with all data, to Holograph Operator, to handle the cross-chain messaging logic
*/
_operator().send{value: msg.value}(
gasLimit,
gasPrice,
toChain,
msg.sender,
_jobNonce(),
holographableContract,
returnedPayload
);
}
/**
* @notice Do not call this function, it will always revert
* @dev Used by getBridgeOutRequestPayload function
* It is purposefully inverted to always revert on a successful call
* Marked as external and not private to allow use inside try/catch of getBridgeOutRequestPayload function
* If this function does not revert and returns a string, it is the actual revert reason
* @param sender address of actual sender that is planning to make a bridgeOutRequest call
* @param toChain holograph chain id of destination chain
* @param holographableContract address of the contract for which the bridge request is being made
* @param bridgeOutPayload actual abi encoded bytes of the data that the holographable contract bridgeOut function will receive
*/
function revertedBridgeOutRequest(
address sender,
uint32 toChain,
address holographableContract,
bytes calldata bridgeOutPayload
) external returns (string memory revertReason) {
/**
* @dev make a bridgeOut function call to the holographable contract inside of a try/catch
*/
try Holographable(holographableContract).bridgeOut(toChain, sender, bridgeOutPayload) returns (
bytes4 selector,
bytes memory payload
) {
/**
* @dev ensure returned selector is bridgeOut function signature, to guarantee that the function was called and succeeded
*/
if (selector != Holographable.bridgeOut.selector) {
/**
* @dev if selector does not match, then it means the request failed
*/
return "HOLOGRAPH: bridge out failed";
}
assembly {
/**
* @dev the entire payload is sent back in a revert
*/
revert(add(payload, 0x20), mload(payload))
}
} catch Error(string memory reason) {
return reason;
} catch {
return "HOLOGRAPH: unknown error";
}
}
/**
* @notice Get the payload created by the bridgeOutRequest function
* @dev Use this function to get the payload that will be generated by a bridgeOutRequest
* Only use this with a static call
* @param toChain Holograph Chain ID where the beam is being sent to
* @param holographableContract address of the contract for which the bridge request is being made
* @param gasLimit maximum amount of gas to spend for executing the beam on destination chain
* @param gasPrice maximum amount of gas price (in destination chain native gas token) to pay on destination chain
* @param bridgeOutPayload actual abi encoded bytes of the data that the holographable contract bridgeOut function will receive
* @return samplePayload bytes made up of the bridgeOutRequest payload
*/
function getBridgeOutRequestPayload(
uint32 toChain,
address holographableContract,
uint256 gasLimit,
uint256 gasPrice,
bytes calldata bridgeOutPayload
) external returns (bytes memory samplePayload) {
/**
* @dev check that the target contract is either Holograph Factory or a deployed holographable contract
*/
require(
_registry().isHolographedContract(holographableContract) || address(_factory()) == holographableContract,
"HOLOGRAPH: not holographed"
);
bytes memory payload;
/**
* @dev the revertedBridgeOutRequest function is wrapped into a try/catch function
*/
try this.revertedBridgeOutRequest(msg.sender, toChain, holographableContract, bridgeOutPayload) returns (
string memory revertReason
) {
/**
* @dev a non reverted result is actually a revert
*/
revert(revertReason);
} catch (bytes memory realResponse) {
/**
* @dev a revert is actually success, so the return data is stored as payload
*/
payload = realResponse;
}
uint256 jobNonce;
assembly {
jobNonce := sload(_jobNonceSlot)
}
/**
* @dev extract hlgFee from operator
*/
uint256 fee = 0;
if (gasPrice < type(uint256).max && gasLimit < type(uint256).max) {
(uint256 hlgFee, ) = _operator().getMessageFee(toChain, gasLimit, gasPrice, bridgeOutPayload);
fee = hlgFee;
}
/**
* @dev the data is abi encoded into actual bridgeOutRequest payload bytes
*/
bytes memory encodedData = abi.encodeWithSelector(
HolographBridgeInterface.bridgeInRequest.selector,
/**
* @dev the latest job nonce is incremented by one
*/
jobNonce + 1,
_holograph().getHolographChainId(),
holographableContract,
_registry().getHToken(_holograph().getHolographChainId()),
address(0),
fee,
true,
payload
);
/**
* @dev this abi encodes the data just like in Holograph Operator
*/
samplePayload = abi.encodePacked(encodedData, gasLimit, gasPrice);
}
/**
* @notice Get the fees associated with sending specific payload
* @dev Will provide exact costs on protocol and message side, combine the two to get total
* @dev @param toChain holograph chain id of destination chain for payload
* @dev @param gasLimit amount of gas to provide for executing payload on destination chain
* @dev @param gasPrice maximum amount to pay for gas price, can be set to 0 and will be chose automatically
* @dev @param crossChainPayload the entire packet being sent cross-chain
* @return hlgFee the amount (in wei) of native gas token that will cost for finalizing job on destiantion chain
* @return msgFee the amount (in wei) of native gas token that will cost for sending message to destiantion chain
*/
function getMessageFee(
uint32,
uint256,
uint256,
bytes calldata
) external view returns (uint256, uint256) {
assembly {
calldatacopy(0, 0, calldatasize())
let result := staticcall(gas(), sload(_operatorSlot), 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @notice Get the address of the Holograph Factory module
* @dev Used for deploying holographable smart contracts
*/
function getFactory() external view returns (address factory) {
assembly {
factory := sload(_factorySlot)
}
}
/**
* @notice Update the Holograph Factory module address
* @param factory address of the Holograph Factory smart contract to use
*/
function setFactory(address factory) external onlyAdmin {
assembly {
sstore(_factorySlot, factory)
}
}
/**
* @notice Get the Holograph Protocol contract
* @dev Used for storing a reference to all the primary modules and variables of the protocol
*/
function getHolograph() external view returns (address holograph) {
assembly {
holograph := sload(_holographSlot)
}
}
/**
* @notice Update the Holograph Protocol contract address
* @param holograph address of the Holograph Protocol smart contract to use
*/
function setHolograph(address holograph) external onlyAdmin {
assembly {
sstore(_holographSlot, holograph)
}
}
/**
* @notice Get the latest job nonce
* @dev You can use the job nonce as a way to calculate total amount of bridge requests that have been made
*/
function getJobNonce() external view returns (uint256 jobNonce) {
assembly {
jobNonce := sload(_jobNonceSlot)
}
}
/**
* @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)
}
}
/**
* @notice Get the Holograph Registry module
* @dev This module stores a reference for all deployed holographable smart contracts
*/
function getRegistry() external view returns (address registry) {
assembly {
registry := sload(_registrySlot)
}
}
/**
* @notice Update the Holograph Registry module address
* @param registry address of the Holograph Registry smart contract to use
*/
function setRegistry(address registry) external onlyAdmin {
assembly {
sstore(_registrySlot, registry)
}
}
/**
* @dev Internal function used for getting the Holograph Factory Interface
*/
function _factory() private view returns (HolographFactoryInterface factory) {
assembly {
factory := sload(_factorySlot)
}
}
/**
* @dev Internal function used for getting the Holograph Interface
*/
function _holograph() private view returns (HolographInterface holograph) {
assembly {
holograph := sload(_holographSlot)
}
}
/**
* @dev Internal nonce, that increments on each call, used for randomness
*/
function _jobNonce() private returns (uint256 jobNonce) {
assembly {
jobNonce := add(sload(_jobNonceSlot), 0x0000000000000000000000000000000000000000000000000000000000000001)
sstore(_jobNonceSlot, jobNonce)
}
}
/**
* @dev Internal function used for getting the Holograph Operator Interface
*/
function _operator() private view returns (HolographOperatorInterface operator) {
assembly {
operator := sload(_operatorSlot)
}
}
/**
* @dev Internal function used for getting the Holograph Registry Interface
*/
function _registry() private view returns (HolographRegistryInterface registry) {
assembly {
registry := sload(_registrySlot)
}
}
/**
* @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();
}
}