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