Pnuttaste/Interchain-message/test/RubicCrossChainV2.spec.ts
Mista J df8039f494 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
2024-07-09 17:32:00 -04:00

612 lines
24 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
});
});
});
});
});
});