diff --git a/contracts/Collateral.sol b/contracts/Collateral.sol index 624313c..e3087ce 100644 --- a/contracts/Collateral.sol +++ b/contracts/Collateral.sol @@ -44,17 +44,27 @@ contract Collateral { assert(token.transfer(msg.sender, amount)); } + function _slash(address account, uint256 percentage) internal invariant { + uint256 amount = (balanceOf(account) * percentage) / 100; + totals.slashed += amount; + subtract(account, amount); + } + modifier invariant() { Totals memory oldTotals = totals; _; assert(totals.deposited >= oldTotals.deposited); assert(totals.withdrawn >= oldTotals.withdrawn); - assert(totals.deposited == totals.balance + totals.withdrawn); + assert(totals.slashed >= oldTotals.slashed); + assert( + totals.deposited == totals.balance + totals.withdrawn + totals.slashed + ); } struct Totals { uint256 balance; uint256 deposited; uint256 withdrawn; + uint256 slashed; } } diff --git a/contracts/TestCollateral.sol b/contracts/TestCollateral.sol new file mode 100644 index 0000000..5f5cb4a --- /dev/null +++ b/contracts/TestCollateral.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Collateral.sol"; + +// exposes internal functions for testing +contract TestCollateral is Collateral { + // solhint-disable-next-line no-empty-blocks + constructor(IERC20 token) Collateral(token) {} + + function slash(address account, uint256 percentage) public { + _slash(account, percentage); + } +} diff --git a/test/Collateral.js b/test/Collateral.js index 1bdaed8..c747a73 100644 --- a/test/Collateral.js +++ b/test/Collateral.js @@ -5,7 +5,7 @@ describe("Collateral", function () { let account0, account1 beforeEach(async function () { - let Collateral = await ethers.getContractFactory("Collateral") + let Collateral = await ethers.getContractFactory("TestCollateral") let TestToken = await ethers.getContractFactory("TestToken") token = await TestToken.deploy() collateral = await Collateral.deploy(token.address) @@ -73,4 +73,20 @@ describe("Collateral", function () { expect(after - before).to.equal(balance) }) }) + + describe("slashing", function () { + beforeEach(async function () { + await token.connect(account0).approve(collateral.address, 1000) + await token.connect(account1).approve(collateral.address, 1000) + await collateral.connect(account0).deposit(1000) + await collateral.connect(account1).deposit(1000) + }) + + it("reduces the amount of collateral by a percentage", async function () { + await collateral.slash(account0.address, 10) + await collateral.slash(account1.address, 5) + expect(await collateral.balanceOf(account0.address)).to.equal(900) + expect(await collateral.balanceOf(account1.address)).to.equal(950) + }) + }) })