new file: Interchain-message/.env.example
new file: Interchain-message/.eslintrc.js new file: Interchain-message/.gitignore new file: Interchain-message/.prettierrc new file: Interchain-message/.solhint.json new file: Interchain-message/.solhintignore new file: Interchain-message/contracts/interfaces/IBridge.sol new file: Interchain-message/contracts/interfaces/IMessageBus.sol new file: Interchain-message/contracts/interfaces/IMessageReceiverApp.sol new file: Interchain-message/contracts/interfaces/IUniswapRouterV3.sol new file: Interchain-message/contracts/interfaces/IWETH.sol new file: Interchain-message/contracts/message/apps/BridgeSwap.sol new file: Interchain-message/contracts/message/apps/RubicRouterV2.sol new file: Interchain-message/contracts/message/apps/RubicRouterV2ETH.sol new file: Interchain-message/contracts/message/apps/SwapBase.sol new file: Interchain-message/contracts/message/apps/TransferSwapInch.sol new file: Interchain-message/contracts/message/apps/TransferSwapV2.sol new file: Interchain-message/contracts/message/apps/TransferSwapV3.sol new file: Interchain-message/contracts/message/framework/MessageReceiverApp.sol new file: Interchain-message/contracts/message/framework/MessageSenderApp.sol new file: Interchain-message/contracts/message/libraries/MessageSenderLib.sol new file: Interchain-message/contracts/message/libraries/MsgDataTypes.sol new file: Interchain-message/contracts/test/MessageBusSender.sol new file: Interchain-message/contracts/test/TestERC20.sol new file: Interchain-message/contracts/test/TestMessages.sol new file: Interchain-message/contracts/test/WETH9.sol new file: Interchain-message/deployments/Readme.md new file: Interchain-message/executor/config/cbridge.toml new file: Interchain-message/executor/config/executor.toml new file: Interchain-message/executor/eth-ks/signer.json new file: Interchain-message/hardhat.config.ts new file: Interchain-message/package-lock.json new file: Interchain-message/package.json new file: Interchain-message/reports/contract_sizes.txt new file: Interchain-message/reports/gas_usage/summary.txt new file: Interchain-message/scripts/deploy/deploy.js new file: Interchain-message/scripts/deploy/deployAVAX.ts new file: Interchain-message/scripts/deploy/deployArbitrum.ts new file: Interchain-message/scripts/deploy/deployAurora.ts new file: Interchain-message/scripts/deploy/deployBSC.ts new file: Interchain-message/scripts/deploy/deployEth.ts new file: Interchain-message/scripts/deploy/deployFantom.ts new file: Interchain-message/scripts/deploy/deployPoly.ts new file: Interchain-message/scripts/privateKey.js new file: Interchain-message/scripts/sendTx/avaxToFantomBridge.js new file: Interchain-message/scripts/sendTx/avaxToFantomNativeV2.js new file: Interchain-message/test/RubicCrossChainBridge.spec.ts new file: Interchain-message/test/RubicCrossChainV2.spec.ts new file: Interchain-message/test/RubicCrossChainV3.spec.ts new file: Interchain-message/test/RubicFallback.spec.ts new file: Interchain-message/test/RubicSettings.spec.ts new file: Interchain-message/test/shared/consts.ts new file: Interchain-message/test/shared/fixtures.ts new file: Interchain-message/test/shared/utils.ts new file: Interchain-message/tsconfig.json deleted: Rubic-Inter-chain-Message-develop.zip deleted: proxy-instant-trades-master.zip deleted: rubic-app-master.zip deleted: rubic-sdk-master.zip
This commit is contained in:
parent
670beacbea
commit
df8039f494
22
Interchain-message/.env.example
Normal file
22
Interchain-message/.env.example
Normal file
|
@ -0,0 +1,22 @@
|
|||
ETHERSCAN_API_KEY=39
|
||||
BSCSCAN_API_KEY=SV
|
||||
POLYGONSCAN_API_KEY=CK
|
||||
AVALANCHE_API_KEY=XD
|
||||
FANTOMSCAN_API_KEY=NV
|
||||
MNEMONIC=32432
|
||||
INFURA_ID_PROJECT=b4
|
||||
|
||||
REPORT_GAS=true
|
||||
|
||||
ROUTERS_POLYGON=0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff,0xE592427A0AEce92De3Edee1F18E0157C05861564,0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506,0xC0788A3aD43d79aa53B09c2EaCc313A787d1d607,0x1111111254fb6c44bAC0beD2854e76F90643097d,0x89D6B81A1Ef25894620D05ba843d83B0A296239e
|
||||
NATIVE_POLYGON=0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270
|
||||
TRANSIT_POLYGON=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
|
||||
BUS_POLYGON=0x265B25e22bcd7f10a5bD6E6410F10537Cc7567e8
|
||||
BUS_POLYGON_MAIN=0xaFDb9C40C7144022811F034EE07Ce2E110093fe6
|
||||
SWAP_TOKEN_POLYGON=0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619
|
||||
|
||||
ROUTERS_BSC_TESTNET=0x9Ac64Cc6e4415144C455BD8E4837Fea55603e5c3
|
||||
NATIVE_BSC_TESTNET=0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd
|
||||
TRANSIT_BSC_TESTNET=0x7d43AABC515C356145049227CeE54B608342c0ad
|
||||
SWAP_TOKEN_BSC_TESTNET=0x5471ea8f739dd37E9B81Be9c5c77754D8AA953E4
|
||||
BUS_BSC_TESTNET=0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA
|
94
Interchain-message/.eslintrc.js
Normal file
94
Interchain-message/.eslintrc.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
overrides: [
|
||||
{
|
||||
files: ['test/**/*.ts', 'scripts/**/*.ts'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
createDefaultProgram: true
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'unused-imports',
|
||||
'import'
|
||||
],
|
||||
extends: [
|
||||
'airbnb-typescript/base',
|
||||
'plugin:prettier/recommended',
|
||||
'prettier'
|
||||
],
|
||||
rules: {
|
||||
'import/prefer-default-export': 'off',
|
||||
'@typescript-eslint/no-useless-constructor': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'class-method-use-this': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
vars: 'all',
|
||||
args: 'all',
|
||||
ignoreRestSiblings: false,
|
||||
argsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
complexity: ['error', 20],
|
||||
eqeqeq: ['error', 'always'],
|
||||
'no-magic-numbers': 'off',
|
||||
'@typescript-eslint/no-magic-numbers': [
|
||||
'warn',
|
||||
{
|
||||
ignore: [-1, 0, 1, 2, 10, 100, 1000, 16, 64, 256],
|
||||
detectObjects: true,
|
||||
ignoreReadonlyClassProperties: true
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
selector: 'enumMember',
|
||||
format: ['UPPER_CASE']
|
||||
}
|
||||
],
|
||||
'no-empty': ['error', { 'allowEmptyCatch': true }],
|
||||
// Styling.
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
indent: 'off',
|
||||
'comma-dangle': 'off',
|
||||
'@typescript-eslint/comma-dangle': ['error', 'never'],
|
||||
// Temporary rules. Remove after full refactoring.
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'@typescript-eslint/dot-notation': 'off',
|
||||
'no-restricted-globals': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
// Temporary rules. Remove as fast as it can be.
|
||||
'max-classes-per-file': 'off',
|
||||
radix: ['warn', 'as-needed'],
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-return-assign': 'off',
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
'LabeledStatement',
|
||||
'WithStatement'
|
||||
],
|
||||
'no-console': [
|
||||
'warn',
|
||||
{
|
||||
allow: ['debug', 'error', 'info']
|
||||
}
|
||||
],
|
||||
'import/export': 0
|
||||
}
|
||||
}
|
||||
],
|
||||
env: {
|
||||
"es6": true
|
||||
},
|
||||
};
|
9
Interchain-message/.gitignore
vendored
Normal file
9
Interchain-message/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
.env
|
||||
typechain-types
|
||||
artifacts
|
||||
cache
|
||||
node_modules
|
||||
.idea
|
||||
.openzeppelin
|
||||
typechain
|
||||
.DS_Store
|
28
Interchain-message/.prettierrc
Normal file
28
Interchain-message/.prettierrc
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/prettierrc",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.sol",
|
||||
"options": {
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.ts"],
|
||||
"options": {
|
||||
"arrowParens": "avoid",
|
||||
"bracketSpacing": true,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "always",
|
||||
"quoteProps": "as-needed",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
16
Interchain-message/.solhint.json
Normal file
16
Interchain-message/.solhint.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "solhint:recommended",
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"compiler-version": ["error", "^0.7.0"],
|
||||
"func-visibility": ["warn", { "ignoreConstructors": true }],
|
||||
"no-empty-blocks": "off",
|
||||
"reason-string": ["warn", { "maxLength": 64 }],
|
||||
"not-rely-on-time": "off",
|
||||
"no-inline-assembly": "off"
|
||||
}
|
||||
}
|
1
Interchain-message/.solhintignore
Normal file
1
Interchain-message/.solhintignore
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
46
Interchain-message/contracts/interfaces/IBridge.sol
Normal file
46
Interchain-message/contracts/interfaces/IBridge.sol
Normal file
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
interface IBridge {
|
||||
function send(
|
||||
address _receiver,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _dstChainId,
|
||||
uint64 _nonce,
|
||||
uint32 _maxSlippage
|
||||
) external;
|
||||
|
||||
function relay(
|
||||
bytes calldata _relayRequest,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external;
|
||||
|
||||
function transfers(bytes32 transferId) external view returns (bool);
|
||||
|
||||
function withdraws(bytes32 withdrawId) external view returns (bool);
|
||||
|
||||
function withdraw(
|
||||
bytes calldata _wdmsg,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @notice Verifies that a message is signed by a quorum among the signers.
|
||||
* @param _msg signed message
|
||||
* @param _sigs list of signatures sorted by signer addresses in ascending order
|
||||
* @param _signers sorted list of current signers
|
||||
* @param _powers powers of current signers
|
||||
*/
|
||||
function verifySigs(
|
||||
bytes memory _msg,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external view;
|
||||
}
|
126
Interchain-message/contracts/interfaces/IMessageBus.sol
Normal file
126
Interchain-message/contracts/interfaces/IMessageBus.sol
Normal file
|
@ -0,0 +1,126 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "../message/libraries/MsgDataTypes.sol";
|
||||
|
||||
interface IMessageBus {
|
||||
function liquidityBridge() external view returns (address);
|
||||
|
||||
function pegBridge() external view returns (address);
|
||||
|
||||
function pegBridgeV2() external view returns (address);
|
||||
|
||||
function pegVault() external view returns (address);
|
||||
|
||||
function pegVaultV2() external view returns (address);
|
||||
|
||||
/**
|
||||
* @notice Calculates the required fee for the message.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
@ @return The required fee.
|
||||
*/
|
||||
function calcFee(bytes calldata _message) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @notice Sends a message to a contract on another chain.
|
||||
* Sender needs to make sure the uniqueness of the message Id, which is computed as
|
||||
* hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message).
|
||||
* If messages with the same Id are sent, only one of them will succeed at dst chain..
|
||||
* A fee is charged in the native gas token.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
*/
|
||||
function sendMessage(
|
||||
address _receiver,
|
||||
uint256 _dstChainId,
|
||||
bytes calldata _message
|
||||
) external payable;
|
||||
|
||||
/**
|
||||
* @notice Sends a message associated with a transfer to a contract on another chain.
|
||||
* If messages with the same srcTransferId are sent, only one of them will succeed at dst chain..
|
||||
* A fee is charged in the native token.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _srcBridge The bridge contract to send the transfer with.
|
||||
* @param _srcTransferId The transfer ID.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
*/
|
||||
function sendMessageWithTransfer(
|
||||
address _receiver,
|
||||
uint256 _dstChainId,
|
||||
address _srcBridge,
|
||||
bytes32 _srcTransferId,
|
||||
bytes calldata _message
|
||||
) external payable;
|
||||
|
||||
/**
|
||||
* @notice Withdraws message fee in the form of native gas token.
|
||||
* @param _account The address receiving the fee.
|
||||
* @param _cumulativeFee The cumulative fee credited to the account. Tracked by SGN.
|
||||
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be
|
||||
* signed-off by +2/3 of the sigsVerifier's current signing power to be delivered.
|
||||
* @param _signers The sorted list of signers.
|
||||
* @param _powers The signing powers of the signers.
|
||||
*/
|
||||
function withdrawFee(
|
||||
address _account,
|
||||
uint256 _cumulativeFee,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @notice Execute a message with a successful transfer.
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _transfer The transfer info.
|
||||
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
|
||||
* +2/3 of the sigsVerifier's current signing power to be delivered.
|
||||
* @param _signers The sorted list of signers.
|
||||
* @param _powers The signing powers of the signers.
|
||||
*/
|
||||
function executeMessageWithTransfer(
|
||||
bytes calldata _message,
|
||||
MsgDataTypes.TransferInfo calldata _transfer,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external payable;
|
||||
|
||||
/**
|
||||
* @notice Execute a message with a refunded transfer.
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _transfer The transfer info.
|
||||
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
|
||||
* +2/3 of the sigsVerifier's current signing power to be delivered.
|
||||
* @param _signers The sorted list of signers.
|
||||
* @param _powers The signing powers of the signers.
|
||||
*/
|
||||
function executeMessageWithTransferRefund(
|
||||
bytes calldata _message, // the same message associated with the original transfer
|
||||
MsgDataTypes.TransferInfo calldata _transfer,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external payable;
|
||||
|
||||
/**
|
||||
* @notice Execute a message not associated with a transfer.
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
|
||||
* +2/3 of the sigsVerifier's current signing power to be delivered.
|
||||
* @param _signers The sorted list of signers.
|
||||
* @param _powers The signing powers of the signers.
|
||||
*/
|
||||
function executeMessage(
|
||||
bytes calldata _message,
|
||||
MsgDataTypes.RouteInfo calldata _route,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external payable;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
interface IMessageReceiverApp {
|
||||
enum ExecutionStatus {
|
||||
Fail, // execution failed, finalized
|
||||
Success, // execution succeeded, finalized
|
||||
Retry // execution rejected, can retry later
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Called by MessageBus (MessageBusReceiver) if the process is originated from MessageBus (MessageBusSender)'s
|
||||
* sendMessageWithTransfer it is only called when the tokens are checked to be arrived at this contract's address.
|
||||
* @param _sender The address of the source app contract
|
||||
* @param _token The address of the token that comes out of the bridge
|
||||
* @param _amount The amount of tokens received at this contract through the cross-chain bridge.
|
||||
* the contract that implements this contract can safely assume that the tokens will arrive before this
|
||||
* function is called.
|
||||
* @param _srcChainId The source chain ID where the transfer is originated from
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessageWithTransfer(
|
||||
address _sender,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
) external payable returns (ExecutionStatus);
|
||||
|
||||
/**
|
||||
* @notice Only called by MessageBus (MessageBusReceiver) if
|
||||
* 1. executeMessageWithTransfer reverts, or
|
||||
* 2. executeMessageWithTransfer returns ExecutionStatus.Fail
|
||||
* @param _sender The address of the source app contract
|
||||
* @param _token The address of the token that comes out of the bridge
|
||||
* @param _amount The amount of tokens received at this contract through the cross-chain bridge.
|
||||
* the contract that implements this contract can safely assume that the tokens will arrive before this
|
||||
* function is called.
|
||||
* @param _srcChainId The source chain ID where the transfer is originated from
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessageWithTransferFallback(
|
||||
address _sender,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
) external payable returns (ExecutionStatus);
|
||||
|
||||
/**
|
||||
* @notice Called by MessageBus (MessageBusReceiver) to process refund of the original transfer from this contract
|
||||
* @param _token The token address of the original transfer
|
||||
* @param _amount The amount of the original transfer
|
||||
* @param _message The same message associated with the original transfer
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessageWithTransferRefund(
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
) external payable returns (ExecutionStatus);
|
||||
|
||||
/**
|
||||
* @notice Called by MessageBus (MessageBusReceiver)
|
||||
* @param _sender The address of the source app contract
|
||||
* @param _srcChainId The source chain ID where the transfer is originated from
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessage(
|
||||
address _sender,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
) external payable returns (ExecutionStatus);
|
||||
}
|
108
Interchain-message/contracts/interfaces/IUniswapRouterV3.sol
Normal file
108
Interchain-message/contracts/interfaces/IUniswapRouterV3.sol
Normal file
|
@ -0,0 +1,108 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
pragma solidity >=0.7.5;
|
||||
pragma abicoder v2;
|
||||
|
||||
/// @title Router token swapping functionality
|
||||
/// @notice Functions for swapping tokens via Uniswap Algebra
|
||||
interface IUniswapRouterV3 {
|
||||
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
|
||||
/// @dev In the implementation you must pay the pool tokens owed for the swap.
|
||||
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
|
||||
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
|
||||
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
|
||||
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
|
||||
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
|
||||
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
|
||||
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
|
||||
function uniswapV3SwapCallback(
|
||||
int256 amount0Delta,
|
||||
int256 amount1Delta,
|
||||
bytes calldata data
|
||||
) external;
|
||||
|
||||
struct ExactInputSingleParams {
|
||||
address tokenIn;
|
||||
address tokenOut;
|
||||
uint24 fee;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountIn;
|
||||
uint256 amountOutMinimum;
|
||||
uint160 sqrtPriceLimitX96;
|
||||
}
|
||||
|
||||
/// @notice Swaps `amountIn` of one token for as much as possible of another token
|
||||
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
|
||||
/// @return amountOut The amount of the received token
|
||||
function exactInputSingle(ExactInputSingleParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256 amountOut);
|
||||
|
||||
struct ExactInputParams {
|
||||
bytes path;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountIn;
|
||||
uint256 amountOutMinimum;
|
||||
}
|
||||
|
||||
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
|
||||
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
|
||||
/// @return amountOut The amount of the received token
|
||||
function exactInput(ExactInputParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256 amountOut);
|
||||
|
||||
struct ExactOutputSingleParams {
|
||||
address tokenIn;
|
||||
address tokenOut;
|
||||
uint24 fee;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountOut;
|
||||
uint256 amountInMaximum;
|
||||
uint160 sqrtPriceLimitX96;
|
||||
}
|
||||
|
||||
/// @notice Swaps as little as possible of one token for `amountOut` of another token
|
||||
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
|
||||
/// @return amountIn The amount of the input token
|
||||
function exactOutputSingle(ExactOutputSingleParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256 amountIn);
|
||||
|
||||
struct ExactOutputParams {
|
||||
bytes path;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountOut;
|
||||
uint256 amountInMaximum;
|
||||
}
|
||||
|
||||
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
|
||||
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
|
||||
/// @return amountIn The amount of the input token
|
||||
function exactOutput(ExactOutputParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256 amountIn);
|
||||
|
||||
function WETH9() external view returns (address);
|
||||
|
||||
function WNativeToken() external view returns (address);
|
||||
|
||||
function refundETH() external payable;
|
||||
|
||||
function refundNativeToken() external payable;
|
||||
|
||||
function unwrapWETH9(uint256 amountMinimum, address recipient)
|
||||
external
|
||||
payable;
|
||||
|
||||
function unwrapWNativeToken(uint256 amountMinimum, address recipient)
|
||||
external
|
||||
payable;
|
||||
}
|
9
Interchain-message/contracts/interfaces/IWETH.sol
Normal file
9
Interchain-message/contracts/interfaces/IWETH.sol
Normal file
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
interface IWETH {
|
||||
function deposit() external payable;
|
||||
|
||||
function withdraw(uint256) external;
|
||||
}
|
99
Interchain-message/contracts/message/apps/BridgeSwap.sol
Normal file
99
Interchain-message/contracts/message/apps/BridgeSwap.sol
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "./SwapBase.sol";
|
||||
|
||||
contract BridgeSwap is SwapBase {
|
||||
function bridgeWithSwapNative(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
address _srcBridgeToken,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecksNative(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
_srcBridgeToken
|
||||
);
|
||||
|
||||
_sendBridgeMessage(
|
||||
_receiver,
|
||||
_dstChainId,
|
||||
_srcBridgeToken,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
_amountIn
|
||||
);
|
||||
}
|
||||
|
||||
function bridgeWithSwap(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
address _srcBridgeToken,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecks(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
_srcBridgeToken
|
||||
);
|
||||
|
||||
_sendBridgeMessage(
|
||||
_receiver,
|
||||
_dstChainId,
|
||||
_srcBridgeToken,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
_amountIn
|
||||
);
|
||||
}
|
||||
|
||||
function _sendBridgeMessage(
|
||||
address _receiver,
|
||||
uint64 _dstChainId,
|
||||
address _srcBridgeToken,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage,
|
||||
uint256 _fee,
|
||||
uint256 _amountIn
|
||||
) private {
|
||||
BaseCrossChainParams memory _baseParams = BaseCrossChainParams(
|
||||
_srcBridgeToken,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_retrieveDstTokenAddress(_dstSwap),
|
||||
_dstSwap.amountOutMinimum,
|
||||
_dstSwap.receiverEOA,
|
||||
_dstSwap.integrator,
|
||||
address(0)
|
||||
);
|
||||
|
||||
require(
|
||||
_baseParams.dstChainID != uint64(block.chainid),
|
||||
"same chain id"
|
||||
);
|
||||
|
||||
bytes32 id = _sendMessage(
|
||||
_receiver,
|
||||
uint64(_baseParams.dstChainID),
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_beforeSwapAndSendMessage(),
|
||||
_fee,
|
||||
_baseParams.srcInputToken,
|
||||
_baseParams.srcInputAmount,
|
||||
true
|
||||
);
|
||||
|
||||
emit CrossChainRequestSent(id, _baseParams);
|
||||
}
|
||||
}
|
392
Interchain-message/contracts/message/apps/RubicRouterV2.sol
Normal file
392
Interchain-message/contracts/message/apps/RubicRouterV2.sol
Normal file
|
@ -0,0 +1,392 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "./TransferSwapV2.sol";
|
||||
import "./TransferSwapV3.sol";
|
||||
import "./TransferSwapInch.sol";
|
||||
import "./BridgeSwap.sol";
|
||||
|
||||
contract RubicRouterV2 is
|
||||
TransferSwapV2,
|
||||
TransferSwapV3,
|
||||
TransferSwapInch,
|
||||
BridgeSwap
|
||||
{
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
|
||||
constructor(
|
||||
uint256 _fixedCryptoFee,
|
||||
uint256 _RubicPlatformFee,
|
||||
address[] memory _routers,
|
||||
address[] memory _tokens,
|
||||
uint256[] memory _minTokenAmounts,
|
||||
uint256[] memory _maxTokenAmounts,
|
||||
uint256[] memory _blockchainIDs,
|
||||
uint256[] memory _blockchainToGasFee,
|
||||
address _relayer,
|
||||
address _messageBus,
|
||||
address _nativeWrap
|
||||
) {
|
||||
initialize(
|
||||
_fixedCryptoFee,
|
||||
_RubicPlatformFee,
|
||||
_routers,
|
||||
_tokens,
|
||||
_minTokenAmounts,
|
||||
_maxTokenAmounts,
|
||||
_blockchainIDs,
|
||||
_blockchainToGasFee
|
||||
);
|
||||
|
||||
nativeWrap = _nativeWrap;
|
||||
messageBus = _messageBus;
|
||||
_setupRole(RELAYER_ROLE, _relayer);
|
||||
}
|
||||
|
||||
function initialize(
|
||||
uint256 _fixedCryptoFee,
|
||||
uint256 _RubicPlatformFee,
|
||||
address[] memory _routers,
|
||||
address[] memory _tokens,
|
||||
uint256[] memory _minTokenAmounts,
|
||||
uint256[] memory _maxTokenAmounts,
|
||||
uint256[] memory _blockchainIDs,
|
||||
uint256[] memory _blockchainToGasFee
|
||||
) private initializer {
|
||||
__WithDestinationFunctionalityInit(
|
||||
_fixedCryptoFee,
|
||||
_RubicPlatformFee,
|
||||
_routers,
|
||||
_tokens,
|
||||
_minTokenAmounts,
|
||||
_maxTokenAmounts,
|
||||
_blockchainIDs,
|
||||
_blockchainToGasFee
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice called by MessageBus when the tokens are checked to be arrived at this contract's address.
|
||||
sends the amount received to the receiver. swaps beforehand if swap behavior is defined in message
|
||||
* NOTE: if the swap fails, it sends the tokens received directly to the receiver as fallback behavior
|
||||
* @param _token the address of the token sent through the bridge
|
||||
* @param _amount the amount of tokens received at this contract through the cross-chain bridge
|
||||
* @param _srcChainId source chain ID
|
||||
* @param _message SwapRequestDst message that defines the swap behavior on this destination chain
|
||||
*/
|
||||
function executeMessageWithTransfer(
|
||||
address,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _relayer
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
onlyMessageBus
|
||||
nonReentrant
|
||||
whenNotPaused
|
||||
onlyRelayer(_relayer)
|
||||
returns (ExecutionStatus)
|
||||
{
|
||||
SwapRequestDest memory m = abi.decode((_message), (SwapRequestDest));
|
||||
bytes32 id = _computeSwapRequestId(
|
||||
m.swap.receiverEOA,
|
||||
_srcChainId,
|
||||
uint64(block.chainid),
|
||||
_message
|
||||
);
|
||||
|
||||
_amount = accrueTokenFees(
|
||||
m.swap.integrator,
|
||||
integratorToFeeInfo[m.swap.integrator],
|
||||
_amount,
|
||||
0,
|
||||
_token
|
||||
);
|
||||
|
||||
address _outputToken = _retrieveDstTokenAddress(m.swap);
|
||||
|
||||
if (m.swap.version == SwapVersion.v3) {
|
||||
_executeDstSwapV3(_token, _outputToken, _amount, id, m);
|
||||
} else if (m.swap.version == SwapVersion.bridge) {
|
||||
_executeDstBridge(_token, _amount, id, m);
|
||||
} else {
|
||||
_executeDstSwapV2(_token, _outputToken, _amount, id, m);
|
||||
}
|
||||
|
||||
// always return true since swap failure is already handled in-place
|
||||
return ExecutionStatus.Success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice called by MessageBus when the executeMessageWithTransfer call fails. does nothing but emitting a "fail" event
|
||||
* @param _srcChainId source chain ID
|
||||
* @param _message SwapRequestDst message that defines the swap behavior on this destination chain
|
||||
* execution on dst chain
|
||||
*/
|
||||
function executeMessageWithTransferFallback(
|
||||
address, // _sender
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _relayer
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
onlyMessageBus
|
||||
nonReentrant
|
||||
onlyRelayer(_relayer)
|
||||
returns (ExecutionStatus)
|
||||
{
|
||||
SwapRequestDest memory m = abi.decode((_message), (SwapRequestDest));
|
||||
|
||||
bytes32 id = _computeSwapRequestId(
|
||||
m.swap.receiverEOA,
|
||||
_srcChainId,
|
||||
uint64(block.chainid),
|
||||
_message
|
||||
);
|
||||
|
||||
// collect data about failed cross-chain for manual refund
|
||||
refundDetails[id] = RefundData(
|
||||
m.swap.integrator,
|
||||
_token,
|
||||
_amount,
|
||||
m.swap.receiverEOA,
|
||||
m.swap.nativeOut
|
||||
);
|
||||
|
||||
// Failed status means user hasn't received funds
|
||||
_afterTargetProcessing(id, _token, _amount, SwapStatus.Failed);
|
||||
// always return Fail to mark this transfer as failed since if this function is called then there nothing more
|
||||
// we can do in this app as the swap failures are already handled in executeMessageWithTransfer
|
||||
return ExecutionStatus.Fail;
|
||||
}
|
||||
|
||||
// called on source chain for handling of bridge failures (bad liquidity, bad slippage, etc...)
|
||||
function executeMessageWithTransferRefund(
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
bytes calldata _message,
|
||||
address _relayer
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
onlyMessageBus
|
||||
nonReentrant
|
||||
whenNotPaused
|
||||
onlyRelayer(_relayer)
|
||||
returns (ExecutionStatus)
|
||||
{
|
||||
SwapRequestDest memory m = abi.decode((_message), (SwapRequestDest));
|
||||
|
||||
bytes32 id = _computeSwapRequestId(
|
||||
m.swap.receiverEOA,
|
||||
uint64(block.chainid),
|
||||
m.dstChainId,
|
||||
_message
|
||||
);
|
||||
|
||||
_sendToken(_token, _amount, m.swap.receiverEOA, m.swap.nativeOut);
|
||||
|
||||
_afterTargetProcessing(id, _token, _amount, SwapStatus.Fallback);
|
||||
|
||||
return ExecutionStatus.Success;
|
||||
}
|
||||
|
||||
// no need to swap, directly send the bridged token to user
|
||||
function _executeDstBridge(
|
||||
address _inputToken,
|
||||
uint256 _amount,
|
||||
bytes32 _id,
|
||||
SwapRequestDest memory _msgDst
|
||||
) private {
|
||||
_sendToken(
|
||||
_inputToken,
|
||||
_amount,
|
||||
_msgDst.swap.receiverEOA,
|
||||
_msgDst.swap.nativeOut
|
||||
);
|
||||
|
||||
_afterTargetProcessing(_id, _inputToken, _amount, SwapStatus.Succeeded);
|
||||
}
|
||||
|
||||
function _executeDstSwapV2(
|
||||
address _inputToken,
|
||||
address _outputToken,
|
||||
uint256 _amount,
|
||||
bytes32 _id,
|
||||
SwapRequestDest memory _msgDst
|
||||
) private isTransit(_inputToken, _msgDst.swap.path[0]) {
|
||||
SwapInfoV2 memory _dstSwap = SwapInfoV2({
|
||||
dex: _msgDst.swap.dex,
|
||||
path: _msgDst.swap.path,
|
||||
deadline: _msgDst.swap.deadline,
|
||||
amountOutMinimum: _msgDst.swap.amountOutMinimum
|
||||
});
|
||||
uint256 balanceBefore = IERC20Upgradeable(_outputToken).balanceOf(
|
||||
address(this)
|
||||
);
|
||||
(bool success, ) = _trySwapV2(_dstSwap, _amount);
|
||||
uint256 dstAmount = IERC20Upgradeable(_outputToken).balanceOf(
|
||||
address(this)
|
||||
) - balanceBefore;
|
||||
if (success) {
|
||||
_sendToken(
|
||||
_outputToken,
|
||||
dstAmount,
|
||||
_msgDst.swap.receiverEOA,
|
||||
_msgDst.swap.nativeOut
|
||||
);
|
||||
_afterTargetProcessing(
|
||||
_id,
|
||||
_outputToken,
|
||||
dstAmount,
|
||||
SwapStatus.Succeeded
|
||||
);
|
||||
} else {
|
||||
// handle swap failure, send the received token directly to receiver
|
||||
_sendToken(
|
||||
_inputToken,
|
||||
_amount,
|
||||
_msgDst.swap.receiverEOA,
|
||||
_msgDst.swap.nativeOut
|
||||
);
|
||||
_afterTargetProcessing(
|
||||
_id,
|
||||
_inputToken,
|
||||
_amount,
|
||||
SwapStatus.Fallback
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _executeDstSwapV3(
|
||||
address _inputToken,
|
||||
address _outputToken,
|
||||
uint256 _amount,
|
||||
bytes32 _id,
|
||||
SwapRequestDest memory _msgDst
|
||||
)
|
||||
private
|
||||
isTransit(_inputToken, address(_getFirstBytes20(_msgDst.swap.pathV3)))
|
||||
{
|
||||
SwapInfoV3 memory _dstSwap = SwapInfoV3({
|
||||
dex: _msgDst.swap.dex,
|
||||
path: _msgDst.swap.pathV3,
|
||||
deadline: _msgDst.swap.deadline,
|
||||
amountOutMinimum: _msgDst.swap.amountOutMinimum
|
||||
});
|
||||
|
||||
uint256 balanceBefore = IERC20Upgradeable(_outputToken).balanceOf(
|
||||
address(this)
|
||||
);
|
||||
(bool success, ) = _trySwapV3(_dstSwap, _amount);
|
||||
uint256 dstAmount = IERC20Upgradeable(_outputToken).balanceOf(
|
||||
address(this)
|
||||
) - balanceBefore;
|
||||
|
||||
if (success) {
|
||||
_sendToken(
|
||||
_outputToken,
|
||||
dstAmount,
|
||||
_msgDst.swap.receiverEOA,
|
||||
_msgDst.swap.nativeOut
|
||||
);
|
||||
_afterTargetProcessing(
|
||||
_id,
|
||||
_outputToken,
|
||||
dstAmount,
|
||||
SwapStatus.Succeeded
|
||||
);
|
||||
} else {
|
||||
// handle swap failure, send the received token directly to receiver
|
||||
_sendToken(
|
||||
_inputToken,
|
||||
_amount,
|
||||
_msgDst.swap.receiverEOA,
|
||||
_msgDst.swap.nativeOut
|
||||
);
|
||||
_afterTargetProcessing(
|
||||
_id,
|
||||
_inputToken,
|
||||
_amount,
|
||||
SwapStatus.Fallback
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _sendToken(
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
address _receiver,
|
||||
bool _nativeOut
|
||||
) private {
|
||||
if (_token == nativeWrap && _nativeOut == true) {
|
||||
IWETH(nativeWrap).withdraw(_amount);
|
||||
sendToken(address(0), _amount, _receiver);
|
||||
} else {
|
||||
sendToken(_token, _amount, _receiver);
|
||||
}
|
||||
}
|
||||
|
||||
function _afterTargetProcessing(
|
||||
bytes32 _id,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
SwapStatus _status
|
||||
) private {
|
||||
processedTransactions[_id] = _status;
|
||||
emit CrossChainProcessed(_id, _token, _amount, _status);
|
||||
}
|
||||
|
||||
function sweepTokens(address _token, uint256 _amount) external onlyAdmin {
|
||||
sendToken(_token, _amount, msg.sender);
|
||||
}
|
||||
|
||||
function manualRefund(bytes32 _id)
|
||||
external
|
||||
nonReentrant
|
||||
onlyManagerOrAdmin
|
||||
{
|
||||
SwapStatus _status = processedTransactions[_id];
|
||||
require(
|
||||
_status != SwapStatus.Succeeded && _status != SwapStatus.Fallback
|
||||
);
|
||||
|
||||
RefundData memory refundParams = refundDetails[_id];
|
||||
|
||||
uint256 _amount = accrueTokenFees(
|
||||
refundParams.integrator,
|
||||
integratorToFeeInfo[refundParams.integrator],
|
||||
refundParams.amount,
|
||||
0,
|
||||
refundParams.token
|
||||
);
|
||||
|
||||
_sendToken(
|
||||
refundParams.token,
|
||||
_amount,
|
||||
refundParams.to,
|
||||
refundParams.nativeOut
|
||||
);
|
||||
processedTransactions[_id] = SwapStatus.Fallback;
|
||||
}
|
||||
|
||||
function setNativeWrap(address _nativeWrap) external onlyManagerOrAdmin {
|
||||
nativeWrap = _nativeWrap;
|
||||
}
|
||||
|
||||
function setMessageBus(address _messageBus) external onlyManagerOrAdmin {
|
||||
messageBus = _messageBus;
|
||||
emit MessageBusUpdated(messageBus);
|
||||
}
|
||||
}
|
326
Interchain-message/contracts/message/apps/RubicRouterV2ETH.sol
Normal file
326
Interchain-message/contracts/message/apps/RubicRouterV2ETH.sol
Normal file
|
@ -0,0 +1,326 @@
|
|||
//// SPDX-License-Identifier: MIT
|
||||
//
|
||||
//pragma solidity >=0.8.9;
|
||||
//
|
||||
//import './TransferSwapV2.sol';
|
||||
//import './TransferSwapV3.sol';
|
||||
//import './TransferSwapInch.sol';
|
||||
//import './BridgeSwap.sol';
|
||||
//
|
||||
//
|
||||
//contract RubicRouterV2ETH is TransferSwapV2, TransferSwapV3, TransferSwapInch, BridgeSwap {
|
||||
// using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
// using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
//
|
||||
// event CrossChainProcessed(bytes32 id, uint256 dstAmount, SwapStatus status);
|
||||
//
|
||||
// /// @dev This modifier prevents using executor functions
|
||||
// modifier onlyExecutor(address _executor) {
|
||||
// require(hasRole(EXECUTOR_ROLE, _executor), 'SwapBase: caller is not an executor');
|
||||
// _;
|
||||
// }
|
||||
//
|
||||
// constructor(
|
||||
// uint256[] memory _blockchainIDs,
|
||||
// uint256[] memory _cryptoFees,
|
||||
// uint256[] memory _platformFees,
|
||||
// address[] memory _tokens,
|
||||
// uint256[] memory _minTokenAmounts,
|
||||
// uint256[] memory _maxTokenAmounts,
|
||||
// address[] memory _routers,
|
||||
// address _executor,
|
||||
// address _messageBus,
|
||||
// address _nativeWrap
|
||||
// ) {
|
||||
// initialize(
|
||||
// _blockchainIDs,
|
||||
// _cryptoFees,
|
||||
// _platformFees,
|
||||
// _tokens,
|
||||
// _minTokenAmounts,
|
||||
// _maxTokenAmounts,
|
||||
// _routers
|
||||
// );
|
||||
//
|
||||
// nativeWrap = _nativeWrap;
|
||||
// messageBus = _messageBus;
|
||||
// _setupRole(EXECUTOR_ROLE, _executor);
|
||||
// }
|
||||
//
|
||||
// function initialize(
|
||||
// uint256[] memory _blockchainIDs,
|
||||
// uint256[] memory _cryptoFees,
|
||||
// uint256[] memory _platformFees,
|
||||
// address[] memory _tokens,
|
||||
// uint256[] memory _minTokenAmounts,
|
||||
// uint256[] memory _maxTokenAmounts,
|
||||
// address[] memory _routers
|
||||
// ) private initializer {
|
||||
// __MultipleTransitTokenInit(
|
||||
// _blockchainIDs,
|
||||
// _cryptoFees,
|
||||
// _platformFees,
|
||||
// _tokens,
|
||||
// _minTokenAmounts,
|
||||
// _maxTokenAmounts,
|
||||
// _routers
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @notice called by MessageBus when the tokens are checked to be arrived at this contract's address.
|
||||
// sends the amount received to the receiver. swaps beforehand if swap behavior is defined in message
|
||||
// * NOTE: if the swap fails, it sends the tokens received directly to the receiver as fallback behavior
|
||||
// * @param _token the address of the token sent through the bridge
|
||||
// * @param _amount the amount of tokens received at this contract through the cross-chain bridge
|
||||
// * @param _srcChainId source chain ID
|
||||
// * @param _message SwapRequestV2 message that defines the swap behavior on this destination chain
|
||||
// */
|
||||
// function executeMessageWithTransfer(
|
||||
// address,
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// uint64 _srcChainId,
|
||||
// bytes calldata _message,
|
||||
// address _executor
|
||||
// )
|
||||
// external
|
||||
// payable
|
||||
// override
|
||||
// onlyMessageBus
|
||||
// nonReentrant
|
||||
// whenNotPaused
|
||||
// onlyExecutor(_executor)
|
||||
// returns (ExecutionStatus)
|
||||
// {
|
||||
// SwapRequestDest memory m = abi.decode((_message), (SwapRequestDest));
|
||||
// bytes32 id = _computeSwapRequestId(m.receiver, _srcChainId, uint64(block.chainid), _message);
|
||||
//
|
||||
// if (_token == nativeWrap) {
|
||||
// IWETH(nativeWrap).deposit{value: _amount}();
|
||||
// }
|
||||
//
|
||||
// _amount = calculateFee(m.swap.integrator, _amount, uint256(_srcChainId), _token);
|
||||
//
|
||||
// if (m.swap.version == SwapVersion.v3) {
|
||||
// _executeDstSwapV3(_token, _amount, id, m);
|
||||
// } else if (m.swap.version == SwapVersion.bridge) {
|
||||
// _executeDstBridge(_token, _amount, id, m);
|
||||
// } else {
|
||||
// _executeDstSwapV2(_token, _amount, id, m);
|
||||
// }
|
||||
//
|
||||
// // always return true since swap failure is already handled in-place
|
||||
// return ExecutionStatus.Success;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @notice called by MessageBus when the executeMessageWithTransfer call fails. does nothing but emitting a "fail" event
|
||||
// * @param _srcChainId source chain ID
|
||||
// * @param _message SwapRequest message that defines the swap behavior on this destination chain
|
||||
// * execution on dst chain
|
||||
// */
|
||||
// function executeMessageWithTransferFallback(
|
||||
// address, // _sender
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// uint64 _srcChainId,
|
||||
// bytes calldata _message,
|
||||
// address _executor
|
||||
// ) external payable override onlyMessageBus nonReentrant onlyExecutor(_executor) returns (ExecutionStatus) {
|
||||
// SwapRequestDest memory m = abi.decode((_message), (SwapRequestDest));
|
||||
//
|
||||
// bytes32 id = _computeSwapRequestId(m.receiver, _srcChainId, uint64(block.chainid), _message);
|
||||
//
|
||||
// if (_token == nativeWrap) {
|
||||
// IWETH(nativeWrap).deposit{value: _amount}();
|
||||
// }
|
||||
//
|
||||
// // Failed status means user hasn't received funds
|
||||
// SwapStatus status = SwapStatus.Failed;
|
||||
// processedTransactions[id] = status;
|
||||
// emit CrossChainProcessed(id, _amount, status);
|
||||
// // always return Fail to mark this transfer as failed since if this function is called then there nothing more
|
||||
// // we can do in this app as the swap failures are already handled in executeMessageWithTransfer
|
||||
// return ExecutionStatus.Fail;
|
||||
// }
|
||||
//
|
||||
// // called on source chain for handling of bridge failures (bad liquidity, bad slippage, etc...)
|
||||
// function executeMessageWithTransferRefund(
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// bytes calldata _message,
|
||||
// address _executor
|
||||
// )
|
||||
// external
|
||||
// payable
|
||||
// override
|
||||
// onlyMessageBus
|
||||
// nonReentrant
|
||||
// whenNotPaused
|
||||
// onlyExecutor(_executor)
|
||||
// returns (ExecutionStatus)
|
||||
// {
|
||||
// SwapRequestDest memory m = abi.decode((_message), (SwapRequestDest));
|
||||
//
|
||||
// bytes32 id = _computeSwapRequestId(m.receiver, uint64(block.chainid), m.dstChainId, _message);
|
||||
//
|
||||
// if (_token == nativeWrap) {
|
||||
// IWETH(nativeWrap).deposit{value: _amount}();
|
||||
// }
|
||||
//
|
||||
// _sendToken(_token, _amount, m.receiver, m.swap.nativeOut);
|
||||
//
|
||||
// SwapStatus status = SwapStatus.Fallback;
|
||||
// processedTransactions[id] = status;
|
||||
// emit CrossChainProcessed(id, _amount, status);
|
||||
//
|
||||
// return ExecutionStatus.Success;
|
||||
// }
|
||||
//
|
||||
// // no need to swap, directly send the bridged token to user
|
||||
// function _executeDstBridge(
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// bytes32 _id,
|
||||
// SwapRequestDest memory _msgDst
|
||||
// ) private {
|
||||
// require(
|
||||
// _token == _msgDst.swap.path[0],
|
||||
// 'bridged token must be the same as the first token in destination swap path'
|
||||
// );
|
||||
// require(_msgDst.swap.path.length == 1, 'dst bridge expected');
|
||||
// _sendToken(_msgDst.swap.path[0], _amount, _msgDst.receiver, _msgDst.swap.nativeOut);
|
||||
//
|
||||
// SwapStatus status;
|
||||
// status = SwapStatus.Succeeded;
|
||||
//
|
||||
// processedTransactions[_id] = status;
|
||||
// emit CrossChainProcessed(_id, _amount, status);
|
||||
// }
|
||||
//
|
||||
// function _executeDstSwapV2(
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// bytes32 _id,
|
||||
// SwapRequestDest memory _msgDst
|
||||
// ) private {
|
||||
// require(
|
||||
// _token == _msgDst.swap.path[0],
|
||||
// 'bridged token must be the same as the first token in destination swap path'
|
||||
// );
|
||||
// require(_msgDst.swap.path.length > 1, 'dst swap expected');
|
||||
//
|
||||
// uint256 dstAmount;
|
||||
// SwapStatus status;
|
||||
//
|
||||
// SwapInfoV2 memory _dstSwap = SwapInfoV2({
|
||||
// dex: _msgDst.swap.dex,
|
||||
// path: _msgDst.swap.path,
|
||||
// deadline: _msgDst.swap.deadline,
|
||||
// amountOutMinimum: _msgDst.swap.amountOutMinimum
|
||||
// });
|
||||
//
|
||||
// bool success;
|
||||
// (success, dstAmount) = _trySwapV2(_dstSwap, _amount);
|
||||
// if (success) {
|
||||
// _sendToken(_dstSwap.path[_dstSwap.path.length - 1], dstAmount, _msgDst.receiver, _msgDst.swap.nativeOut);
|
||||
// status = SwapStatus.Succeeded;
|
||||
// processedTransactions[_id] = status;
|
||||
// } else {
|
||||
// // handle swap failure, send the received token directly to receiver
|
||||
// _sendToken(_token, _amount, _msgDst.receiver, _msgDst.swap.nativeOut);
|
||||
// dstAmount = _amount;
|
||||
// status = SwapStatus.Fallback;
|
||||
// processedTransactions[_id] = status;
|
||||
// }
|
||||
//
|
||||
// emit CrossChainProcessed(_id, dstAmount, status);
|
||||
// }
|
||||
//
|
||||
// function _executeDstSwapV3(
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// bytes32 _id,
|
||||
// SwapRequestDest memory _msgDst
|
||||
// ) private {
|
||||
// require(
|
||||
// _token == address(_getFirstBytes20(_msgDst.swap.pathV3)),
|
||||
// 'bridged token must be the same as the first token in destination swap path'
|
||||
// );
|
||||
// require(_msgDst.swap.pathV3.length > 20, 'dst swap expected');
|
||||
//
|
||||
// uint256 dstAmount;
|
||||
// SwapStatus status;
|
||||
//
|
||||
// SwapInfoV3 memory _dstSwap = SwapInfoV3({
|
||||
// dex: _msgDst.swap.dex,
|
||||
// path: _msgDst.swap.pathV3,
|
||||
// deadline: _msgDst.swap.deadline,
|
||||
// amountOutMinimum: _msgDst.swap.amountOutMinimum
|
||||
// });
|
||||
//
|
||||
// bool success;
|
||||
// (success, dstAmount) = _trySwapV3(_dstSwap, _amount);
|
||||
// if (success) {
|
||||
// _sendToken(address(_getLastBytes20(_dstSwap.path)), dstAmount, _msgDst.receiver, _msgDst.swap.nativeOut);
|
||||
// status = SwapStatus.Succeeded;
|
||||
// processedTransactions[_id] = status;
|
||||
// } else {
|
||||
// // handle swap failure, send the received token directly to receiver
|
||||
// _sendToken(_token, _amount, _msgDst.receiver, _msgDst.swap.nativeOut);
|
||||
// dstAmount = _amount;
|
||||
// status = SwapStatus.Fallback;
|
||||
// processedTransactions[_id] = status;
|
||||
// }
|
||||
//
|
||||
// emit CrossChainProcessed(_id, dstAmount, status);
|
||||
// }
|
||||
//
|
||||
// function _sendToken(
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// address _receiver,
|
||||
// bool _nativeOut
|
||||
// ) private {
|
||||
// if (_token == nativeWrap && _nativeOut == true) {
|
||||
// IWETH(nativeWrap).withdraw(_amount);
|
||||
// (bool sent, ) = _receiver.call{value: _amount, gas: 50000}('');
|
||||
// require(sent, 'failed to send native');
|
||||
// } else {
|
||||
// IERC20Upgradeable(_token).safeTransfer(_receiver, _amount);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function sweepTokens(
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// bool _nativeOut
|
||||
// ) external onlyManagerOrAdmin {
|
||||
// _sendToken(_token, _amount, msg.sender, _nativeOut);
|
||||
// }
|
||||
//
|
||||
// function manualRefund(
|
||||
// bytes32 _id,
|
||||
// address _token,
|
||||
// uint256 _amount,
|
||||
// address _to,
|
||||
// bool _nativeOut
|
||||
// ) external nonReentrant {
|
||||
// require(
|
||||
// hasRole(MANAGER_ROLE, msg.sender) || hasRole(EXECUTOR_ROLE, msg.sender) || hasRole(DEFAULT_ADMIN_ROLE, msg.sender)
|
||||
// );
|
||||
// require(processedTransactions[_id] != SwapStatus.Succeeded && processedTransactions[_id] != SwapStatus.Fallback);
|
||||
// _sendToken(_token, _amount, _to, _nativeOut);
|
||||
// processedTransactions[_id] = SwapStatus.Fallback;
|
||||
// }
|
||||
//
|
||||
// function setNativeWrap(address _nativeWrap) external onlyManagerOrAdmin {
|
||||
// nativeWrap = _nativeWrap;
|
||||
// }
|
||||
//
|
||||
// function setMessageBus(address _messageBus) public onlyManagerOrAdmin {
|
||||
// messageBus = _messageBus;
|
||||
// emit MessageBusUpdated(messageBus);
|
||||
// }
|
||||
//}
|
280
Interchain-message/contracts/message/apps/SwapBase.sol
Normal file
280
Interchain-message/contracts/message/apps/SwapBase.sol
Normal file
|
@ -0,0 +1,280 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "rubic-bridge-base/contracts/architecture/WithDestinationFunctionality.sol";
|
||||
|
||||
import "rubic-bridge-base/contracts/libraries/SmartApprove.sol";
|
||||
|
||||
import "../framework/MessageSenderApp.sol";
|
||||
import "../../interfaces/IWETH.sol";
|
||||
|
||||
contract SwapBase is MessageSenderApp, WithDestinationFunctionality {
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
|
||||
address public nativeWrap;
|
||||
uint64 public nonce;
|
||||
|
||||
mapping(bytes32 => RefundData) public refundDetails;
|
||||
|
||||
modifier isTransit(address _transitToken, address _tokenInPath) {
|
||||
checkIsTransit(_transitToken, _tokenInPath);
|
||||
_;
|
||||
}
|
||||
|
||||
// ============== struct for refunds ==============
|
||||
|
||||
struct RefundData {
|
||||
address integrator; // integrator address in order to take commission
|
||||
address token; // transit token
|
||||
uint256 amount; // amount of transit token
|
||||
address to; // recipient
|
||||
bool nativeOut; // receive wrapped/native
|
||||
}
|
||||
|
||||
// ============== struct for V2 like dexes ==============
|
||||
|
||||
struct SwapInfoV2 {
|
||||
address dex; // the DEX to use for the swap
|
||||
// if this array has only one element, it means no need to swap
|
||||
address[] path;
|
||||
// the following fields are only needed if path.length > 1
|
||||
uint256 deadline; // deadline for the swap
|
||||
uint256 amountOutMinimum; // minimum receive amount for the swap
|
||||
}
|
||||
|
||||
// ============== struct for V3 like dexes ==============
|
||||
|
||||
struct SwapInfoV3 {
|
||||
address dex; // the DEX to use for the swap
|
||||
bytes path;
|
||||
uint256 deadline;
|
||||
uint256 amountOutMinimum;
|
||||
}
|
||||
|
||||
// ============== struct for inch swap ==============
|
||||
|
||||
struct SwapInfoInch {
|
||||
address dex;
|
||||
// path is tokenIn, tokenOut
|
||||
address[] path;
|
||||
bytes data;
|
||||
uint256 amountOutMinimum;
|
||||
}
|
||||
|
||||
// ============== struct dstSwap ==============
|
||||
// This is needed to make v2 -> SGN -> v3 swaps and etc.
|
||||
|
||||
struct SwapInfoDest {
|
||||
address dex; // dex address
|
||||
bool nativeOut;
|
||||
address receiverEOA; // EOA recipient in dst chain
|
||||
address integrator;
|
||||
SwapVersion version; // identifies swap type
|
||||
address[] path; // path address for v2 and inch
|
||||
bytes pathV3; // path address for v3
|
||||
uint256 deadline; // for v2 and v3
|
||||
uint256 amountOutMinimum;
|
||||
}
|
||||
|
||||
struct SwapRequestDest {
|
||||
SwapInfoDest swap;
|
||||
uint64 nonce;
|
||||
uint64 dstChainId;
|
||||
}
|
||||
|
||||
enum SwapVersion {
|
||||
v2,
|
||||
v3,
|
||||
bridge
|
||||
}
|
||||
|
||||
// ============== common checks for src swaps ==============
|
||||
|
||||
function _deriveFeeAndPerformChecksNative(
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
address _integrator,
|
||||
address srcInputToken
|
||||
) internal onlyEOA whenNotPaused returns (uint256 _fee) {
|
||||
require(srcInputToken == nativeWrap, "token mismatch");
|
||||
require(msg.value >= _amountIn, "amount insufficient");
|
||||
IWETH(nativeWrap).deposit{value: _amountIn}();
|
||||
|
||||
_fee =
|
||||
accrueFixedAndGasFees(
|
||||
_integrator,
|
||||
integratorToFeeInfo[_integrator],
|
||||
_dstChainId
|
||||
) -
|
||||
_amountIn;
|
||||
}
|
||||
|
||||
function _deriveFeeAndPerformChecks(
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
address _integrator,
|
||||
address srcInputToken
|
||||
) internal onlyEOA whenNotPaused returns (uint256 _fee) {
|
||||
IERC20Upgradeable(srcInputToken).safeTransferFrom(
|
||||
msg.sender,
|
||||
address(this),
|
||||
_amountIn
|
||||
);
|
||||
|
||||
_fee = accrueFixedAndGasFees(
|
||||
_integrator,
|
||||
integratorToFeeInfo[_integrator],
|
||||
_dstChainId
|
||||
);
|
||||
}
|
||||
|
||||
// ============== Celer call ==============
|
||||
|
||||
function _sendMessage(
|
||||
address _receiver,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage,
|
||||
uint64 _nonce,
|
||||
uint256 _fee,
|
||||
address _srcOutputToken,
|
||||
uint256 _srcAmtOut,
|
||||
bool _success
|
||||
) internal returns (bytes32 id) {
|
||||
if (!_success) revert("src swap failed");
|
||||
|
||||
require(_srcAmtOut >= minTokenAmount[_srcOutputToken], "less than min");
|
||||
if (maxTokenAmount[_srcOutputToken] > 0) {
|
||||
require(
|
||||
_srcAmtOut <= maxTokenAmount[_srcOutputToken],
|
||||
"greater than max"
|
||||
);
|
||||
}
|
||||
|
||||
id = _crossChainTransferWithSwap(
|
||||
_receiver,
|
||||
_dstChainId,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_nonce,
|
||||
_fee,
|
||||
_srcOutputToken,
|
||||
_srcAmtOut
|
||||
);
|
||||
}
|
||||
|
||||
function _crossChainTransferWithSwap(
|
||||
address _receiver,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage,
|
||||
uint64 _nonce,
|
||||
uint256 _fee,
|
||||
address srcOutputToken,
|
||||
uint256 srcAmtOut
|
||||
) private returns (bytes32 id) {
|
||||
// todo increment nonce in compute ...
|
||||
require(_dstSwap.path.length > 0, "empty dst swap path");
|
||||
bytes memory message = abi.encode(
|
||||
SwapRequestDest({
|
||||
swap: _dstSwap,
|
||||
nonce: nonce,
|
||||
dstChainId: _dstChainId
|
||||
})
|
||||
);
|
||||
id = _computeSwapRequestId(
|
||||
_dstSwap.receiverEOA,
|
||||
uint64(block.chainid),
|
||||
_dstChainId,
|
||||
message
|
||||
);
|
||||
|
||||
sendMessageWithTransfer(
|
||||
_receiver,
|
||||
srcOutputToken,
|
||||
srcAmtOut,
|
||||
_dstChainId,
|
||||
_nonce,
|
||||
_maxBridgeSlippage,
|
||||
message,
|
||||
_fee
|
||||
);
|
||||
}
|
||||
|
||||
// ============== Utilities ==============
|
||||
|
||||
function _beforeSwapAndSendMessage() internal returns (uint64) {
|
||||
return ++nonce;
|
||||
}
|
||||
|
||||
function _retrieveDstTokenAddress(SwapInfoDest memory _swapInfo)
|
||||
internal
|
||||
pure
|
||||
returns (address)
|
||||
{
|
||||
if (_swapInfo.version == SwapVersion.v3) {
|
||||
require(_swapInfo.pathV3.length > 20, "dst swap expected");
|
||||
|
||||
return address(_getLastBytes20(_swapInfo.pathV3));
|
||||
} else if (_swapInfo.version == SwapVersion.v2) {
|
||||
require(_swapInfo.path.length > 1, "dst swap expected");
|
||||
|
||||
return _swapInfo.path[_swapInfo.path.length - 1];
|
||||
} else {
|
||||
require(_swapInfo.path.length == 1, "dst bridge expected");
|
||||
return _swapInfo.path[0];
|
||||
}
|
||||
}
|
||||
|
||||
// returns address of first token for V3
|
||||
function _getFirstBytes20(bytes memory input)
|
||||
internal
|
||||
pure
|
||||
returns (bytes20 result)
|
||||
{
|
||||
assembly {
|
||||
result := mload(add(input, 32))
|
||||
}
|
||||
}
|
||||
|
||||
// returns address of tokenOut for V3
|
||||
function _getLastBytes20(bytes memory input)
|
||||
internal
|
||||
pure
|
||||
returns (bytes20 result)
|
||||
{
|
||||
uint256 offset = input.length + 12;
|
||||
assembly {
|
||||
result := mload(add(input, offset))
|
||||
}
|
||||
}
|
||||
|
||||
function _computeSwapRequestId(
|
||||
address _receiverEOA,
|
||||
uint64 _srcChainId,
|
||||
uint64 _dstChainId,
|
||||
bytes memory _message
|
||||
) internal pure returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
_receiverEOA,
|
||||
_srcChainId,
|
||||
_dstChainId,
|
||||
_message
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to check if the address in path is transit token received from Celer
|
||||
*/
|
||||
function checkIsTransit(address _transitToken, address _tokenInPath)
|
||||
internal
|
||||
pure
|
||||
{
|
||||
require(_transitToken == _tokenInPath, "first token must be transit");
|
||||
}
|
||||
}
|
146
Interchain-message/contracts/message/apps/TransferSwapInch.sol
Normal file
146
Interchain-message/contracts/message/apps/TransferSwapInch.sol
Normal file
|
@ -0,0 +1,146 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "./SwapBase.sol";
|
||||
|
||||
contract TransferSwapInch is SwapBase {
|
||||
using AddressUpgradeable for address payable;
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
|
||||
function transferWithSwapInchNative(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoInch calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecksNative(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.path[0]
|
||||
);
|
||||
|
||||
_swapAndSendMessageInch(
|
||||
_receiver,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_srcSwap,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
_srcSwap.path[_srcSwap.path.length - 1]
|
||||
);
|
||||
}
|
||||
|
||||
function transferWithSwapInch(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoInch calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecks(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.path[0]
|
||||
);
|
||||
|
||||
_swapAndSendMessageInch(
|
||||
_receiver,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_srcSwap,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
_srcSwap.path[_srcSwap.path.length - 1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sends a cross-chain transfer via the liquidity pool-based bridge and sends a message specifying a wanted swap action on the
|
||||
destination chain via the message bus
|
||||
* @param _receiver the app contract that implements the MessageReceiver abstract contract
|
||||
* NOTE not to be confused with the receiver field in SwapInfoInch which is an EOA address of a user
|
||||
* @param _amountIn the input amount that the user wants to swap and/or bridge
|
||||
* @param _dstChainId destination chain ID
|
||||
* @param _srcSwap a struct containing swap related requirements
|
||||
* @param _dstSwap a struct containing swap related requirements
|
||||
* @param _maxBridgeSlippage the max acceptable slippage at bridge, given as percentage in point (pip). Eg. 5000 means 0.5%.
|
||||
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the
|
||||
* transfer can be refunded.
|
||||
* @param _fee the fee to pay to MessageBus.
|
||||
*/
|
||||
function _swapAndSendMessageInch(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoInch calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage,
|
||||
uint256 _fee,
|
||||
address srcOutputToken
|
||||
) private {
|
||||
BaseCrossChainParams memory _baseParams = BaseCrossChainParams(
|
||||
_srcSwap.path[0],
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_retrieveDstTokenAddress(_dstSwap),
|
||||
_dstSwap.amountOutMinimum,
|
||||
msg.sender,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.dex
|
||||
);
|
||||
|
||||
require(_srcSwap.path.length > 1, "empty swap path");
|
||||
|
||||
(bool success, uint256 srcAmtOut) = _trySwapInch(_srcSwap, _amountIn);
|
||||
|
||||
bytes32 id = _sendMessage(
|
||||
_receiver,
|
||||
uint64(_baseParams.dstChainID),
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_beforeSwapAndSendMessage(),
|
||||
_fee,
|
||||
srcOutputToken,
|
||||
srcAmtOut,
|
||||
success
|
||||
);
|
||||
|
||||
emit CrossChainRequestSent(id, _baseParams);
|
||||
}
|
||||
|
||||
function _trySwapInch(SwapInfoInch memory _swap, uint256 _amount)
|
||||
internal
|
||||
returns (bool ok, uint256 amountOut)
|
||||
{
|
||||
if (!availableRouters.contains(_swap.dex)) {
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
SmartApprove.smartApprove(_swap.path[0], _amount, _swap.dex);
|
||||
|
||||
IERC20Upgradeable Transit = IERC20Upgradeable(
|
||||
_swap.path[_swap.path.length - 1]
|
||||
);
|
||||
uint256 transitBalanceBefore = Transit.balanceOf(address(this));
|
||||
|
||||
AddressUpgradeable.functionCall(_swap.dex, _swap.data);
|
||||
|
||||
uint256 balanceDif = Transit.balanceOf(address(this)) -
|
||||
transitBalanceBefore;
|
||||
|
||||
if (balanceDif >= _swap.amountOutMinimum) {
|
||||
return (true, balanceDif);
|
||||
}
|
||||
|
||||
return (false, 0);
|
||||
}
|
||||
}
|
144
Interchain-message/contracts/message/apps/TransferSwapV2.sol
Normal file
144
Interchain-message/contracts/message/apps/TransferSwapV2.sol
Normal file
|
@ -0,0 +1,144 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "./SwapBase.sol";
|
||||
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
|
||||
|
||||
contract TransferSwapV2 is SwapBase {
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
|
||||
function transferWithSwapV2Native(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoV2 calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecksNative(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.path[0]
|
||||
);
|
||||
|
||||
_swapAndSendMessageV2(
|
||||
_receiver,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_srcSwap,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
_srcSwap.path[_srcSwap.path.length - 1]
|
||||
);
|
||||
}
|
||||
|
||||
function transferWithSwapV2(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoV2 calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecks(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.path[0]
|
||||
);
|
||||
|
||||
_swapAndSendMessageV2(
|
||||
_receiver,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_srcSwap,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
_srcSwap.path[_srcSwap.path.length - 1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sends a cross-chain transfer via the liquidity pool-based bridge and sends a message specifying a wanted swap action on the
|
||||
destination chain via the message bus
|
||||
* @param _receiver the app contract that implements the MessageReceiver abstract contract
|
||||
* NOTE not to be confused with the receiver field in SwapInfoV2 which is an EOA address of a user
|
||||
* @param _amountIn the input amount that the user wants to swap and/or bridge
|
||||
* @param _dstChainId destination chain ID
|
||||
* @param _srcSwap a struct containing swap related requirements
|
||||
* @param _dstSwap a struct containing swap related requirements
|
||||
* @param _maxBridgeSlippage the max acceptable slippage at bridge, given as percentage in point (pip). Eg. 5000 means 0.5%.
|
||||
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the
|
||||
* transfer can be refunded.
|
||||
* @param _fee the fee to pay to MessageBus.
|
||||
*/
|
||||
function _swapAndSendMessageV2(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoV2 calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage,
|
||||
uint256 _fee,
|
||||
address _outputToken
|
||||
) private {
|
||||
BaseCrossChainParams memory _baseParams = BaseCrossChainParams(
|
||||
_srcSwap.path[0],
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_retrieveDstTokenAddress(_dstSwap),
|
||||
_dstSwap.amountOutMinimum,
|
||||
msg.sender,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.dex
|
||||
);
|
||||
|
||||
require(_srcSwap.path.length > 1, "empty swap path");
|
||||
|
||||
(bool success, uint256 srcAmtOut) = _trySwapV2(_srcSwap, _amountIn);
|
||||
|
||||
bytes32 id = _sendMessage(
|
||||
_receiver,
|
||||
uint64(_baseParams.dstChainID),
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_beforeSwapAndSendMessage(),
|
||||
_fee,
|
||||
_outputToken,
|
||||
srcAmtOut,
|
||||
success
|
||||
);
|
||||
|
||||
emit CrossChainRequestSent(id, _baseParams);
|
||||
}
|
||||
|
||||
function _trySwapV2(SwapInfoV2 memory _swap, uint256 _amount)
|
||||
internal
|
||||
returns (bool ok, uint256 amountOut)
|
||||
{
|
||||
if (!availableRouters.contains(_swap.dex)) {
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
SmartApprove.smartApprove(_swap.path[0], _amount, _swap.dex);
|
||||
|
||||
try
|
||||
IUniswapV2Router02(_swap.dex).swapExactTokensForTokens(
|
||||
_amount,
|
||||
_swap.amountOutMinimum,
|
||||
_swap.path,
|
||||
address(this),
|
||||
_swap.deadline
|
||||
)
|
||||
returns (uint256[] memory amounts) {
|
||||
return (true, amounts[amounts.length - 1]);
|
||||
} catch {
|
||||
return (false, 0);
|
||||
}
|
||||
}
|
||||
}
|
151
Interchain-message/contracts/message/apps/TransferSwapV3.sol
Normal file
151
Interchain-message/contracts/message/apps/TransferSwapV3.sol
Normal file
|
@ -0,0 +1,151 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "./SwapBase.sol";
|
||||
import "../../interfaces/IUniswapRouterV3.sol";
|
||||
|
||||
contract TransferSwapV3 is SwapBase {
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
|
||||
function transferWithSwapV3Native(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoV3 calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecksNative(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
address(_getFirstBytes20(_srcSwap.path))
|
||||
);
|
||||
|
||||
_swapAndSendMessageV3(
|
||||
_receiver,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_srcSwap,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
address(_getLastBytes20(_srcSwap.path))
|
||||
);
|
||||
}
|
||||
|
||||
function transferWithSwapV3(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoV3 calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage
|
||||
) external payable {
|
||||
uint256 _fee = _deriveFeeAndPerformChecks(
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_dstSwap.integrator,
|
||||
address(_getFirstBytes20(_srcSwap.path))
|
||||
);
|
||||
|
||||
_swapAndSendMessageV3(
|
||||
_receiver,
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_srcSwap,
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_fee,
|
||||
address(_getLastBytes20(_srcSwap.path))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sends a cross-chain transfer via the liquidity pool-based bridge and sends a message specifying a wanted swap action on the
|
||||
destination chain via the message bus
|
||||
* @param _receiver the app contract that implements the MessageReceiver abstract contract
|
||||
* NOTE not to be confused with the receiver field in SwapInfoV3 which is an EOA address of a user
|
||||
* @param _amountIn the input amount that the user wants to swap and/or bridge
|
||||
* @param _dstChainId destination chain ID
|
||||
* @param _srcSwap a struct containing swap related requirements
|
||||
* @param _dstSwap a struct containing swap related requirements
|
||||
* @param _maxBridgeSlippage the max acceptable slippage at bridge, given as percentage in point (pip). Eg. 5000 means 0.5%.
|
||||
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the
|
||||
* transfer can be refunded.
|
||||
* @param _fee the fee to pay to MessageBus.
|
||||
*/
|
||||
function _swapAndSendMessageV3(
|
||||
address _receiver,
|
||||
uint256 _amountIn,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoV3 calldata _srcSwap,
|
||||
SwapInfoDest calldata _dstSwap,
|
||||
uint32 _maxBridgeSlippage,
|
||||
uint256 _fee,
|
||||
address srcOutputToken
|
||||
) private {
|
||||
BaseCrossChainParams memory _baseParams = BaseCrossChainParams(
|
||||
address(_getFirstBytes20(_srcSwap.path)),
|
||||
_amountIn,
|
||||
_dstChainId,
|
||||
_retrieveDstTokenAddress(_dstSwap),
|
||||
_dstSwap.amountOutMinimum,
|
||||
msg.sender,
|
||||
_dstSwap.integrator,
|
||||
_srcSwap.dex
|
||||
);
|
||||
|
||||
require(_srcSwap.path.length > 20, "empty swap path");
|
||||
|
||||
(bool success, uint256 srcAmtOut) = _trySwapV3(_srcSwap, _amountIn);
|
||||
|
||||
bytes32 id = _sendMessage(
|
||||
_receiver,
|
||||
uint64(_baseParams.dstChainID),
|
||||
_dstSwap,
|
||||
_maxBridgeSlippage,
|
||||
_beforeSwapAndSendMessage(), // TODO rename
|
||||
_fee,
|
||||
srcOutputToken,
|
||||
srcAmtOut,
|
||||
success
|
||||
);
|
||||
|
||||
emit CrossChainRequestSent(id, _baseParams);
|
||||
}
|
||||
|
||||
function _trySwapV3(SwapInfoV3 memory _swap, uint256 _amount)
|
||||
internal
|
||||
returns (bool ok, uint256 amountOut)
|
||||
{
|
||||
if (!availableRouters.contains(_swap.dex)) {
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
SmartApprove.smartApprove(
|
||||
address(_getFirstBytes20(_swap.path)),
|
||||
_amount,
|
||||
_swap.dex
|
||||
);
|
||||
|
||||
IUniswapRouterV3.ExactInputParams memory paramsV3 = IUniswapRouterV3
|
||||
.ExactInputParams(
|
||||
_swap.path,
|
||||
address(this),
|
||||
_swap.deadline,
|
||||
_amount,
|
||||
_swap.amountOutMinimum
|
||||
);
|
||||
|
||||
try IUniswapRouterV3(_swap.dex).exactInput(paramsV3) returns (
|
||||
uint256 _amountOut
|
||||
) {
|
||||
return (true, _amountOut);
|
||||
} catch {
|
||||
return (false, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "../../interfaces/IMessageReceiverApp.sol";
|
||||
|
||||
abstract contract MessageReceiverApp is IMessageReceiverApp {
|
||||
address public messageBus;
|
||||
event MessageBusUpdated(address messageBus);
|
||||
|
||||
modifier onlyMessageBus() {
|
||||
checkIsMessageBus();
|
||||
_;
|
||||
}
|
||||
|
||||
function checkIsMessageBus() internal view {
|
||||
require(msg.sender == messageBus, "caller is not message bus");
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Called by MessageBus (MessageBusReceiver) if the process is originated from MessageBus (MessageBusSender)'s
|
||||
* sendMessageWithTransfer it is only called when the tokens are checked to be arrived at this contract's address.
|
||||
* @param _sender The address of the source app contract
|
||||
* @param _token The address of the token that comes out of the bridge
|
||||
* @param _amount The amount of tokens received at this contract through the cross-chain bridge.
|
||||
* the contract that implements this contract can safely assume that the tokens will arrive before this
|
||||
* function is called.
|
||||
* @param _srcChainId The source chain ID where the transfer is originated from
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessageWithTransfer(
|
||||
address _sender,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
)
|
||||
external
|
||||
payable
|
||||
virtual
|
||||
override
|
||||
onlyMessageBus
|
||||
returns (ExecutionStatus)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @notice Only called by MessageBus (MessageBusReceiver) if
|
||||
* 1. executeMessageWithTransfer reverts, or
|
||||
* 2. executeMessageWithTransfer returns ExecutionStatus.Fail
|
||||
* @param _sender The address of the source app contract
|
||||
* @param _token The address of the token that comes out of the bridge
|
||||
* @param _amount The amount of tokens received at this contract through the cross-chain bridge.
|
||||
* the contract that implements this contract can safely assume that the tokens will arrive before this
|
||||
* function is called.
|
||||
* @param _srcChainId The source chain ID where the transfer is originated from
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessageWithTransferFallback(
|
||||
address _sender,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
)
|
||||
external
|
||||
payable
|
||||
virtual
|
||||
override
|
||||
onlyMessageBus
|
||||
returns (ExecutionStatus)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @notice Called by MessageBus (MessageBusReceiver) to process refund of the original transfer from this contract
|
||||
* @param _token The token address of the original transfer
|
||||
* @param _amount The amount of the original transfer
|
||||
* @param _message The same message associated with the original transfer
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessageWithTransferRefund(
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
)
|
||||
external
|
||||
payable
|
||||
virtual
|
||||
override
|
||||
onlyMessageBus
|
||||
returns (ExecutionStatus)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @notice Called by MessageBus (MessageBusReceiver)
|
||||
* @param _sender The address of the source app contract
|
||||
* @param _srcChainId The source chain ID where the transfer is originated from
|
||||
* @param _message Arbitrary message bytes originated from and encoded by the source app contract
|
||||
* @param _executor Address who called the MessageBus execution function
|
||||
*/
|
||||
function executeMessage(
|
||||
address _sender,
|
||||
uint64 _srcChainId,
|
||||
bytes calldata _message,
|
||||
address _executor
|
||||
)
|
||||
external
|
||||
payable
|
||||
virtual
|
||||
override
|
||||
onlyMessageBus
|
||||
returns (ExecutionStatus)
|
||||
{}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "../libraries/MsgDataTypes.sol";
|
||||
import "../libraries/MessageSenderLib.sol";
|
||||
|
||||
import "./MessageReceiverApp.sol";
|
||||
|
||||
abstract contract MessageSenderApp is MessageReceiverApp {
|
||||
// ============== Utility functions called by apps ==============
|
||||
|
||||
/**
|
||||
* @notice Sends a message associated with a transfer to a contract on another chain.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _token The address of the token to be sent.
|
||||
* @param _amount The amount of tokens to be sent.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
|
||||
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
|
||||
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least
|
||||
* (100% - max slippage percentage) * amount or the transfer can be refunded.
|
||||
* Only applicable to the {MsgDataTypes.BridgeSendType.Liquidity}.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
* If message is empty, only the token transfer will be sent
|
||||
* param _bridgeSendType One of the {BridgeSendType} enum.
|
||||
* @param _fee The fee amount to pay to MessageBus.
|
||||
* @return The transfer ID.
|
||||
*/
|
||||
function sendMessageWithTransfer(
|
||||
address _receiver,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _dstChainId,
|
||||
uint64 _nonce,
|
||||
uint32 _maxSlippage,
|
||||
bytes memory _message,
|
||||
uint256 _fee
|
||||
) internal returns (bytes32) {
|
||||
return
|
||||
MessageSenderLib.sendMessageWithTransfer(
|
||||
_receiver,
|
||||
_token,
|
||||
_amount,
|
||||
_dstChainId,
|
||||
_nonce,
|
||||
_maxSlippage,
|
||||
_message,
|
||||
messageBus,
|
||||
_fee
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "rubic-bridge-base/contracts/libraries/SmartApprove.sol";
|
||||
|
||||
import "../../interfaces/IBridge.sol";
|
||||
import "../../interfaces/IMessageBus.sol";
|
||||
|
||||
import "./MsgDataTypes.sol";
|
||||
|
||||
library MessageSenderLib {
|
||||
// ============== Internal library functions called by apps ==============
|
||||
|
||||
/**
|
||||
* @notice Sends a message associated with a transfer to a contract on another chain.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _token The address of the token to be sent.
|
||||
* @param _amount The amount of tokens to be sent.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
|
||||
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
|
||||
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least
|
||||
* (100% - max slippage percentage) * amount or the transfer can be refunded.
|
||||
* Only applicable to the {MsgDataTypes.BridgeSendType.Liquidity}.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
* If message is empty, only the token transfer will be sent
|
||||
* param _bridgeSendType One of the {MsgDataTypes.BridgeSendType} enum.
|
||||
* @param _messageBus The address of the MessageBus on this chain.
|
||||
* @param _fee The fee amount to pay to MessageBus.
|
||||
* @return The transfer ID.
|
||||
*/
|
||||
function sendMessageWithTransfer(
|
||||
address _receiver,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _dstChainId,
|
||||
uint64 _nonce,
|
||||
uint32 _maxSlippage,
|
||||
bytes memory _message,
|
||||
address _messageBus,
|
||||
uint256 _fee
|
||||
) internal returns (bytes32) {
|
||||
return
|
||||
sendMessageWithLiquidityBridgeTransfer(
|
||||
_receiver,
|
||||
_token,
|
||||
_amount,
|
||||
_dstChainId,
|
||||
_nonce,
|
||||
_maxSlippage,
|
||||
_message,
|
||||
_messageBus,
|
||||
_fee
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sends a message to an app on another chain via MessageBus with an associated liquidity bridge transfer.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _token The address of the token to be sent.
|
||||
* @param _amount The amount of tokens to be sent.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
|
||||
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
|
||||
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least
|
||||
* (100% - max slippage percentage) * amount or the transfer can be refunded.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
* If message is empty, only the token transfer will be sent
|
||||
* @param _messageBus The address of the MessageBus on this chain.
|
||||
* @param _fee The fee amount to pay to MessageBus.
|
||||
* @return The transfer ID.
|
||||
*/
|
||||
function sendMessageWithLiquidityBridgeTransfer(
|
||||
address _receiver,
|
||||
address _token,
|
||||
uint256 _amount,
|
||||
uint64 _dstChainId,
|
||||
uint64 _nonce,
|
||||
uint32 _maxSlippage,
|
||||
bytes memory _message,
|
||||
address _messageBus,
|
||||
uint256 _fee
|
||||
) internal returns (bytes32) {
|
||||
address bridge = IMessageBus(_messageBus).liquidityBridge();
|
||||
SmartApprove.smartApprove(_token, _amount, bridge);
|
||||
IBridge(bridge).send(
|
||||
_receiver,
|
||||
_token,
|
||||
_amount,
|
||||
_dstChainId,
|
||||
_nonce,
|
||||
_maxSlippage
|
||||
);
|
||||
bytes32 transferId = keccak256(
|
||||
abi.encodePacked(
|
||||
address(this),
|
||||
_receiver,
|
||||
_token,
|
||||
_amount,
|
||||
_dstChainId,
|
||||
_nonce,
|
||||
uint64(block.chainid)
|
||||
)
|
||||
);
|
||||
if (_message.length > 0) {
|
||||
IMessageBus(_messageBus).sendMessageWithTransfer{value: _fee}(
|
||||
_receiver,
|
||||
_dstChainId,
|
||||
bridge,
|
||||
transferId,
|
||||
_message
|
||||
);
|
||||
}
|
||||
return transferId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
library MsgDataTypes {
|
||||
// bridge operation type at the sender side (src chain)
|
||||
enum BridgeSendType {
|
||||
Null,
|
||||
Liquidity,
|
||||
PegDeposit,
|
||||
PegBurn,
|
||||
PegV2Deposit,
|
||||
PegV2Burn,
|
||||
PegV2BurnFrom
|
||||
}
|
||||
|
||||
// bridge operation type at the receiver side (dst chain)
|
||||
enum TransferType {
|
||||
Null,
|
||||
LqRelay, // relay through liquidity bridge
|
||||
LqWithdraw, // withdraw from liquidity bridge
|
||||
PegMint, // mint through pegged token bridge
|
||||
PegWithdraw, // withdraw from original token vault
|
||||
PegV2Mint, // mint through pegged token bridge v2
|
||||
PegV2Withdraw // withdraw from original token vault v2
|
||||
}
|
||||
|
||||
enum MsgType {
|
||||
MessageWithTransfer,
|
||||
MessageOnly
|
||||
}
|
||||
|
||||
enum TxStatus {
|
||||
Null,
|
||||
Success,
|
||||
Fail,
|
||||
Fallback,
|
||||
Pending // transient state within a transaction
|
||||
}
|
||||
|
||||
struct TransferInfo {
|
||||
TransferType t;
|
||||
address sender;
|
||||
address receiver;
|
||||
address token;
|
||||
uint256 amount;
|
||||
uint64 wdseq; // only needed for LqWithdraw (refund)
|
||||
uint64 srcChainId;
|
||||
bytes32 refId;
|
||||
bytes32 srcTxHash; // src chain msg tx hash
|
||||
}
|
||||
|
||||
struct RouteInfo {
|
||||
address sender;
|
||||
address receiver;
|
||||
uint64 srcChainId;
|
||||
bytes32 srcTxHash; // src chain msg tx hash
|
||||
}
|
||||
|
||||
struct MsgWithTransferExecutionParams {
|
||||
bytes message;
|
||||
TransferInfo transfer;
|
||||
bytes[] sigs;
|
||||
address[] signers;
|
||||
uint256[] powers;
|
||||
}
|
||||
|
||||
struct BridgeTransferParams {
|
||||
bytes request;
|
||||
bytes[] sigs;
|
||||
address[] signers;
|
||||
uint256[] powers;
|
||||
}
|
||||
}
|
162
Interchain-message/contracts/test/MessageBusSender.sol
Normal file
162
Interchain-message/contracts/test/MessageBusSender.sol
Normal file
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
interface ISigsVerifier {
|
||||
/**
|
||||
* @notice Verifies that a message is signed by a quorum among the signers.
|
||||
* @param _msg signed message
|
||||
* @param _sigs list of signatures sorted by signer addresses in ascending order
|
||||
* @param _signers sorted list of current signers
|
||||
* @param _powers powers of current signers
|
||||
*/
|
||||
function verifySigs(
|
||||
bytes memory _msg,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external view;
|
||||
}
|
||||
|
||||
contract MessageBusSender {
|
||||
ISigsVerifier public immutable sigsVerifier;
|
||||
|
||||
uint256 public feeBase;
|
||||
uint256 public feePerByte;
|
||||
mapping(address => uint256) public withdrawnFees;
|
||||
|
||||
event Message(
|
||||
address indexed sender,
|
||||
address receiver,
|
||||
uint256 dstChainId,
|
||||
bytes message,
|
||||
uint256 fee
|
||||
);
|
||||
|
||||
event MessageWithTransfer(
|
||||
address indexed sender,
|
||||
address receiver,
|
||||
uint256 dstChainId,
|
||||
address bridge,
|
||||
bytes32 srcTransferId,
|
||||
bytes message,
|
||||
uint256 fee
|
||||
);
|
||||
|
||||
event FeeBaseUpdated(uint256 feeBase);
|
||||
event FeePerByteUpdated(uint256 feePerByte);
|
||||
|
||||
constructor(ISigsVerifier _sigsVerifier) {
|
||||
sigsVerifier = _sigsVerifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sends a message to a contract on another chain.
|
||||
* Sender needs to make sure the uniqueness of the message Id, which is computed as
|
||||
* hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message).
|
||||
* If messages with the same Id are sent, only one of them will succeed at dst chain.
|
||||
* A fee is charged in the native gas token.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
*/
|
||||
function sendMessage(
|
||||
address _receiver,
|
||||
uint256 _dstChainId,
|
||||
bytes calldata _message
|
||||
) external payable {
|
||||
require(_dstChainId != block.chainid, "Invalid chainId");
|
||||
uint256 minFee = calcFee(_message);
|
||||
require(msg.value >= minFee, "Insufficient fee");
|
||||
emit Message(msg.sender, _receiver, _dstChainId, _message, msg.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sends a message associated with a transfer to a contract on another chain.
|
||||
* If messages with the same srcTransferId are sent, only one of them will succeed.
|
||||
* A fee is charged in the native token.
|
||||
* @param _receiver The address of the destination app contract.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _srcBridge The bridge contract to send the transfer with.
|
||||
* @param _srcTransferId The transfer ID.
|
||||
* @param _dstChainId The destination chain ID.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
*/
|
||||
function sendMessageWithTransfer(
|
||||
address _receiver,
|
||||
uint256 _dstChainId,
|
||||
address _srcBridge,
|
||||
bytes32 _srcTransferId,
|
||||
bytes calldata _message
|
||||
) external payable {
|
||||
require(_dstChainId != block.chainid, "Invalid chainId");
|
||||
uint256 minFee = calcFee(_message);
|
||||
require(msg.value >= minFee, "Insufficient fee");
|
||||
// SGN needs to verify
|
||||
// 1. msg.sender matches sender of the src transfer
|
||||
// 2. dstChainId matches dstChainId of the src transfer
|
||||
// 3. bridge is either liquidity bridge, peg src vault, or peg dst bridge
|
||||
emit MessageWithTransfer(
|
||||
msg.sender,
|
||||
_receiver,
|
||||
_dstChainId,
|
||||
_srcBridge,
|
||||
_srcTransferId,
|
||||
_message,
|
||||
msg.value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Withdraws message fee in the form of native gas token.
|
||||
* @param _account The address receiving the fee.
|
||||
* @param _cumulativeFee The cumulative fee credited to the account. Tracked by SGN.
|
||||
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be
|
||||
* signed-off by +2/3 of the sigsVerifier's current signing power to be delivered.
|
||||
* @param _signers The sorted list of signers.
|
||||
* @param _powers The signing powers of the signers.
|
||||
*/
|
||||
function withdrawFee(
|
||||
address _account,
|
||||
uint256 _cumulativeFee,
|
||||
bytes[] calldata _sigs,
|
||||
address[] calldata _signers,
|
||||
uint256[] calldata _powers
|
||||
) external {
|
||||
bytes32 domain = keccak256(
|
||||
abi.encodePacked(block.chainid, address(this), "withdrawFee")
|
||||
);
|
||||
sigsVerifier.verifySigs(
|
||||
abi.encodePacked(domain, _account, _cumulativeFee),
|
||||
_sigs,
|
||||
_signers,
|
||||
_powers
|
||||
);
|
||||
uint256 amount = _cumulativeFee - withdrawnFees[_account];
|
||||
require(amount > 0, "No new amount to withdraw");
|
||||
withdrawnFees[_account] = _cumulativeFee;
|
||||
(bool sent, ) = _account.call{value: amount, gas: 50000}("");
|
||||
require(sent, "failed to withdraw fee");
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the required fee for the message.
|
||||
* @param _message Arbitrary message bytes to be decoded by the destination app contract.
|
||||
@ @return The required fee.
|
||||
*/
|
||||
function calcFee(bytes calldata _message) public view returns (uint256) {
|
||||
return feeBase + _message.length * feePerByte;
|
||||
}
|
||||
|
||||
// -------------------- Admin --------------------
|
||||
|
||||
function setFeePerByte(uint256 _fee) external {
|
||||
feePerByte = _fee;
|
||||
emit FeePerByteUpdated(feePerByte);
|
||||
}
|
||||
|
||||
function setFeeBase(uint256 _fee) external {
|
||||
feeBase = _fee;
|
||||
emit FeeBaseUpdated(feeBase);
|
||||
}
|
||||
}
|
97
Interchain-message/contracts/test/TestERC20.sol
Normal file
97
Interchain-message/contracts/test/TestERC20.sol
Normal file
|
@ -0,0 +1,97 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
contract TestERC20 {
|
||||
mapping(address => uint256) public balanceOf;
|
||||
mapping(address => mapping(address => uint256)) public allowance;
|
||||
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
|
||||
event Approval(
|
||||
address indexed owner,
|
||||
address indexed spender,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
constructor() {
|
||||
mint(msg.sender, 100000000000 ether);
|
||||
}
|
||||
|
||||
function decimals() external pure returns (uint256) {
|
||||
return 18;
|
||||
}
|
||||
|
||||
function mint(address to, uint256 amount) public {
|
||||
uint256 balanceNext = balanceOf[to] + amount;
|
||||
require(balanceNext >= amount, "overflow balance");
|
||||
balanceOf[to] = balanceNext;
|
||||
}
|
||||
|
||||
function transfer(address recipient, uint256 amount)
|
||||
external
|
||||
returns (bool)
|
||||
{
|
||||
uint256 balanceBefore = balanceOf[msg.sender];
|
||||
require(balanceBefore >= amount, "insufficient balance");
|
||||
balanceOf[msg.sender] = balanceBefore - amount;
|
||||
|
||||
uint256 balanceRecipient = balanceOf[recipient];
|
||||
require(
|
||||
balanceRecipient + amount >= balanceRecipient,
|
||||
"recipient balance overflow"
|
||||
);
|
||||
if (!isDeflationary) {
|
||||
balanceOf[recipient] = balanceRecipient + amount;
|
||||
} else {
|
||||
balanceOf[recipient] =
|
||||
balanceRecipient +
|
||||
(amount - (amount * 5) / 100);
|
||||
}
|
||||
|
||||
emit Transfer(msg.sender, recipient, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function approve(address spender, uint256 amount) external returns (bool) {
|
||||
allowance[msg.sender][spender] = amount;
|
||||
emit Approval(msg.sender, spender, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isDeflationary = false;
|
||||
|
||||
function setDefl() external {
|
||||
isDeflationary = true;
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 amount
|
||||
) external returns (bool) {
|
||||
uint256 allowanceBefore = allowance[sender][msg.sender];
|
||||
require(allowanceBefore >= amount, "allowance insufficient");
|
||||
|
||||
allowance[sender][msg.sender] = allowanceBefore - amount;
|
||||
|
||||
uint256 balanceRecipient = balanceOf[recipient];
|
||||
require(
|
||||
balanceRecipient + amount >= balanceRecipient,
|
||||
"overflow balance recipient"
|
||||
);
|
||||
if (!isDeflationary) {
|
||||
balanceOf[recipient] = balanceRecipient + amount;
|
||||
} else {
|
||||
balanceOf[recipient] =
|
||||
balanceRecipient +
|
||||
(amount - (amount * 5) / 100);
|
||||
}
|
||||
uint256 balanceSender = balanceOf[sender];
|
||||
require(balanceSender >= amount, "underflow balance sender");
|
||||
balanceOf[sender] = balanceSender - amount;
|
||||
|
||||
emit Transfer(sender, recipient, amount);
|
||||
return true;
|
||||
}
|
||||
}
|
47
Interchain-message/contracts/test/TestMessages.sol
Normal file
47
Interchain-message/contracts/test/TestMessages.sol
Normal file
|
@ -0,0 +1,47 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
import "../message/apps/SwapBase.sol";
|
||||
|
||||
contract TestMessages is SwapBase {
|
||||
constructor() {}
|
||||
|
||||
function getMessage(
|
||||
SwapInfoDest memory _dstSwap,
|
||||
uint64 _nonce,
|
||||
uint64 _dstChainId
|
||||
) public pure returns (bytes memory) {
|
||||
bytes memory message = abi.encode(
|
||||
SwapRequestDest({
|
||||
swap: _dstSwap,
|
||||
nonce: _nonce,
|
||||
dstChainId: _dstChainId
|
||||
})
|
||||
);
|
||||
return message;
|
||||
}
|
||||
|
||||
function getID(
|
||||
uint64 _chainId,
|
||||
uint64 _dstChainId,
|
||||
SwapInfoDest memory _dstSwap,
|
||||
uint64 _nonce
|
||||
) public pure returns (bytes32) {
|
||||
bytes memory message = abi.encode(
|
||||
SwapRequestDest({
|
||||
swap: _dstSwap,
|
||||
nonce: _nonce,
|
||||
dstChainId: _dstChainId
|
||||
})
|
||||
);
|
||||
bytes32 id = SwapBase._computeSwapRequestId(
|
||||
_dstSwap.receiverEOA,
|
||||
_chainId,
|
||||
_dstChainId,
|
||||
message
|
||||
);
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
64
Interchain-message/contracts/test/WETH9.sol
Normal file
64
Interchain-message/contracts/test/WETH9.sol
Normal file
|
@ -0,0 +1,64 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.8.9;
|
||||
|
||||
contract WETH9 {
|
||||
string public name = "Wrapped Ether";
|
||||
string public symbol = "WETH";
|
||||
uint8 public decimals = 18;
|
||||
|
||||
event Approval(address indexed src, address indexed guy, uint256 wad);
|
||||
event Transfer(address indexed src, address indexed dst, uint256 wad);
|
||||
event Deposit(address indexed dst, uint256 wad);
|
||||
event Withdrawal(address indexed src, uint256 wad);
|
||||
|
||||
mapping(address => uint256) public balanceOf;
|
||||
mapping(address => mapping(address => uint256)) public allowance;
|
||||
|
||||
function deposit() public payable {
|
||||
balanceOf[msg.sender] += msg.value;
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
function withdraw(uint256 wad) public {
|
||||
require(balanceOf[msg.sender] >= wad);
|
||||
balanceOf[msg.sender] -= wad;
|
||||
payable(msg.sender).transfer(wad);
|
||||
emit Withdrawal(msg.sender, wad);
|
||||
}
|
||||
|
||||
function mint(uint256 amount) public {
|
||||
uint256 balanceNext = balanceOf[msg.sender] + amount;
|
||||
require(balanceNext >= amount, "overflow balance");
|
||||
balanceOf[msg.sender] = balanceNext;
|
||||
}
|
||||
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return address(this).balance;
|
||||
}
|
||||
|
||||
function approve(address guy, uint256 wad) public returns (bool) {
|
||||
allowance[msg.sender][guy] = wad;
|
||||
emit Approval(msg.sender, guy, wad);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transfer(address dst, uint256 wad) public returns (bool) {
|
||||
return transferFrom(msg.sender, dst, wad);
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address src,
|
||||
address dst,
|
||||
uint256 wad
|
||||
) public returns (bool) {
|
||||
require(balanceOf[src] >= wad);
|
||||
|
||||
balanceOf[src] -= wad;
|
||||
balanceOf[dst] += wad;
|
||||
|
||||
emit Transfer(src, dst, wad);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
22
Interchain-message/deployments/Readme.md
Normal file
22
Interchain-message/deployments/Readme.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
**Deployments**
|
||||
|
||||
Ethereum
|
||||
[0x53dC7535028e2fcaCa0d847AD108b9240C0801b1](https://etherscan.io/address/0x53dc7535028e2fcaca0d847ad108b9240c0801b1)
|
||||
|
||||
Fantom
|
||||
[0x6B362d1C67D7E4Ff1467dFA38dE642f9D125E8Ad](https://ftmscan.com/address/0x6B362d1C67D7E4Ff1467dFA38dE642f9D125E8Ad)
|
||||
|
||||
BNB
|
||||
[0x8523F3DC779bF2df1151906E933b9225A439Db85](https://bscscan.com/address/0x8523F3DC779bF2df1151906E933b9225A439Db85)
|
||||
|
||||
Polygon
|
||||
[0x0F43F9B778100951Ba18812E8D23E8245df17699](https://polygonscan.com/address/0x0F43F9B778100951Ba18812E8D23E8245df17699)
|
||||
|
||||
Avalanche
|
||||
[0xd82bf61411Ce2F6bd46Fd1e3b0459979809D4787](https://snowtrace.io/address/0xd82bf61411Ce2F6bd46Fd1e3b0459979809D4787)
|
||||
|
||||
Arbitrum
|
||||
[0x53dC7535028e2fcaCa0d847AD108b9240C0801b1](https://arbiscan.io/address/0x53dC7535028e2fcaCa0d847AD108b9240C0801b1)
|
||||
|
||||
Aurora
|
||||
[0x53dC7535028e2fcaCa0d847AD108b9240C0801b1](https://aurorascan.dev/address/0x53dC7535028e2fcaCa0d847AD108b9240C0801b1)
|
28
Interchain-message/executor/config/cbridge.toml
Normal file
28
Interchain-message/executor/config/cbridge.toml
Normal file
|
@ -0,0 +1,28 @@
|
|||
[[multichain]]
|
||||
chainID = 5
|
||||
name = "Goerli"
|
||||
gateway = "https://goerli.infura.io/v3/b44c0379c63b443cb6021964cec0481f" # fill in your Goerli rpc provider url
|
||||
# cBridge (liquidity bridge) contract address. Executor relies on events from this
|
||||
# contract to double check and make sure funds are transfered to the destination
|
||||
# before it attempts messages on the destination chain
|
||||
cbridge = "0x358234B325EF9eA8115291A8b81b7d33A2Fa762D"
|
||||
# MessageBus contract address. Executor relies this to keep a message execution
|
||||
# history (just so you can debug or help out angry customers).
|
||||
msgbus = "0x942E8e0e4b021F55b89660c886146e0Ec57F4b5B"
|
||||
blkinterval = 15 # polling interval
|
||||
blkdelay = 5 # how many blocks confirmations are required
|
||||
maxblkdelta = 5000 # max number of blocks per poll request
|
||||
|
||||
[[multichain]]
|
||||
chainID = 97
|
||||
name = "BSC Testnet"
|
||||
gateway = "https://data-seed-prebsc-2-s3.binance.org:8545/"
|
||||
cbridge = "0xf89354F314faF344Abd754924438bA798E306DF2"
|
||||
msgbus = "0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA"
|
||||
blkinterval = 3
|
||||
blkdelay = 8
|
||||
maxblkdelta = 5000
|
||||
# on some EVM chains the gas estimation can be off. the below fields
|
||||
# are added to make up for the inconsistancies.
|
||||
addgasgwei = 2 # add 2 gwei to gas price
|
||||
addgasestimateratio = 0.3 # multiply gas limit by this ratio
|
25
Interchain-message/executor/config/executor.toml
Normal file
25
Interchain-message/executor/config/executor.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[executor]
|
||||
# since we don't want the executor to execute messages that are not sent by our
|
||||
# SimpleBatchTransfer contract, the following items are added to filter only
|
||||
# the ones we care about
|
||||
[[executor.contracts]]
|
||||
chainid = 5 # Goerli
|
||||
address = "0x96C8a3cad47D0249cF20dD5C6B0Fa0c711a310f5"
|
||||
[[executor.contracts]]
|
||||
chainid = 97 # Bsc testnet
|
||||
address = "0xD3F24401591C9e205A938288280C65Da7e5E74f0"
|
||||
|
||||
[sgnd]
|
||||
# SGN testnet node0 grpc. executor reads available messages from this endpoint
|
||||
sgn_grpc = "35.165.81.166:9090"
|
||||
# SGN testnet gateway grpc. all tx operations to the SGN is delegated through
|
||||
# a gateway in this test phase
|
||||
gateway_grpc = "35.165.81.166:20000"
|
||||
|
||||
[eth]
|
||||
# Fully qualified absolute path only, "~" would not work
|
||||
signer_keystore = ""
|
||||
signer_passphrase = ""
|
||||
|
||||
[db]
|
||||
url = "localhost:26257"
|
0
Interchain-message/executor/eth-ks/signer.json
Normal file
0
Interchain-message/executor/eth-ks/signer.json
Normal file
155
Interchain-message/hardhat.config.ts
Normal file
155
Interchain-message/hardhat.config.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
import '@typechain/hardhat';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import '@nomiclabs/hardhat-waffle';
|
||||
import '@nomiclabs/hardhat-etherscan';
|
||||
import 'hardhat-contract-sizer';
|
||||
import 'hardhat-gas-reporter';
|
||||
import '@openzeppelin/hardhat-upgrades';
|
||||
|
||||
import { SolcUserConfig } from 'hardhat/types'
|
||||
|
||||
import * as dotenv from 'dotenv';
|
||||
import {float} from "hardhat/internal/core/params/argumentTypes";
|
||||
dotenv.config();
|
||||
const DEFAULT_PRIVATE_KEY = process.env.MNEMONIC || '1000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
|
||||
const DEFAULT_COMPILER_SETTINGS: SolcUserConfig = {
|
||||
version: '0.8.15',
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200,
|
||||
},
|
||||
metadata: {
|
||||
bytecodeHash: 'none',
|
||||
},
|
||||
evmVersion: "istanbul",
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 137,
|
||||
forking: {
|
||||
url: `https://polygon-rpc.com`,
|
||||
blockNumber: 27081600 // hardcode block number to increase performance of the local cache
|
||||
},
|
||||
allowUnlimitedContractSize: true,
|
||||
loggingEnabled: false,
|
||||
accounts:{
|
||||
count:100
|
||||
}
|
||||
},
|
||||
eth: {
|
||||
url: `https://mainnet.infura.io/v3/${process.env.INFURA_ID_PROJECT}`,
|
||||
chainId: 1,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`],
|
||||
gasPrice: 20000000000
|
||||
},
|
||||
ropsten: {
|
||||
url: `https://ropsten.infura.io/v3/${process.env.INFURA_ID_PROJECT}`,
|
||||
chainId: 3,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
rinkeby: {
|
||||
url: `https://rinkeby.infura.io/v3/${process.env.INFURA_ID_PROJECT}`,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${process.env.INFURA_ID_PROJECT}`,
|
||||
chainId: 5,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
kovan: {
|
||||
url: `https://kovan.infura.io/v3/${process.env.INFURA_ID_PROJECT}`,
|
||||
chainId: 42,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
bscTest: {
|
||||
url: `https://data-seed-prebsc-2-s3.binance.org:8545`,
|
||||
chainId: 97,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
bsc: {
|
||||
url: `https://bsc-dataseed.binance.org/`,
|
||||
chainId: 56,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
polygonMumbai: {
|
||||
url: `https://rpc-mumbai.maticvigil.com`,
|
||||
chainId: 80001,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
polygon: {
|
||||
url: `https://polygon-rpc.com`,
|
||||
chainId: 137,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
avalanche: {
|
||||
url: `https://api.avax.network/ext/bc/C/rpc`,
|
||||
chainId: 43114,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
fantom: {
|
||||
url: `https://rpc.ftm.tools/`,
|
||||
chainId: 250,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
arbitrum: {
|
||||
url: `https://arb1.arbitrum.io/rpc`,
|
||||
chainId: 42161,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`]
|
||||
},
|
||||
aurora: {
|
||||
url: `https://mainnet.aurora.dev`,
|
||||
chainId: 1313161554,
|
||||
accounts: [`0x${DEFAULT_PRIVATE_KEY}`],
|
||||
gasPrice: 80000000 //0.08 gwei
|
||||
},
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: {
|
||||
mainnet: process.env.ETHERSCAN_API_KEY,
|
||||
ropsten: process.env.ETHERSCAN_API_KEY,
|
||||
rinkeby: process.env.ETHERSCAN_API_KEY,
|
||||
goerli: process.env.ETHERSCAN_API_KEY,
|
||||
kovan: process.env.ETHERSCAN_API_KEY,
|
||||
// binance smart chain
|
||||
bsc: process.env.BSCSCAN_API_KEY,
|
||||
bscTestnet: process.env.BSCSCAN_API_KEY,
|
||||
// fantom mainnet
|
||||
opera: process.env.FANTOMSCAN_API_KEY,
|
||||
ftmTestnet: process.env.FANTOMSCAN_API_KEY,
|
||||
// polygon
|
||||
polygon: process.env.POLYGONSCAN_API_KEY,
|
||||
polygonMumbai: process.env.POLYGONSCAN_API_KEY,
|
||||
// avalanche
|
||||
avalanche: process.env.AVALANCHE_API_KEY,
|
||||
avalancheFujiTestnet: process.env.AVALANCHE_API_KEY,
|
||||
// arbitrum
|
||||
arbitrumOne: process.env.ARBITRUM_API_KEY,
|
||||
arbitrumTestnet: process.env.ARBITRUM_API_KEY,
|
||||
aurora: process.env.AURORA_API_KEY
|
||||
},
|
||||
//apiKey: `${process.env.AURORA_API_KEY}`,
|
||||
},
|
||||
solidity: {
|
||||
compilers: [DEFAULT_COMPILER_SETTINGS]
|
||||
},
|
||||
contractSizer: {
|
||||
alphaSort: false,
|
||||
disambiguatePaths: true,
|
||||
runOnCompile: false
|
||||
},
|
||||
typechain: {
|
||||
outDir: 'typechain',
|
||||
target: 'ethers-v5'
|
||||
},
|
||||
gasReporter: {
|
||||
enabled: process.env.REPORT_GAS === 'true' ? true : false,
|
||||
noColors: true,
|
||||
outputFile: 'reports/gas_usage/summary.txt'
|
||||
}
|
||||
}
|
43016
Interchain-message/package-lock.json
generated
Normal file
43016
Interchain-message/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
Interchain-message/package.json
Normal file
54
Interchain-message/package.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "sample-hardhat-project",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"lint": "./node_modules/.bin/solhint -f table contracts/**/*.sol && eslint test/** ",
|
||||
"format": "prettier --write contracts/**/*.sol && eslint test/** --fix",
|
||||
"test": "npx hardhat test"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.3",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"@openzeppelin/contracts": "^4.5.0",
|
||||
"@truffle/hdwallet-provider": "^1.5.1",
|
||||
"@typechain/ethers-v5": "^8.0.5",
|
||||
"@typechain/hardhat": "^3.0.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"ganache-core": "^2.13.2",
|
||||
"hardhat": "^2.7.0",
|
||||
"keythereum": "^1.2.0",
|
||||
"rubic-bridge-base": "^1.4.4",
|
||||
"truffle-plugin-verify": "^0.5.15",
|
||||
"web3": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-etherscan": "^3.0.3",
|
||||
"@openzeppelin/contracts-upgradeable": "^4.5.2",
|
||||
"@openzeppelin/hardhat-upgrades": "^1.17.0",
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v3-periphery": "^1.4.0",
|
||||
"chai": "^4.3.4",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-config-airbnb-typescript": "^14.0.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-unused-imports": "^1.1.5",
|
||||
"eth-gas-reporter": "^0.2.25",
|
||||
"hardhat-contract-sizer": "^2.5.1",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.18",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
BIN
Interchain-message/reports/contract_sizes.txt
Normal file
BIN
Interchain-message/reports/contract_sizes.txt
Normal file
Binary file not shown.
43
Interchain-message/reports/gas_usage/summary.txt
Normal file
43
Interchain-message/reports/gas_usage/summary.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
·--------------------------------------------------------|---------------------------|-------------|-----------------------------·
|
||||
| Solc version: 0.8.15 · Optimizer enabled: true · Runs: 200 · Block limit: 30000000 gas │
|
||||
·························································|···························|·············|······························
|
||||
| Methods │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| Contract · Method · Min · Max · Avg · # calls · eur (avg) │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · bridgeWithSwap · - · - · 262215 · 2 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · bridgeWithSwapNative · - · - · 250042 · 2 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · executeMessageWithTransfer · 142780 · 316447 · 245409 · 24 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · executeMessageWithTransferFallback · 171487 · 173352 · 172410 · 6 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · executeMessageWithTransferRefund · 115069 · 116933 · 115991 · 6 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · setGasFeeOfBlockchain · - · - · 48537 · 5 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · setIntegratorInfo · - · - · 50507 · 6 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · setMaxTokenAmount · 51120 · 51132 · 51121 · 11 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · sweepTokens · 53410 · 58597 · 56004 · 2 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · transferWithSwapV2 · 364621 · 369290 · 366956 · 4 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · transferWithSwapV2Native · - · - · 357606 · 2 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · transferWithSwapV3 · - · - · 379021 · 2 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · transferWithSwapV3Native · - · - · 358774 · 2 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| TestERC20 · approve · 46712 · 58446 · 52579 · 6 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| TestERC20 · transfer · 34527 · 63540 · 57057 · 35 · - │
|
||||
··················|······································|·············|·············|·············|···············|··············
|
||||
| Deployments · · % of limit · │
|
||||
·························································|·············|·············|·············|···············|··············
|
||||
| RubicRouterV2 · - · - · 5286604 · 17.6 % · - │
|
||||
·························································|·············|·············|·············|···············|··············
|
||||
| TestMessages · - · - · 2319848 · 7.7 % · - │
|
||||
·--------------------------------------------------------|-------------|-------------|-------------|---------------|-------------·
|
93
Interchain-message/scripts/deploy/deploy.js
Normal file
93
Interchain-message/scripts/deploy/deploy.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
const hre = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory("RubicRouterV2");
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address _supportedDex,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// BNB Chain 56
|
||||
// MessageBus 0x223fB0CeB2C6e5310264EFe38151d7D083db91f1
|
||||
// BNB native token address in BSC: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
|
||||
// USDC token address in BSC: 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
// '0x223fB0CeB2C6e5310264EFe38151d7D083db91f1',
|
||||
// '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
// '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'
|
||||
// );
|
||||
|
||||
// MATIC Polygon 137
|
||||
// MessageBus 0x265B25e22bcd7f10a5bD6E6410F10537Cc7567e8
|
||||
// MATIC native token address in BSC: 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270
|
||||
// USDC token address in BSC: 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// MATIC Polygon Testnet 80001
|
||||
// MessageBus 0x7d43AABC515C356145049227CeE54B608342c0ad
|
||||
// MATIC native token address:
|
||||
// USDC token address:
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
// '0x265B25e22bcd7f10a5bD6E6410F10537Cc7567e8',
|
||||
// '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
// '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270');
|
||||
|
||||
// BNB TEST 97
|
||||
// MessageBus 0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA
|
||||
// WBNB native token address in BSC: 0x094616F0BdFB0b526bD735Bf66Eca0Ad254ca81F
|
||||
// USDC token address in BSC: 0x9744ae566c64B6B6f7F9A4dD50f7496Df6Fef990
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
'0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA',
|
||||
['0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'],
|
||||
'0x094616F0BdFB0b526bD735Bf66Eca0Ad254ca81F'
|
||||
);
|
||||
|
||||
// GOERLI TEST 5
|
||||
// MessageBus 0x942E8e0e4b021F55b89660c886146e0Ec57F4b5B
|
||||
// WETH native token address in BSC: 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6
|
||||
// USDC token address in BSC: 0xCe7F7c709E8c74D8ad069Ed28abF25ddC43b32a9
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// const CrossChainSwapDeploy = await CrossChainSwap.deploy('0x942E8e0e4b021F55b89660c886146e0Ec57F4b5B', '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506', '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6');
|
||||
|
||||
// AVALANCHE 43114
|
||||
// MessageBus 0x7d43AABC515C356145049227CeE54B608342c0ad
|
||||
// WAVAX native token address in AVALANCHE: 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
|
||||
// USDC token address in AVALANCHE: 0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// const CrossChainSwapDeploy = await CrossChainSwap.deploy('0x7d43AABC515C356145049227CeE54B608342c0ad', '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506', '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7');
|
||||
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log("CrossChainSwapDeploy deployed to:", CrossChainSwapDeploy.address);
|
||||
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run("verify:verify", {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
'0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA',
|
||||
['0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'],
|
||||
'0x094616F0BdFB0b526bD735Bf66Eca0Ad254ca81F'
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
72
Interchain-message/scripts/deploy/deployAVAX.ts
Normal file
72
Interchain-message/scripts/deploy/deployAVAX.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { ethers } from 'hardhat';
|
||||
|
||||
const hre = require('hardhat');
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// AVALANCHE 43114
|
||||
// MessageBus 0x5a926eeeAFc4D217ADd17e9641e8cE23Cd01Ad57
|
||||
// WAVAX native token address in AVALANCHE: 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
|
||||
// USDC token address in AVALANCHE: 0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106',
|
||||
'0x60aE616a2155Ee3d9A68541Ba4544862310933d4',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x5a926eeeAFc4D217ADd17e9641e8cE23Cd01Ad57',
|
||||
'0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106',
|
||||
'0x60aE616a2155Ee3d9A68541Ba4544862310933d4',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x5a926eeeAFc4D217ADd17e9641e8cE23Cd01Ad57',
|
||||
'0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
72
Interchain-message/scripts/deploy/deployArbitrum.ts
Normal file
72
Interchain-message/scripts/deploy/deployArbitrum.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { ethers } from 'hardhat';
|
||||
|
||||
const hre = require('hardhat');
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// ETH Arbitrum 42161
|
||||
// MessageBus 0x3Ad9d0648CDAA2426331e894e980D0a5Ed16257f
|
||||
// ETH native token address in ARBITRUM: 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
|
||||
// USDC token address: 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8
|
||||
// SUSHI:
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('100'),
|
||||
'0',
|
||||
[
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0xe592427a0aece92de3edee1f18e0157c05861564',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x3Ad9d0648CDAA2426331e894e980D0a5Ed16257f',
|
||||
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('100'),
|
||||
'0',
|
||||
[
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0xe592427a0aece92de3edee1f18e0157c05861564',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x3Ad9d0648CDAA2426331e894e980D0a5Ed16257f',
|
||||
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
69
Interchain-message/scripts/deploy/deployAurora.ts
Normal file
69
Interchain-message/scripts/deploy/deployAurora.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
const hre = require('hardhat');
|
||||
const { ethers } = require('hardhat');
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// ETH Aurora 1313161554
|
||||
// MessageBus 0xc1a2D967DfAa6A10f3461bc21864C23C1DD51EeA
|
||||
// ETH native token address : 0xC9BdeEd33CD01541e1eeD10f90519d2C06Fe3feB
|
||||
// USDC token address: 0xB12BFcA5A55806AaF64E99521918A4bf0fC40802
|
||||
// SUSHI:
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'0', // 0.3%
|
||||
[
|
||||
'0x2CB45Edb4517d5947aFdE3BEAbF95A582506858B',
|
||||
'0xa3a1eF5Ae6561572023363862e238aFA84C72ef5'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0xc1a2D967DfAa6A10f3461bc21864C23C1DD51EeA',
|
||||
'0xC9BdeEd33CD01541e1eeD10f90519d2C06Fe3feB'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'0', // 0.3%
|
||||
[
|
||||
'0x2CB45Edb4517d5947aFdE3BEAbF95A582506858B',
|
||||
'0xa3a1eF5Ae6561572023363862e238aFA84C72ef5'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0xc1a2D967DfAa6A10f3461bc21864C23C1DD51EeA',
|
||||
'0xC9BdeEd33CD01541e1eeD10f90519d2C06Fe3feB'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
67
Interchain-message/scripts/deploy/deployBSC.ts
Normal file
67
Interchain-message/scripts/deploy/deployBSC.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
const hre = require('hardhat');
|
||||
import { ethers } from 'hardhat';
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
|
||||
// BNB Chain 56
|
||||
// MessageBus 0x95714818fdd7a5454F73Da9c777B3ee6EbAEEa6B
|
||||
// BNB native token address in BSC: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
|
||||
// USDC token address in BSC: 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0x10ed43c718714eb63d5aa57b78b54704e256024e',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0xcF0feBd3f17CEf5b47b0cD257aCf6025c5BFf3b7',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x95714818fdd7a5454F73Da9c777B3ee6EbAEEa6B',
|
||||
'0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0x10ed43c718714eb63d5aa57b78b54704e256024e',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0xcF0feBd3f17CEf5b47b0cD257aCf6025c5BFf3b7',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x95714818fdd7a5454F73Da9c777B3ee6EbAEEa6B',
|
||||
'0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
74
Interchain-message/scripts/deploy/deployEth.ts
Normal file
74
Interchain-message/scripts/deploy/deployEth.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { ethers } from 'hardhat';
|
||||
|
||||
const hre = require('hardhat');
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// ETH 1
|
||||
// MessageBus 0x4066D196A423b2b3B8B054f4F40efB47a74E200C
|
||||
// WETH native token address in ETHEREUM: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
|
||||
// USDC token address in Eth: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
|
||||
// SUSHI: 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
|
||||
'0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F',
|
||||
'0xE592427A0AEce92De3Edee1F18E0157C05861564',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x4066D196A423b2b3B8B054f4F40efB47a74E200C',
|
||||
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
|
||||
'0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F',
|
||||
'0xE592427A0AEce92De3Edee1F18E0157C05861564',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0x4066D196A423b2b3B8B054f4F40efB47a74E200C',
|
||||
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
74
Interchain-message/scripts/deploy/deployFantom.ts
Normal file
74
Interchain-message/scripts/deploy/deployFantom.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { ethers } from 'hardhat';
|
||||
|
||||
const hre = require('hardhat');
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// FTM Fantom 250
|
||||
// MessageBus 0xFF4E183a0Ceb4Fa98E63BbF8077B929c8E5A2bA4
|
||||
// FTM native token address in BSC: 0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83
|
||||
// USDC token address in BSC: 0x04068DA6C83AFCFA0e13ba15A6696662335D5B75
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0xf491e7b69e4244ad4002bc14e878a34207e38c29',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0x16327E3FbDaCA3bcF7E38F5Af2599D2DDc33aE52',
|
||||
'0x1111111254fb6c44bac0bed2854e76f90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0xFF4E183a0Ceb4Fa98E63BbF8077B929c8E5A2bA4',
|
||||
'0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('1000'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0xf491e7b69e4244ad4002bc14e878a34207e38c29',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0x16327E3FbDaCA3bcF7E38F5Af2599D2DDc33aE52',
|
||||
'0x1111111254fb6c44bac0bed2854e76f90643097d'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'0x503cef47ce5e37aa62544a363bef3c9b62d42116',
|
||||
'0xFF4E183a0Ceb4Fa98E63BbF8077B929c8E5A2bA4',
|
||||
'0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
71
Interchain-message/scripts/deploy/deployPoly.ts
Normal file
71
Interchain-message/scripts/deploy/deployPoly.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
const hre = require('hardhat');
|
||||
import { ethers } from 'hardhat';
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory('RubicRouterV2');
|
||||
|
||||
// MATIC Polygon 137
|
||||
// MessageBus 0xaFDb9C40C7144022811F034EE07Ce2E110093fe6
|
||||
// MATIC native token address in BSC: 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270
|
||||
// USDC token address in BSC: 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const CrossChainSwapDeploy = await CrossChainSwap.deploy(
|
||||
ethers.utils.parseEther('1').div('100'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0xa5e0829caced8ffdd4de3c43696c57f7d7a678ff',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0xC0788A3aD43d79aa53B09c2EaCc313A787d1d607',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d',
|
||||
'0x89D6B81A1Ef25894620D05ba843d83B0A296239e',
|
||||
'0xE592427A0AEce92De3Edee1F18E0157C05861564'
|
||||
],
|
||||
['0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174'],
|
||||
['10000000'],
|
||||
['200000000'],
|
||||
['56'],
|
||||
['500'],
|
||||
'0x00009cc27c811a3e0FdD2Fd737afCc721B67eE8e',
|
||||
'0xaFDb9C40C7144022811F034EE07Ce2E110093fe6',
|
||||
'0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270'
|
||||
);
|
||||
|
||||
await CrossChainSwapDeploy.deployed();
|
||||
|
||||
console.log('CrossChainSwapDeploy deployed to:', CrossChainSwapDeploy.address);
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: CrossChainSwapDeploy.address,
|
||||
constructorArguments: [
|
||||
ethers.utils.parseEther('1').div('100'),
|
||||
'3000', // 0.3%
|
||||
[
|
||||
'0xa5e0829caced8ffdd4de3c43696c57f7d7a678ff',
|
||||
'0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0xC0788A3aD43d79aa53B09c2EaCc313A787d1d607',
|
||||
'0x1111111254fb6c44bAC0beD2854e76F90643097d',
|
||||
'0x89D6B81A1Ef25894620D05ba843d83B0A296239e',
|
||||
'0xE592427A0AEce92De3Edee1F18E0157C05861564'
|
||||
],
|
||||
['0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174'],
|
||||
['10000000'],
|
||||
['200000000'],
|
||||
['56'],
|
||||
['500'],
|
||||
'0x00009cc27c811a3e0FdD2Fd737afCc721B67eE8e',
|
||||
'0xaFDb9C40C7144022811F034EE07Ce2E110093fe6',
|
||||
'0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
7
Interchain-message/scripts/privateKey.js
Normal file
7
Interchain-message/scripts/privateKey.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
var keythereum = require("keythereum");
|
||||
var datadir = "./";
|
||||
var address= "503cef47ce5e37aa62544a363bef3c9b62d42116";
|
||||
const password = "";
|
||||
var keyObject = keythereum.importFromFile(address, datadir);
|
||||
var privateKey = keythereum.recover(password, keyObject);
|
||||
console.log(privateKey.toString('hex'));
|
77
Interchain-message/scripts/sendTx/avaxToFantomBridge.js
Normal file
77
Interchain-message/scripts/sendTx/avaxToFantomBridge.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
const hre = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory("SwapMain");
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// AVALANCHE 43114
|
||||
// MessageBus 0x5a926eeeAFc4D217ADd17e9641e8cE23Cd01Ad57
|
||||
// WAVAX native token address in AVALANCHE: 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
|
||||
// USDC token address in AVALANCHE: 0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// FTM Fantom 250
|
||||
// MessageBus 0xFF4E183a0Ceb4Fa98E63BbF8077B929c8E5A2bA4
|
||||
// FTM native token address in BSC: 0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83
|
||||
// USDC token address in BSC: 0x04068DA6C83AFCFA0e13ba15A6696662335D5B75
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const address = '';
|
||||
|
||||
const crossChainSwap = await CrossChainSwap.attach(address);
|
||||
|
||||
// src bridge --- dst swap
|
||||
await crossChainSwap.bridgeWithSwap(
|
||||
0x93f56C28b66Fa3EEF980ab11a8a0E9D09c6576f5,
|
||||
21000000000000000000,
|
||||
43114,
|
||||
'0x04068DA6C83AFCFA0e13ba15A6696662335D5B75',
|
||||
['0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0x0000000000000000000000000000000000000000',
|
||||
'1',
|
||||
['0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7'],
|
||||
'0x',
|
||||
'999999999999999',
|
||||
'0'
|
||||
],
|
||||
1000000,
|
||||
true,
|
||||
{value: 21.00001}
|
||||
);
|
||||
|
||||
// src bridge --- dst bridge
|
||||
await crossChainSwap.bridgeWithSwap(
|
||||
0x93f56C28b66Fa3EEF980ab11a8a0E9D09c6576f5,
|
||||
21000000000000000000,
|
||||
43114,
|
||||
'0x04068DA6C83AFCFA0e13ba15A6696662335D5B75',
|
||||
['0x0000000000000000000000000000000000000000', // no need in dex
|
||||
'0x0000000000000000000000000000000000000000', // without integrator
|
||||
'4', // bridge
|
||||
['0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664'],
|
||||
'0x', // no need in bytes data
|
||||
'0', // no need in deadline
|
||||
'0' // no need in amountOut min
|
||||
],
|
||||
1000000,
|
||||
true,
|
||||
{value: 21.00001}
|
||||
);
|
||||
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
61
Interchain-message/scripts/sendTx/avaxToFantomNativeV2.js
Normal file
61
Interchain-message/scripts/sendTx/avaxToFantomNativeV2.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
const hre = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
const CrossChainSwap = await hre.ethers.getContractFactory("SwapMain");
|
||||
/*
|
||||
* constructor
|
||||
* address _messageBus,
|
||||
* address[] memory _supportedDEXes,
|
||||
* address _nativeWrap
|
||||
*/
|
||||
|
||||
// AVALANCHE 43114
|
||||
// MessageBus 0x5a926eeeAFc4D217ADd17e9641e8cE23Cd01Ad57
|
||||
// WAVAX native token address in AVALANCHE: 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
|
||||
// USDC token address in AVALANCHE: 0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
// FTM Fantom 250
|
||||
// MessageBus 0xFF4E183a0Ceb4Fa98E63BbF8077B929c8E5A2bA4
|
||||
// FTM native token address in BSC: 0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83
|
||||
// USDC token address in BSC: 0x04068DA6C83AFCFA0e13ba15A6696662335D5B75
|
||||
// SUSHI: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506
|
||||
|
||||
const address = '';
|
||||
|
||||
const crossChainSwap = await CrossChainSwap.attach(address);
|
||||
|
||||
await crossChainSwap.transferWithSwapV2Native(
|
||||
0x93f56C28b66Fa3EEF980ab11a8a0E9D09c6576f5,
|
||||
21000000000000000000,
|
||||
43114,
|
||||
['0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
['0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83', '0x04068DA6C83AFCFA0e13ba15A6696662335D5B75'],
|
||||
'999999999999999',
|
||||
'0'
|
||||
],
|
||||
['0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
|
||||
'0x0000000000000000000000000000000000000000',
|
||||
'1',
|
||||
['0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7'],
|
||||
'0x',
|
||||
'999999999999999',
|
||||
'0'
|
||||
],
|
||||
1000000,
|
||||
true,
|
||||
{value: 21.00001}
|
||||
);
|
||||
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
478
Interchain-message/test/RubicCrossChainBridge.spec.ts
Normal file
478
Interchain-message/test/RubicCrossChainBridge.spec.ts
Normal file
|
@ -0,0 +1,478 @@
|
|||
import { ethers, network, waffle } from 'hardhat';
|
||||
import { swapContractFixtureInFork } from './shared/fixtures';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
import { RubicRouterV2, TestERC20, TestMessages, WETH9 } from '../typechain';
|
||||
import { calcCryptoFees, calcTokenFees } from 'rubic-bridge-base/lib';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
DEADLINE,
|
||||
DST_CHAIN_ID,
|
||||
DEFAULT_AMOUNT_IN,
|
||||
VERSION_V2,
|
||||
ZERO_ADDRESS,
|
||||
DEFAULT_AMOUNT_OUT_MIN,
|
||||
EXECUTOR_ADDRESS,
|
||||
INTEGRATOR,
|
||||
DEFAULT_AMOUNT_IN_USDC,
|
||||
MESSAGE_BUS_FEE
|
||||
} from './shared/consts';
|
||||
import { BigNumber as BN, BigNumberish, ContractTransaction } from 'ethers';
|
||||
const hre = require('hardhat');
|
||||
|
||||
const createFixtureLoader = waffle.createFixtureLoader;
|
||||
|
||||
const envConfig = require('dotenv').config();
|
||||
const {
|
||||
ROUTERS_POLYGON: TEST_ROUTERS,
|
||||
NATIVE_POLYGON: TEST_NATIVE,
|
||||
BUS_POLYGON_MAIN: TEST_BUS
|
||||
} = envConfig.parsed || {};
|
||||
|
||||
describe('RubicCrossChainBridge', () => {
|
||||
let wallet: Wallet, other: Wallet;
|
||||
//let swapToken: TestERC20;
|
||||
let transitToken: TestERC20;
|
||||
let swapMain: RubicRouterV2;
|
||||
let router: string;
|
||||
let wnative: WETH9;
|
||||
//let chainId: number;
|
||||
|
||||
let testMessagesContract: TestMessages;
|
||||
|
||||
let loadFixture: ReturnType<typeof createFixtureLoader>;
|
||||
|
||||
async function callbridgeWithSwapNative({
|
||||
receiver = null,
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
dstChainID = DST_CHAIN_ID,
|
||||
srcBridgeToken = wnative.address,
|
||||
nativeIn = null,
|
||||
integrator = ZERO_ADDRESS,
|
||||
nativeOut = true
|
||||
} = {}): Promise<ContractTransaction> {
|
||||
const { totalCryptoFee } = await calcCryptoFees({
|
||||
bridge: swapMain,
|
||||
integrator,
|
||||
dstChainID
|
||||
});
|
||||
|
||||
return swapMain.bridgeWithSwapNative(
|
||||
receiver === null ? wallet.address : receiver,
|
||||
amountIn,
|
||||
dstChainID,
|
||||
srcBridgeToken,
|
||||
{
|
||||
dex: router,
|
||||
nativeOut: nativeOut,
|
||||
receiverEOA: other.address,
|
||||
integrator: integrator,
|
||||
version: VERSION_V2,
|
||||
path: [wnative.address, transitToken.address],
|
||||
pathV3: '0x',
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum: DEFAULT_AMOUNT_OUT_MIN
|
||||
},
|
||||
'10000',
|
||||
{
|
||||
value:
|
||||
nativeIn === null ? amountIn.add(totalCryptoFee).add(MESSAGE_BUS_FEE) : nativeIn
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function callbridgeWithSwap({
|
||||
receiver = null,
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
dstChainID = DST_CHAIN_ID,
|
||||
srcBridgeToken = transitToken.address,
|
||||
nativeIn = null,
|
||||
nativeOut = true,
|
||||
integrator = ZERO_ADDRESS
|
||||
} = {}): Promise<ContractTransaction> {
|
||||
const { totalCryptoFee } = await calcCryptoFees({
|
||||
bridge: swapMain,
|
||||
integrator,
|
||||
dstChainID
|
||||
});
|
||||
|
||||
return swapMain.bridgeWithSwap(
|
||||
receiver === null ? wallet.address : receiver,
|
||||
amountIn,
|
||||
dstChainID,
|
||||
srcBridgeToken,
|
||||
{
|
||||
dex: router,
|
||||
nativeOut: nativeOut,
|
||||
receiverEOA: other.address,
|
||||
integrator: integrator,
|
||||
version: VERSION_V2,
|
||||
path: [wnative.address, transitToken.address],
|
||||
pathV3: '0x',
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum: DEFAULT_AMOUNT_OUT_MIN
|
||||
},
|
||||
'10000',
|
||||
{ value: nativeIn === null ? totalCryptoFee.add(MESSAGE_BUS_FEE) : nativeIn }
|
||||
);
|
||||
}
|
||||
|
||||
async function getMessage(
|
||||
messagesContract: TestMessages,
|
||||
_nonce: BigNumberish,
|
||||
dstChainId: BigNumberish,
|
||||
{
|
||||
dex = router,
|
||||
receiverEOA = other.address,
|
||||
integrator = ethers.constants.AddressZero,
|
||||
version = VERSION_V2,
|
||||
path = [wnative.address, transitToken.address],
|
||||
pathV3 = '0x',
|
||||
deadline = DEADLINE,
|
||||
amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
_receiver = wallet.address,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
return messagesContract.getMessage(
|
||||
{
|
||||
dex,
|
||||
nativeOut,
|
||||
receiverEOA,
|
||||
integrator,
|
||||
version,
|
||||
path,
|
||||
pathV3,
|
||||
deadline,
|
||||
amountOutMinimum
|
||||
},
|
||||
_nonce,
|
||||
dstChainId
|
||||
);
|
||||
}
|
||||
|
||||
// async function getID(
|
||||
// messagesContract: TestMessages,
|
||||
// _nonce: BigNumberish,
|
||||
// {
|
||||
// dex = router,
|
||||
// integrator = ZERO_ADDRESS,
|
||||
// version = VERSION,
|
||||
// path = [wnative.address, transitToken.address],
|
||||
// pathV3 = '0x',
|
||||
// deadline = DEADLINE,
|
||||
// amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
// _receiver = wallet.address,
|
||||
// nativeOut = true,
|
||||
// _srcChainId = chainId,
|
||||
// _dstChainId = DST_CHAIN_ID
|
||||
// } = {}
|
||||
// ): Promise<string> {
|
||||
// return messagesContract.getID(
|
||||
// _receiver,
|
||||
// _srcChainId,
|
||||
// _dstChainId,
|
||||
// {
|
||||
// dex,
|
||||
// nativeOut,
|
||||
// integrator,
|
||||
// version,
|
||||
// path,
|
||||
// pathV3,
|
||||
// deadline,
|
||||
// amountOutMinimum
|
||||
// },
|
||||
// _nonce
|
||||
// );
|
||||
// }
|
||||
|
||||
before('create fixture loader', async () => {
|
||||
[wallet, other] = await (ethers as any).getSigners();
|
||||
loadFixture = createFixtureLoader([wallet, other]);
|
||||
//chainId = (await ethers.provider.getNetwork()).chainId;
|
||||
});
|
||||
|
||||
beforeEach('deploy fixture', async () => {
|
||||
({ swapMain, transitToken, wnative, router, testMessagesContract } = await loadFixture(
|
||||
swapContractFixtureInFork
|
||||
));
|
||||
});
|
||||
|
||||
it('constructor initializes', async () => {
|
||||
expect(await swapMain.nativeWrap()).to.eq(TEST_NATIVE);
|
||||
expect(await swapMain.messageBus()).to.eq(TEST_BUS);
|
||||
|
||||
const routers = TEST_ROUTERS.split(',');
|
||||
expect(await swapMain.getAvailableRouters()).to.deep.eq(routers);
|
||||
});
|
||||
|
||||
describe('#WithBridgeTests', () => {
|
||||
describe('#bridgeWithSwapNative', () => {
|
||||
it('Should bridge native and transfer through Celer', async () => {
|
||||
await swapMain.setMaxTokenAmount(wnative.address, ethers.utils.parseEther('1000'));
|
||||
//const ID = await getID(testMessagesContract, (await swapMain.nonce()).add('1'));
|
||||
|
||||
await expect(callbridgeWithSwapNative()).to.emit(swapMain, 'CrossChainRequestSent');
|
||||
//.withArgs(ID, DST_CHAIN_ID, DEFAULT_AMOUNT_IN, wnative.address);
|
||||
});
|
||||
});
|
||||
describe('#bridgeWithSwap', () => {
|
||||
it('Should fail transfering with big amount', async () => {
|
||||
await transitToken.approve(swapMain.address, ethers.constants.MaxUint256);
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
await expect(callbridgeWithSwap()).to.be.revertedWith('amount too large');
|
||||
});
|
||||
|
||||
it('Should fail transfering with small amount', async () => {
|
||||
await transitToken.approve(swapMain.address, ethers.constants.MaxUint256);
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
await expect(
|
||||
callbridgeWithSwap({
|
||||
amountIn: ethers.BigNumber.from('10000000')
|
||||
})
|
||||
).to.be.revertedWith('amount too small');
|
||||
});
|
||||
it('Should bridge transitToken and transfer through Сeler', async () => {
|
||||
await transitToken.approve(swapMain.address, ethers.constants.MaxUint256);
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
//const ID = await getID(testMessagesContract, (await swapMain.nonce()).add('1'));
|
||||
|
||||
await expect(
|
||||
callbridgeWithSwap({
|
||||
amountIn: DEFAULT_AMOUNT_IN_USDC
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainRequestSent');
|
||||
//.withArgs(ID, DST_CHAIN_ID, DEFAULT_AMOUNT_IN_USDC, transitToken.address);
|
||||
});
|
||||
});
|
||||
describe('#executeMessageWithTransfer', () => {
|
||||
beforeEach('setup for target executions', async () => {
|
||||
// transfer 1000 USDC
|
||||
await transitToken.transfer(swapMain.address, ethers.BigNumber.from('1000000000'));
|
||||
});
|
||||
describe('target bridge should emit correct event', async () => {
|
||||
let nonce: BN;
|
||||
let message: string;
|
||||
|
||||
beforeEach('setup before bridge', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: ZERO_ADDRESS,
|
||||
version: 2, // bridge version
|
||||
path: [transitToken.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('0') // not used
|
||||
});
|
||||
});
|
||||
it('should successfully bridge token with rubic fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
let tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
// take only platform comission in transit token
|
||||
const { feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000')
|
||||
});
|
||||
await expect(Number(tokenBalanceAfter)).to.be.eq(feeAmount);
|
||||
});
|
||||
|
||||
it('should successfully bridge native with rubic fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
const abiCoder = ethers.utils.defaultAbiCoder;
|
||||
|
||||
const storageBalancePositionWeth = ethers.utils.keccak256(
|
||||
abiCoder.encode(['address'], [bus.address]) +
|
||||
abiCoder.encode(['uint256'], [3]).slice(2, 66)
|
||||
);
|
||||
|
||||
await network.provider.send('hardhat_setStorageAt', [
|
||||
wnative.address,
|
||||
storageBalancePositionWeth,
|
||||
abiCoder.encode(['uint256'], [ethers.utils.parseEther('100000')])
|
||||
]);
|
||||
|
||||
await wnative
|
||||
.connect(bus)
|
||||
.transfer(swapMain.address, ethers.utils.parseEther('1'));
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: ZERO_ADDRESS,
|
||||
version: 2, // bridge version
|
||||
path: [wnative.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('0') // not used
|
||||
});
|
||||
|
||||
//let tokenBalanceBefore = await wnative.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
wnative.address,
|
||||
ethers.utils.parseEther('1'), // 1 ether
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
let tokenBalanceAfter = await wnative.balanceOf(swapMain.address);
|
||||
const { feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.utils.parseEther('1')
|
||||
});
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
|
||||
it('should fail bridge with incorrect path', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: ZERO_ADDRESS,
|
||||
version: 2, // bridge version
|
||||
path: [transitToken.address, wnative.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('0') // not used
|
||||
});
|
||||
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.be.revertedWith('dst bridge expected');
|
||||
});
|
||||
|
||||
describe('target bridge should take integrator & rubic fee', async () => {
|
||||
beforeEach('set integrator and rubic fee', async () => {
|
||||
await swapMain.setIntegratorInfo(INTEGRATOR, {
|
||||
isIntegrator: true,
|
||||
tokenFee: '3000',
|
||||
RubicTokenShare: '400000',
|
||||
RubicFixedCryptoShare: '800000',
|
||||
fixedFeeAmount: ethers.utils.parseEther('2')
|
||||
}); // 0.3 %
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: ZERO_ADDRESS,
|
||||
integrator: INTEGRATOR,
|
||||
version: 2, // bridge version
|
||||
path: [transitToken.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('0') // not used
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully bridge transitToken with rubic & integrator fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
|
||||
const { integratorFee, RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000'),
|
||||
integrator: INTEGRATOR
|
||||
});
|
||||
|
||||
await expect(integratorCollectedFee1).to.be.eq(integratorFee);
|
||||
|
||||
// take platform comission in transit token
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
611
Interchain-message/test/RubicCrossChainV2.spec.ts
Normal file
611
Interchain-message/test/RubicCrossChainV2.spec.ts
Normal file
|
@ -0,0 +1,611 @@
|
|||
import { ethers, network, waffle } from 'hardhat';
|
||||
import { swapContractFixtureInFork } from './shared/fixtures';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
import { RubicRouterV2, TestERC20, TestMessages, WETH9 } from '../typechain';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
DEADLINE,
|
||||
DST_CHAIN_ID,
|
||||
DEFAULT_AMOUNT_IN,
|
||||
VERSION_V2,
|
||||
DEFAULT_AMOUNT_OUT_MIN,
|
||||
EXECUTOR_ADDRESS,
|
||||
INTEGRATOR,
|
||||
DEFAULT_AMOUNT_IN_USDC,
|
||||
MESSAGE_BUS_FEE
|
||||
} from './shared/consts';
|
||||
import { BigNumber as BN, BigNumberish, ContractTransaction } from 'ethers';
|
||||
import { getRouterV2 } from './shared/utils';
|
||||
import { calcCryptoFees, calcTokenFees } from 'rubic-bridge-base/lib';
|
||||
const hre = require('hardhat');
|
||||
|
||||
const createFixtureLoader = waffle.createFixtureLoader;
|
||||
|
||||
const envConfig = require('dotenv').config();
|
||||
const {
|
||||
ROUTERS_POLYGON: TEST_ROUTERS,
|
||||
NATIVE_POLYGON: TEST_NATIVE,
|
||||
BUS_POLYGON_MAIN: TEST_BUS
|
||||
} = envConfig.parsed || {};
|
||||
|
||||
describe('RubicCrossChainV2', () => {
|
||||
let wallet: Wallet, other: Wallet;
|
||||
let swapToken: TestERC20;
|
||||
let transitToken: TestERC20;
|
||||
let swapMain: RubicRouterV2;
|
||||
let router: string;
|
||||
let wnative: WETH9;
|
||||
|
||||
let testMessagesContract: TestMessages;
|
||||
|
||||
let loadFixture: ReturnType<typeof createFixtureLoader>;
|
||||
|
||||
async function callTransferWithSwapV2Native(
|
||||
amountOutMinimum: BigNumberish,
|
||||
{
|
||||
receiver = null,
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
dstChainID = DST_CHAIN_ID,
|
||||
srcDEX = router,
|
||||
srcPath = [wnative.address, transitToken.address],
|
||||
nativeIn = null,
|
||||
integrator = INTEGRATOR,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<ContractTransaction> {
|
||||
const { totalCryptoFee } = await calcCryptoFees({
|
||||
bridge: swapMain,
|
||||
integrator,
|
||||
dstChainID
|
||||
});
|
||||
|
||||
return swapMain.transferWithSwapV2Native(
|
||||
receiver === null ? wallet.address : receiver,
|
||||
amountIn,
|
||||
dstChainID,
|
||||
{
|
||||
dex: srcDEX,
|
||||
path: srcPath,
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum
|
||||
},
|
||||
{
|
||||
dex: router,
|
||||
nativeOut: nativeOut,
|
||||
receiverEOA: other.address,
|
||||
integrator: integrator,
|
||||
version: VERSION_V2,
|
||||
path: [wnative.address, transitToken.address],
|
||||
pathV3: '0x',
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum: DEFAULT_AMOUNT_OUT_MIN
|
||||
},
|
||||
'10000',
|
||||
{
|
||||
value:
|
||||
nativeIn === null ? amountIn.add(totalCryptoFee).add(MESSAGE_BUS_FEE) : nativeIn
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function callTransferWithSwapV2(
|
||||
amountOutMinimum: BigNumberish,
|
||||
{
|
||||
receiver = null,
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
dstChainID = DST_CHAIN_ID,
|
||||
srcDEX = router,
|
||||
srcPath = [wnative.address, transitToken.address],
|
||||
nativeIn = null,
|
||||
integrator = INTEGRATOR,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<ContractTransaction> {
|
||||
const { totalCryptoFee } = await calcCryptoFees({
|
||||
bridge: swapMain,
|
||||
integrator,
|
||||
dstChainID
|
||||
});
|
||||
|
||||
return swapMain.transferWithSwapV2(
|
||||
receiver === null ? wallet.address : receiver,
|
||||
amountIn,
|
||||
dstChainID,
|
||||
{
|
||||
dex: srcDEX,
|
||||
path: srcPath,
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum
|
||||
},
|
||||
{
|
||||
dex: router,
|
||||
nativeOut: nativeOut,
|
||||
receiverEOA: other.address,
|
||||
integrator: integrator,
|
||||
version: VERSION_V2,
|
||||
path: [wnative.address, transitToken.address],
|
||||
pathV3: '0x',
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum: DEFAULT_AMOUNT_OUT_MIN
|
||||
},
|
||||
'10000',
|
||||
{ value: nativeIn === null ? totalCryptoFee.add(MESSAGE_BUS_FEE) : nativeIn }
|
||||
);
|
||||
}
|
||||
|
||||
async function getAmountOutMin(
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
path = [wnative.address, transitToken.address]
|
||||
) {
|
||||
const routerV2 = await getRouterV2(wallet, router);
|
||||
|
||||
return (await routerV2.getAmountsOut(amountIn, path))[1];
|
||||
}
|
||||
|
||||
// async function getAmountIn(
|
||||
// amountOut = DEFAULT_AMOUNT_OUT_MIN,
|
||||
// path = [transitToken.address, swapToken.address]
|
||||
// ) {
|
||||
// const routerV2 = await getRouterV2(wallet, router);
|
||||
// return (await routerV2.getAmountsIn(amountOut, path))[0];
|
||||
// }
|
||||
|
||||
async function getMessage(
|
||||
messagesContract: TestMessages,
|
||||
_nonce: BigNumberish,
|
||||
dstChainId: BigNumberish,
|
||||
{
|
||||
dex = router,
|
||||
receiverEOA = other.address,
|
||||
integrator = ethers.constants.AddressZero,
|
||||
version = VERSION_V2,
|
||||
path = [wnative.address, transitToken.address],
|
||||
pathV3 = '0x',
|
||||
deadline = DEADLINE,
|
||||
amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
_receiver = wallet.address,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
return messagesContract.getMessage(
|
||||
{
|
||||
dex,
|
||||
nativeOut,
|
||||
receiverEOA,
|
||||
integrator,
|
||||
version,
|
||||
path,
|
||||
pathV3,
|
||||
deadline,
|
||||
amountOutMinimum
|
||||
},
|
||||
_nonce,
|
||||
dstChainId
|
||||
);
|
||||
}
|
||||
|
||||
// async function getID(
|
||||
// messagesContract: TestMessages,
|
||||
// _nonce: BigNumberish,
|
||||
// {
|
||||
// dex = router,
|
||||
// receiverEOA = other.address,
|
||||
// integrator = INTEGRATOR,
|
||||
// version = VERSION_V2,
|
||||
// path = [wnative.address, transitToken.address],
|
||||
// pathV3 = '0x',
|
||||
// deadline = DEADLINE,
|
||||
// amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
// _receiver = wallet.address,
|
||||
// nativeOut = true,
|
||||
// _srcChainId = chainId,
|
||||
// _dstChainId = DST_CHAIN_ID
|
||||
// } = {}
|
||||
// ): Promise<string> {
|
||||
// return messagesContract.getID(
|
||||
// _srcChainId,
|
||||
// _dstChainId,
|
||||
// {
|
||||
// dex,
|
||||
// nativeOut,
|
||||
// receiverEOA,
|
||||
// integrator,
|
||||
// version,
|
||||
// path,
|
||||
// pathV3,
|
||||
// deadline,
|
||||
// amountOutMinimum
|
||||
// },
|
||||
// _nonce
|
||||
// );
|
||||
// }
|
||||
|
||||
before('create fixture loader', async () => {
|
||||
[wallet, other] = await (ethers as any).getSigners();
|
||||
loadFixture = createFixtureLoader([wallet, other]);
|
||||
});
|
||||
|
||||
beforeEach('deploy fixture', async () => {
|
||||
({ swapMain, swapToken, transitToken, wnative, router, testMessagesContract } =
|
||||
await loadFixture(swapContractFixtureInFork));
|
||||
});
|
||||
|
||||
it('constructor initializes', async () => {
|
||||
expect(await swapMain.nativeWrap()).to.eq(TEST_NATIVE);
|
||||
expect(await swapMain.messageBus()).to.eq(TEST_BUS);
|
||||
|
||||
const routers = TEST_ROUTERS.split(',');
|
||||
expect(await swapMain.getAvailableRouters()).to.deep.eq(routers);
|
||||
});
|
||||
|
||||
describe('#WithSwapTests', () => {
|
||||
describe('#transferWithSwapV2Native', () => {
|
||||
it('Should swap native to token and transfer through Celer', async () => {
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
const amountOutMin = await getAmountOutMin(
|
||||
ethers.BigNumber.from('20000000000000000000')
|
||||
);
|
||||
const _amountIn = ethers.BigNumber.from('20000000000000000000');
|
||||
|
||||
await expect(
|
||||
callTransferWithSwapV2Native(amountOutMin, {
|
||||
amountIn: _amountIn,
|
||||
srcPath: [wnative.address, transitToken.address]
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainRequestSent');
|
||||
});
|
||||
it('Should swap native to token and fail transfer through Celer', async () => {
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
const amountOutMin = await getAmountOutMin();
|
||||
|
||||
await expect(
|
||||
callTransferWithSwapV2Native(amountOutMin, {
|
||||
srcPath: [wnative.address, transitToken.address]
|
||||
})
|
||||
).to.be.revertedWith('amount too small');
|
||||
});
|
||||
});
|
||||
describe('#transferWithSwapV2', () => {
|
||||
it('Should swap token to transitToken and transfer through Сeler', async () => {
|
||||
await swapToken.approve(swapMain.address, ethers.constants.MaxUint256);
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
const amountOutMin = await getAmountOutMin(DEFAULT_AMOUNT_IN, [
|
||||
swapToken.address,
|
||||
transitToken.address
|
||||
]);
|
||||
|
||||
//const ID = await getID(testMessagesContract, (await swapMain.nonce()).add('1'));
|
||||
await expect(
|
||||
callTransferWithSwapV2(amountOutMin, {
|
||||
srcPath: [swapToken.address, transitToken.address]
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainRequestSent');
|
||||
//.withArgs(ID, DST_CHAIN_ID, DEFAULT_AMOUNT_IN, swapToken.address);
|
||||
});
|
||||
|
||||
it('Should swap token to native and transfer through Сeler', async () => {
|
||||
await swapToken.approve(swapMain.address, ethers.constants.MaxUint256);
|
||||
await swapMain.setMaxTokenAmount(wnative.address, ethers.utils.parseEther('10000'));
|
||||
|
||||
// amountIn is 100$
|
||||
const amountOutMin = await getAmountOutMin(DEFAULT_AMOUNT_IN_USDC, [
|
||||
swapToken.address,
|
||||
wnative.address
|
||||
]);
|
||||
|
||||
//const ID = await getID(testMessagesContract, (await swapMain.nonce()).add('1'));
|
||||
|
||||
await expect(
|
||||
callTransferWithSwapV2(amountOutMin, {
|
||||
srcPath: [swapToken.address, wnative.address]
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainRequestSent');
|
||||
//.withArgs(ID, DST_CHAIN_ID, DEFAULT_AMOUNT_IN, swapToken.address);
|
||||
});
|
||||
});
|
||||
describe('#executeMessageWithTransfer', () => {
|
||||
beforeEach('setup for target executions', async () => {
|
||||
// transfer 1000 USDC
|
||||
await transitToken.transfer(swapMain.address, ethers.BigNumber.from('1000000000'));
|
||||
});
|
||||
describe('target swap should emit correct event', async () => {
|
||||
let nonce: BN;
|
||||
let message: string;
|
||||
|
||||
beforeEach('setup before swap', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [transitToken.address, swapToken.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('200000000000000000') // 0.2 eth for 1000$ is min
|
||||
});
|
||||
});
|
||||
it('should successfully swap V2 with rubic fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
let tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
const { feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000')
|
||||
});
|
||||
// take only platform comission in transit token
|
||||
await expect(Number(tokenBalanceAfter)).to.be.eq(feeAmount);
|
||||
});
|
||||
|
||||
it('should fail swap V2 with rubic fee and transfer tokens', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [transitToken.address, swapToken.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('2000000000000000000') // 2 eth for 1000$ is minOut, too much
|
||||
});
|
||||
|
||||
//const tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
const { RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000')
|
||||
//integrator: INTEGRATOR,
|
||||
});
|
||||
|
||||
// take only platform comission in transit token
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
await expect(Number(integratorCollectedFee1)).to.be.eq(0);
|
||||
});
|
||||
|
||||
describe('target swap should take integrator & rubic fee', async () => {
|
||||
beforeEach('set integrator and rubic fee', async () => {
|
||||
await swapMain.setIntegratorInfo(INTEGRATOR, {
|
||||
isIntegrator: true,
|
||||
tokenFee: '3000',
|
||||
RubicTokenShare: '400000',
|
||||
RubicFixedCryptoShare: '800000',
|
||||
fixedFeeAmount: ethers.utils.parseEther('2')
|
||||
}); // 0.3 %
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [transitToken.address, swapToken.address],
|
||||
integrator: INTEGRATOR,
|
||||
amountOutMinimum: ethers.BigNumber.from('200000000000000000') // 0.2 eth for 1000$ is minOut, too much
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully swapV2 token to token with rubic & integrator fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
|
||||
const { integratorFee, RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000'),
|
||||
integrator: INTEGRATOR
|
||||
});
|
||||
|
||||
await expect(integratorCollectedFee1).to.be.eq(integratorFee);
|
||||
|
||||
// take platform comission in transit token
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
|
||||
it('should successfully swapV2 token to native with rubic & integrator fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [transitToken.address, wnative.address],
|
||||
integrator: INTEGRATOR,
|
||||
amountOutMinimum: ethers.BigNumber.from('20000000000000000') // 0.02 eth for 1000$ is minOut
|
||||
});
|
||||
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
|
||||
const { integratorFee, RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000'),
|
||||
integrator: INTEGRATOR
|
||||
});
|
||||
|
||||
await expect(integratorCollectedFee1).to.be.eq(integratorFee);
|
||||
|
||||
// take platform comission in transit token
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
|
||||
it('should fail swap V2 with rubic & integrator fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [transitToken.address, swapToken.address],
|
||||
integrator: INTEGRATOR,
|
||||
amountOutMinimum: ethers.BigNumber.from('20000000000000000000') // 20 eth for 1000$ is min out
|
||||
});
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
|
||||
const { integratorFee, RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000'),
|
||||
integrator: INTEGRATOR
|
||||
});
|
||||
|
||||
await expect(integratorCollectedFee1).to.be.eq(integratorFee);
|
||||
|
||||
// take platform comission in transit token
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
547
Interchain-message/test/RubicCrossChainV3.spec.ts
Normal file
547
Interchain-message/test/RubicCrossChainV3.spec.ts
Normal file
|
@ -0,0 +1,547 @@
|
|||
import { ethers, network, waffle } from 'hardhat';
|
||||
import { swapContractFixtureInFork } from './shared/fixtures';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
import { RubicRouterV2, TestERC20, TestMessages, WETH9 } from '../typechain';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
DEADLINE,
|
||||
DST_CHAIN_ID,
|
||||
DEFAULT_AMOUNT_IN,
|
||||
VERSION_V2,
|
||||
VERSION_V3,
|
||||
ZERO_ADDRESS,
|
||||
DEFAULT_AMOUNT_OUT_MIN,
|
||||
EXECUTOR_ADDRESS,
|
||||
INTEGRATOR,
|
||||
MESSAGE_BUS_FEE
|
||||
} from './shared/consts';
|
||||
import { BigNumber as BN, BigNumberish, ContractTransaction, BytesLike } from 'ethers';
|
||||
import { calcCryptoFees, calcTokenFees } from 'rubic-bridge-base/lib';
|
||||
// import { getRouterV3 } from './shared/utils';
|
||||
const hre = require('hardhat');
|
||||
|
||||
const createFixtureLoader = waffle.createFixtureLoader;
|
||||
|
||||
const envConfig = require('dotenv').config();
|
||||
const {
|
||||
ROUTERS_POLYGON: TEST_ROUTERS,
|
||||
NATIVE_POLYGON: TEST_NATIVE,
|
||||
BUS_POLYGON_MAIN: TEST_BUS
|
||||
} = envConfig.parsed || {};
|
||||
|
||||
describe('RubicCrossChainV3', () => {
|
||||
let wallet: Wallet, other: Wallet;
|
||||
let swapToken: TestERC20;
|
||||
let transitToken: TestERC20;
|
||||
let swapMain: RubicRouterV2;
|
||||
let router: string;
|
||||
let routerV3: string;
|
||||
let wnative: WETH9;
|
||||
|
||||
let testMessagesContract: TestMessages;
|
||||
|
||||
let loadFixture: ReturnType<typeof createFixtureLoader>;
|
||||
|
||||
// async function toBytes32(address): Promise<string> {
|
||||
// return '0x000000000000000000000000' + address.slice(2, address.length);
|
||||
// }
|
||||
|
||||
async function encodePath(tokens): Promise<string> {
|
||||
return tokens[0] + '000bb8' + tokens[1].slice(2);
|
||||
}
|
||||
|
||||
// function encodePathReverse(tokens){
|
||||
// const zeros = '0'.repeat(24)
|
||||
// return (tokens[1] + '000bb8' + tokens[0].slice(2))
|
||||
// }
|
||||
|
||||
async function callTransferWithSwapV3Native(
|
||||
amountOutMinimum: BigNumberish,
|
||||
srcPathBytes: BytesLike,
|
||||
{
|
||||
receiver = null,
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
dstChainID = DST_CHAIN_ID,
|
||||
srcDEX = routerV3,
|
||||
nativeIn = null,
|
||||
integrator = INTEGRATOR,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<ContractTransaction> {
|
||||
const { totalCryptoFee } = await calcCryptoFees({
|
||||
bridge: swapMain,
|
||||
integrator,
|
||||
dstChainID
|
||||
});
|
||||
|
||||
return swapMain.transferWithSwapV3Native(
|
||||
receiver === null ? wallet.address : receiver,
|
||||
amountIn,
|
||||
dstChainID,
|
||||
{
|
||||
dex: srcDEX,
|
||||
path: srcPathBytes,
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum
|
||||
},
|
||||
{
|
||||
dex: routerV3,
|
||||
nativeOut: nativeOut,
|
||||
receiverEOA: other.address,
|
||||
integrator: integrator,
|
||||
version: VERSION_V2,
|
||||
path: [wnative.address, transitToken.address],
|
||||
pathV3: '0x',
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum: DEFAULT_AMOUNT_OUT_MIN
|
||||
},
|
||||
'10000',
|
||||
{
|
||||
value:
|
||||
nativeIn === null ? amountIn.add(totalCryptoFee).add(MESSAGE_BUS_FEE) : nativeIn
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function callTransferWithSwapV3(
|
||||
amountOutMinimum: BigNumberish,
|
||||
srcPathBytes: BytesLike,
|
||||
{
|
||||
receiver = null,
|
||||
amountIn = DEFAULT_AMOUNT_IN,
|
||||
dstChainID = DST_CHAIN_ID,
|
||||
srcDEX = routerV3,
|
||||
nativeIn = null,
|
||||
integrator = INTEGRATOR,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<ContractTransaction> {
|
||||
const { totalCryptoFee } = await calcCryptoFees({
|
||||
bridge: swapMain,
|
||||
integrator,
|
||||
dstChainID
|
||||
});
|
||||
|
||||
return swapMain.transferWithSwapV3(
|
||||
receiver === null ? wallet.address : receiver,
|
||||
amountIn,
|
||||
dstChainID,
|
||||
{
|
||||
dex: srcDEX,
|
||||
path: srcPathBytes,
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum
|
||||
},
|
||||
{
|
||||
dex: router,
|
||||
nativeOut: nativeOut,
|
||||
receiverEOA: other.address,
|
||||
integrator: integrator,
|
||||
version: VERSION_V2,
|
||||
path: [wnative.address, transitToken.address],
|
||||
pathV3: '0x',
|
||||
deadline: DEADLINE,
|
||||
amountOutMinimum: DEFAULT_AMOUNT_OUT_MIN
|
||||
},
|
||||
'10000',
|
||||
{ value: nativeIn === null ? totalCryptoFee.add(MESSAGE_BUS_FEE) : nativeIn }
|
||||
);
|
||||
}
|
||||
|
||||
// async function getAmountOutMinV3(
|
||||
// amountIn = DEFAULT_AMOUNT_IN,
|
||||
// path = [swapToken.address, transitToken.address]
|
||||
// ): Promise<BN> {
|
||||
// const _path = pack(['address', 'uint24', 'address'], [path[0], FeeAmount.LOW, path[1]]);
|
||||
// return quoter.callStatic.quoteExactInput(_path, amountIn);
|
||||
// }
|
||||
|
||||
async function getMessage(
|
||||
messagesContract: TestMessages,
|
||||
_nonce: BigNumberish,
|
||||
dstChainId: BigNumberish,
|
||||
{
|
||||
dex = routerV3,
|
||||
receiverEOA = other.address,
|
||||
integrator = ethers.constants.AddressZero,
|
||||
version = VERSION_V3,
|
||||
path = [wnative.address, transitToken.address],
|
||||
pathV3 = '0x',
|
||||
deadline = DEADLINE,
|
||||
amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
_receiver = wallet.address,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
return messagesContract.getMessage(
|
||||
{
|
||||
dex,
|
||||
receiverEOA,
|
||||
nativeOut,
|
||||
integrator,
|
||||
version,
|
||||
path,
|
||||
pathV3,
|
||||
deadline,
|
||||
amountOutMinimum
|
||||
},
|
||||
_nonce,
|
||||
dstChainId
|
||||
);
|
||||
}
|
||||
|
||||
// async function getID(
|
||||
// messagesContract: TestMessages,
|
||||
// _nonce: BigNumberish,
|
||||
// {
|
||||
// dex = routerV3,
|
||||
// receiverEOA = other.address,
|
||||
// integrator = INTEGRATOR,
|
||||
// version = VERSION_V3,
|
||||
// path = [wnative.address, transitToken.address],
|
||||
// pathV3 = '0x',
|
||||
// deadline = DEADLINE,
|
||||
// amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
// _receiver = wallet.address,
|
||||
// nativeOut = true,
|
||||
// _srcChainId = chainId,
|
||||
// _dstChainId = DST_CHAIN_ID
|
||||
// } = {}
|
||||
// ): Promise<string> {
|
||||
// return messagesContract.getID(
|
||||
// _srcChainId,
|
||||
// _dstChainId,
|
||||
// {
|
||||
// dex,
|
||||
// nativeOut,
|
||||
// receiverEOA,
|
||||
// integrator,
|
||||
// version,
|
||||
// path,
|
||||
// pathV3,
|
||||
// deadline,
|
||||
// amountOutMinimum
|
||||
// },
|
||||
// _nonce
|
||||
// );
|
||||
// }
|
||||
|
||||
before('create fixture loader', async () => {
|
||||
[wallet, other] = await (ethers as any).getSigners();
|
||||
loadFixture = createFixtureLoader([wallet, other]);
|
||||
});
|
||||
|
||||
beforeEach('deploy fixture', async () => {
|
||||
({ swapMain, swapToken, transitToken, wnative, router, routerV3, testMessagesContract } =
|
||||
await loadFixture(swapContractFixtureInFork));
|
||||
});
|
||||
|
||||
it('constructor initializes', async () => {
|
||||
expect(await swapMain.nativeWrap()).to.eq(TEST_NATIVE);
|
||||
expect(await swapMain.messageBus()).to.eq(TEST_BUS);
|
||||
|
||||
const routers = TEST_ROUTERS.split(',');
|
||||
expect(await swapMain.getAvailableRouters()).to.deep.eq(routers);
|
||||
});
|
||||
|
||||
describe('#WithSwapTests', () => {
|
||||
describe('#transferWithSwapV3Native', () => {
|
||||
it('Should swap native and transfer through Celer', async () => {
|
||||
//const ID = await getID(testMessagesContract, (await swapMain.nonce()).add('1'));
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
const path = await encodePath([wnative.address, transitToken.address]);
|
||||
const _amountIn = ethers.BigNumber.from('20000000000000000000');
|
||||
|
||||
await expect(
|
||||
callTransferWithSwapV3Native(0, path, {
|
||||
amountIn: _amountIn
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainRequestSent');
|
||||
//.withArgs(ID, DST_CHAIN_ID, _amountIn, wnative.address);
|
||||
});
|
||||
it('Should fail transfer through Celer', async () => {
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
|
||||
const path = await encodePath([wnative.address, transitToken.address]);
|
||||
|
||||
await expect(callTransferWithSwapV3Native(0, path)).to.be.revertedWith(
|
||||
'amount too small'
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('#transferWithSwapV3', () => {
|
||||
it('Should swap transitToken and transfer through Сeler', async () => {
|
||||
await swapToken.approve(swapMain.address, ethers.constants.MaxUint256);
|
||||
await swapMain.setMaxTokenAmount(
|
||||
transitToken.address,
|
||||
ethers.utils.parseEther('1000')
|
||||
);
|
||||
// const ID = await getID(testMessagesContract, (await swapMain.nonce()).add('1'));
|
||||
|
||||
const path = await encodePath([swapToken.address, transitToken.address]);
|
||||
|
||||
await expect(callTransferWithSwapV3(0, path)).to.emit(
|
||||
swapMain,
|
||||
'CrossChainRequestSent'
|
||||
);
|
||||
// .withArgs(ID, DST_CHAIN_ID, DEFAULT_AMOUNT_IN, swapToken.address);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#executeMessageWithTransfer', () => {
|
||||
beforeEach('setup for target executions', async () => {
|
||||
// transfer 1000 USDC
|
||||
await transitToken.transfer(swapMain.address, ethers.BigNumber.from('1000000000'));
|
||||
});
|
||||
describe('target swap should emit correct event', async () => {
|
||||
let nonce: BN;
|
||||
let message: string;
|
||||
|
||||
beforeEach('setup before swap', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
const pathV3 = await encodePath([transitToken.address, swapToken.address]);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [ZERO_ADDRESS],
|
||||
pathV3: pathV3,
|
||||
amountOutMinimum: ethers.BigNumber.from('0')
|
||||
});
|
||||
});
|
||||
it('should successfully swap V3 with rubic fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
let tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
// take only platform comission in transit token
|
||||
const { feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000')
|
||||
});
|
||||
// take only platform comission in transit token
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
|
||||
it('should fail swap V3 with rubic fee and transfer tokens', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
const pathV3 = await encodePath([transitToken.address, wnative.address]);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [ZERO_ADDRESS],
|
||||
pathV3: pathV3,
|
||||
amountOutMinimum: ethers.BigNumber.from('2000000000000000000') // 2 eth for 1000$ is minOut, too much
|
||||
});
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
|
||||
let tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
const { RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000')
|
||||
//integrator: INTEGRATOR,
|
||||
});
|
||||
|
||||
// take only platform comission in transit token
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
await expect(Number(integratorCollectedFee1)).to.be.eq(0);
|
||||
});
|
||||
|
||||
describe('target swap should take integrator & rubic fee', async () => {
|
||||
beforeEach('set integrator and rubic fee', async () => {
|
||||
await swapMain.setIntegratorInfo(INTEGRATOR, {
|
||||
isIntegrator: true,
|
||||
tokenFee: '3000',
|
||||
RubicTokenShare: '400000',
|
||||
RubicFixedCryptoShare: '800000',
|
||||
fixedFeeAmount: ethers.utils.parseEther('2')
|
||||
});
|
||||
|
||||
const pathV3 = await encodePath([transitToken.address, swapToken.address]);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [ZERO_ADDRESS],
|
||||
integrator: INTEGRATOR,
|
||||
pathV3: pathV3,
|
||||
amountOutMinimum: ethers.BigNumber.from('0')
|
||||
});
|
||||
});
|
||||
it('should successfully swap V3 with rubic & integrator fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000'
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
|
||||
const { integratorFee, RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000'),
|
||||
integrator: INTEGRATOR
|
||||
});
|
||||
|
||||
await expect(integratorCollectedFee1).to.be.eq(integratorFee);
|
||||
|
||||
// take platform comission in transit token
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
it('should fail swap V3 with rubic & integrator fee', async () => {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
//let tokenBalanceBefore = await transitToken.balanceOf(swapMain.address);
|
||||
|
||||
const pathV3 = await encodePath([transitToken.address, swapToken.address]);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
path: [ZERO_ADDRESS],
|
||||
integrator: INTEGRATOR,
|
||||
pathV3: pathV3,
|
||||
amountOutMinimum: ethers.BigNumber.from('20000000000000000000') // 20 eth for 1000$ is min out
|
||||
});
|
||||
|
||||
await expect(
|
||||
_swapMain.executeMessageWithTransfer(
|
||||
ethers.constants.AddressZero,
|
||||
transitToken.address,
|
||||
ethers.BigNumber.from('1000000000'),
|
||||
DST_CHAIN_ID,
|
||||
message,
|
||||
EXECUTOR_ADDRESS
|
||||
)
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
|
||||
const tokenBalanceAfter = await transitToken.balanceOf(swapMain.address);
|
||||
const collectedFee1 = await swapMain.availableRubicTokenFee(
|
||||
transitToken.address
|
||||
);
|
||||
|
||||
const integratorCollectedFee1 = await swapMain.availableIntegratorTokenFee(
|
||||
transitToken.address,
|
||||
INTEGRATOR
|
||||
);
|
||||
|
||||
const { integratorFee, RubicFee, feeAmount } = await calcTokenFees({
|
||||
bridge: swapMain,
|
||||
amountWithFee: ethers.BigNumber.from('1000000000'),
|
||||
integrator: INTEGRATOR
|
||||
});
|
||||
|
||||
await expect(integratorCollectedFee1).to.be.eq(integratorFee);
|
||||
|
||||
// take platform comission in transit token
|
||||
await expect(collectedFee1).to.be.eq(RubicFee);
|
||||
|
||||
await expect(tokenBalanceAfter).to.be.eq(feeAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
368
Interchain-message/test/RubicFallback.spec.ts
Normal file
368
Interchain-message/test/RubicFallback.spec.ts
Normal file
|
@ -0,0 +1,368 @@
|
|||
import { ethers, network, waffle } from 'hardhat';
|
||||
import { swapContractFixtureInFork } from './shared/fixtures';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
import { RubicRouterV2, TestERC20, TestMessages, WETH9 } from '../typechain';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
DEADLINE,
|
||||
DST_CHAIN_ID,
|
||||
VERSION,
|
||||
ZERO_ADDRESS,
|
||||
DEFAULT_AMOUNT_OUT_MIN,
|
||||
EXECUTOR_ADDRESS,
|
||||
INTEGRATOR,
|
||||
DEFAULT_AMOUNT_IN_USDC
|
||||
} from './shared/consts';
|
||||
import { BigNumber as BN, BigNumberish, ContractTransaction } from 'ethers';
|
||||
const hre = require('hardhat');
|
||||
|
||||
const createFixtureLoader = waffle.createFixtureLoader;
|
||||
|
||||
const envConfig = require('dotenv').config();
|
||||
const {
|
||||
ROUTERS_POLYGON: TEST_ROUTERS,
|
||||
NATIVE_POLYGON: TEST_NATIVE,
|
||||
BUS_POLYGON_MAIN: TEST_BUS
|
||||
} = envConfig.parsed || {};
|
||||
|
||||
describe('RubicFallback', () => {
|
||||
let wallet: Wallet, other: Wallet;
|
||||
let swapToken: TestERC20;
|
||||
let transitToken: TestERC20;
|
||||
let swapMain: RubicRouterV2;
|
||||
let router: string;
|
||||
let routerV3: string;
|
||||
let wnative: WETH9;
|
||||
let chainId: number;
|
||||
|
||||
let testMessagesContract: TestMessages;
|
||||
|
||||
let loadFixture: ReturnType<typeof createFixtureLoader>;
|
||||
|
||||
async function getMessage(
|
||||
messagesContract: TestMessages,
|
||||
_nonce: BigNumberish,
|
||||
dstChainId: BigNumberish,
|
||||
{
|
||||
dex = router,
|
||||
receiverEOA = other.address,
|
||||
integrator = INTEGRATOR,
|
||||
version = VERSION,
|
||||
path = [wnative.address, transitToken.address],
|
||||
pathV3 = '0x',
|
||||
deadline = DEADLINE,
|
||||
amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
_receiver = wallet.address,
|
||||
nativeOut = true
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
return messagesContract.getMessage(
|
||||
{
|
||||
dex,
|
||||
nativeOut,
|
||||
receiverEOA,
|
||||
integrator,
|
||||
version,
|
||||
path,
|
||||
pathV3,
|
||||
deadline,
|
||||
amountOutMinimum
|
||||
},
|
||||
_nonce,
|
||||
dstChainId
|
||||
);
|
||||
}
|
||||
|
||||
async function encodePath(tokens): Promise<string> {
|
||||
return tokens[0] + '000bb8' + tokens[1].slice(2);
|
||||
}
|
||||
|
||||
async function getID(
|
||||
messagesContract: TestMessages,
|
||||
_nonce: BigNumberish,
|
||||
{
|
||||
dex = router,
|
||||
receiverEOA = other.address,
|
||||
integrator = ZERO_ADDRESS,
|
||||
version = VERSION,
|
||||
path = [wnative.address, transitToken.address],
|
||||
pathV3 = '0x',
|
||||
deadline = DEADLINE,
|
||||
amountOutMinimum = DEFAULT_AMOUNT_OUT_MIN,
|
||||
_receiver = wallet.address,
|
||||
nativeOut = false,
|
||||
_srcChainId = chainId,
|
||||
_dstChainId = DST_CHAIN_ID
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
return messagesContract.getID(
|
||||
_srcChainId,
|
||||
_dstChainId,
|
||||
{
|
||||
dex,
|
||||
nativeOut,
|
||||
receiverEOA,
|
||||
integrator,
|
||||
version,
|
||||
path,
|
||||
pathV3,
|
||||
deadline,
|
||||
amountOutMinimum
|
||||
},
|
||||
_nonce
|
||||
);
|
||||
}
|
||||
|
||||
async function callExecuteMessageWithTransferFallback({
|
||||
sender = ZERO_ADDRESS,
|
||||
tokenIn = transitToken.address,
|
||||
amountIn = DEFAULT_AMOUNT_IN_USDC,
|
||||
srcChainID = chainId,
|
||||
message = '0x',
|
||||
executor = EXECUTOR_ADDRESS
|
||||
} = {}): Promise<ContractTransaction> {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
return _swapMain.executeMessageWithTransferFallback(
|
||||
sender,
|
||||
tokenIn,
|
||||
amountIn,
|
||||
srcChainID,
|
||||
message,
|
||||
executor
|
||||
// { value: nativeIn === null ? cryptoFee.add(ethers.utils.parseEther('0.01')) : nativeIn }
|
||||
);
|
||||
}
|
||||
|
||||
async function callExecuteMessageWithTransferRefund({
|
||||
tokenIn = transitToken.address,
|
||||
amountIn = DEFAULT_AMOUNT_IN_USDC,
|
||||
message = '0x',
|
||||
executor = EXECUTOR_ADDRESS
|
||||
} = {}): Promise<ContractTransaction> {
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
const _swapMain = swapMain.connect(bus);
|
||||
|
||||
return _swapMain.executeMessageWithTransferRefund(tokenIn, amountIn, message, executor);
|
||||
}
|
||||
|
||||
before('create fixture loader', async () => {
|
||||
[wallet, other] = await (ethers as any).getSigners();
|
||||
loadFixture = createFixtureLoader([wallet, other]);
|
||||
chainId = (await ethers.provider.getNetwork()).chainId;
|
||||
});
|
||||
|
||||
beforeEach('deploy fixture', async () => {
|
||||
({ swapMain, swapToken, transitToken, wnative, router, routerV3, testMessagesContract } =
|
||||
await loadFixture(swapContractFixtureInFork));
|
||||
});
|
||||
|
||||
it('constructor initializes', async () => {
|
||||
expect(await swapMain.nativeWrap()).to.eq(TEST_NATIVE);
|
||||
expect(await swapMain.messageBus()).to.eq(TEST_BUS);
|
||||
|
||||
const routers = TEST_ROUTERS.split(',');
|
||||
expect(await swapMain.getAvailableRouters()).to.deep.eq(routers);
|
||||
});
|
||||
|
||||
describe('#Fallback and refund tests', () => {
|
||||
describe('#executeMessageWithTransferFallback', () => {
|
||||
beforeEach('Setup for target executions', async () => {
|
||||
// transfer 1000 USDC
|
||||
await transitToken.transfer(swapMain.address, ethers.BigNumber.from('1000000000'));
|
||||
});
|
||||
describe('Fallback should emit correct event in dst chain', async () => {
|
||||
let nonce: BN;
|
||||
let message: string;
|
||||
|
||||
beforeEach('setup before fallback', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: ZERO_ADDRESS,
|
||||
version: 2,
|
||||
path: [transitToken.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('0')
|
||||
});
|
||||
});
|
||||
|
||||
it('Should successfully fallback token with failed bridge', async () => {
|
||||
// const ID = await getID(
|
||||
// testMessagesContract,
|
||||
// (await swapMain.nonce()).add('1'),
|
||||
// {
|
||||
// dex: ZERO_ADDRESS,
|
||||
// version: 2,
|
||||
// path: [transitToken.address],
|
||||
// amountOutMinimum: ethers.BigNumber.from('0')
|
||||
// }
|
||||
// );
|
||||
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
|
||||
await expect(
|
||||
await callExecuteMessageWithTransferFallback({
|
||||
message: message
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
//.withArgs(ID, DEFAULT_AMOUNT_IN_USDC, '3');
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore).to.be.eq(balanceAfter);
|
||||
});
|
||||
|
||||
it('Should successfully fallback token with failed V3', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
const path = await encodePath([transitToken.address, swapToken.address]);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: routerV3,
|
||||
version: 1,
|
||||
pathV3: path
|
||||
});
|
||||
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
|
||||
await expect(
|
||||
await callExecuteMessageWithTransferFallback({
|
||||
message: message
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore).to.be.eq(balanceAfter);
|
||||
});
|
||||
|
||||
it('Should successfully fallback token with failed V2', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: router,
|
||||
version: 0,
|
||||
path: [transitToken.address, swapToken.address]
|
||||
});
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
await expect(
|
||||
await callExecuteMessageWithTransferFallback({
|
||||
message: message
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore).to.be.eq(balanceAfter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#executeMessageWithTransferRefund', () => {
|
||||
beforeEach('Setup for target executions', async () => {
|
||||
// transfer 1000 USDC
|
||||
await transitToken.transfer(swapMain.address, '1000000000');
|
||||
});
|
||||
describe('Refund should emit correct event in src chain', async () => {
|
||||
let nonce: BN;
|
||||
let message: string;
|
||||
|
||||
it('Should successfully refund token with failed bridge', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: ZERO_ADDRESS,
|
||||
version: 2,
|
||||
path: [transitToken.address],
|
||||
amountOutMinimum: ethers.BigNumber.from('0')
|
||||
});
|
||||
|
||||
// const ID = await getID(
|
||||
// testMessagesContract,
|
||||
// (await swapMain.nonce()).add('1'),
|
||||
// {
|
||||
// dex: ZERO_ADDRESS,
|
||||
// version: 2,
|
||||
// path: [transitToken.address],
|
||||
// amountOutMinimum: ethers.BigNumber.from('0')
|
||||
// }
|
||||
// );
|
||||
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
|
||||
await expect(
|
||||
await callExecuteMessageWithTransferRefund({
|
||||
message: message
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
// .withArgs(ID, DEFAULT_AMOUNT_IN_USDC, '3');
|
||||
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore.add(DEFAULT_AMOUNT_IN_USDC)).to.be.eq(
|
||||
balanceAfter
|
||||
);
|
||||
});
|
||||
|
||||
it('Should successfully refund token with failed V3', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
const path = await encodePath([transitToken.address, swapToken.address]);
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: routerV3,
|
||||
version: 1,
|
||||
pathV3: path
|
||||
});
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
|
||||
await expect(
|
||||
await callExecuteMessageWithTransferRefund({
|
||||
message: message
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore.add(DEFAULT_AMOUNT_IN_USDC)).to.be.eq(
|
||||
balanceAfter
|
||||
);
|
||||
});
|
||||
|
||||
it('Should successfully refund token with failed V2', async () => {
|
||||
nonce = (await swapMain.nonce()).add('1');
|
||||
|
||||
message = await getMessage(testMessagesContract, nonce, DST_CHAIN_ID, {
|
||||
dex: router,
|
||||
version: 0,
|
||||
path: [transitToken.address, swapToken.address]
|
||||
});
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
await expect(
|
||||
await callExecuteMessageWithTransferRefund({
|
||||
message: message
|
||||
})
|
||||
).to.emit(swapMain, 'CrossChainProcessed');
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore.add(DEFAULT_AMOUNT_IN_USDC)).to.be.eq(
|
||||
balanceAfter
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
121
Interchain-message/test/RubicSettings.spec.ts
Normal file
121
Interchain-message/test/RubicSettings.spec.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import { ethers, network, waffle } from 'hardhat';
|
||||
import { swapContractFixtureInFork } from './shared/fixtures';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
import { RubicRouterV2, TestERC20, TestMessages, WETH9 } from '../typechain';
|
||||
import { expect } from 'chai';
|
||||
import { DEFAULT_AMOUNT_IN_USDC, DEFAULT_AMOUNT_IN } from './shared/consts';
|
||||
import { BigNumber as BN, BigNumberish, ContractTransaction } from 'ethers';
|
||||
const hre = require('hardhat');
|
||||
|
||||
const createFixtureLoader = waffle.createFixtureLoader;
|
||||
|
||||
const envConfig = require('dotenv').config();
|
||||
const {
|
||||
ROUTERS_POLYGON: TEST_ROUTERS,
|
||||
NATIVE_POLYGON: TEST_NATIVE,
|
||||
BUS_POLYGON_MAIN: TEST_BUS
|
||||
} = envConfig.parsed || {};
|
||||
|
||||
describe('RubicSettings', () => {
|
||||
let wallet: Wallet, other: Wallet;
|
||||
let swapToken: TestERC20;
|
||||
let transitToken: TestERC20;
|
||||
let swapMain: RubicRouterV2;
|
||||
let router: string;
|
||||
let routerV3: string;
|
||||
let wnative: WETH9;
|
||||
// let chainId: number;
|
||||
|
||||
let testMessagesContract: TestMessages;
|
||||
|
||||
let loadFixture: ReturnType<typeof createFixtureLoader>;
|
||||
|
||||
before('create fixture loader', async () => {
|
||||
[wallet, other] = await (ethers as any).getSigners();
|
||||
loadFixture = createFixtureLoader([wallet, other]);
|
||||
// chainId = (await ethers.provider.getNetwork()).chainId;
|
||||
|
||||
await hre.network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [TEST_BUS]
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach('deploy fixture', async () => {
|
||||
({ swapMain, swapToken, transitToken, wnative, router, routerV3, testMessagesContract } =
|
||||
await loadFixture(swapContractFixtureInFork));
|
||||
});
|
||||
|
||||
it('constructor initializes', async () => {
|
||||
expect(await swapMain.nativeWrap()).to.eq(TEST_NATIVE);
|
||||
expect(await swapMain.messageBus()).to.eq(TEST_BUS);
|
||||
|
||||
const routers = TEST_ROUTERS.split(',');
|
||||
expect(await swapMain.getAvailableRouters()).to.deep.eq(routers);
|
||||
});
|
||||
|
||||
describe('#Contract utility tests', () => {
|
||||
describe('#sweepTokens', () => {
|
||||
beforeEach('Setup for target executions', async () => {
|
||||
// transfer 1000 USDC
|
||||
await transitToken.transfer(swapMain.address, ethers.BigNumber.from('1000000000'));
|
||||
|
||||
// create bus and send 1 weth to router
|
||||
const bus = await ethers.getSigner(TEST_BUS);
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
bus.address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
const abiCoder = ethers.utils.defaultAbiCoder;
|
||||
|
||||
const storageBalancePositionWeth = ethers.utils.keccak256(
|
||||
abiCoder.encode(['address'], [bus.address]) +
|
||||
abiCoder.encode(['uint256'], [3]).slice(2, 66)
|
||||
);
|
||||
|
||||
await network.provider.send('hardhat_setStorageAt', [
|
||||
wnative.address,
|
||||
storageBalancePositionWeth,
|
||||
abiCoder.encode(['uint256'], [ethers.utils.parseEther('100000')])
|
||||
]);
|
||||
|
||||
await wnative.connect(bus).transfer(swapMain.address, ethers.utils.parseEther('1'));
|
||||
|
||||
await wnative.connect(bus).transfer(swapMain.address, ethers.utils.parseEther('1'));
|
||||
});
|
||||
|
||||
it('Should successfully sweep tokens', async () => {
|
||||
const balanceBefore = await transitToken.balanceOf(wallet.address);
|
||||
|
||||
await swapMain.sweepTokens(transitToken.address, DEFAULT_AMOUNT_IN_USDC);
|
||||
|
||||
const balanceAfter = await transitToken.balanceOf(wallet.address);
|
||||
await expect(balanceBefore.add(DEFAULT_AMOUNT_IN_USDC)).to.be.eq(balanceAfter);
|
||||
});
|
||||
|
||||
it('Should successfully sweep native', async () => {
|
||||
const balanceBefore = await wnative.balanceOf(swapMain.address);
|
||||
|
||||
await swapMain.sweepTokens(wnative.address, DEFAULT_AMOUNT_IN);
|
||||
|
||||
const balanceAfter = await wnative.balanceOf(swapMain.address);
|
||||
await expect(balanceBefore.sub(DEFAULT_AMOUNT_IN)).to.be.eq(balanceAfter);
|
||||
});
|
||||
|
||||
it('Should successfully fail sweepTokens', async () => {
|
||||
await expect(
|
||||
swapMain
|
||||
.connect(other)
|
||||
.sweepTokens(transitToken.address, DEFAULT_AMOUNT_IN_USDC)
|
||||
).to.be.revertedWith('BridgeBase: not a manager');
|
||||
});
|
||||
|
||||
it('Should successfully fail sweepTokens', async () => {
|
||||
await expect(
|
||||
swapMain.connect(other).sweepTokens(wnative.address, DEFAULT_AMOUNT_IN)
|
||||
).to.be.revertedWith('BridgeBase: not a manager');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
17
Interchain-message/test/shared/consts.ts
Normal file
17
Interchain-message/test/shared/consts.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ethers } from 'hardhat';
|
||||
|
||||
export const DEADLINE = '9999999999999999';
|
||||
export const DST_CHAIN_ID = 5;
|
||||
export const VERSION_V2 = 0;
|
||||
export const VERSION_V3 = 1;
|
||||
export const VERSION = 2;
|
||||
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
export const INTEGRATOR = '0x23a05b3673DFBf0d1Ce9Bfa6407eD0DbD068aF2D'; // random address
|
||||
export const feeDecimals = 10 ** 6;
|
||||
export const FIXED_CRYPTO_FEE = ethers.utils.parseEther('1');
|
||||
export const EXECUTOR_ADDRESS = '0x503CEF47CE5e37AA62544A363BEF3C9b62d42116';
|
||||
export const MESSAGE_BUS_FEE = ethers.utils.parseEther('1').div('100');
|
||||
export const DEFAULT_AMOUNT_IN = ethers.utils.parseEther('1');
|
||||
export const DEFAULT_AMOUNT_OUT_MIN = ethers.utils.parseEther('1');
|
||||
export const DEFAULT_AMOUNT_IN_USDC = ethers.BigNumber.from('100000000');
|
||||
export const DEFAULT_AMOUNT_IN_SWAPTOKEN = ethers.BigNumber.from('100000000000000000000');
|
123
Interchain-message/test/shared/fixtures.ts
Normal file
123
Interchain-message/test/shared/fixtures.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
import { Fixture } from 'ethereum-waffle';
|
||||
import { ethers, network } from 'hardhat';
|
||||
import { TestERC20 } from '../../typechain';
|
||||
import { RubicRouterV2 } from '../../typechain';
|
||||
import { WETH9 } from '../../typechain';
|
||||
import { TestMessages } from '../../typechain';
|
||||
import { MessageBusSender } from '../../typechain';
|
||||
import TokenJSON from '../../artifacts/contracts/test/TestERC20.sol/TestERC20.json';
|
||||
import WETHJSON from '../../artifacts/contracts/test/WETH9.sol/WETH9.json';
|
||||
import MessageBusJSON from '../../artifacts/contracts/test/MessageBusSender.sol/MessageBusSender.json';
|
||||
import { expect } from 'chai';
|
||||
import { DST_CHAIN_ID, EXECUTOR_ADDRESS, FIXED_CRYPTO_FEE } from './consts';
|
||||
|
||||
const envConfig = require('dotenv').config();
|
||||
const {
|
||||
ROUTERS_POLYGON: TEST_ROUTERS,
|
||||
NATIVE_POLYGON: TEST_NATIVE,
|
||||
BUS_POLYGON_MAIN: TEST_BUS,
|
||||
TRANSIT_POLYGON: TEST_TRANSIT,
|
||||
SWAP_TOKEN_POLYGON: TEST_SWAP_TOKEN
|
||||
} = envConfig.parsed || {};
|
||||
|
||||
interface SwapContractFixture {
|
||||
swapMain: RubicRouterV2;
|
||||
swapToken: TestERC20;
|
||||
transitToken: TestERC20;
|
||||
wnative: WETH9;
|
||||
router: string;
|
||||
routerV3: string;
|
||||
testMessagesContract: TestMessages;
|
||||
messageBus: MessageBusSender;
|
||||
}
|
||||
|
||||
export const swapContractFixtureInFork: Fixture<SwapContractFixture> = async function (
|
||||
wallets
|
||||
): Promise<SwapContractFixture> {
|
||||
const tokenFactory = ethers.ContractFactory.fromSolidity(TokenJSON);
|
||||
let transitToken = tokenFactory.attach(TEST_TRANSIT) as TestERC20;
|
||||
transitToken = transitToken.connect(wallets[0]);
|
||||
|
||||
const swapTokenFactory = ethers.ContractFactory.fromSolidity(TokenJSON);
|
||||
let swapToken = swapTokenFactory.attach(TEST_SWAP_TOKEN) as TestERC20;
|
||||
swapToken = swapToken.connect(wallets[0]);
|
||||
|
||||
const wnativeFactory = ethers.ContractFactory.fromSolidity(WETHJSON);
|
||||
let wnative = wnativeFactory.attach(TEST_NATIVE) as WETH9;
|
||||
wnative = wnative.connect(wallets[0]);
|
||||
|
||||
const RubicRouterV2Factory = await ethers.getContractFactory('RubicRouterV2');
|
||||
|
||||
const availableRouters = TEST_ROUTERS.split(',');
|
||||
const router = availableRouters[0];
|
||||
const routerV3 = availableRouters[1];
|
||||
|
||||
const swapMain = (await RubicRouterV2Factory.deploy(
|
||||
FIXED_CRYPTO_FEE,
|
||||
'6000',
|
||||
availableRouters,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
EXECUTOR_ADDRESS,
|
||||
TEST_BUS,
|
||||
TEST_NATIVE
|
||||
)) as RubicRouterV2;
|
||||
|
||||
await swapMain.setGasFeeOfBlockchain(DST_CHAIN_ID, ethers.utils.parseEther('2').div('100'));
|
||||
|
||||
const testMessagesFactory = await ethers.getContractFactory('TestMessages');
|
||||
const testMessagesContract = (await testMessagesFactory.deploy()) as TestMessages;
|
||||
|
||||
const messageBusFactory = ethers.ContractFactory.fromSolidity(MessageBusJSON);
|
||||
let messageBus = messageBusFactory.attach(TEST_BUS) as MessageBusSender;
|
||||
messageBus = messageBus.connect(wallets[0]);
|
||||
|
||||
const abiCoder = ethers.utils.defaultAbiCoder;
|
||||
|
||||
const storageBalancePositionTransit = ethers.utils.keccak256(
|
||||
abiCoder.encode(['address'], [wallets[0].address]) +
|
||||
abiCoder.encode(['uint256'], [0]).slice(2, 66)
|
||||
);
|
||||
|
||||
const storageBalancePositionSwap = ethers.utils.keccak256(
|
||||
abiCoder.encode(['address'], [wallets[0].address]) +
|
||||
abiCoder.encode(['uint256'], [0]).slice(2, 66)
|
||||
);
|
||||
|
||||
await network.provider.send('hardhat_setStorageAt', [
|
||||
transitToken.address,
|
||||
storageBalancePositionTransit,
|
||||
abiCoder.encode(['uint256'], [ethers.utils.parseEther('100000')])
|
||||
]);
|
||||
|
||||
await network.provider.send('hardhat_setStorageAt', [
|
||||
swapToken.address,
|
||||
storageBalancePositionSwap,
|
||||
abiCoder.encode(['uint256'], [ethers.utils.parseEther('100000')])
|
||||
]);
|
||||
|
||||
expect(await transitToken.balanceOf(wallets[0].address)).to.eq(
|
||||
ethers.utils.parseEther('100000')
|
||||
);
|
||||
|
||||
expect(await swapToken.balanceOf(wallets[0].address)).to.eq(ethers.utils.parseEther('100000'));
|
||||
|
||||
await network.provider.send('hardhat_setBalance', [
|
||||
wallets[0].address,
|
||||
'0x152D02C7E14AF6800000' // 100000 eth
|
||||
]);
|
||||
|
||||
return {
|
||||
swapMain,
|
||||
swapToken,
|
||||
transitToken,
|
||||
wnative,
|
||||
router,
|
||||
routerV3,
|
||||
testMessagesContract,
|
||||
messageBus
|
||||
};
|
||||
};
|
63
Interchain-message/test/shared/utils.ts
Normal file
63
Interchain-message/test/shared/utils.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { IUniswapV2Router02 } from '../../typechain';
|
||||
import { IUniswapRouterV3 } from '../../typechain';
|
||||
import UniV2JSON from '@uniswap/v2-periphery/build/UniswapV2Router02.json';
|
||||
import UniV3JSON from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json';
|
||||
import { ethers } from 'hardhat';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
|
||||
export const getRouterV2 = async function (
|
||||
wallet: Wallet,
|
||||
routerAddress: string
|
||||
): Promise<IUniswapV2Router02> {
|
||||
const routerFactory = ethers.ContractFactory.fromSolidity(UniV2JSON);
|
||||
let router = routerFactory.attach(routerAddress) as IUniswapV2Router02;
|
||||
router = router.connect(wallet);
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
// not used
|
||||
// export const createPoolV2 = async function (
|
||||
// wallet: Wallet,
|
||||
// routerAddress: string,
|
||||
// token: string,
|
||||
// tokenAmount = ethers.utils.parseEther('100')
|
||||
// ): Promise<IUniswapV2Router02> {
|
||||
// const router = await getRouterV2(wallet, routerAddress);
|
||||
//
|
||||
// await router.addLiquidityETH(
|
||||
// token,
|
||||
// tokenAmount,
|
||||
// tokenAmount,
|
||||
// ethers.utils.parseEther('100'),
|
||||
// await router.signer.getAddress(),
|
||||
// DEADLINE,
|
||||
// { value: ethers.utils.parseEther('100') }
|
||||
// );
|
||||
//
|
||||
// return router;
|
||||
// };
|
||||
|
||||
export const getRouterV3 = async function (
|
||||
wallet: Wallet,
|
||||
routerAddressV3: string
|
||||
): Promise<IUniswapRouterV3> {
|
||||
const routerFactoryV3 = ethers.ContractFactory.fromSolidity(UniV3JSON);
|
||||
let routerV3 = routerFactoryV3.attach(routerAddressV3) as IUniswapRouterV3;
|
||||
routerV3 = routerV3.connect(wallet);
|
||||
|
||||
return routerV3;
|
||||
};
|
||||
|
||||
export function hexStringToByteArray(hexString) {
|
||||
if (hexString.length % 2 !== 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw 'Must have an even number of hex digits to convert to bytes';
|
||||
}
|
||||
var numBytes = hexString.length / 2;
|
||||
var byteArray = new Uint8Array(numBytes);
|
||||
for (var i = 0; i < numBytes; i++) {
|
||||
byteArray[i] = parseInt(hexString.substr(i * 2, 2), 16);
|
||||
}
|
||||
return byteArray;
|
||||
}
|
15
Interchain-message/tsconfig.json
Normal file
15
Interchain-message/tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2018",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "dist",
|
||||
"typeRoots": ["./typechain", "./node_modules/@types", "./types"],
|
||||
"types": ["@nomiclabs/hardhat-ethers", "@nomiclabs/hardhat-waffle"],
|
||||
"noImplicitAny": false
|
||||
},
|
||||
"include": ["./test"],
|
||||
"files": ["./hardhat.config.ts"]
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user