Merge pull request #284 from CosmWasm/265-wallet-interface-secp256k1-pen

Define Wallet interface and implement for Secp256k1Wallet
This commit is contained in:
Will Clark 2020-07-14 15:12:46 +02:00 committed by GitHub
commit 33a721ab6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 454 additions and 434 deletions

View File

@ -1,5 +1,7 @@
const pen = await Secp256k1Pen.fromMnemonic("enlist hip relief stomach skate base shallow young switch frequent cry park");
const senderAddress = pen.address("cosmos");
const wallet = await Secp256k1Wallet.fromMnemonic(
"enlist hip relief stomach skate base shallow young switch frequent cry park",
);
const [{ address: senderAddress }] = await wallet.getAccounts();
const client = new CosmosClient("http://localhost:1317");
@ -11,8 +13,8 @@ const msg: MsgDelegate = {
// curl http://localhost:1317/staking/validators | jq '.result[0].operator_address'
validator_address: "cosmosvaloper1gjvanqxc774u6ed9thj4gpn9gj5zus5u32enqn",
amount: coin(300000, "ustake"),
}
}
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "120000", // 120k
@ -26,7 +28,7 @@ const { accountNumber, sequence } = await client.getNonce(senderAddress);
console.log("Account/sequence:", accountNumber, sequence);
const signBytes = makeSignBytes([msg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(senderAddress, signBytes);
const signedTx: StdTx = {
msg: [msg],
fee: fee,

View File

@ -1,9 +1,9 @@
const mnemonic = "economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone";
const mnemonic =
"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone";
for (let i of [0, 1, 2, 3, 4]) {
const pen = await Secp256k1Pen.fromMnemonic(mnemonic, makeCosmoshubPath(i));
const pubkey = toBase64(pen.pubkey);
const address = pubkeyToAddress(encodeSecp256k1Pubkey(pen.pubkey), "cosmos");
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic, makeCosmoshubPath(i), "cosmos");
const [{ address, pubkey }] = await wallet.getAccounts();
console.info(`Address ${i}: ${address}`);
console.info(`Pubkey ${i}: ${pubkey}`);
}

View File

@ -1,7 +1,6 @@
const mnemonic = Bip39.encode(Random.getBytes(16)).toString();
const pen = await Secp256k1Pen.fromMnemonic(mnemonic);
const pubkey = encodeSecp256k1Pubkey(pen.pubkey);
const address = pubkeyToAddress(pubkey, "cosmos");
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic);
const [{ address, pubkey }] = await wallet.getAccounts();
console.info("mnemonic:", mnemonic);
console.info("pubkey:", pubkey);

View File

@ -4,7 +4,7 @@ interface Options {
feeToken: string;
gasPrice: number;
bech32prefix: string;
};
}
const defaultOptions: Options = {
httpUrl: "https://lcd.demo-09.cosmwasm.com",
@ -12,7 +12,7 @@ const defaultOptions: Options = {
feeToken: "ucosm",
gasPrice: 0.025,
bech32prefix: "cosmos",
}
};
const defaultFaucetUrl = "https://faucet.demo-09.cosmwasm.com/credit";
@ -22,7 +22,7 @@ const buildFeeTable = (feeToken: string, gasPrice: number): FeeTable => {
return {
amount: [{ amount: amount.toString(), denom: denom }],
gas: gas.toString(),
}
};
};
return {
@ -32,7 +32,7 @@ const buildFeeTable = (feeToken: string, gasPrice: number): FeeTable => {
exec: stdFee(200000, feeToken, gasPrice),
send: stdFee(80000, feeToken, gasPrice),
changeAdmin: stdFee(80000, feeToken, gasPrice),
}
};
};
// TODO: hit faucet
@ -43,22 +43,20 @@ const buildFeeTable = (feeToken: string, gasPrice: number): FeeTable => {
// }
// }
const penAddress = (pen: Secp256k1Pen, prefix: string): string => {
const pubkey = encodeSecp256k1Pubkey(pen.pubkey);
return pubkeyToAddress(pubkey, prefix);
}
const connect = async (mnemonic: string, opts: Partial<Options>): Promise<{
client: SigningCosmWasmClient,
address: string,
const connect = async (
mnemonic: string,
opts: Partial<Options>,
): Promise<{
client: SigningCosmWasmClient;
address: string;
}> => {
const options: Options = {...defaultOptions, ...opts};
const options: Options = { ...defaultOptions, ...opts };
const feeTable = buildFeeTable(options.feeToken, options.gasPrice);
const pen = await Secp256k1Pen.fromMnemonic(mnemonic);
const address = penAddress(pen, options.bech32prefix);
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic);
const [{ address }] = await wallet.getAccounts();
const client = new SigningCosmWasmClient(options.httpUrl, address, signBytes => pen.sign(signBytes), feeTable);
return {client, address};
const client = new SigningCosmWasmClient(options.httpUrl, address, wallet, feeTable);
return { client, address };
};
// smartQuery assumes the content is proper JSON data and parses before returning it
@ -80,32 +78,32 @@ const loadOrCreateMnemonic = (filename: string): string => {
fs.writeFileSync(filename, mnemonic, "utf8");
return mnemonic;
}
}
};
const hitFaucet = async (faucetUrl: string, address: string, ticker: string): Promise<void> => {
const r = await axios.post(defaultFaucetUrl, { ticker, address });
console.log(r.status);
console.log(r.data);
}
};
const randomAddress = async (prefix: string): Promise<string> => {
const mnemonic = Bip39.encode(Random.getBytes(16)).toString();
return mnemonicToAddress(prefix, mnemonic);
}
};
const mnemonicToAddress = async (prefix: string, mnemonic: string): Promise<string> => {
const pen = await Secp256k1Pen.fromMnemonic(mnemonic);
const pubkey = encodeSecp256k1Pubkey(pen.pubkey);
return pubkeyToAddress(pubkey, prefix);
}
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic);
const [{ address }] = await wallet.getAccounts();
return address;
};
const downloadWasm = async (url: string): Promise<Uint8Array> => {
const r = await axios.get(url, { responseType: "arraybuffer"});
const r = await axios.get(url, { responseType: "arraybuffer" });
if (r.status !== 200) {
throw new Error(`Download error: ${r.status}`);
}
return r.data;
}
};
const getAttibute = (logs: readonly logs.Log[], key: string): string|undefined =>
logs[0].events[0].attributes.find(x => x.key == key)?.value
const getAttibute = (logs: readonly logs.Log[], key: string): string | undefined =>
logs[0].events[0].attributes.find((x) => x.key == key)?.value;

View File

@ -14,5 +14,5 @@ const faucetMnemonic =
"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone";
const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6";
const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(faucetMnemonic);
const client = new LcdClient(defaultHttpUrl);

View File

