Merge pull request #482 from cosmos/272-signing-stargate-client
Add SigningStargateClient
This commit is contained in:
commit
38d762af22
@ -6,6 +6,13 @@
|
||||
@cosmjs/launchpad instead.
|
||||
- @cosmjs/launchpad: Add `Secp256k1Wallet` to manage a single raw secp256k1
|
||||
keypair.
|
||||
- @cosmjs/launchpad: `OfflineSigner` type’s `sign` method renamed `signAmino`
|
||||
and `SignResponse` type renamed `AminoSignResponse`.
|
||||
- @cosmjs/launchpad: `Secp256k1HdWallet.sign` method renamed `signAmino`.
|
||||
- @cosmjs/launchpad-ledger: `LedgerSigner.sign` method renamed `signAmino`.
|
||||
- @cosmjs/proto-signing: Add new package for handling transaction signing with
|
||||
protobuf encoding.
|
||||
- @cosmjs/stargate: Add new package for Cosmos SDK Stargate support.
|
||||
|
||||
## 0.23.1 (2020-10-27)
|
||||
|
||||
|
||||
@ -74,7 +74,7 @@ const signDoc = makeSignDoc(
|
||||
account_number,
|
||||
sequence,
|
||||
);
|
||||
const { signed, signature } = await wallet.sign(faucetAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(faucetAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
const broadcastResult = await client.broadcastTx(signedTx);
|
||||
```
|
||||
|
||||
@ -28,7 +28,7 @@ const { accountNumber, sequence } = await client.getSequence(senderAddress);
|
||||
console.log("Account/sequence:", accountNumber, sequence);
|
||||
|
||||
const signDoc = makeSignDoc([msg], fee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await wallet.sign(senderAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(senderAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
|
||||
const result = await client.broadcastTx(signedTx);
|
||||
|
||||
@ -176,7 +176,7 @@ export async function main(originalArgs: readonly string[]): Promise<void> {
|
||||
gas: "89000000",
|
||||
};
|
||||
const signDoc = makeSignDoc([], fee, "chain-xyz", "hello, world", 1, 2);
|
||||
const { signed, signature } = await wallet.sign(address, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(address, signDoc);
|
||||
assert(signed.memo === "hello, world");
|
||||
|
||||
const bechPubkey = "coralvalconspub1zcjduepqvxg72ccnl9r65fv0wn3amlk4sfzqfe2k36l073kjx2qyaf6sk23qw7j8wq";
|
||||
|
||||
@ -107,7 +107,7 @@ describe("CosmWasmClient.searchTx", () => {
|
||||
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.sign(alice.address0, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(alice.address0, signDoc);
|
||||
const tx: WrappedStdTx = {
|
||||
type: "cosmos-sdk/StdTx",
|
||||
value: makeStdTx(signed, signature),
|
||||
|
||||
@ -240,7 +240,7 @@ describe("CosmWasmClient", () => {
|
||||
const chainId = await client.getChainId();
|
||||
const { accountNumber, sequence } = await client.getSequence(alice.address0);
|
||||
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await wallet.sign(alice.address0, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(alice.address0, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
const result = await client.broadcastTx(signedTx);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
|
||||
@ -126,7 +126,7 @@ async function executeContract(
|
||||
|
||||
const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value;
|
||||
const signDoc = makeSignDoc([theMsg], fee, wasmd.chainId, memo, account_number, sequence);
|
||||
const { signed, signature } = await signer.sign(alice.address0, signDoc);
|
||||
const { signed, signature } = await signer.signAmino(alice.address0, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
return client.broadcastTx(signedTx);
|
||||
}
|
||||
|
||||
@ -361,7 +361,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
||||
const { accountNumber, sequence } = await this.getSequence();
|
||||
const chainId = await this.getChainId();
|
||||
const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await this.signer.sign(this.senderAddress, signDoc);
|
||||
const { signed, signature } = await this.signer.signAmino(this.senderAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
return this.broadcastTx(signedTx);
|
||||
}
|
||||
|
||||
@ -57,6 +57,6 @@ export async function sign(
|
||||
},
|
||||
];
|
||||
const signDoc = makeSignDoc(msgs, defaultFee, defaultChainId, defaultMemo, accountNumber, defaultSequence);
|
||||
const { signature } = await signer.sign(fromAddress, signDoc);
|
||||
const { signature } = await signer.signAmino(fromAddress, signDoc);
|
||||
return signature;
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ window.sign = async function sign(signer: LedgerSigner | undefined): Promise<voi
|
||||
const address = document.getElementById("address").value;
|
||||
const signDocJson = document.getElementById("sign-doc").textContent;
|
||||
const signDoc: StdSignDoc = JSON.parse(signDocJson);
|
||||
const signature = await signer.sign(address, signDoc);
|
||||
const signature = await signer.signAmino(address, signDoc);
|
||||
signatureDiv.textContent = JSON.stringify(signature, null, "\t");
|
||||
} catch (error) {
|
||||
signatureDiv.textContent = error;
|
||||
|
||||
@ -120,7 +120,7 @@ describe("LedgerSigner", () => {
|
||||
defaultAccountNumber,
|
||||
defaultSequence,
|
||||
);
|
||||
const { signed, signature } = await signer.sign(fistAccount.address, signDoc);
|
||||
const { signed, signature } = await signer.signAmino(fistAccount.address, signDoc);
|
||||
expect(signed).toEqual(signDoc);
|
||||
const valid = await Secp256k1.verifySignature(
|
||||
Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)),
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
OfflineSigner,
|
||||
StdSignDoc,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { serializeSignDoc, SignResponse } from "@cosmjs/launchpad";
|
||||
import { AminoSignResponse, serializeSignDoc } from "@cosmjs/launchpad";
|
||||
import Transport from "@ledgerhq/hw-transport";
|
||||
|
||||
import { LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger";
|
||||
@ -36,7 +36,7 @@ export class LedgerSigner implements OfflineSigner {
|
||||
return this.accounts;
|
||||
}
|
||||
|
||||
public async sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse> {
|
||||
public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse> {
|
||||
const accounts = this.accounts || (await this.getAccounts());
|
||||
const accountIndex = accounts.findIndex((account) => account.address === signerAddress);
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/// <reference types="ledgerhq__hw-transport" />
|
||||
import { AccountData, OfflineSigner, StdSignDoc } from "@cosmjs/launchpad";
|
||||
import { SignResponse } from "@cosmjs/launchpad";
|
||||
import { AminoSignResponse } from "@cosmjs/launchpad";
|
||||
import Transport from "@ledgerhq/hw-transport";
|
||||
import { LaunchpadLedgerOptions } from "./launchpadledger";
|
||||
export declare class LedgerSigner implements OfflineSigner {
|
||||
@ -9,5 +9,5 @@ export declare class LedgerSigner implements OfflineSigner {
|
||||
private accounts?;
|
||||
constructor(transport: Transport, options?: LaunchpadLedgerOptions);
|
||||
getAccounts(): Promise<readonly AccountData[]>;
|
||||
sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse>;
|
||||
signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse>;
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ describe("CosmosClient.searchTx", () => {
|
||||
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.sign(walletAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
|
||||
const tx: WrappedStdTx = {
|
||||
type: "cosmos-sdk/StdTx",
|
||||
value: makeStdTx(signed, signature),
|
||||
|
||||
@ -232,7 +232,7 @@ describe("CosmosClient", () => {
|
||||
const chainId = await client.getChainId();
|
||||
const { accountNumber, sequence } = await client.getSequence(faucet.address);
|
||||
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await wallet.sign(walletAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
const txResult = await client.broadcastTx(signedTx);
|
||||
assertIsBroadcastTxSuccess(txResult);
|
||||
|
||||
@ -111,7 +111,7 @@ export {
|
||||
} from "./pubkey";
|
||||
export { findSequenceForSignedTx } from "./sequence";
|
||||
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
|
||||
export { AccountData, Algo, OfflineSigner, SignResponse } from "./signer";
|
||||
export { AccountData, Algo, AminoSignResponse, OfflineSigner } from "./signer";
|
||||
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { isStdTx, isWrappedStdTx, makeStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx";
|
||||
export { pubkeyType, PubKey, StdFee, StdSignature } from "./types";
|
||||
|
||||
@ -47,7 +47,7 @@ describe("DistributionExtension", () => {
|
||||
const memo = "Test delegation for wasmd";
|
||||
const { accountNumber, sequence } = await client.getSequence();
|
||||
const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await wallet.sign(faucet.address, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(faucet.address, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
|
||||
const result = await client.broadcastTx(signedTx);
|
||||
|
||||
@ -58,7 +58,7 @@ describe("GovExtension", () => {
|
||||
proposalAccountNumber,
|
||||
proposalSequence,
|
||||
);
|
||||
const { signature: proposalSignature } = await wallet.sign(faucet.address, proposalSignDoc);
|
||||
const { signature: proposalSignature } = await wallet.signAmino(faucet.address, proposalSignDoc);
|
||||
const proposalTx = {
|
||||
msg: [proposalMsg],
|
||||
fee: defaultFee,
|
||||
@ -90,7 +90,7 @@ describe("GovExtension", () => {
|
||||
voteAccountNumber,
|
||||
voteSequence,
|
||||
);
|
||||
const { signature: voteSignature } = await wallet.sign(faucet.address, voteSignDoc);
|
||||
const { signature: voteSignature } = await wallet.signAmino(faucet.address, voteSignDoc);
|
||||
const voteTx = {
|
||||
msg: [voteMsg],
|
||||
fee: defaultFee,
|
||||
|
||||
@ -240,7 +240,7 @@ describe("LcdClient", () => {
|
||||
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.sign(walletAddress, signDoc);
|
||||
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);
|
||||
@ -534,7 +534,7 @@ describe("LcdClient", () => {
|
||||
const { account_number, sequence } = (await client.auth.account(faucet.address)).result.value;
|
||||
|
||||
const signDoc = makeSignDoc([theMsg], fee, wasmd.chainId, memo, account_number, sequence);
|
||||
const { signed, signature } = await wallet.sign(walletAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
const result = await client.broadcastTx(signedTx);
|
||||
expect(result.code).toBeUndefined();
|
||||
@ -593,9 +593,9 @@ describe("LcdClient", () => {
|
||||
const signDoc1 = makeSignDoc([theMsg], fee, wasmd.chainId, memo, an1, sequence1);
|
||||
const signDoc2 = makeSignDoc([theMsg], fee, wasmd.chainId, memo, an2, sequence2);
|
||||
const signDoc3 = makeSignDoc([theMsg], fee, wasmd.chainId, memo, an3, sequence3);
|
||||
const { signature: signature1 } = await account1.sign(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.sign(address2, signDoc2);
|
||||
const { signature: signature3 } = await account3.sign(address3, signDoc3);
|
||||
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,
|
||||
@ -655,7 +655,7 @@ describe("LcdClient", () => {
|
||||
const { account_number, sequence } = (await client.auth.account(walletAddress)).result.value;
|
||||
|
||||
const signDoc = makeSignDoc([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence);
|
||||
const { signed, signature } = await wallet.sign(walletAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
const broadcastResult = await client.broadcastTx(signedTx);
|
||||
expect(broadcastResult.code).toBeUndefined();
|
||||
@ -715,8 +715,8 @@ describe("LcdClient", () => {
|
||||
|
||||
const signDoc1 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1);
|
||||
const signDoc2 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2);
|
||||
const { signature: signature1 } = await account1.sign(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.sign(address2, signDoc2);
|
||||
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
|
||||
const signedTx: StdTx = {
|
||||
msg: [msg2, msg1],
|
||||
fee: fee,
|
||||
@ -786,8 +786,8 @@ describe("LcdClient", () => {
|
||||
|
||||
const signDoc1 = makeSignDoc([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1);
|
||||
const signDoc2 = makeSignDoc([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2);
|
||||
const { signature: signature1 } = await account1.sign(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.sign(address2, signDoc2);
|
||||
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
|
||||
const signedTx: StdTx = {
|
||||
msg: [msg1, msg2],
|
||||
fee: fee,
|
||||
@ -852,8 +852,8 @@ describe("LcdClient", () => {
|
||||
|
||||
const signDoc1 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1);
|
||||
const signDoc2 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2);
|
||||
const { signature: signature1 } = await account1.sign(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.sign(address2, signDoc2);
|
||||
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
|
||||
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
|
||||
const signedTx: StdTx = {
|
||||
msg: [msg2, msg1],
|
||||
fee: fee,
|
||||
|
||||
@ -48,7 +48,7 @@ describe("StakingExtension", () => {
|
||||
const memo = "Test delegation for wasmd";
|
||||
const { accountNumber, sequence } = await client.getSequence();
|
||||
const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await wallet.sign(faucet.address, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(faucet.address, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
|
||||
const result = await client.broadcastTx(signedTx);
|
||||
@ -66,7 +66,7 @@ describe("StakingExtension", () => {
|
||||
const memo = "Test undelegation for wasmd";
|
||||
const { accountNumber, sequence } = await client.getSequence();
|
||||
const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await wallet.sign(faucet.address, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(faucet.address, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
|
||||
const result = await client.broadcastTx(signedTx);
|
||||
|
||||
@ -109,7 +109,7 @@ describe("Secp256k1HdWallet", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("sign", () => {
|
||||
describe("signAmino", () => {
|
||||
it("resolves to valid signature", async () => {
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(defaultMnemonic);
|
||||
const signDoc: StdSignDoc = {
|
||||
@ -120,7 +120,7 @@ describe("Secp256k1HdWallet", () => {
|
||||
account_number: "7",
|
||||
sequence: "54",
|
||||
};
|
||||
const { signed, signature } = await wallet.sign(defaultAddress, signDoc);
|
||||
const { signed, signature } = await wallet.signAmino(defaultAddress, signDoc);
|
||||
expect(signed).toEqual(signDoc);
|
||||
const valid = await Secp256k1.verifySignature(
|
||||
Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)),
|
||||
|
||||
@ -16,7 +16,7 @@ import { assert, isNonNullObject } from "@cosmjs/utils";
|
||||
import { rawSecp256k1PubkeyToAddress } from "./address";
|
||||
import { serializeSignDoc, StdSignDoc } from "./encoding";
|
||||
import { encodeSecp256k1Signature } from "./signature";
|
||||
import { AccountData, OfflineSigner, SignResponse } from "./signer";
|
||||
import { AccountData, AminoSignResponse, OfflineSigner } from "./signer";
|
||||
import {
|
||||
decrypt,
|
||||
encrypt,
|
||||
@ -259,7 +259,7 @@ export class Secp256k1HdWallet implements OfflineSigner {
|
||||
];
|
||||
}
|
||||
|
||||
public async sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse> {
|
||||
public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse> {
|
||||
if (signerAddress !== this.address) {
|
||||
throw new Error(`Address ${signerAddress} not found in wallet`);
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ describe("Secp256k1Wallet", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("sign", () => {
|
||||
describe("signAmino", () => {
|
||||
it("resolves to valid signature", async () => {
|
||||
const signer = await Secp256k1Wallet.fromKey(defaultPrivkey);
|
||||
const signDoc: StdSignDoc = {
|
||||
@ -41,7 +41,7 @@ describe("Secp256k1Wallet", () => {
|
||||
account_number: "7",
|
||||
sequence: "54",
|
||||
};
|
||||
const { signed, signature } = await signer.sign(defaultAddress, signDoc);
|
||||
const { signed, signature } = await signer.signAmino(defaultAddress, signDoc);
|
||||
expect(signed).toEqual(signDoc);
|
||||
const valid = await Secp256k1.verifySignature(
|
||||
Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)),
|
||||
|
||||
@ -3,7 +3,7 @@ import { Secp256k1, Sha256 } from "@cosmjs/crypto";
|
||||
import { rawSecp256k1PubkeyToAddress } from "./address";
|
||||
import { serializeSignDoc, StdSignDoc } from "./encoding";
|
||||
import { encodeSecp256k1Signature } from "./signature";
|
||||
import { AccountData, OfflineSigner, SignResponse } from "./signer";
|
||||
import { AccountData, AminoSignResponse, OfflineSigner } from "./signer";
|
||||
|
||||
/**
|
||||
* A wallet that holds a single secp256k1 keypair.
|
||||
@ -46,7 +46,7 @@ export class Secp256k1Wallet implements OfflineSigner {
|
||||
];
|
||||
}
|
||||
|
||||
public async sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse> {
|
||||
public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse> {
|
||||
if (signerAddress !== this.address) {
|
||||
throw new Error(`Address ${signerAddress} not found in wallet`);
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export interface AccountData {
|
||||
readonly pubkey: Uint8Array;
|
||||
}
|
||||
|
||||
export interface SignResponse {
|
||||
export interface AminoSignResponse {
|
||||
/**
|
||||
* The sign doc that was signed.
|
||||
* This may be different from the input signDoc when the signer modifies it as part of the signing process.
|
||||
@ -34,5 +34,5 @@ export interface OfflineSigner {
|
||||
* @param signerAddress The address of the account that should sign the transaction
|
||||
* @param signDoc The content that should be signed
|
||||
*/
|
||||
readonly sign: (signerAddress: string, signDoc: StdSignDoc) => Promise<SignResponse>;
|
||||
readonly signAmino: (signerAddress: string, signDoc: StdSignDoc) => Promise<AminoSignResponse>;
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ export class SigningCosmosClient extends CosmosClient {
|
||||
const { accountNumber, sequence } = await this.getSequence();
|
||||
const chainId = await this.getChainId();
|
||||
const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence);
|
||||
const { signed, signature } = await this.signer.sign(this.senderAddress, signDoc);
|
||||
const { signed, signature } = await this.signer.signAmino(this.senderAddress, signDoc);
|
||||
const signedTx = makeStdTx(signed, signature);
|
||||
return this.broadcastTx(signedTx);
|
||||
}
|
||||
|
||||
2
packages/launchpad/types/index.d.ts
vendored
2
packages/launchpad/types/index.d.ts
vendored
@ -109,7 +109,7 @@ export {
|
||||
} from "./pubkey";
|
||||
export { findSequenceForSignedTx } from "./sequence";
|
||||
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
|
||||
export { AccountData, Algo, OfflineSigner, SignResponse } from "./signer";
|
||||
export { AccountData, Algo, AminoSignResponse, OfflineSigner } from "./signer";
|
||||
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { isStdTx, isWrappedStdTx, makeStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx";
|
||||
export { pubkeyType, PubKey, StdFee, StdSignature } from "./types";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { HdPath } from "@cosmjs/crypto";
|
||||
import { StdSignDoc } from "./encoding";
|
||||
import { AccountData, OfflineSigner, SignResponse } from "./signer";
|
||||
import { AccountData, AminoSignResponse, OfflineSigner } from "./signer";
|
||||
import { EncryptionConfiguration, KdfConfiguration } from "./wallet";
|
||||
/**
|
||||
* This interface describes a JSON object holding the encrypted wallet and the meta data.
|
||||
@ -70,7 +70,7 @@ export declare class Secp256k1HdWallet implements OfflineSigner {
|
||||
get mnemonic(): string;
|
||||
private get address();
|
||||
getAccounts(): Promise<readonly AccountData[]>;
|
||||
sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse>;
|
||||
signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse>;
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { StdSignDoc } from "./encoding";
|
||||
import { AccountData, OfflineSigner, SignResponse } from "./signer";
|
||||
import { AccountData, OfflineSigner, AminoSignResponse } from "./signer";
|
||||
/**
|
||||
* A wallet that holds a single secp256k1 keypair.
|
||||
*
|
||||
@ -19,5 +19,5 @@ export declare class Secp256k1Wallet implements OfflineSigner {
|
||||
private constructor();
|
||||
private get address();
|
||||
getAccounts(): Promise<readonly AccountData[]>;
|
||||
sign(signerAddress: string, signDoc: StdSignDoc): Promise<SignResponse>;
|
||||
signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse>;
|
||||
}
|
||||
|
||||
4
packages/launchpad/types/signer.d.ts
vendored
4
packages/launchpad/types/signer.d.ts
vendored
@ -7,7 +7,7 @@ export interface AccountData {
|
||||
readonly algo: Algo;
|
||||
readonly pubkey: Uint8Array;
|
||||
}
|
||||
export interface SignResponse {
|
||||
export interface AminoSignResponse {
|
||||
/**
|
||||
* The sign doc that was signed.
|
||||
* This may be different from the input signDoc when the signer modifies it as part of the signing process.
|
||||
@ -29,5 +29,5 @@ export interface OfflineSigner {
|
||||
* @param signerAddress The address of the account that should sign the transaction
|
||||
* @param signDoc The content that should be signed
|
||||
*/
|
||||
readonly sign: (signerAddress: string, signDoc: StdSignDoc) => Promise<SignResponse>;
|
||||
readonly signAmino: (signerAddress: string, signDoc: StdSignDoc) => Promise<AminoSignResponse>;
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto";
|
||||
import { fromBase64, fromHex, toAscii } from "@cosmjs/encoding";
|
||||
import { fromBase64, fromHex } from "@cosmjs/encoding";
|
||||
import { coins } from "@cosmjs/launchpad";
|
||||
|
||||
import { DirectSecp256k1Wallet } from "./directsecp256k1wallet";
|
||||
import { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing";
|
||||
import { faucet, testVectors } from "./testutils.spec";
|
||||
|
||||
describe("DirectSecp256k1Wallet", () => {
|
||||
// m/44'/118'/0'/0/0
|
||||
@ -54,15 +57,30 @@ describe("DirectSecp256k1Wallet", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("sign", () => {
|
||||
describe("signDirect", () => {
|
||||
it("resolves to valid signature", async () => {
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(defaultMnemonic);
|
||||
const message = toAscii("foo bar");
|
||||
const signature = await wallet.sign(defaultAddress, message);
|
||||
const { sequence, bodyBytes } = testVectors[1];
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const pubkey = {
|
||||
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
|
||||
value: fromBase64(faucet.pubkey.value),
|
||||
};
|
||||
const fee = coins(2000, "ucosm");
|
||||
const gasLimit = 200000;
|
||||
const chainId = "simd-testing";
|
||||
const accountNumber = 1;
|
||||
const signDoc = makeSignDoc(
|
||||
fromHex(bodyBytes),
|
||||
makeAuthInfoBytes([pubkey], fee, gasLimit, sequence),
|
||||
chainId,
|
||||
accountNumber,
|
||||
);
|
||||
const signDocBytes = makeSignBytes(signDoc);
|
||||
const { signature } = await wallet.signDirect(faucet.address, signDoc);
|
||||
const valid = await Secp256k1.verifySignature(
|
||||
Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)),
|
||||
sha256(message),
|
||||
defaultPubkey,
|
||||
sha256(signDocBytes),
|
||||
pubkey.value,
|
||||
);
|
||||
expect(valid).toEqual(true);
|
||||
});
|
||||
|
||||
@ -13,9 +13,12 @@ import {
|
||||
encodeSecp256k1Signature,
|
||||
makeCosmoshubPath,
|
||||
rawSecp256k1PubkeyToAddress,
|
||||
StdSignature,
|
||||
} from "@cosmjs/launchpad";
|
||||
|
||||
import { cosmos } from "./codec";
|
||||
import { DirectSignResponse, OfflineDirectSigner } from "./signer";
|
||||
import { makeSignBytes } from "./signing";
|
||||
|
||||
/**
|
||||
* Derivation information required to derive a keypair and an address from a mnemonic.
|
||||
*/
|
||||
@ -25,7 +28,7 @@ interface Secp256k1Derivation {
|
||||
}
|
||||
|
||||
/** A wallet for protobuf based signing using SIGN_MODE_DIRECT */
|
||||
export class DirectSecp256k1Wallet {
|
||||
export class DirectSecp256k1Wallet implements OfflineDirectSigner {
|
||||
/**
|
||||
* Restores a wallet from the given BIP39 mnemonic.
|
||||
*
|
||||
@ -113,13 +116,18 @@ export class DirectSecp256k1Wallet {
|
||||
];
|
||||
}
|
||||
|
||||
public async sign(address: string, message: Uint8Array): Promise<StdSignature> {
|
||||
public async signDirect(address: string, signDoc: cosmos.tx.v1beta1.ISignDoc): Promise<DirectSignResponse> {
|
||||
const signBytes = makeSignBytes(signDoc);
|
||||
if (address !== this.address) {
|
||||
throw new Error(`Address ${address} not found in wallet`);
|
||||
}
|
||||
const hashedMessage = sha256(message);
|
||||
const hashedMessage = sha256(signBytes);
|
||||
const signature = await Secp256k1.createSignature(hashedMessage, this.privkey);
|
||||
const signatureBytes = new Uint8Array([...signature.r(32), ...signature.s(32)]);
|
||||
return encodeSecp256k1Signature(this.pubkey, signatureBytes);
|
||||
const stdSignature = encodeSecp256k1Signature(this.pubkey, signatureBytes);
|
||||
return {
|
||||
signed: signDoc,
|
||||
signature: stdSignature,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export { Coin } from "./msgs";
|
||||
export { cosmosField } from "./decorator";
|
||||
export { Registry } from "./registry";
|
||||
export { cosmosField, registered } from "./decorator";
|
||||
export { EncodeObject, Registry } from "./registry";
|
||||
export { DirectSecp256k1Wallet } from "./directsecp256k1wallet";
|
||||
export { decodePubkey, encodePubkey } from "./pubkey";
|
||||
export { makeAuthInfo, makeSignBytes } from "./signing";
|
||||
export { isOfflineDirectSigner, OfflineDirectSigner, OfflineSigner } from "./signer";
|
||||
export { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing";
|
||||
|
||||
26
packages/proto-signing/src/signer.ts
Normal file
26
packages/proto-signing/src/signer.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { AccountData, OfflineSigner as OfflineAminoSigner, StdSignature } from "@cosmjs/launchpad";
|
||||
|
||||
import { cosmos } from "./codec";
|
||||
|
||||
export interface DirectSignResponse {
|
||||
/**
|
||||
* The sign doc that was signed.
|
||||
* This may be different from the input signDoc when the signer modifies it as part of the signing process.
|
||||
*/
|
||||
readonly signed: cosmos.tx.v1beta1.ISignDoc;
|
||||
readonly signature: StdSignature;
|
||||
}
|
||||
|
||||
export interface OfflineDirectSigner {
|
||||
readonly getAccounts: () => Promise<readonly AccountData[]>;
|
||||
readonly signDirect: (
|
||||
signerAddress: string,
|
||||
signDoc: cosmos.tx.v1beta1.ISignDoc,
|
||||
) => Promise<DirectSignResponse>;
|
||||
}
|
||||
|
||||
export type OfflineSigner = OfflineAminoSigner | OfflineDirectSigner;
|
||||
|
||||
export function isOfflineDirectSigner(signer: OfflineSigner): signer is OfflineDirectSigner {
|
||||
return (signer as OfflineDirectSigner).signDirect !== undefined;
|
||||
}
|
||||
@ -5,52 +5,13 @@ import { cosmos, google } from "./codec";
|
||||
import { DirectSecp256k1Wallet } from "./directsecp256k1wallet";
|
||||
import { defaultRegistry } from "./msgs";
|
||||
import { Registry, TxBodyValue } from "./registry";
|
||||
import { makeAuthInfo, makeSignBytes } from "./signing";
|
||||
import { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing";
|
||||
import { faucet, testVectors } from "./testutils.spec";
|
||||
|
||||
const { Tx, TxRaw } = cosmos.tx.v1beta1;
|
||||
const { PubKey } = cosmos.crypto.secp256k1;
|
||||
const { Any } = google.protobuf;
|
||||
|
||||
const faucet = {
|
||||
mnemonic:
|
||||
"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone",
|
||||
pubkey: {
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
|
||||
},
|
||||
address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
};
|
||||
|
||||
const testVectors = [
|
||||
{
|
||||
sequence: 0,
|
||||
signedTxBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a02080112130a0d0a0575636f736d12043230303010c09a0c1a40c9dd20e07464d3a688ff4b710b1fbc027e495e797cfa0b4804da2ed117959227772de059808f765aa29b8f92edf30f4c2c5a438e30d3fe6897daa7141e3ce6f9",
|
||||
signBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a02080112130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
|
||||
signature:
|
||||
"c9dd20e07464d3a688ff4b710b1fbc027e495e797cfa0b4804da2ed117959227772de059808f765aa29b8f92edf30f4c2c5a438e30d3fe6897daa7141e3ce6f9",
|
||||
},
|
||||
{
|
||||
sequence: 1,
|
||||
signedTxBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c1a40525adc7e61565a509c60497b798c549fbf217bb5cd31b24cc9b419d098cc95330c99ecc4bc72448f85c365a4e3f91299a3d40412fb3751bab82f1940a83a0a4c",
|
||||
signBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
|
||||
signature:
|
||||
"525adc7e61565a509c60497b798c549fbf217bb5cd31b24cc9b419d098cc95330c99ecc4bc72448f85c365a4e3f91299a3d40412fb3751bab82f1940a83a0a4c",
|
||||
},
|
||||
{
|
||||
sequence: 2,
|
||||
signedTxBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212130a0d0a0575636f736d12043230303010c09a0c1a40f3f2ca73806f2abbf6e0fe85f9b8af66f0e9f7f79051fdb8abe5bb8633b17da132e82d577b9d5f7a6dae57a144efc9ccc6eef15167b44b3b22a57240109762af",
|
||||
signBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
|
||||
signature:
|
||||
"f3f2ca73806f2abbf6e0fe85f9b8af66f0e9f7f79051fdb8abe5bb8633b17da132e82d577b9d5f7a6dae57a144efc9ccc6eef15167b44b3b22a57240109762af",
|
||||
},
|
||||
];
|
||||
|
||||
describe("signing", () => {
|
||||
const chainId = "simd-testing";
|
||||
const toAddress = "cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu";
|
||||
@ -134,11 +95,12 @@ describe("signing", () => {
|
||||
|
||||
await Promise.all(
|
||||
testVectors.map(async ({ sequence, signBytes, signedTxBytes }) => {
|
||||
const authInfoBytes = makeAuthInfo([publicKeyAny], feeAmount, gasLimit, sequence);
|
||||
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const authInfoBytes = makeAuthInfoBytes([publicKeyAny], feeAmount, gasLimit, sequence);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const signDocBytes = makeSignBytes(signDoc);
|
||||
expect(toHex(signDocBytes)).toEqual(signBytes);
|
||||
|
||||
const signature = await wallet.sign(address, signDocBytes);
|
||||
const { signature } = await wallet.signDirect(address, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: txBodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
|
||||
@ -9,39 +9,54 @@ const { SignDoc, AuthInfo } = cosmos.tx.v1beta1;
|
||||
/**
|
||||
* Creates and serializes an AuthInfo document using SIGN_MODE_DIRECT.
|
||||
*/
|
||||
export function makeAuthInfo(
|
||||
export function makeAuthInfoBytes(
|
||||
pubkeys: readonly google.protobuf.IAny[],
|
||||
feeAmount: cosmos.base.v1beta1.Coin[],
|
||||
feeAmount: readonly cosmos.base.v1beta1.Coin[],
|
||||
gasLimit: number,
|
||||
sequence: number,
|
||||
signMode = cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_DIRECT,
|
||||
): Uint8Array {
|
||||
const authInfo = {
|
||||
signerInfos: pubkeys.map(
|
||||
(pubkey): cosmos.tx.v1beta1.ISignerInfo => ({
|
||||
publicKey: pubkey,
|
||||
modeInfo: {
|
||||
single: { mode: cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_DIRECT },
|
||||
single: { mode: signMode },
|
||||
},
|
||||
sequence: sequence ? Long.fromNumber(sequence) : undefined,
|
||||
}),
|
||||
),
|
||||
fee: { amount: feeAmount, gasLimit: Long.fromNumber(gasLimit) },
|
||||
fee: { amount: [...feeAmount], gasLimit: Long.fromNumber(gasLimit) },
|
||||
};
|
||||
return Uint8Array.from(AuthInfo.encode(authInfo).finish());
|
||||
}
|
||||
|
||||
export function makeSignBytes(
|
||||
txBody: Uint8Array,
|
||||
authInfo: Uint8Array,
|
||||
export function makeSignDoc(
|
||||
bodyBytes: Uint8Array,
|
||||
authInfoBytes: Uint8Array,
|
||||
chainId: string,
|
||||
accountNumber: number,
|
||||
): Uint8Array {
|
||||
): cosmos.tx.v1beta1.ISignDoc {
|
||||
return {
|
||||
bodyBytes: bodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
chainId: chainId,
|
||||
accountNumber: Long.fromNumber(accountNumber),
|
||||
};
|
||||
}
|
||||
|
||||
export function makeSignBytes({
|
||||
accountNumber,
|
||||
authInfoBytes,
|
||||
bodyBytes,
|
||||
chainId,
|
||||
}: cosmos.tx.v1beta1.ISignDoc): Uint8Array {
|
||||
const signDoc = SignDoc.create(
|
||||
omitDefaults({
|
||||
bodyBytes: txBody,
|
||||
authInfoBytes: authInfo,
|
||||
chainId: chainId,
|
||||
accountNumber: accountNumber,
|
||||
authInfoBytes: authInfoBytes,
|
||||
bodyBytes: bodyBytes,
|
||||
chainId: chainId,
|
||||
}),
|
||||
);
|
||||
return Uint8Array.from(SignDoc.encode(signDoc).finish());
|
||||
|
||||
@ -1,2 +1,48 @@
|
||||
/** @see https://rgxdb.com/r/1NUN74O6 */
|
||||
export const base64Matcher = /^(?:[a-zA-Z0-9+/]{4})*(?:|(?:[a-zA-Z0-9+/]{3}=)|(?:[a-zA-Z0-9+/]{2}==)|(?:[a-zA-Z0-9+/]{1}===))$/;
|
||||
|
||||
export const faucet = {
|
||||
mnemonic:
|
||||
"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone",
|
||||
pubkey: {
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
|
||||
},
|
||||
address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
};
|
||||
|
||||
export const testVectors = [
|
||||
{
|
||||
sequence: 0,
|
||||
signedTxBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a02080112130a0d0a0575636f736d12043230303010c09a0c1a40c9dd20e07464d3a688ff4b710b1fbc027e495e797cfa0b4804da2ed117959227772de059808f765aa29b8f92edf30f4c2c5a438e30d3fe6897daa7141e3ce6f9",
|
||||
bodyBytes:
|
||||
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
|
||||
signBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a02080112130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
|
||||
signature:
|
||||
"c9dd20e07464d3a688ff4b710b1fbc027e495e797cfa0b4804da2ed117959227772de059808f765aa29b8f92edf30f4c2c5a438e30d3fe6897daa7141e3ce6f9",
|
||||
},
|
||||
{
|
||||
sequence: 1,
|
||||
signedTxBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c1a40525adc7e61565a509c60497b798c549fbf217bb5cd31b24cc9b419d098cc95330c99ecc4bc72448f85c365a4e3f91299a3d40412fb3751bab82f1940a83a0a4c",
|
||||
signBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
|
||||
bodyBytes:
|
||||
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
|
||||
signature:
|
||||
"525adc7e61565a509c60497b798c549fbf217bb5cd31b24cc9b419d098cc95330c99ecc4bc72448f85c365a4e3f91299a3d40412fb3751bab82f1940a83a0a4c",
|
||||
},
|
||||
{
|
||||
sequence: 2,
|
||||
signedTxBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212130a0d0a0575636f736d12043230303010c09a0c1a40f3f2ca73806f2abbf6e0fe85f9b8af66f0e9f7f79051fdb8abe5bb8633b17da132e82d577b9d5f7a6dae57a144efc9ccc6eef15167b44b3b22a57240109762af",
|
||||
bodyBytes:
|
||||
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
|
||||
signBytes:
|
||||
"0a93010a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d12073132333435363712670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180212130a0d0a0575636f736d12043230303010c09a0c1a0c73696d642d74657374696e672001",
|
||||
signature:
|
||||
"f3f2ca73806f2abbf6e0fe85f9b8af66f0e9f7f79051fdb8abe5bb8633b17da132e82d577b9d5f7a6dae57a144efc9ccc6eef15167b44b3b22a57240109762af",
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { HdPath } from "@cosmjs/crypto";
|
||||
import { AccountData, StdSignature } from "@cosmjs/launchpad";
|
||||
import { AccountData } from "@cosmjs/launchpad";
|
||||
import { cosmos } from "./codec";
|
||||
import { DirectSignResponse, OfflineDirectSigner } from "./signer";
|
||||
/** A wallet for protobuf based signing using SIGN_MODE_DIRECT */
|
||||
export declare class DirectSecp256k1Wallet {
|
||||
export declare class DirectSecp256k1Wallet implements OfflineDirectSigner {
|
||||
/**
|
||||
* Restores a wallet from the given BIP39 mnemonic.
|
||||
*
|
||||
@ -33,5 +35,5 @@ export declare class DirectSecp256k1Wallet {
|
||||
get mnemonic(): string;
|
||||
private get address();
|
||||
getAccounts(): Promise<readonly AccountData[]>;
|
||||
sign(address: string, message: Uint8Array): Promise<StdSignature>;
|
||||
signDirect(address: string, signDoc: cosmos.tx.v1beta1.ISignDoc): Promise<DirectSignResponse>;
|
||||
}
|
||||
|
||||
7
packages/proto-signing/types/index.d.ts
vendored
7
packages/proto-signing/types/index.d.ts
vendored
@ -1,6 +1,7 @@
|
||||
export { Coin } from "./msgs";
|
||||
export { cosmosField } from "./decorator";
|
||||
export { Registry } from "./registry";
|
||||
export { cosmosField, registered } from "./decorator";
|
||||
export { EncodeObject, Registry } from "./registry";
|
||||
export { DirectSecp256k1Wallet } from "./directsecp256k1wallet";
|
||||
export { decodePubkey, encodePubkey } from "./pubkey";
|
||||
export { makeAuthInfo, makeSignBytes } from "./signing";
|
||||
export { isOfflineDirectSigner, OfflineDirectSigner, OfflineSigner } from "./signer";
|
||||
export { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing";
|
||||
|
||||
19
packages/proto-signing/types/signer.d.ts
vendored
Normal file
19
packages/proto-signing/types/signer.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
import { AccountData, OfflineSigner as OfflineAminoSigner, StdSignature } from "@cosmjs/launchpad";
|
||||
import { cosmos } from "./codec";
|
||||
export interface DirectSignResponse {
|
||||
/**
|
||||
* The sign doc that was signed.
|
||||
* This may be different from the input signDoc when the signer modifies it as part of the signing process.
|
||||
*/
|
||||
readonly signed: cosmos.tx.v1beta1.ISignDoc;
|
||||
readonly signature: StdSignature;
|
||||
}
|
||||
export interface OfflineDirectSigner {
|
||||
readonly getAccounts: () => Promise<readonly AccountData[]>;
|
||||
readonly signDirect: (
|
||||
signerAddress: string,
|
||||
signDoc: cosmos.tx.v1beta1.ISignDoc,
|
||||
) => Promise<DirectSignResponse>;
|
||||
}
|
||||
export declare type OfflineSigner = OfflineAminoSigner | OfflineDirectSigner;
|
||||
export declare function isOfflineDirectSigner(signer: OfflineSigner): signer is OfflineDirectSigner;
|
||||
19
packages/proto-signing/types/signing.d.ts
vendored
19
packages/proto-signing/types/signing.d.ts
vendored
@ -2,15 +2,22 @@ import { cosmos, google } from "./codec";
|
||||
/**
|
||||
* Creates and serializes an AuthInfo document using SIGN_MODE_DIRECT.
|
||||
*/
|
||||
export declare function makeAuthInfo(
|
||||
export declare function makeAuthInfoBytes(
|
||||
pubkeys: readonly google.protobuf.IAny[],
|
||||
feeAmount: cosmos.base.v1beta1.Coin[],
|
||||
feeAmount: readonly cosmos.base.v1beta1.Coin[],
|
||||
gasLimit: number,
|
||||
sequence: number,
|
||||
signMode?: cosmos.tx.signing.v1beta1.SignMode,
|
||||
): Uint8Array;
|
||||
export declare function makeSignBytes(
|
||||
txBody: Uint8Array,
|
||||
authInfo: Uint8Array,
|
||||
export declare function makeSignDoc(
|
||||
bodyBytes: Uint8Array,
|
||||
authInfoBytes: Uint8Array,
|
||||
chainId: string,
|
||||
accountNumber: number,
|
||||
): Uint8Array;
|
||||
): cosmos.tx.v1beta1.ISignDoc;
|
||||
export declare function makeSignBytes({
|
||||
accountNumber,
|
||||
authInfoBytes,
|
||||
bodyBytes,
|
||||
chainId,
|
||||
}: cosmos.tx.v1beta1.ISignDoc): Uint8Array;
|
||||
|
||||
@ -22,11 +22,15 @@ yarn pbjs \
|
||||
--force-long \
|
||||
"$COSMOS_PROTO_DIR/auth/v1beta1/auth.proto" \
|
||||
"$COSMOS_PROTO_DIR/auth/v1beta1/query.proto" \
|
||||
"$COSMOS_PROTO_DIR/bank/v1beta1/bank.proto" \
|
||||
"$COSMOS_PROTO_DIR/bank/v1beta1/query.proto" \
|
||||
"$COSMOS_PROTO_DIR/bank/v1beta1/tx.proto" \
|
||||
"$COSMOS_PROTO_DIR/base/query/v1beta1/pagination.proto" \
|
||||
"$COSMOS_PROTO_DIR/base/v1beta1/coin.proto" \
|
||||
"$COSMOS_PROTO_DIR/crypto/multisig/v1beta1/multisig.proto" \
|
||||
"$COSMOS_PROTO_DIR/crypto/secp256k1/keys.proto" \
|
||||
"$COSMOS_PROTO_DIR/staking/v1beta1/staking.proto" \
|
||||
"$COSMOS_PROTO_DIR/staking/v1beta1/tx.proto" \
|
||||
"$COSMOS_PROTO_DIR/tx/signing/v1beta1/signing.proto" \
|
||||
"$COSMOS_PROTO_DIR/tx/v1beta1/tx.proto" \
|
||||
"$IBC_PROTO_DIR/core/channel/v1/channel.proto" \
|
||||
@ -35,7 +39,12 @@ yarn pbjs \
|
||||
"$IBC_PROTO_DIR/core/commitment/v1/commitment.proto" \
|
||||
"$IBC_PROTO_DIR/core/connection/v1/connection.proto" \
|
||||
"$IBC_PROTO_DIR/core/connection/v1/query.proto" \
|
||||
"$TENDERMINT_PROTO_DIR/crypto/proof.proto"
|
||||
"$TENDERMINT_PROTO_DIR/crypto/keys.proto" \
|
||||
"$TENDERMINT_PROTO_DIR/crypto/proof.proto" \
|
||||
"$TENDERMINT_PROTO_DIR/libs/bits/types.proto" \
|
||||
"$TENDERMINT_PROTO_DIR/types/types.proto" \
|
||||
"$TENDERMINT_PROTO_DIR/types/validator.proto" \
|
||||
"$TENDERMINT_PROTO_DIR/version/types.proto"
|
||||
|
||||
# Work around https://github.com/protobufjs/protobuf.js/issues/1477
|
||||
# shellcheck disable=SC2016
|
||||
|
||||
3342
packages/stargate/src/codec/generated/codecimpl.d.ts
vendored
3342
packages/stargate/src/codec/generated/codecimpl.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
15
packages/stargate/src/encoding.spec.ts
Normal file
15
packages/stargate/src/encoding.spec.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { getMsgType } from "./encoding";
|
||||
|
||||
describe("encoding", () => {
|
||||
describe("getMsgType", () => {
|
||||
it("works for known type url", () => {
|
||||
const msgType = getMsgType("/cosmos.staking.v1beta1.MsgDelegate");
|
||||
expect(msgType).toEqual("cosmos-sdk/MsgDelegate");
|
||||
});
|
||||
|
||||
it("throws for unknown type url", () => {
|
||||
expect(() => getMsgType("/xxx.Unknown")).toThrowError(/type url not known/i);
|
||||
});
|
||||
});
|
||||
});
|
||||
27
packages/stargate/src/encoding.ts
Normal file
27
packages/stargate/src/encoding.ts
Normal file
@ -0,0 +1,27 @@
|
||||
export function getMsgType(typeUrl: string): string {
|
||||
const typeRegister: Record<string, string> = {
|
||||
"/cosmos.bank.v1beta1.MsgSend": "cosmos-sdk/MsgSend",
|
||||
"/cosmos.bank.v1beta1.MsgMultiSend": "cosmos-sdk/MsgMultiSend",
|
||||
"/cosmos.crisis.v1beta1.MsgVerifyInvariant": "cosmos-sdk/MsgVerifyInvariant",
|
||||
"/cosmos.distribution.v1beta1.MsgSetWithdrawAddress": "cosmos-sdk/MsgSetWithdrawAddress",
|
||||
"/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward": "cosmos-sdk/MsgWithdrawDelegatorReward",
|
||||
"/cosmos.distribution.v1beta1.MsgWithdrawValidatorComission": "cosmos-sdk/MsgWithdrawValidatorComission",
|
||||
"/cosmos.distribution.v1beta1.MsgFundCommunityPool": "cosmos-sdk/MsgFundCommunityPool",
|
||||
"/cosmos.evidence.v1beta1.MsgSubmitEvidence": "cosmos-sdk/MsgSubmitEvidence",
|
||||
"/cosmos.gov.v1beta1.MsgSubmitProposal": "cosmos-sdk/MsgSubmitProposal",
|
||||
"/cosmos.gov.v1beta1.MsgVote": "cosmos-sdk/MsgVote",
|
||||
"/cosmos.gov.v1beta1.MsgDeposit": "cosmos-sdk/MsgDeposit",
|
||||
"/cosmos.slashing.v1beta1.MsgUnjail": "cosmos-sdk/MsgUnjail",
|
||||
"/cosmos.staking.v1beta1.MsgCreateValidator": "cosmos-sdk/MsgCreateValidator",
|
||||
"/cosmos.staking.v1beta1.MsgEditValidator": "cosmos-sdk/MsgEditValidator",
|
||||
"/cosmos.staking.v1beta1.MsgDelegate": "cosmos-sdk/MsgDelegate",
|
||||
"/cosmos.staking.v1beta1.MsgBeginRedelegate": "cosmos-sdk/MsgBeginRedelegate",
|
||||
"/cosmos.staking.v1beta1.MsgUndelegate": "cosmos-sdk/MsgUndelegate",
|
||||
"/cosmos.vesting.v1beta1.MsgCreateVestingAccount": "cosmos-sdk/MsgCreateVestingAccount",
|
||||
};
|
||||
const type = typeRegister[typeUrl];
|
||||
if (!type) {
|
||||
throw new Error("Type URL not known");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@ -37,12 +37,12 @@ describe("AuthExtension", () => {
|
||||
it("works for account with pubkey and non-zero sequence", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
|
||||
const account = await client.auth.account(validator.address);
|
||||
const account = await client.auth.account(validator.delegatorAddress);
|
||||
assert(account);
|
||||
|
||||
const pubkey = encodePubkey(validator.pubkey);
|
||||
expect(account).toEqual({
|
||||
address: validator.address,
|
||||
address: validator.delegatorAddress,
|
||||
pubKey: Any.create(pubkey),
|
||||
// accountNumber not set
|
||||
sequence: Long.fromNumber(validator.sequence, true),
|
||||
@ -83,12 +83,12 @@ describe("AuthExtension", () => {
|
||||
it("works for account with pubkey and non-zero sequence", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
|
||||
const account = await client.auth.unverified.account(validator.address);
|
||||
const account = await client.auth.unverified.account(validator.delegatorAddress);
|
||||
assert(account);
|
||||
|
||||
const pubkey = encodePubkey(validator.pubkey);
|
||||
expect(account).toEqual({
|
||||
address: validator.address,
|
||||
address: validator.delegatorAddress,
|
||||
pubKey: Any.create(pubkey),
|
||||
// accountNumber not set
|
||||
sequence: Long.fromNumber(validator.sequence, true),
|
||||
|
||||
198
packages/stargate/src/signingstargateclient.spec.ts
Normal file
198
packages/stargate/src/signingstargateclient.spec.ts
Normal file
@ -0,0 +1,198 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { coin, coins, GasPrice, Secp256k1HdWallet } from "@cosmjs/launchpad";
|
||||
import { Coin, cosmosField, DirectSecp256k1Wallet, registered, Registry } from "@cosmjs/proto-signing";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import { Message } from "protobufjs";
|
||||
|
||||
import { cosmos } from "./codec";
|
||||
import { PrivateSigningStargateClient, SigningStargateClient } from "./signingstargateclient";
|
||||
import { assertIsBroadcastTxSuccess } from "./stargateclient";
|
||||
import { faucet, makeRandomAddress, pendingWithoutSimapp, simapp, validator } from "./testutils.spec";
|
||||
|
||||
describe("SigningStargateClient", () => {
|
||||
describe("constructor", () => {
|
||||
it("can be constructed with default fees", async () => {
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet);
|
||||
const openedClient = (client as unknown) as PrivateSigningStargateClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "2000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom registry", async () => {
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const registry = new Registry();
|
||||
registry.register("/custom.MsgCustom", cosmos.bank.v1beta1.MsgSend);
|
||||
const options = { registry: registry };
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningStargateClient;
|
||||
expect(openedClient.registry.lookupType("/custom.MsgCustom")).toEqual(cosmos.bank.v1beta1.MsgSend);
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price", async () => {
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const options = { gasPrice: gasPrice };
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningStargateClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "251200", // 3.14 * 80_000
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas limits", async () => {
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
};
|
||||
const options = { gasLimits: gasLimits };
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningStargateClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "4000", // 0.025 * 160_000
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "160000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price and gas limits", async () => {
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
};
|
||||
const options = { gasPrice: gasPrice, gasLimits: gasLimits };
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningStargateClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "502400", // 3.14 * 160_000
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "160000",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("sendTokens", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet);
|
||||
|
||||
const transferAmount = coins(7890, "ucosm");
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
const memo = "for dinner";
|
||||
|
||||
// no tokens here
|
||||
const before = await client.getBalance(beneficiaryAddress, "ucosm");
|
||||
expect(before).toBeNull();
|
||||
|
||||
// send
|
||||
const result = await client.sendTokens(faucet.address0, beneficiaryAddress, transferAmount, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
expect(result.rawLog).toBeTruthy();
|
||||
|
||||
// got tokens
|
||||
const after = await client.getBalance(beneficiaryAddress, "ucosm");
|
||||
assert(after);
|
||||
expect(after).toEqual(transferAmount[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("signAndBroadcast", () => {
|
||||
it("works with direct mode", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||
const registry = new Registry();
|
||||
registry.register(msgDelegateTypeUrl, cosmos.staking.v1beta1.MsgDelegate);
|
||||
const options = { registry: registry };
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet, options);
|
||||
|
||||
const msg = cosmos.staking.v1beta1.MsgDelegate.create({
|
||||
delegatorAddress: faucet.address0,
|
||||
validatorAddress: validator.validatorAddress,
|
||||
amount: coin(1234, "ustake"),
|
||||
});
|
||||
const msgAny = {
|
||||
typeUrl: msgDelegateTypeUrl,
|
||||
value: msg,
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "180000", // 180k
|
||||
};
|
||||
const memo = "Use your power wisely";
|
||||
const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
});
|
||||
|
||||
it("works with legacy Amino mode", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const coinTypeUrl = "/cosmos.base.v1beta.Coin";
|
||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||
const registry = new Registry();
|
||||
registry.register(coinTypeUrl, Coin);
|
||||
|
||||
@registered(registry, msgDelegateTypeUrl)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class MsgDelegate extends Message {
|
||||
@cosmosField.string(1)
|
||||
public readonly delegator_address?: string;
|
||||
@cosmosField.string(2)
|
||||
public readonly validator_address?: string;
|
||||
@cosmosField.message(3, Coin)
|
||||
public readonly amount?: Coin;
|
||||
}
|
||||
|
||||
const options = { registry: registry };
|
||||
const client = await SigningStargateClient.connectWithWallet(simapp.tendermintUrl, wallet, options);
|
||||
|
||||
const msg = {
|
||||
delegator_address: faucet.address0,
|
||||
validator_address: validator.validatorAddress,
|
||||
amount: coin(1234, "ustake"),
|
||||
};
|
||||
const msgAny = {
|
||||
typeUrl: msgDelegateTypeUrl,
|
||||
value: msg,
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "200000",
|
||||
};
|
||||
const memo = "Use your power wisely";
|
||||
const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
153
packages/stargate/src/signingstargateclient.ts
Normal file
153
packages/stargate/src/signingstargateclient.ts
Normal file
@ -0,0 +1,153 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
AccountData,
|
||||
buildFeeTable,
|
||||
Coin,
|
||||
CosmosFeeTable,
|
||||
encodeSecp256k1Pubkey,
|
||||
GasLimits,
|
||||
GasPrice,
|
||||
makeSignDoc as makeSignDocAmino,
|
||||
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 { getMsgType } from "./encoding";
|
||||
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> {
|
||||
const accountFromSigner = (await this.signer.getAccounts()).find(
|
||||
(account: AccountData) => 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();
|
||||
|
||||
if (isOfflineDirectSigner(this.signer)) {
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature } = await this.signer.signDirect(address, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: txBodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
signatures: [fromBase64(signature.signature)],
|
||||
});
|
||||
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
||||
return this.broadcastTx(signedTx);
|
||||
}
|
||||
|
||||
// Amino signer
|
||||
const signMode = cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_LEGACY_AMINO_JSON;
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence, signMode);
|
||||
const msgs = messages.map((msg) => ({
|
||||
type: getMsgType(msg.typeUrl),
|
||||
value: msg.value,
|
||||
}));
|
||||
const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence);
|
||||
const signResponse = await this.signer.signAmino(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);
|
||||
}
|
||||
}
|
||||
@ -4,8 +4,8 @@ import { Coin, coins } from "@cosmjs/launchpad";
|
||||
import {
|
||||
DirectSecp256k1Wallet,
|
||||
encodePubkey,
|
||||
makeAuthInfo,
|
||||
makeSignBytes,
|
||||
makeAuthInfoBytes,
|
||||
makeSignDoc,
|
||||
Registry,
|
||||
} from "@cosmjs/proto-signing";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
@ -70,11 +70,11 @@ async function sendTokens(
|
||||
},
|
||||
];
|
||||
const gasLimit = 200000;
|
||||
const authInfoBytes = makeAuthInfo([pubkey], feeAmount, gasLimit, sequence);
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkey], feeAmount, gasLimit, sequence);
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const signature = await wallet.sign(walletAddress, signDocBytes);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature } = await wallet.signDirect(walletAddress, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: txBodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
|
||||
@ -3,8 +3,8 @@ import { fromBase64, toBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
DirectSecp256k1Wallet,
|
||||
encodePubkey,
|
||||
makeAuthInfo,
|
||||
makeSignBytes,
|
||||
makeAuthInfoBytes,
|
||||
makeSignDoc,
|
||||
Registry,
|
||||
} from "@cosmjs/proto-signing";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
@ -90,10 +90,10 @@ describe("StargateClient", () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await StargateClient.connect(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.getAccount(validator.address);
|
||||
const account = await client.getAccount(validator.delegatorAddress);
|
||||
assert(account);
|
||||
expect(account).toEqual({
|
||||
address: validator.address,
|
||||
address: validator.delegatorAddress,
|
||||
pubkey: validator.pubkey,
|
||||
accountNumber: validator.accountNumber,
|
||||
sequence: validator.sequence,
|
||||
@ -292,11 +292,11 @@ describe("StargateClient", () => {
|
||||
},
|
||||
];
|
||||
const gasLimit = 200000;
|
||||
const authInfoBytes = makeAuthInfo([pubkey], feeAmount, gasLimit, sequence);
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkey], feeAmount, gasLimit, sequence);
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const signature = await wallet.sign(address, signDocBytes);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature } = await wallet.signDirect(address, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: txBodyBytes,
|
||||
authInfoBytes: authInfoBytes,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -59,7 +59,8 @@ export const validator = {
|
||||
value: "A/Ltk7FONB0PJOKrLECIxJe5LcJMy9DcWG6X2WVA2xAi",
|
||||
},
|
||||
/** delegator_address from /cosmos.staking.v1beta1.MsgCreateValidator in scripts/simapp/template/.simapp/config/genesis.json */
|
||||
address: "cosmos1gyavpqh80z2v7tcgeycfvf0st2nvjrfcp05dad",
|
||||
delegatorAddress: "cosmos1gyavpqh80z2v7tcgeycfvf0st2nvjrfcp05dad",
|
||||
validatorAddress: "cosmosvaloper1gyavpqh80z2v7tcgeycfvf0st2nvjrfcymqc37",
|
||||
accountNumber: 0,
|
||||
sequence: 1,
|
||||
};
|
||||
|
||||
3342
packages/stargate/types/codec/generated/codecimpl.d.ts
vendored
3342
packages/stargate/types/codec/generated/codecimpl.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1
packages/stargate/types/encoding.d.ts
vendored
Normal file
1
packages/stargate/types/encoding.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export declare function getMsgType(typeUrl: string): string;
|
||||
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