mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-11 14:09:15 +00:00
Merge pull request #764 from cosmos/740-cosmwasmclient-sign
Add SigningCosmWasmClient.sign
This commit is contained in:
commit
ff6fee5e0c
@ -41,7 +41,11 @@ and this project adheres to
|
|||||||
- @cosmjs/stargate: Add `SigningStargateClient.sign`, which allows you to create
|
- @cosmjs/stargate: Add `SigningStargateClient.sign`, which allows you to create
|
||||||
signed transactions without broadcasting them directly. The new type
|
signed transactions without broadcasting them directly. The new type
|
||||||
`SignerData` can be passed into `.sign` to skip querying account number,
|
`SignerData` can be passed into `.sign` to skip querying account number,
|
||||||
sequence and chain ID.
|
sequence and chain ID
|
||||||
|
- @cosmjs/cosmwasm-stargate: Add `SigningCosmWasmClient.sign`, which allows you
|
||||||
|
to create signed transactions without broadcasting them directly. The new type
|
||||||
|
`SignerData` from @cosmjs/stargate can be passed into `.sign` to skip querying
|
||||||
|
account number, sequence and chain ID.
|
||||||
- @cosmjs/stargate: Add constructor `SigningStargateClient.offline` which does
|
- @cosmjs/stargate: Add constructor `SigningStargateClient.offline` which does
|
||||||
not connect to Tendermint. This allows offline signing.
|
not connect to Tendermint. This allows offline signing.
|
||||||
- @cosmjs/stargate: Add `makeMultisignedTx` which allows you to assemble a
|
- @cosmjs/stargate: Add `makeMultisignedTx` which allows you to assemble a
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { DeepPartial, MsgSend } from "@cosmjs/stargate/build/codec/cosmos/bank/v1beta1/tx";
|
import { DeepPartial, MsgSend } from "@cosmjs/stargate/build/codec/cosmos/bank/v1beta1/tx";
|
||||||
import { Coin } from "@cosmjs/stargate/build/codec/cosmos/base/v1beta1/coin";
|
import { Coin } from "@cosmjs/stargate/build/codec/cosmos/base/v1beta1/coin";
|
||||||
import { MsgDelegate } from "@cosmjs/stargate/build/codec/cosmos/staking/v1beta1/tx";
|
import { MsgDelegate } from "@cosmjs/stargate/build/codec/cosmos/staking/v1beta1/tx";
|
||||||
import { Tx } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
|
import { AuthInfo, Tx, TxBody, TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
|
||||||
import { assert, sleep } from "@cosmjs/utils";
|
import { assert, sleep } from "@cosmjs/utils";
|
||||||
import Long from "long";
|
import Long from "long";
|
||||||
import pako from "pako";
|
import pako from "pako";
|
||||||
@ -547,11 +547,9 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
describe("direct mode", () => {
|
describe("direct mode", () => {
|
||||||
it("works", async () => {
|
it("works", async () => {
|
||||||
pendingWithoutWasmd();
|
pendingWithoutWasmd();
|
||||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||||
const registry = new Registry();
|
|
||||||
registry.register(msgDelegateTypeUrl, MsgDelegate);
|
|
||||||
const options = { prefix: wasmd.prefix, registry: registry };
|
|
||||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
const msg = MsgDelegate.fromPartial({
|
const msg = MsgDelegate.fromPartial({
|
||||||
@ -574,13 +572,9 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
|
|
||||||
it("works with a modifying signer", async () => {
|
it("works with a modifying signer", async () => {
|
||||||
pendingWithoutWasmd();
|
pendingWithoutWasmd();
|
||||||
const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, {
|
const options = { prefix: wasmd.prefix };
|
||||||
prefix: wasmd.prefix,
|
const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
});
|
|
||||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||||
const registry = new Registry();
|
|
||||||
registry.register(msgDelegateTypeUrl, MsgDelegate);
|
|
||||||
const options = { prefix: wasmd.prefix, registry: registry };
|
|
||||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
const msg = MsgDelegate.fromPartial({
|
const msg = MsgDelegate.fromPartial({
|
||||||
@ -615,8 +609,8 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
describe("legacy Amino mode", () => {
|
describe("legacy Amino mode", () => {
|
||||||
it("works with bank MsgSend", async () => {
|
it("works with bank MsgSend", async () => {
|
||||||
pendingWithoutWasmd();
|
pendingWithoutWasmd();
|
||||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
|
|
||||||
const options = { prefix: wasmd.prefix };
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
const msgSend: MsgSend = {
|
const msgSend: MsgSend = {
|
||||||
@ -639,8 +633,8 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
|
|
||||||
it("works with staking MsgDelegate", async () => {
|
it("works with staking MsgDelegate", async () => {
|
||||||
pendingWithoutWasmd();
|
pendingWithoutWasmd();
|
||||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
|
|
||||||
const options = { prefix: wasmd.prefix };
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
const msgDelegate: MsgDelegate = {
|
const msgDelegate: MsgDelegate = {
|
||||||
@ -663,8 +657,8 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
|
|
||||||
it("works with wasm MsgStoreCode", async () => {
|
it("works with wasm MsgStoreCode", async () => {
|
||||||
pendingWithoutWasmd();
|
pendingWithoutWasmd();
|
||||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
|
|
||||||
const options = { prefix: wasmd.prefix };
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
const { data, builder, source } = getHackatom();
|
const { data, builder, source } = getHackatom();
|
||||||
|
|
||||||
@ -808,10 +802,8 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
|
|
||||||
it("works with a modifying signer", async () => {
|
it("works with a modifying signer", async () => {
|
||||||
pendingWithoutWasmd();
|
pendingWithoutWasmd();
|
||||||
const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, {
|
|
||||||
prefix: wasmd.prefix,
|
|
||||||
});
|
|
||||||
const options = { prefix: wasmd.prefix };
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
const msg = {
|
const msg = {
|
||||||
@ -843,4 +835,278 @@ describe("SigningCosmWasmClient", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("sign", () => {
|
||||||
|
describe("direct mode", () => {
|
||||||
|
it("works", async () => {
|
||||||
|
pendingWithoutWasmd();
|
||||||
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
|
const msg = MsgDelegate.fromPartial({
|
||||||
|
delegatorAddress: alice.address0,
|
||||||
|
validatorAddress: validator.validatorAddress,
|
||||||
|
amount: coin(1234, "ustake"),
|
||||||
|
});
|
||||||
|
const msgAny = {
|
||||||
|
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||||
|
value: msg,
|
||||||
|
};
|
||||||
|
const fee = {
|
||||||
|
amount: coins(2000, "ucosm"),
|
||||||
|
gas: "180000", // 180k
|
||||||
|
};
|
||||||
|
const memo = "Use your power wisely";
|
||||||
|
const signed = await client.sign(alice.address0, [msgAny], fee, memo);
|
||||||
|
|
||||||
|
// ensure signature is valid
|
||||||
|
const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
|
||||||
|
assertIsBroadcastTxSuccess(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works with a modifying signer", async () => {
|
||||||
|
pendingWithoutWasmd();
|
||||||
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
|
const msg = MsgDelegate.fromPartial({
|
||||||
|
delegatorAddress: alice.address0,
|
||||||
|
validatorAddress: validator.validatorAddress,
|
||||||
|
amount: coin(1234, "ustake"),
|
||||||
|
});
|
||||||
|
const msgAny = {
|
||||||
|
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||||
|
value: msg,
|
||||||
|
};
|
||||||
|
const fee = {
|
||||||
|
amount: coins(2000, "ucosm"),
|
||||||
|
gas: "180000", // 180k
|
||||||
|
};
|
||||||
|
const memo = "Use your power wisely";
|
||||||
|
const signed = await client.sign(alice.address0, [msgAny], fee, memo);
|
||||||
|
|
||||||
|
const body = TxBody.decode(signed.bodyBytes);
|
||||||
|
const authInfo = AuthInfo.decode(signed.authInfoBytes);
|
||||||
|
// From ModifyingDirectSecp256k1HdWallet
|
||||||
|
expect(body.memo).toEqual("This was modified");
|
||||||
|
expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
|
||||||
|
expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
|
||||||
|
|
||||||
|
// ensure signature is valid
|
||||||
|
const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
|
||||||
|
assertIsBroadcastTxSuccess(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("legacy Amino mode", () => {
|
||||||
|
it("works with bank MsgSend", async () => {
|
||||||
|
pendingWithoutWasmd();
|
||||||
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
|
const msgSend: MsgSend = {
|
||||||
|
fromAddress: alice.address0,
|
||||||
|
toAddress: makeRandomAddress(),
|
||||||
|
amount: coins(1234, "ucosm"),
|
||||||
|
};
|
||||||
|
const msgAny = {
|
||||||
|
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||||
|
value: msgSend,
|
||||||
|
};
|
||||||
|
const fee = {
|
||||||
|
amount: coins(2000, "ucosm"),
|
||||||
|
gas: "200000",
|
||||||
|
};
|
||||||
|
const memo = "Use your tokens wisely";
|
||||||
|
const signed = await client.sign(alice.address0, [msgAny], fee, memo);
|
||||||
|
|
||||||
|
// ensure signature is valid
|
||||||
|
const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
|
||||||
|
assertIsBroadcastTxSuccess(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works with staking MsgDelegate", async () => {
|
||||||
|
pendingWithoutWasmd();
|
||||||
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
|
const msgDelegate: MsgDelegate = {
|
||||||
|
delegatorAddress: alice.address0,
|
||||||
|
validatorAddress: validator.validatorAddress,
|
||||||
|
amount: coin(1234, "ustake"),
|
||||||
|
};
|
||||||
|
const msgAny = {
|
||||||
|
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||||
|
value: msgDelegate,
|
||||||
|
};
|
||||||
|
const fee = {
|
||||||
|
amount: coins(2000, "ustake"),
|
||||||
|
gas: "200000",
|
||||||
|
};
|
||||||
|
const memo = "Use your tokens wisely";
|
||||||
|
const signed = await client.sign(alice.address0, [msgAny], fee, memo);
|
||||||
|
|
||||||
|
// ensure signature is valid
|
||||||
|
const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
|
||||||
|
assertIsBroadcastTxSuccess(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works with a custom registry and custom message", async () => {
|
||||||
|
pendingWithoutWasmd();
|
||||||
|
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
|
||||||
|
|
||||||
|
const customRegistry = new Registry();
|
||||||
|
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||||
|
interface CustomMsgDelegate {
|
||||||
|
customDelegatorAddress?: string;
|
||||||
|
customValidatorAddress?: string;
|
||||||
|
customAmount?: Coin;
|
||||||
|
}
|
||||||
|
const baseCustomMsgDelegate: CustomMsgDelegate = {
|
||||||
|
customDelegatorAddress: "",
|
||||||
|
customValidatorAddress: "",
|
||||||
|
};
|
||||||
|
const CustomMsgDelegate = {
|
||||||
|
// Adapted from autogenerated MsgDelegate implementation
|
||||||
|
encode(
|
||||||
|
message: CustomMsgDelegate,
|
||||||
|
writer: protobuf.Writer = protobuf.Writer.create(),
|
||||||
|
): protobuf.Writer {
|
||||||
|
writer.uint32(10).string(message.customDelegatorAddress ?? "");
|
||||||
|
writer.uint32(18).string(message.customValidatorAddress ?? "");
|
||||||
|
if (message.customAmount !== undefined && message.customAmount !== undefined) {
|
||||||
|
Coin.encode(message.customAmount, writer.uint32(26).fork()).ldelim();
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(): CustomMsgDelegate {
|
||||||
|
throw new Error("decode method should not be required");
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(): CustomMsgDelegate {
|
||||||
|
throw new Error("fromJSON method should not be required");
|
||||||
|
},
|
||||||
|
|
||||||
|
fromPartial(object: DeepPartial<CustomMsgDelegate>): CustomMsgDelegate {
|
||||||
|
const message = { ...baseCustomMsgDelegate } as CustomMsgDelegate;
|
||||||
|
if (object.customDelegatorAddress !== undefined && object.customDelegatorAddress !== null) {
|
||||||
|
message.customDelegatorAddress = object.customDelegatorAddress;
|
||||||
|
} else {
|
||||||
|
message.customDelegatorAddress = "";
|
||||||
|
}
|
||||||
|
if (object.customValidatorAddress !== undefined && object.customValidatorAddress !== null) {
|
||||||
|
message.customValidatorAddress = object.customValidatorAddress;
|
||||||
|
} else {
|
||||||
|
message.customValidatorAddress = "";
|
||||||
|
}
|
||||||
|
if (object.customAmount !== undefined && object.customAmount !== null) {
|
||||||
|
message.customAmount = Coin.fromPartial(object.customAmount);
|
||||||
|
} else {
|
||||||
|
message.customAmount = undefined;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(): unknown {
|
||||||
|
throw new Error("toJSON method should not be required");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
customRegistry.register(msgDelegateTypeUrl, CustomMsgDelegate);
|
||||||
|
const customAminoTypes = new AminoTypes({
|
||||||
|
additions: {
|
||||||
|
"/cosmos.staking.v1beta1.MsgDelegate": {
|
||||||
|
aminoType: "cosmos-sdk/MsgDelegate",
|
||||||
|
toAmino: ({
|
||||||
|
customDelegatorAddress,
|
||||||
|
customValidatorAddress,
|
||||||
|
customAmount,
|
||||||
|
}: CustomMsgDelegate): AminoMsgDelegate["value"] => {
|
||||||
|
assert(customDelegatorAddress, "missing customDelegatorAddress");
|
||||||
|
assert(customValidatorAddress, "missing validatorAddress");
|
||||||
|
assert(customAmount, "missing amount");
|
||||||
|
return {
|
||||||
|
delegator_address: customDelegatorAddress,
|
||||||
|
validator_address: customValidatorAddress,
|
||||||
|
amount: {
|
||||||
|
amount: customAmount.amount,
|
||||||
|
denom: customAmount.denom,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
fromAmino: ({
|
||||||
|
delegator_address,
|
||||||
|
validator_address,
|
||||||
|
amount,
|
||||||
|
}: AminoMsgDelegate["value"]): CustomMsgDelegate => ({
|
||||||
|
customDelegatorAddress: delegator_address,
|
||||||
|
customValidatorAddress: validator_address,
|
||||||
|
customAmount: Coin.fromPartial(amount),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const options = { registry: customRegistry, aminoTypes: customAminoTypes };
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
|
const msg: CustomMsgDelegate = {
|
||||||
|
customDelegatorAddress: alice.address0,
|
||||||
|
customValidatorAddress: validator.validatorAddress,
|
||||||
|
customAmount: coin(1234, "ustake"),
|
||||||
|
};
|
||||||
|
const msgAny = {
|
||||||
|
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||||
|
value: msg,
|
||||||
|
};
|
||||||
|
const fee = {
|
||||||
|
amount: coins(2000, "ucosm"),
|
||||||
|
gas: "200000",
|
||||||
|
};
|
||||||
|
const memo = "Use your power wisely";
|
||||||
|
const signed = await client.sign(alice.address0, [msgAny], fee, memo);
|
||||||
|
|
||||||
|
// ensure signature is valid
|
||||||
|
const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
|
||||||
|
assertIsBroadcastTxSuccess(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works with a modifying signer", async () => {
|
||||||
|
pendingWithoutWasmd();
|
||||||
|
const options = { prefix: wasmd.prefix };
|
||||||
|
const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options);
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||||
|
|
||||||
|
const msg: MsgDelegate = {
|
||||||
|
delegatorAddress: alice.address0,
|
||||||
|
validatorAddress: validator.validatorAddress,
|
||||||
|
amount: coin(1234, "ustake"),
|
||||||
|
};
|
||||||
|
const msgAny = {
|
||||||
|
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||||
|
value: msg,
|
||||||
|
};
|
||||||
|
const fee = {
|
||||||
|
amount: coins(2000, "ucosm"),
|
||||||
|
gas: "200000",
|
||||||
|
};
|
||||||
|
const memo = "Use your power wisely";
|
||||||
|
const signed = await client.sign(alice.address0, [msgAny], fee, memo);
|
||||||
|
|
||||||
|
const body = TxBody.decode(signed.bodyBytes);
|
||||||
|
const authInfo = AuthInfo.decode(signed.authInfoBytes);
|
||||||
|
// From ModifyingSecp256k1HdWallet
|
||||||
|
expect(body.memo).toEqual("This was modified");
|
||||||
|
expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
|
||||||
|
expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
|
||||||
|
|
||||||
|
// ensure signature is valid
|
||||||
|
const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
|
||||||
|
assertIsBroadcastTxSuccess(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
GasPrice,
|
GasPrice,
|
||||||
isBroadcastTxFailure,
|
isBroadcastTxFailure,
|
||||||
logs,
|
logs,
|
||||||
|
SignerData,
|
||||||
StdFee,
|
StdFee,
|
||||||
} from "@cosmjs/stargate";
|
} from "@cosmjs/stargate";
|
||||||
import { MsgWithdrawDelegatorReward } from "@cosmjs/stargate/build/codec/cosmos/distribution/v1beta1/tx";
|
import { MsgWithdrawDelegatorReward } from "@cosmjs/stargate/build/codec/cosmos/distribution/v1beta1/tx";
|
||||||
@ -43,6 +44,7 @@ import { MsgDelegate, MsgUndelegate } from "@cosmjs/stargate/build/codec/cosmos/
|
|||||||
import { SignMode } from "@cosmjs/stargate/build/codec/cosmos/tx/signing/v1beta1/signing";
|
import { SignMode } from "@cosmjs/stargate/build/codec/cosmos/tx/signing/v1beta1/signing";
|
||||||
import { TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
|
import { TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
|
||||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||||
|
import { assert } from "@cosmjs/utils";
|
||||||
import Long from "long";
|
import Long from "long";
|
||||||
import pako from "pako";
|
import pako from "pako";
|
||||||
|
|
||||||
@ -402,6 +404,44 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
|||||||
fee: StdFee,
|
fee: StdFee,
|
||||||
memo = "",
|
memo = "",
|
||||||
): Promise<BroadcastTxResponse> {
|
): Promise<BroadcastTxResponse> {
|
||||||
|
const txRaw = await this.sign(signerAddress, messages, fee, memo);
|
||||||
|
const txBytes = TxRaw.encode(txRaw).finish();
|
||||||
|
return this.broadcastTx(txBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sign(
|
||||||
|
signerAddress: string,
|
||||||
|
messages: readonly EncodeObject[],
|
||||||
|
fee: StdFee,
|
||||||
|
memo: string,
|
||||||
|
explicitSignerData?: SignerData,
|
||||||
|
): Promise<TxRaw> {
|
||||||
|
let signerData: SignerData;
|
||||||
|
if (explicitSignerData) {
|
||||||
|
signerData = explicitSignerData;
|
||||||
|
} else {
|
||||||
|
const { accountNumber, sequence } = await this.getSequence(signerAddress);
|
||||||
|
const chainId = await this.getChainId();
|
||||||
|
signerData = {
|
||||||
|
accountNumber: accountNumber,
|
||||||
|
sequence: sequence,
|
||||||
|
chainId: chainId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOfflineDirectSigner(this.signer)
|
||||||
|
? this.signDirect(signerAddress, messages, fee, memo, signerData)
|
||||||
|
: this.signAmino(signerAddress, messages, fee, memo, signerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async signAmino(
|
||||||
|
signerAddress: string,
|
||||||
|
messages: readonly EncodeObject[],
|
||||||
|
fee: StdFee,
|
||||||
|
memo: string,
|
||||||
|
{ accountNumber, sequence, chainId }: SignerData,
|
||||||
|
): Promise<TxRaw> {
|
||||||
|
assert(!isOfflineDirectSigner(this.signer));
|
||||||
const accountFromSigner = (await this.signer.getAccounts()).find(
|
const accountFromSigner = (await this.signer.getAccounts()).find(
|
||||||
(account) => account.address === signerAddress,
|
(account) => account.address === signerAddress,
|
||||||
);
|
);
|
||||||
@ -409,32 +449,6 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
|||||||
throw new Error("Failed to retrieve account from signer");
|
throw new Error("Failed to retrieve account from signer");
|
||||||
}
|
}
|
||||||
const pubkey = encodePubkey(encodeSecp256k1Pubkey(accountFromSigner.pubkey));
|
const pubkey = encodePubkey(encodeSecp256k1Pubkey(accountFromSigner.pubkey));
|
||||||
const { accountNumber, sequence } = await this.getSequence(signerAddress);
|
|
||||||
const chainId = await this.getChainId();
|
|
||||||
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([pubkey], fee.amount, gasLimit, sequence);
|
|
||||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
|
||||||
const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc);
|
|
||||||
const txRaw = TxRaw.fromPartial({
|
|
||||||
bodyBytes: signed.bodyBytes,
|
|
||||||
authInfoBytes: signed.authInfoBytes,
|
|
||||||
signatures: [fromBase64(signature.signature)],
|
|
||||||
});
|
|
||||||
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
|
||||||
return this.broadcastTx(signedTx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Amino signer
|
|
||||||
const signMode = SignMode.SIGN_MODE_LEGACY_AMINO_JSON;
|
const signMode = SignMode.SIGN_MODE_LEGACY_AMINO_JSON;
|
||||||
const msgs = messages.map((msg) => this.aminoTypes.toAmino(msg));
|
const msgs = messages.map((msg) => this.aminoTypes.toAmino(msg));
|
||||||
const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence);
|
const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence);
|
||||||
@ -456,12 +470,44 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
|||||||
signedSequence,
|
signedSequence,
|
||||||
signMode,
|
signMode,
|
||||||
);
|
);
|
||||||
const txRaw = TxRaw.fromPartial({
|
return TxRaw.fromPartial({
|
||||||
bodyBytes: signedTxBodyBytes,
|
bodyBytes: signedTxBodyBytes,
|
||||||
authInfoBytes: signedAuthInfoBytes,
|
authInfoBytes: signedAuthInfoBytes,
|
||||||
signatures: [fromBase64(signature.signature)],
|
signatures: [fromBase64(signature.signature)],
|
||||||
});
|
});
|
||||||
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
}
|
||||||
return this.broadcastTx(signedTx);
|
|
||||||
|
private async signDirect(
|
||||||
|
signerAddress: string,
|
||||||
|
messages: readonly EncodeObject[],
|
||||||
|
fee: StdFee,
|
||||||
|
memo: string,
|
||||||
|
{ accountNumber, sequence, chainId }: SignerData,
|
||||||
|
): Promise<TxRaw> {
|
||||||
|
assert(isOfflineDirectSigner(this.signer));
|
||||||
|
const accountFromSigner = (await this.signer.getAccounts()).find(
|
||||||
|
(account) => account.address === signerAddress,
|
||||||
|
);
|
||||||
|
if (!accountFromSigner) {
|
||||||
|
throw new Error("Failed to retrieve account from signer");
|
||||||
|
}
|
||||||
|
const pubkey = encodePubkey(encodeSecp256k1Pubkey(accountFromSigner.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([pubkey], fee.amount, gasLimit, sequence);
|
||||||
|
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||||
|
const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc);
|
||||||
|
return TxRaw.fromPartial({
|
||||||
|
bodyBytes: signed.bodyBytes,
|
||||||
|
authInfoBytes: signed.authInfoBytes,
|
||||||
|
signatures: [fromBase64(signature.signature)],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,8 +297,8 @@ export class SigningStargateClient extends StargateClient {
|
|||||||
memo = "",
|
memo = "",
|
||||||
): Promise<BroadcastTxResponse> {
|
): Promise<BroadcastTxResponse> {
|
||||||
const txRaw = await this.sign(signerAddress, messages, fee, memo);
|
const txRaw = await this.sign(signerAddress, messages, fee, memo);
|
||||||
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
const txBytes = TxRaw.encode(txRaw).finish();
|
||||||
return this.broadcastTx(signedTx);
|
return this.broadcastTx(txBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user