diff --git a/package.json b/package.json index e5f4c98c..b1768435 100644 --- a/package.json +++ b/package.json @@ -90,4 +90,4 @@ "engines": { "node": ">=20.0" } -} +} \ No newline at end of file diff --git a/src/testcases/run_in_band/chain_manager.test.ts b/src/testcases/run_in_band/chain_manager.test.ts index a7d0f0e2..305f6363 100644 --- a/src/testcases/run_in_band/chain_manager.test.ts +++ b/src/testcases/run_in_band/chain_manager.test.ts @@ -13,6 +13,8 @@ import { updateTokenfactoryParamsProposal, AddSchedule, RemoveSchedule, + updateGlobalFeeParamsProposal, + updateConsumerParamsProposal, } from '@neutron-org/neutronjsplus/dist/proposal'; import { LocalState } from '../../helpers/local_state'; import { RunnerTestSuite, inject } from 'vitest'; @@ -23,6 +25,9 @@ import { QueryClientImpl as AdminQueryClient } from '@neutron-org/neutronjs/cosm import { QueryClientImpl as TokenfactoryQueryClient } from '@neutron-org/neutronjs/osmosis/tokenfactory/v1beta1/query.rpc.Query'; import { QueryClientImpl as UpgradeQueryClient } from '@neutron-org/neutronjs/cosmos/upgrade/v1beta1/query.rpc.Query'; import { QueryClientImpl as DexQueryClient } from '@neutron-org/neutronjs/neutron/dex/query.rpc.Query'; +import { QueryClientImpl as DynamicfeesQueryClient } from '@neutron-org/neutronjs/neutron/dynamicfees/v1/query.rpc.Query'; +import { QueryClientImpl as GlobalfeeQueryClient } from '@neutron-org/neutronjs/gaia/globalfee/v1beta1/query.rpc.Query'; +import { QueryClientImpl as CCVQueryClient } from '@neutron-org/neutronjs/interchain_security/ccv/consumer/v1/query.rpc.Query'; import { SigningNeutronClient } from '../../helpers/signing_neutron_client'; import config from '../../config.json'; import { Wallet } from '../../helpers/wallet'; @@ -39,6 +44,9 @@ describe('Neutron / Chain Manager', () => { let cronQuerier: CronQueryClient; let tokenfactoryQuerier: TokenfactoryQueryClient; let dexQuerier: DexQueryClient; + let dynamicfeesQuerier: DynamicfeesQueryClient; + let globalfeeQuerier: GlobalfeeQueryClient; + let ccvQuerier: CCVQueryClient; let upgradeQuerier: UpgradeQueryClient; let chainManagerAddress: string; @@ -97,6 +105,9 @@ describe('Neutron / Chain Manager', () => { cronQuerier = new CronQueryClient(neutronRpcClient); dexQuerier = new DexQueryClient(neutronRpcClient); upgradeQuerier = new UpgradeQueryClient(neutronRpcClient); + dynamicfeesQuerier = new DynamicfeesQueryClient(neutronRpcClient); + globalfeeQuerier = new GlobalfeeQueryClient(neutronRpcClient); + ccvQuerier = new CCVQueryClient(neutronRpcClient); }); // We need to do this because the real main dao has a super long voting period. @@ -132,7 +143,7 @@ describe('Neutron / Chain Manager', () => { dao: currentOverruleProposalConfig['dao'], close_proposal_on_execution_failure: currentOverruleProposalConfig[ - 'close_proposal_on_execution_failure' + 'close_proposal_on_execution_failure' ], }, }), @@ -206,6 +217,34 @@ describe('Neutron / Chain Manager', () => { whitelisted_lps: true, }, }, + { + update_ccv_params_permission: { + blocks_per_distribution_transmission: true, + distribution_transmission_channel: true, + provider_fee_pool_addr_str: true, + ccv_timeout_period: true, + transfer_timeout_period: true, + consumer_redistribution_fraction: true, + historical_entries: true, + unbonding_period: true, + soft_opt_out_threshold: true, + reward_denoms: true, + provider_reward_denoms: true, + retry_delay_period: true, + }, + }, + { + update_globalfee_params_permission: { + minimum_gas_prices: true, + bypass_min_fee_msg_types: true, + max_total_bypass_min_fee_msg_gas_usage: true, + }, + }, + { + update_dynamicfees_params_permission: { + ntrn_prices: true, + }, + }, ], }, }, @@ -252,6 +291,7 @@ describe('Neutron / Chain Manager', () => { }); test('execute timelocked: success', async () => { + const cronParamsBefore = await cronQuerier.params(); await waitSeconds(10); await subdaoMember1.executeTimelockedProposal(proposalId); @@ -262,6 +302,12 @@ describe('Neutron / Chain Manager', () => { const cronParams = await cronQuerier.params(); expect(cronParams.params.limit).toEqual(42n); + // check that every params field before proposal execution differs from the field after proposal execution + expect( + Object.keys(cronParamsBefore).every( + (key) => cronParamsBefore[key] !== cronParams[key], + ), + ).toBeTrue(); }); }); @@ -287,15 +333,16 @@ describe('Neutron / Chain Manager', () => { '1000', ); - const timelockedProp = - await subdaoMember1.supportAndExecuteProposal(proposalId); - + const timelockedProp = await subdaoMember1.supportAndExecuteProposal( + proposalId, + ); expect(timelockedProp.id).toEqual(proposalId); expect(timelockedProp.status).toEqual('timelocked'); expect(timelockedProp.msgs).toHaveLength(1); }); test('execute timelocked: success', async () => { + const tokenfactoryParamsBefore = await tokenfactoryQuerier.params(); await waitSeconds(10); await subdaoMember1.executeTimelockedProposal(proposalId); @@ -318,13 +365,21 @@ describe('Neutron / Chain Manager', () => { denomCreator: 'neutron1m9l358xunhhwds0568za49mzhvuxx9ux8xafx2', }, ]); + // check that every params field before proposal execution differs from the field after proposal execution + expect( + Object.keys(tokenfactoryParamsBefore).every( + (key) => tokenfactoryParamsBefore[key] !== tokenfactoryParams[key], + ), + ).toBeTrue(); }); }); describe('ALLOW_ONLY: change DEX parameters', () => { let proposalId: number; const newParams = { - fee_tiers: [1, 2, 99], + // types mixed on purpose, to check contract parser. + // Numeric types in neutron-std can be deserialized from both number and string + fee_tiers: ['1', '2', 99], paused: true, max_jits_per_block: 11, good_til_purge_allowance: 50000, @@ -351,6 +406,7 @@ describe('Neutron / Chain Manager', () => { }); test('execute timelocked: success', async () => { + const dexParamsBefore = await dexQuerier.params(); await waitSeconds(10); await subdaoMember1.executeTimelockedProposal(proposalId); @@ -368,6 +424,200 @@ describe('Neutron / Chain Manager', () => { 'neutron10h9stc5v6ntgeygf5xf945njqq5h32r54rf7kf', 'neutron16yn2gcz24s9qwpuxvrhl3xed0pmhrgwx2mz40zrazfc0pt5kq0psucs6xl', ]); + // check that every params field before proposal execution differs from the field after proposal execution + expect( + Object.keys(dexParamsBefore).every( + (key) => dexParamsBefore[key] !== dexParams[key], + ), + ).toBeTrue(); + }); + }); + + describe('ALLOW_ONLY: change Dynamicfees parameters', () => { + let proposalId: number; + beforeAll(async () => { + proposalId = await subdaoMember1.submitDynamicfeesChangeParamsProposal( + chainManagerAddress, + 'Proposal #2', + 'Dynamicfees update params proposal. Will pass', + '1000', + { + ntrn_prices: [{ denom: 'newdenom', amount: '0.5' }], + }, + ); + + const timelockedProp = await subdaoMember1.supportAndExecuteProposal( + proposalId, + ); + + expect(timelockedProp.id).toEqual(proposalId); + expect(timelockedProp.status).toEqual('timelocked'); + expect(timelockedProp.msgs).toHaveLength(1); + }); + + test('execute timelocked: success', async () => { + const dynamicfeesParamsBefore = await dynamicfeesQuerier.params(); + await waitSeconds(10); + + await subdaoMember1.executeTimelockedProposal(proposalId); + const timelockedProp = await subDao.getTimelockedProposal(proposalId); + expect(timelockedProp.id).toEqual(proposalId); + expect(timelockedProp.status).toEqual('executed'); + expect(timelockedProp.msgs).toHaveLength(1); + + const dynamicfeesParams = await dynamicfeesQuerier.params(); + expect(dynamicfeesParams.params.ntrnPrices).toEqual([ + { denom: 'newdenom', amount: '0.5' }, + ]); + // check that every params field before proposal execution differs from the field after proposal execution + expect( + Object.keys(dynamicfeesParamsBefore).every( + (key) => dynamicfeesParamsBefore[key] !== dynamicfeesParams[key], + ), + ).toBeTrue(); + }); + }); + + describe('ALLOW_ONLY: change Globalfee parameters', () => { + let proposalId: number; + beforeAll(async () => { + proposalId = await subdaoMember1.submitUpdateParamsGlobalfeeProposal( + chainManagerAddress, + 'Proposal #3', + 'Globalfee update params proposal. Will pass', + updateGlobalFeeParamsProposal({ + minimum_gas_prices: [{ denom: 'untrn', amount: '0.00111' }], + bypass_min_fee_msg_types: ['/gaia.globalfee.v1beta1.MsgUpdateParams'], + max_total_bypass_min_fee_msg_gas_usage: '12345', + }), + '1000', + ); + + const timelockedProp = await subdaoMember1.supportAndExecuteProposal( + proposalId, + ); + + expect(timelockedProp.id).toEqual(proposalId); + expect(timelockedProp.status).toEqual('timelocked'); + expect(timelockedProp.msgs).toHaveLength(1); + }); + + test('execute timelocked: success', async () => { + const globalfeeParamsBefore = await globalfeeQuerier.params(); + await waitSeconds(10); + + await subdaoMember1.executeTimelockedProposal(proposalId); + const timelockedProp = await subDao.getTimelockedProposal(proposalId); + expect(timelockedProp.id).toEqual(proposalId); + expect(timelockedProp.status).toEqual('executed'); + expect(timelockedProp.msgs).toHaveLength(1); + + const globalfeeParams = await globalfeeQuerier.params(); + expect(globalfeeParams.params.minimumGasPrices).toEqual([ + { denom: 'untrn', amount: '0.00111' }, + ]); + expect(globalfeeParams.params.bypassMinFeeMsgTypes).toEqual([ + '/gaia.globalfee.v1beta1.MsgUpdateParams', + ]); + expect(globalfeeParams.params.maxTotalBypassMinFeeMsgGasUsage).toEqual( + 12345n, + ); + // check that every params field before proposal execution differs from the field after proposal execution + expect( + Object.keys(globalfeeParamsBefore).every( + (key) => globalfeeParamsBefore[key] !== globalfeeParams[key], + ), + ).toBeTrue(); + }); + }); + + describe('ALLOW_ONLY: change ccv consumer parameters', () => { + let proposalId: number; + beforeAll(async () => { + proposalId = await subdaoMember1.submitUpdateParamsConsumerProposal( + chainManagerAddress, + 'Proposal #4', + 'Consumer update params proposal. Will pass', + updateConsumerParamsProposal({ + enabled: true, + blocks_per_distribution_transmission: 321, + distribution_transmission_channel: 'channel-23', + provider_fee_pool_addr_str: chainManagerAddress, + ccv_timeout_period: '32s', + transfer_timeout_period: '23s', + consumer_redistribution_fraction: '0.33', + historical_entries: 123, + unbonding_period: '43s', + soft_opt_out_threshold: '0.55', + reward_denoms: ['tia'], + provider_reward_denoms: ['tia'], + retry_delay_period: '43s', + }), + '1000', + ); + + const timelockedProp = await subdaoMember1.supportAndExecuteProposal( + proposalId, + ); + + expect(timelockedProp.id).toEqual(proposalId); + expect(timelockedProp.status).toEqual('timelocked'); + expect(timelockedProp.msgs).toHaveLength(1); + }); + + test('execute timelocked: success', async () => { + const ccvParamsBefore = await ccvQuerier.queryParams(); + await waitSeconds(10); + + await subdaoMember1.executeTimelockedProposal(proposalId); + console.log( + 'subdao', + subdaoMember1.dao.contracts.proposals['single'].pre_propose.timelock + .address, + ); + const timelockedProp = await subDao.getTimelockedProposal(proposalId); + expect(timelockedProp.id).toEqual(proposalId); + expect(timelockedProp.status).toEqual('executed'); + expect(timelockedProp.msgs).toHaveLength(1); + + const ccvParams = await ccvQuerier.queryParams(); + expect(ccvParams.params.enabled).toEqual(true); + expect(ccvParams.params.blocksPerDistributionTransmission).toEqual(321n); + expect(ccvParams.params.distributionTransmissionChannel).toEqual( + 'channel-23', + ); + expect(ccvParams.params.providerFeePoolAddrStr).toEqual( + chainManagerAddress, + ); + expect(ccvParams.params.ccvTimeoutPeriod).toEqual({ + nanos: 0, + seconds: 32n, + }); + expect(ccvParams.params.transferTimeoutPeriod).toEqual({ + nanos: 0, + seconds: 23n, + }); + expect(ccvParams.params.consumerRedistributionFraction).toEqual('0.33'); + expect(ccvParams.params.historicalEntries).toEqual(123n); + expect(ccvParams.params.unbondingPeriod).toEqual({ + nanos: 0, + seconds: 43n, + }); + expect(ccvParams.params.softOptOutThreshold).toEqual('0.55'); + expect(ccvParams.params.rewardDenoms).toEqual(['tia']); + expect(ccvParams.params.providerRewardDenoms).toEqual(['tia']); + expect(ccvParams.params.retryDelayPeriod).toEqual({ + nanos: 0, + seconds: 43n, + }); + // field 'enabled' is readonly, and should not be changed, always equals true + delete ccvParamsBefore['enabled']; + // check that every params field before proposal execution differs from the field after proposal execution + expect( + Object.keys(ccvParamsBefore).every( + (key) => ccvParamsBefore[key] !== ccvParams[key], + ), + ).toBeTrue(); }); }); diff --git a/src/testcases/run_in_band/slinky.test.ts b/src/testcases/run_in_band/slinky.test.ts index 8ca10f77..d0db4d54 100644 --- a/src/testcases/run_in_band/slinky.test.ts +++ b/src/testcases/run_in_band/slinky.test.ts @@ -163,18 +163,19 @@ describe('Neutron / Slinky', () => { }); describe('grpc oracle', () => { - test('query prices', async () => { - const res: GetPricesResponse = await neutronClient.queryContractSmart( - oracleContract, - { - get_prices: { - currency_pair_ids: ['AAVE/USD'], - }, - }, - ); - expect(res.prices).toHaveLength(1); - expect(+res.prices[0].price.price).toBeGreaterThan(0); - }); + // this query is restricted currently + // test('query prices', async () => { + // const res: GetPricesResponse = await neutronClient.queryContractSmart( + // oracleContract, + // { + // get_prices: { + // currency_pair_ids: ['AAVE/USD'], + // }, + // }, + // ); + // expect(res.prices).toHaveLength(1); + // expect(+res.prices[0].price.price).toBeGreaterThan(0); + // }); test('query price', async () => { const res: GetPriceResponse = await neutronClient.queryContractSmart( @@ -226,15 +227,16 @@ describe('Neutron / Slinky', () => { expect(res.market).toBeDefined(); }); - test('query market map', async () => { - const res = await neutronClient.queryContractSmart(marketmapContract, { - market_map: {}, - }); - expect(res).toBeDefined(); - expect(res.chain_id).toBeDefined(); - expect(res.market_map).toBeDefined(); - expect(res.last_updated).toBeDefined(); - }); + // this query is restricted atm + // test('query market map', async () => { + // const res = await neutronClient.queryContractSmart(marketmapContract, { + // market_map: {}, + // }); + // expect(res).toBeDefined(); + // expect(res.chain_id).toBeDefined(); + // expect(res.market_map).toBeDefined(); + // expect(res.last_updated).toBeDefined(); + // }); test('query params', async () => { const res = await neutronClient.queryContractSmart(marketmapContract, {