Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
178 lines
7.7 KiB
JavaScript
178 lines
7.7 KiB
JavaScript
// Hardhat tests for AereDestinationSettler (ERC-7683 IDestinationSettler).
|
|
//
|
|
// The DESTINATION / filling side of AERE's cross-chain intents. A solver calls
|
|
// fill(orderId, originData, fillerData) to deliver the order's promised output to
|
|
// the recipient on AERE; the settler records the fill and emits Filled so the origin
|
|
// chain can repay the solver behind a QBFT-finality proof.
|
|
//
|
|
// Cases:
|
|
// - valid fill: delivers output to recipient, records the fill, emits Filled,
|
|
// exposes the record + fillCommitment
|
|
// - duplicate fill reverts (AlreadyFilled)
|
|
// - mismatched-output fill reverts (OutputMismatch)
|
|
// - zero-recipient reverts (ZeroRecipient)
|
|
//
|
|
// Run: npx hardhat test test/destination-settler.test.js
|
|
|
|
const { expect } = require("chai");
|
|
const { ethers } = require("hardhat");
|
|
|
|
const ONE = 10n ** 18n;
|
|
const AERE_CHAIN_ID = 2800n;
|
|
|
|
function b32(addr) {
|
|
return ethers.zeroPadValue(addr, 32);
|
|
}
|
|
|
|
// originData: the origin settler's declared output leg (matches AereSpokePool).
|
|
function encodeOriginData(orderId, outputToken, outputAmount, recipient32) {
|
|
return ethers.AbiCoder.defaultAbiCoder().encode(
|
|
["bytes32", "address", "uint256", "bytes32"],
|
|
[orderId, outputToken, outputAmount, recipient32]
|
|
);
|
|
}
|
|
|
|
// fillerData: the solver's actual delivery + origin-chain repayment address.
|
|
function encodeFillerData(deliveredToken, deliveredAmount, deliveredRecipient, repaymentAddress32) {
|
|
return ethers.AbiCoder.defaultAbiCoder().encode(
|
|
["address", "uint256", "address", "bytes32"],
|
|
[deliveredToken, deliveredAmount, deliveredRecipient, repaymentAddress32]
|
|
);
|
|
}
|
|
|
|
async function deployFixture() {
|
|
const [deployer, solver, recipient, other] = await ethers.getSigners();
|
|
|
|
// Output token the solver delivers on AERE.
|
|
const Token = await ethers.getContractFactory("MockERC20Lending");
|
|
const token = await Token.deploy("USD Coin (bridged)", "USDC.e", 6);
|
|
await token.waitForDeployment();
|
|
|
|
const Settler = await ethers.getContractFactory(
|
|
"contracts/intents/AereDestinationSettler.sol:AereDestinationSettler"
|
|
);
|
|
const settler = await Settler.deploy();
|
|
await settler.waitForDeployment();
|
|
|
|
// Fund the solver with output tokens and approve the settler to pull them.
|
|
const funded = 1_000_000n * ONE;
|
|
await token.mint(solver.address, funded);
|
|
await token.connect(solver).approve(await settler.getAddress(), funded);
|
|
|
|
return { deployer, solver, recipient, other, token, settler };
|
|
}
|
|
|
|
describe("AereDestinationSettler - ERC-7683 IDestinationSettler", function () {
|
|
it("valid fill: delivers output to recipient, records the fill, emits Filled, exposes commitment", async function () {
|
|
const { solver, recipient, token, settler } = await deployFixture();
|
|
|
|
const orderId = ethers.id("order-1");
|
|
const outputAmount = 250n * ONE;
|
|
const tokenAddr = await token.getAddress();
|
|
const recipient32 = b32(recipient.address);
|
|
const repayment32 = b32(solver.address);
|
|
|
|
const originData = encodeOriginData(orderId, tokenAddr, outputAmount, recipient32);
|
|
const fillerData = encodeFillerData(tokenAddr, outputAmount, recipient.address, repayment32);
|
|
|
|
const recipBefore = await token.balanceOf(recipient.address);
|
|
|
|
await expect(settler.connect(solver).fill(orderId, originData, fillerData))
|
|
.to.emit(settler, "Filled");
|
|
|
|
// Recipient received exactly the delivered output; settler custodies nothing.
|
|
expect(await token.balanceOf(recipient.address) - recipBefore).to.equal(outputAmount);
|
|
expect(await token.balanceOf(await settler.getAddress())).to.equal(0n);
|
|
|
|
// Fill recorded and readable.
|
|
expect(await settler.isFilled(orderId)).to.equal(true);
|
|
const rec = await settler.getFill(orderId);
|
|
expect(rec.filler).to.equal(solver.address);
|
|
expect(rec.outputToken).to.equal(tokenAddr);
|
|
expect(rec.outputAmount).to.equal(outputAmount);
|
|
expect(rec.recipient).to.equal(recipient.address);
|
|
expect(rec.repaymentAddress).to.equal(repayment32);
|
|
expect(await settler.fillCount()).to.equal(1n);
|
|
|
|
// fillCommitment binds the exact fields the origin-side verifier re-derives.
|
|
const expectedCommitment = ethers.keccak256(
|
|
ethers.AbiCoder.defaultAbiCoder().encode(
|
|
["uint256", "address", "bytes32", "address", "address", "uint256", "address", "bytes32"],
|
|
[
|
|
AERE_CHAIN_ID,
|
|
await settler.getAddress(),
|
|
orderId,
|
|
solver.address,
|
|
tokenAddr,
|
|
outputAmount,
|
|
recipient.address,
|
|
repayment32,
|
|
]
|
|
)
|
|
);
|
|
expect(await settler.fillCommitment(orderId)).to.equal(expectedCommitment);
|
|
});
|
|
|
|
it("duplicate fill reverts (AlreadyFilled)", async function () {
|
|
const { solver, recipient, token, settler } = await deployFixture();
|
|
|
|
const orderId = ethers.id("order-dup");
|
|
const outputAmount = 100n * ONE;
|
|
const tokenAddr = await token.getAddress();
|
|
const originData = encodeOriginData(orderId, tokenAddr, outputAmount, b32(recipient.address));
|
|
const fillerData = encodeFillerData(tokenAddr, outputAmount, recipient.address, b32(solver.address));
|
|
|
|
await settler.connect(solver).fill(orderId, originData, fillerData);
|
|
|
|
await expect(settler.connect(solver).fill(orderId, originData, fillerData))
|
|
.to.be.revertedWithCustomError(settler, "AlreadyFilled")
|
|
.withArgs(orderId);
|
|
});
|
|
|
|
it("mismatched-output fill reverts (OutputMismatch)", async function () {
|
|
const { solver, recipient, other, token, settler } = await deployFixture();
|
|
|
|
const orderId = ethers.id("order-mismatch");
|
|
const outputAmount = 100n * ONE;
|
|
const tokenAddr = await token.getAddress();
|
|
const originData = encodeOriginData(orderId, tokenAddr, outputAmount, b32(recipient.address));
|
|
|
|
// Short delivery (less than the declared output) must revert.
|
|
const shortFiller = encodeFillerData(tokenAddr, outputAmount - 1n, recipient.address, b32(solver.address));
|
|
await expect(settler.connect(solver).fill(orderId, originData, shortFiller))
|
|
.to.be.revertedWithCustomError(settler, "OutputMismatch");
|
|
|
|
// Wrong recipient (delivering to someone other than the declared recipient) must revert.
|
|
const wrongRecipFiller = encodeFillerData(tokenAddr, outputAmount, other.address, b32(solver.address));
|
|
await expect(settler.connect(solver).fill(orderId, originData, wrongRecipFiller))
|
|
.to.be.revertedWithCustomError(settler, "OutputMismatch");
|
|
|
|
// Wrong token (delivering a different token than declared) must revert.
|
|
const OtherToken = await ethers.getContractFactory("MockERC20Lending");
|
|
const otherToken = await OtherToken.deploy("Wrapped Ether", "WETH", 18);
|
|
await otherToken.waitForDeployment();
|
|
await otherToken.mint(solver.address, outputAmount);
|
|
await otherToken.connect(solver).approve(await settler.getAddress(), outputAmount);
|
|
const wrongTokenFiller = encodeFillerData(await otherToken.getAddress(), outputAmount, recipient.address, b32(solver.address));
|
|
await expect(settler.connect(solver).fill(orderId, originData, wrongTokenFiller))
|
|
.to.be.revertedWithCustomError(settler, "OutputMismatch");
|
|
|
|
// Order stayed unfilled through all three rejected attempts.
|
|
expect(await settler.isFilled(orderId)).to.equal(false);
|
|
});
|
|
|
|
it("zero-recipient reverts (ZeroRecipient)", async function () {
|
|
const { solver, token, settler } = await deployFixture();
|
|
|
|
const orderId = ethers.id("order-zero-recip");
|
|
const outputAmount = 100n * ONE;
|
|
const tokenAddr = await token.getAddress();
|
|
// Declared recipient is zero.
|
|
const originData = encodeOriginData(orderId, tokenAddr, outputAmount, ethers.ZeroHash);
|
|
const fillerData = encodeFillerData(tokenAddr, outputAmount, ethers.ZeroAddress, b32(solver.address));
|
|
|
|
await expect(settler.connect(solver).fill(orderId, originData, fillerData))
|
|
.to.be.revertedWithCustomError(settler, "ZeroRecipient");
|
|
});
|
|
});
|