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
This commit is contained in:
182
test/MeridianToken.test.js
Normal file
182
test/MeridianToken.test.js
Normal file
@@ -0,0 +1,182 @@
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user