-
Notifications
You must be signed in to change notification settings - Fork 29
Feat: fast bridge router v2 #330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4f0f815
feat: copy FastBridgeRouter, scaffold new error
ChiTimesChi c09ea0d
test: establish template for FBR v2 tests
ChiTimesChi 205aa45
test: add token tests for FBR V2
ChiTimesChi bece474
feat: decode originSender in FastBridgeRouterV2
ChiTimesChi 7c78303
refactor: deduplicate origin query in tests
ChiTimesChi 91e3201
refactor: deduplicate V2 tests
ChiTimesChi cce2d13
test: add native ETH tests for FBR v2
ChiTimesChi ca314f7
refactor: EOA fallback
ChiTimesChi 112873d
chore: better docs for `_getOriginSender`
ChiTimesChi 9de0434
feat: FBRv2 deploy, configure scripts
ChiTimesChi cfdc7db
build: update network-specific options
ChiTimesChi a9cd93d
deploy: FBR v2
ChiTimesChi 4cc42b3
test: add cases for deadline/minAmountOut
ChiTimesChi 17ccc4d
fix: check deadline/amountOut for no-swap cases
ChiTimesChi 8324cb3
deploy: updated FB RouterV2
ChiTimesChi ff620c8
build: add helper scripts
ChiTimesChi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.17; | ||
|
|
||
| import {DefaultRouter} from "../router/DefaultRouter.sol"; | ||
| import {UniversalTokenLib} from "../router/libs/UniversalToken.sol"; | ||
| import {ActionLib, LimitedToken} from "../router/libs/Structs.sol"; | ||
| import {IFastBridge} from "./interfaces/IFastBridge.sol"; | ||
| import {IFastBridgeRouter, SwapQuery} from "./interfaces/IFastBridgeRouter.sol"; | ||
| import {ISwapQuoter} from "./interfaces/ISwapQuoter.sol"; | ||
|
|
||
| import {Ownable} from "@openzeppelin/contracts-4.5.0/access/Ownable.sol"; | ||
|
|
||
| contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { | ||
| using UniversalTokenLib for address; | ||
|
|
||
| error FastBridgeRouterV2__OriginSenderNotSpecified(); | ||
|
|
||
| /// @notice Emitted when the swap quoter is set. | ||
| /// @param newSwapQuoter The new swap quoter. | ||
| event SwapQuoterSet(address newSwapQuoter); | ||
|
|
||
| /// @notice Emitted when the new FastBridge contract is set. | ||
| /// @param newFastBridge The new FastBridge contract. | ||
| event FastBridgeSet(address newFastBridge); | ||
|
|
||
| /// @inheritdoc IFastBridgeRouter | ||
| bytes1 public constant GAS_REBATE_FLAG = 0x2A; | ||
|
|
||
| /// @inheritdoc IFastBridgeRouter | ||
| address public fastBridge; | ||
| /// @inheritdoc IFastBridgeRouter | ||
| address public swapQuoter; | ||
|
|
||
| constructor(address owner_) { | ||
| transferOwnership(owner_); | ||
| } | ||
|
|
||
| /// @inheritdoc IFastBridgeRouter | ||
| function setFastBridge(address fastBridge_) external onlyOwner { | ||
| fastBridge = fastBridge_; | ||
| emit FastBridgeSet(fastBridge_); | ||
| } | ||
|
|
||
| /// @inheritdoc IFastBridgeRouter | ||
| function setSwapQuoter(address swapQuoter_) external onlyOwner { | ||
| swapQuoter = swapQuoter_; | ||
| emit SwapQuoterSet(swapQuoter_); | ||
| } | ||
|
|
||
| /// @inheritdoc IFastBridgeRouter | ||
| function bridge( | ||
| address recipient, | ||
| uint256 chainId, | ||
| address token, | ||
| uint256 amount, | ||
| SwapQuery memory originQuery, | ||
| SwapQuery memory destQuery | ||
| ) external payable { | ||
| address originSender = _getOriginSender(destQuery.rawParams); | ||
| if (originSender == address(0)) { | ||
| revert FastBridgeRouterV2__OriginSenderNotSpecified(); | ||
| } | ||
| if (originQuery.hasAdapter()) { | ||
| // Perform a swap using the swap adapter, set this contract as recipient | ||
| (token, amount) = _doSwap(address(this), token, amount, originQuery); | ||
| } else { | ||
| // Otherwise, pull the token from the user to this contract | ||
| amount = _pullToken(address(this), token, amount); | ||
| } | ||
| IFastBridge.BridgeParams memory params = IFastBridge.BridgeParams({ | ||
| dstChainId: uint32(chainId), | ||
| sender: originSender, | ||
| to: recipient, | ||
| originToken: token, | ||
| destToken: destQuery.tokenOut, | ||
| originAmount: amount, | ||
| destAmount: destQuery.minAmountOut, | ||
| sendChainGas: _chainGasRequested(destQuery.rawParams), | ||
| deadline: destQuery.deadline | ||
| }); | ||
| token.universalApproveInfinity(fastBridge, amount); | ||
| uint256 msgValue = token == UniversalTokenLib.ETH_ADDRESS ? amount : 0; | ||
| IFastBridge(fastBridge).bridge{value: msgValue}(params); | ||
| } | ||
|
|
||
| /// @inheritdoc IFastBridgeRouter | ||
| function getOriginAmountOut( | ||
| address tokenIn, | ||
| address[] memory rfqTokens, | ||
| uint256 amountIn | ||
| ) external view returns (SwapQuery[] memory originQueries) { | ||
| uint256 len = rfqTokens.length; | ||
| originQueries = new SwapQuery[](len); | ||
| for (uint256 i = 0; i < len; ++i) { | ||
| originQueries[i] = ISwapQuoter(swapQuoter).getAmountOut( | ||
| LimitedToken({actionMask: ActionLib.allActions(), token: tokenIn}), | ||
| rfqTokens[i], | ||
| amountIn | ||
| ); | ||
| // Adjust the Adapter address if it exists | ||
| if (originQueries[i].hasAdapter()) { | ||
| originQueries[i].routerAdapter = address(this); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// @dev Retrieves the origin sender from the raw params. | ||
| /// Note: falls back to msg.sender if origin sender is not specified in the raw params, but | ||
| /// msg.sender is an EOA. | ||
| function _getOriginSender(bytes memory rawParams) internal view returns (address originSender) { | ||
| // Origin sender (if present) is encoded as 20 bytes following the rebate flag | ||
| if (rawParams.length >= 21) { | ||
| // The easiest way to read from memory is to use assembly | ||
| // solhint-disable-next-line no-inline-assembly | ||
| assembly { | ||
| // Skip the rawParams.length (32 bytes) and the rebate flag (1 byte) | ||
| originSender := mload(add(rawParams, 33)) | ||
| // The address is in the highest 160 bits. Shift right by 96 to get it in the lowest 160 bits | ||
| originSender := shr(96, originSender) | ||
| } | ||
| } | ||
| if (originSender == address(0) && msg.sender.code.length == 0) { | ||
| // Fall back to msg.sender if it is an EOA. This maintains backward compatibility | ||
| // for cases where we can safely assume that the origin sender is the same as msg.sender. | ||
| originSender = msg.sender; | ||
| } | ||
| } | ||
|
|
||
| /// @dev Checks if the explicit instruction to send gas to the destination chain was provided. | ||
| function _chainGasRequested(bytes memory rawParams) internal pure returns (bool) { | ||
| return rawParams.length > 0 && rawParams[0] == GAS_REBATE_FLAG; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {Address} from "@openzeppelin/contracts-4.5.0/utils/Address.sol"; | ||
|
|
||
| contract MockSenderContract { | ||
| function doCall(address target, bytes memory data) external payable { | ||
| Address.functionCallWithValue(target, data, msg.value); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using a more recent version of OpenZeppelin contracts.
The import statement uses version 4.5.0 of the OpenZeppelin contracts. Consider using a more recent version if available to benefit from the latest features and security fixes.
import {Address} from "@openzeppelin/contracts-4.5.0/utils/Address.sol";