From de5c8470f08518924a5d06027a2236b4b676508d Mon Sep 17 00:00:00 2001 From: Waves Rider <108881461+ridev6@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:31:30 +0500 Subject: [PATCH] fix pool balance processing and test --- ride/voting_emission.ride | 7 +- test/common_mock/factory_v2.mock.ride | 34 ++++++++ test/components/voting_emission/_hooks.mjs | 2 +- .../finalize_low_balance.spec.mjs | 81 +++++++++++++++---- 4 files changed, 104 insertions(+), 20 deletions(-) diff --git a/ride/voting_emission.ride b/ride/voting_emission.ride index 22df7a26..3fe8e155 100644 --- a/ride/voting_emission.ride +++ b/ride/voting_emission.ride @@ -451,13 +451,14 @@ func processPoolBalanceINTERNAL(poolStr: String) = { # let wxEmission = pool.checkWxEmissionPoolLabel() let balanceIsOkCurrent = factoryContract.invoke("checkBalance", [lpAssetId], []).exactAs[Boolean] - let balanceIsOkPrevious = !this.getBoolean(pool.keyInsufficientBalances(epochPrevious)).valueOrElse(false) + let balanceIsOkPrevious = !this.getBoolean(pool.keyInsufficientBalances(epochPrevious - 1)).valueOrElse(false) # check balance, write if balances is ok # if previous balance and current balance is not ok, # then set status 3, remove from list, remove wx emission label let actions = if (!balanceIsOkCurrent) then { if (!balanceIsOkPrevious) then { strict deleteWxEmissionInv = factoryContract.invoke("deleteWxEmissionPoolLabel", [amountAssetId, priceAssetId], []) + strict modifyWeightInv = factoryContract.invoke("modifyWeight", [lpAssetId, 0], []) let poolAddress = getPoolInfo( amountAssetId, @@ -470,7 +471,9 @@ func processPoolBalanceINTERNAL(poolStr: String) = { DeleteEntry(pool.keyInList()) ] ++ poolsListName.deleteNodeActions(pool.poolToString()) - listActions + [ + IntegerEntry(pool.keyPoolShare(epochPrevious), 0) + ] ++ listActions } else { [ BooleanEntry(pool.keyInsufficientBalances(epochPrevious), true) diff --git a/test/common_mock/factory_v2.mock.ride b/test/common_mock/factory_v2.mock.ride index 04e66062..35a51bb3 100644 --- a/test/common_mock/factory_v2.mock.ride +++ b/test/common_mock/factory_v2.mock.ride @@ -2,6 +2,8 @@ {-# CONTENT_TYPE DAPP #-} {-# SCRIPT_TYPE ACCOUNT #-} +let wavesString = "WAVES" + # pool labels let poolLabelWxEmission = "WX_EMISSION" let validPoolLabels = [poolLabelWxEmission] @@ -9,6 +11,17 @@ let validPoolLabels = [poolLabelWxEmission] func keyWxEmission(amountAssetId: String, priceAssetId: String) = "%s%s%s__wxEmission__" + amountAssetId + "__" + priceAssetId +func keyMappingsBaseAsset2internalId(baseAssetStr: String) + = "%s%s%s__mappings__baseAsset2internalId__" + baseAssetStr +func keyMappingPoolAssetsToPoolContractAddress(internalAmountAssetIdStr: Int, internalPriceAssetIdStr: Int) + = "%d%d%s%s__" + internalAmountAssetIdStr.toString() + "__" + internalPriceAssetIdStr.toString() + "__mappings__poolAssets2PoolContract" +func keyMappingPoolContractToLPAsset(poolContractAddress: String) + = "%s%s%s__" + poolContractAddress + "__mappings__poolContract2LpAsset" + +func parseAssetId(input: String) = { + if (input == wavesString) then unit else input.fromBase58String() +} + @Callable(i) func activateNewPool(poolAddress: String, amountAssetStr: String, priceAssetStr: String, lpAssetName: String, lpAssetDescr: String, poolWeight: Int, poolType: String, logo: String) = { let lpAssetIssueAction = Issue(lpAssetName, lpAssetDescr, 1, 8, true) @@ -17,6 +30,27 @@ func activateNewPool(poolAddress: String, amountAssetStr: String, priceAssetStr: ([lpAssetIssueAction], lpAssetIdStr) } +@Callable(i) +func poolInfoREADONLY(amountAssetIdStr: String, priceAssetIdStr: String) = { + let amountAssetInternalIdOption = this.getInteger(amountAssetIdStr.keyMappingsBaseAsset2internalId()) + let priceAssetInternalIdOption = this.getInteger(priceAssetIdStr.keyMappingsBaseAsset2internalId()) + let poolContractAddressOption = this.getString(keyMappingPoolAssetsToPoolContractAddress(amountAssetInternalIdOption.value(), priceAssetInternalIdOption.value())) + let lpAssetIdOption = this.getString(poolContractAddressOption.value().keyMappingPoolContractToLPAsset()) + let poolExists = amountAssetInternalIdOption.isDefined() && priceAssetInternalIdOption.isDefined() && poolContractAddressOption.isDefined() + + let poolInfo = if (poolExists) then { + ( + poolContractAddressOption.value().addressFromStringValue(), + lpAssetIdOption.value().parseAssetId() + ) + } else unit + + ([], poolInfo) +} + +@Callable(i) +func managePool(poolAddress: String, newStatus: Int) = (nil, unit) + @Callable(i) func checkWxEmissionPoolLabel(amountAssetId: String, priceAssetId: String) = { let haveLabel = match keyWxEmission(amountAssetId, priceAssetId).getBoolean() { diff --git a/test/components/voting_emission/_hooks.mjs b/test/components/voting_emission/_hooks.mjs index 124afbab..d67b22a8 100644 --- a/test/components/voting_emission/_hooks.mjs +++ b/test/components/voting_emission/_hooks.mjs @@ -26,7 +26,7 @@ export const mochaHooks = { const nonce = random(nonceLength, 'Buffer').toString('hex'); this.votingDuration = 3; // setup accounts - const contractNames = ['votingEmission', 'votingEmissionCandidate', 'boosting', 'staking', 'factory', 'assetsStore', 'gwxReward', 'votingEmissionRate']; + const contractNames = ['votingEmission', 'votingEmissionCandidate', 'boosting', 'staking', 'factory', 'assetsStore', 'gwxReward', 'votingEmissionRate', 'pool1', 'pool2']; const userNames = Array.from({ length: 3 }, (_, k) => `user${k}`); const names = [...contractNames, ...userNames, 'pacemaker']; this.accounts = Object.fromEntries(names.map((item) => { diff --git a/test/components/voting_emission/finalize_low_balance.spec.mjs b/test/components/voting_emission/finalize_low_balance.spec.mjs index 786be53c..ee608402 100644 --- a/test/components/voting_emission/finalize_low_balance.spec.mjs +++ b/test/components/voting_emission/finalize_low_balance.spec.mjs @@ -1,4 +1,4 @@ -import { data } from '@waves/waves-transactions'; +import { data, issue } from '@waves/waves-transactions'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { @@ -15,33 +15,58 @@ chai.use(chaiAsPromised); const { expect } = chai; describe(`${process.pid}: voting_emission: finalize (low balance)`, () => { - const amountAssetId1 = 'amountAssetId1'; + let amountAssetId1; const amountAssetId1Internal = 0; - const priceAssetId1 = 'priceAssetId1'; + let priceAssetId1; const priceAssetId1Internal = 1; - const amountAssetId2 = 'amountAssetId2'; + let amountAssetId2; const amountAssetId2Internal = 2; - const priceAssetId2 = 'priceAssetId2'; + let priceAssetId2; const priceAssetId2Internal = 3; const user1GwxAmount = 1000; const user2GwxAmount = 3000; const epochLength = 6; - const poolContract1 = 'poolContract1'; - const poolContract2 = 'poolContract2'; - const poolLpAssetId1 = 'poolLpAssetId1'; - const poolLpAssetId2 = 'poolLpAssetId2'; + let poolLpAssetId1; + let poolLpAssetId2; before(async function () { + [ + { id: amountAssetId1 }, + { id: priceAssetId1 }, + { id: amountAssetId2 }, + { id: priceAssetId2 }, + { id: poolLpAssetId1 }, + { id: poolLpAssetId2 }, + ] = await Promise.all([ + broadcastAndWait(issue({ + name: 'amountAssetId1', description: '', quantity: 1e6 * 1e8, decimals: 8, reissuable: true, chainId, + }, this.accounts.factory.seed)), + broadcastAndWait(issue({ + name: 'priceAssetId1', description: '', quantity: 1e6 * 1e8, decimals: 8, reissuable: true, chainId, + }, this.accounts.factory.seed)), + broadcastAndWait(issue({ + name: 'amountAssetId2', description: '', quantity: 1e6 * 1e8, decimals: 8, reissuable: true, chainId, + }, this.accounts.factory.seed)), + broadcastAndWait(issue({ + name: 'priceAssetId2', description: '', quantity: 1e6 * 1e8, decimals: 8, reissuable: true, chainId, + }, this.accounts.factory.seed)), + broadcastAndWait(issue({ + name: 'poolLpAssetId1', description: '', quantity: 1e6 * 1e8, decimals: 8, reissuable: true, chainId, + }, this.accounts.factory.seed)), + broadcastAndWait(issue({ + name: 'poolLpAssetId2', description: '', quantity: 1e6 * 1e8, decimals: 8, reissuable: true, chainId, + }, this.accounts.factory.seed)), + ]); const { addr: dApp, seed } = this.accounts.votingEmission; await broadcastAndWait(data({ data: [ { key: `%s%s%s__mappings__baseAsset2internalId__${amountAssetId1}`, type: 'integer', value: amountAssetId1Internal }, { key: `%s%s%s__mappings__baseAsset2internalId__${priceAssetId1}`, type: 'integer', value: priceAssetId1Internal }, - { key: `%d%d%s%s__${amountAssetId1Internal}__${priceAssetId1Internal}__mappings__poolAssets2PoolContract`, type: 'string', value: poolContract1 }, - { key: `%s%s%s__${poolContract1}__mappings__poolContract2LpAsset`, type: 'string', value: poolLpAssetId1 }, + { key: `%d%d%s%s__${amountAssetId1Internal}__${priceAssetId1Internal}__mappings__poolAssets2PoolContract`, type: 'string', value: this.accounts.pool1.addr }, + { key: `%s%s%s__${this.accounts.pool1.addr}__mappings__poolContract2LpAsset`, type: 'string', value: poolLpAssetId1 }, { key: `%s%s%s__mappings__baseAsset2internalId__${amountAssetId2}`, type: 'integer', value: amountAssetId2Internal }, { key: `%s%s%s__mappings__baseAsset2internalId__${priceAssetId2}`, type: 'integer', value: priceAssetId2Internal }, - { key: `%d%d%s%s__${amountAssetId2Internal}__${priceAssetId2Internal}__mappings__poolAssets2PoolContract`, type: 'string', value: poolContract2 }, - { key: `%s%s%s__${poolContract2}__mappings__poolContract2LpAsset`, type: 'string', value: poolLpAssetId2 }, + { key: `%d%d%s%s__${amountAssetId2Internal}__${priceAssetId2Internal}__mappings__poolAssets2PoolContract`, type: 'string', value: this.accounts.pool2.addr }, + { key: `%s%s%s__${this.accounts.pool2.addr}__mappings__poolContract2LpAsset`, type: 'string', value: poolLpAssetId2 }, ], additionalFee: 4e5, chainId, @@ -127,6 +152,7 @@ describe(`${process.pid}: voting_emission: finalize (low balance)`, () => { }); it('successfull finalize', async function () { + const epoch = 0; await broadcastAndWait(data({ data: [ { key: `checkBalanceResult__${poolLpAssetId2}`, type: 'boolean', value: false }, @@ -135,7 +161,7 @@ describe(`${process.pid}: voting_emission: finalize (low balance)`, () => { chainId, }, this.accounts.factory.seed)); const { addr: dApp } = this.accounts.votingEmission; - const { value: startHeight } = await api.addresses.fetchDataKey(dApp, '%s%d__startHeight__0'); + const { value: startHeight } = await api.addresses.fetchDataKey(dApp, `%s%d__startHeight__${epoch}`); await waitForHeight(startHeight + epochLength + 1); await votingEmission.setMaxDepth({ dApp, caller: this.accounts.votingEmission.seed, value: 20, @@ -146,13 +172,34 @@ describe(`${process.pid}: voting_emission: finalize (low balance)`, () => { const poolWeightMult = 1e8; const totalVotes = user1GwxAmount + user2GwxAmount; const pool1Share = (user1GwxAmount / totalVotes) * poolWeightMult; + const pool2Share = (user2GwxAmount / totalVotes) * poolWeightMult; + + const dAppState = await api.addresses.data(dApp); + const dAppStateMap = Object.fromEntries(dAppState.map((v) => [v.key, v])); + + expect(dAppStateMap['%s__currentEpoch'].value).to.equal(epoch + 1); + expect(dAppStateMap[`%s%s%s%d__poolShare__${amountAssetId1}__${priceAssetId1}__${epoch}`].value).to.equal(pool1Share); + expect(dAppStateMap[`%s%s%s%d__poolShare__${amountAssetId2}__${priceAssetId2}__${epoch}`].value).to.equal(pool2Share); + }); + + it('pool should be removed from the list', async function () { + const epoch = 1; + const { addr: dApp } = this.accounts.votingEmission; + const { value: startHeight } = await api.addresses.fetchDataKey(dApp, `%s%d__startHeight__${epoch}`); + await waitForHeight(startHeight + epochLength + 1); + await votingEmission.finalize({ + dApp, caller: this.accounts.pacemaker.seed, + }); + const poolWeightMult = 1e8; + const totalVotes = user1GwxAmount + user2GwxAmount; + const pool1Share = (user1GwxAmount / totalVotes) * poolWeightMult; const pool2Share = 0; const dAppState = await api.addresses.data(dApp); const dAppStateMap = Object.fromEntries(dAppState.map((v) => [v.key, v])); - expect(dAppStateMap['%s__currentEpoch'].value).to.equal(1); - expect(dAppStateMap['%s%s%s%d__poolShare__amountAssetId1__priceAssetId1__0'].value).to.equal(pool1Share); - expect(dAppStateMap['%s%s%s%d__poolShare__amountAssetId2__priceAssetId2__0'].value).to.equal(pool2Share); + expect(dAppStateMap['%s__currentEpoch'].value).to.equal(epoch + 1); + expect(dAppStateMap[`%s%s%s%d__poolShare__${amountAssetId1}__${priceAssetId1}__${epoch}`].value).to.equal(pool1Share); + expect(dAppStateMap[`%s%s%s%d__poolShare__${amountAssetId2}__${priceAssetId2}__${epoch}`].value).to.equal(pool2Share); }); });