const { ethers } = require("hardhat"); const { expect } = require("chai"); const AbiCoder = ethers.utils.AbiCoder; const Keccak256 = ethers.utils.keccak256; describe("Livecycle Unit-Tests for Smart Derivative Contract", () => { // Define objects for TradeState enum, since solidity enums cannot provide their member names... const TradeState = { Inactive: 0, Incepted: 1, Confirmed: 2, Active: 3, Terminated: 4, }; const abiCoder = new AbiCoder(); const trade_data = "here are the trade specification { const [_tokenManager, _counterparty1, _counterparty2] = await ethers.getSigners(); tokenManager = _tokenManager; counterparty1 = _counterparty1; counterparty2 = _counterparty2; const ERC20Factory = await ethers.getContractFactory("SDCToken"); const SDCFactory = await ethers.getContractFactory("SDC"); token = await ERC20Factory.deploy(); await token.deployed(); sdc = await SDCFactory.deploy(counterparty1.address, counterparty2.address,counterparty1.address, token.address,marginBufferAmount,terminationFee); await sdc.deployed(); console.log("SDC Address: %s", sdc.address); }); it("Initial minting and approvals for SDC", async () => { await token.connect(counterparty1).mint(counterparty1.address,initialLiquidityBalance); await token.connect(counterparty2).mint(counterparty2.address,initialLiquidityBalance); await token.connect(counterparty1).approve(sdc.address,terminationFee+marginBufferAmount); await token.connect(counterparty2).approve(sdc.address,terminationFee+marginBufferAmount); let allowanceSDCParty1 = await token.connect(counterparty1).allowance(counterparty1.address, sdc.address); let allowanceSDCParty2 = await token.connect(counterparty2).allowance(counterparty2.address, sdc.address); await expect(allowanceSDCParty1).equal(terminationFee+marginBufferAmount); }); it("Counterparty1 incepts a trade", async () => { const incept_call = await sdc.connect(counterparty1).inceptTrade(trade_data,"initialMarketData"); let tradeid = await sdc.connect(counterparty1).getTradeID(); //console.log("TradeId: %s", tradeid); await expect(incept_call).to.emit(sdc, "TradeIncepted").withArgs(counterparty1.address,tradeid,trade_data); let trade_state = await sdc.connect(counterparty1).getTradeState(); await expect(trade_state).equal(TradeState.Incepted); }); it("Counterparty2 confirms a trade", async () => { const confirm_call = await sdc.connect(counterparty2).confirmTrade(trade_data,"initialMarketData"); //console.log("TradeId: %s", await sdc.callStatic.getTradeState()); let balanceSDC = await token.connect(counterparty2).balanceOf(sdc.address); await expect(confirm_call).to.emit(sdc, "TradeConfirmed"); await expect(balanceSDC).equal(2*terminationFee); let trade_state = await sdc.connect(counterparty1).getTradeState(); await expect(trade_state).equal(TradeState.Active); }); it("Processing first prefunding phase", async () => { const call = await sdc.connect(counterparty2).initiatePrefunding(); let balanceSDC = await token.connect(counterparty2).balanceOf(sdc.address); let balanceCP2 = await token.connect(counterparty2).balanceOf(counterparty2.address); await expect(balanceSDC).equal(2*(terminationFee+marginBufferAmount)); await expect(balanceCP2).equal(initialLiquidityBalance-(terminationFee+marginBufferAmount)); await expect(call).to.emit(sdc, "ProcessFunded"); }); it("Initiate and perform first successful settlement in favour to counterparty 1", async () => { const callInitSettlement = await sdc.connect(counterparty2).initiateSettlement(); await expect(callInitSettlement).to.emit(sdc, "ProcessSettlementRequest"); let balanceCP1 = parseInt(await token.connect(counterparty1).balanceOf(counterparty1.address)); let balanceCP2 = parseInt(await token.connect(counterparty2).balanceOf(counterparty1.address)); const callPerformSettlement = await sdc.connect(counterparty2).performSettlement(settlementAmount1,"settlementData"); await expect(callPerformSettlement).to.emit(sdc, "ProcessSettled"); let balanceSDC_afterSettlement = await token.connect(counterparty2).balanceOf(sdc.address); let balanceCP1_afterSettlement = await token.connect(counterparty1).balanceOf(counterparty1.address); let balanceCP2_afterSettlement = await token.connect(counterparty2).balanceOf(counterparty2.address); await expect(balanceSDC_afterSettlement).equal(2*(terminationFee+marginBufferAmount)-settlementAmount1); // SDC balance less settlement await expect(balanceCP1_afterSettlement).equal(balanceCP1+settlementAmount1); // settlement in favour to CP1 await expect(balanceCP2_afterSettlement).equal(balanceCP2); // CP2 balance is not touched as transfer is booked from SDC balance }); it("Process successfully second prefunding phase successful ", async () => { await token.connect(counterparty2).approve(sdc.address,settlementAmount1); // CP2 increases allowance const call = await sdc.connect(counterparty1).initiatePrefunding(); //Prefunding: SDC transfers missing gap amount from CP2 let balanceSDC = await token.connect(counterparty2).balanceOf(sdc.address); let balanceCP2 = await token.connect(counterparty2).balanceOf(counterparty2.address); await expect(balanceSDC).equal(2*(terminationFee+marginBufferAmount)); await expect(balanceCP2).equal(initialLiquidityBalance-(terminationFee+marginBufferAmount)-settlementAmount1); await expect(call).to.emit(sdc, "ProcessFunded"); }); it("Second settlement fails due to high transfer amount in favour to counteparty 2 - Trade terminates", async () => { const callInitSettlement = await sdc.connect(counterparty2).initiateSettlement(); await expect(callInitSettlement).to.emit(sdc, "ProcessSettlementRequest"); const callPerformSettlement = await sdc.connect(counterparty2).performSettlement(settlementAmount2,"settlementData"); await expect(callPerformSettlement).to.emit(sdc, "TradeTerminated"); let balanceSDC = parseInt(await token.connect(counterparty2).balanceOf(sdc.address)); let balanceCP1 = await token.connect(counterparty1).balanceOf(counterparty1.address); let balanceCP2 = await token.connect(counterparty2).balanceOf(counterparty2.address); let expectedBalanceCP1 = initialLiquidityBalance + settlementAmount1 - (marginBufferAmount + terminationFee); //CP1 received settlementAmount1 and paid margin buffer and termination fee let expectedBalanceCP2 = initialLiquidityBalance - settlementAmount1 + (marginBufferAmount + terminationFee); //CP2 paid settlementAmount1 and receives margin buffer and termination fee await expect(balanceCP1).equal(expectedBalanceCP1); await expect(balanceCP2).equal(expectedBalanceCP2); await expect(balanceSDC).equal(0); }); });