stargate: Add SigningCosmosClient
This commit is contained in:
parent
7d3aa4c9ea
commit
1f4f442375
135
packages/stargate/src/signingstargateclient.ts
Normal file
135
packages/stargate/src/signingstargateclient.ts
Normal file
@ -0,0 +1,135 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
buildFeeTable,
|
||||
Coin,
|
||||
CosmosFeeTable,
|
||||
encodeSecp256k1Pubkey,
|
||||
GasLimits,
|
||||
GasPrice,
|
||||
StdFee,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { Int53 } from "@cosmjs/math";
|
||||
import {
|
||||
EncodeObject,
|
||||
encodePubkey,
|
||||
isOfflineDirectSigner,
|
||||
makeAuthInfoBytes,
|
||||
makeSignDoc,
|
||||
OfflineSigner,
|
||||
Registry,
|
||||
} from "@cosmjs/proto-signing";
|
||||
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
|
||||
|
||||
import { cosmos } from "./codec";
|
||||
import { BroadcastTxResponse, StargateClient } from "./stargateclient";
|
||||
|
||||
const { TxRaw } = cosmos.tx.v1beta1;
|
||||
|
||||
const defaultGasPrice = GasPrice.fromString("0.025ucosm");
|
||||
const defaultGasLimits: GasLimits<CosmosFeeTable> = { send: 80000 };
|
||||
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningStargateClient {
|
||||
readonly fees: CosmosFeeTable;
|
||||
readonly registry: Registry;
|
||||
}
|
||||
|
||||
export interface SigningStargateClientOptions {
|
||||
readonly registry?: Registry;
|
||||
readonly gasPrice?: GasPrice;
|
||||
readonly gasLimits?: GasLimits<CosmosFeeTable>;
|
||||
}
|
||||
|
||||
export class SigningStargateClient extends StargateClient {
|
||||
private readonly fees: CosmosFeeTable;
|
||||
private readonly registry: Registry;
|
||||
private readonly signer: OfflineSigner;
|
||||
|
||||
public static async connectWithWallet(
|
||||
endpoint: string,
|
||||
signer: OfflineSigner,
|
||||
options: SigningStargateClientOptions = {},
|
||||
): Promise<SigningStargateClient> {
|
||||
const tmClient = await TendermintClient.connect(endpoint);
|
||||
return new SigningStargateClient(tmClient, signer, options);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
tmClient: TendermintClient,
|
||||
signer: OfflineSigner,
|
||||
options: SigningStargateClientOptions,
|
||||
) {
|
||||
super(tmClient);
|
||||
const { registry = new Registry(), gasPrice = defaultGasPrice, gasLimits = defaultGasLimits } = options;
|
||||
this.fees = buildFeeTable<CosmosFeeTable>(gasPrice, defaultGasLimits, gasLimits);
|
||||
this.registry = registry;
|
||||
this.signer = signer;
|
||||
}
|
||||
|
||||
public async sendTokens(
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
transferAmount: readonly Coin[],
|
||||
memo = "",
|
||||
): Promise<BroadcastTxResponse> {
|
||||
const sendMsg = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: {
|
||||
fromAddress: senderAddress,
|
||||
toAddress: recipientAddress,
|
||||
amount: transferAmount,
|
||||
},
|
||||
};
|
||||
return this.signAndBroadcast(senderAddress, [sendMsg], this.fees.send, memo);
|
||||
}
|
||||
|
||||
public async signAndBroadcast(
|
||||
address: string,
|
||||
messages: readonly EncodeObject[],
|
||||
fee: StdFee,
|
||||
memo = "",
|
||||
): Promise<BroadcastTxResponse> {
|
||||
if (!isOfflineDirectSigner(this.signer)) {
|
||||
throw new Error("Amino signer not yet supported");
|
||||
}
|
||||
|
||||
const accountFromSigner = (await this.signer.getAccounts()).find(
|
||||
(account) => account.address === address,
|
||||
);
|
||||
if (!accountFromSigner) {
|
||||
throw new Error("Failed to retrieve account from signer");
|
||||
}
|
||||
const pubkey = encodeSecp256k1Pubkey(accountFromSigner.pubkey);
|
||||
const accountFromChain = await this.getAccount(address);
|
||||
if (!accountFromChain) {
|
||||
throw new Error("Account not found");
|
||||
}
|
||||
const { accountNumber, sequence } = accountFromChain;
|
||||
if (!pubkey) {
|
||||
throw new Error("Pubkey not known");
|
||||
}
|
||||
const chainId = await this.getChainId();
|
||||
const pubkeyAny = encodePubkey(pubkey);
|
||||
const txBody = {
|
||||
messages: messages,
|
||||
memo: memo,
|
||||
};
|
||||
const txBodyBytes = this.registry.encode({
|
||||
typeUrl: "/cosmos.tx.v1beta1.TxBody",
|
||||
value: txBody,
|
||||
});
|
||||
const gasLimit = Int53.fromString(fee.gas).toNumber();
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence);
|
||||
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const signResponse = await this.signer.signDirect(address, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: txBodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
signatures: [fromBase64(signResponse.signature.signature)],
|
||||
});
|
||||
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
||||
return this.broadcastTx(signedTx);
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,7 @@ export class StargateClient {
|
||||
return new StargateClient(tmClient);
|
||||
}
|
||||
|
||||
private constructor(tmClient: TendermintClient) {
|
||||
protected constructor(tmClient: TendermintClient) {
|
||||
this.tmClient = tmClient;
|
||||
this.queryClient = QueryClient.withExtensions(tmClient, setupAuthExtension, setupBankExtension);
|
||||
}
|
||||
|
||||
36
packages/stargate/types/signingstargateclient.d.ts
vendored
Normal file
36
packages/stargate/types/signingstargateclient.d.ts
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
import { Coin, CosmosFeeTable, GasLimits, GasPrice, StdFee } from "@cosmjs/launchpad";
|
||||
import { EncodeObject, OfflineSigner, Registry } from "@cosmjs/proto-signing";
|
||||
import { BroadcastTxResponse, StargateClient } from "./stargateclient";
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningStargateClient {
|
||||
readonly fees: CosmosFeeTable;
|
||||
readonly registry: Registry;
|
||||
}
|
||||
export interface SigningStargateClientOptions {
|
||||
readonly registry?: Registry;
|
||||
readonly gasPrice?: GasPrice;
|
||||
readonly gasLimits?: GasLimits<CosmosFeeTable>;
|
||||
}
|
||||
export declare class SigningStargateClient extends StargateClient {
|
||||
private readonly fees;
|
||||
private readonly registry;
|
||||
private readonly signer;
|
||||
static connectWithWallet(
|
||||
endpoint: string,
|
||||
signer: OfflineSigner,
|
||||
options?: SigningStargateClientOptions,
|
||||
): Promise<SigningStargateClient>;
|
||||
private constructor();
|
||||
sendTokens(
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
transferAmount: readonly Coin[],
|
||||
memo?: string,
|
||||
): Promise<BroadcastTxResponse>;
|
||||
signAndBroadcast(
|
||||
address: string,
|
||||
messages: readonly EncodeObject[],
|
||||
fee: StdFee,
|
||||
memo?: string,
|
||||
): Promise<BroadcastTxResponse>;
|
||||
}
|
||||
2
packages/stargate/types/stargateclient.d.ts
vendored
2
packages/stargate/types/stargateclient.d.ts
vendored
@ -52,7 +52,7 @@ export declare class StargateClient {
|
||||
private readonly queryClient;
|
||||
private chainId;
|
||||
static connect(endpoint: string): Promise<StargateClient>;
|
||||
private constructor();
|
||||
protected constructor(tmClient: TendermintClient);
|
||||
getChainId(): Promise<string>;
|
||||
getHeight(): Promise<number>;
|
||||
getAccount(searchAddress: string): Promise<Account | null>;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user