mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-10 21:49:15 +00:00
Merge pull request #615 from cosmos/594-amino-signing-cosmwasm
Implement Amino signing for bank/staking/wasm in cosmwasm-stargate
This commit is contained in:
commit
9651c5e3a5
@ -49,6 +49,14 @@
|
||||
- @cosmjs/tendermint-rpc: Remove types `BlockHash`, `TxBytes` and `TxHash`. Use
|
||||
`Uint8Array` instead.
|
||||
|
||||
### Added
|
||||
|
||||
- @cosmjs/utils: Added `assertDefinedAndNotNull`.
|
||||
|
||||
### Removed
|
||||
|
||||
- @cosmjs/utils: `assertDefined` removed in favour of `assertDefinedAndNotNull`.
|
||||
|
||||
## 0.23.2 (2021-01-06)
|
||||
|
||||
### Security
|
||||
|
337
packages/cosmwasm-stargate/src/aminotypes.spec.ts
Normal file
337
packages/cosmwasm-stargate/src/aminotypes.spec.ts
Normal file
@ -0,0 +1,337 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import {
|
||||
MsgClearAdmin,
|
||||
MsgExecuteContract,
|
||||
MsgInstantiateContract,
|
||||
MsgMigrateContract,
|
||||
MsgStoreCode,
|
||||
MsgUpdateAdmin,
|
||||
} from "@cosmjs/cosmwasm-launchpad";
|
||||
import { fromBase64, toUtf8 } from "@cosmjs/encoding";
|
||||
import { coins } from "@cosmjs/launchpad";
|
||||
import { AminoTypes } from "@cosmjs/stargate";
|
||||
import Long from "long";
|
||||
|
||||
import { cosmWasmTypes } from "./aminotypes";
|
||||
import { cosmwasm } from "./codec";
|
||||
|
||||
type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode;
|
||||
type IMsgInstantiateContract = cosmwasm.wasm.v1beta1.IMsgInstantiateContract;
|
||||
type IMsgUpdateAdmin = cosmwasm.wasm.v1beta1.IMsgUpdateAdmin;
|
||||
type IMsgClearAdmin = cosmwasm.wasm.v1beta1.IMsgClearAdmin;
|
||||
type IMsgExecuteContract = cosmwasm.wasm.v1beta1.IMsgExecuteContract;
|
||||
type IMsgMigrateContract = cosmwasm.wasm.v1beta1.IMsgMigrateContract;
|
||||
|
||||
describe("AminoTypes", () => {
|
||||
describe("toAmino", () => {
|
||||
it("works for MsgStoreCode", () => {
|
||||
const msg: IMsgStoreCode = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="),
|
||||
source: "Arrabiata",
|
||||
builder: "Bob",
|
||||
};
|
||||
const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode",
|
||||
value: msg,
|
||||
});
|
||||
const expected: MsgStoreCode = {
|
||||
type: "wasm/MsgStoreCode",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==",
|
||||
source: "Arrabiata",
|
||||
builder: "Bob",
|
||||
},
|
||||
};
|
||||
expect(aminoMsg).toEqual(expected);
|
||||
});
|
||||
|
||||
it("works for MsgInstantiateContract", () => {
|
||||
const msg: IMsgInstantiateContract = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
codeId: Long.fromString("12345"),
|
||||
label: "sticky",
|
||||
initMsg: toUtf8(
|
||||
JSON.stringify({
|
||||
foo: "bar",
|
||||
}),
|
||||
),
|
||||
initFunds: coins(1234, "ucosm"),
|
||||
admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
};
|
||||
const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgInstantiateContract",
|
||||
value: msg,
|
||||
});
|
||||
const expected: MsgInstantiateContract = {
|
||||
type: "wasm/MsgInstantiateContract",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
code_id: "12345",
|
||||
label: "sticky",
|
||||
init_msg: {
|
||||
foo: "bar",
|
||||
},
|
||||
init_funds: coins(1234, "ucosm"),
|
||||
admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
},
|
||||
};
|
||||
expect(aminoMsg).toEqual(expected);
|
||||
});
|
||||
|
||||
it("works for MsgUpdateAdmin", () => {
|
||||
const msg: IMsgUpdateAdmin = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
newAdmin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
};
|
||||
const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin",
|
||||
value: msg,
|
||||
});
|
||||
const expected: MsgUpdateAdmin = {
|
||||
type: "wasm/MsgUpdateAdmin",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
new_admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
},
|
||||
};
|
||||
expect(aminoMsg).toEqual(expected);
|
||||
});
|
||||
|
||||
it("works for MsgClearAdmin", () => {
|
||||
const msg: IMsgClearAdmin = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
};
|
||||
const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgClearAdmin",
|
||||
value: msg,
|
||||
});
|
||||
const expected: MsgClearAdmin = {
|
||||
type: "wasm/MsgClearAdmin",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
},
|
||||
};
|
||||
expect(aminoMsg).toEqual(expected);
|
||||
});
|
||||
|
||||
it("works for MsgExecuteContract", () => {
|
||||
const msg: IMsgExecuteContract = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
msg: toUtf8(
|
||||
JSON.stringify({
|
||||
foo: "bar",
|
||||
}),
|
||||
),
|
||||
sentFunds: coins(1234, "ucosm"),
|
||||
};
|
||||
const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgExecuteContract",
|
||||
value: msg,
|
||||
});
|
||||
const expected: MsgExecuteContract = {
|
||||
type: "wasm/MsgExecuteContract",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
msg: {
|
||||
foo: "bar",
|
||||
},
|
||||
sent_funds: coins(1234, "ucosm"),
|
||||
},
|
||||
};
|
||||
expect(aminoMsg).toEqual(expected);
|
||||
});
|
||||
|
||||
it("works for MsgMigrateContract", () => {
|
||||
const msg: IMsgMigrateContract = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
codeId: Long.fromString("98765"),
|
||||
migrateMsg: toUtf8(
|
||||
JSON.stringify({
|
||||
foo: "bar",
|
||||
}),
|
||||
),
|
||||
};
|
||||
const aminoMsg = new AminoTypes({ additions: cosmWasmTypes }).toAmino({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgMigrateContract",
|
||||
value: msg,
|
||||
});
|
||||
const expected: MsgMigrateContract = {
|
||||
type: "wasm/MsgMigrateContract",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
code_id: "98765",
|
||||
msg: {
|
||||
foo: "bar",
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(aminoMsg).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromAmino", () => {
|
||||
it("works for MsgStoreCode", () => {
|
||||
const aminoMsg: MsgStoreCode = {
|
||||
type: "wasm/MsgStoreCode",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==",
|
||||
source: "Arrabiata",
|
||||
builder: "Bob",
|
||||
},
|
||||
};
|
||||
const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg);
|
||||
const expectedValue: IMsgStoreCode = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="),
|
||||
source: "Arrabiata",
|
||||
builder: "Bob",
|
||||
};
|
||||
expect(msg).toEqual({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode",
|
||||
value: expectedValue,
|
||||
});
|
||||
});
|
||||
|
||||
it("works for MsgInstantiateContract", () => {
|
||||
const aminoMsg: MsgInstantiateContract = {
|
||||
type: "wasm/MsgInstantiateContract",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
code_id: "12345",
|
||||
label: "sticky",
|
||||
init_msg: {
|
||||
foo: "bar",
|
||||
},
|
||||
init_funds: coins(1234, "ucosm"),
|
||||
admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
},
|
||||
};
|
||||
const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg);
|
||||
const expectedValue: IMsgInstantiateContract = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
codeId: Long.fromString("12345"),
|
||||
label: "sticky",
|
||||
initMsg: toUtf8(
|
||||
JSON.stringify({
|
||||
foo: "bar",
|
||||
}),
|
||||
),
|
||||
initFunds: coins(1234, "ucosm"),
|
||||
admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
};
|
||||
expect(msg).toEqual({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgInstantiateContract",
|
||||
value: expectedValue,
|
||||
});
|
||||
});
|
||||
|
||||
it("works for MsgUpdateAdmin", () => {
|
||||
const aminoMsg: MsgUpdateAdmin = {
|
||||
type: "wasm/MsgUpdateAdmin",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
new_admin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
},
|
||||
};
|
||||
const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg);
|
||||
const expectedValue: IMsgUpdateAdmin = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
newAdmin: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
};
|
||||
expect(msg).toEqual({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgUpdateAdmin",
|
||||
value: expectedValue,
|
||||
});
|
||||
});
|
||||
|
||||
it("works for MsgClearAdmin", () => {
|
||||
const aminoMsg: MsgClearAdmin = {
|
||||
type: "wasm/MsgClearAdmin",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
},
|
||||
};
|
||||
const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg);
|
||||
const expectedValue: IMsgClearAdmin = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
};
|
||||
expect(msg).toEqual({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgClearAdmin",
|
||||
value: expectedValue,
|
||||
});
|
||||
});
|
||||
|
||||
it("works for MsgExecuteContract", () => {
|
||||
const aminoMsg: MsgExecuteContract = {
|
||||
type: "wasm/MsgExecuteContract",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
msg: {
|
||||
foo: "bar",
|
||||
},
|
||||
sent_funds: coins(1234, "ucosm"),
|
||||
},
|
||||
};
|
||||
const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg);
|
||||
const expectedValue: IMsgExecuteContract = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
msg: toUtf8(
|
||||
JSON.stringify({
|
||||
foo: "bar",
|
||||
}),
|
||||
),
|
||||
sentFunds: coins(1234, "ucosm"),
|
||||
};
|
||||
expect(msg).toEqual({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgExecuteContract",
|
||||
value: expectedValue,
|
||||
});
|
||||
});
|
||||
|
||||
it("works for MsgMigrateContract", () => {
|
||||
const aminoMsg: MsgMigrateContract = {
|
||||
type: "wasm/MsgMigrateContract",
|
||||
value: {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
code_id: "98765",
|
||||
msg: {
|
||||
foo: "bar",
|
||||
},
|
||||
},
|
||||
};
|
||||
const msg = new AminoTypes({ additions: cosmWasmTypes }).fromAmino(aminoMsg);
|
||||
const expectedValue: IMsgMigrateContract = {
|
||||
sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
|
||||
contract: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
|
||||
codeId: Long.fromString("98765"),
|
||||
migrateMsg: toUtf8(
|
||||
JSON.stringify({
|
||||
foo: "bar",
|
||||
}),
|
||||
),
|
||||
};
|
||||
expect(msg).toEqual({
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgMigrateContract",
|
||||
value: expectedValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
161
packages/cosmwasm-stargate/src/aminotypes.ts
Normal file
161
packages/cosmwasm-stargate/src/aminotypes.ts
Normal file
@ -0,0 +1,161 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import {
|
||||
MsgClearAdmin,
|
||||
MsgExecuteContract,
|
||||
MsgInstantiateContract,
|
||||
MsgMigrateContract,
|
||||
MsgStoreCode,
|
||||
MsgUpdateAdmin,
|
||||
} from "@cosmjs/cosmwasm-launchpad";
|
||||
import { fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding";
|
||||
import { AminoConverter, coinFromProto } from "@cosmjs/stargate";
|
||||
import { assertDefinedAndNotNull } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
|
||||
import { cosmwasm } from "./codec";
|
||||
|
||||
type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode;
|
||||
type IMsgInstantiateContract = cosmwasm.wasm.v1beta1.IMsgInstantiateContract;
|
||||
type IMsgUpdateAdmin = cosmwasm.wasm.v1beta1.IMsgUpdateAdmin;
|
||||
type IMsgClearAdmin = cosmwasm.wasm.v1beta1.IMsgClearAdmin;
|
||||
type IMsgExecuteContract = cosmwasm.wasm.v1beta1.IMsgExecuteContract;
|
||||
type IMsgMigrateContract = cosmwasm.wasm.v1beta1.IMsgMigrateContract;
|
||||
|
||||
export const cosmWasmTypes: Record<string, AminoConverter> = {
|
||||
"/cosmwasm.wasm.v1beta1.MsgStoreCode": {
|
||||
aminoType: "wasm/MsgStoreCode",
|
||||
toAmino: ({ sender, wasmByteCode, source, builder }: IMsgStoreCode): MsgStoreCode["value"] => {
|
||||
assertDefinedAndNotNull(sender, "missing sender");
|
||||
assertDefinedAndNotNull(wasmByteCode, "missing wasmByteCode");
|
||||
assertDefinedAndNotNull(source, "missing source");
|
||||
assertDefinedAndNotNull(builder, "missing builder");
|
||||
return {
|
||||
sender: sender,
|
||||
wasm_byte_code: toBase64(wasmByteCode),
|
||||
source: source,
|
||||
builder: builder,
|
||||
};
|
||||
},
|
||||
fromAmino: ({ sender, wasm_byte_code, source, builder }: MsgStoreCode["value"]): IMsgStoreCode => ({
|
||||
sender: sender,
|
||||
wasmByteCode: fromBase64(wasm_byte_code),
|
||||
source: source,
|
||||
builder: builder,
|
||||
}),
|
||||
},
|
||||
"/cosmwasm.wasm.v1beta1.MsgInstantiateContract": {
|
||||
aminoType: "wasm/MsgInstantiateContract",
|
||||
toAmino: ({
|
||||
sender,
|
||||
codeId,
|
||||
label,
|
||||
initMsg,
|
||||
initFunds,
|
||||
admin,
|
||||
}: IMsgInstantiateContract): MsgInstantiateContract["value"] => {
|
||||
assertDefinedAndNotNull(sender, "missing sender");
|
||||
assertDefinedAndNotNull(codeId, "missing codeId");
|
||||
assertDefinedAndNotNull(label, "missing label");
|
||||
assertDefinedAndNotNull(initMsg, "missing initMsg");
|
||||
assertDefinedAndNotNull(initFunds, "missing initFunds");
|
||||
return {
|
||||
sender: sender,
|
||||
code_id: codeId.toString(),
|
||||
label: label,
|
||||
init_msg: JSON.parse(fromUtf8(initMsg)),
|
||||
init_funds: initFunds.map(coinFromProto),
|
||||
admin: admin ?? undefined,
|
||||
};
|
||||
},
|
||||
fromAmino: ({
|
||||
sender,
|
||||
code_id,
|
||||
label,
|
||||
init_msg,
|
||||
init_funds,
|
||||
admin,
|
||||
}: MsgInstantiateContract["value"]): IMsgInstantiateContract => ({
|
||||
sender: sender,
|
||||
codeId: Long.fromString(code_id),
|
||||
label: label,
|
||||
initMsg: toUtf8(JSON.stringify(init_msg)),
|
||||
initFunds: [...init_funds],
|
||||
admin: admin,
|
||||
}),
|
||||
},
|
||||
"/cosmwasm.wasm.v1beta1.MsgUpdateAdmin": {
|
||||
aminoType: "wasm/MsgUpdateAdmin",
|
||||
toAmino: ({ sender, newAdmin, contract }: IMsgUpdateAdmin): MsgUpdateAdmin["value"] => {
|
||||
assertDefinedAndNotNull(sender, "missing sender");
|
||||
assertDefinedAndNotNull(newAdmin, "missing newAdmin");
|
||||
assertDefinedAndNotNull(contract, "missing contract");
|
||||
return {
|
||||
sender: sender,
|
||||
new_admin: newAdmin,
|
||||
contract: contract,
|
||||
};
|
||||
},
|
||||
fromAmino: ({ sender, new_admin, contract }: MsgUpdateAdmin["value"]): IMsgUpdateAdmin => ({
|
||||
sender: sender,
|
||||
newAdmin: new_admin,
|
||||
contract: contract,
|
||||
}),
|
||||
},
|
||||
"/cosmwasm.wasm.v1beta1.MsgClearAdmin": {
|
||||
aminoType: "wasm/MsgClearAdmin",
|
||||
toAmino: ({ sender, contract }: IMsgClearAdmin): MsgClearAdmin["value"] => {
|
||||
assertDefinedAndNotNull(sender, "missing sender");
|
||||
assertDefinedAndNotNull(contract, "missing contract");
|
||||
return {
|
||||
sender: sender,
|
||||
contract: contract,
|
||||
};
|
||||
},
|
||||
fromAmino: ({ sender, contract }: MsgClearAdmin["value"]): IMsgClearAdmin => ({
|
||||
sender: sender,
|
||||
contract: contract,
|
||||
}),
|
||||
},
|
||||
"/cosmwasm.wasm.v1beta1.MsgExecuteContract": {
|
||||
aminoType: "wasm/MsgExecuteContract",
|
||||
toAmino: ({ sender, contract, msg, sentFunds }: IMsgExecuteContract): MsgExecuteContract["value"] => {
|
||||
assertDefinedAndNotNull(sender, "missing sender");
|
||||
assertDefinedAndNotNull(contract, "missing contract");
|
||||
assertDefinedAndNotNull(msg, "missing msg");
|
||||
assertDefinedAndNotNull(sentFunds, "missing sentFunds");
|
||||
return {
|
||||
sender: sender,
|
||||
contract: contract,
|
||||
msg: JSON.parse(fromUtf8(msg)),
|
||||
sent_funds: sentFunds.map(coinFromProto),
|
||||
};
|
||||
},
|
||||
fromAmino: ({ sender, contract, msg, sent_funds }: MsgExecuteContract["value"]): IMsgExecuteContract => ({
|
||||
sender: sender,
|
||||
contract: contract,
|
||||
msg: toUtf8(JSON.stringify(msg)),
|
||||
sentFunds: [...sent_funds],
|
||||
}),
|
||||
},
|
||||
"/cosmwasm.wasm.v1beta1.MsgMigrateContract": {
|
||||
aminoType: "wasm/MsgMigrateContract",
|
||||
toAmino: ({ sender, contract, codeId, migrateMsg }: IMsgMigrateContract): MsgMigrateContract["value"] => {
|
||||
assertDefinedAndNotNull(sender, "missing sender");
|
||||
assertDefinedAndNotNull(contract, "missing contract");
|
||||
assertDefinedAndNotNull(codeId, "missing codeId");
|
||||
assertDefinedAndNotNull(migrateMsg, "missing migrateMsg");
|
||||
return {
|
||||
sender: sender,
|
||||
contract: contract,
|
||||
code_id: codeId.toString(),
|
||||
msg: JSON.parse(fromUtf8(migrateMsg)),
|
||||
};
|
||||
},
|
||||
fromAmino: ({ sender, contract, code_id, msg }: MsgMigrateContract["value"]): IMsgMigrateContract => ({
|
||||
sender: sender,
|
||||
contract: contract,
|
||||
codeId: Long.fromString(code_id),
|
||||
migrateMsg: toUtf8(JSON.stringify(msg)),
|
||||
}),
|
||||
},
|
||||
};
|
@ -9,7 +9,7 @@ import {
|
||||
parseRawLog,
|
||||
SigningStargateClient,
|
||||
} from "@cosmjs/stargate";
|
||||
import { assert, assertDefined } from "@cosmjs/utils";
|
||||
import { assert, assertDefinedAndNotNull } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
|
||||
import { cosmwasm } from "../codec";
|
||||
@ -395,7 +395,7 @@ describe("WasmExtension", () => {
|
||||
expect(codeId).toBeGreaterThanOrEqual(1);
|
||||
expect(codeId).toBeLessThanOrEqual(200);
|
||||
|
||||
assertDefined(result.data);
|
||||
assertDefinedAndNotNull(result.data);
|
||||
const msgData = fromOneElementArray(result.data);
|
||||
expect(msgData.msgType).toEqual("store-code");
|
||||
expect(MsgStoreCodeResponse.decode(msgData.data!)).toEqual(
|
||||
@ -415,7 +415,7 @@ describe("WasmExtension", () => {
|
||||
const amountAttr = logs.findAttribute(parsedLogs, "transfer", "amount");
|
||||
expect(amountAttr.value).toEqual("1234ucosm,321ustake");
|
||||
|
||||
assertDefined(result.data);
|
||||
assertDefinedAndNotNull(result.data);
|
||||
const msgData = fromOneElementArray(result.data);
|
||||
expect(msgData.msgType).toEqual("instantiate");
|
||||
expect(MsgInstantiateContractResponse.decode(msgData.data!)).toEqual(
|
||||
@ -441,7 +441,7 @@ describe("WasmExtension", () => {
|
||||
value: beneficiaryAddress,
|
||||
});
|
||||
|
||||
assertDefined(result.data);
|
||||
assertDefinedAndNotNull(result.data);
|
||||
const msgData = fromOneElementArray(result.data);
|
||||
expect(msgData.msgType).toEqual("execute");
|
||||
expect(MsgExecuteContractResponse.decode(msgData.data!)).toEqual(
|
||||
|
@ -2,12 +2,21 @@
|
||||
import { UploadMeta } from "@cosmjs/cosmwasm-launchpad";
|
||||
import { sha256 } from "@cosmjs/crypto";
|
||||
import { toHex } from "@cosmjs/encoding";
|
||||
import { coin, coins, GasPrice } from "@cosmjs/launchpad";
|
||||
import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing";
|
||||
import { assertIsBroadcastTxSuccess, codec } from "@cosmjs/stargate";
|
||||
import {
|
||||
coin,
|
||||
coins,
|
||||
GasPrice,
|
||||
MsgDelegate as LaunchpadMsgDelegate,
|
||||
Secp256k1HdWallet,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { Coin, cosmosField, DirectSecp256k1HdWallet, registered, Registry } from "@cosmjs/proto-signing";
|
||||
import { AminoTypes, assertIsBroadcastTxSuccess, codec } from "@cosmjs/stargate";
|
||||
import { assert, sleep } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
import pako from "pako";
|
||||
import { Message } from "protobufjs";
|
||||
|
||||
import { cosmwasm } from "./codec";
|
||||
import { PrivateSigningCosmWasmClient, SigningCosmWasmClient } from "./signingcosmwasmclient";
|
||||
import {
|
||||
alice,
|
||||
@ -15,12 +24,18 @@ import {
|
||||
makeRandomAddress,
|
||||
makeWasmClient,
|
||||
ModifyingDirectSecp256k1HdWallet,
|
||||
ModifyingSecp256k1HdWallet,
|
||||
pendingWithoutWasmd,
|
||||
unused,
|
||||
validator,
|
||||
wasmd,
|
||||
} from "./testutils.spec";
|
||||
|
||||
type IMsgSend = codec.cosmos.bank.v1beta1.IMsgSend;
|
||||
type IMsgDelegate = codec.cosmos.staking.v1beta1.IMsgDelegate;
|
||||
type IMsgStoreCode = cosmwasm.wasm.v1beta1.IMsgStoreCode;
|
||||
|
||||
const { MsgSend } = codec.cosmos.bank.v1beta1;
|
||||
const { MsgDelegate } = codec.cosmos.staking.v1beta1;
|
||||
const { Tx } = codec.cosmos.tx.v1beta1;
|
||||
|
||||
@ -29,15 +44,30 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can be constructed", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
expect(client).toBeTruthy();
|
||||
});
|
||||
|
||||
it("can be constructed with custom registry", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic);
|
||||
const registry = new Registry();
|
||||
registry.register("/custom.MsgCustom", MsgSend);
|
||||
const options = { prefix: wasmd.prefix, registry: registry };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.registry.lookupType("/custom.MsgCustom")).toEqual(MsgSend);
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, { gasPrice });
|
||||
const options = {
|
||||
prefix: wasmd.prefix,
|
||||
gasPrice: GasPrice.fromString("3.14utest"),
|
||||
};
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
upload: {
|
||||
@ -70,10 +100,13 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can be constructed with custom gas limits", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
const options = {
|
||||
prefix: wasmd.prefix,
|
||||
gasLimits: {
|
||||
send: 160000,
|
||||
},
|
||||
};
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, { gasLimits });
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
upload: {
|
||||
@ -106,14 +139,14 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can be constructed with custom gas price and gas limits", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
const options = {
|
||||
prefix: wasmd.prefix,
|
||||
gasPrice: GasPrice.fromString("3.14utest"),
|
||||
gasLimits: {
|
||||
send: 160000,
|
||||
},
|
||||
};
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, {
|
||||
gasPrice,
|
||||
gasLimits,
|
||||
});
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
upload: {
|
||||
@ -148,7 +181,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const wasm = getHackatom().data;
|
||||
const {
|
||||
codeId,
|
||||
@ -167,7 +201,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can set builder and source", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const hackatom = getHackatom();
|
||||
const meta: UploadMeta = {
|
||||
source: "https://crates.io/api/v1/crates/cw-nameservice/0.1.0/download",
|
||||
@ -184,7 +219,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("works with transfer amount", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId } = await client.upload(alice.address0, getHackatom().data);
|
||||
const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")];
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
@ -211,7 +247,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("works with admin", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId } = await client.upload(alice.address0, getHackatom().data);
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
const { contractAddress } = await client.instantiate(
|
||||
@ -233,7 +270,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can instantiate one code multiple times", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId } = await client.upload(alice.address0, getHackatom().data);
|
||||
const contractAddress1 = await client.instantiate(
|
||||
alice.address0,
|
||||
@ -261,7 +299,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can update an admin", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId } = await client.upload(alice.address0, getHackatom().data);
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
const { contractAddress } = await client.instantiate(
|
||||
@ -296,7 +335,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can clear an admin", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId } = await client.upload(alice.address0, getHackatom().data);
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
const { contractAddress } = await client.instantiate(
|
||||
@ -331,7 +371,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("can can migrate from one code ID to another", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId: codeId1 } = await client.upload(alice.address0, getHackatom().data);
|
||||
const { codeId: codeId2 } = await client.upload(alice.address0, getHackatom().data);
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
@ -371,7 +412,8 @@ describe("SigningCosmWasmClient", () => {
|
||||
it("works", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { codeId } = await client.upload(alice.address0, getHackatom().data);
|
||||
// instantiate
|
||||
const transferAmount = [coin(233444, "ucosm"), coin(5454, "ustake")];
|
||||
@ -411,10 +453,36 @@ describe("SigningCosmWasmClient", () => {
|
||||
});
|
||||
|
||||
describe("sendTokens", () => {
|
||||
it("works", async () => {
|
||||
it("works with direct signer", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
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(alice.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]);
|
||||
});
|
||||
|
||||
it("works with legacy Amino signer", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const transferAmount = coins(7890, "ucosm");
|
||||
const beneficiaryAddress = makeRandomAddress();
|
||||
@ -444,7 +512,7 @@ describe("SigningCosmWasmClient", () => {
|
||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||
const registry = new Registry();
|
||||
registry.register(msgDelegateTypeUrl, MsgDelegate);
|
||||
const options = { registry: registry };
|
||||
const options = { prefix: wasmd.prefix, registry: registry };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const msg = MsgDelegate.create({
|
||||
@ -475,7 +543,7 @@ describe("SigningCosmWasmClient", () => {
|
||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||
const registry = new Registry();
|
||||
registry.register(msgDelegateTypeUrl, MsgDelegate);
|
||||
const options = { registry: registry };
|
||||
const options = { prefix: wasmd.prefix, registry: registry };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const msg = MsgDelegate.create({
|
||||
@ -506,5 +574,193 @@ describe("SigningCosmWasmClient", () => {
|
||||
expect(tx.authInfo!.fee!.gasLimit!.toNumber()).toEqual(333333);
|
||||
});
|
||||
});
|
||||
|
||||
describe("legacy Amino mode", () => {
|
||||
// NOTE: One custom registry shared between tests
|
||||
// See https://github.com/protobufjs/protobuf.js#using-decorators
|
||||
// > Decorated types reside in protobuf.roots["decorated"] using a flat structure, so no duplicate names.
|
||||
const customRegistry = new Registry();
|
||||
const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
|
||||
|
||||
@registered(customRegistry, msgDelegateTypeUrl)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class CustomMsgDelegate extends Message {
|
||||
@cosmosField.string(1)
|
||||
public readonly custom_delegator_address?: string;
|
||||
@cosmosField.string(2)
|
||||
public readonly custom_validator_address?: string;
|
||||
@cosmosField.message(3, Coin)
|
||||
public readonly custom_amount?: Coin;
|
||||
}
|
||||
|
||||
it("works with bank MsgSend", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const msgSend: IMsgSend = {
|
||||
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 result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
});
|
||||
|
||||
it("works with staking MsgDelegate", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const msgDelegate: IMsgDelegate = {
|
||||
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 result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
});
|
||||
|
||||
it("works with wasm MsgStoreCode", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
const { data, builder, source } = getHackatom();
|
||||
|
||||
const msgStoreCode: IMsgStoreCode = {
|
||||
sender: alice.address0,
|
||||
wasmByteCode: pako.gzip(data),
|
||||
source: source,
|
||||
builder: builder,
|
||||
};
|
||||
const msgAny = {
|
||||
typeUrl: "/cosmwasm.wasm.v1beta1.MsgStoreCode",
|
||||
value: msgStoreCode,
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ustake"),
|
||||
gas: "1500000",
|
||||
};
|
||||
const memo = "Use your tokens wisely";
|
||||
const result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
});
|
||||
|
||||
it("works with a custom registry and custom message", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, wasmd.prefix);
|
||||
const customAminoTypes = new AminoTypes({
|
||||
prefix: wasmd.prefix,
|
||||
additions: {
|
||||
"/cosmos.staking.v1beta1.MsgDelegate": {
|
||||
aminoType: "cosmos-sdk/MsgDelegate",
|
||||
toAmino: ({
|
||||
custom_delegator_address,
|
||||
custom_validator_address,
|
||||
custom_amount,
|
||||
}: CustomMsgDelegate): LaunchpadMsgDelegate["value"] => {
|
||||
assert(custom_delegator_address, "missing custom_delegator_address");
|
||||
assert(custom_validator_address, "missing validator_address");
|
||||
assert(custom_amount, "missing amount");
|
||||
assert(custom_amount.amount, "missing amount.amount");
|
||||
assert(custom_amount.denom, "missing amount.denom");
|
||||
return {
|
||||
delegator_address: custom_delegator_address,
|
||||
validator_address: custom_validator_address,
|
||||
amount: {
|
||||
amount: custom_amount.amount,
|
||||
denom: custom_amount.denom,
|
||||
},
|
||||
};
|
||||
},
|
||||
fromAmino: ({
|
||||
delegator_address,
|
||||
validator_address,
|
||||
amount,
|
||||
}: LaunchpadMsgDelegate["value"]): CustomMsgDelegate =>
|
||||
CustomMsgDelegate.create({
|
||||
custom_delegator_address: delegator_address,
|
||||
custom_validator_address: validator_address,
|
||||
custom_amount: Coin.create(amount),
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
const options = { prefix: wasmd.prefix, registry: customRegistry, aminoTypes: customAminoTypes };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const msg = {
|
||||
custom_delegator_address: alice.address0,
|
||||
custom_validator_address: validator.validatorAddress,
|
||||
custom_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 result = await client.signAndBroadcast(alice.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
});
|
||||
|
||||
it("works with a modifying signer", async () => {
|
||||
pendingWithoutWasmd();
|
||||
const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm");
|
||||
const options = { prefix: wasmd.prefix };
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
|
||||
|
||||
const msg = {
|
||||
delegatorAddress: alice.address0,
|
||||
validatorAddress: 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(alice.address0, [msgAny], fee, memo);
|
||||
assertIsBroadcastTxSuccess(result);
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
const searchResult = await client.getTx(result.transactionHash);
|
||||
assert(searchResult, "Must find transaction");
|
||||
const tx = Tx.decode(searchResult.tx);
|
||||
// From ModifyingSecp256k1HdWallet
|
||||
expect(tx.body!.memo).toEqual("This was modified");
|
||||
expect({ ...tx.authInfo!.fee!.amount![0] }).toEqual(coin(3000, "ucosm"));
|
||||
expect(tx.authInfo!.fee!.gasLimit!.toNumber()).toEqual(333333);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -21,18 +21,21 @@ import {
|
||||
GasLimits,
|
||||
GasPrice,
|
||||
logs,
|
||||
makeSignDoc as makeSignDocAmino,
|
||||
StdFee,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { Int53, Uint53 } from "@cosmjs/math";
|
||||
import {
|
||||
EncodeObject,
|
||||
encodePubkey,
|
||||
isOfflineDirectSigner,
|
||||
makeAuthInfoBytes,
|
||||
makeSignDoc,
|
||||
OfflineDirectSigner,
|
||||
OfflineSigner,
|
||||
Registry,
|
||||
} from "@cosmjs/proto-signing";
|
||||
import {
|
||||
AminoTypes,
|
||||
BroadcastTxFailure,
|
||||
BroadcastTxResponse,
|
||||
codec,
|
||||
@ -43,10 +46,20 @@ import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
|
||||
import Long from "long";
|
||||
import pako from "pako";
|
||||
|
||||
import { cosmWasmTypes } from "./aminotypes";
|
||||
import { cosmwasm } from "./codec";
|
||||
import { CosmWasmClient } from "./cosmwasmclient";
|
||||
|
||||
const { SignMode } = codec.cosmos.tx.signing.v1beta1;
|
||||
const { TxRaw } = codec.cosmos.tx.v1beta1;
|
||||
const { MsgMultiSend } = codec.cosmos.bank.v1beta1;
|
||||
const {
|
||||
MsgBeginRedelegate,
|
||||
MsgCreateValidator,
|
||||
MsgDelegate,
|
||||
MsgEditValidator,
|
||||
MsgUndelegate,
|
||||
} = codec.cosmos.staking.v1beta1;
|
||||
const {
|
||||
MsgClearAdmin,
|
||||
MsgExecuteContract,
|
||||
@ -81,6 +94,12 @@ function createBroadcastTxErrorMessage(result: BroadcastTxFailure): string {
|
||||
|
||||
function createDefaultRegistry(): Registry {
|
||||
return new Registry([
|
||||
["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend],
|
||||
["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate],
|
||||
["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator],
|
||||
["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate],
|
||||
["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator],
|
||||
["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate],
|
||||
["/cosmwasm.wasm.v1beta1.MsgClearAdmin", MsgClearAdmin],
|
||||
["/cosmwasm.wasm.v1beta1.MsgExecuteContract", MsgExecuteContract],
|
||||
["/cosmwasm.wasm.v1beta1.MsgMigrateContract", MsgMigrateContract],
|
||||
@ -92,6 +111,8 @@ function createDefaultRegistry(): Registry {
|
||||
|
||||
export interface SigningCosmWasmClientOptions {
|
||||
readonly registry?: Registry;
|
||||
readonly aminoTypes?: AminoTypes;
|
||||
readonly prefix?: string;
|
||||
readonly gasPrice?: GasPrice;
|
||||
readonly gasLimits?: GasLimits<CosmosFeeTable>;
|
||||
}
|
||||
@ -99,16 +120,18 @@ export interface SigningCosmWasmClientOptions {
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningCosmWasmClient {
|
||||
readonly fees: CosmWasmFeeTable;
|
||||
readonly registry: Registry;
|
||||
}
|
||||
|
||||
export class SigningCosmWasmClient extends CosmWasmClient {
|
||||
private readonly fees: CosmosFeeTable;
|
||||
private readonly registry: Registry;
|
||||
private readonly signer: OfflineDirectSigner;
|
||||
private readonly signer: OfflineSigner;
|
||||
private readonly aminoTypes: AminoTypes;
|
||||
|
||||
public static async connectWithSigner(
|
||||
endpoint: string,
|
||||
signer: OfflineDirectSigner,
|
||||
signer: OfflineSigner,
|
||||
options: SigningCosmWasmClientOptions = {},
|
||||
): Promise<SigningCosmWasmClient> {
|
||||
const tmClient = await TendermintClient.connect(endpoint, adaptor34);
|
||||
@ -117,17 +140,19 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
||||
|
||||
private constructor(
|
||||
tmClient: TendermintClient,
|
||||
signer: OfflineDirectSigner,
|
||||
signer: OfflineSigner,
|
||||
options: SigningCosmWasmClientOptions,
|
||||
) {
|
||||
super(tmClient);
|
||||
const {
|
||||
registry = createDefaultRegistry(),
|
||||
aminoTypes = new AminoTypes({ additions: cosmWasmTypes, prefix: options.prefix }),
|
||||
gasPrice = defaultGasPrice,
|
||||
gasLimits = defaultGasLimits,
|
||||
} = options;
|
||||
this.fees = buildFeeTable<CosmosFeeTable>(gasPrice, defaultGasLimits, gasLimits);
|
||||
this.registry = registry;
|
||||
this.aminoTypes = aminoTypes;
|
||||
this.signer = signer;
|
||||
}
|
||||
|
||||
@ -355,12 +380,44 @@ export class SigningCosmWasmClient extends CosmWasmClient {
|
||||
});
|
||||
const gasLimit = Int53.fromString(fee.gas).toNumber();
|
||||
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc);
|
||||
if (isOfflineDirectSigner(this.signer)) {
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
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 msgs = messages.map((msg) => this.aminoTypes.toAmino(msg));
|
||||
const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence);
|
||||
const { signature, signed } = await this.signer.signAmino(signerAddress, signDoc);
|
||||
const signedTxBody = {
|
||||
messages: signed.msgs.map((msg) => this.aminoTypes.fromAmino(msg)),
|
||||
memo: signed.memo,
|
||||
};
|
||||
const signedTxBodyBytes = this.registry.encode({
|
||||
typeUrl: "/cosmos.tx.v1beta1.TxBody",
|
||||
value: signedTxBody,
|
||||
});
|
||||
const signedGasLimit = Int53.fromString(signed.fee.gas).toNumber();
|
||||
const signedSequence = Int53.fromString(signed.sequence).toNumber();
|
||||
const signedAuthInfoBytes = makeAuthInfoBytes(
|
||||
[pubkeyAny],
|
||||
signed.fee.amount,
|
||||
signedGasLimit,
|
||||
signedSequence,
|
||||
signMode,
|
||||
);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: signed.bodyBytes,
|
||||
authInfoBytes: signed.authInfoBytes,
|
||||
bodyBytes: signedTxBodyBytes,
|
||||
authInfoBytes: signedAuthInfoBytes,
|
||||
signatures: [fromBase64(signature.signature)],
|
||||
});
|
||||
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
|
||||
|
@ -1,7 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Bip39, EnglishMnemonic, Random, Secp256k1, Slip10, Slip10Curve } from "@cosmjs/crypto";
|
||||
import { Bech32, fromBase64 } from "@cosmjs/encoding";
|
||||
import { coins, makeCosmoshubPath } from "@cosmjs/launchpad";
|
||||
import {
|
||||
AminoSignResponse,
|
||||
coins,
|
||||
makeCosmoshubPath,
|
||||
Secp256k1HdWallet,
|
||||
StdSignDoc,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { DirectSecp256k1HdWallet, DirectSignResponse, makeAuthInfoBytes } from "@cosmjs/proto-signing";
|
||||
import {
|
||||
AuthExtension,
|
||||
@ -205,6 +211,41 @@ export async function makeWasmClient(
|
||||
return QueryClient.withExtensions(tmClient, setupAuthExtension, setupBankExtension, setupWasmExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for testing clients using an Amino signer which modifies the transaction it receives before signing
|
||||
*/
|
||||
export class ModifyingSecp256k1HdWallet extends Secp256k1HdWallet {
|
||||
public static async fromMnemonic(
|
||||
mnemonic: string,
|
||||
hdPath = makeCosmoshubPath(0),
|
||||
prefix = "cosmos",
|
||||
): Promise<ModifyingSecp256k1HdWallet> {
|
||||
const mnemonicChecked = new EnglishMnemonic(mnemonic);
|
||||
const seed = await Bip39.mnemonicToSeed(mnemonicChecked);
|
||||
const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath);
|
||||
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
|
||||
return new ModifyingSecp256k1HdWallet(
|
||||
mnemonicChecked,
|
||||
hdPath,
|
||||
privkey,
|
||||
Secp256k1.compressPubkey(uncompressed),
|
||||
prefix,
|
||||
);
|
||||
}
|
||||
|
||||
public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse> {
|
||||
const modifiedSignDoc = {
|
||||
...signDoc,
|
||||
fee: {
|
||||
amount: coins(3000, "ucosm"),
|
||||
gas: "333333",
|
||||
},
|
||||
memo: "This was modified",
|
||||
};
|
||||
return super.signAmino(signerAddress, modifiedSignDoc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for testing clients using a direct signer which modifies the transaction it receives before signing
|
||||
*/
|
||||
|
2
packages/cosmwasm-stargate/types/aminotypes.d.ts
vendored
Normal file
2
packages/cosmwasm-stargate/types/aminotypes.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { AminoConverter } from "@cosmjs/stargate";
|
||||
export declare const cosmWasmTypes: Record<string, AminoConverter>;
|
@ -9,25 +9,29 @@ import {
|
||||
UploadResult,
|
||||
} from "@cosmjs/cosmwasm-launchpad";
|
||||
import { Coin, CosmosFeeTable, GasLimits, GasPrice, StdFee } from "@cosmjs/launchpad";
|
||||
import { EncodeObject, OfflineDirectSigner, Registry } from "@cosmjs/proto-signing";
|
||||
import { BroadcastTxResponse } from "@cosmjs/stargate";
|
||||
import { EncodeObject, OfflineSigner, Registry } from "@cosmjs/proto-signing";
|
||||
import { AminoTypes, BroadcastTxResponse } from "@cosmjs/stargate";
|
||||
import { CosmWasmClient } from "./cosmwasmclient";
|
||||
export interface SigningCosmWasmClientOptions {
|
||||
readonly registry?: Registry;
|
||||
readonly aminoTypes?: AminoTypes;
|
||||
readonly prefix?: string;
|
||||
readonly gasPrice?: GasPrice;
|
||||
readonly gasLimits?: GasLimits<CosmosFeeTable>;
|
||||
}
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningCosmWasmClient {
|
||||
readonly fees: CosmWasmFeeTable;
|
||||
readonly registry: Registry;
|
||||
}
|
||||
export declare class SigningCosmWasmClient extends CosmWasmClient {
|
||||
private readonly fees;
|
||||
private readonly registry;
|
||||
private readonly signer;
|
||||
private readonly aminoTypes;
|
||||
static connectWithSigner(
|
||||
endpoint: string,
|
||||
signer: OfflineDirectSigner,
|
||||
signer: OfflineSigner,
|
||||
options?: SigningCosmWasmClientOptions,
|
||||
): Promise<SigningCosmWasmClient>;
|
||||
private constructor();
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { fromBase64, toBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
Coin,
|
||||
decodeBech32Pubkey,
|
||||
encodeBech32Pubkey,
|
||||
Msg,
|
||||
@ -14,11 +13,11 @@ import {
|
||||
MsgUndelegate,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { EncodeObject } from "@cosmjs/proto-signing";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import { assertDefinedAndNotNull } from "@cosmjs/utils";
|
||||
|
||||
import { cosmos } from "./codec";
|
||||
import { coinFromProto } from "./stargateclient";
|
||||
|
||||
type ICoin = cosmos.base.v1beta1.ICoin;
|
||||
type IMsgSend = cosmos.bank.v1beta1.IMsgSend;
|
||||
type IMsgMultiSend = cosmos.bank.v1beta1.IMsgMultiSend;
|
||||
type IMsgBeginRedelegate = cosmos.staking.v1beta1.IMsgBeginRedelegate;
|
||||
@ -33,29 +32,18 @@ export interface AminoConverter {
|
||||
readonly fromAmino: (value: any) => any;
|
||||
}
|
||||
|
||||
function checkAmount(amount: readonly ICoin[] | undefined | null): readonly Coin[] {
|
||||
assert(amount, "missing amount");
|
||||
return amount.map((a) => {
|
||||
assert(a.amount, "missing amount");
|
||||
assert(a.denom, "missing denom");
|
||||
return {
|
||||
amount: a.amount,
|
||||
denom: a.denom,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
return {
|
||||
"/cosmos.bank.v1beta1.MsgSend": {
|
||||
aminoType: "cosmos-sdk/MsgSend",
|
||||
toAmino: ({ fromAddress, toAddress, amount }: IMsgSend): MsgSend["value"] => {
|
||||
assert(fromAddress, "missing fromAddress");
|
||||
assert(toAddress, "missing toAddress");
|
||||
assertDefinedAndNotNull(fromAddress, "missing fromAddress");
|
||||
assertDefinedAndNotNull(toAddress, "missing toAddress");
|
||||
assertDefinedAndNotNull(amount, "missing amount");
|
||||
return {
|
||||
from_address: fromAddress,
|
||||
to_address: toAddress,
|
||||
amount: checkAmount(amount),
|
||||
amount: amount.map(coinFromProto),
|
||||
};
|
||||
},
|
||||
fromAmino: ({ from_address, to_address, amount }: MsgSend["value"]): IMsgSend => ({
|
||||
@ -67,21 +55,23 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
"/cosmos.bank.v1beta1.MsgMultiSend": {
|
||||
aminoType: "cosmos-sdk/MsgMultiSend",
|
||||
toAmino: ({ inputs, outputs }: IMsgMultiSend): MsgMultiSend["value"] => {
|
||||
assert(inputs, "missing inputs");
|
||||
assert(outputs, "missing outputs");
|
||||
assertDefinedAndNotNull(inputs, "missing inputs");
|
||||
assertDefinedAndNotNull(outputs, "missing outputs");
|
||||
return {
|
||||
inputs: inputs.map((input) => {
|
||||
assert(input.address, "missing input.address");
|
||||
assertDefinedAndNotNull(input.address, "missing input.address");
|
||||
assertDefinedAndNotNull(input.coins, "missing input.amount");
|
||||
return {
|
||||
address: input.address,
|
||||
coins: checkAmount(input.coins),
|
||||
coins: input.coins.map(coinFromProto),
|
||||
};
|
||||
}),
|
||||
outputs: outputs.map((output) => {
|
||||
assert(output.address, "missing output.address");
|
||||
assertDefinedAndNotNull(output.address, "missing output.address");
|
||||
assertDefinedAndNotNull(output.coins, "missing output.coins");
|
||||
return {
|
||||
address: output.address,
|
||||
coins: checkAmount(output.coins),
|
||||
coins: output.coins.map(coinFromProto),
|
||||
};
|
||||
}),
|
||||
};
|
||||
@ -105,20 +95,15 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
validatorDstAddress,
|
||||
amount,
|
||||
}: IMsgBeginRedelegate): MsgBeginRedelegate["value"] => {
|
||||
assert(delegatorAddress, "missing delegatorAddress");
|
||||
assert(validatorSrcAddress, "missing validatorSrcAddress");
|
||||
assert(validatorDstAddress, "missing validatorDstAddress");
|
||||
assert(amount, "missing amount");
|
||||
assert(amount.amount, "missing amount.amount");
|
||||
assert(amount.denom, "missing amount.denom");
|
||||
assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress");
|
||||
assertDefinedAndNotNull(validatorSrcAddress, "missing validatorSrcAddress");
|
||||
assertDefinedAndNotNull(validatorDstAddress, "missing validatorDstAddress");
|
||||
assertDefinedAndNotNull(amount, "missing amount");
|
||||
return {
|
||||
delegator_address: delegatorAddress,
|
||||
validator_src_address: validatorSrcAddress,
|
||||
validator_dst_address: validatorDstAddress,
|
||||
amount: {
|
||||
amount: amount.amount,
|
||||
denom: amount.denom,
|
||||
},
|
||||
amount: coinFromProto(amount),
|
||||
};
|
||||
},
|
||||
fromAmino: ({
|
||||
@ -144,24 +129,22 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
pubkey,
|
||||
value,
|
||||
}: IMsgCreateValidator): MsgCreateValidator["value"] => {
|
||||
assert(description, "missing description");
|
||||
assert(description.moniker, "missing description.moniker");
|
||||
assert(description.identity, "missing description.identity");
|
||||
assert(description.website, "missing description.website");
|
||||
assert(description.securityContact, "missing description.securityContact");
|
||||
assert(description.details, "missing description.details");
|
||||
assert(commission, "missing commission");
|
||||
assert(commission.rate, "missing commission.rate");
|
||||
assert(commission.maxRate, "missing commission.maxRate");
|
||||
assert(commission.maxChangeRate, "missing commission.maxChangeRate");
|
||||
assert(minSelfDelegation, "missing minSelfDelegation");
|
||||
assert(delegatorAddress, "missing delegatorAddress");
|
||||
assert(validatorAddress, "missing validatorAddress");
|
||||
assert(pubkey, "missing pubkey");
|
||||
assert(pubkey.value, "missing pubkey.value");
|
||||
assert(value, "missing value");
|
||||
assert(value.amount, "missing value.amount");
|
||||
assert(value.denom, "missing value.denom");
|
||||
assertDefinedAndNotNull(description, "missing description");
|
||||
assertDefinedAndNotNull(description.moniker, "missing description.moniker");
|
||||
assertDefinedAndNotNull(description.identity, "missing description.identity");
|
||||
assertDefinedAndNotNull(description.website, "missing description.website");
|
||||
assertDefinedAndNotNull(description.securityContact, "missing description.securityContact");
|
||||
assertDefinedAndNotNull(description.details, "missing description.details");
|
||||
assertDefinedAndNotNull(commission, "missing commission");
|
||||
assertDefinedAndNotNull(commission.rate, "missing commission.rate");
|
||||
assertDefinedAndNotNull(commission.maxRate, "missing commission.maxRate");
|
||||
assertDefinedAndNotNull(commission.maxChangeRate, "missing commission.maxChangeRate");
|
||||
assertDefinedAndNotNull(minSelfDelegation, "missing minSelfDelegation");
|
||||
assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress");
|
||||
assertDefinedAndNotNull(validatorAddress, "missing validatorAddress");
|
||||
assertDefinedAndNotNull(pubkey, "missing pubkey");
|
||||
assertDefinedAndNotNull(pubkey.value, "missing pubkey.value");
|
||||
assertDefinedAndNotNull(value, "missing value");
|
||||
return {
|
||||
description: {
|
||||
moniker: description.moniker,
|
||||
@ -185,10 +168,7 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
},
|
||||
prefix,
|
||||
),
|
||||
value: {
|
||||
amount: value.amount,
|
||||
denom: value.denom,
|
||||
},
|
||||
value: coinFromProto(value),
|
||||
};
|
||||
},
|
||||
fromAmino: ({
|
||||
@ -231,18 +211,13 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
"/cosmos.staking.v1beta1.MsgDelegate": {
|
||||
aminoType: "cosmos-sdk/MsgDelegate",
|
||||
toAmino: ({ delegatorAddress, validatorAddress, amount }: IMsgDelegate): MsgDelegate["value"] => {
|
||||
assert(delegatorAddress, "missing delegatorAddress");
|
||||
assert(validatorAddress, "missing validatorAddress");
|
||||
assert(amount, "missing amount");
|
||||
assert(amount.amount, "missing amount.amount");
|
||||
assert(amount.denom, "missing amount.denom");
|
||||
assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress");
|
||||
assertDefinedAndNotNull(validatorAddress, "missing validatorAddress");
|
||||
assertDefinedAndNotNull(amount, "missing amount");
|
||||
return {
|
||||
delegator_address: delegatorAddress,
|
||||
validator_address: validatorAddress,
|
||||
amount: {
|
||||
amount: amount.amount,
|
||||
denom: amount.denom,
|
||||
},
|
||||
amount: coinFromProto(amount),
|
||||
};
|
||||
},
|
||||
fromAmino: ({ delegator_address, validator_address, amount }: MsgDelegate["value"]): IMsgDelegate => ({
|
||||
@ -259,15 +234,15 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
minSelfDelegation,
|
||||
validatorAddress,
|
||||
}: IMsgEditValidator): MsgEditValidator["value"] => {
|
||||
assert(description, "missing description");
|
||||
assert(description.moniker, "missing description.moniker");
|
||||
assert(description.identity, "missing description.identity");
|
||||
assert(description.website, "missing description.website");
|
||||
assert(description.securityContact, "missing description.securityContact");
|
||||
assert(description.details, "missing description.details");
|
||||
assert(commissionRate, "missing commissionRate");
|
||||
assert(minSelfDelegation, "missing minSelfDelegation");
|
||||
assert(validatorAddress, "missing validatorAddress");
|
||||
assertDefinedAndNotNull(description, "missing description");
|
||||
assertDefinedAndNotNull(description.moniker, "missing description.moniker");
|
||||
assertDefinedAndNotNull(description.identity, "missing description.identity");
|
||||
assertDefinedAndNotNull(description.website, "missing description.website");
|
||||
assertDefinedAndNotNull(description.securityContact, "missing description.securityContact");
|
||||
assertDefinedAndNotNull(description.details, "missing description.details");
|
||||
assertDefinedAndNotNull(commissionRate, "missing commissionRate");
|
||||
assertDefinedAndNotNull(minSelfDelegation, "missing minSelfDelegation");
|
||||
assertDefinedAndNotNull(validatorAddress, "missing validatorAddress");
|
||||
return {
|
||||
description: {
|
||||
moniker: description.moniker,
|
||||
@ -302,18 +277,13 @@ function createDefaultTypes(prefix: string): Record<string, AminoConverter> {
|
||||
"/cosmos.staking.v1beta1.MsgUndelegate": {
|
||||
aminoType: "cosmos-sdk/MsgUndelegate",
|
||||
toAmino: ({ delegatorAddress, validatorAddress, amount }: IMsgUndelegate): MsgUndelegate["value"] => {
|
||||
assert(delegatorAddress, "missing delegatorAddress");
|
||||
assert(validatorAddress, "missing validatorAddress");
|
||||
assert(amount, "missing amount");
|
||||
assert(amount.amount, "missing amount.amount");
|
||||
assert(amount.denom, "missing amount.denom");
|
||||
assertDefinedAndNotNull(delegatorAddress, "missing delegatorAddress");
|
||||
assertDefinedAndNotNull(validatorAddress, "missing validatorAddress");
|
||||
assertDefinedAndNotNull(amount, "missing amount");
|
||||
return {
|
||||
delegator_address: delegatorAddress,
|
||||
validator_address: validatorAddress,
|
||||
amount: {
|
||||
amount: amount.amount,
|
||||
denom: amount.denom,
|
||||
},
|
||||
amount: coinFromProto(amount),
|
||||
};
|
||||
},
|
||||
fromAmino: ({
|
||||
|
@ -1,5 +1,5 @@
|
||||
export * as codec from "./codec";
|
||||
export { AminoTypes } from "./aminotypes";
|
||||
export { AminoConverter, AminoTypes } from "./aminotypes";
|
||||
export { parseRawLog } from "./logs";
|
||||
export {
|
||||
AuthExtension,
|
||||
|
@ -3,7 +3,7 @@ import { iavlSpec, ics23, tendermintSpec, verifyExistence, verifyNonExistence }
|
||||
import { toAscii, toHex } from "@cosmjs/encoding";
|
||||
import { firstEvent } from "@cosmjs/stream";
|
||||
import { Client as TendermintClient, Header, NewBlockHeaderEvent, ProofOp } from "@cosmjs/tendermint-rpc";
|
||||
import { arrayContentEquals, assert, assertDefined, isNonNullObject, sleep } from "@cosmjs/utils";
|
||||
import { arrayContentEquals, assert, assertDefinedAndNotNull, isNonNullObject, sleep } from "@cosmjs/utils";
|
||||
import { Stream } from "xstream";
|
||||
|
||||
type QueryExtensionSetup<P> = (base: QueryClient) => P;
|
||||
@ -233,7 +233,7 @@ export class QueryClient {
|
||||
// this must return the header for height+1
|
||||
// throws an error if height is 0 or undefined
|
||||
private async getNextHeader(height?: number): Promise<Header> {
|
||||
assertDefined(height);
|
||||
assertDefinedAndNotNull(height);
|
||||
if (height === 0) {
|
||||
throw new Error("Query returned height 0, cannot prove it");
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ import {
|
||||
validator,
|
||||
} from "./testutils.spec";
|
||||
|
||||
type IMsgSend = cosmos.bank.v1beta1.IMsgSend;
|
||||
type IMsgDelegate = cosmos.staking.v1beta1.IMsgDelegate;
|
||||
|
||||
const { MsgSend } = cosmos.bank.v1beta1;
|
||||
const { MsgDelegate } = cosmos.staking.v1beta1;
|
||||
const { Tx } = cosmos.tx.v1beta1;
|
||||
@ -126,7 +129,7 @@ describe("SigningStargateClient", () => {
|
||||
});
|
||||
|
||||
describe("sendTokens", () => {
|
||||
it("works", async () => {
|
||||
it("works with direct signer", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
|
||||
@ -149,6 +152,30 @@ describe("SigningStargateClient", () => {
|
||||
assert(after);
|
||||
expect(after).toEqual(transferAmount[0]);
|
||||
});
|
||||
|
||||
it("works with legacy Amino signer", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(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", () => {
|
||||
@ -235,7 +262,7 @@ describe("SigningStargateClient", () => {
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
|
||||
|
||||
const msgSend = {
|
||||
const msgSend: IMsgSend = {
|
||||
fromAddress: faucet.address0,
|
||||
toAddress: makeRandomAddress(),
|
||||
amount: coins(1234, "ucosm"),
|
||||
@ -258,7 +285,7 @@ describe("SigningStargateClient", () => {
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
|
||||
|
||||
const msgDelegate = {
|
||||
const msgDelegate: IMsgDelegate = {
|
||||
delegatorAddress: faucet.address0,
|
||||
validatorAddress: validator.validatorAddress,
|
||||
amount: coin(1234, "ustake"),
|
||||
|
@ -40,6 +40,17 @@ const { TxRaw } = cosmos.tx.v1beta1;
|
||||
const defaultGasPrice = GasPrice.fromString("0.025ucosm");
|
||||
const defaultGasLimits: GasLimits<CosmosFeeTable> = { send: 80000 };
|
||||
|
||||
function createDefaultRegistry(): Registry {
|
||||
return new Registry([
|
||||
["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend],
|
||||
["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate],
|
||||
["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator],
|
||||
["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate],
|
||||
["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator],
|
||||
["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate],
|
||||
]);
|
||||
}
|
||||
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningStargateClient {
|
||||
readonly fees: CosmosFeeTable;
|
||||
@ -49,6 +60,7 @@ export interface PrivateSigningStargateClient {
|
||||
export interface SigningStargateClientOptions {
|
||||
readonly registry?: Registry;
|
||||
readonly aminoTypes?: AminoTypes;
|
||||
readonly prefix?: string;
|
||||
readonly gasPrice?: GasPrice;
|
||||
readonly gasLimits?: GasLimits<CosmosFeeTable>;
|
||||
}
|
||||
@ -57,7 +69,7 @@ export class SigningStargateClient extends StargateClient {
|
||||
private readonly fees: CosmosFeeTable;
|
||||
private readonly registry: Registry;
|
||||
private readonly signer: OfflineSigner;
|
||||
private readonly aminoTypes;
|
||||
private readonly aminoTypes: AminoTypes;
|
||||
|
||||
public static async connectWithSigner(
|
||||
endpoint: string,
|
||||
@ -75,15 +87,8 @@ export class SigningStargateClient extends StargateClient {
|
||||
) {
|
||||
super(tmClient);
|
||||
const {
|
||||
registry = new Registry([
|
||||
["/cosmos.bank.v1beta1.MsgMultiSend", MsgMultiSend],
|
||||
["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate],
|
||||
["/cosmos.staking.v1beta1.MsgCreateValidator", MsgCreateValidator],
|
||||
["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate],
|
||||
["/cosmos.staking.v1beta1.MsgEditValidator", MsgEditValidator],
|
||||
["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate],
|
||||
]),
|
||||
aminoTypes = new AminoTypes(),
|
||||
registry = createDefaultRegistry(),
|
||||
aminoTypes = new AminoTypes({ prefix: options.prefix }),
|
||||
gasPrice = defaultGasPrice,
|
||||
gasLimits = defaultGasLimits,
|
||||
} = options;
|
||||
@ -111,19 +116,19 @@ export class SigningStargateClient extends StargateClient {
|
||||
}
|
||||
|
||||
public async signAndBroadcast(
|
||||
address: string,
|
||||
signerAddress: string,
|
||||
messages: readonly EncodeObject[],
|
||||
fee: StdFee,
|
||||
memo = "",
|
||||
): Promise<BroadcastTxResponse> {
|
||||
const accountFromSigner = (await this.signer.getAccounts()).find(
|
||||
(account: AccountData) => account.address === address,
|
||||
(account: AccountData) => account.address === signerAddress,
|
||||
);
|
||||
if (!accountFromSigner) {
|
||||
throw new Error("Failed to retrieve account from signer");
|
||||
}
|
||||
const pubkey = encodeSecp256k1Pubkey(accountFromSigner.pubkey);
|
||||
const accountFromChain = await this.getAccount(address);
|
||||
const accountFromChain = await this.getAccount(signerAddress);
|
||||
if (!accountFromChain) {
|
||||
throw new Error("Account not found");
|
||||
}
|
||||
@ -146,7 +151,7 @@ export class SigningStargateClient extends StargateClient {
|
||||
if (isOfflineDirectSigner(this.signer)) {
|
||||
const authInfoBytes = makeAuthInfoBytes([pubkeyAny], fee.amount, gasLimit, sequence);
|
||||
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
|
||||
const { signature, signed } = await this.signer.signDirect(address, signDoc);
|
||||
const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc);
|
||||
const txRaw = TxRaw.create({
|
||||
bodyBytes: signed.bodyBytes,
|
||||
authInfoBytes: signed.authInfoBytes,
|
||||
@ -160,7 +165,7 @@ export class SigningStargateClient extends StargateClient {
|
||||
const signMode = cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_LEGACY_AMINO_JSON;
|
||||
const msgs = messages.map((msg) => this.aminoTypes.toAmino(msg));
|
||||
const signDoc = makeSignDocAmino(msgs, fee, chainId, memo, accountNumber, sequence);
|
||||
const { signature, signed } = await this.signer.signAmino(address, signDoc);
|
||||
const { signature, signed } = await this.signer.signAmino(signerAddress, signDoc);
|
||||
const signedTxBody = {
|
||||
messages: signed.msgs.map((msg) => this.aminoTypes.fromAmino(msg)),
|
||||
memo: signed.memo,
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
Client as TendermintClient,
|
||||
DateTime,
|
||||
} from "@cosmjs/tendermint-rpc";
|
||||
import { assert, assertDefined } from "@cosmjs/utils";
|
||||
import { assert, assertDefinedAndNotNull } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
|
||||
import { cosmos } from "./codec";
|
||||
@ -110,10 +110,8 @@ export function accountFromProto(input: IBaseAccount): Account {
|
||||
}
|
||||
|
||||
export function coinFromProto(input: ICoin): Coin {
|
||||
assertDefined(input.amount);
|
||||
assertDefined(input.denom);
|
||||
assert(input.amount !== null);
|
||||
assert(input.denom !== null);
|
||||
assertDefinedAndNotNull(input.amount);
|
||||
assertDefinedAndNotNull(input.denom);
|
||||
return {
|
||||
amount: input.amount,
|
||||
denom: input.denom,
|
||||
|
2
packages/stargate/types/index.d.ts
vendored
2
packages/stargate/types/index.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
export * as codec from "./codec";
|
||||
export { AminoTypes } from "./aminotypes";
|
||||
export { AminoConverter, AminoTypes } from "./aminotypes";
|
||||
export { parseRawLog } from "./logs";
|
||||
export {
|
||||
AuthExtension,
|
||||
|
@ -10,6 +10,7 @@ export interface PrivateSigningStargateClient {
|
||||
export interface SigningStargateClientOptions {
|
||||
readonly registry?: Registry;
|
||||
readonly aminoTypes?: AminoTypes;
|
||||
readonly prefix?: string;
|
||||
readonly gasPrice?: GasPrice;
|
||||
readonly gasLimits?: GasLimits<CosmosFeeTable>;
|
||||
}
|
||||
@ -31,7 +32,7 @@ export declare class SigningStargateClient extends StargateClient {
|
||||
memo?: string,
|
||||
): Promise<BroadcastTxResponse>;
|
||||
signAndBroadcast(
|
||||
address: string,
|
||||
signerAddress: string,
|
||||
messages: readonly EncodeObject[],
|
||||
fee: StdFee,
|
||||
memo?: string,
|
||||
|
@ -1,52 +1,54 @@
|
||||
import { assertDefined } from "./assert";
|
||||
import { assertDefinedAndNotNull } from "./assert";
|
||||
|
||||
describe("assert", () => {
|
||||
describe("assertDefined", () => {
|
||||
describe("assertDefinedAndNotNull", () => {
|
||||
it("passes for simple values", () => {
|
||||
{
|
||||
const value: number | undefined = 123;
|
||||
assertDefined(value);
|
||||
const value: number | undefined | null = 123;
|
||||
assertDefinedAndNotNull(value);
|
||||
expect(value).toEqual(123);
|
||||
}
|
||||
{
|
||||
const value: string | undefined = "abc";
|
||||
assertDefined(value);
|
||||
const value: string | undefined | null = "abc";
|
||||
assertDefinedAndNotNull(value);
|
||||
expect(value).toEqual("abc");
|
||||
}
|
||||
});
|
||||
|
||||
it("passes for falsy values", () => {
|
||||
{
|
||||
const value: number | undefined = 0;
|
||||
assertDefined(value);
|
||||
const value: number | undefined | null = 0;
|
||||
assertDefinedAndNotNull(value);
|
||||
expect(value).toEqual(0);
|
||||
}
|
||||
{
|
||||
const value: string | undefined = "";
|
||||
assertDefined(value);
|
||||
const value: string | undefined | null = "";
|
||||
assertDefinedAndNotNull(value);
|
||||
expect(value).toEqual("");
|
||||
}
|
||||
{
|
||||
const value: null | undefined = null;
|
||||
assertDefined(value);
|
||||
expect(value).toEqual(null);
|
||||
}
|
||||
});
|
||||
|
||||
it("throws for undefined values", () => {
|
||||
{
|
||||
const value: number | undefined = undefined;
|
||||
expect(() => assertDefined(value)).toThrowError("value is undefined");
|
||||
const value: number | undefined | null = undefined;
|
||||
expect(() => assertDefinedAndNotNull(value)).toThrowError("value is undefined or null");
|
||||
}
|
||||
{
|
||||
let value: string | undefined;
|
||||
expect(() => assertDefined(value)).toThrowError("value is undefined");
|
||||
let value: string | undefined | null;
|
||||
expect(() => assertDefinedAndNotNull(value)).toThrowError("value is undefined or null");
|
||||
}
|
||||
});
|
||||
|
||||
it("throws for null values", () => {
|
||||
const value: number | undefined | null = null;
|
||||
expect(() => assertDefinedAndNotNull(value)).toThrowError("value is undefined or null");
|
||||
});
|
||||
|
||||
it("throws with custom message", () => {
|
||||
const value: number | undefined = undefined;
|
||||
expect(() => assertDefined(value, "Bug in the data source")).toThrowError("Bug in the data source");
|
||||
expect(() => assertDefinedAndNotNull(value, "Bug in the data source")).toThrowError(
|
||||
"Bug in the data source",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,8 +5,8 @@ export function assert(condition: any, msg?: string): asserts condition {
|
||||
}
|
||||
}
|
||||
|
||||
export function assertDefined<T>(value: T | undefined, msg?: string): asserts value is T {
|
||||
if (value === undefined) {
|
||||
throw new Error(msg || "value is undefined");
|
||||
export function assertDefinedAndNotNull<T>(value: T | undefined | null, msg?: string): asserts value is T {
|
||||
if (value === undefined || value === null) {
|
||||
throw new Error(msg ?? "value is undefined or null");
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export { arrayContentEquals } from "./arrays";
|
||||
export { assert, assertDefined } from "./assert";
|
||||
export { assert, assertDefinedAndNotNull } from "./assert";
|
||||
export { sleep } from "./sleep";
|
||||
export { isNonNullObject, isUint8Array } from "./typechecks";
|
||||
|
5
packages/utils/types/assert.d.ts
vendored
5
packages/utils/types/assert.d.ts
vendored
@ -1,2 +1,5 @@
|
||||
export declare function assert(condition: any, msg?: string): asserts condition;
|
||||
export declare function assertDefined<T>(value: T | undefined, msg?: string): asserts value is T;
|
||||
export declare function assertDefinedAndNotNull<T>(
|
||||
value: T | undefined | null,
|
||||
msg?: string,
|
||||
): asserts value is T;
|
||||
|
2
packages/utils/types/index.d.ts
vendored
2
packages/utils/types/index.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
export { arrayContentEquals } from "./arrays";
|
||||
export { assert, assertDefined } from "./assert";
|
||||
export { assert, assertDefinedAndNotNull } from "./assert";
|
||||
export { sleep } from "./sleep";
|
||||
export { isNonNullObject, isUint8Array } from "./typechecks";
|
||||
|
Loading…
x
Reference in New Issue
Block a user