cosmjs-util/packages/launchpad/src/lcdapi/lcdclient.spec.ts
2021-05-18 14:27:47 +02:00

864 lines
29 KiB
TypeScript

/* eslint-disable @typescript-eslint/naming-convention */
import { Coin, coins, makeCosmoshubPath, makeSignDoc, Secp256k1HdWallet, StdFee } from "@cosmjs/amino";
import { assert, sleep } from "@cosmjs/utils";
import { isBroadcastTxFailure } from "../cosmosclient";
import { parseLogs } from "../logs";
import { MsgSend } from "../msgs";
import { SigningCosmosClient } from "../signingcosmosclient";
import cosmoshub from "../testdata/cosmoshub.json";
import {
faucet,
launchpad,
launchpadEnabled,
makeRandomAddress,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
tendermintIdMatcher,
unused,
} from "../testutils.spec";
import { isWrappedStdTx, makeStdTx, StdTx } from "../tx";
import { setupAuthExtension } from "./auth";
import { TxsResponse } from "./base";
import { LcdApiArray, LcdClient } from "./lcdclient";
describe("LcdClient", () => {
const defaultRecipientAddress = makeRandomAddress();
it("can be constructed", () => {
const client = new LcdClient(launchpad.endpoint);
expect(client).toBeTruthy();
});
describe("withModules", () => {
interface TotalSupplyAllResponse {
readonly height: string;
readonly result: LcdApiArray<Coin>;
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function setupSupplyExtension(base: LcdClient) {
return {
supply: {
totalAll: async (): Promise<TotalSupplyAllResponse> => {
return base.get(`/supply/total`);
},
},
};
}
interface BankBalancesResponse {
readonly height: string;
readonly result: readonly Coin[];
}
interface BankExtension {
readonly bank: {
readonly balances: (address: string) => Promise<BankBalancesResponse>;
};
}
function setupBankExtension(base: LcdClient): BankExtension {
return {
bank: {
balances: async (address: string) => {
const path = `/bank/balances/${address}`;
return base.get(path);
},
},
};
}
it("works for no extension", async () => {
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint });
expect(client).toBeTruthy();
});
it("works for one extension", async () => {
pendingWithoutLaunchpad();
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupSupplyExtension);
const supply = await client.supply.totalAll();
expect(supply).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ucosm",
},
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ustake",
},
],
});
});
it("works for two extensions", async () => {
pendingWithoutLaunchpad();
const client = LcdClient.withExtensions(
{ apiUrl: launchpad.endpoint },
setupSupplyExtension,
setupBankExtension,
);
const supply = await client.supply.totalAll();
expect(supply).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ucosm",
},
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ustake",
},
],
});
const balances = await client.bank.balances(unused.address);
expect(balances).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
denom: "ucosm",
amount: "1000000000",
},
{
denom: "ustake",
amount: "1000000000",
},
],
});
});
it("can merge two extensions into the same module", async () => {
pendingWithoutLaunchpad();
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function setupSupplyExtensionBasic(base: LcdClient) {
return {
supply: {
totalAll: async () => {
const path = `/supply/total`;
return base.get(path);
},
},
};
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function setupSupplyExtensionPremium(base: LcdClient) {
return {
supply: {
total: async (denom: string) => {
return base.get(`/supply/total/${denom}`);
},
},
};
}
const client = LcdClient.withExtensions(
{ apiUrl: launchpad.endpoint },
setupSupplyExtensionBasic,
setupSupplyExtensionPremium,
);
expect(client.supply.totalAll).toEqual(jasmine.any(Function));
expect(client.supply.total).toEqual(jasmine.any(Function));
});
});
// The /txs endpoints
describe("txById", () => {
let successful:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
}
| undefined;
let unsuccessful:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
}
| undefined;
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
{
const recipient = makeRandomAddress();
const amount = coins(1234567, "ucosm");
const result = await client.sendTokens(recipient, amount);
successful = {
sender: faucet.address0,
recipient: recipient,
hash: result.transactionHash,
};
}
{
const memo = "Sending more than I can afford";
const recipient = makeRandomAddress();
const amount = coins(123456700000000, "ucosm");
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: recipient,
amount: amount,
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "80000", // 80k
};
const { accountNumber, sequence } = await client.getSequence();
const chainId = await client.getChainId();
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx });
const result = await client.broadcastTx(signedTx);
assert(isBroadcastTxFailure(result));
unsuccessful = {
sender: faucet.address0,
recipient: recipient,
hash: transactionId,
};
}
await sleep(75); // wait until transactions are indexed
}
});
it("works for successful transaction", async () => {
pendingWithoutLaunchpad();
assert(successful);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txById(successful.hash);
expect(result.height).toBeGreaterThanOrEqual(1);
expect(result.txhash).toEqual(successful.hash);
expect(result.codespace).toBeUndefined();
expect(result.code).toBeUndefined();
const logs = parseLogs(result.logs);
expect(logs).toEqual([
{
msg_index: 0,
log: "",
events: [
{
type: "message",
attributes: [
{ key: "action", value: "send" },
{ key: "sender", value: successful.sender },
{ key: "module", value: "bank" },
],
},
{
type: "transfer",
attributes: [
{ key: "recipient", value: successful.recipient },
{ key: "sender", value: successful.sender },
{ key: "amount", value: "1234567ucosm" },
],
},
],
},
]);
});
it("works for unsuccessful transaction", async () => {
pendingWithoutLaunchpad();
assert(unsuccessful);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txById(unsuccessful.hash);
expect(result.height).toBeGreaterThanOrEqual(1);
expect(result.txhash).toEqual(unsuccessful.hash);
expect(result.codespace).toEqual("sdk");
expect(result.code).toEqual(5);
expect(result.logs).toBeUndefined();
expect(result.raw_log).toContain("insufficient funds");
});
});
describe("txsQuery", () => {
let broadcasted:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
readonly height: number;
readonly tx: TxsResponse;
}
| undefined;
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const recipient = makeRandomAddress();
const amount = coins(1234567, "ucosm");
const result = await client.sendTokens(recipient, amount);
await sleep(75); // wait until tx is indexed
const txDetails = await new LcdClient(launchpad.endpoint).txById(result.transactionHash);
broadcasted = {
sender: faucet.address0,
recipient: recipient,
hash: result.transactionHash,
height: Number.parseInt(txDetails.height, 10),
tx: txDetails,
};
}
});
it("can query transactions by height", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`tx.height=${broadcasted.height}&limit=26`);
expect(result).toEqual({
count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string
limit: "26",
page_number: "1",
page_total: "1",
total_count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string
txs: jasmine.arrayContaining([broadcasted.tx]),
});
});
it("can query transactions by ID", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`tx.hash=${broadcasted.hash}&limit=26`);
expect(result).toEqual({
count: "1",
limit: "26",
page_number: "1",
page_total: "1",
total_count: "1",
txs: [broadcasted.tx],
});
});
it("can query transactions by sender", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`message.sender=${broadcasted.sender}&limit=200`);
expect(parseInt(result.count, 10)).toBeGreaterThanOrEqual(1);
expect(parseInt(result.limit, 10)).toEqual(200);
expect(parseInt(result.page_number, 10)).toEqual(1);
expect(parseInt(result.page_total, 10)).toEqual(1);
expect(parseInt(result.total_count, 10)).toBeGreaterThanOrEqual(1);
expect(result.txs.length).toBeGreaterThanOrEqual(1);
expect(result.txs[result.txs.length - 1]).toEqual(broadcasted.tx);
});
it("can query transactions by recipient", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`transfer.recipient=${broadcasted.recipient}&limit=200`);
expect(parseInt(result.count, 10)).toEqual(1);
expect(parseInt(result.limit, 10)).toEqual(200);
expect(parseInt(result.page_number, 10)).toEqual(1);
expect(parseInt(result.page_total, 10)).toEqual(1);
expect(parseInt(result.total_count, 10)).toEqual(1);
expect(result.txs.length).toBeGreaterThanOrEqual(1);
expect(result.txs[result.txs.length - 1]).toEqual(broadcasted.tx);
});
it("can filter by tx.hash and tx.minheight", async () => {
pending("This combination is broken 🤷‍♂️. Handle client-side at higher level.");
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const hashQuery = `tx.hash=${broadcasted.hash}`;
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=0`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${broadcasted.height - 1}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${broadcasted.height}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${broadcasted.height + 1}`);
expect(count).toEqual("0");
}
});
it("can filter by recipient and tx.minheight", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const recipientQuery = `transfer.recipient=${broadcasted.recipient}`;
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=0`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${broadcasted.height - 1}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${broadcasted.height}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${broadcasted.height + 1}`);
expect(count).toEqual("0");
}
});
it("can filter by recipient and tx.maxheight", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const recipientQuery = `transfer.recipient=${broadcasted.recipient}`;
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=9999999999999`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${broadcasted.height + 1}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${broadcasted.height}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${broadcasted.height - 1}`);
expect(count).toEqual("0");
}
});
});
describe("encodeTx", () => {
it("works for cosmoshub example", async () => {
pendingWithoutLaunchpad();
const client = new LcdClient(launchpad.endpoint);
assert(isWrappedStdTx(cosmoshub.tx));
const response = await client.encodeTx(cosmoshub.tx);
expect(response).toEqual(
jasmine.objectContaining({
tx: cosmoshub.tx_data,
}),
);
});
});
describe("broadcastTx", () => {
it("can send tokens", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const memo = "My first contract on chain";
const theMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number, sequence } = (await client.auth.account(faucet.address0)).result.value;
const signDoc = makeSignDoc([theMsg], fee, launchpad.chainId, memo, account_number, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
expect(result.code).toBeUndefined();
expect(result).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
txhash: jasmine.stringMatching(tendermintIdMatcher),
// code is not set
raw_log: jasmine.stringMatching(/^\[.+\]$/i),
logs: jasmine.any(Array),
gas_wanted: jasmine.stringMatching(nonNegativeIntegerMatcher),
gas_used: jasmine.stringMatching(nonNegativeIntegerMatcher),
});
});
it("can't send transaction with additional signatures", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const account3 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(2)],
});
const [address1, address2, address3] = await Promise.all(
[account1, account2, account3].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const theMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const { account_number: an3, sequence: sequence3 } = (await client.auth.account(address3)).result.value;
const signDoc1 = makeSignDoc([theMsg], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([theMsg], fee, launchpad.chainId, memo, an2, sequence2);
const signDoc3 = makeSignDoc([theMsg], fee, launchpad.chainId, memo, an3, sequence3);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const { signature: signature3 } = await account3.signAmino(address3, signDoc3);
const signedTx: StdTx = {
msg: [theMsg],
fee: fee,
memo: memo,
signatures: [signature1, signature2, signature3],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toEqual(4);
expect(broadcastResult.raw_log).toContain("wrong number of signers");
});
it("can send multiple messages with one signature", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: walletAddress,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: walletAddress,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number, sequence } = (await client.auth.account(walletAddress)).result.value;
const signDoc = makeSignDoc([msg1, msg2], fee, launchpad.chainId, memo, account_number, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toBeUndefined();
});
it("can send multiple messages with multiple signatures", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address2,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const signDoc1 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an2, sequence2);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const signedTx: StdTx = {
msg: [msg2, msg1],
fee: fee,
memo: memo,
signatures: [signature2, signature1],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toBeUndefined();
await sleep(500);
const searched = await client.txsQuery(`tx.hash=${broadcastResult.txhash}`);
expect(searched.txs.length).toEqual(1);
expect(searched.txs[0].tx.value.signatures).toEqual([signature2, signature1]);
});
it("can't send transaction with wrong signature order (1)", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address2,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const signDoc1 = makeSignDoc([msg1, msg2], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([msg1, msg2], fee, launchpad.chainId, memo, an2, sequence2);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const signedTx: StdTx = {
msg: [msg1, msg2],
fee: fee,
memo: memo,
signatures: [signature2, signature1],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toEqual(8);
});
it("can't send transaction with wrong signature order (2)", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address2,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const signDoc1 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an2, sequence2);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const signedTx: StdTx = {
msg: [msg2, msg1],
fee: fee,
memo: memo,
signatures: [signature1, signature2],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toEqual(8);
});
});
});