mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-10 13:47:12 +00:00
Merge branch 'main' into issue334/implement_instantiate_permission
This commit is contained in:
commit
32c4057e1e
@ -20,9 +20,16 @@ and this project adheres to
|
||||
([#1266]).
|
||||
- @cosmjs/stargate: `IndexedTx` and `DeliverTxResponse` now have a
|
||||
`msgResponses` field ([#1305]).
|
||||
- @cosmjs/cosmwasm-stargate: Add `CosmWasmClient.broadcastTxSync` and
|
||||
`SigningCosmWasmClient.signAndBroadcastSync` to allow broadcasting without
|
||||
waiting for block inclusion. ([#1396])
|
||||
- @cosmjs/stargateAdd `StargateClient.broadcastTxSync` and
|
||||
`SigningStargateClient.signAndBroadcastSync` to allow broadcasting without
|
||||
waiting for block inclusion. ([#1396])
|
||||
|
||||
[#1266]: https://github.com/cosmos/cosmjs/issues/1266
|
||||
[#1305]: https://github.com/cosmos/cosmjs/issues/1305
|
||||
[#1396]: https://github.com/cosmos/cosmjs/pull/1396
|
||||
[#1407]: https://github.com/cosmos/cosmjs/pull/1407
|
||||
|
||||
### Changed
|
||||
|
@ -296,13 +296,8 @@ export class CosmWasmClient {
|
||||
: pollForTx(txId);
|
||||
};
|
||||
|
||||
const broadcasted = await this.forceGetTmClient().broadcastTxSync({ tx });
|
||||
if (broadcasted.code) {
|
||||
return Promise.reject(
|
||||
new BroadcastTxError(broadcasted.code, broadcasted.codespace ?? "", broadcasted.log),
|
||||
);
|
||||
}
|
||||
const transactionId = toHex(broadcasted.hash).toUpperCase();
|
||||
const transactionId = await this.broadcastTxSync(tx);
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
pollForTx(transactionId).then(
|
||||
(value) => {
|
||||
@ -317,6 +312,31 @@ export class CosmWasmClient {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a signed transaction to the network without monitoring it.
|
||||
*
|
||||
* If broadcasting is rejected by the node for some reason (e.g. because of a CheckTx failure),
|
||||
* an error is thrown.
|
||||
*
|
||||
* If the transaction is broadcasted, a `string` containing the hash of the transaction is returned. The caller then
|
||||
* usually needs to check if the transaction was included in a block and was successful.
|
||||
*
|
||||
* @returns Returns the hash of the transaction
|
||||
*/
|
||||
public async broadcastTxSync(tx: Uint8Array): Promise<string> {
|
||||
const broadcasted = await this.forceGetTmClient().broadcastTxSync({ tx });
|
||||
|
||||
if (broadcasted.code) {
|
||||
return Promise.reject(
|
||||
new BroadcastTxError(broadcasted.code, broadcasted.codespace ?? "", broadcasted.log),
|
||||
);
|
||||
}
|
||||
|
||||
const transactionId = toHex(broadcasted.hash).toUpperCase();
|
||||
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* getCodes() returns all codes and is just looping through all pagination pages.
|
||||
*
|
||||
|
@ -614,6 +614,40 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
||||
return this.broadcastTx(txBytes, this.broadcastTimeoutMs, this.broadcastPollIntervalMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transaction with the given messages, fee and memo. Then signs and broadcasts the transaction.
|
||||
*
|
||||
* This method is useful if you want to send a transaction in broadcast,
|
||||
* without waiting for it to be placed inside a block, because for example
|
||||
* I would like to receive the hash to later track the transaction with another tool.
|
||||
*
|
||||
* @param signerAddress The address that will sign transactions using this instance. The signer must be able to sign with this address.
|
||||
* @param messages
|
||||
* @param fee
|
||||
* @param memo
|
||||
*
|
||||
* @returns Returns the hash of the transaction
|
||||
*/
|
||||
public async signAndBroadcastSync(
|
||||
signerAddress: string,
|
||||
messages: readonly EncodeObject[],
|
||||
fee: StdFee | "auto" | number,
|
||||
memo = "",
|
||||
): Promise<string> {
|
||||
let usedFee: StdFee;
|
||||
if (fee == "auto" || typeof fee === "number") {
|
||||
assertDefined(this.gasPrice, "Gas price must be set in the client options when auto gas is used.");
|
||||
const gasEstimation = await this.simulate(signerAddress, messages, memo);
|
||||
const multiplier = typeof fee === "number" ? fee : 1.3;
|
||||
usedFee = calculateFee(Math.round(gasEstimation * multiplier), this.gasPrice);
|
||||
} else {
|
||||
usedFee = fee;
|
||||
}
|
||||
const txRaw = await this.sign(signerAddress, messages, usedFee, memo);
|
||||
const txBytes = TxRaw.encode(txRaw).finish();
|
||||
return this.broadcastTxSync(txBytes);
|
||||
}
|
||||
|
||||
public async sign(
|
||||
signerAddress: string,
|
||||
messages: readonly EncodeObject[],
|
||||
|
@ -690,6 +690,160 @@ describe("SigningStargateClient", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("signAndBroadcastSync", () => {
|
||||
describe("direct mode", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
simapp.tendermintUrl,
|
||||
wallet,
|
||||
defaultSigningClientOptions,
|
||||
);
|
||||
|
||||
const msgSend: MsgSend = {
|
||||
fromAddress: faucet.address0,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: coins(1234, "ucosm"),
|
||||
};
|
||||
|
||||
const msgAny: MsgSendEncodeObject = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "222000", // 222k
|
||||
};
|
||||
const memo = "Use your power wisely";
|
||||
const transactionHash = await client.signAndBroadcastSync(faucet.address0, [msgAny], fee, memo);
|
||||
|
||||
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
|
||||
|
||||
await sleep(simapp.blockTime * 1.5);
|
||||
});
|
||||
|
||||
it("works with auto gas", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, {
|
||||
...defaultSigningClientOptions,
|
||||
gasPrice: defaultGasPrice,
|
||||
});
|
||||
|
||||
const msgSend: MsgSend = {
|
||||
fromAddress: faucet.address0,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: coins(1234, "ucosm"),
|
||||
};
|
||||
|
||||
const msgAny: MsgSendEncodeObject = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
const transactionHash = await client.signAndBroadcastSync(faucet.address0, [msgAny], "auto");
|
||||
|
||||
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
|
||||
|
||||
await sleep(simapp.blockTime * 1.5);
|
||||
});
|
||||
|
||||
it("works with a modifying signer", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
simapp.tendermintUrl,
|
||||
wallet,
|
||||
defaultSigningClientOptions,
|
||||
);
|
||||
|
||||
const msgSend: MsgSend = {
|
||||
fromAddress: faucet.address0,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: coins(1234, "ucosm"),
|
||||
};
|
||||
|
||||
const msgAny: MsgSendEncodeObject = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "222000", // 222k
|
||||
};
|
||||
const memo = "Use your power wisely";
|
||||
const transactionHash = await client.signAndBroadcastSync(faucet.address0, [msgAny], fee, memo);
|
||||
|
||||
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
|
||||
|
||||
await sleep(simapp.blockTime * 1.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("legacy Amino mode", () => {
|
||||
it("works with special characters in memo", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
simapp.tendermintUrl,
|
||||
wallet,
|
||||
defaultSigningClientOptions,
|
||||
);
|
||||
|
||||
const msgSend: MsgSend = {
|
||||
fromAddress: faucet.address0,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: coins(1234, "ucosm"),
|
||||
};
|
||||
const msgAny: MsgSendEncodeObject = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "200000",
|
||||
};
|
||||
const memo = "ampersand:&,lt:<,gt:>";
|
||||
const transactionHash = await client.signAndBroadcastSync(faucet.address0, [msgAny], fee, memo);
|
||||
|
||||
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
|
||||
|
||||
await sleep(simapp.blockTime * 1.5);
|
||||
});
|
||||
|
||||
it("works with bank MsgSend", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(
|
||||
simapp.tendermintUrl,
|
||||
wallet,
|
||||
defaultSigningClientOptions,
|
||||
);
|
||||
|
||||
const msgSend: MsgSend = {
|
||||
fromAddress: faucet.address0,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: coins(1234, "ucosm"),
|
||||
};
|
||||
const msgAny: MsgSendEncodeObject = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "200000",
|
||||
};
|
||||
const memo = "Use your tokens wisely";
|
||||
const transactionHash = await client.signAndBroadcastSync(faucet.address0, [msgAny], fee, memo);
|
||||
|
||||
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
|
||||
|
||||
await sleep(simapp.blockTime * 1.5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("sign", () => {
|
||||
describe("direct mode", () => {
|
||||
it("works", async () => {
|
||||
|
@ -326,6 +326,32 @@ export class SigningStargateClient extends StargateClient {
|
||||
return this.broadcastTx(txBytes, this.broadcastTimeoutMs, this.broadcastPollIntervalMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is useful if you want to send a transaction in broadcast,
|
||||
* without waiting for it to be placed inside a block, because for example
|
||||
* I would like to receive the hash to later track the transaction with another tool.
|
||||
* @returns Returns the hash of the transaction
|
||||
*/
|
||||
public async signAndBroadcastSync(
|
||||
signerAddress: string,
|
||||
messages: readonly EncodeObject[],
|
||||
fee: StdFee | "auto" | number,
|
||||
memo = "",
|
||||
): Promise<string> {
|
||||
let usedFee: StdFee;
|
||||
if (fee == "auto" || typeof fee === "number") {
|
||||
assertDefined(this.gasPrice, "Gas price must be set in the client options when auto gas is used.");
|
||||
const gasEstimation = await this.simulate(signerAddress, messages, memo);
|
||||
const multiplier = typeof fee === "number" ? fee : 1.3;
|
||||
usedFee = calculateFee(Math.round(gasEstimation * multiplier), this.gasPrice);
|
||||
} else {
|
||||
usedFee = fee;
|
||||
}
|
||||
const txRaw = await this.sign(signerAddress, messages, usedFee, memo);
|
||||
const txBytes = TxRaw.encode(txRaw).finish();
|
||||
return this.broadcastTxSync(txBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets account number and sequence from the API, creates a sign doc,
|
||||
* creates a single signature and assembles the signed transaction.
|
||||
|
@ -554,4 +554,68 @@ describe("StargateClient", () => {
|
||||
client.disconnect();
|
||||
}, 30_000);
|
||||
});
|
||||
|
||||
describe("broadcastTxSync", () => {
|
||||
it("broadcasts sync a transaction, to get transaction hash", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await StargateClient.connect(simapp.tendermintUrl);
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const [{ address, pubkey: pubkeyBytes }] = await wallet.getAccounts();
|
||||
const pubkey = encodePubkey({
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: toBase64(pubkeyBytes),
|
||||
});
|
||||
const registry = new Registry();
|
||||
const txBodyFields: TxBodyEncodeObject = {
|
||||
typeUrl: "/cosmos.tx.v1beta1.TxBody",
|
||||
value: {
|
||||
messages: [
|
||||
{
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: {
|
||||
fromAddress: address,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: [
|
||||
{
|
||||
denom: "ucosm",
|
||||
amount: "1234567",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const txBodyBytes = registry.encode(txBodyFields);
|
||||
const { accountNumber, sequence } = (await client.getSequence(address))!;
|
||||
const feeAmount = coins(2000, "ucosm");
|
||||
const gasLimit = 200000;
|
||||
const feeGranter = undefined;
|
||||
const feePayer = undefined;
|
||||
const authInfoBytes = makeAuthInfoBytes(
|
||||
[{ pubkey, sequence }],
|
||||
feeAmount,
|
||||
gasLimit,
|
||||
feeGranter,
|
||||
feePayer,
|
||||
);
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature } = await wallet.signDirect(address, signDoc);
|
||||
const txRaw = TxRaw.fromPartial({
|
||||
bodyBytes: txBodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
signatures: [fromBase64(signature.signature)],
|
||||
});
|
||||
const txRawBytes = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
||||
const transactionHash = await client.broadcastTxSync(txRawBytes);
|
||||
|
||||
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
|
||||
|
||||
await sleep(simapp.blockTime * 1.5);
|
||||
|
||||
client.disconnect();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -464,13 +464,8 @@ export class StargateClient {
|
||||
: pollForTx(txId);
|
||||
};
|
||||
|
||||
const broadcasted = await this.forceGetTmClient().broadcastTxSync({ tx });
|
||||
if (broadcasted.code) {
|
||||
return Promise.reject(
|
||||
new BroadcastTxError(broadcasted.code, broadcasted.codespace ?? "", broadcasted.log),
|
||||
);
|
||||
}
|
||||
const transactionId = toHex(broadcasted.hash).toUpperCase();
|
||||
const transactionId = await this.broadcastTxSync(tx);
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
pollForTx(transactionId).then(
|
||||
(value) => {
|
||||
@ -485,6 +480,31 @@ export class StargateClient {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a signed transaction to the network without monitoring it.
|
||||
*
|
||||
* If broadcasting is rejected by the node for some reason (e.g. because of a CheckTx failure),
|
||||
* an error is thrown.
|
||||
*
|
||||
* If the transaction is broadcasted, a `string` containing the hash of the transaction is returned. The caller then
|
||||
* usually needs to check if the transaction was included in a block and was successful.
|
||||
*
|
||||
* @returns Returns the hash of the transaction
|
||||
*/
|
||||
public async broadcastTxSync(tx: Uint8Array): Promise<string> {
|
||||
const broadcasted = await this.forceGetTmClient().broadcastTxSync({ tx });
|
||||
|
||||
if (broadcasted.code) {
|
||||
return Promise.reject(
|
||||
new BroadcastTxError(broadcasted.code, broadcasted.codespace ?? "", broadcasted.log),
|
||||
);
|
||||
}
|
||||
|
||||
const transactionId = toHex(broadcasted.hash).toUpperCase();
|
||||
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
private async txsQuery(query: string): Promise<IndexedTx[]> {
|
||||
const results = await this.forceGetTmClient().txSearchAll({ query: query });
|
||||
return results.txs.map((tx): IndexedTx => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user