Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: feemarket integration #NTRN-295 #300

Merged
merged 7 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"test": "yarn test:parallel && yarn test:run_in_band",
"test:parallel": "jest -b src/testcases/parallel",
"test:run_in_band": "yarn test:tge:auction && yarn test:tge:credits && 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_stargate && yarn test:globalfee && yarn test:dex_bindings && yarn test:pob && yarn test:slinky && yarn test:chain_manager",
"test:run_in_band": "yarn test:tge:auction && yarn test:tge:credits && 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_stargate && yarn test:dex_bindings && yarn test:slinky && yarn test:chain_manager && yarn test:feemarket",
"test:simple": "jest -b src/testcases/parallel/simple",
"test:slinky": "jest -b src/testcases/run_in_band/slinky",
"test:stargate_queries": "jest -b src/testcases/parallel/stargate_queries",
Expand All @@ -21,7 +21,6 @@
"test:tge:credits": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/run_in_band/tge.credits",
"test:tokenomics": "jest -b src/testcases/run_in_band/tokenomics",
"test:dao": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/parallel/dao_assert",
"test:globalfee": "jest -b src/testcases/run_in_band/globalfee",
"test:ibc_hooks": "jest -b src/testcases/run_in_band/ibc_hooks",
"test:parameters": "jest -b src/testcases/run_in_band/parameters",
"test:chain_manager": "jest -b src/testcases/run_in_band/chain_manager",
Expand All @@ -33,8 +32,8 @@
"test:voting_registry": "jest -b src/testcases/parallel/voting_registry",
"test:float": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/run_in_band/float",
"test:dex_stargate": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/run_in_band/dex_stargate",
"test:pob": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/run_in_band/pob",
"test:dex_bindings": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/run_in_band/dex_bindings",
"test:feemarket": "NO_WAIT_CHANNEL1=1 NO_WAIT_HTTP2=1 NO_WAIT_CHANNEL2=1 NO_WAIT_DELAY=1 jest -b src/testcases/run_in_band/feemarket",
"gen:proto": "bash ./gen-proto.sh",
"lint": "eslint ./src",
"fmt": "eslint ./src --fix",
Expand All @@ -47,7 +46,7 @@
"@cosmos-client/core": "^0.47.4",
"@cosmos-client/cosmwasm": "^0.40.3",
"@cosmos-client/ibc": "^1.2.1",
"@neutron-org/neutronjsplus": "https://github.com/neutron-org/neutronjsplus.git#b0acb8a3bb7ad3af7cde5f67ba259a94c2fa3e4f",
"@neutron-org/neutronjsplus": "https://github.com/neutron-org/neutronjsplus.git#bd92e7b5ef1d5eb7042b78a957e57561dd379764",
"@types/lodash": "^4.14.182",
"@types/long": "^5.0.0",
"axios": "^0.27.2",
Expand Down Expand Up @@ -86,9 +85,11 @@
"eslint --max-warnings=0",
"jest --bail --findRelatedTests"
],
"./**/*.{ts,tsx,js,jsx,md,json}": ["prettier --write"]
"./**/*.{ts,tsx,js,jsx,md,json}": [
"prettier --write"
]
},
"engines": {
"node": ">=16.0 <17"
}
}
}
1 change: 1 addition & 0 deletions setup/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ services:
environment:
- RUN_BACKGROUND=0
- ORACLE_ADDRESS=oracle:8080
- FEEMARKET_ENABLED=false
networks:
- neutron-testing

Expand Down
270 changes: 270 additions & 0 deletions src/testcases/run_in_band/feemarket.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
import Long from 'long';
import '@neutron-org/neutronjsplus';
import {
WalletWrapper,
CosmosWrapper,
NEUTRON_DENOM,
IBC_ATOM_DENOM,
packAnyMsg,
} from '@neutron-org/neutronjsplus/dist/cosmos';
import { TestStateLocalCosmosTestNet } from '@neutron-org/neutronjsplus';
import { getWithAttempts } from '@neutron-org/neutronjsplus/dist/wait';
import {
Dao,
DaoMember,
getDaoContracts,
} from '@neutron-org/neutronjsplus/dist/dao';
import { DynamicFeesParams } from '@neutron-org/neutronjsplus/dist/feemarket';
import { DecCoin } from '@neutron-org/neutronjsplus/dist/proto/neutron/cosmos/base/v1beta1/coin_pb';
import { MsgSend } from '@neutron-org/neutronjsplus/dist/proto/cosmos_sdk/cosmos/bank/v1beta1/tx_pb';

