From 3d5cb86a10ea5a4cca0e938edc25048f1ded735d Mon Sep 17 00:00:00 2001 From: timofeev Date: Wed, 2 Apr 2025 23:08:25 +0300 Subject: [PATCH 1/4] Dynamic size optimization for liquidity tree - dynamic and fixed versions combined for tests --- contracts/LiquidityTree.sol | 110 +++++++++++++++++---------- contracts/mock/LiquidityProtocol.sol | 2 +- test/liquidityTree-test.js | 26 +++---- utils/utils.js | 4 +- 4 files changed, 86 insertions(+), 56 deletions(-) diff --git a/contracts/LiquidityTree.sol b/contracts/LiquidityTree.sol index ed68945..5b761b1 100644 --- a/contracts/LiquidityTree.sol +++ b/contracts/LiquidityTree.sol @@ -14,7 +14,9 @@ contract LiquidityTree is ILiquidityTree { } uint48 immutable LIQUIDITYNODES; // = 1_099_511_627_776; // begining of data nodes (top at node #1) - uint48 immutable LIQUIDITYLASTNODE; // LIQUIDITYNODES * 2 - 1 + uint48 immutable LIQUIDITYMAXNODE; // the biggest possible size of the tree + uint48 public liquidityLastNode; + uint48 public root; uint48 public nextNode; // next unused node number for adding liquidity @@ -56,12 +58,24 @@ contract LiquidityTree is ILiquidityTree { | 4 (nextNode)| 5 | 6 | 7 | +-------------+----------+---------+---------+ * @param liquidityNodes count of leaves - possible single liquidity addings + * @param dynamicSize if true, the tree will initially have 1 leaf, and then double its size every time it's needed + * if false, constant size always */ - constructor(uint48 liquidityNodes) { + constructor(uint48 liquidityNodes, bool dynamicSize) { LIQUIDITYNODES = liquidityNodes; - LIQUIDITYLASTNODE = liquidityNodes * 2 - 1; + LIQUIDITYMAXNODE = liquidityNodes * 2 - 1; nextNode = liquidityNodes; updateId++; // start from non zero + + if (dynamicSize) { + // the tree starts to increase from the middle + liquidityLastNode = liquidityNodes; + root = liquidityNodes; + } else { + // the whole tree initialized + liquidityLastNode = LIQUIDITYMAXNODE; + root = 1; + } } /** @@ -72,16 +86,16 @@ contract LiquidityTree is ILiquidityTree { function nodeWithdrawView( uint48 leaf ) public view override returns (uint128 withdrawAmount) { - if (leaf < LIQUIDITYNODES || leaf > LIQUIDITYLASTNODE) return 0; + if (leaf < LIQUIDITYNODES || leaf > liquidityLastNode) return 0; if (treeNode[leaf].updateId == 0) return 0; return _getPushView( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, leaf, - treeNode[1].amount + treeNode[root].amount ); } @@ -93,16 +107,16 @@ contract LiquidityTree is ILiquidityTree { // if no leaves, distribution to the whole tree uint48 leaf = nextNode > LIQUIDITYNODES ? nextNode - 1 - : LIQUIDITYLASTNODE; + : liquidityLastNode; // push changes from top node down to the leaf - if (treeNode[1].amount != 0) - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + if (treeNode[root].amount != 0) + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); _pushLazy( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, LIQUIDITYNODES, leaf, amount, @@ -122,12 +136,12 @@ contract LiquidityTree is ILiquidityTree { uint48 lastUsedNode = nextNode - 1; if (leaf > lastUsedNode) leaf = lastUsedNode; - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); if ( _isNeedUpdateWholeLeaves( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, LIQUIDITYNODES, leaf, amount, @@ -135,13 +149,13 @@ contract LiquidityTree is ILiquidityTree { ) ) { leaf = lastUsedNode; // push to the all used leaves [LIQUIDITYNODES, lastUsedNode] - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); } _pushLazy( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, LIQUIDITYNODES, leaf, amount, @@ -183,8 +197,24 @@ contract LiquidityTree is ILiquidityTree { function _nodeAddLiquidity( uint128 amount ) internal checkAmount(amount) returns (uint48 resNode) { - if (nextNode > LIQUIDITYLASTNODE) revert LeafNumberRangeExceeded(); - _updateUp(nextNode, amount, false, ++updateId); + if (nextNode > LIQUIDITYMAXNODE) revert LeafNumberRangeExceeded(); + uint64 updateId_ = ++updateId; + + // when a tree leaves number limit is exceeded, it needs to become bigger + if (nextNode > liquidityLastNode) { + // double the number of leaves + liquidityLastNode += nextNode - LIQUIDITYNODES; + + // initialize new root + uint48 oldRoot_ = root; + uint48 newRoot_ = oldRoot_ / 2; + root = newRoot_; + + // update new root of the tree with the top amount + treeNode[newRoot_].updateId = updateId_; + treeNode[newRoot_].amount = treeNode[oldRoot_].amount; + } + _updateUp(nextNode, amount, false, updateId_); resNode = nextNode; nextNode++; } @@ -224,7 +254,7 @@ contract LiquidityTree is ILiquidityTree { if (percent > FixedMath.ONE) revert IncorrectPercent(); // push changes from top node down to the leaf, if leaf is not up to date - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); // remove amount (percent of amount) from leaf to it's parents withdrawAmount = uint128(treeNode[leaf].amount.mul(percent)); @@ -297,7 +327,7 @@ contract LiquidityTree is ILiquidityTree { bool isSub, uint64 updateId_ ) internal { - if (node == 1 && !isSub && treeNode[node].amount == 0) { + if (node == root && !isSub && treeNode[node].amount == 0) { _changeAmount(node, amount, isSub, updateId_); return; } @@ -323,7 +353,7 @@ contract LiquidityTree is ILiquidityTree { uint256 sumAmounts = lAmount + rAmount; if (sumAmounts == 0) { - if (node == 1 || (treeNode[node].amount > 0)) + if (node == root || (treeNode[node].amount > 0)) _changeAmount(node, amount, isSub, updateId_); return; } @@ -368,7 +398,7 @@ contract LiquidityTree is ILiquidityTree { updateId_ ); } - if (node == 1 || (treeNode[node].amount > 0)) + if (node == root || (treeNode[node].amount > 0)) _changeAmount(node, amount, isSub, updateId_); } @@ -377,16 +407,16 @@ contract LiquidityTree is ILiquidityTree { * @param amount value to removeamount */ function _remove(uint128 amount) internal checkAmount(amount) { - if (treeNode[1].amount < amount) revert InsufficientTopNodeAmount(); + if (treeNode[root].amount < amount) revert InsufficientTopNodeAmount(); uint48 leaf = nextNode - 1; // push changes from top node down to the leaf - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); _pushLazy( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, LIQUIDITYNODES, leaf, amount, @@ -405,14 +435,14 @@ contract LiquidityTree is ILiquidityTree { ) internal checkLeaf(leaf) checkAmount(amount) { uint48 lastUsedNode = nextNode - 1; if (leaf > lastUsedNode) leaf = lastUsedNode; - if (treeNode[1].amount < amount) revert InsufficientTopNodeAmount(); + if (treeNode[root].amount < amount) revert InsufficientTopNodeAmount(); - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); if ( _isNeedUpdateWholeLeaves( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, LIQUIDITYNODES, leaf, amount, @@ -420,13 +450,13 @@ contract LiquidityTree is ILiquidityTree { ) ) { leaf = lastUsedNode; // push to the all used leaves [LIQUIDITYNODES, lastUsedNode] - _push(1, LIQUIDITYNODES, LIQUIDITYLASTNODE, leaf, ++updateId); + _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); } _pushLazy( - 1, + root, LIQUIDITYNODES, - LIQUIDITYLASTNODE, + liquidityLastNode, LIQUIDITYNODES, leaf, amount, @@ -467,7 +497,7 @@ contract LiquidityTree is ILiquidityTree { ) internal { _changeAmount(child, amount, isSub, updateId_); // if not top parent - if (child != 1) { + if (child != root) { _updateUp(_getParent(child), amount, isSub, updateId_); } } @@ -655,10 +685,10 @@ contract LiquidityTree is ILiquidityTree { */ function _getParent( uint48 fromNumber - ) internal pure returns (uint48 parentNumber) { + ) internal view returns (uint48 parentNumber) { // if requested from top - if (fromNumber == 1) { - return 1; + if (fromNumber == root) { + return root; } return fromNumber / 2; } @@ -719,7 +749,7 @@ contract LiquidityTree is ILiquidityTree { } function _checkLeaf(uint48 leaf) internal view { - if (leaf < LIQUIDITYNODES || leaf > LIQUIDITYLASTNODE) + if (leaf < LIQUIDITYNODES || leaf > liquidityLastNode) revert IncorrectLeaf(); } } diff --git a/contracts/mock/LiquidityProtocol.sol b/contracts/mock/LiquidityProtocol.sol index 7cda3fe..a9c4934 100644 --- a/contracts/mock/LiquidityProtocol.sol +++ b/contracts/mock/LiquidityProtocol.sol @@ -8,7 +8,7 @@ import "../interface/ILiquidityTree.sol"; interface ILiquidityProtocol is ILiquidityTree {} contract LiquidityProtocol is LiquidityTree { - constructor(uint48 liquidityNodes) LiquidityTree(liquidityNodes) {} + constructor(uint48 liquidityNodes, bool dynamicSize) LiquidityTree(liquidityNodes, dynamicSize) {} function add(uint128 amount) external { _add(amount); diff --git a/test/liquidityTree-test.js b/test/liquidityTree-test.js index b5a480f..328acdf 100644 --- a/test/liquidityTree-test.js +++ b/test/liquidityTree-test.js @@ -44,7 +44,7 @@ describe("LiquidityTree", () => { let sTree, firstLeaf; describe("Big tree", async () => { beforeEach(async () => { - sTree = await prepareTree(ethers, BIG_TREE_LEAFS); + sTree = await prepareTree(ethers, BIG_TREE_LEAFS, true); firstLeaf = await sTree.nextNode(); }); it("100 zero profit distributions on 100 leaves", async () => { @@ -114,7 +114,7 @@ describe("LiquidityTree", () => { await sTree.nodeAddLiquidity(TOKENS_100); } lastFilledLeaf = (await sTree.nextNode()) - 1n; - initLiquidity = (await sTree.treeNode(1)).amount; + initLiquidity = (await sTree.treeNode(await sTree.root())).amount; // get 100 for the "game" await sTree.remove(TOKENS_100); @@ -129,7 +129,7 @@ describe("LiquidityTree", () => { } // all withdrawn - expect((await sTree.treeNode(1)).amount).to.be.equal(ZERO); + expect((await sTree.treeNode((await sTree.root()))).amount).to.be.equal(ZERO); // withdrawn sum is all deposited + distributed 100 expect(totalWitdrawn).to.be.equal(initLiquidity + TOKENS_100); }); @@ -143,7 +143,7 @@ describe("LiquidityTree", () => { } // all withdrawn - expect((await sTree.treeNode(1)).amount).to.be.equal(ZERO); + expect((await sTree.treeNode((await sTree.root()))).amount).to.be.equal(ZERO); // withdrawn sum is all deposited - distributed 90 loss expect(totalWitdrawn).to.be.equal(initLiquidity - TOKENS_90); }); @@ -151,7 +151,7 @@ describe("LiquidityTree", () => { }); describe("small tree (16 leaves)", (async) => { beforeEach(async () => { - sTree = await prepareTree(ethers, SMALL_TREE_LEAFS); + sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, false); }); it("add liquidity to 7 leafs, top add 70, withdraw leaf", async () => { for (const i of Array(7).keys()) { @@ -874,7 +874,7 @@ describe("LiquidityTree", () => { }); describe("small tree (16 leaves) with empty lists", (async) => { beforeEach(async () => { - sTree = await prepareTree(ethers, SMALL_TREE_LEAFS); + sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, false); }); it("add liquidity to 2 leafs, withdraw first leaf, removeLimit first", async () => { for (const i of Array(2).keys()) { @@ -1707,7 +1707,7 @@ describe("LiquidityTree", () => { }); describe("Example tree (4 leaves)", async () => { before(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS); + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); }); it("nodeAddLiquidity(100$)", async () => { await sTree.nodeAddLiquidity(TOKENS_100); @@ -1810,7 +1810,7 @@ describe("LiquidityTree", () => { }); describe("Example tree (4 leaves) fair distribution", async () => { before(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS); + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); }); it("add liquidity 10$ in each of 4 leafs", async () => { await sTree.nodeAddLiquidity(TOKENS_10); @@ -1926,7 +1926,7 @@ describe("LiquidityTree", () => { }); describe("Example tree (4 leaves) fair distribution removing from range with 0", async () => { beforeEach(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS); + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); }); it("add liquidity 60$ for all leaves on tree, withdraw #5, removeLimit(50, 6)", async () => { for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); @@ -2048,7 +2048,7 @@ describe("LiquidityTree", () => { }); describe("Example tree (4 leaves) fair distribution Alice, Bob, Clarc", async () => { beforeEach(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS); + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); }); it("There are 10000$ of liquidity, Bob added 1000$, remove 2000$ lose of leaves 4, 5, Clarc not affected", async () => { // Alice and Bob added @@ -2158,7 +2158,7 @@ describe("LiquidityTree", () => { }); describe("Example tree (2 leaves) fair distribution", async () => { before(async () => { - sTree = await prepareTree(ethers, TINY_TREE_LEAFS); + sTree = await prepareTree(ethers, TINY_TREE_LEAFS, false); }); it("add liquidity 10$ in each of 2 leafs", async () => { await sTree.nodeAddLiquidity(TOKENS_10); @@ -2206,7 +2206,7 @@ describe("LiquidityTree", () => { }); describe("Example tree (8 leaves) fair distribution", async () => { beforeEach(async () => { - sTree = await prepareTree(ethers, MIDDLE_TREE_LEAFS); + sTree = await prepareTree(ethers, MIDDLE_TREE_LEAFS, false); }); it("add liquidity 45$, remove 45$, try withdraw it", async () => { // Add three leaves so the one we will be using is the last of the left "main branch" @@ -3684,7 +3684,7 @@ describe("LiquidityTree", () => { it("addLimit(100, 8) to empty tree after series of depo/withdraw", async () => { describe("Example tree (8 leaves) fair distribution", async () => { beforeEach(async () => { - sTree = await prepareTree(ethers, MIDDLE_TREE_LEAFS); + sTree = await prepareTree(ethers, MIDDLE_TREE_LEAFS, false); // depo/withdraw #8-#12 for (const i of Array(5).keys()) { await sTree.nodeAddLiquidity(1); diff --git a/utils/utils.js b/utils/utils.js index 4957539..7f21603 100644 --- a/utils/utils.js +++ b/utils/utils.js @@ -36,9 +36,9 @@ const getNodeAmount = async (sTree, node) => { return (await sTree.treeNode(node)).amount; }; -const prepareTree = async (ethers, leafs) => { +const prepareTree = async (ethers, leafs, isDynamicSized) => { const LIQUIDITYTREE = await ethers.getContractFactory("LiquidityProtocol"); - let tree = await LIQUIDITYTREE.deploy(leafs); + let tree = await LIQUIDITYTREE.deploy(leafs, isDynamicSized); await tree.waitForDeployment(); return tree; }; From 74cc35b1c58626f4f7b899075da70679be521649 Mon Sep 17 00:00:00 2001 From: timofeev Date: Wed, 2 Apr 2025 23:23:38 +0300 Subject: [PATCH 2/4] Added comments --- contracts/LiquidityTree.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/LiquidityTree.sol b/contracts/LiquidityTree.sol index 5b761b1..f4b62d5 100644 --- a/contracts/LiquidityTree.sol +++ b/contracts/LiquidityTree.sol @@ -15,8 +15,8 @@ contract LiquidityTree is ILiquidityTree { uint48 immutable LIQUIDITYNODES; // = 1_099_511_627_776; // begining of data nodes (top at node #1) uint48 immutable LIQUIDITYMAXNODE; // the biggest possible size of the tree - uint48 public liquidityLastNode; - uint48 public root; + uint48 public liquidityLastNode; // current maximum usable leaf of the tree + uint48 public root; // current root of the tree uint48 public nextNode; // next unused node number for adding liquidity From a4e455566f7e0578320abbff58ce0d11847171a9 Mon Sep 17 00:00:00 2001 From: Maksim Kiselev Date: Wed, 16 Apr 2025 17:51:42 +0200 Subject: [PATCH 3/4] Added dynamic version for part of tests --- contracts/LiquidityTree.sol | 10 +- contracts/mock/LiquidityProtocol.sol | 5 +- test/liquidityTree-test.js | 2559 +++++++++++++++++++++----- 3 files changed, 2073 insertions(+), 501 deletions(-) diff --git a/contracts/LiquidityTree.sol b/contracts/LiquidityTree.sol index f4b62d5..a9f314d 100644 --- a/contracts/LiquidityTree.sol +++ b/contracts/LiquidityTree.sol @@ -199,12 +199,12 @@ contract LiquidityTree is ILiquidityTree { ) internal checkAmount(amount) returns (uint48 resNode) { if (nextNode > LIQUIDITYMAXNODE) revert LeafNumberRangeExceeded(); uint64 updateId_ = ++updateId; - + // when a tree leaves number limit is exceeded, it needs to become bigger if (nextNode > liquidityLastNode) { // double the number of leaves liquidityLastNode += nextNode - LIQUIDITYNODES; - + // initialize new root uint48 oldRoot_ = root; uint48 newRoot_ = oldRoot_ / 2; @@ -409,7 +409,11 @@ contract LiquidityTree is ILiquidityTree { function _remove(uint128 amount) internal checkAmount(amount) { if (treeNode[root].amount < amount) revert InsufficientTopNodeAmount(); - uint48 leaf = nextNode - 1; + // if no leaves, remove from the whole tree + uint48 leaf = nextNode > LIQUIDITYNODES + ? nextNode - 1 + : liquidityLastNode; + // push changes from top node down to the leaf _push(root, LIQUIDITYNODES, liquidityLastNode, leaf, ++updateId); diff --git a/contracts/mock/LiquidityProtocol.sol b/contracts/mock/LiquidityProtocol.sol index a9c4934..355882d 100644 --- a/contracts/mock/LiquidityProtocol.sol +++ b/contracts/mock/LiquidityProtocol.sol @@ -8,7 +8,10 @@ import "../interface/ILiquidityTree.sol"; interface ILiquidityProtocol is ILiquidityTree {} contract LiquidityProtocol is LiquidityTree { - constructor(uint48 liquidityNodes, bool dynamicSize) LiquidityTree(liquidityNodes, dynamicSize) {} + constructor( + uint48 liquidityNodes, + bool dynamicSize + ) LiquidityTree(liquidityNodes, dynamicSize) {} function add(uint128 amount) external { _add(amount); diff --git a/test/liquidityTree-test.js b/test/liquidityTree-test.js index 328acdf..0611a91 100644 --- a/test/liquidityTree-test.js +++ b/test/liquidityTree-test.js @@ -22,13 +22,13 @@ const TOKENS_10 = tokens(10); const TOKENS_5 = tokens(5); const ZERO = tokens(0); const BIG_TREE_LEAFS = 1_099_511_627_776; -2_199_023_255_564; const SMALL_TREE_LEAFS = 16; const MIDDLE_TREE_LEAFS = 8; const TINY_TREE_LEAFS = 2; const EXAMPLE_TREE_LEAFS = 4; const WITHDRAW_50_PERCENT = 500000000000n; const WITHDRAW_100_PERCENT = 10 ** 12; +const IS_DYNAMIC = [false, true]; const checkTreeIsEmpty = async (sTree) => { for (const i of Array(32).keys()) { @@ -41,115 +41,117 @@ const checkNodeAmountTo = async (sTree, nodeNumber, tokens) => { }; describe("LiquidityTree", () => { - let sTree, firstLeaf; - describe("Big tree", async () => { - beforeEach(async () => { - sTree = await prepareTree(ethers, BIG_TREE_LEAFS, true); - firstLeaf = await sTree.nextNode(); - }); - it("100 zero profit distributions on 100 leaves", async () => { - for (const iterator of Array(100).keys()) { - await sTree.nodeAddLiquidity(TOKENS_100); - } - let lastFilledLeaf = (await sTree.nextNode()) - 1n; + IS_DYNAMIC.forEach((isDynamic) => { + describe(`Big tree ${isDynamic ? "dynamic" : "static"}`, async () => { + let sTree, firstLeaf; + beforeEach(async () => { + sTree = await prepareTree(ethers, BIG_TREE_LEAFS, isDynamic); + firstLeaf = await sTree.nextNode(); + }); + it("100 zero profit distributions on 100 leaves", async () => { + for (const iterator of Array(100).keys()) { + await sTree.nodeAddLiquidity(TOKENS_100); + } + let lastFilledLeaf = (await sTree.nextNode()) - 1n; - // cycle of 100 "getting liquidity"/"distribution zero" - for (const iterator of Array(100).keys()) { - // get 100 for game - await sTree.remove(TOKENS_100); + // cycle of 100 "getting liquidity"/"distribution zero" + for (const iterator of Array(100).keys()) { + // get 100 for game + await sTree.remove(TOKENS_100); - // distribute back 100, zero profit - await sTree.addLimit(TOKENS_100, lastFilledLeaf); - } + // distribute back 100, zero profit + await sTree.addLimit(TOKENS_100, lastFilledLeaf); + } - expect(await sTree.nodeWithdrawView(lastFilledLeaf)).to.be.equal(TOKENS_100); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(lastFilledLeaf))).to.be.equal(TOKENS_100); - }); - it("Add huge amounts >300mln leaves, some adds/removes, multiple small nodeAddLiquidity/nodeWithraw, and adds/removes again", async () => { - await sTree.nodeAddLiquidity(tokens(1)); - await sTree.nodeWithdraw((await sTree.nextNode()) - 1n); - await sTree.nodeAddLiquidity(tokens(600_000_000)); - for (const iterator of Array(10).keys()) { + expect(await sTree.nodeWithdrawView(lastFilledLeaf)).to.be.equal(TOKENS_100); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(lastFilledLeaf))).to.be.equal(TOKENS_100); + }); + it("Add huge amounts >300mln leaves, some adds/removes, multiple small nodeAddLiquidity/nodeWithraw, and adds/removes again", async () => { + await sTree.nodeAddLiquidity(tokens(1)); + await sTree.nodeWithdraw((await sTree.nextNode()) - 1n); + await sTree.nodeAddLiquidity(tokens(600_000_000)); for (const iterator of Array(10).keys()) { - await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); - await sTree.nodeAddLiquidity(tokens(1)); - await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); - await sTree.nodeWithdraw((await sTree.nextNode()) - 1n); - await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + for (const iterator of Array(10).keys()) { + await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + await sTree.nodeAddLiquidity(tokens(1)); + await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + await sTree.nodeWithdraw((await sTree.nextNode()) - 1n); + await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + } + + for (const iterator of Array(10).keys()) { + await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + await sTree.nodeAddLiquidity(tokens(1)); + await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_50_PERCENT); // 50% + await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + } } - for (const iterator of Array(10).keys()) { - await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + for (const iterator of Array(39).keys()) { await sTree.nodeAddLiquidity(tokens(1)); - await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); - await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_50_PERCENT); // 50% - await sTree.addLimit(tokens(10), (await sTree.nextNode()) - 1n); + await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_100_PERCENT); // 100% } - } - - for (const iterator of Array(39).keys()) { - await sTree.nodeAddLiquidity(tokens(1)); - await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_100_PERCENT); // 100% - } - await sTree.remove(tokens(1000)); + await sTree.remove(tokens(1000)); - for (const iterator of Array(19).keys()) { - await sTree.nodeAddLiquidity(tokens(1)); - await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_100_PERCENT); // 100% - } - await sTree.remove(tokens(1000)); - - for (const iterator of Array(9).keys()) { - await sTree.nodeAddLiquidity(tokens(1)); - await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_100_PERCENT); // 100% - } - await sTree.remove(tokens(1000)); - await sTree.removeLimit(tokens(10), (await sTree.nextNode()) - 1n); - }); - describe("add 1000, get 100", async () => { - let lastFilledLeaf, initLiquidity; - beforeEach(async () => { - // initial deposites - for (const iterator of Array(100).keys()) { - await sTree.nodeAddLiquidity(TOKENS_100); + for (const iterator of Array(19).keys()) { + await sTree.nodeAddLiquidity(tokens(1)); + await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_100_PERCENT); // 100% } - lastFilledLeaf = (await sTree.nextNode()) - 1n; - initLiquidity = (await sTree.treeNode(await sTree.root())).amount; + await sTree.remove(tokens(1000)); - // get 100 for the "game" - await sTree.remove(TOKENS_100); + for (const iterator of Array(9).keys()) { + await sTree.nodeAddLiquidity(tokens(1)); + await sTree.nodeWithdrawPercent((await sTree.nextNode()) - 1n, WITHDRAW_100_PERCENT); // 100% + } + await sTree.remove(tokens(1000)); + await sTree.removeLimit(tokens(10), (await sTree.nextNode()) - 1n); }); - it("return profit: 100 back + 100 distribution on 100 leaves, finally 101 on each leaf", async () => { - // return 200 from game result - await sTree.addLimit(TOKENS_200, lastFilledLeaf); + describe("add 1000, get 100", async () => { + let lastFilledLeaf, initLiquidity; + beforeEach(async () => { + // initial deposites + for (const iterator of Array(100).keys()) { + await sTree.nodeAddLiquidity(TOKENS_100); + } + lastFilledLeaf = (await sTree.nextNode()) - 1n; + initLiquidity = (await sTree.treeNode(await sTree.root())).amount; - let totalWitdrawn = 0n; - for (const i of Array(100).keys()) { - totalWitdrawn += await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(firstLeaf + BigInt(i))); - } + // get 100 for the "game" + await sTree.remove(TOKENS_100); + }); + it("return profit: 100 back + 100 distribution on 100 leaves, finally 101 on each leaf", async () => { + // return 200 from game result + await sTree.addLimit(TOKENS_200, lastFilledLeaf); - // all withdrawn - expect((await sTree.treeNode((await sTree.root()))).amount).to.be.equal(ZERO); - // withdrawn sum is all deposited + distributed 100 - expect(totalWitdrawn).to.be.equal(initLiquidity + TOKENS_100); - }); - it("return 10 profit: distribute -90 on 100 leaves, finally 99.10 on each leaf", async () => { - // return 10 from game result, actual distributing -90 loss - await sTree.addLimit(tokens(10), lastFilledLeaf); + let totalWitdrawn = 0n; + for (const i of Array(100).keys()) { + totalWitdrawn += await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(firstLeaf + BigInt(i))); + } - let totalWitdrawn = 0n; - for (const i of Array(100).keys()) { - totalWitdrawn += await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(firstLeaf + BigInt(i))); - } + // all withdrawn + checkNodeAmountTo(sTree, await sTree.root(), ZERO); + // withdrawn sum is all deposited + distributed 100 + expect(totalWitdrawn).to.be.equal(initLiquidity + TOKENS_100); + }); + it("return 10 profit: distribute -90 on 100 leaves, finally 99.10 on each leaf", async () => { + // return 10 from game result, actual distributing -90 loss + await sTree.addLimit(tokens(10), lastFilledLeaf); - // all withdrawn - expect((await sTree.treeNode((await sTree.root()))).amount).to.be.equal(ZERO); - // withdrawn sum is all deposited - distributed 90 loss - expect(totalWitdrawn).to.be.equal(initLiquidity - TOKENS_90); + let totalWitdrawn = 0n; + for (const i of Array(100).keys()) { + totalWitdrawn += await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(firstLeaf + BigInt(i))); + } + + // all withdrawn + checkNodeAmountTo(sTree, await sTree.root(), ZERO); + // withdrawn sum is all deposited - distributed 90 loss + expect(totalWitdrawn).to.be.equal(initLiquidity - TOKENS_90); + }); }); }); }); - describe("small tree (16 leaves)", (async) => { + describe("small tree (16 leaves) static", async () => { beforeEach(async () => { sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, false); }); @@ -205,9 +207,9 @@ describe("LiquidityTree", () => { 40 + 20 + 10 = 70 */ - expect((await sTree.treeNode(4)).amount).to.be.eq(tokens(440)); - expect((await sTree.treeNode(10)).amount).to.be.eq(tokens(220)); - expect((await sTree.treeNode(22)).amount).to.be.eq(tokens(110)); + checkNodeAmountTo(sTree, 4, tokens(440)); + checkNodeAmountTo(sTree, 10, tokens(220)); + checkNodeAmountTo(sTree, 22, tokens(110)); let withdrawPreview = await sTree.nodeWithdrawView(16); expect(withdrawPreview).to.be.equal(tokens(110)); @@ -233,10 +235,10 @@ describe("LiquidityTree", () => { withdrawn from leaf 16 110 */ - expect((await sTree.treeNode(4)).amount).to.be.eq(tokens(330)); - expect((await sTree.treeNode(8)).amount).to.be.eq(tokens(110)); - expect((await sTree.treeNode(9)).amount).to.be.eq(tokens(220)); - expect((await sTree.treeNode(16)).amount).to.be.equal("0"); + checkNodeAmountTo(sTree, 4, tokens(330)); + checkNodeAmountTo(sTree, 8, tokens(110)); + checkNodeAmountTo(sTree, 9, tokens(220)); + checkNodeAmountTo(sTree, 16, ZERO); }); it("add liquidity to 6 leafs, top add 60", async () => { for (const i of Array(6).keys()) { @@ -287,9 +289,9 @@ describe("LiquidityTree", () => { 40 + 20 = 60 */ - expect((await sTree.treeNode(4)).amount).to.be.eq(tokens(440)); - expect((await sTree.treeNode(10)).amount).to.be.eq(tokens(220)); - expect((await sTree.treeNode(22)).amount).to.be.equal("0"); + checkNodeAmountTo(sTree, 4, tokens(440)); + checkNodeAmountTo(sTree, 10, tokens(220)); + checkNodeAmountTo(sTree, 22, ZERO); }); it("add liquidity to 7 leafs, top remove 100, withdraw leaf #1 add for 7 leaves", async () => { for (const i of Array(7).keys()) { @@ -872,163 +874,1631 @@ describe("LiquidityTree", () => { }); }); }); - describe("small tree (16 leaves) with empty lists", (async) => { + describe("small tree (16 leaves) dynamic", async () => { beforeEach(async () => { - sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, false); + sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, true); }); - it("add liquidity to 2 leafs, withdraw first leaf, removeLimit first", async () => { - for (const i of Array(2).keys()) { - await sTree.nodeAddLiquidity(TOKENS_10); + it("add liquidity to 7 leafs, top add 70, withdraw leaf", async () => { + for (const i of Array(7).keys()) { + await sTree.nodeAddLiquidity(TOKENS_100); } /* Liquidity tree structure after nodeAddLiquidity: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(20) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(20) | 3 | - +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ - | 4(20) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(20) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(10) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + +-----------------------------------------------------------------------+ + | 2(700) | + +-----------------------------------+---------------+-------------------- + | 4(400) | 5(300) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(200) | 9(200) | 10(200) | 11(100) | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100)| 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + 100 100 100 100 100 100 100 */ - await checkNodeAmountTo(sTree, 1, TOKENS_20); - await checkNodeAmountTo(sTree, 2, TOKENS_20); - await checkNodeAmountTo(sTree, 3, ZERO); - await checkNodeAmountTo(sTree, 4, TOKENS_20); - for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); - await checkNodeAmountTo(sTree, 8, TOKENS_20); - for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); - await checkNodeAmountTo(sTree, 16, TOKENS_10); - await checkNodeAmountTo(sTree, 17, TOKENS_10); + checkNodeAmountTo(sTree, 1, ZERO); + checkNodeAmountTo(sTree, 2, tokens(700)); + checkNodeAmountTo(sTree, 4, TOKENS_400); + checkNodeAmountTo(sTree, 5, tokens(300)); + checkNodeAmountTo(sTree, 8, TOKENS_200); + checkNodeAmountTo(sTree, 9, TOKENS_200); + checkNodeAmountTo(sTree, 10, TOKENS_200); + checkNodeAmountTo(sTree, 11, TOKENS_100); - // withdraw first leaf - await sTree.nodeWithdraw(16); + await sTree.add(tokens(70)); /* - Liquidity tree structure after nodeAddLiquidity: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(10) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(10) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(10) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(10) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - */ + Liquidity tree structure after add(TOKENS_100) on top 1: + +----------------------------------------------------------------------------+ + | 2(770) | + +-----------------------------------+----------------------------------------+ + | 4(440) | 5(330) | + +-----------------+-----------------+-----------------+----------------------+ + | 8(200) | 9(200) | 10(220) | 11(110) | + +--------+--------+--------+--------+--------+--------+-------------+--------+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(110) | 23 | + +--------+--------+--------+--------+--------+--------+-------------+--------+ + 100 100 100 100 100 100 100 - await checkNodeAmountTo(sTree, 1, TOKENS_10); - await checkNodeAmountTo(sTree, 2, TOKENS_10); - await checkNodeAmountTo(sTree, 3, ZERO); - await checkNodeAmountTo(sTree, 4, TOKENS_10); - for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); - await checkNodeAmountTo(sTree, 8, TOKENS_10); - for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); - await checkNodeAmountTo(sTree, 17, TOKENS_10); + 70 tokens been spread for nodes: + 4 - 40 70 * 400 / (400+300) + 10 - 20 (70 - 40) * 200 / (200 + 100) + 22 - 10 (70 - 40) * 100 / (200 + 100) + 40 + 20 + 10 = 70 + */ + checkNodeAmountTo(sTree, 1, ZERO); + checkNodeAmountTo(sTree, 2, tokens(700)); + checkNodeAmountTo(sTree, 4, tokens(440)); + checkNodeAmountTo(sTree, 10, tokens(220)); + checkNodeAmountTo(sTree, 22, tokens(110)); - // remove limit with first leaf. Leaf is emty, removing from right leaf's branch - await sTree.removeLimit(tokens(5), 16); - /* - Liquidity tree structure after removeLimit: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(5) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(5) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(5) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(5) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + let withdrawPreview = await sTree.nodeWithdrawView(16); + expect(withdrawPreview).to.be.equal(tokens(110)); - await checkNodeAmountTo(sTree, 1, TOKENS_5); - await checkNodeAmountTo(sTree, 2, TOKENS_5); - await checkNodeAmountTo(sTree, 3, ZERO); - await checkNodeAmountTo(sTree, 4, TOKENS_5); - for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); - await checkNodeAmountTo(sTree, 8, TOKENS_5); - for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); - await checkNodeAmountTo(sTree, 17, TOKENS_10); - expect(await sTree.nodeWithdrawView(17)).to.be.eq(TOKENS_5); + let tx = await sTree.nodeWithdraw(16); + expect(await getWithdrawnAmount(sTree, tx)).to.be.equal(withdrawPreview); - await sTree.removeLimit(tokens(5), 16); + /* + Liquidity tree structure after nodeWithdraw(16): + +----------------------------------------------------------------------------+ + | 2(660) | + +-----------------------------------+----------------------------------------+ + | 4(330) | 5(330) | + +-----------------+-----------------+-----------------+----------------------+ + | 8(110) | 9(220) | 10(220) | 11(110) | + +--------+--------+--------+--------+--------+--------+-------------+--------+ + | 16(0) | 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(110) | 23 | + +--------+--------+--------+--------+--------+--------+-------------+--------+ + 100 100 100 100 100 100 + + withdrawn from leaf 16 110 + */ + checkNodeAmountTo(sTree, 1, ZERO); + checkNodeAmountTo(sTree, 2, tokens(660)); + checkNodeAmountTo(sTree, 4, tokens(330)); + checkNodeAmountTo(sTree, 8, tokens(110)); + checkNodeAmountTo(sTree, 9, tokens(220)); + checkNodeAmountTo(sTree, 16, ZERO); + }); + it("add liquidity to 6 leafs, top add 60", async () => { + for (const i of Array(6).keys()) { + await sTree.nodeAddLiquidity(TOKENS_100); + } /* Liquidity tree structure after nodeAddLiquidity: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(0) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(0) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(0) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + +-----------------------------------------------------------------------+ + | 2(600) | + +-----------------------------------+---------------+-------------------- + | 4(400) | 5(200) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(200) | 9(200) | 10(200) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + 100 100 100 100 100 100 + */ + checkNodeAmountTo(sTree, 1, ZERO); + checkNodeAmountTo(sTree, 2, tokens(600)); + checkNodeAmountTo(sTree, 4, TOKENS_400); + checkNodeAmountTo(sTree, 5, TOKENS_200); + checkNodeAmountTo(sTree, 8, TOKENS_200); + checkNodeAmountTo(sTree, 9, TOKENS_200); + checkNodeAmountTo(sTree, 10, TOKENS_200); - await checkNodeAmountTo(sTree, 1, ZERO); - await checkNodeAmountTo(sTree, 2, ZERO); - await checkNodeAmountTo(sTree, 3, ZERO); - await checkNodeAmountTo(sTree, 4, ZERO); - for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); - await checkNodeAmountTo(sTree, 8, ZERO); - for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await sTree.add(TOKENS_60); + /* + Liquidity tree structure after add: + +-----------------------------------------------------------------------+ + | 2(660) | + +-----------------------------------+-----------------------------------+ + | 4(440) | 5(220) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(200) | 9(200) | 10(220) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + 100 100 100 100 100 100 - await checkNodeAmountTo(sTree, 17, TOKENS_5); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(17))).to.be.eq(ZERO); + 60 tokens been spread for nodes: + 4 - 40 60 * 400 / (400+200) + 5 - 20 60 * 200 / (400+200) + 40 + 20 = 60 + */ - await checkTreeIsEmpty(sTree); + checkNodeAmountTo(sTree, 1, ZERO); + checkNodeAmountTo(sTree, 2, tokens(660)); + checkNodeAmountTo(sTree, 4, tokens(440)); + checkNodeAmountTo(sTree, 10, tokens(220)); + checkNodeAmountTo(sTree, 22, ZERO); }); - it("add liquidity to 4 leafs, withdraw first 2 leaf, removeLimit first 2 leaves affected right 2 leaves", async () => { - for (const i of Array(4).keys()) { - await sTree.nodeAddLiquidity(TOKENS_10); + it("add liquidity to 7 leafs, top remove 100, withdraw leaf #1 add for 7 leaves", async () => { + for (const i of Array(7).keys()) { + await sTree.nodeAddLiquidity(TOKENS_100); } /* Liquidity tree structure after nodeAddLiquidity: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(40) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(40) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(40) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(20) | 9(20) | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(10) | 17(10) | 18(10) | 19(10) | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + +-----------------------------------------------------------------------+ + | 2(700) | + +-----------------------------------+-----------------------------------+ + | 4(400) | 5(300) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(200) | 9(200) | 10(200) | 11(100) | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100)| 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + 100 100 100 100 100 100 100 */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, tokens(700)); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, tokens(300)); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_200); + await checkNodeAmountTo(sTree, 11, TOKENS_100); - for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_40); - await checkNodeAmountTo(sTree, 3, ZERO); - await checkNodeAmountTo(sTree, 4, TOKENS_40); - for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); - for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_20); - for (const i of Array(5).keys()) await checkNodeAmountTo(sTree, i + 10, ZERO); - for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); + await sTree.remove(tokens(70)); - // withdraw first 2 leaves - await sTree.nodeWithdraw(16); - await sTree.nodeWithdraw(17); - /*Liquidity tree structure after nodeWithdraw: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(20) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(20) | 3 | - +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ - | 4(20) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9(20) | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(0) | 18(10) | 19(10) | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + /* + Liquidity tree structure after remove(70): + +---------------------------------------------------------------------------+ + | 2(630) | + +-----------------------------------+---------------------------------------+ + | 4(360) | 5(270) | + +-----------------+-----------------+-----------------+---------------------+ + | 8(200) | 9(200) | 10(180) | 11(90) | + +--------+--------+--------+--------+--------+--------+------------+--------+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(90) | 23 | + +--------+--------+--------+--------+--------+--------+------------+--------+ + 100 100 100 100 100 100 100 + + 70 tokens been removed for nodes: + 1 630 = 700 - 70 + 2 630 = 700 - 70 + 4 360 = 400 - 40 (70 * 400 / (400+300)) + 5 270 = 300 - 30 (70 * 300 / (400+300)) + 10 200 = 200 - 30 * 200 / (200+100) + 11 90 = 100 - 30 * 100 / (200+100) + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, tokens(630)); + await checkNodeAmountTo(sTree, 4, tokens(360)); + await checkNodeAmountTo(sTree, 5, tokens(270)); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, tokens(180)); + await checkNodeAmountTo(sTree, 11, TOKENS_90); + + let withdrawPreview = await sTree.nodeWithdrawView(16); + let tx = await sTree.nodeWithdraw(16); + + /* + Liquidity tree structure after nodeWithdraw(16): + +---------------------------------------------------------------------------+ + | 2(540) | + +-----------------------------------+---------------------------------------+ + | 4(270) | 5(270) | + +-----------------+-----------------+-----------------+---------------------+ + | 8(90) | 9(200) | 10(180) | 11(90) | + +--------+--------+--------+--------+--------+--------+------------+--------+ + | 16(0) | 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(90) | 23 | + +--------+--------+--------+--------+--------+--------+------------+--------+ + 100 100 100 100 100 100 + + 90 tokens been withdrawn from leaf 16 + */ + + expect(withdrawPreview).to.be.equal(TOKENS_90); + expect(await getWithdrawnAmount(sTree, tx)).to.be.equal(withdrawPreview); + + // add liquidity + await sTree.nodeAddLiquidity(TOKENS_100); + + /* + Liquidity tree structure after nodeAddLiquidity(TOKENS_100): + +---------------------------------------------------------------------------+ + | 2(640) | + +-----------------------------------+---------------------------------------+ + | 4(270) | 5(370) | + +-----------------+-----------------+-----------------+---------------------+ + | 8(90) | 9(180) | 10(180) | 11(190) | + +--------+--------+--------+--------+--------+--------+------------+--------+ + | 16(0) | 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(90) | 23(100)| + +--------+--------+--------+--------+--------+--------+------------+--------+ + 100 100 100 100 100 100 100 + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, tokens(640)); + await checkNodeAmountTo(sTree, 4, tokens(270)); + await checkNodeAmountTo(sTree, 5, tokens(370)); + await checkNodeAmountTo(sTree, 8, TOKENS_90); + await checkNodeAmountTo(sTree, 9, tokens(180)); + await checkNodeAmountTo(sTree, 10, tokens(180)); + await checkNodeAmountTo(sTree, 11, tokens(190)); + await checkNodeAmountTo(sTree, 23, TOKENS_100); + + //addLimit only for leaves [16-22], 23 not included + await sTree.addLimit(TOKENS_60, 22); + + /*+---------------------------------------------------------------------------+ + | 2(700) | + +-----------------------------------+---------------------------------------+ + | 4(300) | 5(400) | + +-----------------+-----------------+-----------------+---------------------+ + | 8(90) | 9(180) | 10(200) | 11(200) | + +--------+--------+--------+--------+--------+--------+------------+--------+ + | 16(0) | 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100) | 23(100)| + +--------+--------+--------+--------+--------+--------+------------+--------+ + 100 100 100 100 100 100 100 + + 2 changed 640 -> 700 + 4 changed 270 -> 300 by 30 + 5 changed 370 -> 400 by 30 + 23 not changed because it is excluded by addLimit(TOKENS_100, 22) + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, tokens(700)); + await checkNodeAmountTo(sTree, 4, tokens(300)); + await checkNodeAmountTo(sTree, 5, TOKENS_400); + await checkNodeAmountTo(sTree, 8, TOKENS_90); + await checkNodeAmountTo(sTree, 9, tokens(180)); + await checkNodeAmountTo(sTree, 10, TOKENS_200); + await checkNodeAmountTo(sTree, 11, TOKENS_200); + await checkNodeAmountTo(sTree, 22, TOKENS_100); + await checkNodeAmountTo(sTree, 23, TOKENS_100); + + // checksum correctness node = left child + right child + expect((await getNodeAmount(sTree, 4)) + (await getNodeAmount(sTree, 5))).to.be.equal( + await getNodeAmount(sTree, 2), + ); + expect((await getNodeAmount(sTree, 10)) + (await getNodeAmount(sTree, 11))).to.be.equal( + await getNodeAmount(sTree, 5), + ); + expect((await getNodeAmount(sTree, 23)) + (await getNodeAmount(sTree, 22))).to.be.equal( + await getNodeAmount(sTree, 11), + ); + // check leaves total 17-23 amount equal top node + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo( + sTree, + 2, + (await sTree.nodeWithdrawView(23)) + + (await sTree.nodeWithdrawView(22)) + + (await sTree.nodeWithdrawView(21)) + + (await sTree.nodeWithdrawView(20)) + + (await sTree.nodeWithdrawView(19)) + + (await sTree.nodeWithdrawView(18)) + + (await sTree.nodeWithdrawView(17)) + + (await sTree.nodeWithdrawView(16)), + ); + + //addLimit only for leaves [16-22], 23 not included + let node2amount = await getNodeAmount(sTree, 2); + let node4amount = await getNodeAmount(sTree, 4); + let node20amount = await sTree.nodeWithdrawView(20); + let node21amount = await sTree.nodeWithdrawView(21); + let node22amount = await sTree.nodeWithdrawView(22); + let node23amount = await sTree.nodeWithdrawView(23); + + await sTree.addLimit(TOKENS_100, 20); + /* + Liquidity tree structure after addLimit(100, 22): + +---------------------------------------------------------------------------+ + | 2(800) | + +-----------------------------------+---------------------------------------+ + | 4(375) | 5(425) | + +-----------------+-----------------+-----------------+---------------------+ + | 8(90) | 9(180) | 10(225) | 11(200) | + +--------+--------+--------+--------+--------+--------+------------+--------+ + | 16(0) | 17(100)| 18(100)| 19(100)| 20(125)| 21(100)| 22(100) | 23(100)| + +--------+--------+--------+--------+--------+--------+------------+--------+ + 100 100 100 100 100 100 100 + + 2 changed 700 -> 800 + 4 changed 300 -> 375 by 75 (100 * 3/4) + 5 changed 400 -> 425 by 25 (100 * 1/4) + 23 not changed because it is excluded by addLimit(TOKENS_100, 22) + */ + + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, node2amount + TOKENS_100); + await checkNodeAmountTo(sTree, 4, node4amount + tokens(75)); + await checkNodeAmountTo(sTree, 20, node20amount + tokens(25)); + await checkNodeAmountTo(sTree, 21, node21amount); + await checkNodeAmountTo(sTree, 22, node22amount); + await checkNodeAmountTo(sTree, 23, TOKENS_100); + + // checksum correctness node = left child + right child + expect((await getNodeAmount(sTree, 4)) + (await getNodeAmount(sTree, 5))).to.be.equal( + await getNodeAmount(sTree, 2), + ); + expect((await getNodeAmount(sTree, 10)) + (await getNodeAmount(sTree, 11))).to.be.equal( + await getNodeAmount(sTree, 5), + ); + expect((await getNodeAmount(sTree, 23)) + (await getNodeAmount(sTree, 22))).to.be.equal( + await getNodeAmount(sTree, 11), + ); + // check leaves total 17-23 amount equal top node + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo( + sTree, + 2, + (await sTree.nodeWithdrawView(23)) + + (await sTree.nodeWithdrawView(22)) + + (await sTree.nodeWithdrawView(21)) + + (await sTree.nodeWithdrawView(20)) + + (await sTree.nodeWithdrawView(19)) + + (await sTree.nodeWithdrawView(18)) + + (await sTree.nodeWithdrawView(17)) + + (await sTree.nodeWithdrawView(16)), + ); + + // withdraw all and check tree zeroed + for (const i of Array(8).keys()) await sTree.nodeWithdraw(i + 16); + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 7 leafs, remove limit for 7 leaves", async () => { + for (const i of Array(9).keys()) await sTree.nodeAddLiquidity(TOKENS_100); + /* + Liquidity tree structure after nodeAddLiquidity: + +--------------------------------------------------------------------------------------------------------------------+ + | 1(900) | + +-----------------------------------------------------------------------+--------------------------------------------+ + | 2(800) | 3(100) | + +-----------------------------------+-----------------------------------+------------------------+-------------------+ + | 4(400) | 5(400) | 6(100) | 7 | + +-----------------+-----------------+-----------------+-----------------+--------------+---------+---------+---------+ + | 8(200) | 9(200) | 10(200) | 11(200) | 12(100) | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+----+----+----+----+----+----+----+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100)| 23(100)| 24(100) | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+----+----+----+----+----+----+----+ + 100 100 100 100 100 100 100 100 100 + */ + + await checkNodeAmountTo(sTree, 1, tokens(900)); + await checkNodeAmountTo(sTree, 2, tokens(800)); + await checkNodeAmountTo(sTree, 3, TOKENS_100); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_400); + await checkNodeAmountTo(sTree, 6, TOKENS_100); + await checkNodeAmountTo(sTree, 7, ZERO); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_200); + await checkNodeAmountTo(sTree, 12, TOKENS_100); + + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 13, ZERO); + for (const i of Array(9).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_100); + + await sTree.removeLimit(tokens(210), 22); + /* + Liquidity tree structure after removeLimit(22, 210), #23, #24 must be unchanged: + +--------------------------------------------------------------------------------------------------------------------+ + | 1(690) | + +-----------------------------------------------------------------------+--------------------------------------------+ + | 2(590) | 3(100) | + +-----------------------------------+-----------------------------------+------------------------+-------------------+ + | 4(280) | 5(310) | 6(100) | 7 | + +-----------------+-----------------+-----------------+-----------------+--------------+---------+---------+---------+ + | 8(200) | 9(200) | 10(140) | 11(170) | 12(100) | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+----+----+----+----+----+----+----+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(70) | 23(100)| 24(100) | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+----+----+----+----+----+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, tokens(690)); + await checkNodeAmountTo(sTree, 2, tokens(590)); + await checkNodeAmountTo(sTree, 3, TOKENS_100); + await checkNodeAmountTo(sTree, 4, tokens(280)); + await checkNodeAmountTo(sTree, 5, tokens(310)); + await checkNodeAmountTo(sTree, 6, TOKENS_100); + await checkNodeAmountTo(sTree, 7, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, tokens(140)); + await checkNodeAmountTo(sTree, 11, tokens(170)); + await checkNodeAmountTo(sTree, 12, TOKENS_100); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 13, ZERO); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_100); + await checkNodeAmountTo(sTree, 22, tokens(70)); + await checkNodeAmountTo(sTree, 23, TOKENS_100); + await checkNodeAmountTo(sTree, 24, TOKENS_100); + + await sTree.addLimit(tokens(210), 22); + /* + Liquidity tree structure after addLimit(210, 22) + +--------------------------------------------------------------------------------------------------------------------+ + | 1(900) | + +-----------------------------------------------------------------------+--------------------------------------------+ + | 2(800) | 3(100) | + +-----------------------------------+-----------------------------------+------------------------+-------------------+ + | 4(400) | 5(400) | 6(100) | 7 | + +-----------------+-----------------+-----------------+-----------------+--------------+---------+---------+---------+ + | 8(200) | 9(200) | 10(200) | 11(200) | 12(100) | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+----+----+----+----+----+----+----+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100)| 23(100)| 24(100) | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+----+----+----+----+----+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, tokens(900)); + await checkNodeAmountTo(sTree, 2, tokens(800)); + await checkNodeAmountTo(sTree, 3, TOKENS_100); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_400); + await checkNodeAmountTo(sTree, 6, TOKENS_100); + await checkNodeAmountTo(sTree, 7, ZERO); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_200); + await checkNodeAmountTo(sTree, 12, TOKENS_100); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 13, ZERO); + for (const i of Array(9).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_100); + + for (const i of Array(9).keys()) expect(await sTree.nodeWithdrawView(i + 16)).to.be.eq(TOKENS_100); + for (const i of Array(9).keys()) await sTree.nodeWithdraw(i + 16); + + await checkTreeIsEmpty(sTree); + }); + describe("3 iterates with (add liquidity 100 and top remove 100), mixed addLimit", async () => { + beforeEach(async () => { + for (const i of Array(3).keys()) { + await sTree.nodeAddLiquidity(tokens(110 - i * 10)); + await sTree.remove(tokens(10 + 10 * i)); + } + + /* + Liquidity tree structure after mixed add + remove + +---------------------------------------+ + | 4(240) | + +-----------------+---------------------+ + | 8(160) | 9(80) | + +--------+--------+------------+--------+ + | 16(100)| 17(100)| 18(80) | 19 | + +--------+--------+------------+--------+ + 100 100 100 + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(240)); + await checkNodeAmountTo(sTree, 8, tokens(160)); + await checkNodeAmountTo(sTree, 9, tokens(80)); + await checkNodeAmountTo(sTree, 16, TOKENS_100); // added 110 and removed 10 + await checkNodeAmountTo(sTree, 17, TOKENS_100); // added 100 and not updated (lazy) + await checkNodeAmountTo(sTree, 18, tokens(80)); // added 90 and removed 10 + + expect(await sTree.nodeWithdrawView(16)).to.be.eq(tokens(80)); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(tokens(80)); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(tokens(80)); + }); + it("straight addings (liquidity returns)", async () => { + await sTree.addLimit(tokens(160), 16); + /* + Liquidity tree structure after addLimit(tokens(10), 16) + +---------------------------------------+ + | 4(400) | + +-----------------+---------------------+ + | 8(320) | 9(80) | + +--------+--------+------------+--------+ + | 16(240)| 17(80) | 18(80) | 19 | + +--------+--------+------------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 8, tokens(320)); + await checkNodeAmountTo(sTree, 9, tokens(80)); + await checkNodeAmountTo(sTree, 16, tokens(240)); // updated to 80 and added (limit) 160 + await checkNodeAmountTo(sTree, 17, tokens(80)); // updated from parent + await checkNodeAmountTo(sTree, 18, tokens(80)); // not changed + + expect(await sTree.nodeWithdrawView(16)).to.be.eq(tokens(240)); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(tokens(80)); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(tokens(80)); + + await sTree.addLimit(tokens(240), 17); + /* + Liquidity tree structure after addLimit(tokens(240), 17): + +---------------------------------------+ + | 4(640) | + +-----------------+---------------------+ + | 8(560) | 9(80) | + +--------+--------+------------+--------+ + | 16(240)| 17(80) | 18(80) | 19 | + +--------+--------+------------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(640)); + await checkNodeAmountTo(sTree, 8, tokens(560)); + await checkNodeAmountTo(sTree, 9, tokens(80)); + await checkNodeAmountTo(sTree, 16, tokens(240)); + await checkNodeAmountTo(sTree, 17, tokens(80)); + await checkNodeAmountTo(sTree, 18, tokens(80)); + + expect(await sTree.nodeWithdrawView(16)).to.be.eq(tokens(420)); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(tokens(140)); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(tokens(80)); + + await sTree.addLimit(tokens(160), 18); + /* + Liquidity tree structure after addLimit(tokens(160), 18): + +---------------------------------------+ + | 4(800) | + +-----------------+---------------------+ + | 8(700) | 9(100) | + +--------+--------+------------+--------+ + | 16(240)| 17(80) | 18(100) | 19 | + +--------+--------+------------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(800)); + await checkNodeAmountTo(sTree, 8, tokens(700)); // 140 of 160 + await checkNodeAmountTo(sTree, 9, TOKENS_100); // 20 of 160 + await checkNodeAmountTo(sTree, 16, tokens(240)); + await checkNodeAmountTo(sTree, 17, tokens(80)); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + + let withdrawView16 = await sTree.nodeWithdrawView(16); + let withdrawView17 = await sTree.nodeWithdrawView(17); + let withdrawView18 = await sTree.nodeWithdrawView(18); + + expect(withdrawView16).to.be.eq(tokens(525)); // 420 + 140 * 420/560 + expect(withdrawView17).to.be.eq(tokens(175)); // 140 + 140 * 140/560 + expect(withdrawView18).to.be.eq(TOKENS_100); + + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(16))).to.be.equal(withdrawView16); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(17))).to.be.equal(withdrawView17); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(18))).to.be.equal(withdrawView18); + }); + it("reverse addings", async () => { + await sTree.addLimit(tokens(30), 18); + /* + Liquidity tree structure after nodeAddLiquidity(TOKENS_30): + +---------------------------------------+ + | 4(270) | + +-----------------+---------------------+ + | 8(180) | 9(90) | + +--------+--------+------------+--------+ + | 16(100)| 17(100)| 18(90) | 19 | + +--------+--------+------------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(270)); + await checkNodeAmountTo(sTree, 8, tokens(180)); + await checkNodeAmountTo(sTree, 9, TOKENS_90); + await checkNodeAmountTo(sTree, 16, TOKENS_100); // added 110 and removed 10 + await checkNodeAmountTo(sTree, 17, TOKENS_100); // added 100 and not updated (lazy) + await checkNodeAmountTo(sTree, 18, TOKENS_90); + + expect(await sTree.nodeWithdrawView(16)).to.be.eq(TOKENS_90); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(TOKENS_90); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(TOKENS_90); + + await sTree.addLimit(tokens(20), 17); + /* + Liquidity tree structure after nodeAddLiquidity(TOKENS_20): + +---------------------------------------+ + | 4(290) | + +-----------------+---------------------+ + | 8(200) | 9(90) | + +--------+--------+------------+--------+ + | 16(90) | 17(90) | 18(90) | 19 | + +--------+--------+------------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(290)); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_90); + await checkNodeAmountTo(sTree, 16, TOKENS_90); // pushed changes before add + await checkNodeAmountTo(sTree, 17, TOKENS_90); // pushed changes before add + await checkNodeAmountTo(sTree, 18, TOKENS_90); + + expect(await sTree.nodeWithdrawView(16)).to.be.eq(TOKENS_100); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(TOKENS_100); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(TOKENS_90); + + await sTree.addLimit(tokens(10), 16); + /* + Liquidity tree structure after nodeAddLiquidity(TOKENS_10): + +---------------------------------------+ + | 4(300) | + +-----------------+---------------------+ + | 8(210) | 9(90) | + +--------+--------+------------+--------+ + | 16(110)| 17(100)| 18(90) | 19 | + +--------+--------+------------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(300)); + await checkNodeAmountTo(sTree, 8, tokens(210)); + await checkNodeAmountTo(sTree, 9, TOKENS_90); + await checkNodeAmountTo(sTree, 16, tokens(110)); + await checkNodeAmountTo(sTree, 17, TOKENS_100); // pushed changes + await checkNodeAmountTo(sTree, 18, TOKENS_90); + + expect(await sTree.nodeWithdrawView(16)).to.be.eq(tokens(110)); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(TOKENS_100); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(TOKENS_90); + + let withdrawView17 = await sTree.nodeWithdrawView(17); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(17))).to.be.equal(withdrawView17); + // get 50 % of leaf 18 (90) + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdrawPercent(18, WITHDRAW_50_PERCENT))).to.be.equal( + tokens(45), + ); + // get rest of leaf 18 (45) + let withdrawView18 = await sTree.nodeWithdrawView(18); + expect(withdrawView18).to.be.equal(tokens(45)); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(18))).to.be.equal(withdrawView18); + + //check double withdraw + withdrawView18 = await sTree.nodeWithdrawView(18); + expect(withdrawView18).to.be.equal(ZERO); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(18))).to.be.equal(withdrawView18); + + await sTree.removeLimit(tokens(10), 16); + /*+---------------------------------------+ + | 4(100) | + +-----------------+---------------------+ + | 8(100) | 9(0) | + +--------+--------+------------+--------+ + | 16(100)| 17(0) | 18(0) | 19 | + +--------+--------+------------+--------+*/ + for (const i of Array(3).keys()) { + await checkNodeAmountTo(sTree, 2 ** (i + 2), TOKENS_100); + } + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(16))).to.be.equal(TOKENS_100); + for (const i of Array(32).keys()) { + await checkNodeAmountTo(sTree, i + 1, ZERO); + } + }); + }); + }); + describe("small tree (16 leaves) with empty lists, static", async () => { + beforeEach(async () => { + sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, false); + }); + it("add liquidity to 2 leafs, withdraw first leaf, removeLimit first", async () => { + for (const i of Array(2).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + /* + Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(20) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(20) | 3 | + +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ + | 4(20) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(20) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(10) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, TOKENS_20); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_20); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_20); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_10); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + + // withdraw first leaf + await sTree.nodeWithdraw(16); + /* + Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(10) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(10) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(10) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(10) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, TOKENS_10); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_10); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_10); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.removeLimit(tokens(5), 16); + /* + Liquidity tree structure after removeLimit: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(5) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(5) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(5) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(5) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + + await checkNodeAmountTo(sTree, 1, TOKENS_5); + await checkNodeAmountTo(sTree, 2, TOKENS_5); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_5); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_5); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(TOKENS_5); + + await sTree.removeLimit(tokens(5), 16); + /* + Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(0) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(0) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(0) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(10) | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, ZERO); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, ZERO); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + + await checkNodeAmountTo(sTree, 17, TOKENS_5); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(17))).to.be.eq(ZERO); + + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 4 leafs, withdraw first 2 leaf, removeLimit first 2 leaves affected right 2 leaves", async () => { + for (const i of Array(4).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + /* + Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(40) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(40) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(40) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(20) | 9(20) | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(10) | 17(10) | 18(10) | 19(10) | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + */ + + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_40); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_40); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_20); + for (const i of Array(5).keys()) await checkNodeAmountTo(sTree, i + 10, ZERO); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); + + // withdraw first 2 leaves + await sTree.nodeWithdraw(16); + await sTree.nodeWithdraw(17); + /*Liquidity tree structure after nodeWithdraw: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(20) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(20) | 3 | + +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ + | 4(20) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9(20) | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(0) | 18(10) | 19(10) | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_20); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_20); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 9, TOKENS_20); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 10, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 18, TOKENS_10); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.removeLimit(tokens(10), 17); + /*Liquidity tree structure after removeLimit: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(10) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(10) | 3 | + +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ + | 4(10) | 5 | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9(20) | 10 | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(0) | 18(10) | 19(10) | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_10); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_10); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 9, TOKENS_20); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 10, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 18, TOKENS_10); + + expect(await sTree.nodeWithdrawView(17)).to.be.eq(ZERO); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(TOKENS_5); + expect(await sTree.nodeWithdrawView(19)).to.be.eq(TOKENS_5); + + // withdraw all liquidity, tree zeroed + await sTree.nodeWithdraw(18); + await sTree.nodeWithdraw(19); + + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 6 leafs, withdraw first 2 leaf, removeLimit first 2 leaves affected right 4 leaves", async () => { + for (const i of Array(2).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + for (const i of Array(2).keys()) { + await sTree.nodeAddLiquidity(tokens(3)); + } + for (const i of Array(2).keys()) { + await sTree.nodeAddLiquidity(tokens(12)); + } + /*Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(50) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(50) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(26) | 5(24) | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(20) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(10) | 17(10) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_50); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(26)); + await checkNodeAmountTo(sTree, 5, tokens(24)); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 6, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_20); + await checkNodeAmountTo(sTree, 9, tokens(6)); + await checkNodeAmountTo(sTree, 10, tokens(24)); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 11, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 18, tokens(3)); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 20, tokens(12)); + + // withdraw first 2 leaves + await sTree.nodeWithdraw(16); + await sTree.nodeWithdraw(17); + /*Liquidity tree structure after nodeWithdraw: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(30) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(30) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(6) | 5(24) | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_30); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(6)); + await checkNodeAmountTo(sTree, 5, tokens(24)); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 6, ZERO); + await checkNodeAmountTo(sTree, 9, tokens(6)); + await checkNodeAmountTo(sTree, 10, tokens(24)); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 11, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 18, tokens(3)); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 20, tokens(12)); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.removeLimit(tokens(10), 17); + + /*Liquidity tree structure after removeLimit: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(20) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(20) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(4) | 5(16) | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9(6) | 10(16) | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_20); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(4)); + await checkNodeAmountTo(sTree, 5, tokens(16)); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 6, ZERO); + await checkNodeAmountTo(sTree, 9, tokens(6)); + await checkNodeAmountTo(sTree, 10, tokens(16)); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 11, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 18, tokens(3)); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 20, tokens(12)); + + expect(await sTree.nodeWithdrawView(17)).to.be.eq(ZERO); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(tokens(2)); + expect(await sTree.nodeWithdrawView(19)).to.be.eq(tokens(2)); + expect(await sTree.nodeWithdrawView(20)).to.be.eq(tokens(8)); + expect(await sTree.nodeWithdrawView(21)).to.be.eq(tokens(8)); + + // withdraw all liquidity, tree zeroed + await sTree.nodeWithdraw(18); + await sTree.nodeWithdraw(19); + await sTree.nodeWithdraw(20); + await sTree.nodeWithdraw(21); + + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 6 leafs, withdraw first 2 leaf, addLimit first 2 leaves affected right 4 leaves", async () => { + for (const i of Array(2).keys()) await sTree.nodeAddLiquidity(TOKENS_10); + for (const i of Array(2).keys()) await sTree.nodeAddLiquidity(tokens(3)); + for (const i of Array(2).keys()) await sTree.nodeAddLiquidity(tokens(12)); + /* + Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(50) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(50) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(26) | 5(24) | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(20) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(10) | 17(10) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, TOKENS_50); + await checkNodeAmountTo(sTree, 2, TOKENS_50); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(26)); + await checkNodeAmountTo(sTree, 5, tokens(24)); + await checkNodeAmountTo(sTree, 6, ZERO); + await checkNodeAmountTo(sTree, 7, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_20); + await checkNodeAmountTo(sTree, 9, tokens(6)); + await checkNodeAmountTo(sTree, 10, tokens(24)); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 11, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_10); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + await checkNodeAmountTo(sTree, 18, tokens(3)); + await checkNodeAmountTo(sTree, 19, tokens(3)); + await checkNodeAmountTo(sTree, 20, tokens(12)); + await checkNodeAmountTo(sTree, 21, tokens(12)); + + // withdraw first 2 leaves + await sTree.nodeWithdraw(16); + await sTree.nodeWithdraw(17); + /* + Liquidity tree structure after nodeWithdraw: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(30) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(30) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(6) | 5(24) | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, TOKENS_30); + await checkNodeAmountTo(sTree, 2, TOKENS_30); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(6)); + await checkNodeAmountTo(sTree, 5, tokens(24)); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 6, ZERO); + await checkNodeAmountTo(sTree, 9, tokens(6)); + await checkNodeAmountTo(sTree, 10, tokens(24)); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 11, ZERO); + await checkNodeAmountTo(sTree, 18, tokens(3)); + await checkNodeAmountTo(sTree, 19, tokens(3)); + await checkNodeAmountTo(sTree, 20, tokens(12)); + await checkNodeAmountTo(sTree, 21, tokens(12)); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.addLimit(tokens(10), 17); + /* + Liquidity tree structure after removeLimit: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(40) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(40) | 3 | + +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ + | 4(8) | 5(32) | 6 | 7 | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(0) | 9(6) | 10(32) | 11 | 12 | 13 | 14 | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + */ + + await checkNodeAmountTo(sTree, 1, TOKENS_40); + await checkNodeAmountTo(sTree, 2, TOKENS_40); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, tokens(8)); + await checkNodeAmountTo(sTree, 5, tokens(32)); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 6, ZERO); + await checkNodeAmountTo(sTree, 9, tokens(6)); + await checkNodeAmountTo(sTree, 10, tokens(32)); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 11, ZERO); + await checkNodeAmountTo(sTree, 18, tokens(3)); + await checkNodeAmountTo(sTree, 19, tokens(3)); + await checkNodeAmountTo(sTree, 20, tokens(12)); + await checkNodeAmountTo(sTree, 21, tokens(12)); + + expect(await sTree.nodeWithdrawView(17)).to.be.eq(ZERO); + expect(await sTree.nodeWithdrawView(18)).to.be.eq(tokens(4)); + expect(await sTree.nodeWithdrawView(19)).to.be.eq(tokens(4)); + expect(await sTree.nodeWithdrawView(20)).to.be.eq(tokens(16)); + expect(await sTree.nodeWithdrawView(21)).to.be.eq(tokens(16)); + + // withdraw all liquidity, tree zeroed + for (const i of Array(4).keys()) await sTree.nodeWithdraw(i + 18); + + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 16 leafs, withdraw first 15 leaf, addLimit, removeLimit affected last leaf", async () => { + for (const i of Array(16).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + /*Liquidity tree structure after nodeAddLiquidity: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(160) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(80) | 3(80) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(40) | 5(40) | 6(40) | 7(40) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(20) | 9(20) | 10(20) | 11(20) | 12(20) | 13(20) | 14(20) | 15(20) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(10) | 17(10) | 18(10)| 19(10) | 20(10) | 21(10)| 22(10)| 23(10) | 24(10) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(10) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, tokens(160)); + await checkNodeAmountTo(sTree, 2, TOKENS_80); + await checkNodeAmountTo(sTree, 3, TOKENS_80); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 4, TOKENS_40); + for (const i of Array(8).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_20); + for (const i of Array(16).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); + + // withdraw first 15 leaves + for (const i of Array(15).keys()) await sTree.nodeWithdraw(i + 16); + + /*Liquidity tree structure after nodeWithdraw: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(10) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(0) | 3(10) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(0) | 6(0) | 7(10) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(0) | 12(0) | 13(0) | 14(0) | 15(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(0) | 24(0) | 25(0) | 26(0) | 27(0) | 28(0) | 29(0) | 30(0) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_10); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_10); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 4, ZERO); + await checkNodeAmountTo(sTree, 7, TOKENS_10); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 15, TOKENS_10); + for (const i of Array(15).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + await checkNodeAmountTo(sTree, 31, TOKENS_10); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.addLimit(tokens(5), 16); + /*Liquidity tree structure after addLimit: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(15) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(0) | 3(10) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(0) | 6(0) | 7(10) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(0) | 12(0) | 13(0) | 14(0) | 15(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(0) | 24(0) | 25(0) | 26(0) | 27(0) | 28(0) | 29(0) | 30(0) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, tokens(15)); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_10); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 4, ZERO); + await checkNodeAmountTo(sTree, 7, TOKENS_10); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 15, TOKENS_10); + for (const i of Array(15).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + await checkNodeAmountTo(sTree, 31, TOKENS_10); // unchanged because of lazy (update stoped at #1) + + expect(await sTree.nodeWithdrawView(31)).to.be.eq(tokens(15)); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.removeLimit(tokens(10), 30); + /*Liquidity tree structure after removeLimit: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(5) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(0) | 3(10) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(0) | 6(0) | 7(10) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(0) | 12(0) | 13(0) | 14(0) | 15(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(0) | 24(0) | 25(0) | 26(0) | 27(0) | 28(0) | 29(0) | 30(0) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_5); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, tokens(15)); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 4, ZERO); + await checkNodeAmountTo(sTree, 7, tokens(15)); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 15, tokens(15)); + for (const i of Array(15).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + await checkNodeAmountTo(sTree, 31, tokens(15)); + + // withdraw all liquidity, tree zeroed + await sTree.nodeWithdraw(31); + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 16 leafs, removeLimit for insufficient leaves (left subtree), affected last leaves", async () => { + for (const i of Array(16).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + /*Liquidity tree structure after nodeAddLiquidity: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(160) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(80) | 3(80) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(40) | 5(40) | 6(40) | 7(40) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(20) | 9(20) | 10(20) | 11(20) | 12(20) | 13(20) | 14(20) | 15(20) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(10) | 17(10) | 18(10)| 19(10) | 20(10) | 21(10)| 22(10)| 23(10) | 24(10) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(10) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, tokens(160)); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 2, TOKENS_80); // unchanged because of lazy (update stoped at #1) + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 4, TOKENS_40); // unchanged because of lazy (update stoped at #1) + for (const i of Array(8).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_20); // unchanged because of lazy (update stoped at #1) + for (const i of Array(16).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); // unchanged because of lazy (update stoped at #1) + + await sTree.removeLimit(tokens(96), 22); // insufficient for total 7*10 = 70 amount, remove from top + /* + Liquidity tree structure after nodeAddLiquidity: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(64) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(80) | 3(80) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(40) | 5(40) | 6(40) | 7(40) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(20) | 9(20) | 10(20) | 11(20) | 12(20) | 13(20) | 14(20) | 15(20) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(10) | 17(10) | 18(10)| 19(10) | 20(10) | 21(10)| 22(10)| 23(10) | 24(10) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(10) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(64)); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 2, TOKENS_80); // unchanged because of lazy (update stoped at #1) + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 4, TOKENS_40); // unchanged because of lazy (update stoped at #1) + for (const i of Array(8).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_20); // unchanged because of lazy (update stoped at #1) + for (const i of Array(16).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); // unchanged because of lazy (update stoped at #1) + + for (const i of Array(16).keys()) expect(await sTree.nodeWithdrawView(i + 16)).to.be.eq(tokens(4)); //10 - 6 for every leaf + + for (const i of Array(16).keys()) await sTree.nodeWithdraw(i + 16); + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 16 leafs, addLimit for zeroed leaves (left subtree), affected all non zero leaves", async () => { + for (const i of Array(16).keys()) await sTree.nodeAddLiquidity(TOKENS_10); + for (const i of Array(7).keys()) await sTree.nodeWithdraw(i + 16); + /*Liquidity tree structure after nodeAddLiquidity and removed #16 - #22: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(90) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(10) | 3(80) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(10) | 6(40) | 7(40) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(10) | 12(20) | 13(20) | 14(20) | 15(20) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(10) | 24(10) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(10) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_90); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_80); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, TOKENS_10); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 6, TOKENS_40); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 11, TOKENS_10); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 12, TOKENS_20); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + for (const i of Array(9).keys()) await checkNodeAmountTo(sTree, i + 23, TOKENS_10); + + await sTree.addLimit(TOKENS_90, 22); + /*Liquidity tree structure after addLimit to zeroed #22, add to all tree: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(180) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(10) | 3(80) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(10) | 6(40) | 7(40) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(10) | 12(20) | 13(20) | 14(20) | 15(20) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(10) | 24(10) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(10) | 31(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, tokens(180)); + // All rest nodes unchanged because of lazy (update stoped at #1) + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_80); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, TOKENS_10); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 6, TOKENS_40); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 11, TOKENS_10); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 12, TOKENS_20); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + for (const i of Array(9).keys()) await checkNodeAmountTo(sTree, i + 23, TOKENS_10); + + for (const i of Array(7).keys()) expect(await sTree.nodeWithdrawView(i + 16)).to.be.eq(ZERO); //not distributed on zero values + for (const i of Array(9).keys()) expect(await sTree.nodeWithdrawView(i + 23)).to.be.eq(TOKENS_20); //10 + 10 for every leaf from #23 + + // withdraw all and check tree zeroed + for (const i of Array(9).keys()) await sTree.nodeWithdraw(i + 23); + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 15 leafs, addLimit for zeroed leaves (right subtree), affected all non zero leaves", async () => { + for (const i of Array(15).keys()) await sTree.nodeAddLiquidity(TOKENS_10); + for (const i of Array(9).keys()) await sTree.nodeWithdraw(i + 16); + /*Liquidity tree structure after nodeAddLiquidity and removed #16 - #24: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(60) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(0) | 3(60) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(0) | 6(30) | 7(30) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(0) | 12(10) | 13(20) | 14(20) | 15(10) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(0) | 24(0) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(10) | 31(0) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_60); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_60); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 6, TOKENS_30); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 12, TOKENS_10); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 13, TOKENS_20); + await checkNodeAmountTo(sTree, 15, TOKENS_10); + for (const i of Array(9).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 25, TOKENS_10); + await checkNodeAmountTo(sTree, 31, ZERO); + + await sTree.addLimit(TOKENS_60, 24); + /*Liquidity tree structure after nodeAddLiquidity and removed #16 - #24: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(120) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(0) | 3(120) | + +-----------------------------------+-----------------------------------+---------------------------------------+-------------------------------------+ + | 4(0) | 5(0) | 6(60) | 7(60) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+-----------------+ + | 8(0) | 9(0) | 10(0) | 11(0) | 12(10) | 13(20) | 14(20) | 15(20) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+ + | 16(0) | 17(0) | 18(0) | 19(0) | 20(0) | 21(0) | 22(0) | 23(0) | 24(0) | 25(10) | 26(10) | 27(10) | 28(10) | 29(10) | 30(20) | 31(0) | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+--------+--------+*/ + await checkNodeAmountTo(sTree, 1, tokens(120)); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, tokens(120)); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 6, TOKENS_60); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 8, ZERO); + await checkNodeAmountTo(sTree, 12, TOKENS_10); + await checkNodeAmountTo(sTree, 13, TOKENS_20); + await checkNodeAmountTo(sTree, 14, TOKENS_40); + await checkNodeAmountTo(sTree, 15, TOKENS_20); + for (const i of Array(9).keys()) await checkNodeAmountTo(sTree, i + 16, ZERO); + for (const i of Array(5).keys()) await checkNodeAmountTo(sTree, i + 25, TOKENS_10); + await checkNodeAmountTo(sTree, 30, TOKENS_20); + await checkNodeAmountTo(sTree, 31, ZERO); + + // All add distributed to non zero leaves + for (const i of Array(6).keys()) expect(await sTree.nodeWithdrawView(i + 25)).to.be.eq(TOKENS_20); + + // withdraw all and check tree zeroed + for (const i of Array(6).keys()) await sTree.nodeWithdraw(i + 25); + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 22 leafs, top remove 7, withdraw leaf", async () => { + for (const i of Array(14).keys()) await sTree.nodeAddLiquidity(TOKENS_100); + /*Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(1400) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(800) | 3(600) | + +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ + | 4(400) | 5(400) | 6(400) | 7(200) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(200) | 9(200) | 10(200) | 11(200) | 12(200) | 13(200) | 14(200) | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100)| 23(100)| 24(100) | 25(100) | 26(100) | 27(100) | 28(100) | 29(100) | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + 100 100 100 100 100 100 100 100 100 100 100 100 100 100 */ + await checkNodeAmountTo(sTree, 1, tokens(1400)); + await checkNodeAmountTo(sTree, 2, tokens(800)); + await checkNodeAmountTo(sTree, 3, tokens(600)); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_400); + await checkNodeAmountTo(sTree, 6, TOKENS_400); + await checkNodeAmountTo(sTree, 7, TOKENS_200); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_200); + await checkNodeAmountTo(sTree, 11, TOKENS_200); + await checkNodeAmountTo(sTree, 12, TOKENS_200); + await checkNodeAmountTo(sTree, 13, TOKENS_200); + await checkNodeAmountTo(sTree, 14, TOKENS_200); + await checkNodeAmountTo(sTree, 16, TOKENS_100); + await checkNodeAmountTo(sTree, 17, TOKENS_100); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + await checkNodeAmountTo(sTree, 19, TOKENS_100); + await checkNodeAmountTo(sTree, 20, TOKENS_100); + await checkNodeAmountTo(sTree, 21, TOKENS_100); + await checkNodeAmountTo(sTree, 22, TOKENS_100); + await checkNodeAmountTo(sTree, 23, TOKENS_100); + await checkNodeAmountTo(sTree, 24, TOKENS_100); + await checkNodeAmountTo(sTree, 25, TOKENS_100); + await checkNodeAmountTo(sTree, 26, TOKENS_100); + await checkNodeAmountTo(sTree, 27, TOKENS_100); + await checkNodeAmountTo(sTree, 28, TOKENS_100); + await checkNodeAmountTo(sTree, 29, TOKENS_100); + + // withdraw part of nodes + await sTree.nodeWithdraw(21); + await sTree.nodeWithdraw(22); + await sTree.nodeWithdraw(23); + await sTree.nodeWithdraw(24); + await sTree.nodeWithdraw(25); + await sTree.nodeWithdraw(28); + await sTree.nodeWithdraw(29); + + /* + Liquidity tree structure after nodeAddLiquidity: + +---------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(700) | + +-----------------------------------------------------------------------+---------------------------------------------------------------------+ + | 2(500) | 3(200) | + +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ + | 4(400) | 5(100) | 6(200) | 7(0) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ + | 8(200) | 9(200) | 10(100) | 11(0) | 12(0) | 13(200) | 14(0) | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(0) | 22(0) | 23(0) | 24(0) | 25(0) | 26(100) | 27(100) | 28(0) | 29(0) | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + 100 100 100 100 100 100 100 + */ + await checkNodeAmountTo(sTree, 1, tokens(700)); + await checkNodeAmountTo(sTree, 2, tokens(500)); + await checkNodeAmountTo(sTree, 3, TOKENS_200); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_100); + await checkNodeAmountTo(sTree, 6, TOKENS_200); + await checkNodeAmountTo(sTree, 7, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_100); + await checkNodeAmountTo(sTree, 11, ZERO); + await checkNodeAmountTo(sTree, 12, ZERO); + await checkNodeAmountTo(sTree, 13, TOKENS_200); + await checkNodeAmountTo(sTree, 14, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_100); + await checkNodeAmountTo(sTree, 17, TOKENS_100); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + await checkNodeAmountTo(sTree, 19, TOKENS_100); + await checkNodeAmountTo(sTree, 20, TOKENS_100); + await checkNodeAmountTo(sTree, 21, ZERO); + await checkNodeAmountTo(sTree, 22, ZERO); + await checkNodeAmountTo(sTree, 23, ZERO); + await checkNodeAmountTo(sTree, 24, ZERO); + await checkNodeAmountTo(sTree, 25, ZERO); + await checkNodeAmountTo(sTree, 26, TOKENS_100); + await checkNodeAmountTo(sTree, 27, TOKENS_100); + await checkNodeAmountTo(sTree, 28, ZERO); + await checkNodeAmountTo(sTree, 29, ZERO); + + // remove 7 + await sTree.remove(tokens(7)); + + /* + Liquidity tree structure after nodeAddLiquidity: + +-----------------------------------------------------------------------------------------------------------------------------------------------------+ + | 1(693) | + +-----------------------------------------------------------------------+-----------------------------------------------------------------------------+ + | 2(495) | 3(198) | + +-----------------------------------+---------------+-----------------------------------------------------------+-------------------------------------+ + | 4(400) | 5(100) | 6(198) | 7(0) | + +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+---------------------------+---------+ + | 8(200) | 9(200) | 10(100) | 11(0) | 12(0) | 13(200) | 14(0) | 15 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+--------------+------------+----+----+ + | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(0) | 22(0) | 23(0) | 24(0) | 25(0) | 26(100) | 27(100) | 28(0) | 29(0) | 30 | 31 | + +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+--------------+------------+----+----+ + 100 100 100 100 100 100 100 + */ + + await checkNodeAmountTo(sTree, 1, tokens(693)); + await checkNodeAmountTo(sTree, 2, tokens(495)); + await checkNodeAmountTo(sTree, 3, tokens(198)); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_100); + await checkNodeAmountTo(sTree, 6, tokens(198)); + await checkNodeAmountTo(sTree, 7, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_100); + await checkNodeAmountTo(sTree, 11, ZERO); + await checkNodeAmountTo(sTree, 12, ZERO); + await checkNodeAmountTo(sTree, 13, TOKENS_200); + await checkNodeAmountTo(sTree, 14, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_100); + await checkNodeAmountTo(sTree, 17, TOKENS_100); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + await checkNodeAmountTo(sTree, 19, TOKENS_100); + await checkNodeAmountTo(sTree, 20, TOKENS_100); + await checkNodeAmountTo(sTree, 21, ZERO); + await checkNodeAmountTo(sTree, 22, ZERO); + await checkNodeAmountTo(sTree, 23, ZERO); + await checkNodeAmountTo(sTree, 24, ZERO); + await checkNodeAmountTo(sTree, 25, ZERO); + await checkNodeAmountTo(sTree, 26, TOKENS_100); + await checkNodeAmountTo(sTree, 27, TOKENS_100); + await checkNodeAmountTo(sTree, 28, ZERO); + await checkNodeAmountTo(sTree, 29, ZERO); + + expect(await sTree.nodeWithdrawView(20)).to.be.equal(tokens(99)); + }); + }); + describe("small tree (16 leaves) with empty lists, dynamic ", async () => { + beforeEach(async () => { + sTree = await prepareTree(ethers, SMALL_TREE_LEAFS, true); + }); + it("add liquidity to 2 leafs, withdraw first leaf, removeLimit first", async () => { + for (const i of Array(2).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + /* + Liquidity tree structure after nodeAddLiquidity: + +-----------------+ + | 8(20) | + +--------+--------+ + | 16(10) | 17(10) | + +--------+--------+ + */ + + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_20); + for (const i of Array(6).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_10); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + + // withdraw first leaf + await sTree.nodeWithdraw(16); + /* + Liquidity tree structure after nodeAddLiquidity: + +-----------------+ + | 8(10) | + +--------+--------+ + | 16(0) | 17(10) | + +--------+--------+ + */ + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_10); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + + // remove limit with first leaf. Leaf is emty, removing from right leaf's branch + await sTree.removeLimit(tokens(5), 16); + /* + Liquidity tree structure after removeLimit: + +-----------------+ + | 8(5) | + +--------+--------+ + | 16(0) | 17(10) | + +--------+--------+ + */ + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_5); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + await checkNodeAmountTo(sTree, 17, TOKENS_10); + expect(await sTree.nodeWithdrawView(17)).to.be.eq(TOKENS_5); + + await sTree.removeLimit(tokens(5), 16); + /* + Liquidity tree structure after nodeAddLiquidity: + +-----------------+ + | 8(0) | + +--------+--------+ + | 16(0) | 17(10) | + +--------+--------+ + */ + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + await checkNodeAmountTo(sTree, 8, ZERO); + for (const i of Array(7).keys()) await checkNodeAmountTo(sTree, i + 9, ZERO); + + await checkNodeAmountTo(sTree, 17, TOKENS_5); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(17))).to.be.eq(ZERO); + + await checkTreeIsEmpty(sTree); + }); + it("add liquidity to 4 leafs, withdraw first 2 leaf, removeLimit first 2 leaves affected right 2 leaves", async () => { + for (const i of Array(4).keys()) { + await sTree.nodeAddLiquidity(TOKENS_10); + } + /* + Liquidity tree structure after nodeAddLiquidity: + +-----------------------------------+ + | 4(40) | + +-----------------+-----------------+ + | 8(20) | 9(20) | + +--------+--------+--------+--------+ + | 16(10) | 17(10) | 18(10) | 19(10) | + +--------+--------+--------+--------+ + */ + + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); + await checkNodeAmountTo(sTree, 3, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_40); + for (const i of Array(3).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 8, TOKENS_20); + for (const i of Array(5).keys()) await checkNodeAmountTo(sTree, i + 10, ZERO); + for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 16, TOKENS_10); + + // withdraw first 2 leaves + await sTree.nodeWithdraw(16); + await sTree.nodeWithdraw(17); + /*Liquidity tree structure after nodeWithdraw: + +-----------------------------------+ + | 4(20) | + +-----------------+-----------------+ + | 8(0) | 9(20) | + +--------+--------+--------+--------+ + | 16(0) | 17(0) | 18(10) | 19(10) | + +--------+--------+--------+--------+ + */ - for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_20); + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, TOKENS_20); for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); @@ -1039,19 +2509,15 @@ describe("LiquidityTree", () => { // remove limit with first leaf. Leaf is emty, removing from right leaf's branch await sTree.removeLimit(tokens(10), 17); /*Liquidity tree structure after removeLimit: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(10) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(10) | 3 | - +-----------------------------------+---------------+-----------------------------------------------------------+-----------------------------+ - | 4(10) | 5 | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9(20) | 10 | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(0) | 18(10) | 19(10) | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ - await checkNodeAmountTo(sTree, 1, TOKENS_10); - await checkNodeAmountTo(sTree, 2, TOKENS_10); + +-----------------------------------+ + | 4(10) | + +-----------------+-----------------+ + | 8(0) | 9(20) | + +--------+--------+--------+--------+ + | 16(0) | 17(0) | 18(10) | 19(10) | + +--------+--------+--------+--------+ + */ + for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, 2 ** i, ZERO); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, TOKENS_10); for (const i of Array(4).keys()) await checkNodeAmountTo(sTree, i + 5, ZERO); @@ -1080,18 +2546,18 @@ describe("LiquidityTree", () => { await sTree.nodeAddLiquidity(tokens(12)); } /*Liquidity tree structure after nodeAddLiquidity: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(50) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(50) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(26) | 5(24) | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(20) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(10) | 17(10) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ - for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_50); + +-----------------------------------------------------------------------+ + | 2(50) | + +-----------------------------------+-----------------------------------+ + | 4(26) | 5(24) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(20) | 9(6) | 10(24) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(10) | 17(10) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, TOKENS_50); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, tokens(26)); await checkNodeAmountTo(sTree, 5, tokens(24)); @@ -1108,18 +2574,18 @@ describe("LiquidityTree", () => { await sTree.nodeWithdraw(16); await sTree.nodeWithdraw(17); /*Liquidity tree structure after nodeWithdraw: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(30) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(30) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(6) | 5(24) | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ - for (const i of Array(2).keys()) await checkNodeAmountTo(sTree, i + 1, TOKENS_30); + +-----------------------------------------------------------------------+ + | 2(30) | + +-----------------------------------+-----------------------------------+ + | 4(6) | 5(24) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(0) | 9(6) | 10(24) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, TOKENS_30); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, tokens(6)); await checkNodeAmountTo(sTree, 5, tokens(24)); @@ -1134,18 +2600,17 @@ describe("LiquidityTree", () => { await sTree.removeLimit(tokens(10), 17); /*Liquidity tree structure after removeLimit: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(20) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(20) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(4) | 5(16) | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9(6) | 10(16) | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+*/ - await checkNodeAmountTo(sTree, 1, TOKENS_20); + +-----------------------------------------------------------------------+ + | 2(20) | + +-----------------------------------+-----------------------------------+ + | 4(4) | 5(16) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(0) | 9(6) | 10(16) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + */ + await checkNodeAmountTo(sTree, 1, ZERO); await checkNodeAmountTo(sTree, 2, TOKENS_20); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, tokens(4)); @@ -1177,20 +2642,17 @@ describe("LiquidityTree", () => { for (const i of Array(2).keys()) await sTree.nodeAddLiquidity(tokens(12)); /* Liquidity tree structure after nodeAddLiquidity: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(50) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(50) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(26) | 5(24) | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(20) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(10) | 17(10) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + +-----------------------------------------------------------------------+ + | 2(50) | + +-----------------------------------+-----------------------------------+ + | 4(26) | 5(24) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(20) | 9(6) | 10(24) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(10) | 17(10) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ */ - - await checkNodeAmountTo(sTree, 1, TOKENS_50); + await checkNodeAmountTo(sTree, 1, ZERO); await checkNodeAmountTo(sTree, 2, TOKENS_50); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, tokens(26)); @@ -1213,20 +2675,16 @@ describe("LiquidityTree", () => { await sTree.nodeWithdraw(17); /* Liquidity tree structure after nodeWithdraw: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(30) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(30) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(6) | 5(24) | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9(6) | 10(24) | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + +-----------------------------------------------------------------------+ + | 2(30) | + +-----------------------------------+-----------------------------------+ + | 4(6) | 5(24) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(0) | 9(6) | 10(24) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ */ - - await checkNodeAmountTo(sTree, 1, TOKENS_30); await checkNodeAmountTo(sTree, 2, TOKENS_30); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, tokens(6)); @@ -1244,20 +2702,18 @@ describe("LiquidityTree", () => { await sTree.addLimit(tokens(10), 17); /* Liquidity tree structure after removeLimit: - +---------------------------------------------------------------------------------------------------------------------------------------------+ - | 1(40) | - +-----------------------------------------------------------------------+---------------------------------------------------------------------+ - | 2(40) | 3 | - +-----------------------------------+-----------------------------------+---------------------------------------+-----------------------------+ - | 4(8) | 5(32) | 6 | 7 | - +-----------------+-----------------+-----------------+-----------------+-------------------+-------------------+-------------------+---------+ - | 8(0) | 9(6) | 10(32) | 11 | 12 | 13 | 14 | 15 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ - | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | - +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ + +-----------------------------------------------------------------------+ + | 2(40) | + +-----------------------------------+-----------------------------------+ + | 4(8) | 5(32) | + +-----------------+-----------------+-----------------+-----------------+ + | 8(0) | 9(6) | 10(32) | 11 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + | 16(0) | 17(0) | 18(3) | 19(3) | 20(12) | 21(12)| 22 | 23 | + +--------+--------+--------+--------+--------+--------+--------+--------+ */ - await checkNodeAmountTo(sTree, 1, TOKENS_40); + await checkNodeAmountTo(sTree, 1, ZERO); await checkNodeAmountTo(sTree, 2, TOKENS_40); await checkNodeAmountTo(sTree, 3, ZERO); await checkNodeAmountTo(sTree, 4, tokens(8)); @@ -1572,34 +3028,34 @@ describe("LiquidityTree", () => { | 16(100)| 17(100)| 18(100)| 19(100)| 20(100)| 21(100)| 22(100)| 23(100)| 24(100) | 25(100) | 26(100) | 27(100) | 28(100) | 29(100) | 30 | 31 | +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ 100 100 100 100 100 100 100 100 100 100 100 100 100 100 */ - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(1400)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(tokens(800)); - expect(await getNodeAmount(sTree, 3)).to.be.equal(tokens(600)); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_400); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_400); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_400); - expect(await getNodeAmount(sTree, 7)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 8)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 9)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 10)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 11)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 12)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 13)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 14)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 16)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 17)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 18)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 19)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 20)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 21)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 22)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 23)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 24)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 25)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 26)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 27)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 28)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 29)).to.be.equal(TOKENS_100); + await checkNodeAmountTo(sTree, 1, tokens(1400)); + await checkNodeAmountTo(sTree, 2, tokens(800)); + await checkNodeAmountTo(sTree, 3, tokens(600)); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_400); + await checkNodeAmountTo(sTree, 6, TOKENS_400); + await checkNodeAmountTo(sTree, 7, TOKENS_200); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_200); + await checkNodeAmountTo(sTree, 11, TOKENS_200); + await checkNodeAmountTo(sTree, 12, TOKENS_200); + await checkNodeAmountTo(sTree, 13, TOKENS_200); + await checkNodeAmountTo(sTree, 14, TOKENS_200); + await checkNodeAmountTo(sTree, 16, TOKENS_100); + await checkNodeAmountTo(sTree, 17, TOKENS_100); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + await checkNodeAmountTo(sTree, 19, TOKENS_100); + await checkNodeAmountTo(sTree, 20, TOKENS_100); + await checkNodeAmountTo(sTree, 21, TOKENS_100); + await checkNodeAmountTo(sTree, 22, TOKENS_100); + await checkNodeAmountTo(sTree, 23, TOKENS_100); + await checkNodeAmountTo(sTree, 24, TOKENS_100); + await checkNodeAmountTo(sTree, 25, TOKENS_100); + await checkNodeAmountTo(sTree, 26, TOKENS_100); + await checkNodeAmountTo(sTree, 27, TOKENS_100); + await checkNodeAmountTo(sTree, 28, TOKENS_100); + await checkNodeAmountTo(sTree, 29, TOKENS_100); // withdraw part of nodes await sTree.nodeWithdraw(21); @@ -1625,34 +3081,34 @@ describe("LiquidityTree", () => { +--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+---------+---------+---------+---------+----+----+ 100 100 100 100 100 100 100 */ - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(700)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(tokens(500)); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_400); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 7)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 8)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 9)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 10)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 11)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 12)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 13)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 14)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 16)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 17)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 18)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 19)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 20)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 21)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 22)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 23)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 24)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 25)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 26)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 27)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 28)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 29)).to.be.equal(ZERO); + await checkNodeAmountTo(sTree, 1, tokens(700)); + await checkNodeAmountTo(sTree, 2, tokens(500)); + await checkNodeAmountTo(sTree, 3, TOKENS_200); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_100); + await checkNodeAmountTo(sTree, 6, TOKENS_200); + await checkNodeAmountTo(sTree, 7, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_100); + await checkNodeAmountTo(sTree, 11, ZERO); + await checkNodeAmountTo(sTree, 12, ZERO); + await checkNodeAmountTo(sTree, 13, TOKENS_200); + await checkNodeAmountTo(sTree, 14, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_100); + await checkNodeAmountTo(sTree, 17, TOKENS_100); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + await checkNodeAmountTo(sTree, 19, TOKENS_100); + await checkNodeAmountTo(sTree, 20, TOKENS_100); + await checkNodeAmountTo(sTree, 21, ZERO); + await checkNodeAmountTo(sTree, 22, ZERO); + await checkNodeAmountTo(sTree, 23, ZERO); + await checkNodeAmountTo(sTree, 24, ZERO); + await checkNodeAmountTo(sTree, 25, ZERO); + await checkNodeAmountTo(sTree, 26, TOKENS_100); + await checkNodeAmountTo(sTree, 27, TOKENS_100); + await checkNodeAmountTo(sTree, 28, ZERO); + await checkNodeAmountTo(sTree, 29, ZERO); // remove 7 await sTree.remove(tokens(7)); @@ -1673,39 +3129,39 @@ describe("LiquidityTree", () => { 100 100 100 100 100 100 100 */ - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(693)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(tokens(495)); - expect(await getNodeAmount(sTree, 3)).to.be.equal(tokens(198)); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_400); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 6)).to.be.equal(tokens(198)); - expect(await getNodeAmount(sTree, 7)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 8)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 9)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 10)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 11)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 12)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 13)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 14)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 16)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 17)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 18)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 19)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 20)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 21)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 22)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 23)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 24)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 25)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 26)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 27)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 28)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 29)).to.be.equal(ZERO); + await checkNodeAmountTo(sTree, 1, tokens(693)); + await checkNodeAmountTo(sTree, 2, tokens(495)); + await checkNodeAmountTo(sTree, 3, tokens(198)); + await checkNodeAmountTo(sTree, 4, TOKENS_400); + await checkNodeAmountTo(sTree, 5, TOKENS_100); + await checkNodeAmountTo(sTree, 6, tokens(198)); + await checkNodeAmountTo(sTree, 7, ZERO); + await checkNodeAmountTo(sTree, 8, TOKENS_200); + await checkNodeAmountTo(sTree, 9, TOKENS_200); + await checkNodeAmountTo(sTree, 10, TOKENS_100); + await checkNodeAmountTo(sTree, 11, ZERO); + await checkNodeAmountTo(sTree, 12, ZERO); + await checkNodeAmountTo(sTree, 13, TOKENS_200); + await checkNodeAmountTo(sTree, 14, ZERO); + await checkNodeAmountTo(sTree, 16, TOKENS_100); + await checkNodeAmountTo(sTree, 17, TOKENS_100); + await checkNodeAmountTo(sTree, 18, TOKENS_100); + await checkNodeAmountTo(sTree, 19, TOKENS_100); + await checkNodeAmountTo(sTree, 20, TOKENS_100); + await checkNodeAmountTo(sTree, 21, ZERO); + await checkNodeAmountTo(sTree, 22, ZERO); + await checkNodeAmountTo(sTree, 23, ZERO); + await checkNodeAmountTo(sTree, 24, ZERO); + await checkNodeAmountTo(sTree, 25, ZERO); + await checkNodeAmountTo(sTree, 26, TOKENS_100); + await checkNodeAmountTo(sTree, 27, TOKENS_100); + await checkNodeAmountTo(sTree, 28, ZERO); + await checkNodeAmountTo(sTree, 29, ZERO); expect(await sTree.nodeWithdrawView(20)).to.be.equal(tokens(99)); }); }); - describe("Example tree (4 leaves)", async () => { + describe("Example tree (4 leaves) static", async () => { before(async () => { sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); }); @@ -1719,10 +3175,10 @@ describe("LiquidityTree", () => { +-------------+-----------+---------+---------+ | 4 (100$) | 5 (200$) | 6 (0$) | 7 (0$) | +-------------+-----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_200); + await checkNodeAmountTo(sTree, 1, TOKENS_300); + await checkNodeAmountTo(sTree, 2, TOKENS_300); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + await checkNodeAmountTo(sTree, 5, TOKENS_200); }); it("remove(30$) and addliquidity", async () => { await sTree.remove(TOKENS_30); @@ -1733,10 +3189,10 @@ describe("LiquidityTree", () => { +-------------+-----------+---------+---------+ | 4 (100$) | 5 (200$) | 6 (0$) | 7 (0$) | +-------------+-----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_270); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_270); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_200); + await checkNodeAmountTo(sTree, 1, TOKENS_270); + await checkNodeAmountTo(sTree, 2, TOKENS_270); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + await checkNodeAmountTo(sTree, 5, TOKENS_200); await sTree.nodeAddLiquidity(TOKENS_300); /*+----------------------------------------------+ @@ -1746,12 +3202,121 @@ describe("LiquidityTree", () => { +-------------+-----------+----------+---------+ | 4 (100$) | 5 (200$) | 6 (300$) | 7 (0$) | +-------------+-----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(570)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_270); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_100); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_200); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_300); + await checkNodeAmountTo(sTree, 1, tokens(570)); + await checkNodeAmountTo(sTree, 2, TOKENS_270); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + await checkNodeAmountTo(sTree, 5, TOKENS_200); + await checkNodeAmountTo(sTree, 6, TOKENS_300); + }); + it("addLimit(15$, #5)", async () => { + await sTree.addLimit(tokens(15), 5); + /*+----------------------------------------------+ + | 1 (585$) | + +-------------------------+--------------------+ + | 2 (285$) | 3 (300$) | + +-------------+-----------+----------+---------+ + | 4 (100$) | 5 (200$) | 6 (300$) | 7 (0$) | + +-------------+-----------+---------+---------+*/ + await checkNodeAmountTo(sTree, 1, tokens(585)); + await checkNodeAmountTo(sTree, 2, tokens(285)); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, TOKENS_90); + await checkNodeAmountTo(sTree, 5, tokens(180)); + await checkNodeAmountTo(sTree, 6, TOKENS_300); + }); + it("nodeWithdraw(4)", async () => { + let withdrawAmount4 = await sTree.nodeWithdrawView(4); + let tx4 = await sTree.nodeWithdraw(4); + /*+----------------------------------------------+ + | 1 (490$) | + +-------------------------+--------------------+ + | 2 (190$) | 3 (300$) | + +-------------+-----------+----------+---------+ + | 4 (0$) | 5 (200$) | 6 (300$) | 7 (0$) | + +-------------+-----------+---------+---------+*/ + await checkNodeAmountTo(sTree, 1, tokens(490)); + await checkNodeAmountTo(sTree, 2, TOKENS_190); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, TOKENS_190); + await checkNodeAmountTo(sTree, 6, TOKENS_300); + expect(withdrawAmount4).to.be.equal(tokens(95)); + expect(await getWithdrawnAmount(sTree, tx4)).to.be.equal(withdrawAmount4); + }); + it("nodeWithdraw(5)", async () => { + let withdrawAmount5 = await sTree.nodeWithdrawView(5); + let tx5 = await sTree.nodeWithdraw(5); + /*+----------------------------------------------+ + | 1 (300$) | + +-------------------------+--------------------+ + | 2 (0$) | 3 (300$) | + +-------------+-----------+----------+---------+ + | 4 (0$) | 5 (0$) | 6 (300$) | 7 (0$) | + +-------------+-----------+---------+---------+*/ + await checkNodeAmountTo(sTree, 1, TOKENS_300); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_300); + expect(withdrawAmount5).to.be.equal(TOKENS_190); + expect(await getWithdrawnAmount(sTree, tx5)).to.be.equal(withdrawAmount5); + }); + }); + describe("Example tree (4 leaves) dynamic", async () => { + before(async () => { + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, true); + }); + it("nodeAddLiquidity(100$)", async () => { + await sTree.nodeAddLiquidity(TOKENS_100); + /*+-------------+ + | 4 (100$) | + +-------------+*/ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + }); + it("nodeAddLiquidity(100$)", async () => { + await sTree.nodeAddLiquidity(TOKENS_200); + /*+-------------------------+ + | 2 (300$) | + +-------------+-----------+ + | 4 (100$) | 5 (200$) | + +-------------+-----------+*/ + + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, TOKENS_300); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + await checkNodeAmountTo(sTree, 5, TOKENS_200); + }); + it("remove(30$) and addliquidity", async () => { + await sTree.remove(TOKENS_30); + /*+-------------------------+ + | 2 (270$) | + +-------------+-----------+ + | 4 (100$) | 5 (200$) | + +-------------+-----------+*/ + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, TOKENS_270); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + await checkNodeAmountTo(sTree, 5, TOKENS_200); + + await sTree.nodeAddLiquidity(TOKENS_300); + /*+----------------------------------------------+ + | 1 (570$) | + +-------------------------+--------------------+ + | 2 (270$) | 3 (300$) | + +-------------+-----------+----------+---------+ + | 4 (100$) | 5 (200$) | 6 (300$) | 7 (0$) | + +-------------+-----------+---------+---------+*/ + + await checkNodeAmountTo(sTree, 1, tokens(570)); + await checkNodeAmountTo(sTree, 2, TOKENS_270); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, TOKENS_100); + await checkNodeAmountTo(sTree, 5, TOKENS_200); + await checkNodeAmountTo(sTree, 6, TOKENS_300); }); it("addLimit(15$, #5)", async () => { await sTree.addLimit(tokens(15), 5); @@ -1762,12 +3327,12 @@ describe("LiquidityTree", () => { +-------------+-----------+----------+---------+ | 4 (100$) | 5 (200$) | 6 (300$) | 7 (0$) | +-------------+-----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(585)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(tokens(285)); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_90); - expect(await getNodeAmount(sTree, 5)).to.be.equal(tokens(180)); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_300); + await checkNodeAmountTo(sTree, 1, tokens(585)); + await checkNodeAmountTo(sTree, 2, tokens(285)); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, TOKENS_90); + await checkNodeAmountTo(sTree, 5, tokens(180)); + await checkNodeAmountTo(sTree, 6, TOKENS_300); }); it("nodeWithdraw(4)", async () => { let withdrawAmount4 = await sTree.nodeWithdrawView(4); @@ -1779,12 +3344,12 @@ describe("LiquidityTree", () => { +-------------+-----------+----------+---------+ | 4 (0$) | 5 (200$) | 6 (300$) | 7 (0$) | +-------------+-----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(490)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_190); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 4)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_190); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_300); + await checkNodeAmountTo(sTree, 1, tokens(490)); + await checkNodeAmountTo(sTree, 2, TOKENS_190); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, TOKENS_190); + await checkNodeAmountTo(sTree, 6, TOKENS_300); expect(withdrawAmount4).to.be.equal(tokens(95)); expect(await getWithdrawnAmount(sTree, tx4)).to.be.equal(withdrawAmount4); }); @@ -1798,12 +3363,12 @@ describe("LiquidityTree", () => { +-------------+-----------+----------+---------+ | 4 (0$) | 5 (0$) | 6 (300$) | 7 (0$) | +-------------+-----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 2)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_300); - expect(await getNodeAmount(sTree, 4)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 5)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_300); + await checkNodeAmountTo(sTree, 1, TOKENS_300); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_300); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_300); expect(withdrawAmount5).to.be.equal(TOKENS_190); expect(await getWithdrawnAmount(sTree, tx5)).to.be.equal(withdrawAmount5); }); @@ -1818,13 +3383,13 @@ describe("LiquidityTree", () => { await sTree.nodeAddLiquidity(TOKENS_10); await sTree.nodeAddLiquidity(TOKENS_10); - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_40); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 7)).to.be.equal(TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_40); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + await checkNodeAmountTo(sTree, 4, TOKENS_10); + await checkNodeAmountTo(sTree, 5, TOKENS_10); + await checkNodeAmountTo(sTree, 6, TOKENS_10); + await checkNodeAmountTo(sTree, 7, TOKENS_10); /* +--------------------------------------------+ @@ -1839,13 +3404,13 @@ describe("LiquidityTree", () => { it("add 40$ to the whole tree", async () => { await sTree.add(tokens(40)); - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_80); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 7)).to.be.equal(TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_80); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + await checkNodeAmountTo(sTree, 4, TOKENS_10); + await checkNodeAmountTo(sTree, 5, TOKENS_10); + await checkNodeAmountTo(sTree, 6, TOKENS_10); + await checkNodeAmountTo(sTree, 7, TOKENS_10); /* +--------------------------------------------+ @@ -1860,13 +3425,13 @@ describe("LiquidityTree", () => { it("withdraw whole liquidity from leaf #5", async () => { await sTree.nodeWithdrawPercent(5, WITHDRAW_100_PERCENT); - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_60); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_40); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 5)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 7)).to.be.equal(TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_60); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, TOKENS_40); + await checkNodeAmountTo(sTree, 4, TOKENS_20); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_10); + await checkNodeAmountTo(sTree, 7, TOKENS_10); /* +--------------------------------------------+ @@ -1881,13 +3446,13 @@ describe("LiquidityTree", () => { it("add liquidity 50$ on tree for only leaves 4,5,6", async () => { await sTree.addLimit(TOKENS_50, 6); - expect(await getNodeAmount(sTree, 1)).to.be.equal(tokens(110)); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 3)).to.be.equal(tokens(65)); - expect(await getNodeAmount(sTree, 4)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 5)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 6)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 7)).to.be.equal(TOKENS_20); + await checkNodeAmountTo(sTree, 1, tokens(110)); + await checkNodeAmountTo(sTree, 2, TOKENS_45); + await checkNodeAmountTo(sTree, 3, tokens(65)); + await checkNodeAmountTo(sTree, 4, TOKENS_20); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_45); + await checkNodeAmountTo(sTree, 7, TOKENS_20); /* +--------------------------------------------+ @@ -1915,13 +3480,13 @@ describe("LiquidityTree", () => { +-------------+----------+---------+---------+ */ - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 2)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 4)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 5)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 6)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 7)).to.be.equal(TOKENS_20); + await checkNodeAmountTo(sTree, 1, TOKENS_20); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, ZERO); + await checkNodeAmountTo(sTree, 7, TOKENS_20); }); }); describe("Example tree (4 leaves) fair distribution removing from range with 0", async () => { @@ -2164,9 +3729,9 @@ describe("LiquidityTree", () => { await sTree.nodeAddLiquidity(TOKENS_10); await sTree.nodeAddLiquidity(TOKENS_10); - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_20); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_20); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_10); /* +------------------------+ | 1 (20$) | @@ -2178,9 +3743,9 @@ describe("LiquidityTree", () => { it("add 20$ to the whole tree", async () => { await sTree.add(TOKENS_20); - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_40); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_10); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_40); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_10); /* +------------------------+ | 1 (40$) | @@ -2192,9 +3757,9 @@ describe("LiquidityTree", () => { it("add 10$ to the leaf #2", async () => { await sTree.addLimit(TOKENS_10, 2); - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_50); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_30); - expect(await getNodeAmount(sTree, 3)).to.be.equal(TOKENS_20); + await checkNodeAmountTo(sTree, 1, TOKENS_50); + await checkNodeAmountTo(sTree, 2, TOKENS_30); + await checkNodeAmountTo(sTree, 3, TOKENS_20); /* +------------------------+ | 1 (50$) | @@ -2231,10 +3796,10 @@ describe("LiquidityTree", () => { | 8 (0$) | 9 (0$) | 10 (0$) | 11 (45$)| 12 (0$) | 13 (0$) | 14 (0$)| 15 (0$)| +-------------+----------+---------+---------+-------------+----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 2)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 11)).to.be.equal(TOKENS_45); + await checkNodeAmountTo(sTree, 1, TOKENS_45); + await checkNodeAmountTo(sTree, 2, TOKENS_45); + await checkNodeAmountTo(sTree, 5, TOKENS_45); + await checkNodeAmountTo(sTree, 11, TOKENS_45); // Remove all current liquidity from the tree await sTree.remove(TOKENS_45); @@ -2249,10 +3814,10 @@ describe("LiquidityTree", () => { | 8 (0$) | 9 (0$) | 10 (0$) | 11 (45$)| 12 (0$) | 13 (0$) | 14 (0$)| 15 (0$)| +-------------+----------+---------+---------+-------------+----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 2)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 11)).to.be.equal(TOKENS_45); + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 5, TOKENS_45); + await checkNodeAmountTo(sTree, 11, TOKENS_45); // Add another node so 'tree.updateId' propagates back to the root when we do a push await sTree.nodeAddLiquidity(1); @@ -2270,10 +3835,10 @@ describe("LiquidityTree", () => { +-------------+----------+---------+---------+-------------+----------+---------+---------+ | 8 (0$) | 9 (0$) | 10 (0$) | 11 (45$)| 12 (0$) | 13 (0$) | 14 (0$)| 15 (0$)| +-------------+----------+---------+---------+-------------+----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 2)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 5)).to.be.equal(TOKENS_45); - expect(await getNodeAmount(sTree, 11)).to.be.equal(TOKENS_45); + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 5, TOKENS_45); + await checkNodeAmountTo(sTree, 11, TOKENS_45); // Withdraw 0 percent, this should do nothing, just updates (push) actual values from top to the leaf. await sTree.nodeWithdrawPercent(11, 0); @@ -2288,10 +3853,10 @@ describe("LiquidityTree", () => { +-------------+----------+---------+---------+-------------+----------+---------+---------+ | 8 (0$) | 9 (0$) | 10 (0$) | 11 (0$) | 12 (0$) | 13 (0$) | 14 (0$)| 15 (0$)| +-------------+----------+---------+---------+-------------+----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 2)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 5)).to.be.equal(ZERO); - expect(await getNodeAmount(sTree, 11)).to.be.equal(ZERO); + await checkNodeAmountTo(sTree, 1, ZERO); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 11, ZERO); }); it("add liquidity add/withdraw, removeLimit and remove", async () => { await sTree.nodeAddLiquidity(3); // leaf #8 @@ -2498,7 +4063,7 @@ describe("LiquidityTree", () => { +-------------+----------+---------+---------+-------------+----------+---------+---------+ | 8 (0$) | 9 (0$) | 10 (0$) | 11 (0$) | 12 (0$) | 13 (0$) | 14 (0$)| 15 (0$)| +-------------+----------+---------+---------+-------------+----------+---------+---------+*/ - expect(await getNodeAmount(sTree, 1)).to.be.equal(1); + await checkNodeAmountTo(sTree, 1, 1); for (const i of Array(15).keys()) await checkNodeAmountTo(sTree, i + 2, 0); await sTree.remove(1); From 370f1a1fdb50ff6a9a2d0f5d0967b832ad231e37 Mon Sep 17 00:00:00 2001 From: Maksim Kiselev Date: Thu, 17 Apr 2025 11:10:55 +0200 Subject: [PATCH 4/4] Added dynamic version to part of tests --- test/liquidityTree-test.js | 763 +++++++++++++++++++------------------ 1 file changed, 387 insertions(+), 376 deletions(-) diff --git a/test/liquidityTree-test.js b/test/liquidityTree-test.js index 0611a91..b7010e1 100644 --- a/test/liquidityTree-test.js +++ b/test/liquidityTree-test.js @@ -3373,400 +3373,411 @@ describe("LiquidityTree", () => { expect(await getWithdrawnAmount(sTree, tx5)).to.be.equal(withdrawAmount5); }); }); - describe("Example tree (4 leaves) fair distribution", async () => { - before(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); - }); - it("add liquidity 10$ in each of 4 leafs", async () => { - await sTree.nodeAddLiquidity(TOKENS_10); - await sTree.nodeAddLiquidity(TOKENS_10); - await sTree.nodeAddLiquidity(TOKENS_10); - await sTree.nodeAddLiquidity(TOKENS_10); + IS_DYNAMIC.forEach((isDynamic) => { + describe(`Example tree (4 leaves) fair distribution ${isDynamic ? "dynamic" : "static"}`, async () => { + before(async () => { + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, isDynamic); + }); + it("add liquidity 10$ in each of 4 leafs", async () => { + await sTree.nodeAddLiquidity(TOKENS_10); + await sTree.nodeAddLiquidity(TOKENS_10); + await sTree.nodeAddLiquidity(TOKENS_10); + await sTree.nodeAddLiquidity(TOKENS_10); - await checkNodeAmountTo(sTree, 1, TOKENS_40); - await checkNodeAmountTo(sTree, 2, TOKENS_20); - await checkNodeAmountTo(sTree, 3, TOKENS_20); - await checkNodeAmountTo(sTree, 4, TOKENS_10); - await checkNodeAmountTo(sTree, 5, TOKENS_10); - await checkNodeAmountTo(sTree, 6, TOKENS_10); - await checkNodeAmountTo(sTree, 7, TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_40); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + await checkNodeAmountTo(sTree, 4, TOKENS_10); + await checkNodeAmountTo(sTree, 5, TOKENS_10); + await checkNodeAmountTo(sTree, 6, TOKENS_10); + await checkNodeAmountTo(sTree, 7, TOKENS_10); - /* - +--------------------------------------------+ - | 1 (40$) | - +------------------------+-------------------+ - | 2 (20$) | 3 (20$) | - +-------------+----------+---------+---------+ - | 4 (10$) | 5 (10$) | 6 (10$) | 7 (10$) | - +-------------+----------+---------+---------+ - */ - }); - it("add 40$ to the whole tree", async () => { - await sTree.add(tokens(40)); + /* + +--------------------------------------------+ + | 1 (40$) | + +------------------------+-------------------+ + | 2 (20$) | 3 (20$) | + +-------------+----------+---------+---------+ + | 4 (10$) | 5 (10$) | 6 (10$) | 7 (10$) | + +-------------+----------+---------+---------+ + */ + }); + it("add 40$ to the whole tree", async () => { + await sTree.add(tokens(40)); - await checkNodeAmountTo(sTree, 1, TOKENS_80); - await checkNodeAmountTo(sTree, 2, TOKENS_20); - await checkNodeAmountTo(sTree, 3, TOKENS_20); - await checkNodeAmountTo(sTree, 4, TOKENS_10); - await checkNodeAmountTo(sTree, 5, TOKENS_10); - await checkNodeAmountTo(sTree, 6, TOKENS_10); - await checkNodeAmountTo(sTree, 7, TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_80); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + await checkNodeAmountTo(sTree, 4, TOKENS_10); + await checkNodeAmountTo(sTree, 5, TOKENS_10); + await checkNodeAmountTo(sTree, 6, TOKENS_10); + await checkNodeAmountTo(sTree, 7, TOKENS_10); - /* - +--------------------------------------------+ - | 1 (80$) | - +------------------------+-------------------+ - | 2 (20$) | 3 (20$) | - +-------------+----------+---------+---------+ - | 4 (10$) | 5 (10$) | 6 (10$) | 7 (10$) | - +-------------+----------+---------+---------+ - */ - }); - it("withdraw whole liquidity from leaf #5", async () => { - await sTree.nodeWithdrawPercent(5, WITHDRAW_100_PERCENT); + /* + +--------------------------------------------+ + | 1 (80$) | + +------------------------+-------------------+ + | 2 (20$) | 3 (20$) | + +-------------+----------+---------+---------+ + | 4 (10$) | 5 (10$) | 6 (10$) | 7 (10$) | + +-------------+----------+---------+---------+ + */ + }); + it("withdraw whole liquidity from leaf #5", async () => { + await sTree.nodeWithdrawPercent(5, WITHDRAW_100_PERCENT); - await checkNodeAmountTo(sTree, 1, TOKENS_60); - await checkNodeAmountTo(sTree, 2, TOKENS_20); - await checkNodeAmountTo(sTree, 3, TOKENS_40); - await checkNodeAmountTo(sTree, 4, TOKENS_20); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, TOKENS_10); - await checkNodeAmountTo(sTree, 7, TOKENS_10); + await checkNodeAmountTo(sTree, 1, TOKENS_60); + await checkNodeAmountTo(sTree, 2, TOKENS_20); + await checkNodeAmountTo(sTree, 3, TOKENS_40); + await checkNodeAmountTo(sTree, 4, TOKENS_20); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_10); + await checkNodeAmountTo(sTree, 7, TOKENS_10); - /* - +--------------------------------------------+ - | 1 (60$) | - +------------------------+-------------------+ - | 2 (20$) | 3 (40$) | - +-------------+----------+---------+---------+ - | 4 (20$) | 5 (0$) | 6 (10$) | 7 (10$) | - +-------------+----------+---------+---------+ - */ - }); - it("add liquidity 50$ on tree for only leaves 4,5,6", async () => { - await sTree.addLimit(TOKENS_50, 6); + /* + +--------------------------------------------+ + | 1 (60$) | + +------------------------+-------------------+ + | 2 (20$) | 3 (40$) | + +-------------+----------+---------+---------+ + | 4 (20$) | 5 (0$) | 6 (10$) | 7 (10$) | + +-------------+----------+---------+---------+ + */ + }); + it("add liquidity 50$ on tree for only leaves 4,5,6", async () => { + await sTree.addLimit(TOKENS_50, 6); - await checkNodeAmountTo(sTree, 1, tokens(110)); - await checkNodeAmountTo(sTree, 2, TOKENS_45); - await checkNodeAmountTo(sTree, 3, tokens(65)); - await checkNodeAmountTo(sTree, 4, TOKENS_20); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, TOKENS_45); - await checkNodeAmountTo(sTree, 7, TOKENS_20); + await checkNodeAmountTo(sTree, 1, tokens(110)); + await checkNodeAmountTo(sTree, 2, TOKENS_45); + await checkNodeAmountTo(sTree, 3, tokens(65)); + await checkNodeAmountTo(sTree, 4, TOKENS_20); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_45); + await checkNodeAmountTo(sTree, 7, TOKENS_20); - /* - +--------------------------------------------+ - | 1 (110$) | - +------------------------+-------------------+ - | 2 (45$) | 3 (65$) | - +-------------+----------+---------+---------+ - | 4 (20$) | 5 (0$) | 6 (45$) | 7 (20$) | - +-------------+----------+---------+---------+ - */ - let withdrawView4 = await sTree.nodeWithdrawView(4); - let withdrawView6 = await sTree.nodeWithdrawView(6); - expect(withdrawView4).to.be.equal(TOKENS_45); - expect(withdrawView6).to.be.equal(TOKENS_45); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(4))).to.be.equal(withdrawView4); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(6))).to.be.equal(withdrawView6); + /* + +--------------------------------------------+ + | 1 (110$) | + +------------------------+-------------------+ + | 2 (45$) | 3 (65$) | + +-------------+----------+---------+---------+ + | 4 (20$) | 5 (0$) | 6 (45$) | 7 (20$) | + +-------------+----------+---------+---------+ + */ + let withdrawView4 = await sTree.nodeWithdrawView(4); + let withdrawView6 = await sTree.nodeWithdrawView(6); + expect(withdrawView4).to.be.equal(TOKENS_45); + expect(withdrawView6).to.be.equal(TOKENS_45); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(4))).to.be.equal(withdrawView4); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(6))).to.be.equal(withdrawView6); - /* - +--------------------------------------------+ - | 1 (20$) | - +------------------------+-------------------+ - | 2 (0$) | 3 (20$) | - +-------------+----------+---------+---------+ - | 4 (0$) | 5 (0$) | 6 (0$) | 7 (20$) | - +-------------+----------+---------+---------+ - */ + /* + +--------------------------------------------+ + | 1 (20$) | + +------------------------+-------------------+ + | 2 (0$) | 3 (20$) | + +-------------+----------+---------+---------+ + | 4 (0$) | 5 (0$) | 6 (0$) | 7 (20$) | + +-------------+----------+---------+---------+ + */ - await checkNodeAmountTo(sTree, 1, TOKENS_20); - await checkNodeAmountTo(sTree, 2, ZERO); - await checkNodeAmountTo(sTree, 3, TOKENS_20); - await checkNodeAmountTo(sTree, 4, ZERO); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, ZERO); - await checkNodeAmountTo(sTree, 7, TOKENS_20); + await checkNodeAmountTo(sTree, 1, TOKENS_20); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, ZERO); + await checkNodeAmountTo(sTree, 7, TOKENS_20); + }); }); }); - describe("Example tree (4 leaves) fair distribution removing from range with 0", async () => { - beforeEach(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); - }); - it("add liquidity 60$ for all leaves on tree, withdraw #5, removeLimit(50, 6)", async () => { - for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); - await sTree.nodeWithdraw(5); - /* - +--------------------------------------------+ - | 1 (180$) | - +------------------------+-------------------+ - | 2 (60$) | 3 (120$) | - +-------------+----------+---------+---------+ - | 4 (60$) | 5 (0$) | 6 (60$) | 7 (60$) | - +-------------+----------+---------+---------+ - */ - await checkNodeAmountTo(sTree, 1, tokens(180)); - await checkNodeAmountTo(sTree, 2, TOKENS_60); - await checkNodeAmountTo(sTree, 3, tokens(120)); - await checkNodeAmountTo(sTree, 4, TOKENS_60); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, TOKENS_60); - await checkNodeAmountTo(sTree, 7, TOKENS_60); - - await sTree.nodeWithdraw(6); - await sTree.removeLimit(TOKENS_50, 6); - /* - +--------------------------------------------+ - | 1 (70$) | - +------------------------+-------------------+ - | 2 (10$) | 3 (60$) | - +-------------+----------+---------+---------+ - | 4 (60$) | 5 (0$) | 6 (0$) | 7 (60$) | - +-------------+----------+---------+---------+ - */ - await checkNodeAmountTo(sTree, 1, tokens(70)); - await checkNodeAmountTo(sTree, 2, TOKENS_10); - await checkNodeAmountTo(sTree, 3, TOKENS_60); - await checkNodeAmountTo(sTree, 4, TOKENS_60); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, ZERO); - await checkNodeAmountTo(sTree, 7, TOKENS_60); - }); - it("add liquidity 60$ for all leaves on tree, withdraw #5, addLimit(50, 6)", async () => { - for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); - await sTree.nodeWithdraw(5); - /* - +--------------------------------------------+ - | 1 (180$) | - +------------------------+-------------------+ - | 2 (60$) | 3 (120$) | - +-------------+----------+---------+---------+ - | 4 (60$) | 5 (0$) | 6 (60$) | 7 (60$) | - +-------------+----------+---------+---------+ - */ - await checkNodeAmountTo(sTree, 1, tokens(180)); - await checkNodeAmountTo(sTree, 2, TOKENS_60); - await checkNodeAmountTo(sTree, 3, tokens(120)); - await checkNodeAmountTo(sTree, 4, TOKENS_60); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, TOKENS_60); - await checkNodeAmountTo(sTree, 7, TOKENS_60); - - await sTree.nodeWithdraw(6); - await sTree.addLimit(TOKENS_50, 6); - /* - +--------------------------------------------+ - | 1 (170$) | - +------------------------+-------------------+ - | 2 (110$) | 3 (60$) | - +-------------+----------+---------+---------+ - | 4 (60$) | 5 (0$) | 6 (0$) | 7 (60$) | - +-------------+----------+---------+---------+ - */ - await checkNodeAmountTo(sTree, 1, tokens(170)); - await checkNodeAmountTo(sTree, 2, tokens(110)); - await checkNodeAmountTo(sTree, 3, TOKENS_60); - await checkNodeAmountTo(sTree, 4, TOKENS_60); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, ZERO); - await checkNodeAmountTo(sTree, 7, TOKENS_60); - }); - it("add liquidity 60$ for all leaves on tree, withdraw #4, #5, addLimit(50, 6)", async () => { - for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); - await sTree.nodeWithdraw(4); - await sTree.nodeWithdraw(5); - /* - +--------------------------------------------+ - | 1 (120$) | - +------------------------+-------------------+ - | 2 (0$) | 3 (120$) | - +-------------+----------+---------+---------+ - | 4 (0$) | 5 (0$) | 6 (60$) | 7 (60$) | - +-------------+----------+---------+---------+ - */ - await checkNodeAmountTo(sTree, 1, tokens(120)); - await checkNodeAmountTo(sTree, 2, ZERO); - await checkNodeAmountTo(sTree, 3, tokens(120)); - await checkNodeAmountTo(sTree, 4, ZERO); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, TOKENS_60); - await checkNodeAmountTo(sTree, 7, TOKENS_60); + IS_DYNAMIC.forEach((isDynamic) => { + describe(`Example tree (4 leaves) fair distribution removing from range with 0 ${isDynamic ? "dynamic" : "static"}`, async () => { + beforeEach(async () => { + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, isDynamic); + }); + it("add liquidity 60$ for all leaves on tree, withdraw #5, removeLimit(50, 6)", async () => { + for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); + await sTree.nodeWithdraw(5); + /* + +--------------------------------------------+ + | 1 (180$) | + +------------------------+-------------------+ + | 2 (60$) | 3 (120$) | + +-------------+----------+---------+---------+ + | 4 (60$) | 5 (0$) | 6 (60$) | 7 (60$) | + +-------------+----------+---------+---------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(180)); + await checkNodeAmountTo(sTree, 2, TOKENS_60); + await checkNodeAmountTo(sTree, 3, tokens(120)); + await checkNodeAmountTo(sTree, 4, TOKENS_60); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_60); + await checkNodeAmountTo(sTree, 7, TOKENS_60); + + await sTree.nodeWithdraw(6); + await sTree.removeLimit(TOKENS_50, 6); + /* + +--------------------------------------------+ + | 1 (70$) | + +------------------------+-------------------+ + | 2 (10$) | 3 (60$) | + +-------------+----------+---------+---------+ + | 4 (60$) | 5 (0$) | 6 (0$) | 7 (60$) | + +-------------+----------+---------+---------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(70)); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_60); + await checkNodeAmountTo(sTree, 4, TOKENS_60); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, ZERO); + await checkNodeAmountTo(sTree, 7, TOKENS_60); + }); + it("add liquidity 60$ for all leaves on tree, withdraw #5, addLimit(50, 6)", async () => { + for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); + await sTree.nodeWithdraw(5); + /* + +--------------------------------------------+ + | 1 (180$) | + +------------------------+-------------------+ + | 2 (60$) | 3 (120$) | + +-------------+----------+---------+---------+ + | 4 (60$) | 5 (0$) | 6 (60$) | 7 (60$) | + +-------------+----------+---------+---------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(180)); + await checkNodeAmountTo(sTree, 2, TOKENS_60); + await checkNodeAmountTo(sTree, 3, tokens(120)); + await checkNodeAmountTo(sTree, 4, TOKENS_60); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_60); + await checkNodeAmountTo(sTree, 7, TOKENS_60); + + await sTree.nodeWithdraw(6); + await sTree.addLimit(TOKENS_50, 6); + /* + +--------------------------------------------+ + | 1 (170$) | + +------------------------+-------------------+ + | 2 (110$) | 3 (60$) | + +-------------+----------+---------+---------+ + | 4 (60$) | 5 (0$) | 6 (0$) | 7 (60$) | + +-------------+----------+---------+---------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(170)); + await checkNodeAmountTo(sTree, 2, tokens(110)); + await checkNodeAmountTo(sTree, 3, TOKENS_60); + await checkNodeAmountTo(sTree, 4, TOKENS_60); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, ZERO); + await checkNodeAmountTo(sTree, 7, TOKENS_60); + }); + it("add liquidity 60$ for all leaves on tree, withdraw #4, #5, addLimit(50, 6)", async () => { + for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(TOKENS_60); + await sTree.nodeWithdraw(4); + await sTree.nodeWithdraw(5); + /* + +--------------------------------------------+ + | 1 (120$) | + +------------------------+-------------------+ + | 2 (0$) | 3 (120$) | + +-------------+----------+---------+---------+ + | 4 (0$) | 5 (0$) | 6 (60$) | 7 (60$) | + +-------------+----------+---------+---------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(120)); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, tokens(120)); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, TOKENS_60); + await checkNodeAmountTo(sTree, 7, TOKENS_60); - await sTree.addLimit(TOKENS_50, 6); - /* - +--------------------------------------------+ - | 1 (170$) | - +------------------------+-------------------+ - | 2 (0$) | 3 (170$) | - +-------------+----------+---------+---------+ - | 4 (0$) | 5 (0$) | 6 (110$)| 7 (60$) | - +-------------+----------+---------+---------+ - */ - await checkNodeAmountTo(sTree, 1, tokens(170)); - await checkNodeAmountTo(sTree, 2, ZERO); - await checkNodeAmountTo(sTree, 3, tokens(170)); - await checkNodeAmountTo(sTree, 4, ZERO); - await checkNodeAmountTo(sTree, 5, ZERO); - await checkNodeAmountTo(sTree, 6, tokens(110)); - await checkNodeAmountTo(sTree, 7, TOKENS_60); + await sTree.addLimit(TOKENS_50, 6); + /* + +--------------------------------------------+ + | 1 (170$) | + +------------------------+-------------------+ + | 2 (0$) | 3 (170$) | + +-------------+----------+---------+---------+ + | 4 (0$) | 5 (0$) | 6 (110$)| 7 (60$) | + +-------------+----------+---------+---------+ + */ + await checkNodeAmountTo(sTree, 1, tokens(170)); + await checkNodeAmountTo(sTree, 2, ZERO); + await checkNodeAmountTo(sTree, 3, tokens(170)); + await checkNodeAmountTo(sTree, 4, ZERO); + await checkNodeAmountTo(sTree, 5, ZERO); + await checkNodeAmountTo(sTree, 6, tokens(110)); + await checkNodeAmountTo(sTree, 7, TOKENS_60); + }); }); }); - describe("Example tree (4 leaves) fair distribution Alice, Bob, Clarc", async () => { - beforeEach(async () => { - sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, false); - }); - it("There are 10000$ of liquidity, Bob added 1000$, remove 2000$ lose of leaves 4, 5, Clarc not affected", async () => { - // Alice and Bob added - await sTree.nodeAddLiquidity(tokens(5000)); - await sTree.nodeAddLiquidity(tokens(5000)); - /*+---------------------------------------------+ - | 1 (10000$) | - +-------------------------+-------------------+ - | 2 (10000$) | 3 (0$) | - +-------------+-----------+---------+---------+ - | 4 (5000$) | 5 (5000$) | 6 (0$) | 7 (0$) | - +-------------+-----------+---------+---------+ - ^ ^ - Alice | Bob | */ - - // Clarc added - await sTree.nodeAddLiquidity(tokens(1000)); - /*+------------------------------------------------+ - | 1 (11000$) | - +-------------------------+----------------------+ - | 2 (10000$) | 3 (1000$) | - +-------------+-----------+------------+---------+ - | 4 (5000$) | 5 (5000$) | 6 (1000$) | 7 (0$) | - +-------------+-----------+------------+---------+ - ^ - Clarc | */ - - // remove 2000$, afects only leaves 4, 5 - await sTree.removeLimit(tokens(2000), 5); - - /*+------------------------------------------------+ - | 1 (9000$) | - +-------------------------+----------------------+ - | 2 (8000$) | 3 (1000$) | - +-------------+-----------+------------+---------+ - | 4 (5000$) | 5 (4000$) | 6 (1000$) | 7 (0$) | - +-------------+-----------+------------+---------+*/ - checkNodeAmountTo(sTree, 1, tokens(9000)); - checkNodeAmountTo(sTree, 2, tokens(8000)); - checkNodeAmountTo(sTree, 3, tokens(1000)); - checkNodeAmountTo(sTree, 4, tokens(5000)); // not affected because of lazy update - checkNodeAmountTo(sTree, 5, tokens(5000)); // not affected because of lazy update - checkNodeAmountTo(sTree, 6, tokens(1000)); - checkNodeAmountTo(sTree, 7, tokens(0)); - - expect(await sTree.nodeWithdrawView(4)).to.be.equal(tokens(4000)); - expect(await sTree.nodeWithdrawView(5)).to.be.equal(tokens(4000)); - expect(await sTree.nodeWithdrawView(6)).to.be.equal(tokens(1000)); - - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(4))).to.be.equal(tokens(4000)); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(5))).to.be.equal(tokens(4000)); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(6))).to.be.equal(tokens(1000)); - }); - it("There are 15000$ of liquidity, Bob added 1000$, remove 3000$ lose of leaves 4, 5, 6. Clarc affected", async () => { - // Alice, Bob and Clarc added - await sTree.nodeAddLiquidity(tokens(5000)); - await sTree.nodeAddLiquidity(tokens(5000)); - await sTree.nodeAddLiquidity(tokens(5000)); - /*+------------------------------------------------+ - | 1 (15000$) | - +-------------------------+----------------------+ - | 2 (10000$) | 3 (5000$) | - +-------------+-----------+------------+---------+ - | 4 (5000$) | 5 (5000$) | 6 (5000$) | 7 (0$) | - +-------------+-----------+------------+---------+ - ^ ^ ^ - Alice | Bob | Clarc | */ - - // condition resolves with 3000$ lose of LP (afects only leaves 4, 5, 6) - await sTree.removeLimit(tokens(3000), 6); - - /*+------------------------------------------------+ - | 1 (12000$) | - +-------------------------+----------------------+ - | 2 (10000$) | 3 (4000$) | - +-------------+-----------+------------+---------+ - | 4 (5000$) | 5 (5000$) | 6 (4000$) | 7 (0$) | - +-------------+-----------+------------+---------+*/ - checkNodeAmountTo(sTree, 1, tokens(12000)); - checkNodeAmountTo(sTree, 2, tokens(10000)); - checkNodeAmountTo(sTree, 3, tokens(4000)); - checkNodeAmountTo(sTree, 4, tokens(5000)); // not affected because of lazy update - checkNodeAmountTo(sTree, 5, tokens(5000)); // not affected because of lazy update - checkNodeAmountTo(sTree, 6, tokens(4000)); - checkNodeAmountTo(sTree, 7, tokens(0)); - - expect(await sTree.nodeWithdrawView(4)).to.be.equal(tokens(4000)); - expect(await sTree.nodeWithdrawView(5)).to.be.equal(tokens(4000)); - expect(await sTree.nodeWithdrawView(6)).to.be.equal(tokens(4000)); - - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(4))).to.be.equal(tokens(4000)); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(5))).to.be.equal(tokens(4000)); - expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(6))).to.be.equal(tokens(4000)); - }); - it("Try add 0 liquidity", async () => { - await expect(sTree.nodeAddLiquidity(0)).to.be.revertedWithCustomError(sTree, "IncorrectAmount"); - }); - it("Try add liquidity to already filled leaves range", async () => { - // fill up keaves range - for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(tokens(1)); - await expect(sTree.nodeAddLiquidity(tokens(1))).to.be.revertedWithCustomError(sTree, "LeafNumberRangeExceeded"); - }); - it("Try remove more liquidity than exists", async () => { - await sTree.nodeAddLiquidity(TOKENS_10); - await expect(sTree.removeLimit(TOKENS_100, 4)).to.be.revertedWithCustomError(sTree, "InsufficientTopNodeAmount"); + IS_DYNAMIC.forEach((isDynamic) => { + describe(`Example tree (4 leaves) fair distribution Alice, Bob, Clarc ${isDynamic ? "dynamic" : "static"}`, async () => { + beforeEach(async () => { + sTree = await prepareTree(ethers, EXAMPLE_TREE_LEAFS, isDynamic); + }); + it("There are 10000$ of liquidity, Bob added 1000$, remove 2000$ lose of leaves 4, 5, Clarc not affected", async () => { + // Alice and Bob added + await sTree.nodeAddLiquidity(tokens(5000)); + await sTree.nodeAddLiquidity(tokens(5000)); + /*+---------------------------------------------+ + | 1 (10000$) | + +-------------------------+-------------------+ + | 2 (10000$) | 3 (0$) | + +-------------+-----------+---------+---------+ + | 4 (5000$) | 5 (5000$) | 6 (0$) | 7 (0$) | + +-------------+-----------+---------+---------+ + ^ ^ + Alice | Bob | */ + + // Clarc added + await sTree.nodeAddLiquidity(tokens(1000)); + /*+------------------------------------------------+ + | 1 (11000$) | + +-------------------------+----------------------+ + | 2 (10000$) | 3 (1000$) | + +-------------+-----------+------------+---------+ + | 4 (5000$) | 5 (5000$) | 6 (1000$) | 7 (0$) | + +-------------+-----------+------------+---------+ + ^ + Clarc | */ + + // remove 2000$, afects only leaves 4, 5 + await sTree.removeLimit(tokens(2000), 5); + + /*+------------------------------------------------+ + | 1 (9000$) | + +-------------------------+----------------------+ + | 2 (8000$) | 3 (1000$) | + +-------------+-----------+------------+---------+ + | 4 (5000$) | 5 (4000$) | 6 (1000$) | 7 (0$) | + +-------------+-----------+------------+---------+*/ + checkNodeAmountTo(sTree, 1, tokens(9000)); + checkNodeAmountTo(sTree, 2, tokens(8000)); + checkNodeAmountTo(sTree, 3, tokens(1000)); + checkNodeAmountTo(sTree, 4, tokens(5000)); // not affected because of lazy update + checkNodeAmountTo(sTree, 5, tokens(5000)); // not affected because of lazy update + checkNodeAmountTo(sTree, 6, tokens(1000)); + checkNodeAmountTo(sTree, 7, tokens(0)); + + expect(await sTree.nodeWithdrawView(4)).to.be.equal(tokens(4000)); + expect(await sTree.nodeWithdrawView(5)).to.be.equal(tokens(4000)); + expect(await sTree.nodeWithdrawView(6)).to.be.equal(tokens(1000)); + + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(4))).to.be.equal(tokens(4000)); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(5))).to.be.equal(tokens(4000)); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(6))).to.be.equal(tokens(1000)); + }); + it("There are 15000$ of liquidity, Bob added 1000$, remove 3000$ lose of leaves 4, 5, 6. Clarc affected", async () => { + // Alice, Bob and Clarc added + await sTree.nodeAddLiquidity(tokens(5000)); + await sTree.nodeAddLiquidity(tokens(5000)); + await sTree.nodeAddLiquidity(tokens(5000)); + /*+------------------------------------------------+ + | 1 (15000$) | + +-------------------------+----------------------+ + | 2 (10000$) | 3 (5000$) | + +-------------+-----------+------------+---------+ + | 4 (5000$) | 5 (5000$) | 6 (5000$) | 7 (0$) | + +-------------+-----------+------------+---------+ + ^ ^ ^ + Alice | Bob | Clarc | */ + + // condition resolves with 3000$ lose of LP (afects only leaves 4, 5, 6) + await sTree.removeLimit(tokens(3000), 6); + + /*+------------------------------------------------+ + | 1 (12000$) | + +-------------------------+----------------------+ + | 2 (10000$) | 3 (4000$) | + +-------------+-----------+------------+---------+ + | 4 (5000$) | 5 (5000$) | 6 (4000$) | 7 (0$) | + +-------------+-----------+------------+---------+*/ + checkNodeAmountTo(sTree, 1, tokens(12000)); + checkNodeAmountTo(sTree, 2, tokens(10000)); + checkNodeAmountTo(sTree, 3, tokens(4000)); + checkNodeAmountTo(sTree, 4, tokens(5000)); // not affected because of lazy update + checkNodeAmountTo(sTree, 5, tokens(5000)); // not affected because of lazy update + checkNodeAmountTo(sTree, 6, tokens(4000)); + checkNodeAmountTo(sTree, 7, tokens(0)); + + expect(await sTree.nodeWithdrawView(4)).to.be.equal(tokens(4000)); + expect(await sTree.nodeWithdrawView(5)).to.be.equal(tokens(4000)); + expect(await sTree.nodeWithdrawView(6)).to.be.equal(tokens(4000)); + + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(4))).to.be.equal(tokens(4000)); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(5))).to.be.equal(tokens(4000)); + expect(await getWithdrawnAmount(sTree, await sTree.nodeWithdraw(6))).to.be.equal(tokens(4000)); + }); + it("Try add 0 liquidity", async () => { + await expect(sTree.nodeAddLiquidity(0)).to.be.revertedWithCustomError(sTree, "IncorrectAmount"); + }); + it("Try add liquidity to already filled leaves range", async () => { + // fill up keaves range + for (const i of Array(4).keys()) await sTree.nodeAddLiquidity(tokens(1)); + await expect(sTree.nodeAddLiquidity(tokens(1))).to.be.revertedWithCustomError(sTree, "LeafNumberRangeExceeded"); + }); + it("Try remove more liquidity than exists", async () => { + await sTree.nodeAddLiquidity(TOKENS_10); + await expect(sTree.removeLimit(TOKENS_100, 4)).to.be.revertedWithCustomError( + sTree, + "InsufficientTopNodeAmount", + ); + }); }); }); - describe("Example tree (2 leaves) fair distribution", async () => { - before(async () => { - sTree = await prepareTree(ethers, TINY_TREE_LEAFS, false); - }); - it("add liquidity 10$ in each of 2 leafs", async () => { - await sTree.nodeAddLiquidity(TOKENS_10); - await sTree.nodeAddLiquidity(TOKENS_10); - - await checkNodeAmountTo(sTree, 1, TOKENS_20); - await checkNodeAmountTo(sTree, 2, TOKENS_10); - await checkNodeAmountTo(sTree, 3, TOKENS_10); - /* - +------------------------+ - | 1 (20$) | - +-------------+----------+ - | 2 (10$) | 3 (10$) | - +-------------+----------+ - */ - }); - it("add 20$ to the whole tree", async () => { - await sTree.add(TOKENS_20); - - await checkNodeAmountTo(sTree, 1, TOKENS_40); - await checkNodeAmountTo(sTree, 2, TOKENS_10); - await checkNodeAmountTo(sTree, 3, TOKENS_10); - /* - +------------------------+ - | 1 (40$) | - +-------------+----------+ - | 2 (10$) | 3 (10$) | - +-------------+----------+ - */ - }); - it("add 10$ to the leaf #2", async () => { - await sTree.addLimit(TOKENS_10, 2); + IS_DYNAMIC.forEach((isDynamic) => { + describe(`Example tree (2 leaves) fair distribution ${isDynamic ? "dynamic" : "static"}`, async () => { + before(async () => { + sTree = await prepareTree(ethers, TINY_TREE_LEAFS, isDynamic); + }); + it("add liquidity 10$ in each of 2 leafs", async () => { + await sTree.nodeAddLiquidity(TOKENS_10); + await sTree.nodeAddLiquidity(TOKENS_10); - await checkNodeAmountTo(sTree, 1, TOKENS_50); - await checkNodeAmountTo(sTree, 2, TOKENS_30); - await checkNodeAmountTo(sTree, 3, TOKENS_20); - /* - +------------------------+ - | 1 (50$) | - +-------------+----------+ - | 2 (30$) | 3 (20$) | - +-------------+----------+ - */ + await checkNodeAmountTo(sTree, 1, TOKENS_20); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_10); + /* + +------------------------+ + | 1 (20$) | + +-------------+----------+ + | 2 (10$) | 3 (10$) | + +-------------+----------+ + */ + }); + it("add 20$ to the whole tree", async () => { + await sTree.add(TOKENS_20); + + await checkNodeAmountTo(sTree, 1, TOKENS_40); + await checkNodeAmountTo(sTree, 2, TOKENS_10); + await checkNodeAmountTo(sTree, 3, TOKENS_10); + /* + +------------------------+ + | 1 (40$) | + +-------------+----------+ + | 2 (10$) | 3 (10$) | + +-------------+----------+ + */ + }); + it("add 10$ to the leaf #2", async () => { + await sTree.addLimit(TOKENS_10, 2); + + await checkNodeAmountTo(sTree, 1, TOKENS_50); + await checkNodeAmountTo(sTree, 2, TOKENS_30); + await checkNodeAmountTo(sTree, 3, TOKENS_20); + /* + +------------------------+ + | 1 (50$) | + +-------------+----------+ + | 2 (30$) | 3 (20$) | + +-------------+----------+ + */ + }); }); }); describe("Example tree (8 leaves) fair distribution", async () => {