Skip to content

Commit c61141e

Browse files
committed
Merged main into upstream main
2 parents f5da82b + f4400a4 commit c61141e

File tree

330 files changed

+72367
-20677
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

330 files changed

+72367
-20677
lines changed

.github/env.properties

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
NODE_URL=https://eth-mainnet.alchemyapi.io/v2/NbZ2px662CNSwdw3ZxdaZNe31yZbyddK
2-
BLOCK_NUMBER=18383559
1+
NODE_URL=https://eth-mainnet.g.alchemy.com/v2/DAcPqBIVkeOOgYLlHxFUQ0jySiZ-k8_6
2+
BLOCK_NUMBER=19911900

contracts/CrossChainDispatcher.sol

+30-11
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ error BridgeTokenNotSupported();
2929
error InvalidSlippageParam();
3030
error InvalidPayload();
3131

32+
// Note: The `IPool` wasn't updated to avoid changing interface
33+
// Refs: https://github.com/autonomoussoftware/metronome-synth/issues/877
34+
interface IPoolV4 is IPool {
35+
function isBridgingActive() external view returns (bool);
36+
}
37+
3238
/**
3339
* @title Cross-chain dispatcher
3440
*/
35-
contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1 {
41+
contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV2 {
3642
using SafeERC20 for IERC20;
3743
using BytesLib for bytes;
3844

@@ -96,7 +102,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
96102
}
97103

98104
modifier onlyIfBridgingIsNotPaused() {
99-
if (!isBridgingActive) revert BridgingIsPaused();
105+
if (!isBridgingActive || !IPoolV4(address(IManageable(msg.sender).pool())).isBridgingActive())
106+
revert BridgingIsPaused();
100107
_;
101108
}
102109

@@ -193,7 +200,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
193200
uint256 _requestId,
194201
uint256 _sgPoolId,
195202
address _account,
196-
uint256 _amountOutMin
203+
uint256 _amountOutMin,
204+
uint256 _callbackTxNativeFee
197205
) = CrossChainLib.decodeLeverageSwapPayload(payload_);
198206

199207
address _bridgeToken = IStargatePool(IStargateFactory(stargateComposer.factory()).getPool(_sgPoolId)).token();
@@ -216,7 +224,7 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
216224
tokenIn: _bridgeToken,
217225
dstChainId: _srcChainId,
218226
amountIn: amountIn_,
219-
nativeFee: poolRegistry.quoter().quoteLeverageCallbackNativeFee(_srcChainId),
227+
nativeFee: _callbackTxNativeFee + extraCallbackTxNativeFee[_requestId],
220228
payload: CrossChainLib.encodeLeverageCallbackPayload(_srcSmartFarmingManager, _requestId),
221229
refundAddress: _account,
222230
dstGasForCall: leverageCallbackTxGasLimit,
@@ -315,7 +323,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
315323
address _dstProxyOFT,
316324
uint256 _requestId,
317325
address _account,
318-
uint256 _amountOutMin
326+
uint256 _amountOutMin,
327+
uint256 _callbackTxNativeFee
319328
) = CrossChainLib.decodeFlashRepaySwapPayload(payload_);
320329

321330
address _syntheticToken = IProxyOFT(_dstProxyOFT).token();
@@ -344,7 +353,7 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
344353
refundAddress: _account,
345354
dstGasForCall: flashRepayCallbackTxGasLimit,
346355
dstNativeAmount: 0,
347-
nativeFee: poolRegistry.quoter().quoteFlashRepayCallbackNativeFee(_srcChainId)
356+
nativeFee: _callbackTxNativeFee + extraCallbackTxNativeFee[_requestId]
348357
})
349358
);
350359
}
@@ -380,7 +389,11 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
380389
payload_
381390
);
382391

383-
(, , uint256 _requestId, address _account, ) = CrossChainLib.decodeFlashRepaySwapPayload(payload_);
392+
(, , uint256 _requestId, address _account, , ) = CrossChainLib.decodeFlashRepaySwapPayload(payload_);
393+
394+
if (msg.value > 0) {
395+
extraCallbackTxNativeFee[_requestId] += msg.value;
396+
}
384397

