Merge pull request #496 from cosmos/478-stargate-faucet

Support Stargate in faucet
This commit is contained in:
Simon Warta 2020-10-28 19:15:26 +01:00 committed by GitHub
commit a23e9ef686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 507 additions and 176 deletions

View File

@ -36,7 +36,7 @@
"test-node": "node jasmine-testrunner.js",
"test": "yarn build-or-skip && yarn test-node",
"coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet",
"start-dev": "FAUCET_CREDIT_AMOUNT_UCOSM=10000000 FAUCET_CREDIT_AMOUNT_USTAKE=5000000 FAUCET_CONCURRENCY=3 FAUCET_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\" ./bin/cosmos-faucet start \"http://localhost:1317\"",
"start-dev": "FAUCET_CREDIT_AMOUNT_UCOSM=10000000 FAUCET_CREDIT_AMOUNT_USTAKE=100000 FAUCET_CONCURRENCY=3 FAUCET_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\" ./bin/cosmos-faucet start \"http://localhost:26657\"",
"start-coralnet": "FAUCET_ADDRESS_PREFIX=coral FAUCET_TOKENS=\"ushell,ureef\" FAUCET_CREDIT_AMOUNT_USHELL=10000000 FAUCET_CREDIT_AMOUNT_UREEF=2000000 FAUCET_CONCURRENCY=3 FAUCET_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\" ./bin/cosmos-faucet start \"https://lcd.coralnet.cosmwasm.com\""
},
"dependencies": {
@ -44,6 +44,8 @@
"@cosmjs/encoding": "^0.23.1",
"@cosmjs/launchpad": "^0.23.1",
"@cosmjs/math": "^0.23.1",
"@cosmjs/proto-signing": "^0.23.1",
"@cosmjs/stargate": "^0.23.1",
"@cosmjs/utils": "^0.23.1",
"@koa/cors": "^3.0.0",
"koa": "^2.11.0",

View File

@ -1,4 +1,5 @@
import { CosmosClient } from "@cosmjs/launchpad";
import { StargateClient } from "@cosmjs/stargate";
import { Webserver } from "../api/webserver";
import * as constants from "../constants";
@ -15,18 +16,27 @@ export async function start(args: readonly string[]): Promise<void> {
// Connection
const blockchainBaseUrl = args[0];
console.info(`Connecting to blockchain ${blockchainBaseUrl} ...`);
const chainId = await new CosmosClient(blockchainBaseUrl).getChainId();
let chainId;
let stargate = true;
try {
chainId = await (await StargateClient.connect(blockchainBaseUrl)).getChainId();
} catch (_error) {
chainId = await new CosmosClient(blockchainBaseUrl).getChainId();
stargate = false;
}
console.info(`Connected to network: ${chainId}`);
// Faucet
if (!constants.mnemonic) throw new Error("The FAUCET_MNEMONIC environment variable is not set");
const logging = true;
const faucet = await Faucet.make(
blockchainBaseUrl,
constants.addressPrefix,
constants.tokenConfig,
constants.mnemonic,
constants.concurrency,
true,
stargate,
logging,
);
const chainTokens = faucet.configuredTokens();
console.info("Chain tokens:", chainTokens);

View File

@ -1,6 +1,7 @@
import { Random } from "@cosmjs/crypto";
import { Bech32 } from "@cosmjs/encoding";
import { CosmosClient } from "@cosmjs/launchpad";
import { StargateClient } from "@cosmjs/stargate";
import { assert } from "@cosmjs/utils";
import { Faucet } from "./faucet";
@ -12,7 +13,12 @@ function pendingWithoutWasmd(): void {
}
}
const httpUrl = "http://localhost:1317";
function pendingWithoutSimapp(): void {
if (!process.env.SIMAPP_ENABLED) {
return pending("Set SIMAPP_ENABLED to enabled Stargate node-based tests");
}
}
const defaultTokenConfig: TokenConfiguration = {
bankTokens: ["ucosm", "ustake"],
};
@ -26,143 +32,433 @@ 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";
describe("Faucet", () => {
describe("constructor", () => {
it("can be constructed", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
expect(faucet).toBeTruthy();
});
});
describe("launchpad", () => {
const apiUrl = "http://localhost:1317";
const stargate = false;
describe("availableTokens", () => {
it("is empty when no tokens are configured", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, { bankTokens: [] }, faucetMnemonic, 3);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual([]);
describe("constructor", () => {
it("can be constructed", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
expect(faucet).toBeTruthy();
});
});
it("is not empty with default token config", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("send", () => {
it("can send bank token", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
const recipient = makeRandomAddress();
await faucet.send({
amount: {
amount: "23456",
denom: "ucosm",
},
sender: faucet.holderAddress,
recipient: recipient,
describe("availableTokens", () => {
it("is empty when no tokens are configured", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
{ bankTokens: [] },
faucetMnemonic,
3,
stargate,
);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual([]);
});
const readOnlyClient = new CosmosClient(httpUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "23456",
denom: "ucosm",
},
]);
it("is not empty with default token config", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("send", () => {
it("can send bank token", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.send({
amount: {
amount: "23456",
denom: "ucosm",
},
sender: faucet.holderAddress,
recipient: recipient,
});
const readOnlyClient = new CosmosClient(apiUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "23456",
denom: "ucosm",
},
]);
});
});
describe("refill", () => {
it("works", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
await faucet.refill();
const readOnlyClient = new CosmosClient(apiUrl);
const distributorBalance = (await readOnlyClient.getAccount(faucet.distributorAddresses[0]))?.balance;
assert(distributorBalance);
expect(distributorBalance).toEqual([
jasmine.objectContaining({
denom: "ucosm",
}),
jasmine.objectContaining({
denom: "ustake",
}),
]);
expect(Number.parseInt(distributorBalance[0].amount, 10)).toBeGreaterThanOrEqual(80_000000);
expect(Number.parseInt(distributorBalance[1].amount, 10)).toBeGreaterThanOrEqual(80_000000);
});
});
describe("credit", () => {
it("works for fee token", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ucosm");
const readOnlyClient = new CosmosClient(apiUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "10000000",
denom: "ucosm",
},
]);
});
it("works for stake token", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ustake");
const readOnlyClient = new CosmosClient(apiUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "10000000",
denom: "ustake",
},
]);
});
});
describe("configuredTokens", () => {
it("works", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const tickers = faucet.configuredTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("loadAccounts", () => {
it("works", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
1,
stargate,
);
const accounts = await faucet.loadAccounts();
const readOnlyClient = new CosmosClient(apiUrl);
const expectedHolderAccount = await readOnlyClient.getAccount(faucet.holderAddress);
const expectedDistributorAccount = await readOnlyClient.getAccount(faucet.distributorAddresses[0]);
assert(expectedHolderAccount);
assert(expectedDistributorAccount);
expect(accounts).toEqual([
jasmine.objectContaining({
address: expectedHolderAccount.address,
balance: expectedHolderAccount.balance,
}),
jasmine.objectContaining({
address: expectedDistributorAccount.address,
balance: expectedDistributorAccount.balance,
}),
]);
});
});
});
describe("refill", () => {
it("works", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
await faucet.refill();
const readOnlyClient = new CosmosClient(httpUrl);
const distributorBalance = (await readOnlyClient.getAccount(faucet.distributorAddresses[0]))?.balance;
assert(distributorBalance);
expect(distributorBalance).toEqual([
jasmine.objectContaining({
denom: "ucosm",
}),
jasmine.objectContaining({
denom: "ustake",
}),
]);
expect(Number.parseInt(distributorBalance[0].amount, 10)).toBeGreaterThanOrEqual(80_000000);
expect(Number.parseInt(distributorBalance[1].amount, 10)).toBeGreaterThanOrEqual(80_000000);
});
});
describe("stargate", () => {
const apiUrl = "localhost:26657";
const stargate = true;
let originalEnvVariable: string | undefined;
describe("credit", () => {
it("works for fee token", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ucosm");
const readOnlyClient = new CosmosClient(httpUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "10000000",
denom: "ucosm",
},
]);
beforeAll(() => {
originalEnvVariable = process.env.FAUCET_CREDIT_AMOUNT_USTAKE;
process.env.FAUCET_CREDIT_AMOUNT_USTAKE = "100000";
});
it("works for stake token", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ustake");
const readOnlyClient = new CosmosClient(httpUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "10000000",
denom: "ustake",
},
]);
afterAll(() => {
process.env.FAUCET_CREDIT_AMOUNT_USTAKE = originalEnvVariable;
});
});
describe("configuredTokens", () => {
it("works", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 3);
const tickers = faucet.configuredTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
describe("constructor", () => {
it("can be constructed", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
expect(faucet).toBeTruthy();
});
});
});
describe("loadAccounts", () => {
it("works", async () => {
pendingWithoutWasmd();
const faucet = await Faucet.make(httpUrl, defaultAddressPrefix, defaultTokenConfig, faucetMnemonic, 1);
const accounts = await faucet.loadAccounts();
describe("availableTokens", () => {
it("is empty when no tokens are configured", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
{ bankTokens: [] },
faucetMnemonic,
3,
stargate,
);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual([]);
});
const readOnlyClient = new CosmosClient(httpUrl);
const expectedHolderAccount = await readOnlyClient.getAccount(faucet.holderAddress);
const expectedDistributorAccount = await readOnlyClient.getAccount(faucet.distributorAddresses[0]);
assert(expectedHolderAccount);
assert(expectedDistributorAccount);
expect(accounts).toEqual([
jasmine.objectContaining({
address: expectedHolderAccount.address,
balance: expectedHolderAccount.balance,
}),
jasmine.objectContaining({
address: expectedDistributorAccount.address,
balance: expectedDistributorAccount.balance,
}),
]);
it("is not empty with default token config", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("send", () => {
it("can send bank token", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.send({
amount: {
amount: "23456",
denom: "ucosm",
},
sender: faucet.holderAddress,
recipient: recipient,
});
const readOnlyClient = await StargateClient.connect(apiUrl);
const account = await readOnlyClient.getAllBalancesUnverified(recipient);
assert(account);
expect(account).toEqual([
{
amount: "23456",
denom: "ucosm",
},
]);
});
});
describe("refill", () => {
it("works", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
await faucet.refill();
const readOnlyClient = await StargateClient.connect(apiUrl);
const distributorBalance = await readOnlyClient.getAllBalancesUnverified(
faucet.distributorAddresses[0],
);
assert(distributorBalance);
expect(distributorBalance).toEqual([
jasmine.objectContaining({
denom: "ucosm",
}),
jasmine.objectContaining({
denom: "ustake",
}),
]);
expect(Number.parseInt(distributorBalance[0].amount, 10)).toBeGreaterThanOrEqual(80_000000);
expect(Number.parseInt(distributorBalance[1].amount, 10)).toBeGreaterThanOrEqual(800000);
});
});
describe("credit", () => {
it("works for fee token", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ucosm");
const readOnlyClient = await StargateClient.connect(apiUrl);
const balance = await readOnlyClient.getAllBalancesUnverified(recipient);
assert(balance);
expect(balance).toEqual([
{
amount: "10000000",
denom: "ucosm",
},
]);
});
it("works for stake token", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ustake");
const readOnlyClient = await StargateClient.connect(apiUrl);
const balance = await readOnlyClient.getAllBalancesUnverified(recipient);
assert(balance);
expect(balance).toEqual([
{
amount: "100000",
denom: "ustake",
},
]);
});
});
describe("configuredTokens", () => {
it("works", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
3,
stargate,
);
const tickers = faucet.configuredTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("loadAccounts", () => {
it("works", async () => {
pendingWithoutSimapp();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
1,
stargate,
);
const accounts = await faucet.loadAccounts();
const readOnlyClient = await StargateClient.connect(apiUrl);
const expectedHolderBalance = await readOnlyClient.getAllBalancesUnverified(faucet.holderAddress);
const expectedDistributorBalance = await readOnlyClient.getAllBalancesUnverified(
faucet.distributorAddresses[0],
);
assert(expectedHolderBalance);
assert(expectedDistributorBalance);
expect(accounts).toEqual([
jasmine.objectContaining({
address: faucet.holderAddress,
balance: expectedHolderBalance,
}),
jasmine.objectContaining({
address: faucet.distributorAddresses[0],
balance: expectedDistributorBalance,
}),
]);
});
});
});
});

