Skip to content

Commit

Permalink
Merged main into upstream main
Browse files Browse the repository at this point in the history
  • Loading branch information
rokso committed May 31, 2024
2 parents f5da82b + f4400a4 commit c61141e
Show file tree
Hide file tree
Showing 330 changed files with 72,367 additions and 20,677 deletions.
4 changes: 2 additions & 2 deletions .github/env.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
NODE_URL=https://eth-mainnet.alchemyapi.io/v2/NbZ2px662CNSwdw3ZxdaZNe31yZbyddK
BLOCK_NUMBER=18383559
NODE_URL=https://eth-mainnet.g.alchemy.com/v2/DAcPqBIVkeOOgYLlHxFUQ0jySiZ-k8_6
BLOCK_NUMBER=19911900
41 changes: 30 additions & 11 deletions contracts/CrossChainDispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@ error BridgeTokenNotSupported();
error InvalidSlippageParam();
error InvalidPayload();

// Note: The `IPool` wasn't updated to avoid changing interface
// Refs: https://github.com/autonomoussoftware/metronome-synth/issues/877
interface IPoolV4 is IPool {
function isBridgingActive() external view returns (bool);
}

/**
* @title Cross-chain dispatcher
*/
contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1 {
contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV2 {
using SafeERC20 for IERC20;
using BytesLib for bytes;

Expand Down Expand Up @@ -96,7 +102,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
}

modifier onlyIfBridgingIsNotPaused() {
if (!isBridgingActive) revert BridgingIsPaused();
if (!isBridgingActive || !IPoolV4(address(IManageable(msg.sender).pool())).isBridgingActive())
revert BridgingIsPaused();
_;
}

Expand Down Expand Up @@ -193,7 +200,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
uint256 _requestId,
uint256 _sgPoolId,
address _account,
uint256 _amountOutMin
uint256 _amountOutMin,
uint256 _callbackTxNativeFee
) = CrossChainLib.decodeLeverageSwapPayload(payload_);

address _bridgeToken = IStargatePool(IStargateFactory(stargateComposer.factory()).getPool(_sgPoolId)).token();
Expand All @@ -216,7 +224,7 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
tokenIn: _bridgeToken,
dstChainId: _srcChainId,
amountIn: amountIn_,
nativeFee: poolRegistry.quoter().quoteLeverageCallbackNativeFee(_srcChainId),
nativeFee: _callbackTxNativeFee + extraCallbackTxNativeFee[_requestId],
payload: CrossChainLib.encodeLeverageCallbackPayload(_srcSmartFarmingManager, _requestId),
refundAddress: _account,
dstGasForCall: leverageCallbackTxGasLimit,
Expand Down Expand Up @@ -315,7 +323,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
address _dstProxyOFT,
uint256 _requestId,
address _account,
uint256 _amountOutMin
uint256 _amountOutMin,
uint256 _callbackTxNativeFee
) = CrossChainLib.decodeFlashRepaySwapPayload(payload_);

address _syntheticToken = IProxyOFT(_dstProxyOFT).token();
Expand Down Expand Up @@ -344,7 +353,7 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
refundAddress: _account,
dstGasForCall: flashRepayCallbackTxGasLimit,
dstNativeAmount: 0,
nativeFee: poolRegistry.quoter().quoteFlashRepayCallbackNativeFee(_srcChainId)
nativeFee: _callbackTxNativeFee + extraCallbackTxNativeFee[_requestId]
})
);
}
Expand Down Expand Up @@ -380,7 +389,11 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
payload_
);

(, , uint256 _requestId, address _account, ) = CrossChainLib.decodeFlashRepaySwapPayload(payload_);
(, , uint256 _requestId, address _account, , ) = CrossChainLib.decodeFlashRepaySwapPayload(payload_);

if (msg.value > 0) {
extraCallbackTxNativeFee[_requestId] += msg.value;
}

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

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

if (msg.value > 0) {
extraCallbackTxNativeFee[_requestId] += msg.value;
}

if (msg.sender == _account) {
// Note: If `swapAmountOutMin[_requestId]` is `0` (default value), swap function will use payload's slippage param
if (newAmountOutMin_ == 0) revert InvalidSlippageParam();
Expand Down Expand Up @@ -472,7 +489,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
dstProxyOFT_: _dstProxyOFT,
requestId_: _requestId,
account_: _account,
amountOutMin_: amountOutMin_
amountOutMin_: amountOutMin_,
callbackTxNativeFee_: callbackTxNativeFee_
});
}

Expand Down Expand Up @@ -508,7 +526,7 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
uint256 amountIn_,
uint256 amountOutMin_,
bytes calldata lzArgs_
) external payable override nonReentrant onlyIfSmartFarmingManager {
) external payable override nonReentrant onlyIfSmartFarmingManager onlyIfBridgingIsNotPaused {
address _account = account_; // stack too deep

(uint16 _dstChainId, uint256 _callbackTxNativeFee, uint64 _leverageSwapTxGasLimit) = CrossChainLib.decodeLzArgs(
Expand All @@ -534,7 +552,8 @@ contract CrossChainDispatcher is ReentrancyGuard, CrossChainDispatcherStorageV1
requestId_: _requestId,
sgPoolId_: _sgPoolId,
account_: _account,
amountOutMin_: _amountOutMin
amountOutMin_: _amountOutMin,
callbackTxNativeFee_: _callbackTxNativeFee
});
}

