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

Improve DX - Stargate sign and broadcast #1396

Merged
merged 12 commits into from
Jun 19, 2023
Merged
34 changes: 27 additions & 7 deletions packages/cosmwasm-stargate/src/cosmwasmclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,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) => {
Expand All @@ -316,6 +311,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.
*
Expand Down
34 changes: 34 additions & 0 deletions packages/cosmwasm-stargate/src/signingcosmwasmclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,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[],
Expand Down
26 changes: 26 additions & 0 deletions packages/stargate/src/signingstargateclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 27 additions & 7 deletions packages/stargate/src/stargateclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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 => {
Expand Down