const config = require('../../config.json');

describe('Neutron / Fee Market', () => {
let testState: TestStateLocalCosmosTestNet;
let neutronChain: CosmosWrapper;
let neutronAccount: WalletWrapper;
let daoMember: DaoMember;
let daoMain: Dao;

beforeAll(async () => {
testState = new TestStateLocalCosmosTestNet(config);
await testState.init();
neutronChain = new CosmosWrapper(
testState.sdk1,
testState.blockWaiter1,
NEUTRON_DENOM,
);
neutronAccount = new WalletWrapper(
neutronChain,
testState.wallets.qaNeutron.genQaWal1,
);

const daoCoreAddress = await neutronChain.getNeutronDAOCore();
const daoContracts = await getDaoContracts(neutronChain, daoCoreAddress);
daoMain = new Dao(neutronChain, daoContracts);
daoMember = new DaoMember(neutronAccount, daoMain);
await daoMember.bondFunds('10000');
await getWithAttempts(
neutronChain.blockWaiter,
async () =>
await daoMain.queryVotingPower(
daoMember.user.wallet.address.toString(),
),
async (response) => response.power == 10000,
20,
);

await daoMember.user.msgSend(daoMain.contracts.core.address, '1000', {
gas_limit: Long.fromString('200000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '500' }],
});
await executeSwitchFeemarket(daoMember, 'enable feemarket', true);
});

let counter = 1;

const executeSwitchFeemarket = async (
daoMember: DaoMember,
kind: string,
enabled: boolean,
) => {
const params = (await neutronChain.getFeemarketParams()).params;
params.enabled = enabled;

const chainManagerAddress = (await neutronChain.getChainAdmins())[0];
const proposalId = await daoMember.submitFeeMarketChangeParamsProposal(
chainManagerAddress,
'Change Proposal - ' + kind + ' #' + counter,
'Param change proposal. It will change enabled params of feemarket module.',
'1000',
params,
);

await daoMember.voteYes(proposalId, 'single', {
gas_limit: Long.fromString('4000000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '100000' }],
});
await daoMain.checkPassedProposal(proposalId);
await daoMember.executeProposalWithAttempts(proposalId, {
gas_limit: Long.fromString('4000000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '100000' }],
});

counter++;
};

const executeChangeGasPrices = async (
daoMember: DaoMember,
kind: string,
params: DynamicFeesParams,
) => {
const chainManagerAddress = (await neutronChain.getChainAdmins())[0];
const proposalId = await daoMember.submitDynamicfeesChangeParamsProposal(
chainManagerAddress,
'Change Proposal - ' + kind + ' #' + counter,
'Param change proposal. It will change gas price list of dynamicfees/feemarket module.',
'1000',
params,
);

await daoMember.voteYes(proposalId, 'single', {
gas_limit: Long.fromString('4000000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '100000' }],
});
await daoMain.checkPassedProposal(proposalId);
await daoMember.executeProposalWithAttempts(proposalId, {
gas_limit: Long.fromString('4000000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '100000' }],
});

counter++;
};

test('success tx', async () => {
const res = await neutronAccount.msgSend(
daoMain.contracts.core.address,
'1000',
{
gas_limit: Long.fromString('200000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '500' }], // 0.0025
},
);

await neutronChain.blockWaiter.waitBlocks(2);

expect(res.code).toEqual(0);
});

test('failed: insufficient fee', async () => {
await expect(
neutronAccount.msgSend(daoMain.contracts.core.address, '1000', {
gas_limit: Long.fromString('200000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '200' }], // 0.001
}),
).rejects.toThrowError(
/error checking fee: got: 200untrn required: 500untrn, minGasPrice: 0.002500000000000000untrn/,
);
});

test('additional ibc denom', async () => {
await expect(
neutronAccount.msgSend(daoMain.contracts.core.address, '1000', {
gas_limit: Long.fromString('200000'),
amount: [{ denom: IBC_ATOM_DENOM, amount: '200' }],
}),
).rejects.toThrowError(
/unable to get min gas price for denom uibcatom: unknown denom/,
);

// 5 ntrn per ATOM, gives atom gas price 5 times lower, 0.0005 IBC_ATOM_DENOM and 0.0025 NTRN

await executeChangeGasPrices(daoMember, 'dynamicfees gasprices', {
ntrn_prices: [DecCoin.fromJson({ denom: IBC_ATOM_DENOM, amount: '5' })],
});

await expect(
neutronAccount.msgSend(daoMain.contracts.core.address, '1000', {
gas_limit: Long.fromString('200000'),
amount: [{ denom: IBC_ATOM_DENOM, amount: '50' }], // 0.00025
}),
).rejects.toThrowError(
/error checking fee: got: 50uibcatom required: 100uibcatom, minGasPrice: 0.000500000000000000uibcatom/,
);

const res = await neutronAccount.msgSend(
daoMain.contracts.core.address,
'1000',
{
gas_limit: Long.fromString('200000'),
amount: [{ denom: IBC_ATOM_DENOM, amount: '100' }], // 0.0005
},
);

await neutronChain.blockWaiter.waitBlocks(2);

expect(res.code).toEqual(0);
});

test('disable/enable feemarket module', async () => {
await executeSwitchFeemarket(daoMember, 'disable feemarket', false);

// feemarket disabled
// with a zero fee we fail due to default cosmos ante handler check
await expect(
neutronAccount.msgSend(daoMain.contracts.core.address, '1000', {
gas_limit: Long.fromString('200000'),
amount: [{ denom: IBC_ATOM_DENOM, amount: '0' }],
}),
).rejects.toThrowError(
/insufficient fees; got: 0uibcatom required: 500ibc\/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2,500untrn: insufficient fee/,
);

await neutronChain.blockWaiter.waitBlocks(2);

await executeSwitchFeemarket(daoMember, 'enable feemarket', true);

// feemarket enabled
// with a zero fee we fail due to feemarket ante handler check
await expect(
neutronAccount.msgSend(daoMain.contracts.core.address, '1000', {
gas_limit: Long.fromString('200000'),
amount: [{ denom: daoMember.user.chain.denom, amount: '0' }],
}),
).rejects.toThrowError(
/error checking fee: got: 0untrn required: 500untrn, minGasPrice: 0.002500000000000000untrn/,
);
});
test('gas price gets up and down', async () => {
const msgSend = new MsgSend({
fromAddress: neutronAccount.wallet.address.toString(),
toAddress: daoMain.contracts.core.address,
amount: [{ denom: neutronAccount.chain.denom, amount: '1000' }],
});
let ntrnGasPrice = Number(
(await neutronChain.getGasPrice('untrn')).price.amount,
);
const requiredGas = '30000000';
// due to rounding poor accuracy, it's recommended pay a little bit more fees
const priceAdjustment = 1.05;
for (let i = 0; i < 15; i++) {
const fees = Math.floor(
Number(requiredGas) * ntrnGasPrice * priceAdjustment,
).toString();
// 1200msgs consume ~27m gas
const res = await neutronAccount.execTx(
{
gas_limit: Long.fromString(requiredGas),
amount: [{ denom: daoMember.user.chain.denom, amount: fees }],
},
new Array(1200).fill(
packAnyMsg('/cosmos.bank.v1beta1.MsgSend', msgSend),
),
);
expect(res?.tx_response.code).toEqual(0);
const currNtrnGasPrice = Number(
(await neutronChain.getGasPrice('untrn')).price.amount,
);
// gas price constantly grows on 95% full blocks
expect(currNtrnGasPrice).toBeGreaterThan(ntrnGasPrice);
ntrnGasPrice = currNtrnGasPrice;
const prices = await neutronChain.getGasPrices();
console.log(prices);
}
console.log('------');
for (;;) {
await neutronChain.blockWaiter.waitBlocks(1);
const currNtrnGasPrice = Number(
(await neutronChain.getGasPrice('untrn')).price.amount,
);
// gas price constantly get down when blocks are empty
expect(currNtrnGasPrice).toBeLessThan(ntrnGasPrice);
ntrnGasPrice = currNtrnGasPrice;
const prices = await neutronChain.getGasPrices();
console.log(prices);
if (currNtrnGasPrice == 0.0025) {
break;
}
}
});
});
Loading
Loading