Files
Meridian/test/MeridianToken.test.js
Claude AI 7f001ff5f0 Initial Meridian Protocol implementation
- Complete MeridianToken standard with compliance & reserve checks
- Multi-custodian ReserveAggregator supporting bank/crypto/fund admin
- Comprehensive Compliance engine with KYC/AML/sanctions
- Full interface definitions and deployment scripts
- Test suite for core functionality
- Ready for GBP launch with Anchorage custody integration
2026-04-16 19:42:26 +00:00

183 lines
8.0 KiB
JavaScript

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Meridian Protocol", function () {
let meridianToken, compliance, reserveAggregator;
let owner, addr1, addr2;
let custodianId;
beforeEach(async function () {
[owner, addr1, addr2] = await ethers.getSigners();
// Deploy Compliance
const Compliance = await ethers.getContractFactory("Compliance");
compliance = await Compliance.deploy(owner.address);
await compliance.deployed();
// Deploy ReserveAggregator
const ReserveAggregator = await ethers.getContractFactory("ReserveAggregator");
reserveAggregator = await ReserveAggregator.deploy(owner.address);
await reserveAggregator.deployed();
// Deploy MeridianToken
const MeridianToken = await ethers.getContractFactory("MeridianToken");
meridianToken = await MeridianToken.deploy(
"Meridian GBP",
"MGBP",
"GBP",
"Meridian LLC",
reserveAggregator.address,
compliance.address,
owner.address
);
await meridianToken.deployed();
// Add test custodian
custodianId = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("test-custodian"));
await reserveAggregator.addCustodian(
custodianId,
"Test Custodian",
owner.address, // Use owner as oracle for testing
3600, // 1 hour heartbeat
2 // fund admin type
);
});
describe("Deployment", function () {
it("Should deploy with correct parameters", async function () {
expect(await meridianToken.name()).to.equal("Meridian GBP");
expect(await meridianToken.symbol()).to.equal("MGBP");
expect(await meridianToken.currency()).to.equal("GBP");
expect(await meridianToken.issuerName()).to.equal("Meridian LLC");
expect(await meridianToken.minimumCollateralRatio()).to.equal(10200);
});
it("Should have correct access roles", async function () {
const MINTER_ROLE = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MINTER_ROLE"));
expect(await meridianToken.hasRole(MINTER_ROLE, owner.address)).to.be.true;
});
});
describe("Compliance", function () {
it("Should whitelist addresses correctly", async function () {
await compliance.whitelistAddress(addr1.address, 2, "UK", 0);
expect(await compliance.isWhitelisted(addr1.address)).to.be.true;
expect(await compliance.isSanctioned(addr1.address)).to.be.false;
});
it("Should set velocity limits based on KYC level", async function () {
await compliance.whitelistAddress(addr1.address, 1, "UK", 0);
const limits = await compliance.getVelocityLimits(addr1.address);
expect(limits.dailyLimit).to.equal(ethers.utils.parseEther("1000"));
});
});
describe("Reserve Aggregator", function () {
it("Should add custodians correctly", async function () {
const custodianInfo = await reserveAggregator.getCustodianInfo(custodianId);
expect(custodianInfo.name).to.equal("Test Custodian");
expect(custodianInfo.isActive).to.be.true;
expect(custodianInfo.custodianType).to.equal(2);
});
it("Should attest reserves", async function () {
const reserveAmount = ethers.utils.parseEther("1020000");
const documentHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("reserve-doc"));
await reserveAggregator.attestReserves(
custodianId,
reserveAmount,
documentHash
);
const attestation = await reserveAggregator.getLatestAttestation(custodianId);
expect(attestation.balance).to.equal(reserveAmount);
expect(attestation.isValid).to.be.true;
});
});
describe("Token Operations", function () {
beforeEach(async function () {
// Whitelist recipient
await compliance.whitelistAddress(addr1.address, 2, "UK", 0);
// Attest reserves
const reserveAmount = ethers.utils.parseEther("1020000"); // £1.02M
const documentHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("reserve-doc"));
await reserveAggregator.attestReserves(custodianId, reserveAmount, documentHash);
});
it("Should mint tokens with sufficient reserves", async function () {
const mintAmount = ethers.utils.parseEther("1000000"); // £1M
const attestationHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mint-attestation"));
await meridianToken.mint(addr1.address, mintAmount, attestationHash);
expect(await meridianToken.balanceOf(addr1.address)).to.equal(mintAmount);
expect(await meridianToken.totalSupply()).to.equal(mintAmount);
});
it("Should reject mint with insufficient reserves", async function () {
const mintAmount = ethers.utils.parseEther("1100000"); // £1.1M (exceeds 102% ratio)
const attestationHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mint-attestation"));
await expect(
meridianToken.mint(addr1.address, mintAmount, attestationHash)
).to.be.revertedWith("Insufficient reserves for mint");
});
it("Should reject mint to non-whitelisted address", async function () {
const mintAmount = ethers.utils.parseEther("1000");
const attestationHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mint-attestation"));
await expect(
meridianToken.mint(addr2.address, mintAmount, attestationHash)
).to.be.revertedWith("Recipient not whitelisted");
});
it("Should calculate collateralization ratio correctly", async function () {
const mintAmount = ethers.utils.parseEther("1000000");
const attestationHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mint-attestation"));
await meridianToken.mint(addr1.address, mintAmount, attestationHash);
const [ratio, isValid] = await meridianToken.getCurrentCollateralizationRatio();
expect(isValid).to.be.true;
expect(ratio).to.equal(10200); // 102%
});
});
describe("Transfer Restrictions", function () {
beforeEach(async function () {
// Setup: mint tokens to addr1
await compliance.whitelistAddress(addr1.address, 2, "UK", 0);
await compliance.whitelistAddress(addr2.address, 2, "US", 0);
const reserveAmount = ethers.utils.parseEther("1020000");
const documentHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("reserve-doc"));
await reserveAggregator.attestReserves(custodianId, reserveAmount, documentHash);
const mintAmount = ethers.utils.parseEther("1000");
const attestationHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("mint-attestation"));
await meridianToken.mint(addr1.address, mintAmount, attestationHash);
});
it("Should allow transfer between whitelisted addresses", async function () {
const transferAmount = ethers.utils.parseEther("100");
await meridianToken.connect(addr1).transfer(addr2.address, transferAmount);
expect(await meridianToken.balanceOf(addr2.address)).to.equal(transferAmount);
});
it("Should reject transfer to non-whitelisted address", async function () {
const transferAmount = ethers.utils.parseEther("100");
const addr3 = ethers.Wallet.createRandom();
await expect(
meridianToken.connect(addr1).transfer(addr3.address, transferAmount)
).to.be.revertedWith("Recipient not whitelisted");
});
});
});