diff --git a/package.json b/package.json index 9e6da88b..0f39805e 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,10 @@ "scripts": { "test": "yarn test:parallel && yarn test:run_in_band", "test:parallel": "vitest --run src/testcases/parallel --bail 1 --reporter=basic", - "test:run_in_band": "yarn test:feemarket && yarn test:globalfee && yarn test:interchaintx && yarn test:interchain_kv_query && yarn test:interchain_tx_query_plain && yarn test:tokenomics && yarn test:reserve && yarn test:ibc_hooks && yarn test:float && yarn test:parameters && yarn test:dex_grpc && yarn test:dex_bindings && yarn test:slinky && yarn test:chain_manager && yarn test:tokenfactory", + "test:run_in_band": "yarn test:feemarket && yarn test:globalfee && yarn test:interchaintx && yarn test:interchain_kv_query && yarn test:interchain_tx_query_plain && yarn test:tokenomics && yarn test:reserve && yarn test:ibc_hooks && yarn test:float && yarn test:parameters && yarn test:slinky && yarn test:chain_manager && yarn test:tokenfactory && yarn test:cron", "test:ibc_transfer": "vitest --run src/testcases/parallel/ibc_transfer --bail 1", "test:slinky": "vitest --run src/testcases/run_in_band/slinky --bail 1", + "test:cron": "vitest --run src/testcases/run_in_band/cron --bail 1", "test:grpc_queries": "vitest --run src/testcases/parallel/grpc_queries --bail 1", "test:interchaintx": "vitest --run src/testcases/run_in_band/interchaintx --bail 1", "test:interchain_kv_query": "vitest --run src/testcases/run_in_band/interchain_kv_query --bail 1", @@ -89,4 +90,4 @@ "engines": { "node": ">=20.0" } -} +} \ No newline at end of file diff --git a/src/helpers/constants.ts b/src/helpers/constants.ts index 62f61fd2..e9e395f4 100644 --- a/src/helpers/constants.ts +++ b/src/helpers/constants.ts @@ -59,6 +59,7 @@ export const CONTRACTS = { FLOATY: '../contracts_thirdparty/floaty_2.0.wasm', DEX_GRPC: 'dex_grpc.wasm', DEX_DEV: 'dex.wasm', + CRON: 'cron.wasm', // TGE liquidity migration related contracts with fixed versions diff --git a/src/testcases/parallel/governance.test.ts b/src/testcases/parallel/governance.test.ts index 483b74f9..bcd2f09a 100644 --- a/src/testcases/parallel/governance.test.ts +++ b/src/testcases/parallel/governance.test.ts @@ -321,9 +321,9 @@ describe('Neutron / Governance', () => { msg: '{"test_msg": {"return_err": false, "arg": "proposal_11"}}', }, ], - execution_stage: 0, // TODO: update neutronjs here + execution_stage: 'EXECUTION_STAGE_BEGIN_BLOCKER', }, - true, // just to check that bindings are ok + true, // just to check that bindings are ok ); }); @@ -365,7 +365,7 @@ describe('Neutron / Governance', () => { msg: '{"test_msg": {"return_err": false, "arg": "three_messages"}}', }, ], - execution_stage: 0, // TODO + execution_stage: 'EXECUTION_STAGE_BEGIN_BLOCKER', }, ); }); @@ -390,7 +390,7 @@ describe('Neutron / Governance', () => { msg: '{"test_msg": {"return_err": true, "arg": ""}}', }, ], - execution_stage: 0, + execution_stage: 'EXECUTION_STAGE_BEGIN_BLOCKER', }, ); }); diff --git a/src/testcases/run_in_band/cron.test.ts b/src/testcases/run_in_band/cron.test.ts new file mode 100644 index 00000000..bc0beee7 --- /dev/null +++ b/src/testcases/run_in_band/cron.test.ts @@ -0,0 +1,301 @@ +import '@neutron-org/neutronjsplus'; +import { LocalState } from '../../helpers/local_state'; +import { Wallet } from '../../helpers/wallet'; +import { CONTRACTS } from '../../helpers/constants'; +import { + Dao, + DaoMember, + getDaoContracts, + getNeutronDAOCore, +} from '@neutron-org/neutronjsplus/dist/dao'; +import { RunnerTestSuite, inject } from 'vitest'; +import { NEUTRON_DENOM } from '../../helpers/constants'; +import { QueryClientImpl as CronQueryClient } from '@neutron-org/neutronjs/neutron/cron/query.rpc.Query'; +import { QueryClientImpl as AdminQueryClient } from '@neutron-org/neutronjs/cosmos/adminmodule/adminmodule/query.rpc.Query'; +import { SigningNeutronClient } from '../../helpers/signing_neutron_client'; +import config from '../../config.json'; + +describe('Neutron / Cron', () => { + let testState: LocalState; + let neutronWallet: Wallet; + let neutronClient: SigningNeutronClient; + let mainDao: Dao; + let daoMember: DaoMember; + + let chainManagerAddress: string; + let contractAddress: string; + let proposalId: number; + + let cronQuerier: CronQueryClient; + + beforeAll(async (suite: RunnerTestSuite) => { + testState = await LocalState.create(config, inject('mnemonics'), suite); + neutronWallet = await testState.nextWallet('neutron'); + neutronClient = await SigningNeutronClient.connectWithSigner( + testState.rpcNeutron, + neutronWallet.directwallet, + neutronWallet.address, + ); + const neutronRpcClient = await testState.neutronRpcClient(); + const daoCoreAddress = await getNeutronDAOCore( + neutronClient, + neutronRpcClient, + ); // add assert for some addresses + const daoContracts = await getDaoContracts(neutronClient, daoCoreAddress); + mainDao = new Dao(neutronClient, daoContracts); + daoMember = new DaoMember( + mainDao, + neutronClient.client, + neutronWallet.address, + NEUTRON_DENOM, + ); + + const queryClient = new AdminQueryClient(neutronRpcClient); + const admins = await queryClient.admins(); + chainManagerAddress = admins.admins[0]; + + cronQuerier = new CronQueryClient(neutronRpcClient); + }); + + describe('Contracts', () => { + let codeId: number; + test('store contract', async () => { + codeId = await neutronClient.upload(CONTRACTS.CRON); + expect(codeId).toBeGreaterThan(0); + }); + test('instantiate', async () => { + contractAddress = await neutronClient.instantiate(codeId, {}); + }); + }); + + describe('prepare: bond funds', () => { + test('bond from wallet', async () => { + await daoMember.bondFunds('10000'); + await neutronClient.getWithAttempts( + async () => await mainDao.queryVotingPower(daoMember.user), + async (response) => response.power == 10000, + 20, + ); + }); + }); + + describe('send a bit funds to core contracts', () => { + test('send funds from wallet', async () => { + const res = await neutronClient.sendTokens( + mainDao.contracts.core.address, + [ + { + denom: NEUTRON_DENOM, + amount: '1000', + }, + ], + { + gas: '4000000', + amount: [{ denom: NEUTRON_DENOM, amount: '10000' }], + }, + ); + expect(res.code).toEqual(0); + }); + }); + + describe('create proposal #1', () => { + test('add schedule #1', async () => { + proposalId = await daoMember.submitAddSchedule( + chainManagerAddress, + 'Proposal #1', + '', + '1000', + { + name: 'schedule1', + period: 5, + msgs: [ + { + contract: contractAddress, + msg: '{"add_begin_blocker_schedule": {"name": "schedule1"}}', + }, + ], + execution_stage: 'EXECUTION_STAGE_BEGIN_BLOCKER', + }, + ); + + await daoMember.voteYes(proposalId); + await mainDao.checkPassedProposal(proposalId); + await daoMember.executeProposalWithAttempts(proposalId); + }); + + test('check that schedule was added', async () => { + const res = await cronQuerier.schedules(); + expect(res.schedules.length).toEqual(1); + }); + + test('check that msg from schedule was executed', async () => { + await neutronClient.waitBlocks(15); + + const queryResult: number = await neutronClient.queryContractSmart( + contractAddress, + { + get_begin_blocker_schedule_counter: { + name: 'schedule1', + }, + }, + ); + + expect(queryResult).toBeGreaterThanOrEqual(2); + }); + }); + + describe('create proposal #2', () => { + test('remove schedule #1', async () => { + proposalId = await daoMember.submitRemoveSchedule( + chainManagerAddress, + 'Proposal #2', + '', + '1000', + { + name: 'schedule1', + }, + ); + + await daoMember.voteYes(proposalId); + await mainDao.checkPassedProposal(proposalId); + await daoMember.executeProposalWithAttempts(proposalId); + }); + + test('check that schedule was removed', async () => { + const res = await cronQuerier.schedules(); + expect(res.schedules.length).toEqual(0); + }); + + test('check that msg from schedule was not executed because schedule was removed', async () => { + const oldQueryResult: number = await neutronClient.queryContractSmart( + contractAddress, + { + get_begin_blocker_schedule_counter: { + name: 'schedule1', + }, + }, + ); + + await neutronClient.waitBlocks(10); + + const newQueryResult: number = await neutronClient.queryContractSmart( + contractAddress, + { + get_begin_blocker_schedule_counter: { + name: 'schedule1', + }, + }, + ); + + expect(newQueryResult).toEqual(oldQueryResult); + }); + }); + + describe('create proposal #3', () => { + test('add schedule #2', async () => { + proposalId = await daoMember.submitAddSchedule( + chainManagerAddress, + 'Proposal #3', + '', + '1000', + { + name: 'schedule2', + period: 5, + msgs: [ + { + contract: contractAddress, + msg: '{"add_begin_blocker_schedule": {"name": "schedule2"}}', + }, + { + contract: contractAddress, + msg: '{"unknown_msg": {"name": "schedule2"}}', + }, + { + contract: contractAddress, + msg: '{"add_begin_blocker_schedule": {"name": "schedule2"}}', + }, + ], + execution_stage: 'EXECUTION_STAGE_BEGIN_BLOCKER', + }, + ); + + await daoMember.voteYes(proposalId); + await mainDao.checkPassedProposal(proposalId); + await daoMember.executeProposalWithAttempts(proposalId); + }); + + test('check that schedule was added', async () => { + const res = await cronQuerier.schedules(); + expect(res.schedules.length).toEqual(1); + }); + + test('check that no msgs from schedule were executed because there is an error in the second msg', async () => { + await neutronClient.waitBlocks(10); + + const queryResult: number = await neutronClient.queryContractSmart( + contractAddress, + { + get_end_blocker_schedule_counter: { + name: 'schedule2', + }, + }, + ); + + expect(queryResult).toEqual(null); + }); + }); + + describe('create proposal #4', () => { + test('add schedule #3', async () => { + proposalId = await daoMember.submitAddSchedule( + chainManagerAddress, + 'Proposal #4', + '', + '1000', + { + name: 'shedule3', + period: 5, + msgs: [ + { + contract: contractAddress, + msg: '{"add_end_blocker_schedule": {"name": "schedule3"}}', + }, + { + contract: contractAddress, + msg: '{"add_end_blocker_schedule": {"name": "schedule3"}}', + }, + { + contract: contractAddress, + msg: '{"add_end_blocker_schedule": {"name": "schedule3"}}', + }, + ], + execution_stage: 'EXECUTION_STAGE_END_BLOCKER', + }, + ); + + await daoMember.voteYes(proposalId); + await mainDao.checkPassedProposal(proposalId); + await daoMember.executeProposalWithAttempts(proposalId); + }); + + test('check that schedule was added', async () => { + const res = await cronQuerier.schedules(); + expect(res.schedules.length).toEqual(2); + }); + + test('check that msgs from schedule was executed', async () => { + await neutronClient.waitBlocks(15); + + const queryResult: number = await neutronClient.queryContractSmart( + contractAddress, + { + get_end_blocker_schedule_counter: { + name: 'schedule3', + }, + }, + ); + + expect(queryResult).toBeGreaterThanOrEqual(6); + }); + }); +});