View File

@ -1,14 +1,18 @@
import {
assertIsBroadcastTxSuccess,
assertIsBroadcastTxSuccess as assertIsBroadcastTxSuccessLaunchpad,
CosmosClient,
OfflineSigner,
SigningCosmosClient,
} from "@cosmjs/launchpad";
import {
assertIsBroadcastTxSuccess as assertIsBroadcastTxSuccessStargate,
SigningStargateClient,
StargateClient,
} from "@cosmjs/stargate";
import { sleep } from "@cosmjs/utils";
import * as constants from "./constants";
import { debugAccount, logAccountsState, logSendJob } from "./debugging";
import { createWallets } from "./profile";
import { createClients, createWallets } from "./profile";
import { TokenConfiguration, TokenManager } from "./tokenmanager";
import { MinimalAccount, SendJob } from "./types";
@ -23,10 +27,13 @@ export class Faucet {
config: TokenConfiguration,
mnemonic: string,
numberOfDistributors: number,
stargate = true,
logging = false,
): Promise<Faucet> {
const wallets = await createWallets(mnemonic, addressPrefix, numberOfDistributors, logging);
return new Faucet(apiUrl, addressPrefix, config, wallets, logging);
const wallets = await createWallets(mnemonic, addressPrefix, numberOfDistributors, stargate, logging);
const clients = await createClients(apiUrl, wallets);
const readonlyClient = stargate ? await StargateClient.connect(apiUrl) : new CosmosClient(apiUrl);
return new Faucet(addressPrefix, config, clients, readonlyClient, logging);
}
public readonly addressPrefix: string;
@ -35,39 +42,28 @@ export class Faucet {
private readonly tokenConfig: TokenConfiguration;
private readonly tokenManager: TokenManager;
private readonly readOnlyClient: CosmosClient;
private readonly clients: { [senderAddress: string]: SigningCosmosClient };
private readonly readOnlyClient: CosmosClient | StargateClient;
private readonly clients: { [senderAddress: string]: SigningCosmosClient | SigningStargateClient };
private readonly logging: boolean;
private creditCount = 0;
private constructor(
apiUrl: string,
addressPrefix: string,
config: TokenConfiguration,
wallets: ReadonlyArray<readonly [string, OfflineSigner]>,
clients: ReadonlyArray<readonly [string, SigningCosmosClient | SigningStargateClient]>,
readonlyClient: CosmosClient | StargateClient,
logging = false,
) {
this.addressPrefix = addressPrefix;
this.tokenConfig = config;
this.tokenManager = new TokenManager(config);
this.readOnlyClient = new CosmosClient(apiUrl);
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, wallet] of wallets) {
clients[senderAddress] = new SigningCosmosClient(
apiUrl,
senderAddress,
wallet,
constants.gasPrice,
constants.gasLimits,
);
}
this.clients = clients;
this.readOnlyClient = readonlyClient;
[this.holderAddress, ...this.distributorAddresses] = clients.map(([address]) => address);
this.clients = clients.reduce(
(acc, [senderAddress, client]) => ({ ...acc, [senderAddress]: client }),
{},
);
this.logging = logging;
}
@ -75,9 +71,7 @@ export class Faucet {
* Returns a list of denoms of tokens owned by the the holder and configured in the faucet
*/
public async availableTokens(): Promise<string[]> {
const holderAccount = await this.readOnlyClient.getAccount(this.holderAddress);
const balance = holderAccount ? holderAccount.balance : [];
const { balance } = await this.loadAccount(this.holderAddress);
return balance
.filter((b) => b.amount !== "0")
.map((b) => this.tokenConfig.bankTokens.find((token) => token == b.denom))
@ -89,8 +83,13 @@ export class Faucet {
* Throws an error if the transaction failed.
*/
public async send(job: SendJob): Promise<void> {
const result = await this.clients[job.sender].sendTokens(job.recipient, [job.amount], constants.memo);
assertIsBroadcastTxSuccess(result);
const client = this.clients[job.sender];
if (client instanceof SigningCosmosClient) {
const result = await client.sendTokens(job.recipient, [job.amount], constants.memo);
return assertIsBroadcastTxSuccessLaunchpad(result);
}
const result = await client.sendTokens(job.sender, job.recipient, [job.amount], constants.memo);
assertIsBroadcastTxSuccessStargate(result);
}
/** Use one of the distributor accounts to send tokens to user */
@ -111,24 +110,21 @@ export class Faucet {
return Array.from(this.tokenConfig.bankTokens);
}
public async loadAccount(address: string): Promise<MinimalAccount> {
const balance =
this.readOnlyClient instanceof CosmosClient
? (await this.readOnlyClient.getAccount(address))?.balance ?? []
: await this.readOnlyClient.getAllBalancesUnverified(address);
return {
address: address,
balance: balance,
};
}
public async loadAccounts(): Promise<readonly MinimalAccount[]> {
const addresses = [this.holderAddress, ...this.distributorAddresses];
return Promise.all(
addresses.map(
async (address): Promise<MinimalAccount> => {
const response = await this.readOnlyClient.getAccount(address);
if (response) {
return response;
} else {
return {
address: address,
balance: [],
};
}
},
),
);
return Promise.all(addresses.map(this.loadAccount.bind(this)));
}
public async refill(): Promise<void> {

View File

@ -1,19 +1,25 @@
import { pathToString } from "@cosmjs/crypto";
import { makeCosmoshubPath, OfflineSigner, Secp256k1HdWallet } from "@cosmjs/launchpad";
import { makeCosmoshubPath, Secp256k1HdWallet, SigningCosmosClient } from "@cosmjs/launchpad";
import { DirectSecp256k1Wallet, isOfflineDirectSigner, OfflineSigner } from "@cosmjs/proto-signing";
import { SigningStargateClient } from "@cosmjs/stargate";
import * as constants from "./constants";
export async function createWallets(
mnemonic: string,
addressPrefix: string,
numberOfDistributors: number,
stargate = true,
logging = false,
): Promise<ReadonlyArray<readonly [string, OfflineSigner]>> {
const createWallet = stargate ? DirectSecp256k1Wallet.fromMnemonic : Secp256k1HdWallet.fromMnemonic;
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 wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, path, addressPrefix);
const wallet = await createWallet(mnemonic, path, addressPrefix);
const [{ address }] = await wallet.getAccounts();
if (logging) {
const role = i === 0 ? "token holder " : `distributor ${i}`;
@ -24,3 +30,22 @@ export async function createWallets(
return wallets;
}
export async function createClients(
apiUrl: string,
wallets: ReadonlyArray<readonly [string, OfflineSigner]>,
): Promise<ReadonlyArray<readonly [string, SigningCosmosClient | SigningStargateClient]>> {
// we need one client per sender
return Promise.all(
wallets.map(
async ([senderAddress, wallet]): Promise<
readonly [string, SigningCosmosClient | SigningStargateClient]
> => [
senderAddress,
isOfflineDirectSigner(wallet)
? await SigningStargateClient.connectWithWallet(apiUrl, wallet, {})
: new SigningCosmosClient(apiUrl, senderAddress, wallet, constants.gasPrice, constants.gasLimits),
],
),
);
}

View File

@ -1 +1,2 @@
export { StargateClient } from "./stargateclient";
export { assertIsBroadcastTxSuccess, StargateClient } from "./stargateclient";
export { SigningStargateClient } from "./signingstargateclient";

View File

@ -1 +1,2 @@
export { StargateClient } from "./stargateclient";
export { assertIsBroadcastTxSuccess, StargateClient } from "./stargateclient";
export { SigningStargateClient } from "./signingstargateclient";