@@ -40,6 +40,14 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
40
40
address public escrow;
41
41
/// Addresses for which this mapping is true are allowed to send callhooks in outbound transfers
42
42
mapping (address => bool ) public callhookAllowlist;
43
+ /// Total amount minted from L2
44
+ uint256 public totalMintedFromL2;
45
+ /// Accumulated allowance for tokens minted from L2 at lastL2MintAllowanceUpdateBlock
46
+ uint256 public accumulatedL2MintAllowanceSnapshot;
47
+ /// Block at which new L2 allowance starts accumulating
48
+ uint256 public lastL2MintAllowanceUpdateBlock;
49
+ /// New L2 mint allowance per block
50
+ uint256 public l2MintAllowancePerBlock;
43
51
44
52
/// Emitted when an outbound transfer is initiated, i.e. tokens are deposited from L1 to L2
45
53
event DepositInitiated (
@@ -71,6 +79,14 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
71
79
event AddedToCallhookAllowlist (address newAllowlisted );
72
80
/// Emitted when an address is removed from the callhook allowlist
73
81
event RemovedFromCallhookAllowlist (address notAllowlisted );
82
+ /// Emitted when the L2 mint allowance per block is updated
83
+ event L2MintAllowanceUpdated (
84
+ uint256 accumulatedL2MintAllowanceSnapshot ,
85
+ uint256 l2MintAllowancePerBlock ,
86
+ uint256 lastL2MintAllowanceUpdateBlock
87
+ );
88
+ /// Emitted when tokens are minted due to an incoming transfer from L2
89
+ event TokensMintedFromL2 (uint256 amount );
74
90
75
91
/**
76
92
* @dev Allows a function to be called only by the gateway's L2 counterpart.
@@ -182,6 +198,56 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
182
198
emit RemovedFromCallhookAllowlist (_notAllowlisted);
183
199
}
184
200
201
+ /**
202
+ * @dev Updates the L2 mint allowance per block
203
+ * It is meant to be called _after_ the issuancePerBlock is updated in L2.
204
+ * The caller should provide the new issuance per block and the block at which it was updated,
205
+ * the function will automatically compute the values so that the bridge's mint allowance
206
+ * correctly tracks the maximum rewards minted in L2.
207
+ * @param _l2IssuancePerBlock New issuancePerBlock that has been set in L2
208
+ * @param _updateBlockNum L1 Block number at which issuancePerBlock was updated in L2
209
+ */
210
+ function updateL2MintAllowance (uint256 _l2IssuancePerBlock , uint256 _updateBlockNum )
211
+ external
212
+ onlyGovernor
213
+ {
214
+ require (_updateBlockNum < block .number , "BLOCK_MUST_BE_PAST " );
215
+ require (_updateBlockNum > lastL2MintAllowanceUpdateBlock, "BLOCK_MUST_BE_INCREMENTING " );
216
+ accumulatedL2MintAllowanceSnapshot = accumulatedL2MintAllowanceAtBlock (_updateBlockNum);
217
+ lastL2MintAllowanceUpdateBlock = _updateBlockNum;
218
+ l2MintAllowancePerBlock = _l2IssuancePerBlock;
219
+ emit L2MintAllowanceUpdated (
220
+ accumulatedL2MintAllowanceSnapshot,
221
+ l2MintAllowancePerBlock,
222
+ lastL2MintAllowanceUpdateBlock
223
+ );
224
+ }
225
+
226
+ /**
227
+ * @dev Manually sets the parameters used to compute the L2 mint allowance
228
+ * The use of this function is not recommended, use updateL2MintAllowance instead;
229
+ * this one is only meant to be used as a backup recovery if a previous call to
230
+ * updateL2MintAllowance was done with incorrect values.
231
+ * @param _accumulatedL2MintAllowanceSnapshot Accumulated L2 mint allowance at L1 block _lastL2MintAllowanceUpdateBlock
232
+ * @param _l2MintAllowancePerBlock L2 issuance per block since block number _lastL2MintAllowanceUpdateBlock
233
+ * @param _lastL2MintAllowanceUpdateBlock L1 Block number at which issuancePerBlock was last updated in L2
234
+ */
235
+ function setL2MintAllowanceParametersManual (
236
+ uint256 _accumulatedL2MintAllowanceSnapshot ,
237
+ uint256 _l2MintAllowancePerBlock ,
238
+ uint256 _lastL2MintAllowanceUpdateBlock
239
+ ) external onlyGovernor {
240
+ require (_lastL2MintAllowanceUpdateBlock < block .number , "BLOCK_MUST_BE_PAST " );
241
+ accumulatedL2MintAllowanceSnapshot = _accumulatedL2MintAllowanceSnapshot;
242
+ l2MintAllowancePerBlock = _l2MintAllowancePerBlock;
243
+ lastL2MintAllowanceUpdateBlock = _lastL2MintAllowanceUpdateBlock;
244
+ emit L2MintAllowanceUpdated (
245
+ accumulatedL2MintAllowanceSnapshot,
246
+ l2MintAllowancePerBlock,
247
+ lastL2MintAllowanceUpdateBlock
248
+ );
249
+ }
250
+
185
251
/**
186
252
* @notice Creates and sends a retryable ticket to transfer GRT to L2 using the Arbitrum Inbox.
187
253
* The tokens are escrowed by the gateway until they are withdrawn back to L1.
@@ -277,8 +343,10 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
277
343
require (_l1Token == address (token), "TOKEN_NOT_GRT " );
278
344
279
345
uint256 escrowBalance = token.balanceOf (escrow);
280
- // If the bridge doesn't have enough tokens, something's very wrong!
281
- require (_amount <= escrowBalance, "BRIDGE_OUT_OF_FUNDS " );
346
+ if (_amount > escrowBalance) {
347
+ // This will revert if trying to mint more than allowed
348
+ _mintFromL2 (_amount.sub (escrowBalance));
349
+ }
282
350
token.transferFrom (escrow, _to, _amount);
283
351
284
352
emit WithdrawalFinalized (_l1Token, _from, _to, 0 , _amount);
@@ -381,4 +449,42 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess
381
449
(maxSubmissionCost, extraData) = abi.decode (extraData, (uint256 , bytes ));
382
450
return (from, maxSubmissionCost, extraData);
383
451
}
452
+
453
+ /**
454
+ * @dev Get the accumulated L2 mint allowance at a particular block number
455
+ * @param _blockNum Block at which allowance will be computed
456
+ * @return The accumulated GRT amount that can be minted from L2 at the specified block
457
+ */
458
+ function accumulatedL2MintAllowanceAtBlock (uint256 _blockNum ) public view returns (uint256 ) {
459
+ require (_blockNum >= lastL2MintAllowanceUpdateBlock, "INVALID_BLOCK_FOR_MINT_ALLOWANCE " );
460
+ return
461
+ accumulatedL2MintAllowanceSnapshot.add (
462
+ l2MintAllowancePerBlock.mul (_blockNum.sub (lastL2MintAllowanceUpdateBlock))
463
+ );
464
+ }
465
+
466
+ /**
467
+ * @dev Mint new L1 tokens coming from L2
468
+ * This will check if the amount to mint is within the L2's mint allowance, and revert otherwise.
469
+ * The tokens will be sent to the bridge escrow (from where they will then be sent to the destinatary
470
+ * of the current inbound transfer).
471
+ * @param _amount Number of tokens to mint
472
+ */
473
+ function _mintFromL2 (uint256 _amount ) internal {
474
+ // If we're trying to mint more than allowed, something's gone terribly wrong
475
+ // (either the L2 issuance is wrong, or the Arbitrum bridge has been compromised)
476
+ require (_l2MintAmountAllowed (_amount), "INVALID_L2_MINT_AMOUNT " );
477
+ totalMintedFromL2 = totalMintedFromL2.add (_amount);
478
+ graphToken ().mint (escrow, _amount);
479
+ emit TokensMintedFromL2 (_amount);
480
+ }
481
+
482
+ /**
483
+ * @dev Check if minting a certain amount of tokens from L2 is within allowance
484
+ * @param _amount Number of tokens that would be minted
485
+ * @return true if minting those tokens is allowed, or false if it would be over allowance
486
+ */
487
+ function _l2MintAmountAllowed (uint256 _amount ) internal view returns (bool ) {
488
+ return (totalMintedFromL2.add (_amount) <= accumulatedL2MintAllowanceAtBlock (block .number ));
489
+ }
384
490
}
0 commit comments