Pnuttaste/Interchain-message/test/RubicCrossChainV3.spec.ts

548 lines
21 KiB
TypeScript
Raw Normal View History

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 21:32:00 +00:00
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);
});
});
});
});
});
});