Add query extension tx for simulation

This commit is contained in:
Simon Warta 2021-11-17 16:28:16 +01:00
parent 25556ae4a8
commit 8a944ddf21
4 changed files with 188 additions and 0 deletions

View File

@ -78,7 +78,9 @@ export {
setupGovExtension,
setupIbcExtension,
setupStakingExtension,
setupTxExtension,
StakingExtension,
TxExtension,
} from "./queries";
export {
SearchByHeightQuery,

View File

@ -10,4 +10,5 @@ export { DistributionExtension, setupDistributionExtension } from "./distributio
export { setupGovExtension, GovExtension, GovProposalId, GovParamsType } from "./gov";
export { IbcExtension, setupIbcExtension } from "./ibc";
export { setupStakingExtension, StakingExtension } from "./staking";
export { setupTxExtension, TxExtension } from "./tx";
export { createPagination, createProtobufRpcClient, ProtobufRpcClient } from "./utils";

View File

@ -0,0 +1,104 @@
import { coin, coins, DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { assertDefined, sleep } from "@cosmjs/utils";
import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx";
import Long from "long";
import { defaultRegistryTypes, SigningStargateClient } from "../signingstargateclient";
import { assertIsBroadcastTxSuccess, StargateClient } from "../stargateclient";
import {
defaultSigningClientOptions,
faucet,
makeRandomAddress,
pendingWithoutSimapp,
simapp,
simappEnabled,
validator,
} from "../testutils.spec";
import { QueryClient } from "./queryclient";
import { setupTxExtension, TxExtension } from "./tx";
import { longify } from "./utils";
async function makeClientWithTx(rpcUrl: string): Promise<[QueryClient & TxExtension, Tendermint34Client]> {
const tmClient = await Tendermint34Client.connect(rpcUrl);
return [QueryClient.withExtensions(tmClient, setupTxExtension), tmClient];
}
describe("TxExtension", () => {
const defaultFee = {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
};
let txHash: string | undefined;
let memo: string | undefined;
beforeAll(async () => {
if (simappEnabled()) {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = await SigningStargateClient.connectWithSigner(
simapp.tendermintUrl,
wallet,
defaultSigningClientOptions,
);
{
const recipient = makeRandomAddress();
memo = `Test tx ${Date.now()}`;
const result = await client.sendTokens(
faucet.address0,
recipient,
coins(25000, "ucosm"),
defaultFee,
memo,
);
assertIsBroadcastTxSuccess(result);
txHash = result.transactionHash;
}
await sleep(75); // wait until transactions are indexed
}
});
describe("getTx", () => {
it("works", async () => {
pendingWithoutSimapp();
assertDefined(txHash);
assertDefined(memo);
const [client, tmClient] = await makeClientWithTx(simapp.tendermintUrl);
const response = await client.tx.getTx(txHash);
expect(response.tx?.body?.memo).toEqual(memo);
tmClient.disconnect();
});
});
describe("simulate", () => {
it("works", async () => {
pendingWithoutSimapp();
assertDefined(txHash);
assertDefined(memo);
const [client, tmClient] = await makeClientWithTx(simapp.tendermintUrl);
const sequenceClient = await StargateClient.connect(simapp.tendermintUrl);
const registry = new Registry(defaultRegistryTypes);
const msg: MsgDelegate = {
delegatorAddress: faucet.address0,
validatorAddress: validator.validatorAddress,
amount: coin(25000, "ustake"),
};
const msgAny = registry.encodeAsAny({
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: msg,
});
const { sequence } = await sequenceClient.getSequence(faucet.address0);
const response = await client.tx.simulate([msgAny], "foo", faucet.pubkey0, sequence);
expect(response.gasInfo?.gasUsed.toNumber()).toBeGreaterThanOrEqual(101_000);
expect(response.gasInfo?.gasUsed.toNumber()).toBeLessThanOrEqual(107_000);
expect(response.gasInfo?.gasWanted).toEqual(longify(Long.UZERO));
tmClient.disconnect();
});
});
});

View File

@ -0,0 +1,81 @@
import { Pubkey } from "@cosmjs/amino";
import { encodePubkey } from "@cosmjs/proto-signing";
import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing";
import {
GetTxRequest,
GetTxResponse,
ServiceClientImpl,
SimulateRequest,
SimulateResponse,
} from "cosmjs-types/cosmos/tx/v1beta1/service";
import { AuthInfo, Fee, Tx, TxBody } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { Any } from "cosmjs-types/google/protobuf/any";
import Long from "long";
import { QueryClient } from "./queryclient";
import { createProtobufRpcClient } from "./utils";
export interface TxExtension {
readonly tx: {
getTx: (txId: string) => Promise<GetTxResponse>;
simulate: (
messages: readonly Any[],
memo: string | undefined,
signer: Pubkey,
sequence: number,
) => Promise<SimulateResponse>;
// Add here with tests:
// - broadcastTx
// - getTxsEvent
};
}
export function setupTxExtension(base: QueryClient): TxExtension {
// Use this service to get easy typed access to query methods
// This cannot be used for proof verification
const rpc = createProtobufRpcClient(base);
const queryService = new ServiceClientImpl(rpc);
return {
tx: {
getTx: async (txId: string) => {
const request: GetTxRequest = {
hash: txId,
};
const response = await queryService.GetTx(request);
return response;
},
simulate: async (
messages: readonly Any[],
memo: string | undefined,
signer: Pubkey,
sequence: number,
) => {
const request = SimulateRequest.fromPartial({
tx: Tx.fromPartial({
authInfo: AuthInfo.fromPartial({
fee: Fee.fromPartial({}),
signerInfos: [
{
publicKey: encodePubkey(signer),
sequence: Long.fromNumber(sequence, true),
modeInfo: { single: { mode: SignMode.SIGN_MODE_DIRECT } },
},
],
}),
body: TxBody.fromPartial({
messages: Array.from(messages),
memo: memo,
}),
signatures: [new Uint8Array()],
}),
// Sending serialized `txBytes` is the future. But
// this is not available in Comsos SDK 0.42.
txBytes: undefined,
});
const response = await queryService.Simulate(request);
return response;
},
},
};
}