-
Notifications
You must be signed in to change notification settings - Fork 14
/
BridgeFactory.sol
305 lines (258 loc) · 10.9 KB
/
BridgeFactory.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
pragma solidity ^0.6.8;
pragma experimental ABIEncoderV2;
import "../utils/TieredOwnable.sol";
import "../interfaces/ISkyweaverAssets.sol";
import "multi-token-standard/contracts/utils/SafeMath.sol";
import "multi-token-standard/contracts/interfaces/IERC165.sol";
import "multi-token-standard/contracts/interfaces/IERC1155.sol";
import "multi-token-standard/contracts/interfaces/IERC1155TokenReceiver.sol";
/**
* This is a contract allowing contract owner to mint up to N assets per
* 6 hours. Anyone can send SW assets or ARC to this contract, which will
* then get burned for the former and reserved for Horizon for the latter.
* Most of the logic for what to mint and what is considered a valid burn
* is kept off-chain, in a L2 network.
*/
contract BridgeFactory is IERC1155TokenReceiver, TieredOwnable {
using SafeMath for uint256;
/***********************************|
| Variables |
|__________________________________*/
// Token information
ISkyweaverAssets immutable internal skyweaverAssets; // ERC-1155 Skyweaver assets contract
IERC1155 immutable internal arcadeumCoin; // ERC-1155 Arcadeum Coin contract
uint256 immutable internal arcadeumCoinID; // ID of ARC token in respective ERC-1155 contract
// Bridge variables
uint256 internal period; // Current period
uint256 internal availableSupply; // Amount of silvers that can currently be minted
uint256 internal periodMintLimit; // Amount that can be minted within 24h
uint256 constant internal PERIOD_LENGTH = 6 hours; // Length of each mint periods
// Nonce to be used when salt is not provided by the users for Deposit and Redeposit events
uint256 internal saltNonce = uint256(keccak256("org.skyweaver.bridge.initial.nonce"));
event PeriodMintLimitChanged(uint256 oldMintingLimit, uint256 newMintingLimit);
event Deposit(address indexed recipient, bytes32 salt);
event ReDeposit(address indexed recipient, uint256[] ids, uint256[] amounts, bytes32 salt);
/***********************************|
| Constructor |
|__________________________________*/
/**
* @notice Create factory, link skyweaver assets and store initial parameters
* @param _assetsAddr The address of the ERC-1155 Assets Token contract
* @param _arcadeumCoinAddr The address of the arcadeum coin contract
* @param _arcadeumCoinID The ID of the arcadeum coin
* @param _periodMintLimit Can only mint N assets per period
*/
constructor(
address _assetsAddr,
address _arcadeumCoinAddr,
uint256 _arcadeumCoinID,
uint256 _periodMintLimit
) public {
require(
_assetsAddr != address(0) &&
_arcadeumCoinAddr != address(0) &&
_periodMintLimit > 0,
"BridgeFactory#constructor: INVALID_INPUT"
);
// Assets
skyweaverAssets = ISkyweaverAssets(_assetsAddr);
arcadeumCoin = IERC1155(_arcadeumCoinAddr);
arcadeumCoinID = _arcadeumCoinID;
// Set current period
period = livePeriod();
availableSupply = _periodMintLimit;
// Rewards parameters
periodMintLimit = _periodMintLimit;
emit PeriodMintLimitChanged(0, _periodMintLimit);
}
/***********************************|
| Management Methods |
|__________________________________*/
/**
* @notice Will update the daily mint limit
* @dev This change will take effect immediatly once executed
* @param _newPeriodMintLimit Amount of assets that can be minted within 24h
*/
function updatePeriodMintLimit(uint256 _newPeriodMintLimit) external onlyOwnerTier(HIGHEST_OWNER_TIER) {
// Immediately update supply instead of waiting for next period
if (availableSupply > _newPeriodMintLimit) {
availableSupply = _newPeriodMintLimit;
}
emit PeriodMintLimitChanged(periodMintLimit, _newPeriodMintLimit);
periodMintLimit = _newPeriodMintLimit;
}
/**
* @notice Send current ARC balance of conquest contract to recipient
* @param _recipient Address where the currency will be sent to
* @param _data Data to pass with transfer function
*/
function withdraw(address _recipient, bytes calldata _data) external onlyOwnerTier(HIGHEST_OWNER_TIER) {
require(_recipient != address(0x0), "BridgeFactory#withdraw: INVALID_RECIPIENT");
uint256 thisBalance = arcadeumCoin.balanceOf(address(this), arcadeumCoinID);
arcadeumCoin.safeTransferFrom(address(this), _recipient, arcadeumCoinID, thisBalance, _data);
}
/***********************************|
| Receiver Method Handler |
|__________________________________*/
/**
* @notice Prevents receiving Ether or calls to unsuported methods
*/
fallback () external {
revert("BridgeFactory#_: UNSUPPORTED_METHOD");
}
/**
* @notice Burns SW asset when received, or store ARC
* @dev Make sure to send the right assets and the correct amount
* as it's not validated in this contract.
* @param _from Source address
* @param _id Id of Token being transferred
* @param _amount Amount of Token _id being transferred
* @param _data Should be a bytes32 salt provided by user
*/
function onERC1155Received(
address, // _operator
address _from,
uint256 _id,
uint256 _amount,
bytes memory _data
)
public override returns(bytes4)
{
if (msg.sender == address(arcadeumCoin)) {
// Do nothing, just store the ARC until withdrawl
require(_id == arcadeumCoinID, "BridgeFactory#onERC1155Received: INVALID_ARC_ID");
} else if (msg.sender == address(skyweaverAssets)) {
// Burn asset received
ISkyweaverAssets(msg.sender).burn(_id, _amount);
} else {
revert("BridgeFactory#onERC1155Received: INVALID_TOKEN");
}
// Get salt from _data argument or generate it if missing
bytes32 salt = _data.length == 0 ? generateSalt() : abi.decode(_data, (bytes32));
emit Deposit(_from, salt);
return IERC1155TokenReceiver.onERC1155Received.selector;
}
/**
* @notice Burns SW assets when received, or store ARC
* @dev Make sure to send the right assets and the correct amount
* as it's not validated in this contract.
* @param _from Source address
* @param _ids An array containing ids of each Token being transferred
* @param _amounts An array containing amounts of each Token being transferred
* @param _data Should be a bytes32 salt provided by user
*/
function onERC1155BatchReceived(
address, // _operator
address _from,
uint256[] memory _ids,
uint256[] memory _amounts,
bytes memory _data
)
public override returns(bytes4)
{
if (msg.sender == address(skyweaverAssets)) {
// Burn assets received
ISkyweaverAssets(msg.sender).batchBurn(_ids, _amounts);
// Arc tribute
} else if (msg.sender == address(arcadeumCoin)) {
// Do nothing, just store the ARC until withdrawl
require(_ids[0] == arcadeumCoinID, "BridgeFactory#onERC1155BatchReceived: INVALID_ARC_ID");
require(_ids.length == 1, "BridgeFactory#onERC1155BatchReceived: INVALID_ARRAY_LENGTH");
} else {
revert("BridgeFactory#onERC1155BatchReceived: INVALID_TOKEN");
}
// Get salt from _data argument or generate it if missing
bytes32 salt = _data.length == 0 ? generateSalt() : abi.decode(_data, (bytes32));
emit Deposit(_from, salt);
return IERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
/***********************************|
| Minting Functions |
|__________________________________*/
/**
* @notice Will mint tokens to user
* @dev Can only mint up to the periodMintLimit in a given 6hour period
* @param _to The address that receives the assets
* @param _ids Array of Tokens ID that are minted
* @param _amounts Amount of Tokens id minted for each corresponding Token id in _tokenIds
*/
function batchMint(address _to, uint256[] calldata _ids, uint256[] calldata _amounts)
external onlyOwnerTier(1) returns (bool success)
{
uint256 live_period = livePeriod();
uint256 stored_period = period;
// Get the available supply based on period
uint256 available_supply = live_period == stored_period ? availableSupply : periodMintLimit;
// If there is an insufficient available supply, a ReDeposit event will
// be emitted and minting will be aborted. This allows the bridge operator
// to re-mint the tokens to the recipient on the other chain.
for (uint256 i = 0; i < _ids.length; i++) {
// Overflow is used to determine if a redeposit should happen or a mint should happen
uint256 new_available_supply = available_supply - _amounts[i];
if (new_available_supply <= available_supply) {
available_supply = new_available_supply;
} else {
emit ReDeposit(_to, _ids, _amounts, generateSalt());
return false;
}
}
// Store available supply
availableSupply = available_supply;
// Update period if changed
if (live_period != stored_period) {
period = live_period;
}
// Mint assets
skyweaverAssets.batchMint(_to, _ids, _amounts, "");
return true;
}
/***********************************|
| Getter Functions |
|__________________________________*/
/**
* @notice Returns the address of the factory manager contract
*/
function getSkyweaverAssets() external view returns (address) {
return address(skyweaverAssets);
}
/**
* @notice Returns the daily minting limit
*/
function getPeriodMintLimit() external view returns (uint256) {
return periodMintLimit;
}
/**
* @notice Returns how many cards can currently be minted by this factory
*/
function getAvailableSupply() external view returns (uint256) {
return livePeriod() == period ? availableSupply : periodMintLimit;
}
/***********************************|
| Utility Functions |
|__________________________________*/
/**
* @notice Will generate a salt based on the saltNonce and increment the nonce
*/
function generateSalt() internal returns (bytes32) {
uint256 salt_nonce = saltNonce;
saltNonce = salt_nonce + 1;
return keccak256(abi.encode(salt_nonce));
}
/**
* @notice Calculate the current period
*/
function livePeriod() public view returns (uint256) {
return now / PERIOD_LENGTH;
}
/**
* @notice Indicates whether a contract implements the `ERC1155TokenReceiver` functions and so can accept ERC1155 token types.
* @param interfaceID The ERC-165 interface ID that is queried for support.s
* @dev This function MUST return true if it implements the ERC1155TokenReceiver interface and ERC-165 interface.
* This function MUST NOT consume more than 5,000 gas.
* @return Wheter ERC-165 or ERC1155TokenReceiver interfaces are supported.
*/
function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
return interfaceID == type(IERC165).interfaceId ||
interfaceID == type(IERC1155TokenReceiver).interfaceId;
}
}