/
ERC721PortalFacet.sol
202 lines (184 loc) · 7.17 KB
/
ERC721PortalFacet.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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "../interfaces/IERC721PortalFacet.sol";
import "../libraries/LibDiamond.sol";
import "../libraries/LibGovernance.sol";
import "../libraries/LibFeeCalculator.sol";
import "../libraries/LibPayment.sol";
import "../libraries/LibRouter.sol";
import "../libraries/LibERC721.sol";
import "../WrappedERC721.sol";
contract ERC721PortalFacet is IERC721PortalFacet, ERC721Holder {
using SafeERC20 for IERC20;
/// @notice Mints wrapped `_tokenId` to the `receiver` address.
/// Must be authorised by the configured supermajority threshold of `signatures` from the `members` set.
/// @param _sourceChain ID of the source chain
/// @param _transactionId The source transaction ID + log index
/// @param _wrappedToken The address of the wrapped ERC-721 token on the current chain
/// @param _tokenId The target token ID
/// @param _metadata The tokenID's metadata, used to be queried as ERC-721.tokenURI
/// @param _receiver The address of the receiver on this chain
/// @param _signatures The array of signatures from the members, authorising the operation
function mintERC721(
uint256 _sourceChain,
bytes memory _transactionId,
address _wrappedToken,
uint256 _tokenId,
string memory _metadata,
address _receiver,
bytes[] calldata _signatures
) external override whenNotPaused {
LibGovernance.validateSignaturesLength(_signatures.length);
bytes32 ethHash = computeMessage(
_sourceChain,
block.chainid,
_transactionId,
_wrappedToken,
_tokenId,
_metadata,
_receiver
);
LibRouter.Storage storage rs = LibRouter.routerStorage();
require(
!rs.hashesUsed[ethHash],
"ERC721PortalFacet: transaction already submitted"
);
validateAndStoreTx(ethHash, _signatures);
WrappedERC721(_wrappedToken).safeMint(_receiver, _tokenId, _metadata);
emit MintERC721(
_sourceChain,
_transactionId,
_wrappedToken,
_tokenId,
_metadata,
_receiver
);
}
/// @notice Burns `_tokenId` of `wrappedToken` and initializes a portal transaction to the target chain
/// The wrappedToken's fee payment is transferred to the contract upon execution.
/// @param _targetChain The target chain to which the wrapped asset will be transferred
/// @param _wrappedToken The address of the wrapped token
/// @param _tokenId The tokenID of `wrappedToken` to burn
/// @param _paymentToken The current payment token
/// @param _fee The fee amount for the wrapped token's payment token
/// @param _receiver The address of the receiver on the target chain
function burnERC721(
uint256 _targetChain,
address _wrappedToken,
uint256 _tokenId,
address _paymentToken,
uint256 _fee,
bytes memory _receiver
) public override whenNotPaused {
address payment = LibERC721.erc721Payment(_wrappedToken);
require(
LibPayment.containsPaymentToken(payment),
"ERC721PortalFacet: payment token not supported"
);
require(
_paymentToken == payment,
"ERC721PortalFacet: _paymentToken does not match the current set payment token"
);
uint256 currentFee = LibERC721.erc721Fee(_wrappedToken);
require(
_fee == currentFee,
"ERC721PortalFacet: _fee does not match current set payment token fee"
);
IERC20(payment).safeTransferFrom(msg.sender, address(this), _fee);
LibFeeCalculator.accrueFee(payment, _fee);
WrappedERC721(_wrappedToken).burn(_tokenId);
emit BurnERC721(
_targetChain,
_wrappedToken,
_tokenId,
_receiver,
payment,
_fee
);
}
/// @notice Sets ERC-721 contract payment token and fee amount
/// @param _erc721 The target ERC-721 contract
/// @param _payment The target payment token
/// @param _fee The fee required upon every portal transfer
function setERC721Payment(
address _erc721,
address _payment,
uint256 _fee
) external override {
LibDiamond.enforceIsContractOwner();
require(
LibPayment.containsPaymentToken(_payment),
"ERC721PortalFacet: payment token not supported"
);
LibERC721.setERC721PaymentFee(_erc721, _payment, _fee);
emit SetERC721Payment(_erc721, _payment, _fee);
}
/// @notice Returns the payment token for an ERC-721
/// @param _erc721 The address of the ERC-721 Token
function erc721Payment(address _erc721)
external
view
override
returns (address)
{
return LibERC721.erc721Payment(_erc721);
}
/// @notice Returns the payment fee for an ERC-721
/// @param _erc721 The address of the ERC-721 Token
function erc721Fee(address _erc721)
external
view
override
returns (uint256)
{
return LibERC721.erc721Fee(_erc721);
}
/// @notice Computes the bytes32 ethereum signed message hash for signatures
/// @param _sourceChain The chain where the bridge transaction was initiated from
/// @param _targetChain The target chain of the bridge transaction.
/// Should always be the current chainId.
/// @param _transactionId The transaction ID of the bridge transaction
/// @param _token The address of the token on this chain
/// @param _tokenId The token ID for the _token
/// @param _metadata The metadata for the token ID
/// @param _receiver The receiving address on the current chain
function computeMessage(
uint256 _sourceChain,
uint256 _targetChain,
bytes memory _transactionId,
address _token,
uint256 _tokenId,
string memory _metadata,
address _receiver
) internal pure returns (bytes32) {
bytes32 hashedData = keccak256(
abi.encode(
_sourceChain,
_targetChain,
_transactionId,
_token,
_tokenId,
_metadata,
_receiver
)
);
return ECDSA.toEthSignedMessageHash(hashedData);
}
/// @notice Validates the signatures and the data and saves the transaction
/// @param _ethHash The hashed data
/// @param _signatures The array of signatures from the members, authorising the operation
function validateAndStoreTx(bytes32 _ethHash, bytes[] calldata _signatures)
internal
{
LibRouter.Storage storage rs = LibRouter.routerStorage();
LibGovernance.validateSignatures(_ethHash, _signatures);
rs.hashesUsed[_ethHash] = true;
}
/// Modifier to make a function callable only when the contract is not paused
modifier whenNotPaused() {
LibGovernance.enforceNotPaused();
_;
}
}