From c12f065ada34cd86f524217e87ee66573e0bd7dc Mon Sep 17 00:00:00 2001 From: Ranmocy Sheng Date: Wed, 8 Dec 2021 16:37:08 +0000 Subject: [PATCH] Fix MesonStates._removeExpiredSwaps() --- contracts/test/MesonStatesTest.sol | 27 +++++++++++++ contracts/utils/MesonStates.sol | 14 +++---- test/MesonStates.spec.ts | 62 ++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 8 deletions(-) diff --git a/contracts/test/MesonStatesTest.sol b/contracts/test/MesonStatesTest.sol index 096fbce4..6d5bb430 100644 --- a/contracts/test/MesonStatesTest.sol +++ b/contracts/test/MesonStatesTest.sol @@ -1,9 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity =0.8.6; +import "../libraries/List.sol"; +import "../utils/MesonHelpers.sol"; import "../utils/MesonStates.sol"; contract MesonStatesTest is MesonStates { + using List for List.Bytes32List; + constructor(address token) { _addTokenToSwapList(token); } @@ -15,4 +19,27 @@ contract MesonStatesTest is MesonStates { function decreaseSupply(address token, uint256 amount) public { _decreaseSupply(token, amount); } + + function updateDemand(address token, uint256 metaAmount) public { + _updateDemand(token, metaAmount); + } + + function removeExpiredSwaps(address token) public { + _removeExpiredSwaps(token); + } + + function addRecentSwap(address token, bytes32 id, uint256 metaAmount, uint256 ts) public { + Swap memory swap = Swap(id, metaAmount, ts); + _recentSwaps[token][id] = swap; + _recentSwapLists[token].addItem(id); + _tokenDemand[token] = LowGasSafeMath.add(_tokenDemand[token], metaAmount); + } + + function getRecentSwap(address token, bytes32 id) public view returns (Swap memory) { + return _recentSwaps[token][id]; + } + + function getRecentSwapList(address token) public view returns (List.Bytes32List memory) { + return _recentSwapLists[token]; + } } diff --git a/contracts/utils/MesonStates.sol b/contracts/utils/MesonStates.sol index 4bf9792b..fe2bc9ac 100644 --- a/contracts/utils/MesonStates.sol +++ b/contracts/utils/MesonStates.sol @@ -13,8 +13,8 @@ contract MesonStates is MesonHelpers { mapping(address => bool) public supportedTokens; - mapping(address => mapping(bytes32 => Swap)) private _swaps; - mapping(address => List.Bytes32List) private _recentSwapLists; + mapping(address => mapping(bytes32 => Swap)) internal _recentSwaps; + mapping(address => List.Bytes32List) internal _recentSwapLists; mapping(address => uint256) internal _tokenSupply; mapping(address => uint256) internal _tokenDemand; @@ -60,7 +60,7 @@ contract MesonStates is MesonHelpers { uint256 ts = block.timestamp; bytes32 id = keccak256(abi.encodePacked(ts, token, metaAmount)); // TODO something else Swap memory swap = Swap(id, metaAmount, ts); - _swaps[token][id] = swap; + _recentSwaps[token][id] = swap; _recentSwapLists[token].addItem(id); _tokenDemand[token] = LowGasSafeMath.add(_tokenDemand[token], metaAmount); } @@ -72,15 +72,13 @@ contract MesonStates is MesonHelpers { List.Bytes32List storage list = _recentSwapLists[token]; (bool success, bytes32 id) = list.getTail(); - if (!success) return; // list is empty, ignore - - while (_swaps[token][id].ts + TOTAL_DEMAND_CALC_PERIOD < current) { + while (success && (_recentSwaps[token][id].ts + TOTAL_DEMAND_CALC_PERIOD < current)) { _tokenDemand[token] = LowGasSafeMath.sub( _tokenDemand[token], - _swaps[token][id].metaAmount + _recentSwaps[token][id].metaAmount ); list.popItem(); - delete _swaps[token][id]; + delete _recentSwaps[token][id]; (success, id) = list.getTail(); } } diff --git a/test/MesonStates.spec.ts b/test/MesonStates.spec.ts index c72af50c..f73ad6bd 100644 --- a/test/MesonStates.spec.ts +++ b/test/MesonStates.spec.ts @@ -45,4 +45,66 @@ describe('MesonStates', () => { await expect(contract.decreaseSupply(token, 200)).to.be.revertedWith('overdrawn') }) }) + + describe('#updateDemand', () => { + it('update demand', async () => { + await contract.updateDemand(token, 100) + + // Verify it's added to the list + const list = await contract.getRecentSwapList(token) + expect(list._items).has.lengthOf(1) + // Verify it's added to the mapping + const swap = await contract.getRecentSwap(token, list._items[0]) + expect(swap.id).to.not.equal(0) + // Verify demand/supply + expect(await contract.totalDemandFor(token)).to.equal(100); + expect(await contract.totalSupplyFor(token)).to.equal(0); + }) + }) + + describe('#removeExpiredSwaps', () => { + const id0 = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const id1 = "0x464f9a206dee56c06e8f668bc1933f6286aa952f7cea9d677e18b5f089555fa1"; + const id2 = "0x464f9a206dee56c06e8f668bc1933f6286aa952f7cea9d677e18b5f089555fa2"; + const amount = 100; + const expiredTs = 1; + const validTs = Math.floor(Date.now() / 1000); + + async function verifySwapRecords(remainingIds: string[], removedIds: string[]) { + const list = await contract.getRecentSwapList(token) + expect(list._length).to.equal(remainingIds.length) + // TODO: removed ones should be GCed + expect(list._items).has.lengthOf(remainingIds.length + removedIds.length) + for (const id of remainingIds) { + expect((await contract.getRecentSwap(token, id)).id).to.equal(id) + } + for (const id of removedIds) { + expect((await contract.getRecentSwap(token, id)).id).to.equal(id0) + } + } + + it('remove expired with no swap request', async () => { + await contract.removeExpiredSwaps(token) + await verifySwapRecords([], []) + }) + + it('remove expired with one expired request', async () => { + await contract.addRecentSwap(token, id1, amount, expiredTs) + await contract.removeExpiredSwaps(token) + await verifySwapRecords([], [id1]) + }) + + it('remove expired with one non-expired request', async () => { + await contract.addRecentSwap(token, id1, amount, validTs) + await contract.removeExpiredSwaps(token) + await verifySwapRecords([id1], []) + }) + + it('remove expired with one expired and one non-expired requests', async () => { + await contract.addRecentSwap(token, id1, amount, expiredTs) + await contract.addRecentSwap(token, id2, amount, validTs) + await contract.removeExpiredSwaps(token) + await verifySwapRecords([id2], [id1]) + }) + }) })