@ -97,10 +97,10 @@ export function main(originalArgs: readonly string[]): void {
"MsgDelegate",
"MsgSend",
"LcdClient",
"Pen",
"OfflineSigner",
"PubKey",
"pubkeyToAddress",
"Secp256k1Pen",
"Secp256k1Wallet",
"SigningCosmosClient",
"StdFee",
"StdTx",
@ -145,11 +145,10 @@ export function main(originalArgs: readonly string[]): void {
assert(Decimal.fromAtomics("12870000", 6).toString() === "12.87");
const mnemonic = Bip39.encode(Random.getBytes(16)).toString();
const pen = await Secp256k1Pen.fromMnemonic(mnemonic, makeCosmoshubPath(0));
const pubkey = encodeSecp256k1Pubkey(pen.pubkey);
const address = pubkeyToAddress(pubkey, "cosmos");
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic, makeCosmoshubPath(0));
const [{ address }] = await wallet.getAccounts();
const data = toAscii("foo bar");
const signature = await pen.sign(data);
const signature = await wallet.sign(address, data);
console.info("Done testing, will exit now.");
process.exit(0);

View File

@ -7,7 +7,7 @@ import {
LcdClient,
makeSignBytes,
MsgSend,
Secp256k1Pen,
Secp256k1Wallet,
} from "@cosmjs/sdk38";
import { assert, sleep } from "@cosmjs/utils";
@ -48,10 +48,8 @@ describe("CosmWasmClient.searchTx", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(wasmd.endpoint, alice.address0, (signBytes) =>
pen.sign(signBytes),
);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(wasmd.endpoint, alice.address0, wallet);
{
const recipient = makeRandomAddress();
@ -107,7 +105,7 @@ describe("CosmWasmClient.searchTx", () => {
const { accountNumber, sequence } = await client.getNonce();
const chainId = await client.getChainId();
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(alice.address0, signBytes);
const tx: CosmosSdkTx = {
type: "cosmos-sdk/StdTx",
value: {

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/camelcase */
import { Sha256 } from "@cosmjs/crypto";
import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@cosmjs/encoding";
import { makeSignBytes, MsgSend, Secp256k1Pen, StdFee } from "@cosmjs/sdk38";
import { makeSignBytes, MsgSend, Secp256k1Wallet, StdFee } from "@cosmjs/sdk38";
import { assert, sleep } from "@cosmjs/utils";
import { ReadonlyDate } from "readonly-date";
@ -204,7 +204,7 @@ describe("CosmWasmClient", () => {
describe("postTx", () => {
it("works", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new CosmWasmClient(wasmd.endpoint);
const memo = "My first contract on chain";
@ -235,7 +235,7 @@ describe("CosmWasmClient", () => {
const chainId = await client.getChainId();
const { accountNumber, sequence } = await client.getNonce(alice.address0);
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(alice.address0, signBytes);
const signedTx = {
msg: [sendMsg],
fee: fee,
@ -388,10 +388,8 @@ describe("CosmWasmClient", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(wasmd.endpoint, alice.address0, (signBytes) =>
pen.sign(signBytes),
);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(wasmd.endpoint, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() };
const { contractAddress } = await client.instantiate(codeId, initMsg, "random hackatom");
@ -441,10 +439,8 @@ describe("CosmWasmClient", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(wasmd.endpoint, alice.address0, (signBytes) =>
pen.sign(signBytes),
);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(wasmd.endpoint, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() };
const { contractAddress } = await client.instantiate(codeId, initMsg, "a different hackatom");

View File

@ -8,9 +8,9 @@ import {
coins,
LcdClient,
makeSignBytes,
Pen,
OfflineSigner,
PostTxsResponse,
Secp256k1Pen,
Secp256k1Wallet,
setupAuthExtension,
StdFee,
} from "@cosmjs/sdk38";
@ -47,7 +47,7 @@ function makeWasmClient(apiUrl: string): WasmClient {
async function uploadContract(
client: WasmClient,
pen: Pen,
signer: OfflineSigner,
contract: ContractUploadInstructions,
): Promise<PostTxsResponse> {
const memo = "My first contract on chain";
@ -72,14 +72,14 @@ async function uploadContract(
const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value;
const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence);
const signature = await pen.sign(signBytes);
const signature = await signer.sign(alice.address0, signBytes);
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
return client.postTx(signedTx);
}
async function instantiateContract(
client: WasmClient,
pen: Pen,
signer: OfflineSigner,
codeId: number,
beneficiaryAddress: string,
transferAmount?: readonly Coin[],
@ -110,14 +110,14 @@ async function instantiateContract(
const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value;
const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence);
const signature = await pen.sign(signBytes);
const signature = await signer.sign(alice.address0, signBytes);
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
return client.postTx(signedTx);
}
async function executeContract(
client: WasmClient,
pen: Pen,
signer: OfflineSigner,
contractAddress: string,
msg: object,
): Promise<PostTxsResponse> {
@ -138,7 +138,7 @@ async function executeContract(
const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value;
const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence);
const signature = await pen.sign(signBytes);
const signature = await signer.sign(alice.address0, signBytes);
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
return client.postTx(signedTx);
}
@ -256,7 +256,7 @@ describe("wasm", () => {
describe("postTx", () => {
it("can upload, instantiate and execute wasm", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = makeWasmClient(wasmd.endpoint);
const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")];
@ -267,7 +267,7 @@ describe("wasm", () => {
// upload
{
// console.log("Raw log:", result.raw_log);
const result = await uploadContract(client, pen, getHackatom());
const result = await uploadContract(client, wallet, getHackatom());
expect(result.code).toBeFalsy();
const logs = parseLogs(result.logs);
const codeIdAttr = findAttribute(logs, "message", "code_id");
@ -281,7 +281,7 @@ describe("wasm", () => {
// instantiate
{
const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount);
const result = await instantiateContract(client, wallet, codeId, beneficiaryAddress, transferAmount);
expect(result.code).toBeFalsy();
// console.log("Raw log:", result.raw_log);
const logs = parseLogs(result.logs);
@ -297,7 +297,7 @@ describe("wasm", () => {
// execute
{
const result = await executeContract(client, pen, contractAddress, { release: {} });
const result = await executeContract(client, wallet, contractAddress, { release: {} });
expect(result.data).toEqual("F00BAA");
expect(result.code).toBeFalsy();
// console.log("Raw log:", result.logs);
@ -324,7 +324,7 @@ describe("wasm", () => {
describe("query", () => {
it("can list upload code", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = makeWasmClient(wasmd.endpoint);
// check with contracts were here first to compare
@ -334,7 +334,7 @@ describe("wasm", () => {
// upload data
const hackatom = getHackatom();
const result = await uploadContract(client, pen, hackatom);
const result = await uploadContract(client, wallet, hackatom);
expect(result.code).toBeFalsy();
const logs = parseLogs(result.logs);
const codeIdAttr = findAttribute(logs, "message", "code_id");
@ -362,7 +362,7 @@ describe("wasm", () => {
it("can list contracts and get info", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = makeWasmClient(wasmd.endpoint);
const beneficiaryAddress = makeRandomAddress();
const transferAmount: readonly Coin[] = [
@ -378,7 +378,7 @@ describe("wasm", () => {
if (existingInfos.length > 0) {
codeId = existingInfos[existingInfos.length - 1].id;
} else {
const uploadResult = await uploadContract(client, pen, getHackatom());
const uploadResult = await uploadContract(client, wallet, getHackatom());
expect(uploadResult.code).toBeFalsy();
const uploadLogs = parseLogs(uploadResult.logs);
const codeIdAttr = findAttribute(uploadLogs, "message", "code_id");
@ -394,7 +394,7 @@ describe("wasm", () => {
expect(contract.label).toMatch(/^.+$/);
}
const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount);
const result = await instantiateContract(client, wallet, codeId, beneficiaryAddress, transferAmount);
expect(result.code).toBeFalsy();
const logs = parseLogs(result.logs);
const contractAddressAttr = findAttribute(logs, "message", "contract_address");
@ -438,12 +438,12 @@ describe("wasm", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const uploadResult = await uploadContract(client, pen, getHackatom());
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const uploadResult = await uploadContract(client, wallet, getHackatom());
assert(!uploadResult.code);
const uploadLogs = parseLogs(uploadResult.logs);
const codeId = Number.parseInt(findAttribute(uploadLogs, "message", "code_id").value, 10);
const instantiateResult = await instantiateContract(client, pen, codeId, makeRandomAddress());
const instantiateResult = await instantiateContract(client, wallet, codeId, makeRandomAddress());
assert(!instantiateResult.code);
const instantiateLogs = parseLogs(instantiateResult.logs);
const contractAddressAttr = findAttribute(instantiateLogs, "message", "contract_address");

View File

@ -1,6 +1,6 @@
import { Sha256 } from "@cosmjs/crypto";
import { toHex } from "@cosmjs/encoding";
import { AuthExtension, coin, coins, LcdClient, Secp256k1Pen, setupAuthExtension } from "@cosmjs/sdk38";
import { AuthExtension, coin, coins, LcdClient, Secp256k1Wallet, setupAuthExtension } from "@cosmjs/sdk38";
import { assert } from "@cosmjs/utils";
import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient";
@ -17,8 +17,8 @@ function makeWasmClient(apiUrl: string): LcdClient & AuthExtension & WasmExtensi
describe("SigningCosmWasmClient", () => {
describe("makeReadOnly", () => {
it("can be constructed", async () => {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
expect(client).toBeTruthy();
});
});
@ -26,8 +26,8 @@ describe("SigningCosmWasmClient", () => {
describe("getHeight", () => {
it("always uses authAccount implementation", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
@ -44,8 +44,8 @@ describe("SigningCosmWasmClient", () => {
describe("upload", () => {
it("works", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const wasm = getHackatom().data;
const {
codeId,
@ -63,8 +63,8 @@ describe("SigningCosmWasmClient", () => {
it("can set builder and source", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const hackatom = getHackatom();
const meta: UploadMeta = {
@ -82,8 +82,8 @@ describe("SigningCosmWasmClient", () => {
describe("instantiate", () => {
it("works with transfer amount", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")];
@ -108,8 +108,8 @@ describe("SigningCosmWasmClient", () => {
it("works with admin", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
@ -131,8 +131,8 @@ describe("SigningCosmWasmClient", () => {
it("can instantiate one code multiple times", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const contractAddress1 = await client.instantiate(
@ -158,8 +158,8 @@ describe("SigningCosmWasmClient", () => {
describe("updateAdmin", () => {
it("can update an admin", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
@ -191,8 +191,8 @@ describe("SigningCosmWasmClient", () => {
describe("clearAdmin", () => {
it("can clear an admin", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
@ -224,8 +224,8 @@ describe("SigningCosmWasmClient", () => {
describe("migrate", () => {
it("can can migrate from one code ID to another", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId: codeId1 } = await client.upload(getHackatom().data);
const { codeId: codeId2 } = await client.upload(getHackatom().data);
@ -263,8 +263,8 @@ describe("SigningCosmWasmClient", () => {
describe("execute", () => {
it("works", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const { codeId } = await client.upload(getHackatom().data);
// instantiate
@ -304,8 +304,8 @@ describe("SigningCosmWasmClient", () => {
describe("sendTokens", () => {
it("works", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
// instantiate
const transferAmount = coins(7890, "ucosm");

View File

@ -7,6 +7,7 @@ import {
coins,
makeSignBytes,
MsgSend,
OfflineSigner,
StdFee,
StdSignature,
StdTx,
@ -164,7 +165,7 @@ function createPostTxErrorMessage(result: PostTxFailure): string {
export class SigningCosmWasmClient extends CosmWasmClient {
public readonly senderAddress: string;
private readonly signCallback: SigningCallback;
private readonly signer: OfflineSigner;
private readonly fees: FeeTable;
/**
@ -175,14 +176,14 @@ export class SigningCosmWasmClient extends CosmWasmClient {
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param senderAddress The address that will sign and send transactions using this instance
* @param signCallback An asynchonous callback to create a signature for a given transaction. This can be implemented using secure key stores that require user interaction.
* @param signer An implementation of OfflineSigner which can provide signatures for transactions, potentially requiring user input.
* @param customFees The fees that are paid for transactions
* @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns
*/
public constructor(
apiUrl: string,
senderAddress: string,
signCallback: SigningCallback,
signer: OfflineSigner,
customFees?: Partial<FeeTable>,
broadcastMode = BroadcastMode.Block,
) {
@ -190,7 +191,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
this.anyValidAddress = senderAddress;
this.senderAddress = senderAddress;
this.signCallback = signCallback;
this.signer = signer;
this.fees = { ...defaultFees, ...(customFees || {}) };
}
@ -222,7 +223,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([storeCodeMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [storeCodeMsg],
fee: fee,
@ -272,7 +273,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const chainId = await this.getChainId();
const signBytes = makeSignBytes([instantiateMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [instantiateMsg],
fee: fee,
@ -306,7 +307,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([updateAdminMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [updateAdminMsg],
fee: fee,
@ -336,7 +337,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([clearAdminMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [clearAdminMsg],
fee: fee,
@ -374,7 +375,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([msg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [msg],
fee: fee,
@ -412,7 +413,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([executeMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [executeMsg],
fee: fee,
@ -449,7 +450,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [sendMsg],
fee: fee,

View File

@ -1,4 +1,4 @@
import { BroadcastMode, Coin, StdFee, StdSignature } from "@cosmjs/sdk38";
import { BroadcastMode, Coin, OfflineSigner, StdFee, StdSignature } from "@cosmjs/sdk38";
import { Account, CosmWasmClient, GetNonceResult, PostTxResult } from "./cosmwasmclient";
import { Log } from "./logs";
export interface SigningCallback {
@ -83,7 +83,7 @@ export interface ExecuteResult {
}
export declare class SigningCosmWasmClient extends CosmWasmClient {
readonly senderAddress: string;
private readonly signCallback;
private readonly signer;
private readonly fees;
/**
* Creates a new client with signing capability to interact with a CosmWasm blockchain. This is the bigger brother of CosmWasmClient.
@ -93,14 +93,14 @@ export declare class SigningCosmWasmClient extends CosmWasmClient {
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param senderAddress The address that will sign and send transactions using this instance
* @param signCallback An asynchonous callback to create a signature for a given transaction. This can be implemented using secure key stores that require user interaction.
* @param signer An implementation of OfflineSigner which can provide signatures for transactions, potentially requiring user input.
* @param customFees The fees that are paid for transactions
* @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns
*/
constructor(
apiUrl: string,
senderAddress: string,
signCallback: SigningCallback,
signer: OfflineSigner,
customFees?: Partial<FeeTable>,
broadcastMode?: BroadcastMode,
);

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/camelcase */
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm";
import { Coin, coins, makeCosmoshubPath, Secp256k1Pen } from "@cosmjs/sdk38";
import { Coin, coins, makeCosmoshubPath, Secp256k1Wallet } from "@cosmjs/sdk38";
import {
BalanceResponse,
@ -40,26 +40,19 @@ describe("Staking demo", () => {
it("works", async () => {
pendingWithoutWasmd();
// The owner of the contract that will collect the tax
const ownerPen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const ownerAddress = ownerPen.address("cosmos");
const ownerClient = new SigningCosmWasmClient(httpUrl, ownerAddress, (signBytes) =>
ownerPen.sign(signBytes),
);
const ownerWallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const [{ address: ownerAddress }] = await ownerWallet.getAccounts();
const ownerClient = new SigningCosmWasmClient(httpUrl, ownerAddress, ownerWallet);
// a user of the contract
const userPen = await Secp256k1Pen.fromMnemonic(alice.mnemonic, makeCosmoshubPath(1));
const userAddress = userPen.address("cosmos");
const userClient = new SigningCosmWasmClient(
httpUrl,
userAddress,
(signBytes) => userPen.sign(signBytes),
{
exec: {
amount: coins(5000, "ucosm"),
gas: "300000", // 300k, needed for unbonding
},
const userWallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic, makeCosmoshubPath(1));
const [{ address: userAddress }] = await userWallet.getAccounts();
const userClient = new SigningCosmWasmClient(httpUrl, userAddress, userWallet, {
exec: {
amount: coins(5000, "ucosm"),
gas: "300000", // 300k, needed for unbonding
},
);
});
const initMsg: InitMsg = {
name: params.name,

View File

@ -1,7 +1,7 @@
import { Bip39, Random } from "@cosmjs/crypto";
import * as constants from "../constants";
import { createPens } from "../profile";
import { createWallets } from "../profile";
export async function generate(args: readonly string[]): Promise<void> {
if (args.length < 1) {
@ -16,5 +16,5 @@ export async function generate(args: readonly string[]): Promise<void> {
console.info(`FAUCET_MNEMONIC="${mnemonic}"`);
// Log the addresses
await createPens(mnemonic, chainId, constants.concurrency, true);
await createWallets(mnemonic, chainId, constants.concurrency, true);
}

View File

@ -1,8 +1,8 @@
import { CosmosClient, Pen, SigningCosmosClient } from "@cosmjs/sdk38";
import { CosmosClient, OfflineSigner, SigningCosmosClient } from "@cosmjs/sdk38";
import { sleep } from "@cosmjs/utils";
import { debugAccount, logAccountsState, logSendJob } from "./debugging";
import { createPens } from "./profile";
import { createWallets } from "./profile";
import { TokenManager } from "./tokenmanager";
import { MinimalAccount, SendJob, TokenConfiguration } from "./types";
@ -19,8 +19,8 @@ export class Faucet {
numberOfDistributors: number,
logging = false,
): Promise<Faucet> {
const pens = await createPens(mnemonic, addressPrefix, numberOfDistributors, logging);
return new Faucet(apiUrl, addressPrefix, config, pens, logging);
const wallets = await createWallets(mnemonic, addressPrefix, numberOfDistributors, logging);
return new Faucet(apiUrl, addressPrefix, config, wallets, logging);
}
public readonly addressPrefix: string;
@ -38,7 +38,7 @@ export class Faucet {
apiUrl: string,
addressPrefix: string,
config: TokenConfiguration,
pens: ReadonlyArray<readonly [string, Pen]>,
wallets: ReadonlyArray<readonly [string, OfflineSigner]>,
logging = false,
) {
this.addressPrefix = addressPrefix;
@ -47,15 +47,13 @@ export class Faucet {
this.readOnlyClient = new CosmosClient(apiUrl);
this.holderAddress = pens[0][0];
this.distributorAddresses = pens.slice(1).map((pair) => pair[0]);
this.holderAddress = wallets[0][0];
this.distributorAddresses = wallets.slice(1).map((pair) => pair[0]);
// we need one client per sender
const clients: { [senderAddress: string]: SigningCosmosClient } = {};
for (const [senderAddress, pen] of pens) {
clients[senderAddress] = new SigningCosmosClient(apiUrl, senderAddress, (signBytes) =>
pen.sign(signBytes),
);
for (const [senderAddress, wallet] of wallets) {
clients[senderAddress] = new SigningCosmosClient(apiUrl, senderAddress, wallet);
}
this.clients = clients;
this.logging = logging;

View File

@ -1,26 +1,26 @@
import { pathToString } from "@cosmjs/crypto";
import { makeCosmoshubPath, Pen, Secp256k1Pen } from "@cosmjs/sdk38";
import { makeCosmoshubPath, OfflineSigner, Secp256k1Wallet } from "@cosmjs/sdk38";
export async function createPens(
export async function createWallets(
mnemonic: string,
addressPrefix: string,
numberOfDistributors: number,
logging = false,
): Promise<ReadonlyArray<readonly [string, Pen]>> {
const pens = new Array<readonly [string, Pen]>();
): Promise<ReadonlyArray<readonly [string, OfflineSigner]>> {
const wallets = new Array<readonly [string, OfflineSigner]>();
// first account is the token holder
const numberOfIdentities = 1 + numberOfDistributors;
for (let i = 0; i < numberOfIdentities; i++) {
const path = makeCosmoshubPath(i);
const pen = await Secp256k1Pen.fromMnemonic(mnemonic, path);
const address = pen.address(addressPrefix);
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic, path, addressPrefix);
const [{ address }] = await wallet.getAccounts();
if (logging) {
const role = i === 0 ? "token holder " : `distributor ${i}`;
console.info(`Created ${role} (${pathToString(path)}): ${address}`);
}
pens.push([address, pen]);
wallets.push([address, wallet]);
}
return pens;
return wallets;
}

View File

@ -6,7 +6,6 @@ import { CosmosClient, isPostTxFailure } from "./cosmosclient";
import { makeSignBytes } from "./encoding";
import { LcdClient } from "./lcdapi";
import { isMsgSend, MsgSend } from "./msgs";
import { Secp256k1Pen } from "./pen";
import { SigningCosmosClient } from "./signingcosmosclient";
import {
faucet,
@ -17,6 +16,7 @@ import {
wasmdEnabled,
} from "./testutils.spec";
import { CosmosSdkTx } from "./types";
import { Secp256k1Wallet } from "./wallet";
interface TestTxSend {
readonly sender: string;
@ -32,10 +32,10 @@ describe("CosmosClient.searchTx", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) =>
pen.sign(signBytes),
);
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet);
{
const memo = "Sending more than I can afford";
@ -58,7 +58,7 @@ describe("CosmosClient.searchTx", () => {
const { accountNumber, sequence } = await client.getNonce();
const chainId = await client.getChainId();
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(walletAddress, signBytes);
const tx: CosmosSdkTx = {
type: "cosmos-sdk/StdTx",
value: {

View File

@ -6,7 +6,6 @@ import { CosmosClient, isPostTxFailure, PrivateCosmWasmClient } from "./cosmoscl
import { makeSignBytes } from "./encoding";
import { findAttribute } from "./logs";
import { MsgSend } from "./msgs";
import { Secp256k1Pen } from "./pen";
import cosmoshub from "./testdata/cosmoshub.json";
import {
faucet,
@ -17,6 +16,7 @@ import {
wasmd,
} from "./testutils.spec";
import { StdFee } from "./types";
import { Secp256k1Wallet } from "./wallet";
const blockTime = 1_000; // ms
@ -193,7 +193,9 @@ describe("CosmosClient", () => {
describe("postTx", () => {
it("works", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new CosmosClient(wasmd.endpoint);
const memo = "My first contract on chain";
@ -224,7 +226,7 @@ describe("CosmosClient", () => {
const chainId = await client.getChainId();
const { accountNumber, sequence } = await client.getNonce(faucet.address);
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(walletAddress, signBytes);
const signedTx = {
msg: [sendMsg],
fee: fee,

View File

@ -50,9 +50,9 @@ export {
TxsResponse,
} from "./lcdapi";
export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs";
export { Pen, Secp256k1Pen, makeCosmoshubPath } from "./pen";
export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey";
export { findSequenceForSignedTx } from "./sequence";
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
export { FeeTable, SigningCallback, SigningCosmosClient } from "./signingcosmosclient";
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
export { OfflineSigner, Secp256k1Wallet, makeCosmoshubPath } from "./wallet";

View File

@ -1,13 +1,11 @@
/* eslint-disable @typescript-eslint/camelcase */
import { assert, sleep } from "@cosmjs/utils";
import { rawSecp256k1PubkeyToAddress } from "../address";
import { Coin } from "../coins";
import { isPostTxFailure } from "../cosmosclient";
import { makeSignBytes } from "../encoding";
import { parseLogs } from "../logs";
import { MsgSend } from "../msgs";
import { makeCosmoshubPath, Secp256k1Pen } from "../pen";
import { SigningCosmosClient } from "../signingcosmosclient";
import cosmoshub from "../testdata/cosmoshub.json";
import {
@ -21,6 +19,7 @@ import {
wasmdEnabled,
} from "../testutils.spec";
import { StdFee } from "../types";
import { makeCosmoshubPath, Secp256k1Wallet } from "../wallet";
import { setupAuthExtension } from "./auth";
import { TxsResponse } from "./base";
import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient";
@ -217,10 +216,10 @@ describe("LcdClient", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) =>
pen.sign(signBytes),
);
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet);
{
const recipient = makeRandomAddress();
@ -267,7 +266,7 @@ describe("LcdClient", () => {
const { accountNumber, sequence } = await client.getNonce();
const chainId = await client.getChainId();
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(walletAddress, signBytes);
const signedTx = {
msg: [sendMsg],
fee: fee,
@ -351,10 +350,8 @@ describe("LcdClient", () => {
beforeAll(async () => {
if (wasmdEnabled()) {
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) =>
pen.sign(signBytes),
);
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet);
const recipient = makeRandomAddress();
const transferAmount = [
@ -534,7 +531,9 @@ describe("LcdClient", () => {
describe("postTx", () => {
it("can send tokens", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const memo = "My first contract on chain";
const theMsg: MsgSend = {
@ -565,7 +564,7 @@ describe("LcdClient", () => {
const { account_number, sequence } = (await client.auth.account(faucet.address)).result.value;
const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence);
const signature = await pen.sign(signBytes);
const signature = await wallet.sign(walletAddress, signBytes);
const signedTx = makeSignedTx(theMsg, fee, memo, signature);
const result = await client.postTx(signedTx);
expect(result.code).toBeUndefined();
@ -582,12 +581,14 @@ describe("LcdClient", () => {
it("can't send transaction with additional signatures", async () => {
pendingWithoutWasmd();
const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const account3 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(2));
const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos");
const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos");
const address3 = rawSecp256k1PubkeyToAddress(account3.pubkey, "cosmos");
const account1 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const account3 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(2));
const [address1, address2, address3] = await Promise.all(
[account1, account2, account3].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const theMsg: MsgSend = {
@ -622,9 +623,9 @@ describe("LcdClient", () => {
const signBytes1 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an1, sequence1);
const signBytes2 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an2, sequence2);
const signBytes3 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an3, sequence3);
const signature1 = await account1.sign(signBytes1);
const signature2 = await account2.sign(signBytes2);
const signature3 = await account3.sign(signBytes3);
const signature1 = await account1.sign(address1, signBytes1);
const signature2 = await account2.sign(address2, signBytes2);
const signature3 = await account3.sign(address3, signBytes3);
const signedTx = {
msg: [theMsg],
fee: fee,
@ -638,14 +639,15 @@ describe("LcdClient", () => {
it("can send multiple messages with one signature", async () => {
pendingWithoutWasmd();
const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos");
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
from_address: walletAddress,
to_address: defaultRecipientAddress,
amount: [
{
@ -658,7 +660,7 @@ describe("LcdClient", () => {
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
from_address: walletAddress,
to_address: defaultRecipientAddress,
amount: [
{
@ -680,10 +682,10 @@ describe("LcdClient", () => {
};
const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension);
const { account_number, sequence } = (await client.auth.account(address1)).result.value;
const { account_number, sequence } = (await client.auth.account(walletAddress)).result.value;
const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence);
const signature1 = await account1.sign(signBytes);
const signature1 = await wallet.sign(walletAddress, signBytes);
const signedTx = {
msg: [msg1, msg2],
fee: fee,
@ -696,10 +698,13 @@ describe("LcdClient", () => {
it("can send multiple messages with multiple signatures", async () => {
pendingWithoutWasmd();
const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos");
const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos");
const account1 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
@ -745,8 +750,8 @@ describe("LcdClient", () => {
const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1);
const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2);
const signature1 = await account1.sign(signBytes1);
const signature2 = await account2.sign(signBytes2);
const signature1 = await account1.sign(address1, signBytes1);
const signature2 = await account2.sign(address2, signBytes2);
const signedTx = {
msg: [msg2, msg1],
fee: fee,
@ -764,10 +769,13 @@ describe("LcdClient", () => {
it("can't send transaction with wrong signature order (1)", async () => {
pendingWithoutWasmd();
const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos");
const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos");
const account1 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
@ -813,8 +821,8 @@ describe("LcdClient", () => {
const signBytes1 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1);
const signBytes2 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2);
const signature1 = await account1.sign(signBytes1);
const signature2 = await account2.sign(signBytes2);
const signature1 = await account1.sign(address1, signBytes1);
const signature2 = await account2.sign(address2, signBytes2);
const signedTx = {
msg: [msg1, msg2],
fee: fee,
@ -827,10 +835,13 @@ describe("LcdClient", () => {
it("can't send transaction with wrong signature order (2)", async () => {
pendingWithoutWasmd();
const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos");
const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos");
const account1 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const account2 = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
@ -876,8 +887,8 @@ describe("LcdClient", () => {
const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1);
const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2);
const signature1 = await account1.sign(signBytes1);
const signature2 = await account2.sign(signBytes2);
const signature1 = await account1.sign(address1, signBytes1);
const signature2 = await account2.sign(address2, signBytes2);
const signedTx = {
msg: [msg2, msg1],
fee: fee,

View File

@ -1,54 +0,0 @@
import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto";
import { fromHex, toAscii } from "@cosmjs/encoding";
import { Secp256k1Pen } from "./pen";
import { decodeSignature } from "./signature";
describe("Sec256k1Pen", () => {
it("can be constructed", async () => {
const pen = await Secp256k1Pen.fromMnemonic(
"zebra slush diet army arrest purpose hawk source west glimpse custom record",
);
expect(pen).toBeTruthy();
});
describe("pubkey", () => {
it("returns compressed pubkey", async () => {
// special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling
// m/44'/118'/0'/0/0
// pubkey: 02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6
const pen = await Secp256k1Pen.fromMnemonic(
"special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling",
);
expect(pen.pubkey).toEqual(
fromHex("02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6"),
);
});
});
describe("sign", () => {
it("creates correct signatures", async () => {
const pen = await Secp256k1Pen.fromMnemonic(
"special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling",
);
const data = toAscii("foo bar");
const { pubkey, signature } = decodeSignature(await pen.sign(data));
const valid = await Secp256k1.verifySignature(
Secp256k1Signature.fromFixedLength(signature),
new Sha256(data).digest(),
pubkey,
);
expect(valid).toEqual(true);
});
});
describe("address", () => {
it("creates same address as Go imlementation", async () => {
const pen = await Secp256k1Pen.fromMnemonic(
"oyster design unusual machine spread century engine gravity focus cave carry slot",
);
expect(pen.address("cosmos")).toEqual("cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u");
});
});
});

View File

@ -1,92 +0,0 @@
import {
Bip39,
EnglishMnemonic,
Secp256k1,
Sha256,
Sha512,
Slip10,
Slip10Curve,
Slip10RawIndex,
} from "@cosmjs/crypto";
import { rawSecp256k1PubkeyToAddress } from "./address";
import { encodeSecp256k1Signature } from "./signature";
import { StdSignature } from "./types";
export type PrehashType = "sha256" | "sha512" | null;
/**
* A pen is the most basic tool you can think of for signing. It works
* everywhere and can be used intuitively by everyone. However, it does not
* come with a great amount of features. End of semi suitable metaphor.
*
* This wraps a single keypair and allows for signing.
*
* Non-goals of this types are: multi account support, persistency, data migrations,
* obfuscation of sensitive data.
*/
export interface Pen {
readonly pubkey: Uint8Array;
readonly sign: (signBytes: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
}
function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array {
switch (type) {
case null:
return new Uint8Array([...bytes]);
case "sha256":
return new Sha256(bytes).digest();
case "sha512":
return new Sha512(bytes).digest();
default:
throw new Error("Unknown prehash type");
}
}
/**
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
* with 0-based account index `a`.
*/
export function makeCosmoshubPath(a: number): readonly Slip10RawIndex[] {
return [
Slip10RawIndex.hardened(44),
Slip10RawIndex.hardened(118),
Slip10RawIndex.hardened(0),
Slip10RawIndex.normal(0),
Slip10RawIndex.normal(a),
];
}
export class Secp256k1Pen implements Pen {
public static async fromMnemonic(
mnemonic: string,
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
): Promise<Secp256k1Pen> {
const seed = await Bip39.mnemonicToSeed(new EnglishMnemonic(mnemonic));
const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath);
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
return new Secp256k1Pen(privkey, Secp256k1.compressPubkey(uncompressed));
}
public readonly pubkey: Uint8Array;
private readonly privkey: Uint8Array;
private constructor(privkey: Uint8Array, pubkey: Uint8Array) {
this.privkey = privkey;
this.pubkey = pubkey;
}
/**
* Creates and returns a signature
*/
public async sign(signBytes: Uint8Array, prehashType: PrehashType = "sha256"): Promise<StdSignature> {
const message = prehash(signBytes, prehashType);
const signature = await Secp256k1.createSignature(message, this.privkey);
const fixedLengthSignature = new Uint8Array([...signature.r(32), ...signature.s(32)]);
return encodeSecp256k1Signature(this.pubkey, fixedLengthSignature);
}
public address(prefix: string): string {
return rawSecp256k1PubkeyToAddress(this.pubkey, prefix);
}
}

View File

@ -2,9 +2,9 @@ import { assert } from "@cosmjs/utils";
import { Coin } from "./coins";
import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmosclient";
import { Secp256k1Pen } from "./pen";
import { SigningCosmosClient } from "./signingcosmosclient";
import { makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec";
import { Secp256k1Wallet } from "./wallet";
const httpUrl = "http://localhost:1317";
@ -21,8 +21,8 @@ const faucet = {
describe("SigningCosmosClient", () => {
describe("makeReadOnly", () => {
it("can be constructed", async () => {
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet);
expect(client).toBeTruthy();
});
});
@ -30,8 +30,8 @@ describe("SigningCosmosClient", () => {
describe("getHeight", () => {
it("always uses authAccount implementation", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
@ -48,8 +48,8 @@ describe("SigningCosmosClient", () => {
describe("sendTokens", () => {
it("works", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet);
// instantiate
const transferAmount: readonly Coin[] = [

View File

@ -3,11 +3,8 @@ import { Account, CosmosClient, GetNonceResult, PostTxResult } from "./cosmoscli
import { makeSignBytes } from "./encoding";
import { BroadcastMode } from "./lcdapi";
import { MsgSend } from "./msgs";
import { StdFee, StdSignature, StdTx } from "./types";
export interface SigningCallback {
(signBytes: Uint8Array): Promise<StdSignature>;
}
import { StdFee, StdTx } from "./types";
import { OfflineSigner } from "./wallet";
export interface FeeTable {
readonly upload: StdFee;
@ -38,7 +35,7 @@ const defaultFees: FeeTable = {
export class SigningCosmosClient extends CosmosClient {
public readonly senderAddress: string;
private readonly signCallback: SigningCallback;
private readonly signer: OfflineSigner;
private readonly fees: FeeTable;
/**
@ -49,14 +46,14 @@ export class SigningCosmosClient extends CosmosClient {
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param senderAddress The address that will sign and send transactions using this instance
* @param signCallback An asynchonous callback to create a signature for a given transaction. This can be implemented using secure key stores that require user interaction.
* @param signer An implementation of OfflineSigner which can provide signatures for transactions, potentially requiring user input.
* @param customFees The fees that are paid for transactions
* @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns
*/
public constructor(
apiUrl: string,
senderAddress: string,
signCallback: SigningCallback,
signer: OfflineSigner,
customFees?: Partial<FeeTable>,
broadcastMode = BroadcastMode.Block,
) {
@ -64,7 +61,7 @@ export class SigningCosmosClient extends CosmosClient {
this.anyValidAddress = senderAddress;
this.senderAddress = senderAddress;
this.signCallback = signCallback;
this.signer = signer;
this.fees = { ...defaultFees, ...(customFees || {}) };
}
@ -95,7 +92,7 @@ export class SigningCosmosClient extends CosmosClient {
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signature = await this.signer.sign(this.senderAddress, signBytes);
const signedTx: StdTx = {
msg: [sendMsg],
fee: fee,

View File

@ -0,0 +1,52 @@
import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto";
import { fromBase64, fromHex, toAscii } from "@cosmjs/encoding";
import { Secp256k1Wallet } from "./wallet";
describe("Secp256k1Wallet", () => {
// m/44'/118'/0'/0/0
// pubkey: 02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6
const defaultMnemonic = "special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling";
const defaultPubkey = fromHex("02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6");
const defaultAddress = "cosmos1jhg0e7s6gn44tfc5k37kr04sznyhedtc9rzys5";
it("can be constructed", async () => {
const wallet = await Secp256k1Wallet.fromMnemonic(defaultMnemonic);
expect(wallet).toBeTruthy();
});
describe("getAccounts", () => {
it("resolves to a list of accounts if enabled", async () => {
const wallet = await Secp256k1Wallet.fromMnemonic(defaultMnemonic);
const accounts = await wallet.getAccounts();
expect(accounts.length).toEqual(1);
expect(accounts[0]).toEqual({
address: defaultAddress,
algo: "secp256k1",
pubkey: defaultPubkey,
});
});
it("creates the same address as Go implementation", async () => {
const wallet = await Secp256k1Wallet.fromMnemonic(
"oyster design unusual machine spread century engine gravity focus cave carry slot",
);
const [{ address }] = await wallet.getAccounts();
expect(address).toEqual("cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u");
});
});
describe("sign", () => {
it("resolves to valid signature if enabled", async () => {
const wallet = await Secp256k1Wallet.fromMnemonic(defaultMnemonic);
const message = toAscii("foo bar");
const signature = await wallet.sign(defaultAddress, message);
const valid = await Secp256k1.verifySignature(
Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)),
new Sha256(message).digest(),
defaultPubkey,
);
expect(valid).toEqual(true);
});
});
});

View File

@ -0,0 +1,116 @@
import {
Bip39,
EnglishMnemonic,
Secp256k1,
Sha256,
Sha512,
Slip10,
Slip10Curve,
Slip10RawIndex,
} from "@cosmjs/crypto";
import { rawSecp256k1PubkeyToAddress } from "./address";
import { encodeSecp256k1Signature } from "./signature";
import { StdSignature } from "./types";
export type PrehashType = "sha256" | "sha512" | null;
export type Algo = "secp256k1" | "ed25519" | "sr25519";
export interface AccountData {
// bech32-encoded
readonly address: string;
readonly algo: Algo;
readonly pubkey: Uint8Array;
}
export interface OfflineSigner {
/**
* Get AccountData array from wallet. Rejects if not enabled.
*/
readonly getAccounts: () => Promise<readonly AccountData[]>;
/**
* Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled.
*/
readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
}
function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array {
switch (type) {
case null:
return new Uint8Array([...bytes]);
case "sha256":
return new Sha256(bytes).digest();
case "sha512":
return new Sha512(bytes).digest();
default:
throw new Error("Unknown prehash type");
}
}
/**
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
* with 0-based account index `a`.
*/
export function makeCosmoshubPath(a: number): readonly Slip10RawIndex[] {
return [
Slip10RawIndex.hardened(44),
Slip10RawIndex.hardened(118),
Slip10RawIndex.hardened(0),
Slip10RawIndex.normal(0),
Slip10RawIndex.normal(a),
];
}
export class Secp256k1Wallet implements OfflineSigner {
public static async fromMnemonic(
mnemonic: string,
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
prefix = "cosmos",
): Promise<Secp256k1Wallet> {
const seed = await Bip39.mnemonicToSeed(new EnglishMnemonic(mnemonic));
const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath);
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
return new Secp256k1Wallet(privkey, Secp256k1.compressPubkey(uncompressed), prefix);
}
private readonly pubkey: Uint8Array;
private readonly privkey: Uint8Array;
private readonly prefix: string;
private readonly algo: Algo = "secp256k1";
private constructor(privkey: Uint8Array, pubkey: Uint8Array, prefix: string) {
this.privkey = privkey;
this.pubkey = pubkey;
this.prefix = prefix;
}
private get address(): string {
return rawSecp256k1PubkeyToAddress(this.pubkey, this.prefix);
}
public async getAccounts(): Promise<readonly AccountData[]> {
return [
{
address: this.address,
algo: this.algo,
pubkey: this.pubkey,
},
];
}
public async sign(
address: string,
message: Uint8Array,
prehashType: PrehashType = "sha256",
): Promise<StdSignature> {
if (address !== this.address) {
throw new Error(`Address ${address} not found in wallet`);
}
const hashedMessage = prehash(message, prehashType);
const signature = await Secp256k1.createSignature(hashedMessage, this.privkey);
const signatureBytes = new Uint8Array([...signature.r(32), ...signature.s(32)]);
return encodeSecp256k1Signature(this.pubkey, signatureBytes);
}
}

View File

@ -48,9 +48,9 @@ export {
TxsResponse,
} from "./lcdapi";
export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs";
export { Pen, Secp256k1Pen, makeCosmoshubPath } from "./pen";
export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey";
export { findSequenceForSignedTx } from "./sequence";
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
export { FeeTable, SigningCallback, SigningCosmosClient } from "./signingcosmosclient";
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
export { OfflineSigner, Secp256k1Wallet, makeCosmoshubPath } from "./wallet";

View File

@ -1,33 +0,0 @@
import { Slip10RawIndex } from "@cosmjs/crypto";
import { StdSignature } from "./types";
export declare type PrehashType = "sha256" | "sha512" | null;
/**
* A pen is the most basic tool you can think of for signing. It works
* everywhere and can be used intuitively by everyone. However, it does not
* come with a great amount of features. End of semi suitable metaphor.
*
* This wraps a single keypair and allows for signing.
*
* Non-goals of this types are: multi account support, persistency, data migrations,
* obfuscation of sensitive data.
*/
export interface Pen {
readonly pubkey: Uint8Array;
readonly sign: (signBytes: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
}
/**
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
* with 0-based account index `a`.
*/
export declare function makeCosmoshubPath(a: number): readonly Slip10RawIndex[];
export declare class Secp256k1Pen implements Pen {
static fromMnemonic(mnemonic: string, hdPath?: readonly Slip10RawIndex[]): Promise<Secp256k1Pen>;
readonly pubkey: Uint8Array;
private readonly privkey;
private constructor();
/**
* Creates and returns a signature
*/
sign(signBytes: Uint8Array, prehashType?: PrehashType): Promise<StdSignature>;
address(prefix: string): string;
}

View File

@ -1,10 +1,8 @@
import { Coin } from "./coins";
import { Account, CosmosClient, GetNonceResult, PostTxResult } from "./cosmosclient";
import { BroadcastMode } from "./lcdapi";
import { StdFee, StdSignature } from "./types";
export interface SigningCallback {
(signBytes: Uint8Array): Promise<StdSignature>;
}
import { StdFee } from "./types";
import { OfflineSigner } from "./wallet";
export interface FeeTable {
readonly upload: StdFee;
readonly init: StdFee;
@ -13,7 +11,7 @@ export interface FeeTable {
}
export declare class SigningCosmosClient extends CosmosClient {
readonly senderAddress: string;
private readonly signCallback;
private readonly signer;
private readonly fees;
/**
* Creates a new client with signing capability to interact with a CosmWasm blockchain. This is the bigger brother of CosmWasmClient.
@ -23,14 +21,14 @@ export declare class SigningCosmosClient extends CosmosClient {
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param senderAddress The address that will sign and send transactions using this instance
* @param signCallback An asynchonous callback to create a signature for a given transaction. This can be implemented using secure key stores that require user interaction.
* @param signer An implementation of OfflineSigner which can provide signatures for transactions, potentially requiring user input.
* @param customFees The fees that are paid for transactions
* @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns
*/
constructor(
apiUrl: string,
senderAddress: string,
signCallback: SigningCallback,
signer: OfflineSigner,
customFees?: Partial<FeeTable>,
broadcastMode?: BroadcastMode,
);

39
packages/sdk38/types/wallet.d.ts vendored Normal file
View File

@ -0,0 +1,39 @@
import { Slip10RawIndex } from "@cosmjs/crypto";
import { StdSignature } from "./types";
export declare type PrehashType = "sha256" | "sha512" | null;
export declare type Algo = "secp256k1" | "ed25519" | "sr25519";
export interface AccountData {
readonly address: string;
readonly algo: Algo;
readonly pubkey: Uint8Array;
}
export interface OfflineSigner {
/**
* Get AccountData array from wallet. Rejects if not enabled.
*/
readonly getAccounts: () => Promise<readonly AccountData[]>;
/**
* Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled.
*/
readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
}
/**
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
* with 0-based account index `a`.
*/
export declare function makeCosmoshubPath(a: number): readonly Slip10RawIndex[];
export declare class Secp256k1Wallet implements OfflineSigner {
static fromMnemonic(
mnemonic: string,
hdPath?: readonly Slip10RawIndex[],
prefix?: string,
): Promise<Secp256k1Wallet>;
private readonly pubkey;
private readonly privkey;
private readonly prefix;
private readonly algo;
private constructor();
private get address();
getAccounts(): Promise<readonly AccountData[]>;
sign(address: string, message: Uint8Array, prehashType?: PrehashType): Promise<StdSignature>;
}

View File

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/camelcase */
const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm");
const { Secp256k1Pen } = require("@cosmjs/sdk38");
const { Secp256k1Wallet } = require("@cosmjs/sdk38");
const fs = require("fs");
const httpUrl = "http://localhost:1317";
@ -134,8 +134,8 @@ const initDataJade = {
};
async function main() {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const wasm = fs.readFileSync(__dirname + "/contracts/cw-erc20.wasm");
const uploadReceipt = await client.upload(wasm, codeMeta, "Upload ERC20 contract");

View File

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/camelcase */
const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm");
const { Secp256k1Pen } = require("@cosmjs/sdk38");
const { Secp256k1Wallet } = require("@cosmjs/sdk38");
const fs = require("fs");
const httpUrl = "http://localhost:1317";
@ -36,8 +36,8 @@ const luxury = {
};
async function main() {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
const wasm = fs.readFileSync(__dirname + "/contracts/cw-nameservice.wasm");
const uploadReceipt = await client.upload(wasm, codeMeta, "Upload Name Service code");

View File

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/camelcase */
const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm");
const { coins, Secp256k1Pen } = require("@cosmjs/sdk38");
const { coins, Secp256k1Wallet } = require("@cosmjs/sdk38");
const fs = require("fs");
const httpUrl = "http://localhost:1317";
@ -38,8 +38,8 @@ const fees = {
};
async function main() {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes), fees);
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet, fees);
const wasm = fs.readFileSync(__dirname + "/contracts/staking.wasm");
const uploadReceipt = await client.upload(wasm, codeMeta, "Upload Staking code");