Expand Down
14 changes: 13 additions & 1 deletion contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ error MaxLiquidableTooHigh();
/**
* @title Pool contract
*/
contract Pool is ReentrancyGuard, Pauseable, PoolStorageV3 {
contract Pool is ReentrancyGuard, Pauseable, PoolStorageV4 {
using SafeERC20 for IERC20;
using SafeERC20 for ISyntheticToken;
using WadRayMath for uint256;
Expand All @@ -52,6 +52,9 @@ contract Pool is ReentrancyGuard, Pauseable, PoolStorageV3 {
*/
uint256 public constant MAX_TOKENS_PER_USER = 30;

/// @notice Emitted when flag for pause bridge transfer is toggled
event BridgingIsActiveUpdated(bool newIsActive);

/// @notice Emitted when protocol liquidation fee is updated
event DebtFloorUpdated(uint256 oldDebtFloorInUsd, uint256 newDebtFloorInUsd);

Expand Down Expand Up @@ -781,4 +784,13 @@ contract Pool is ReentrancyGuard, Pauseable, PoolStorageV3 {
emit SmartFarmingManagerUpdated(_current, newSmartFarmingManager_);
smartFarmingManager = newSmartFarmingManager_;
}

/**
* @notice Pause/Unpause bridge transfers
*/
function toggleBridgingIsActive() external onlyGovernor {
bool _newIsBridgingActive = !isBridgingActive;
emit BridgingIsActiveUpdated(_newIsBridgingActive);
isBridgingActive = _newIsBridgingActive;
}
}
16 changes: 14 additions & 2 deletions contracts/ProxyOFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@ pragma solidity 0.8.9;

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

error AddressIsNull();
error SenderIsNotTheOwner();
error BridgingIsPaused();
error SenderIsNotCrossChainDispatcher();
error DestinationChainNotAllowed();

// Note: The `ICrossChainDispatcher` wasn't updated to avoid changing interface
// Refs: https://github.com/autonomoussoftware/metronome-synth/issues/877
interface ICrossChainDispatcherExtended is ICrossChainDispatcher {
function isDestinationChainSupported(uint16 dstChainId_) external view returns (bool);
}

/**
* @title The ProxyOFT contract
Expand Down Expand Up @@ -52,12 +60,16 @@ contract ProxyOFT is ComposableOFTCoreUpgradeable, ProxyOFTStorageV1 {
/// @inheritdoc OFTCoreUpgradeable
function _debitFrom(
address from_,
uint16 /*dstChainId_*/,
uint16 dstChainId_,
bytes memory /*toAddress_*/,
uint amount_
) internal override returns (uint256 _sent) {
if (!syntheticToken.poolRegistry().crossChainDispatcher().isBridgingActive()) revert BridgingIsPaused();
ICrossChainDispatcher _crossChainDispatcher = syntheticToken.poolRegistry().crossChainDispatcher();
if (msg.sender != from_) revert SenderIsNotTheOwner();
if (!_crossChainDispatcher.isBridgingActive()) revert BridgingIsPaused();
if (!ICrossChainDispatcherExtended(address(_crossChainDispatcher)).isDestinationChainSupported(dstChainId_))
revert DestinationChainNotAllowed();

syntheticToken.burn(from_, amount_);
return amount_;
}
Expand Down
2 changes: 2 additions & 0 deletions contracts/Quoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ contract Quoter is Initializable, QuoterStorageV1 {
address(type(uint160).max),
type(uint256).max,
address(type(uint160).max),
type(uint256).max,
type(uint256).max
),
_lzTxParams: IStargateRouter.lzTxObj({
Expand Down Expand Up @@ -196,6 +197,7 @@ contract Quoter is Initializable, QuoterStorageV1 {
type(uint256).max,
type(uint256).max,
address(type(uint160).max),
type(uint256).max,
type(uint256).max
);

Expand Down
4 changes: 2 additions & 2 deletions contracts/SmartFarmingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ contract SmartFarmingManager is ReentrancyGuard, Manageable, SmartFarmingManager
IDebtToken _debtToken = pool.debtTokenOf(_request.syntheticToken);
(uint256 _maxRepayAmount, ) = _debtToken.quoteRepayIn(_debtToken.balanceOf(_request.account));
uint256 _repayAmount = Math.min(swapAmountOut_, _maxRepayAmount);
(_repaid, ) = _debtToken.repay(_request.account, _repayAmount);
if (_repayAmount > 0) (_repaid, ) = _debtToken.repay(_request.account, _repayAmount);
if (_repaid < _request.repayAmountMin) revert FlashRepaySlippageTooHigh();

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

/**
Expand Down
55 changes: 37 additions & 18 deletions contracts/lib/CrossChainLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,20 @@ library CrossChainLib {
address dstProxyOFT_,
uint256 requestId_,
address account_,
uint256 amountOutMin_
uint256 amountOutMin_,
uint256 callbackTxNativeFee_
) internal pure returns (bytes memory _payload) {
return
abi.encode(
FLASH_REPAY,
abi.encode(srcSmartFarmingManager_, dstProxyOFT_, requestId_, account_, amountOutMin_)
abi.encode(
srcSmartFarmingManager_,
dstProxyOFT_,
requestId_,
account_,
amountOutMin_,
callbackTxNativeFee_
)
);
}

Expand All @@ -62,15 +70,16 @@ library CrossChainLib {
internal
pure
returns (
address srcSmartFarmingManager_,
address dstProxyOFT_,
uint256 requestId_,
address account_,
uint256 amountOutMin_
address _srcSmartFarmingManager,
address _dstProxyOFT,
uint256 _requestId,
address _account,
uint256 _amountOutMin,
uint256 _callbackTxNativeFee
)
{
(, payload_) = abi.decode(payload_, (uint8, bytes));
return abi.decode(payload_, (address, address, uint256, address, uint256));
return abi.decode(payload_, (address, address, uint256, address, uint256, uint256));
}

function encodeLeverageSwapPayload(
Expand All @@ -79,12 +88,21 @@ library CrossChainLib {
uint256 requestId_,
uint256 sgPoolId_,
address account_,
uint256 amountOutMin_
uint256 amountOutMin_,
uint256 callbackTxNativeFee_
) internal pure returns (bytes memory _payload) {
return
abi.encode(
LEVERAGE,
abi.encode(srcSmartFarmingManager_, dstProxyOFT_, requestId_, sgPoolId_, account_, amountOutMin_)
abi.encode(
srcSmartFarmingManager_,
dstProxyOFT_,
requestId_,
sgPoolId_,
account_,
amountOutMin_,
callbackTxNativeFee_
)
);
}

Expand All @@ -94,16 +112,17 @@ library CrossChainLib {
internal
pure
returns (
address srcSmartFarmingManager_,
address dstProxyOFT_,
uint256 requestId_,
uint256 sgPoolId_,
address account_,
uint256 amountOutMin_
address _srcSmartFarmingManager,
address _dstProxyOFT,
uint256 _requestId,
uint256 _sgPoolId,
address _account,
uint256 _amountOutMin,
uint256 _callbackTxNativeFee
)
{
(, payload_) = abi.decode(payload_, (uint8, bytes));
return abi.decode(payload_, (address, address, uint256, uint256, address, uint256));
return abi.decode(payload_, (address, address, uint256, uint256, address, uint256, uint256));
}

function encodeLzArgs(
Expand All @@ -116,7 +135,7 @@ library CrossChainLib {

function decodeLzArgs(
bytes memory lzArgs_
) internal pure returns (uint16 dstChainId_, uint256 callbackNativeFee_, uint64 swapTxGasLimit_) {
) internal pure returns (uint16 _dstChainId, uint256 _callbackNativeFee, uint64 _swapTxGasLimit) {
return abi.decode(lzArgs_, (uint16, uint256, uint64));
}
}
7 changes: 7 additions & 0 deletions contracts/storage/CrossChainDispatcherStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,10 @@ abstract contract CrossChainDispatcherStorageV1 is ICrossChainDispatcher {
*/
address public sgeth;
}

abstract contract CrossChainDispatcherStorageV2 is CrossChainDispatcherStorageV1 {
/**
* @notice Store extra amount sent when retrying a failed tx due to low native fee
*/
mapping(uint256 => uint256) public extraCallbackTxNativeFee;
}
7 changes: 7 additions & 0 deletions contracts/storage/PoolStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,10 @@ abstract contract PoolStorageV3 is PoolStorageV2 {
*/
ISmartFarmingManager public smartFarmingManager;
}

abstract contract PoolStorageV4 is PoolStorageV3 {
/**
* @notice Flag that pause/unpause pool's cross-chain activities
*/
bool public isBridgingActive;
}
13 changes: 12 additions & 1 deletion contracts/upgraders/ProxyOFTUpgrader.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ contract ProxyOFTUpgrader is UpgraderBase {
}

/// @inheritdoc UpgraderBase
function _calls() internal pure override returns (bytes[] memory _callsList) {
function _calls() internal pure virtual override returns (bytes[] memory _callsList) {
_callsList = new bytes[](1);
_callsList[0] = abi.encodeWithSignature("syntheticToken()");
}
}

contract ProxyOFTUpgraderV2 is ProxyOFTUpgrader {
// solhint-disable-next-line no-empty-blocks
constructor(address _owner) ProxyOFTUpgrader(_owner) {}

/// @inheritdoc UpgraderBase
function _calls() internal pure virtual override returns (bytes[] memory _callsList) {
_callsList = new bytes[](1);
_callsList[0] = abi.encodeWithSignature("token()");
}
}
Loading

0 comments on commit c61141e

Please sign in to comment.