forked from Synthetixio/synthetix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBaseSynthetixBridge.sol
233 lines (178 loc) · 8.49 KB
/
BaseSynthetixBridge.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
pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;
// Inheritance
import "./Owned.sol";
import "./MixinResolver.sol";
import "./MixinSystemSettings.sol";
import "./interfaces/IBaseSynthetixBridge.sol";
// Libraries
import "./Math.sol";
import "./SafeDecimalMath.sol";
// Internal references
import "./interfaces/ISynthetix.sol";
import "./interfaces/IRewardEscrowV2.sol";
import "./interfaces/IIssuer.sol";
import "./interfaces/IFeePool.sol";
import "./interfaces/IExchangeRates.sol";
import "./interfaces/ISystemStatus.sol";
import "@eth-optimism/contracts/iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol";
contract BaseSynthetixBridge is Owned, MixinSystemSettings, IBaseSynthetixBridge {
using SafeMath for uint;
using SafeDecimalMath for uint;
/* ========== ADDRESS RESOLVER CONFIGURATION ========== */
bytes32 private constant CONTRACT_EXT_MESSENGER = "ext:Messenger";
bytes32 internal constant CONTRACT_SYNTHETIX = "Synthetix";
bytes32 private constant CONTRACT_REWARDESCROW = "RewardEscrowV2";
bytes32 private constant CONTRACT_ISSUER = "Issuer";
bytes32 private constant CONTRACT_FEEPOOL = "FeePool";
bytes32 private constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";
bytes32 private constant CONTRACT_EXCHANGERATES = "ExchangeRates";
bytes32 private constant CONTRACT_SYSTEM_STATUS = "SystemStatus";
// have to define this function like this here because contract name is required for FlexibleStorage
function CONTRACT_NAME() public pure returns (bytes32);
bool public initiationActive;
bytes32 private constant SYNTH_TRANSFER_NAMESPACE = "SynthTransfer";
bytes32 private constant SYNTH_TRANSFER_SENT = "Sent";
bytes32 private constant SYNTH_TRANSFER_RECV = "Recv";
// ========== CONSTRUCTOR ==========
constructor(address _owner, address _resolver) public Owned(_owner) MixinSystemSettings(_resolver) {
initiationActive = true;
}
// ========== INTERNALS ============
function messenger() internal view returns (iAbs_BaseCrossDomainMessenger) {
return iAbs_BaseCrossDomainMessenger(requireAndGetAddress(CONTRACT_EXT_MESSENGER));
}
function synthetix() internal view returns (ISynthetix) {
return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX));
}
function rewardEscrowV2() internal view returns (IRewardEscrowV2) {
return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW));
}
function issuer() internal view returns (IIssuer) {
return IIssuer(requireAndGetAddress(CONTRACT_ISSUER));
}
function feePool() internal view returns (IFeePool) {
return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL));
}
function flexibleStorage() internal view returns (IFlexibleStorage) {
return IFlexibleStorage(requireAndGetAddress(CONTRACT_FLEXIBLESTORAGE));
}
function exchangeRates() internal view returns (IExchangeRates) {
return IExchangeRates(requireAndGetAddress(CONTRACT_EXCHANGERATES));
}
function systemStatus() internal view returns (ISystemStatus) {
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEM_STATUS));
}
function initiatingActive() internal view {
require(initiationActive, "Initiation deactivated");
}
function counterpart() internal view returns (address);
function onlyAllowFromCounterpart() internal view {
// ensure function only callable from the L2 bridge via messenger (aka relayer)
iAbs_BaseCrossDomainMessenger _messenger = messenger();
require(msg.sender == address(_messenger), "Only the relayer can call this");
require(_messenger.xDomainMessageSender() == counterpart(), "Only a counterpart bridge can invoke");
}
/* ========== VIEWS ========== */
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](8);
newAddresses[0] = CONTRACT_EXT_MESSENGER;
newAddresses[1] = CONTRACT_SYNTHETIX;
newAddresses[2] = CONTRACT_REWARDESCROW;
newAddresses[3] = CONTRACT_ISSUER;
newAddresses[4] = CONTRACT_FEEPOOL;
newAddresses[5] = CONTRACT_FLEXIBLESTORAGE;
newAddresses[6] = CONTRACT_EXCHANGERATES;
newAddresses[7] = CONTRACT_SYSTEM_STATUS;
addresses = combineArrays(existingAddresses, newAddresses);
}
function synthTransferSent() external view returns (uint) {
return _sumTransferAmounts(SYNTH_TRANSFER_SENT);
}
function synthTransferReceived() external view returns (uint) {
return _sumTransferAmounts(SYNTH_TRANSFER_RECV);
}
// ========== MODIFIERS ============
modifier requireInitiationActive() {
initiatingActive();
_;
}
modifier onlyCounterpart() {
onlyAllowFromCounterpart();
_;
}
// ========= RESTRICTED FUNCTIONS ==============
function suspendInitiation() external onlyOwner {
require(initiationActive, "Initiation suspended");
initiationActive = false;
emit InitiationSuspended();
}
function resumeInitiation() external onlyOwner {
require(!initiationActive, "Initiation not suspended");
initiationActive = true;
emit InitiationResumed();
}
function initiateSynthTransfer(
bytes32 currencyKey,
address destination,
uint amount
) external requireInitiationActive {
require(destination != address(0), "Cannot send to zero address");
require(getCrossChainSynthTransferEnabled(currencyKey) > 0, "Synth not enabled for cross chain transfer");
systemStatus().requireSynthActive(currencyKey);
_incrementSynthsTransferCounter(SYNTH_TRANSFER_SENT, currencyKey, amount);
bool rateInvalid = issuer().burnSynthsWithoutDebt(currencyKey, msg.sender, amount);
require(!rateInvalid, "Cannot initiate if synth rate is invalid");
// create message payload
bytes memory messageData =
abi.encodeWithSelector(this.finalizeSynthTransfer.selector, currencyKey, destination, amount);
// relay the message to Bridge on L1 via L2 Messenger
messenger().sendMessage(
counterpart(),
messageData,
uint32(getCrossDomainMessageGasLimit(CrossDomainMessageGasLimits.Withdrawal))
);
emit InitiateSynthTransfer(currencyKey, destination, amount);
}
function finalizeSynthTransfer(
bytes32 currencyKey,
address destination,
uint amount
) external onlyCounterpart {
_incrementSynthsTransferCounter(SYNTH_TRANSFER_RECV, currencyKey, amount);
issuer().issueSynthsWithoutDebt(currencyKey, destination, amount);
emit FinalizeSynthTransfer(currencyKey, destination, amount);
}
// ==== INTERNAL FUNCTIONS ====
function _incrementSynthsTransferCounter(
bytes32 group,
bytes32 currencyKey,
uint amount
) internal {
bytes32 key = keccak256(abi.encodePacked(SYNTH_TRANSFER_NAMESPACE, group, currencyKey));
uint currentSynths = flexibleStorage().getUIntValue(CONTRACT_NAME(), key);
flexibleStorage().setUIntValue(CONTRACT_NAME(), key, currentSynths.add(amount));
}
function _sumTransferAmounts(bytes32 group) internal view returns (uint sum) {
// get list of synths from issuer
bytes32[] memory currencyKeys = issuer().availableCurrencyKeys();
// get all synth rates
(uint[] memory rates, bool isInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
require(!isInvalid, "Rates are invalid");
// get all values
bytes32[] memory transferAmountKeys = new bytes32[](currencyKeys.length);
for (uint i = 0; i < currencyKeys.length; i++) {
transferAmountKeys[i] = keccak256(abi.encodePacked(SYNTH_TRANSFER_NAMESPACE, group, currencyKeys[i]));
}
uint[] memory transferAmounts = flexibleStorage().getUIntValues(CONTRACT_NAME(), transferAmountKeys);
for (uint i = 0; i < currencyKeys.length; i++) {
sum = sum.add(transferAmounts[i].multiplyDecimalRound(rates[i]));
}
}
// ========== EVENTS ==========
event InitiationSuspended();
event InitiationResumed();
event InitiateSynthTransfer(bytes32 indexed currencyKey, address indexed destination, uint256 amount);
event FinalizeSynthTransfer(bytes32 indexed currencyKey, address indexed destination, uint256 amount);
}