Merge pull request #496 from cosmos/478-stargate-faucet
Support Stargate in faucet
This commit is contained in:
commit
a23e9ef686
@ -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",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export { StargateClient } from "./stargateclient";
|
||||
export { assertIsBroadcastTxSuccess, StargateClient } from "./stargateclient";
|
||||
export { SigningStargateClient } from "./signingstargateclient";
|
||||
|
||||
3
packages/stargate/types/index.d.ts
vendored
3
packages/stargate/types/index.d.ts
vendored
@ -1 +1,2 @@
|
||||
export { StargateClient } from "./stargateclient";
|
||||
export { assertIsBroadcastTxSuccess, StargateClient } from "./stargateclient";
|
||||
export { SigningStargateClient } from "./signingstargateclient";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user