// test/integration/full-protocol.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { loadFixture, time } = require("@nomicfoundation/hardhat-network-helpers");
describe("Full Protocol Integration", function () {
async function deployFullProtocolFixture() {
const [owner, user1, user2, relayer, feeRecipient] = await ethers.getSigners();
// Deploy all contracts
const IU2U = await ethers.getContractFactory("IU2U");
const iu2u = await IU2U.deploy();
const MockGateway = await ethers.getContractFactory("MockAxelarGateway");
const gateway = await MockGateway.deploy();
const MockGasService = await ethers.getContractFactory("MockAxelarGasService");
const gasService = await MockGasService.deploy();
const MockOracle = await ethers.getContractFactory("MockDIAOracle");
const oracle = await MockOracle.deploy();
const MockRouter = await ethers.getContractFactory("MockRouter");
const router = await MockRouter.deploy();
const CrossChainAggregator = await ethers.getContractFactory("CrossChainAggregator");
const aggregator = await CrossChainAggregator.deploy(
gateway.address,
gasService.address,
oracle.address,
[router.address],
[0]
);
const MetaTxGasCreditVault = await ethers.getContractFactory("MetaTxGasCreditVault");
const gasCreditVault = await MetaTxGasCreditVault.deploy(iu2u.address, oracle.address);
const MetaTxGateway = await ethers.getContractFactory("MetaTxGateway");
const metaTxGateway = await MetaTxGateway.deploy(
aggregator.address,
gasCreditVault.address,
gateway.address,
gasService.address
);
const IU2UExecutable = await ethers.getContractFactory("IU2UExecutable");
const executable = await IU2UExecutable.deploy(
gateway.address,
gasService.address,
aggregator.address
);
// Setup tokens
const MockERC20 = await ethers.getContractFactory("MockERC20");
const usdc = await MockERC20.deploy("USD Coin", "USDC", 6);
const usdt = await MockERC20.deploy("Tether USD", "USDT", 6);
// Initial setup
await iu2u.transfer(user1.address, ethers.utils.parseEther("1000"));
await usdc.mint(user1.address, "1000000000"); // 1000 USDC (6 decimals)
await usdt.mint(user1.address, "1000000000"); // 1000 USDT (6 decimals)
// Configure contracts
await aggregator.setFeeRecipient(feeRecipient.address);
await aggregator.setProtocolFee(30);
await metaTxGateway.setRelayerStatus(relayer.address, true);
await oracle.setPrice("gwei", ethers.utils.parseUnits("20", "gwei"));
return {
iu2u,
aggregator,
metaTxGateway,
gasCreditVault,
executable,
usdc,
usdt,
oracle,
owner,
user1,
user2,
relayer,
feeRecipient
};
}
describe("Basic Swap Flow", function () {
it("Should complete full swap workflow", async function () {
const { aggregator, usdc, usdt, user1 } = await loadFixture(deployFullProtocolFixture);
const amountIn = "100000000"; // 100 USDC
const minAmountOut = "95000000"; // 95 USDT
// 1. User approves tokens
await usdc.connect(user1).approve(aggregator.address, amountIn);
// 2. Get quote
const [amountOut, gasEstimate] = await aggregator.getQuote(
usdc.address,
usdt.address,
amountIn,
0
);
expect(amountOut).to.be.gte(minAmountOut);
// 3. Execute swap
const swapParams = {
tokenIn: usdc.address,
tokenOut: usdt.address,
amountIn: amountIn,
minAmountOut: minAmountOut,
routerType: 0,
to: user1.address,
deadline: Math.floor(Date.now() / 1000) + 300,
swapData: "0x"
};
const initialUSDCBalance = await usdc.balanceOf(user1.address);
const initialUSDTBalance = await usdt.balanceOf(user1.address);
await aggregator.connect(user1).executeSwap(swapParams);
const finalUSDCBalance = await usdc.balanceOf(user1.address);
const finalUSDTBalance = await usdt.balanceOf(user1.address);
expect(finalUSDCBalance).to.equal(initialUSDCBalance.sub(amountIn));
expect(finalUSDTBalance).to.be.gt(initialUSDTBalance);
});
});
describe("Meta-Transaction Flow", function () {
it("Should execute gasless transaction", async function () {
const { aggregator, metaTxGateway, gasCreditVault, iu2u, usdc, usdt, user1, relayer } =
await loadFixture(deployFullProtocolFixture);
// 1. User deposits gas credits
const gasDepositAmount = ethers.utils.parseEther("100");
await iu2u.connect(user1).approve(gasCreditVault.address, gasDepositAmount);
await gasCreditVault.connect(user1).depositGasCredit(gasDepositAmount);
// 2. Prepare meta-transaction
const swapAmount = "50000000"; // 50 USDC
await usdc.connect(user1).approve(aggregator.address, swapAmount);
const metaTxParams = {
user: user1.address,
tokenIn: usdc.address,
tokenOut: usdt.address,
amountIn: swapAmount,
minAmountOut: "47500000", // 47.5 USDT
routerType: 0,
deadline: Math.floor(Date.now() / 1000) + 300,
swapData: "0x"
};
// 3. Relayer executes meta-transaction
const initialUserBalance = await usdt.balanceOf(user1.address);
const initialGasCredit = await gasCreditVault.getGasCredit(user1.address);
await metaTxGateway.connect(relayer).executeMetaSwap(metaTxParams);
const finalUserBalance = await usdt.balanceOf(user1.address);
const finalGasCredit = await gasCreditVault.getGasCredit(user1.address);
expect(finalUserBalance).to.be.gt(initialUserBalance);
expect(finalGasCredit).to.be.lt(initialGasCredit);
});
});
describe("Cross-Chain Integration", function () {
it("Should handle cross-chain message execution", async function () {
const { executable, aggregator, usdc, usdt, user1 } = await loadFixture(deployFullProtocolFixture);
// Simulate cross-chain message execution
const sourceChain = "ethereum";
const sourceAddress = "0x1234567890123456789012345678901234567890";
const swapData = ethers.utils.defaultAbiCoder.encode(
["address", "address", "uint256", "uint256", "uint8", "address"],
[usdc.address, usdt.address, "100000000", "95000000", 0, user1.address]
);
// Mint tokens to executable contract for the swap
await usdc.mint(executable.address, "100000000");
const initialBalance = await usdt.balanceOf(user1.address);
// Execute cross-chain swap
await executable._testExecute(sourceChain, sourceAddress, swapData);
const finalBalance = await usdt.balanceOf(user1.address);
expect(finalBalance).to.be.gt(initialBalance);
});
});
describe("Fee Distribution", function () {
it("Should correctly distribute protocol fees", async function () {
const { aggregator, usdc, usdt, user1, feeRecipient } = await loadFixture(deployFullProtocolFixture);
const amountIn = "1000000000"; // 1000 USDC
await usdc.connect(user1).approve(aggregator.address, amountIn);
const swapParams = {
tokenIn: usdc.address,
tokenOut: usdt.address,
amountIn: amountIn,
minAmountOut: "950000000",
routerType: 0,
to: user1.address,
deadline: Math.floor(Date.now() / 1000) + 300,
swapData: "0x"
};
const initialFeeBalance = await usdt.balanceOf(feeRecipient.address);
await aggregator.connect(user1).executeSwap(swapParams);
const finalFeeBalance = await usdt.balanceOf(feeRecipient.address);
// Check that fees were collected (0.3% of output)
const expectedFee = ethers.BigNumber.from("950000000").mul(30).div(10000);
expect(finalFeeBalance.sub(initialFeeBalance)).to.be.closeTo(expectedFee, "1000000");
});
});
describe("Emergency Scenarios", function () {
it("Should handle contract pause", async function () {
const { aggregator, usdc, usdt, user1, owner } = await loadFixture(deployFullProtocolFixture);
// Pause contract
await aggregator.connect(owner).pause();
const swapParams = {
tokenIn: usdc.address,
tokenOut: usdt.address,
amountIn: "100000000",
minAmountOut: "95000000",
routerType: 0,
to: user1.address,
deadline: Math.floor(Date.now() / 1000) + 300,
swapData: "0x"
};
await expect(
aggregator.connect(user1).executeSwap(swapParams)
).to.be.revertedWith("Pausable: paused");
// Unpause and try again
await aggregator.connect(owner).unpause();
await usdc.connect(user1).approve(aggregator.address, "100000000");
await expect(
aggregator.connect(user1).executeSwap(swapParams)
).to.emit(aggregator, "SwapExecuted");
});
});
});