Move send to Faucet; and test

This commit is contained in:
Simon Warta 2020-02-10 11:31:17 +01:00
parent d378df6b8c
commit 940c41ba64
4 changed files with 109 additions and 52 deletions

View File

@ -13,7 +13,6 @@ import {
identitiesOfFirstWallet,
loadAccounts,
loadTokenTickers,
send,
} from "../../multichainhelpers";
import { setSecretAndCreateIdentities } from "../../profile";
import { SendJob } from "../../types";
@ -138,7 +137,7 @@ export async function start(args: ReadonlyArray<string>): Promise<void> {
tokenTicker: ticker,
};
logSendJob(job);
await send(profile, connection, connector.codec, job);
await faucet.send(profile, job);
} catch (e) {
console.error(e);
throw new HttpError(500, "Sending tokens failed");

View File

@ -1,5 +1,10 @@
import { CosmWasmCodec, CosmWasmConnection, TokenConfiguration } from "@cosmwasm/bcp";
import { CosmosAddressBech32Prefix } from "@cosmwasm/sdk";
import { Address, ChainId, Identity, TokenTicker } from "@iov/bcp";
import { Random } from "@iov/crypto";
import { Bech32 } from "@iov/encoding";
import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol";
import { assert } from "@iov/utils";
import { Faucet } from "./faucet";
@ -13,22 +18,55 @@ const httpUrl = "http://localhost:1317";
const defaultConfig: TokenConfiguration = {
bankTokens: [
{
ticker: "TOKENZ",
name: "The tokenz",
fractionalDigits: 6,
denom: "utokenz",
name: "Fee Token",
ticker: "COSM",
denom: "ucosm",
},
{
ticker: "TRASH",
name: "Trash token",
fractionalDigits: 3,
denom: "mtrash",
fractionalDigits: 6,
name: "Staking Token",
ticker: "STAKE",
denom: "ustake",
},
],
erc20Tokens: [
// {
// contractAddress: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
// fractionalDigits: 5,
// ticker: "ASH",
// name: "Ash Token",
// },
// {
// contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd",
// fractionalDigits: 0,
// ticker: "BASH",
// name: "Bash Token",
// },
],
};
const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix;
const defaultChainId = "cosmos:testing" as ChainId;
const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens);
function makeRandomAddress(): Address {
return Bech32.encode(defaultPrefix, Random.getBytes(20)) as Address;
}
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 faucetPath = HdPaths.cosmos(0);
async function makeProfile(): Promise<{ readonly profile: UserProfile; readonly holder: Identity }> {
const profile = new UserProfile();
const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucetMnemonic));
const holder = await profile.createIdentity(wallet.id, defaultChainId, faucetPath);
return {
profile: profile,
holder: holder,
};
}
describe("Faucet", () => {
describe("constructor", () => {
it("can be constructed", async () => {
@ -39,4 +77,34 @@ describe("Faucet", () => {
connection.disconnect();
});
});
describe("send", () => {
it("can send", async () => {
pendingWithoutCosmos();
const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig);
const { profile, holder } = await makeProfile();
const faucet = new Faucet(defaultConfig, connection, codec);
const recipient = makeRandomAddress();
await faucet.send(profile, {
amount: {
quantity: "23456",
fractionalDigits: 6,
tokenTicker: "COSM" as TokenTicker,
},
tokenTicker: "COSM" as TokenTicker,
sender: holder,
recipient: recipient,
});
const account = await connection.getAccount({ address: recipient });
assert(account);
expect(account.balance).toEqual([
{
quantity: "23456",
fractionalDigits: 6,
tokenTicker: "COSM" as TokenTicker,
},
]);
connection.disconnect();
});
});
});

View File

@ -1,5 +1,11 @@
import { TokenConfiguration } from "@cosmwasm/bcp";
import { BlockchainConnection, TxCodec } from "@iov/bcp";
import {
BlockchainConnection,
isBlockInfoFailed,
isBlockInfoPending,
SendTransaction,
TxCodec,
} from "@iov/bcp";
import { UserProfile } from "@iov/keycontrol";
import { sleep } from "@iov/utils";
@ -9,7 +15,6 @@ import {
identitiesOfFirstWallet,
loadAccounts,
loadTokenTickers,
send,
} from "./multichainhelpers";
import { TokenManager } from "./tokenmanager";
import { SendJob } from "./types";
@ -27,6 +32,30 @@ export class Faucet {
this.codec = codec;
}
/**
* Creates and posts a send transaction. Then waits until the transaction is in a block.
*/
public async send(profile: UserProfile, job: SendJob): Promise<void> {
const sendWithFee = await this.connection.withDefaultFee<SendTransaction>({
kind: "bcp/send",
chainId: this.connection.chainId(),
sender: this.codec.identityToAddress(job.sender),
senderPubkey: job.sender.pubkey,
recipient: job.recipient,
memo: "Make love, not war",
amount: job.amount,
});
const nonce = await this.connection.getNonce({ pubkey: job.sender.pubkey });
const signed = await profile.signTransaction(job.sender, sendWithFee, this.codec, nonce);
const post = await this.connection.postTx(this.codec.bytesToPost(signed));
const blockInfo = await post.blockInfo.waitFor(info => !isBlockInfoPending(info));
if (isBlockInfoFailed(blockInfo)) {
throw new Error(`Sending tokens failed. Code: ${blockInfo.code}, message: ${blockInfo.message}`);
}
}
public async refill(profile: UserProfile): Promise<void> {
console.info(`Connected to network: ${this.connection.chainId()}`);
console.info(`Tokens on network: ${(await loadTokenTickers(this.connection)).join(", ")}`);
@ -63,7 +92,7 @@ export class Faucet {
if (jobs.length > 0) {
for (const job of jobs) {
logSendJob(job);
await send(profile, this.connection, this.codec, job);
await this.send(profile, job);
await sleep(50);
}

View File

@ -1,17 +1,7 @@
import {
Account,
BlockchainConnection,
Identity,
isBlockInfoFailed,
isBlockInfoPending,
SendTransaction,
TokenTicker,
TxCodec,
} from "@iov/bcp";
import { Account, BlockchainConnection, Identity, TokenTicker } from "@iov/bcp";
import { UserProfile } from "@iov/keycontrol";
import { identityToAddress } from "./addresses";
import { SendJob } from "./types";
export function identitiesOfFirstWallet(profile: UserProfile): ReadonlyArray<Identity> {
const wallet = profile.wallets.value[0];
@ -49,35 +39,6 @@ export async function loadTokenTickers(
return (await connection.getAllTokens()).map(token => token.tokenTicker);
}
/**
* Creates and posts a send transaction. Then waits until the transaction is in a block.
*/
export async function send(
profile: UserProfile,
connection: BlockchainConnection,
codec: TxCodec,
job: SendJob,
): Promise<void> {
const sendWithFee = await connection.withDefaultFee<SendTransaction>({
kind: "bcp/send",
chainId: connection.chainId(),
sender: codec.identityToAddress(job.sender),
senderPubkey: job.sender.pubkey,
recipient: job.recipient,
memo: "We ❤️ developers iov.one",
amount: job.amount,
});
const nonce = await connection.getNonce({ pubkey: job.sender.pubkey });
const signed = await profile.signTransaction(job.sender, sendWithFee, codec, nonce);
const post = await connection.postTx(codec.bytesToPost(signed));
const blockInfo = await post.blockInfo.waitFor(info => !isBlockInfoPending(info));
if (isBlockInfoFailed(blockInfo)) {
throw new Error(`Sending tokens failed. Code: ${blockInfo.code}, message: ${blockInfo.message}`);
}
}
export function availableTokensFromHolder(holderAccount: Account): ReadonlyArray<TokenTicker> {
return holderAccount.balance.map(coin => coin.tokenTicker);
}