Merge pull request #96 from confio/expose-send

Expose sendToken() on CosmWasmClient
This commit is contained in:
merge-when-green[bot]
2020-02-17 12:14:43 +00:00
committed by GitHub
3 changed files with 129 additions and 75 deletions
+41 -38
View File
@@ -222,50 +222,22 @@ describe("CosmWasmClient", () => {
beforeAll(async () => { beforeAll(async () => {
if (cosmosEnabled()) { if (cosmosEnabled()) {
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = CosmWasmClient.makeReadOnly(httpUrl); const client = CosmWasmClient.makeWritable(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const memo = "My first contract on chain"; const recipient = makeRandomAddress();
const sendMsg: MsgSend = { const transferAmount = [
type: "cosmos-sdk/MsgSend", {
value: { denom: "ucosm",
from_address: faucet.address, amount: "1234567",
to_address: makeRandomAddress(),
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
}, },
}; ];
const result = await client.sendTokens(recipient, transferAmount);
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const chainId = await client.chainId();
const { accountNumber, sequence } = await client.getNonce(faucet.address);
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signedTx = {
msg: [sendMsg],
fee: fee,
memo: memo,
signatures: [signature],
};
const result = await client.postTx(marshalTx(signedTx));
await sleep(50); // wait until tx is indexed await sleep(50); // wait until tx is indexed
const txDetails = await new RestClient(httpUrl).txsById(result.transactionHash); const txDetails = await new RestClient(httpUrl).txsById(result.transactionHash);
posted = { posted = {
sender: sendMsg.value.from_address, sender: faucet.address,
recipient: sendMsg.value.to_address, recipient: recipient,
hash: result.transactionHash, hash: result.transactionHash,
height: Number.parseInt(txDetails.height, 10), height: Number.parseInt(txDetails.height, 10),
tx: txDetails.tx, tx: txDetails.tx,
@@ -446,6 +418,37 @@ describe("CosmWasmClient", () => {
}); });
}); });
describe("sendTokens", () => {
it("works", async () => {
pendingWithoutCosmos();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = CosmWasmClient.makeWritable(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
// instantiate
const transferAmount: readonly Coin[] = [
{
amount: "7890",
denom: "ucosm",
},
];
const beneficiaryAddress = makeRandomAddress();
// no tokens here
const before = await client.getAccount(beneficiaryAddress);
expect(before).toBeUndefined();
// send
const result = await client.sendTokens(beneficiaryAddress, transferAmount, "for dinner");
const [firstLog] = result.logs;
expect(firstLog).toBeTruthy();
// got tokens
const after = await client.getAccount(beneficiaryAddress);
assert(after);
expect(after.coins).toEqual(transferAmount);
});
});
describe("queryContractRaw", () => { describe("queryContractRaw", () => {
const configKey = toAscii("config"); const configKey = toAscii("config");
const otherKey = toAscii("this_does_not_exist"); const otherKey = toAscii("this_does_not_exist");
+73 -35
View File
@@ -10,39 +10,40 @@ import {
CosmosSdkTx, CosmosSdkTx,
MsgExecuteContract, MsgExecuteContract,
MsgInstantiateContract, MsgInstantiateContract,
MsgSend,
MsgStoreCode, MsgStoreCode,
StdFee, StdFee,
StdSignature, StdSignature,
} from "./types"; } from "./types";
const defaultUploadFee: StdFee = { export interface FeeTable {
amount: [ readonly upload: StdFee;
{ readonly init: StdFee;
amount: "5000", readonly exec: StdFee;
denom: "ucosm", readonly send: StdFee;
}, }
],
gas: "1000000", // one million
};
const defaultInitFee: StdFee = { function singleAmount(amount: number, denom: string): readonly Coin[] {
amount: [ return [{ amount: amount.toString(), denom: denom }];
{ }
amount: "5000",
denom: "ucosm",
},
],
gas: "500000", // 500k
};
const defaultExecFee: StdFee = { const defaultFees: FeeTable = {
amount: [ upload: {
{ amount: singleAmount(25000, "ucosm"),
amount: "5000", gas: "1000000", // one million
denom: "ucosm", },
}, init: {
], amount: singleAmount(12500, "ucosm"),
gas: "200000", // 200k gas: "500000", // 500k
},
exec: {
amount: singleAmount(5000, "ucosm"),
gas: "200000", // 200k
},
send: {
amount: singleAmount(2000, "ucosm"),
gas: "80000", // 80k
},
}; };
export interface SigningCallback { export interface SigningCallback {
@@ -98,22 +99,28 @@ export interface ExecuteResult {
export class CosmWasmClient { export class CosmWasmClient {
public static makeReadOnly(url: string): CosmWasmClient { public static makeReadOnly(url: string): CosmWasmClient {
return new CosmWasmClient(url); return new CosmWasmClient(url, undefined, {});
} }
public static makeWritable( public static makeWritable(
url: string, url: string,
senderAddress: string, senderAddress: string,
signCallback: SigningCallback, signCallback: SigningCallback,
feeTable?: Partial<FeeTable>,
): CosmWasmClient { ): CosmWasmClient {
return new CosmWasmClient(url, { return new CosmWasmClient(
senderAddress: senderAddress, url,
signCallback: signCallback, {
}); senderAddress: senderAddress,
signCallback: signCallback,
},
feeTable || {},
);
} }
private readonly restClient: RestClient; private readonly restClient: RestClient;
private readonly signingData: SigningData | undefined; private readonly signingData: SigningData | undefined;
private readonly fees: FeeTable;
private get senderAddress(): string { private get senderAddress(): string {
if (!this.signingData) throw new Error("Signing data not set in this client"); if (!this.signingData) throw new Error("Signing data not set in this client");
@@ -125,9 +132,10 @@ export class CosmWasmClient {
return this.signingData.signCallback; return this.signingData.signCallback;
} }
private constructor(url: string, signingData?: SigningData) { private constructor(url: string, signingData: SigningData | undefined, customFees: Partial<FeeTable>) {
this.restClient = new RestClient(url); this.restClient = new RestClient(url);
this.signingData = signingData; this.signingData = signingData;
this.fees = { ...defaultFees, ...customFees };
} }
public async chainId(): Promise<string> { public async chainId(): Promise<string> {
@@ -234,7 +242,7 @@ export class CosmWasmClient {
builder: "", builder: "",
}, },
}; };
const fee = defaultUploadFee; const fee = this.fees.upload;
const { accountNumber, sequence } = await this.getNonce(); const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.chainId(); const chainId = await this.chainId();
const signBytes = makeSignBytes([storeCodeMsg], fee, chainId, memo, accountNumber, sequence); const signBytes = makeSignBytes([storeCodeMsg], fee, chainId, memo, accountNumber, sequence);
@@ -270,7 +278,7 @@ export class CosmWasmClient {
init_funds: transferAmount || [], init_funds: transferAmount || [],
}, },
}; };
const fee = defaultInitFee; const fee = this.fees.init;
const { accountNumber, sequence } = await this.getNonce(); const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.chainId(); const chainId = await this.chainId();
const signBytes = makeSignBytes([instantiateMsg], fee, chainId, memo, accountNumber, sequence); const signBytes = makeSignBytes([instantiateMsg], fee, chainId, memo, accountNumber, sequence);
@@ -304,7 +312,7 @@ export class CosmWasmClient {
sent_funds: transferAmount || [], sent_funds: transferAmount || [],
}, },
}; };
const fee = defaultExecFee; const fee = this.fees.exec;
const { accountNumber, sequence } = await this.getNonce(); const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.chainId(); const chainId = await this.chainId();
const signBytes = makeSignBytes([executeMsg], fee, chainId, memo, accountNumber, sequence); const signBytes = makeSignBytes([executeMsg], fee, chainId, memo, accountNumber, sequence);
@@ -322,6 +330,36 @@ export class CosmWasmClient {
}; };
} }
public async sendTokens(
recipientAddress: string,
transferAmount: readonly Coin[],
memo = "",
): Promise<PostTxResult> {
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
// eslint-disable-next-line @typescript-eslint/camelcase
from_address: this.senderAddress,
// eslint-disable-next-line @typescript-eslint/camelcase
to_address: recipientAddress,
amount: transferAmount,
},
};
const fee = this.fees.send;
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.chainId();
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signedTx = {
msg: [sendMsg],
fee: fee,
memo: memo,
signatures: [signature],
};
return this.postTx(marshalTx(signedTx));
}
/** /**
* Returns the data at the key if present (raw contract dependent storage data) * Returns the data at the key if present (raw contract dependent storage data)
* or null if no data at this key. * or null if no data at this key.
+15 -2
View File
@@ -1,6 +1,12 @@
import { Log } from "./logs"; import { Log } from "./logs";
import { BlockResponse, TxsResponse } from "./restclient"; import { BlockResponse, TxsResponse } from "./restclient";
import { Coin, CosmosSdkAccount, CosmosSdkTx, StdSignature } from "./types"; import { Coin, CosmosSdkAccount, CosmosSdkTx, StdFee, StdSignature } from "./types";
export interface FeeTable {
readonly upload: StdFee;
readonly init: StdFee;
readonly exec: StdFee;
readonly send: StdFee;
}
export interface SigningCallback { export interface SigningCallback {
(signBytes: Uint8Array): Promise<StdSignature>; (signBytes: Uint8Array): Promise<StdSignature>;
} }
@@ -29,9 +35,15 @@ export interface ExecuteResult {
} }
export declare class CosmWasmClient { export declare class CosmWasmClient {
static makeReadOnly(url: string): CosmWasmClient; static makeReadOnly(url: string): CosmWasmClient;
static makeWritable(url: string, senderAddress: string, signCallback: SigningCallback): CosmWasmClient; static makeWritable(
url: string,
senderAddress: string,
signCallback: SigningCallback,
feeTable?: Partial<FeeTable>,
): CosmWasmClient;
private readonly restClient; private readonly restClient;
private readonly signingData; private readonly signingData;
private readonly fees;
private get senderAddress(); private get senderAddress();
private get signCallback(); private get signCallback();
private constructor(); private constructor();
@@ -71,6 +83,7 @@ export declare class CosmWasmClient {
memo?: string, memo?: string,
transferAmount?: readonly Coin[], transferAmount?: readonly Coin[],
): Promise<ExecuteResult>; ): Promise<ExecuteResult>;
sendTokens(recipientAddress: string, transferAmount: readonly Coin[], memo?: string): Promise<PostTxResult>;
/** /**
* Returns the data at the key if present (raw contract dependent storage data) * Returns the data at the key if present (raw contract dependent storage data)
* or null if no data at this key. * or null if no data at this key.