385398
if (msg.sender == _account) {
386399
// Note: If `swapAmountOutMin[_requestId]` is `0` (default value), swap function will use payload's slippage param
@@ -409,12 +422,16 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
409422
bytes calldata payload_,
410423
uint256 newAmountOutMin_
411424
) external payable nonReentrant {
412-
(, address _dstProxyOFT, uint256 _requestId, , address _account, ) = CrossChainLib.decodeLeverageSwapPayload(
425+
(, address _dstProxyOFT, uint256 _requestId, , address _account, , ) = CrossChainLib.decodeLeverageSwapPayload(
413426
payload_
414427
);
415428

416429
if (!_isValidProxyOFT(_dstProxyOFT)) revert InvalidPayload();
417430

431+
if (msg.value > 0) {
432+
extraCallbackTxNativeFee[_requestId] += msg.value;
433+
}
434+
418435
if (msg.sender == _account) {
419436
// Note: If `swapAmountOutMin[_requestId]` is `0` (default value), swap function will use payload's slippage param
420437
if (newAmountOutMin_ == 0) revert InvalidSlippageParam();
@@ -472,7 +489,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
472489
dstProxyOFT_: _dstProxyOFT,
473490
requestId_: _requestId,
474491
account_: _account,
475-
amountOutMin_: amountOutMin_
492+
amountOutMin_: amountOutMin_,
493+
callbackTxNativeFee_: callbackTxNativeFee_
476494
});
477495
}
478496

@@ -508,7 +526,7 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
508526
uint256 amountIn_,
509527
uint256 amountOutMin_,
510528
bytes calldata lzArgs_
511-
) external payable override nonReentrant onlyIfSmartFarmingManager {
529+
) external payable override nonReentrant onlyIfSmartFarmingManager onlyIfBridgingIsNotPaused {
512530
address _account = account_; // stack too deep
513531

514532
(uint16 _dstChainId, uint256 _callbackTxNativeFee, uint64 _leverageSwapTxGasLimit) = CrossChainLib.decodeLzArgs(
@@ -534,7 +552,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
534552
requestId_: _requestId,
535553
sgPoolId_: _sgPoolId,
536554
account_: _account,
537-
amountOutMin_: _amountOutMin
555+
amountOutMin_: _amountOutMin,
556+
callbackTxNativeFee_: _callbackTxNativeFee
538557
});
539558
}
540559

contracts/Pool.sol

