feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
This commit is contained in:
@@ -1,5 +1,25 @@
|
||||
const { expect } = require("chai");
|
||||
const { ethers } = require("hardhat");
|
||||
const { ethers, network } = require("hardhat");
|
||||
const path = require("path");
|
||||
|
||||
const mockRouterArtifact = require(path.join(
|
||||
__dirname,
|
||||
"../../out/CCIPWETH9Bridge.t.sol/MockCCIPRouter.json"
|
||||
));
|
||||
|
||||
async function expectEvent(txPromise, contract, eventName) {
|
||||
const tx = await txPromise;
|
||||
const receipt = await tx.wait();
|
||||
const foundEvent = receipt.logs.some((log) => {
|
||||
try {
|
||||
return contract.interface.parseLog(log)?.name === eventName;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
expect(foundEvent).to.equal(true);
|
||||
}
|
||||
|
||||
describe("CCIP Integration", function () {
|
||||
let ccipLogger, ccipReporter;
|
||||
@@ -9,9 +29,13 @@ describe("CCIP Integration", function () {
|
||||
beforeEach(async function () {
|
||||
[owner, relayer] = await ethers.getSigners();
|
||||
|
||||
// Deploy mock routers for testing
|
||||
// In production, these would be the actual Chainlink CCIP routers
|
||||
const MockRouter = await ethers.getContractFactory("MockCCIPRouter");
|
||||
// Reuse the Foundry-built mock router so this suite doesn't depend on a
|
||||
// separate Hardhat-only mock artifact.
|
||||
const MockRouter = new ethers.ContractFactory(
|
||||
mockRouterArtifact.abi,
|
||||
mockRouterArtifact.bytecode.object,
|
||||
owner
|
||||
);
|
||||
mockRouter = await MockRouter.deploy();
|
||||
await mockRouter.waitForDeployment();
|
||||
|
||||
@@ -44,11 +68,13 @@ describe("CCIP Integration", function () {
|
||||
const toAddr = relayer.address;
|
||||
const value = ethers.parseEther("1.0");
|
||||
|
||||
await expect(
|
||||
await expectEvent(
|
||||
ccipReporter.reportTx(txHash, fromAddr, toAddr, value, "0x", {
|
||||
value: ethers.parseEther("0.01"),
|
||||
})
|
||||
).to.emit(ccipReporter, "SingleTxReported");
|
||||
}),
|
||||
ccipReporter,
|
||||
"SingleTxReported"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should report a batch of transactions", async function () {
|
||||
@@ -61,11 +87,13 @@ describe("CCIP Integration", function () {
|
||||
const tos = [relayer.address, owner.address];
|
||||
const values = [ethers.parseEther("1.0"), ethers.parseEther("2.0")];
|
||||
|
||||
await expect(
|
||||
await expectEvent(
|
||||
ccipReporter.reportBatch(batchId, txHashes, froms, tos, values, "0x", {
|
||||
value: ethers.parseEther("0.01"),
|
||||
})
|
||||
).to.emit(ccipReporter, "BatchReported");
|
||||
}),
|
||||
ccipReporter,
|
||||
"BatchReported"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should estimate fee correctly", async function () {
|
||||
@@ -78,11 +106,10 @@ describe("CCIP Integration", function () {
|
||||
txHashes,
|
||||
froms,
|
||||
tos,
|
||||
values,
|
||||
"0x"
|
||||
values
|
||||
);
|
||||
|
||||
expect(fee).to.be.gt(0);
|
||||
expect(fee > 0n).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -103,14 +130,33 @@ describe("CCIP Integration", function () {
|
||||
const message = {
|
||||
messageId: ethers.keccak256(ethers.toUtf8Bytes("test-message")),
|
||||
sourceChainSelector: "0x000000000000008a",
|
||||
sender: await ccipReporter.getAddress(),
|
||||
sender: ethers.zeroPadValue(await ccipReporter.getAddress(), 32),
|
||||
data: payload,
|
||||
destTokenAmounts: [],
|
||||
tokenAmounts: [],
|
||||
};
|
||||
|
||||
await expect(
|
||||
mockRouter.deliverMessage(await ccipLogger.getAddress(), message)
|
||||
).to.emit(ccipLogger, "RemoteBatchLogged");
|
||||
const routerAddress = await mockRouter.getAddress();
|
||||
await network.provider.request({
|
||||
method: "hardhat_impersonateAccount",
|
||||
params: [routerAddress],
|
||||
});
|
||||
await network.provider.send("hardhat_setBalance", [
|
||||
routerAddress,
|
||||
"0x56BC75E2D63100000",
|
||||
]);
|
||||
|
||||
const routerSigner = await ethers.getSigner(routerAddress);
|
||||
|
||||
await expectEvent(
|
||||
ccipLogger.connect(routerSigner).ccipReceive(message),
|
||||
ccipLogger,
|
||||
"RemoteBatchLogged"
|
||||
);
|
||||
|
||||
await network.provider.request({
|
||||
method: "hardhat_stopImpersonatingAccount",
|
||||
params: [routerAddress],
|
||||
});
|
||||
});
|
||||
|
||||
it("Should prevent replay attacks", async function () {
|
||||
@@ -128,13 +174,25 @@ describe("CCIP Integration", function () {
|
||||
const message = {
|
||||
messageId: ethers.keccak256(ethers.toUtf8Bytes("test-message-1")),
|
||||
sourceChainSelector: "0x000000000000008a",
|
||||
sender: await ccipReporter.getAddress(),
|
||||
sender: ethers.zeroPadValue(await ccipReporter.getAddress(), 32),
|
||||
data: payload,
|
||||
destTokenAmounts: [],
|
||||
tokenAmounts: [],
|
||||
};
|
||||
|
||||
const routerAddress = await mockRouter.getAddress();
|
||||
await network.provider.request({
|
||||
method: "hardhat_impersonateAccount",
|
||||
params: [routerAddress],
|
||||
});
|
||||
await network.provider.send("hardhat_setBalance", [
|
||||
routerAddress,
|
||||
"0x56BC75E2D63100000",
|
||||
]);
|
||||
|
||||
const routerSigner = await ethers.getSigner(routerAddress);
|
||||
|
||||
// First delivery should succeed
|
||||
await mockRouter.deliverMessage(await ccipLogger.getAddress(), message);
|
||||
await ccipLogger.connect(routerSigner).ccipReceive(message);
|
||||
|
||||
// Second delivery with same batchId should fail
|
||||
const message2 = {
|
||||
@@ -142,10 +200,19 @@ describe("CCIP Integration", function () {
|
||||
messageId: ethers.keccak256(ethers.toUtf8Bytes("test-message-2")),
|
||||
};
|
||||
|
||||
await expect(
|
||||
mockRouter.deliverMessage(await ccipLogger.getAddress(), message2)
|
||||
).to.be.revertedWith("CCIPLogger: batch already processed");
|
||||
let reverted = false;
|
||||
try {
|
||||
await ccipLogger.connect(routerSigner).ccipReceive(message2);
|
||||
} catch (error) {
|
||||
reverted = error.message.includes("CCIPLogger: batch already processed");
|
||||
}
|
||||
|
||||
expect(reverted).to.equal(true);
|
||||
|
||||
await network.provider.request({
|
||||
method: "hardhat_stopImpersonatingAccount",
|
||||
params: [routerAddress],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user