+13-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ error MaxLiquidableTooHigh();
3838
/**
3939
* @title Pool contract
4040
*/
41-
contract Pool is ReentrancyGuard, Pauseable, PoolStorageV3 {
41+
contract Pool is ReentrancyGuard, Pauseable, PoolStorageV4 {
4242
using SafeERC20 for IERC20;
4343
using SafeERC20 for ISyntheticToken;
4444
using WadRayMath for uint256;
@@ -52,6 +52,9 @@ contract Pool is ReentrancyGuard, Pauseable, PoolStorageV3 {
5252
*/
5353
uint256 public constant MAX_TOKENS_PER_USER = 30;
5454

55+
/// @notice Emitted when flag for pause bridge transfer is toggled
56+
event BridgingIsActiveUpdated(bool newIsActive);
57+
5558
/// @notice Emitted when protocol liquidation fee is updated
5659
event DebtFloorUpdated(uint256 oldDebtFloorInUsd, uint256 newDebtFloorInUsd);
5760

@@ -781,4 +784,13 @@ contract Pool is ReentrancyGuard, Pauseable, PoolStorageV3 {
781784
emit SmartFarmingManagerUpdated(_current, newSmartFarmingManager_);
782785
smartFarmingManager = newSmartFarmingManager_;
783786
}
787+
788+
/**
789+
* @notice Pause/Unpause bridge transfers
790+
*/
791+
function toggleBridgingIsActive() external onlyGovernor {
792+
bool _newIsBridgingActive = !isBridgingActive;
793+
emit BridgingIsActiveUpdated(_newIsBridgingActive);
794+
isBridgingActive = _newIsBridgingActive;
795+
}
784796
}

contracts/ProxyOFT.sol

+14-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,19 @@ pragma solidity 0.8.9;
44

55
import "./dependencies/@layerzerolabs/solidity-examples/contracts-upgradeable/token/oft/composable/ComposableOFTCoreUpgradeable.sol";
66
import "./storage/ProxyOFTStorage.sol";
7+
import "./interfaces/ICrossChainDispatcher.sol";
78

89
error AddressIsNull();
910
error SenderIsNotTheOwner();
1011
error BridgingIsPaused();
1112
error SenderIsNotCrossChainDispatcher();
13+
error DestinationChainNotAllowed();
14+
15+
// Note: The `ICrossChainDispatcher` wasn't updated to avoid changing interface
16+
// Refs: https://github.com/autonomoussoftware/metronome-synth/issues/877
17+
interface ICrossChainDispatcherExtended is ICrossChainDispatcher {
18+
function isDestinationChainSupported(uint16 dstChainId_) external view returns (bool);
19+
}
1220

1321
/**
1422
* @title The ProxyOFT contract
@@ -52,12 +60,16 @@ contract ProxyOFT is ComposableOFTCoreUpgradeable, ProxyOFTStorageV1 {
5260
/// @inheritdoc OFTCoreUpgradeable
5361
function _debitFrom(
5462
address from_,
55-
uint16 /*dstChainId_*/,
63+
uint16 dstChainId_,
5664
bytes memory /*toAddress_*/,
5765
uint amount_
5866
) internal override returns (uint256 _sent) {
59-
if (!syntheticToken.poolRegistry().crossChainDispatcher().isBridgingActive()) revert BridgingIsPaused();
67+
ICrossChainDispatcher _crossChainDispatcher = syntheticToken.poolRegistry().crossChainDispatcher();
6068
if (msg.sender != from_) revert SenderIsNotTheOwner();
69+
if (!_crossChainDispatcher.isBridgingActive()) revert BridgingIsPaused();
70+
if (!ICrossChainDispatcherExtended(address(_crossChainDispatcher)).isDestinationChainSupported(dstChainId_))
71+
revert DestinationChainNotAllowed();
72+
6173
syntheticToken.burn(from_, amount_);
6274
return amount_;
6375
}

contracts/Quoter.sol

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ contract Quoter is Initializable, QuoterStorageV1 {
164164
address(type(uint160).max),
165165
type(uint256).max,
166166
address(type(uint160).max),
167+
type(uint256).max,
167168
type(uint256).max
168169
),
169170
_lzTxParams: IStargateRouter.lzTxObj({
@@ -196,6 +197,7 @@ contract Quoter is Initializable, QuoterStorageV1 {
196197
type(uint256).max,
197198
type(uint256).max,
198199
address(type(uint160).max),
200+
type(uint256).max,
199201
type(uint256).max
200202
);
201203

contracts/SmartFarmingManager.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ contract SmartFarmingManager is ReentrancyGuard, Manageable, SmartFarmingManager
252252
IDebtToken _debtToken = pool.debtTokenOf(_request.syntheticToken);
253253
(uint256 _maxRepayAmount, ) = _debtToken.quoteRepayIn(_debtToken.balanceOf(_request.account));
254254
uint256 _repayAmount = Math.min(swapAmountOut_, _maxRepayAmount);
255-
(_repaid, ) = _debtToken.repay(_request.account, _repayAmount);
255+
if (_repayAmount > 0) (_repaid, ) = _debtToken.repay(_request.account, _repayAmount);
256256
if (_repaid < _request.repayAmountMin) revert FlashRepaySlippageTooHigh();
257257

258258
// 4. refund synthetic token in excess
@@ -694,7 +694,7 @@ contract SmartFarmingManager is ReentrancyGuard, Manageable, SmartFarmingManager
694694
* Note: The cross-chain code mostly uses LZ chain ids but in this case, we're using native id.
695695
*/
696696
function _nextCrossChainRequestId() private returns (uint256 _id) {
697-
return uint256(keccak256(abi.encode(block.chainid, ++crossChainRequestsLength)));
697+
return uint256(keccak256(abi.encode(block.chainid, address(this), ++crossChainRequestsLength)));
698698
}
699699

700700
/**

contracts/lib/CrossChainLib.sol

+37-18
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,20 @@ library CrossChainLib {
4747
address dstProxyOFT_,
4848
uint256 requestId_,
4949
address account_,
50-
uint256 amountOutMin_
50+
uint256 amountOutMin_,
51+
uint256 callbackTxNativeFee_
5152
) internal pure returns (bytes memory _payload) {
5253
return
5354
abi.encode(
5455
FLASH_REPAY,
55-
abi.encode(srcSmartFarmingManager_, dstProxyOFT_, requestId_, account_, amountOutMin_)
56+
abi.encode(
57+
srcSmartFarmingManager_,
58+
dstProxyOFT_,
59+
requestId_,
60+
account_,
61+
amountOutMin_,
62+
callbackTxNativeFee_
63+
)
5664
);
5765
}
5866

@@ -62,15 +70,16 @@ library CrossChainLib {
6270
internal
6371
pure
6472
returns (
65-
address srcSmartFarmingManager_,
66-
address dstProxyOFT_,
67-
uint256 requestId_,
68-
address account_,
69-
uint256 amountOutMin_
73+
address _srcSmartFarmingManager,
74+
address _dstProxyOFT,
75+
uint256 _requestId,
76+
address _account,
77+
uint256 _amountOutMin,
78+
uint256 _callbackTxNativeFee
7079
)
7180
{
7281
(, payload_) = abi.decode(payload_, (uint8, bytes));
73-
return abi.decode(payload_, (address, address, uint256, address, uint256));
82+
return abi.decode(payload_, (address, address, uint256, address, uint256, uint256));
7483
}
7584

7685
function encodeLeverageSwapPayload(
@@ -79,12 +88,21 @@ library CrossChainLib {
7988
uint256 requestId_,
8089
uint256 sgPoolId_,
8190
address account_,
82-
uint256 amountOutMin_
91+
uint256 amountOutMin_,
92+
uint256 callbackTxNativeFee_
8393
) internal pure returns (bytes memory _payload) {
8494
return
8595
abi.encode(
8696
LEVERAGE,
87-
abi.encode(srcSmartFarmingManager_, dstProxyOFT_, requestId_, sgPoolId_, account_, amountOutMin_)
97+
abi.encode(
98+
srcSmartFarmingManager_,
99+
dstProxyOFT_,
100+
requestId_,
101+
sgPoolId_,
102+
account_,
103+
amountOutMin_,
104+
callbackTxNativeFee_
105+
)
88106
);
89107
}
90108

@@ -94,16 +112,17 @@ library CrossChainLib {
94112
internal
95113
pure
96114
returns (
97-
address srcSmartFarmingManager_,
98-
address dstProxyOFT_,
99-
uint256 requestId_,
100-
uint256 sgPoolId_,
101-
address account_,
102-
uint256 amountOutMin_
115+
address _srcSmartFarmingManager,
116+
address _dstProxyOFT,
117+
uint256 _requestId,
118+
uint256 _sgPoolId,
119+
address _account,
120+
uint256 _amountOutMin,
121+
uint256 _callbackTxNativeFee
103122
)
104123
{
105124
(, payload_) = abi.decode(payload_, (uint8, bytes));
106-
return abi.decode(payload_, (address, address, uint256, uint256, address, uint256));
125+
return abi.decode(payload_, (address, address, uint256, uint256, address, uint256, uint256));
107126
}
108127

109128
function encodeLzArgs(
@@ -116,7 +135,7 @@ library CrossChainLib {
116135

117136
function decodeLzArgs(
118137
bytes memory lzArgs_
119-
) internal pure returns (uint16 dstChainId_, uint256 callbackNativeFee_, uint64 swapTxGasLimit_) {
138+
) internal pure returns (uint16 _dstChainId, uint256 _callbackNativeFee, uint64 _swapTxGasLimit) {
120139
return abi.decode(lzArgs_, (uint16, uint256, uint64));
121140
}
122141
}

contracts/storage/CrossChainDispatcherStorage.sol

+7
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,10 @@ abstract contract CrossChainDispatcherStorageV1 is ICrossChainDispatcher {
8383
*/
8484
address public sgeth;
8585
}
86+
87+
abstract contract CrossChainDispatcherStorageV2 is CrossChainDispatcherStorageV1 {
88+
/**
89+
* @notice Store extra amount sent when retrying a failed tx due to low native fee
90+
*/
91+
mapping(uint256 => uint256) public extraCallbackTxNativeFee;
92+
}

contracts/storage/PoolStorage.sol

+7
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,10 @@ abstract contract PoolStorageV3 is PoolStorageV2 {
104104
*/
105105
ISmartFarmingManager public smartFarmingManager;
106106
}
107+
108+
abstract contract PoolStorageV4 is PoolStorageV3 {
109+
/**
110+
* @notice Flag that pause/unpause pool's cross-chain activities
111+
*/
112+
bool public isBridgingActive;
113+
}

contracts/upgraders/ProxyOFTUpgrader.sol

+12-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,19 @@ contract ProxyOFTUpgrader is UpgraderBase {
1010
}
1111

1212
/// @inheritdoc UpgraderBase
13-
function _calls() internal pure override returns (bytes[] memory _callsList) {
13+
function _calls() internal pure virtual override returns (bytes[] memory _callsList) {
1414
_callsList = new bytes[](1);
1515
_callsList[0] = abi.encodeWithSignature("syntheticToken()");
1616
}
1717
}
18+
19+
contract ProxyOFTUpgraderV2 is ProxyOFTUpgrader {
20+
// solhint-disable-next-line no-empty-blocks
21+
constructor(address _owner) ProxyOFTUpgrader(_owner) {}
22+
23+
/// @inheritdoc UpgraderBase
24+
function _calls() internal pure virtual override returns (bytes[] memory _callsList) {
25+
_callsList = new bytes[](1);
26+
_callsList[0] = abi.encodeWithSignature("token()");
27+
}
28+
}

0 commit comments

Comments
 (0)