commit
0bd8d5ecf2
16
CHANGELOG.md
16
CHANGELOG.md
@ -3,6 +3,10 @@
|
||||
## 0.23.0 (unreleased)
|
||||
|
||||
- @cosmjs/cosmwasm: Rename `CosmWasmClient.postTx` method to `.broadcastTx`.
|
||||
- @cosmjs/cosmwasm: Rename `FeeTable` type to `CosmWasmFeeTable`.
|
||||
- @cosmjs/cosmwasm: `SigningCosmWasmClient` constructor now takes optional
|
||||
arguments `gasPrice` and `gasLimits` instead of `customFees` for easier
|
||||
customization.
|
||||
- @cosmjs/cosmwasm: Rename `SigningCosmWasmClient.signAndPost` method to
|
||||
`.signAndBroadcast`.
|
||||
- @cosmjs/cosmwasm: Use stricter type `Record<string, unknown>` for smart query,
|
||||
@ -13,7 +17,18 @@
|
||||
- @cosmjs/encoding: Add `limit` parameter to `Bech32.encode` and `.decode`. The
|
||||
new default limit for decoding is infinity (was 90 before). Set it to 90 to
|
||||
create a strict decoder.
|
||||
- @cosmjs/faucet: Environmental variable `FAUCET_FEE` renamed to
|
||||
`FAUCET_GAS_PRICE` and now only accepts one token. Environmental variable
|
||||
`FAUCET_GAS` renamed to `FAUCET_GAS_LIMIT`.
|
||||
- @cosmjs/launchpad: Rename `FeeTable` type to `CosmosFeeTable` and export a new
|
||||
more generic type `FeeTable`.
|
||||
- @cosmjs/launchpad: Add new class `GasPrice`, new helper type `GasLimits` and
|
||||
new helper function `buildFeeTable` for easier handling of gas prices and
|
||||
fees.
|
||||
- @cosmjs/launchpad: Rename `CosmosClient.postTx` method to `.broadcastTx`.
|
||||
- @cosmjs/launchpad: `SigningCosmosClient` constructor now takes optional
|
||||
arguments `gasPrice` and `gasLimits` instead of `customFees` for easier
|
||||
customization.
|
||||
- @cosmjs/launchpad: Rename `SigningCosmosClient.signAndPost` method to
|
||||
`.signAndBroadcast`.
|
||||
- @cosmjs/launchpad: Rename `PostTx`-related types to `BroadcastTxResult`,
|
||||
@ -24,6 +39,7 @@
|
||||
`isSearchBySentFromOrToQuery` and `isSearchByTagsQuery`.
|
||||
- @cosmjs/launchpad: Change type of `TxsResponse.logs` and
|
||||
`BroadcastTxsResponse.logs` to `unknown[]`.
|
||||
- @cosmjs/math: Add `.multiply` method to `Decimal` class.
|
||||
- @cosmjs/tendermint-rpc: Make `BroadcastTxCommitResponse.height` non-optional.
|
||||
- @cosmjs/tendermint-rpc: Change type of `GenesisResponse.appState` to
|
||||
`Record<string, unknown> | undefined`.
|
||||
|
||||
@ -16,8 +16,8 @@ const defaultOptions: Options = {
|
||||
|
||||
const defaultFaucetUrl = "https://faucet.demo-10.cosmwasm.com/credit";
|
||||
|
||||
const buildFeeTable = (feeToken: string, gasPrice: number): FeeTable => {
|
||||
const stdFee = (gas: number, denom: string, price: number) => {
|
||||
const buildFeeTable = (feeToken: string, gasPrice: number): CosmWasmFeeTable => {
|
||||
const calculateFee = (gas: number, denom: string, price: number) => {
|
||||
const amount = Math.floor(gas * price);
|
||||
return {
|
||||
amount: [{ amount: amount.toString(), denom: denom }],
|
||||
@ -26,12 +26,12 @@ const buildFeeTable = (feeToken: string, gasPrice: number): FeeTable => {
|
||||
};
|
||||
|
||||
return {
|
||||
upload: stdFee(1000000, feeToken, gasPrice),
|
||||
init: stdFee(500000, feeToken, gasPrice),
|
||||
migrate: stdFee(500000, feeToken, gasPrice),
|
||||
exec: stdFee(200000, feeToken, gasPrice),
|
||||
send: stdFee(80000, feeToken, gasPrice),
|
||||
changeAdmin: stdFee(80000, feeToken, gasPrice),
|
||||
upload: calculateFee(1000000, feeToken, gasPrice),
|
||||
init: calculateFee(500000, feeToken, gasPrice),
|
||||
migrate: calculateFee(500000, feeToken, gasPrice),
|
||||
exec: calculateFee(200000, feeToken, gasPrice),
|
||||
send: calculateFee(80000, feeToken, gasPrice),
|
||||
changeAdmin: calculateFee(80000, feeToken, gasPrice),
|
||||
};
|
||||
};
|
||||
|
||||
@ -51,11 +51,11 @@ const connect = async (
|
||||
address: string;
|
||||
}> => {
|
||||
const options: Options = { ...defaultOptions, ...opts };
|
||||
const feeTable = buildFeeTable(options.feeToken, options.gasPrice);
|
||||
const gasPrice = GasPrice.fromString(`${options.gasPrice}${options.feeToken}`);
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic);
|
||||
const [{ address }] = await wallet.getAccounts();
|
||||
|
||||
const client = new SigningCosmWasmClient(options.httpUrl, address, wallet, feeTable);
|
||||
const client = new SigningCosmWasmClient(options.httpUrl, address, wallet, gasPrice);
|
||||
return { client, address };
|
||||
};
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ export async function main(originalArgs: readonly string[]): Promise<void> {
|
||||
"SearchTxFilter",
|
||||
// signingcosmwasmclient
|
||||
"ExecuteResult",
|
||||
"FeeTable",
|
||||
"CosmWasmFeeTable",
|
||||
"InstantiateResult",
|
||||
"SigningCosmWasmClient",
|
||||
"UploadMeta",
|
||||
@ -102,6 +102,7 @@ export async function main(originalArgs: readonly string[]): Promise<void> {
|
||||
"BroadcastTxResult",
|
||||
"Coin",
|
||||
"CosmosClient",
|
||||
"GasPrice",
|
||||
"Msg",
|
||||
"MsgDelegate",
|
||||
"MsgSend",
|
||||
|
||||
@ -21,7 +21,7 @@ export {
|
||||
} from "./cosmwasmclient";
|
||||
export {
|
||||
ExecuteResult,
|
||||
FeeTable,
|
||||
CosmWasmFeeTable,
|
||||
InstantiateOptions,
|
||||
InstantiateResult,
|
||||
MigrateResult,
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
AuthExtension,
|
||||
coin,
|
||||
coins,
|
||||
GasPrice,
|
||||
LcdClient,
|
||||
MsgDelegate,
|
||||
Secp256k1Wallet,
|
||||
@ -15,7 +16,7 @@ import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { PrivateCosmWasmClient } from "./cosmwasmclient";
|
||||
import { setupWasmExtension, WasmExtension } from "./lcdapi/wasm";
|
||||
import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient";
|
||||
import { PrivateSigningCosmWasmClient, SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient";
|
||||
import {
|
||||
alice,
|
||||
getHackatom,
|
||||
@ -38,6 +39,200 @@ describe("SigningCosmWasmClient", () => {
|
||||
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet);
|
||||
expect(client).toBeTruthy();
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet, gasPrice);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
upload: {
|
||||
amount: [
|
||||
{
|
||||
amount: "3140000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "1000000",
|
||||
},
|
||||
init: {
|
||||
amount: [
|
||||
{
|
||||
amount: "1570000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "500000",
|
||||
},
|
||||
migrate: {
|
||||
amount: [
|
||||
{
|
||||
amount: "628000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "200000",
|
||||
},
|
||||
exec: {
|
||||
amount: [
|
||||
{
|
||||
amount: "628000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "200000",
|
||||
},
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "251200",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
changeAdmin: {
|
||||
amount: [
|
||||
{
|
||||
amount: "251200",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas limits", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
};
|
||||
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet, undefined, gasLimits);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
upload: {
|
||||
amount: [
|
||||
{
|
||||
amount: "25000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "1000000",
|
||||
},
|
||||
init: {
|
||||
amount: [
|
||||
{
|
||||
amount: "12500",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "500000",
|
||||
},
|
||||
migrate: {
|
||||
amount: [
|
||||
{
|
||||
amount: "5000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "200000",
|
||||
},
|
||||
exec: {
|
||||
amount: [
|
||||
{
|
||||
amount: "5000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "200000",
|
||||
},
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "4000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "160000",
|
||||
},
|
||||
changeAdmin: {
|
||||
amount: [
|
||||
{
|
||||
amount: "2000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price and gas limits", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
};
|
||||
const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet, gasPrice, gasLimits);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmWasmClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
upload: {
|
||||
amount: [
|
||||
{
|
||||
amount: "3140000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "1000000",
|
||||
},
|
||||
init: {
|
||||
amount: [
|
||||
{
|
||||
amount: "1570000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "500000",
|
||||
},
|
||||
migrate: {
|
||||
amount: [
|
||||
{
|
||||
amount: "628000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "200000",
|
||||
},
|
||||
exec: {
|
||||
amount: [
|
||||
{
|
||||
amount: "628000",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "200000",
|
||||
},
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "502400",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "160000",
|
||||
},
|
||||
changeAdmin: {
|
||||
amount: [
|
||||
{
|
||||
amount: "251200",
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getHeight", () => {
|
||||
|
||||
@ -5,8 +5,11 @@ import {
|
||||
BroadcastMode,
|
||||
BroadcastTxFailure,
|
||||
BroadcastTxResult,
|
||||
buildFeeTable,
|
||||
Coin,
|
||||
coins,
|
||||
CosmosFeeTable,
|
||||
GasLimits,
|
||||
GasPrice,
|
||||
isBroadcastTxFailure,
|
||||
makeSignBytes,
|
||||
Msg,
|
||||
@ -31,14 +34,13 @@ import {
|
||||
} from "./msgs";
|
||||
|
||||
/**
|
||||
* Those fees are used by the higher level methods of SigningCosmWasmClient
|
||||
* These fees are used by the higher level methods of SigningCosmWasmClient
|
||||
*/
|
||||
export interface FeeTable {
|
||||
export interface CosmWasmFeeTable extends CosmosFeeTable {
|
||||
readonly upload: StdFee;
|
||||
readonly init: StdFee;
|
||||
readonly exec: StdFee;
|
||||
readonly migrate: StdFee;
|
||||
readonly send: StdFee;
|
||||
/** Paid when setting the contract admin to a new address or unsetting it */
|
||||
readonly changeAdmin: StdFee;
|
||||
}
|
||||
@ -52,31 +54,14 @@ function prepareBuilder(buider: string | undefined): string {
|
||||
}
|
||||
}
|
||||
|
||||
const defaultFees: FeeTable = {
|
||||
upload: {
|
||||
amount: coins(25000, "ucosm"),
|
||||
gas: "1000000", // one million
|
||||
},
|
||||
init: {
|
||||
amount: coins(12500, "ucosm"),
|
||||
gas: "500000", // 500k
|
||||
},
|
||||
migrate: {
|
||||
amount: coins(5000, "ucosm"),
|
||||
gas: "200000", // 200k
|
||||
},
|
||||
exec: {
|
||||
amount: coins(5000, "ucosm"),
|
||||
gas: "200000", // 200k
|
||||
},
|
||||
send: {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "80000", // 80k
|
||||
},
|
||||
changeAdmin: {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "80000", // 80k
|
||||
},
|
||||
const defaultGasPrice = GasPrice.fromString("0.025ucosm");
|
||||
const defaultGasLimits: GasLimits<CosmWasmFeeTable> = {
|
||||
upload: 1000000,
|
||||
init: 500000,
|
||||
migrate: 200000,
|
||||
exec: 200000,
|
||||
send: 80000,
|
||||
changeAdmin: 80000,
|
||||
};
|
||||
|
||||
export interface UploadMeta {
|
||||
@ -158,11 +143,16 @@ function createBroadcastTxErrorMessage(result: BroadcastTxFailure): string {
|
||||
return `Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`;
|
||||
}
|
||||
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningCosmWasmClient {
|
||||
readonly fees: CosmWasmFeeTable;
|
||||
}
|
||||
|
||||
export class SigningCosmWasmClient extends CosmWasmClient {
|
||||
public readonly senderAddress: string;
|
||||
|
||||
private readonly signer: OfflineSigner;
|
||||
private readonly fees: FeeTable;
|
||||
private readonly fees: CosmWasmFeeTable;
|
||||
|
||||
/**
|
||||
* Creates a new client with signing capability to interact with a CosmWasm blockchain. This is the bigger brother of CosmWasmClient.
|
||||
@ -173,22 +163,23 @@ 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 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 gasPrice The price paid per unit of gas
|
||||
* @param gasLimits Custom overrides for gas limits related to specific transaction types
|
||||
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
|
||||
*/
|
||||
public constructor(
|
||||
apiUrl: string,
|
||||
senderAddress: string,
|
||||
signer: OfflineSigner,
|
||||
customFees?: Partial<FeeTable>,
|
||||
gasPrice: GasPrice = defaultGasPrice,
|
||||
gasLimits: Partial<GasLimits<CosmWasmFeeTable>> = {},
|
||||
broadcastMode = BroadcastMode.Block,
|
||||
) {
|
||||
super(apiUrl, broadcastMode);
|
||||
this.anyValidAddress = senderAddress;
|
||||
|
||||
this.senderAddress = senderAddress;
|
||||
this.signer = signer;
|
||||
this.fees = { ...defaultFees, ...(customFees || {}) };
|
||||
this.fees = buildFeeTable<CosmWasmFeeTable>(gasPrice, defaultGasLimits, gasLimits);
|
||||
}
|
||||
|
||||
public async getSequence(address?: string): Promise<GetSequenceResult> {
|
||||
|
||||
2
packages/cosmwasm/types/index.d.ts
vendored
2
packages/cosmwasm/types/index.d.ts
vendored
@ -20,7 +20,7 @@ export {
|
||||
} from "./cosmwasmclient";
|
||||
export {
|
||||
ExecuteResult,
|
||||
FeeTable,
|
||||
CosmWasmFeeTable,
|
||||
InstantiateOptions,
|
||||
InstantiateResult,
|
||||
MigrateResult,
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
import { BroadcastMode, BroadcastTxResult, Coin, Msg, OfflineSigner, StdFee } from "@cosmjs/launchpad";
|
||||
import {
|
||||
BroadcastMode,
|
||||
BroadcastTxResult,
|
||||
Coin,
|
||||
CosmosFeeTable,
|
||||
GasLimits,
|
||||
GasPrice,
|
||||
Msg,
|
||||
OfflineSigner,
|
||||
StdFee,
|
||||
} from "@cosmjs/launchpad";
|
||||
import { Account, CosmWasmClient, GetSequenceResult } from "./cosmwasmclient";
|
||||
import { Log } from "./logs";
|
||||
/**
|
||||
* Those fees are used by the higher level methods of SigningCosmWasmClient
|
||||
* These fees are used by the higher level methods of SigningCosmWasmClient
|
||||
*/
|
||||
export interface FeeTable {
|
||||
export interface CosmWasmFeeTable extends CosmosFeeTable {
|
||||
readonly upload: StdFee;
|
||||
readonly init: StdFee;
|
||||
readonly exec: StdFee;
|
||||
readonly migrate: StdFee;
|
||||
readonly send: StdFee;
|
||||
/** Paid when setting the contract admin to a new address or unsetting it */
|
||||
readonly changeAdmin: StdFee;
|
||||
}
|
||||
@ -81,6 +90,10 @@ export interface ExecuteResult {
|
||||
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
|
||||
readonly transactionHash: string;
|
||||
}
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningCosmWasmClient {
|
||||
readonly fees: CosmWasmFeeTable;
|
||||
}
|
||||
export declare class SigningCosmWasmClient extends CosmWasmClient {
|
||||
readonly senderAddress: string;
|
||||
private readonly signer;
|
||||
@ -94,14 +107,16 @@ 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 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 gasPrice The price paid per unit of gas
|
||||
* @param gasLimits Custom overrides for gas limits related to specific transaction types
|
||||
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
|
||||
*/
|
||||
constructor(
|
||||
apiUrl: string,
|
||||
senderAddress: string,
|
||||
signer: OfflineSigner,
|
||||
customFees?: Partial<FeeTable>,
|
||||
gasPrice?: GasPrice,
|
||||
gasLimits?: Partial<GasLimits<CosmWasmFeeTable>>,
|
||||
broadcastMode?: BroadcastMode,
|
||||
);
|
||||
getSequence(address?: string): Promise<GetSequenceResult>;
|
||||
|
||||
@ -47,9 +47,9 @@ Environment variables
|
||||
FAUCET_CONCURRENCY Number of distributor accounts. Defaults to 5.
|
||||
FAUCET_PORT Port of the webserver. Defaults to 8000.
|
||||
FAUCET_MEMO Memo for send transactions. Defaults to unset.
|
||||
FAUCET_FEE Fee for send transactions as a comma separated list,
|
||||
e.g. "200ushell,30ureef". Defaults to "2000ucosm".
|
||||
FAUCET_GAS Gas for send transactions. Defaults to 80000.
|
||||
FAUCET_GAS_PRICE Gas price for transactions as a comma separated list.
|
||||
Defaults to "0.025ucosm".
|
||||
FAUCET_GAS_LIMIT Gas limit for send transactions. Defaults to 80000.
|
||||
FAUCET_MNEMONIC Secret mnemonic that serves as the base secret for the
|
||||
faucet HD accounts
|
||||
FAUCET_ADDRESS_PREFIX The bech32 address prefix. Defaults to "cosmos".
|
||||
|
||||
@ -20,9 +20,9 @@ Environment variables
|
||||
FAUCET_CONCURRENCY Number of distributor accounts. Defaults to 5.
|
||||
FAUCET_PORT Port of the webserver. Defaults to 8000.
|
||||
FAUCET_MEMO Memo for send transactions. Defaults to unset.
|
||||
FAUCET_FEE Fee for send transactions as a comma separated list,
|
||||
e.g. "200ushell,30ureef". Defaults to "2000ucosm".
|
||||
FAUCET_GAS Gas for send transactions. Defaults to 80000.
|
||||
FAUCET_GAS_PRICE Gas price for transactions as a comma separated list.
|
||||
Defaults to "0.025ucosm".
|
||||
FAUCET_GAS_LIMIT Gas limit for send transactions. Defaults to 80000.
|
||||
FAUCET_MNEMONIC Secret mnemonic that serves as the base secret for the
|
||||
faucet HD accounts
|
||||
FAUCET_ADDRESS_PREFIX The bech32 address prefix. Defaults to "cosmos".
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { Coin, parseCoins } from "@cosmjs/launchpad";
|
||||
import { CosmosFeeTable, GasLimits, GasPrice } from "@cosmjs/launchpad";
|
||||
|
||||
import { TokenConfiguration } from "./tokenmanager";
|
||||
import { parseBankTokens } from "./tokens";
|
||||
|
||||
export const binaryName = "cosmos-faucet";
|
||||
export const memo: string | undefined = process.env.FAUCET_MEMO;
|
||||
export const fee: readonly Coin[] = parseCoins(process.env.FAUCET_FEE || "2000ucosm");
|
||||
export const gas: string = process.env.FAUCET_GAS || "80000";
|
||||
export const gasPrice = GasPrice.fromString(process.env.FAUCET_GAS_PRICE || "0.025ucosm");
|
||||
export const gasLimits: GasLimits<CosmosFeeTable> = {
|
||||
send: parseInt(process.env.FAUCET_GAS_LIMIT || "80000", 10),
|
||||
};
|
||||
export const concurrency: number = Number.parseInt(process.env.FAUCET_CONCURRENCY || "", 10) || 5;
|
||||
export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) || 8000;
|
||||
export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {
|
||||
assertIsBroadcastTxSuccess,
|
||||
CosmosClient,
|
||||
FeeTable,
|
||||
OfflineSigner,
|
||||
SigningCosmosClient,
|
||||
} from "@cosmjs/launchpad";
|
||||
@ -57,17 +56,16 @@ export class Faucet {
|
||||
this.holderAddress = wallets[0][0];
|
||||
this.distributorAddresses = wallets.slice(1).map((pair) => pair[0]);
|
||||
|
||||
const fees: Partial<FeeTable> = {
|
||||
send: {
|
||||
amount: constants.fee,
|
||||
gas: constants.gas,
|
||||
},
|
||||
};
|
||||
|
||||
// we need one client per sender
|
||||
const clients: { [senderAddress: string]: SigningCosmosClient } = {};
|
||||
for (const [senderAddress, wallet] of wallets) {
|
||||
clients[senderAddress] = new SigningCosmosClient(apiUrl, senderAddress, wallet, fees);
|
||||
clients[senderAddress] = new SigningCosmosClient(
|
||||
apiUrl,
|
||||
senderAddress,
|
||||
wallet,
|
||||
constants.gasPrice,
|
||||
constants.gasLimits,
|
||||
);
|
||||
}
|
||||
this.clients = clients;
|
||||
this.logging = logging;
|
||||
|
||||
23
packages/launchpad/src/gas.spec.ts
Normal file
23
packages/launchpad/src/gas.spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
|
||||
import { GasPrice } from "./gas";
|
||||
|
||||
describe("GasPrice", () => {
|
||||
it("can be constructed", () => {
|
||||
const inputs = ["3.14", "3", "0.14"];
|
||||
inputs.forEach((input) => {
|
||||
const gasPrice = new GasPrice(Decimal.fromUserInput(input, 18), "utest");
|
||||
expect(gasPrice.amount.toString()).toEqual(input);
|
||||
expect(gasPrice.denom).toEqual("utest");
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed from a config string", () => {
|
||||
const inputs = ["3.14", "3", "0.14"];
|
||||
inputs.forEach((input) => {
|
||||
const gasPrice = GasPrice.fromString(`${input}utest`);
|
||||
expect(gasPrice.amount.toString()).toEqual(input);
|
||||
expect(gasPrice.denom).toEqual("utest");
|
||||
});
|
||||
});
|
||||
});
|
||||
56
packages/launchpad/src/gas.ts
Normal file
56
packages/launchpad/src/gas.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { Decimal, Uint53 } from "@cosmjs/math";
|
||||
|
||||
import { coins } from "./coins";
|
||||
import { StdFee } from "./types";
|
||||
|
||||
export type FeeTable = Record<string, StdFee>;
|
||||
|
||||
export class GasPrice {
|
||||
public readonly amount: Decimal;
|
||||
public readonly denom: string;
|
||||
|
||||
constructor(amount: Decimal, denom: string) {
|
||||
this.amount = amount;
|
||||
this.denom = denom;
|
||||
}
|
||||
|
||||
public static fromString(gasPrice: string): GasPrice {
|
||||
const matchResult = gasPrice.match(/^(?<amount>.+?)(?<denom>[a-z]+)$/);
|
||||
if (!matchResult) {
|
||||
throw new Error("Invalid gas price string");
|
||||
}
|
||||
const { amount, denom } = matchResult.groups as { readonly amount: string; readonly denom: string };
|
||||
if (denom.length < 3 || denom.length > 127) {
|
||||
throw new Error("Gas price denomination must be between 3 and 127 characters");
|
||||
}
|
||||
const fractionalDigits = 18;
|
||||
const decimalAmount = Decimal.fromUserInput(amount, fractionalDigits);
|
||||
return new GasPrice(decimalAmount, denom);
|
||||
}
|
||||
}
|
||||
|
||||
export type GasLimits<T extends Record<string, StdFee>> = {
|
||||
readonly [key in keyof T]: number;
|
||||
};
|
||||
|
||||
function calculateFee(gasLimit: number, { denom, amount: gasPriceAmount }: GasPrice): StdFee {
|
||||
const amount = Math.ceil(gasPriceAmount.multiply(new Uint53(gasLimit)).toFloatApproximation());
|
||||
return {
|
||||
amount: coins(amount, denom),
|
||||
gas: gasLimit.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildFeeTable<T extends Record<string, StdFee>>(
|
||||
gasPrice: GasPrice,
|
||||
defaultGasLimits: GasLimits<T>,
|
||||
gasLimits: Partial<GasLimits<T>>,
|
||||
): T {
|
||||
return Object.entries(defaultGasLimits).reduce(
|
||||
(feeTable, [type, defaultGasLimit]) => ({
|
||||
...feeTable,
|
||||
[type]: calculateFee(gasLimits[type] || defaultGasLimit, gasPrice),
|
||||
}),
|
||||
{} as T,
|
||||
);
|
||||
}
|
||||
@ -29,6 +29,7 @@ export {
|
||||
isSearchByTagsQuery,
|
||||
} from "./cosmosclient";
|
||||
export { makeSignBytes } from "./encoding";
|
||||
export { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas";
|
||||
export {
|
||||
AuthAccountsResponse,
|
||||
AuthExtension,
|
||||
@ -97,7 +98,7 @@ export {
|
||||
} from "./pubkey";
|
||||
export { findSequenceForSignedTx } from "./sequence";
|
||||
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
|
||||
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
|
||||
export {
|
||||
AccountData,
|
||||
|
||||
@ -33,7 +33,7 @@ describe("DistributionExtension", () => {
|
||||
beforeAll(async () => {
|
||||
if (wasmdEnabled()) {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet, {});
|
||||
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet);
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
const msg: MsgDelegate = {
|
||||
|
||||
@ -31,7 +31,7 @@ describe("GovExtension", () => {
|
||||
beforeAll(async () => {
|
||||
if (wasmdEnabled()) {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet, {});
|
||||
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet);
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
const proposalMsg = {
|
||||
|
||||
@ -34,7 +34,7 @@ describe("StakingExtension", () => {
|
||||
beforeAll(async () => {
|
||||
if (wasmdEnabled()) {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet, {});
|
||||
const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, wallet);
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
{
|
||||
|
||||
@ -3,9 +3,10 @@ import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { Coin, coin, coins } from "./coins";
|
||||
import { assertIsBroadcastTxSuccess, PrivateCosmosClient } from "./cosmosclient";
|
||||
import { GasPrice } from "./gas";
|
||||
import { MsgDelegate } from "./msgs";
|
||||
import { Secp256k1Wallet } from "./secp256k1wallet";
|
||||
import { SigningCosmosClient } from "./signingcosmosclient";
|
||||
import { PrivateSigningCosmosClient, SigningCosmosClient } from "./signingcosmosclient";
|
||||
import { makeRandomAddress, pendingWithoutWasmd, validatorAddress } from "./testutils.spec";
|
||||
|
||||
const httpUrl = "http://localhost:1317";
|
||||
@ -22,10 +23,80 @@ const faucet = {
|
||||
|
||||
describe("SigningCosmosClient", () => {
|
||||
describe("makeReadOnly", () => {
|
||||
it("can be constructed", async () => {
|
||||
it("can be constructed with default fees", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet);
|
||||
expect(client).toBeTruthy();
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmosClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "2000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet, gasPrice);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmosClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "251200", // 3.14 * 80_000
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "80000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas limits", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
};
|
||||
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet, undefined, gasLimits);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmosClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "4000", // 0.025 * 160_000
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "160000",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("can be constructed with custom gas price and gas limits", async () => {
|
||||
const wallet = await Secp256k1Wallet.fromMnemonic(faucet.mnemonic);
|
||||
const gasPrice = GasPrice.fromString("3.14utest");
|
||||
const gasLimits = {
|
||||
send: 160000,
|
||||
};
|
||||
const client = new SigningCosmosClient(httpUrl, faucet.address, wallet, gasPrice, gasLimits);
|
||||
const openedClient = (client as unknown) as PrivateSigningCosmosClient;
|
||||
expect(openedClient.fees).toEqual({
|
||||
send: {
|
||||
amount: [
|
||||
{
|
||||
amount: "502400", // 3.14 * 160_000
|
||||
denom: "utest",
|
||||
},
|
||||
],
|
||||
gas: "160000",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,31 +1,33 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Coin, coins } from "./coins";
|
||||
import { Coin } from "./coins";
|
||||
import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient";
|
||||
import { makeSignBytes } from "./encoding";
|
||||
import { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas";
|
||||
import { BroadcastMode } from "./lcdapi";
|
||||
import { Msg, MsgSend } from "./msgs";
|
||||
import { StdFee, StdTx } from "./types";
|
||||
import { OfflineSigner } from "./wallet";
|
||||
|
||||
/**
|
||||
* Those fees are used by the higher level methods of SigningCosmosClient
|
||||
* These fees are used by the higher level methods of SigningCosmosClient
|
||||
*/
|
||||
export interface FeeTable {
|
||||
export interface CosmosFeeTable extends FeeTable {
|
||||
readonly send: StdFee;
|
||||
}
|
||||
|
||||
const defaultFees: FeeTable = {
|
||||
send: {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "80000", // 80k
|
||||
},
|
||||
};
|
||||
const defaultGasPrice = GasPrice.fromString("0.025ucosm");
|
||||
const defaultGasLimits: GasLimits<CosmosFeeTable> = { send: 80000 };
|
||||
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningCosmosClient {
|
||||
readonly fees: CosmosFeeTable;
|
||||
}
|
||||
|
||||
export class SigningCosmosClient extends CosmosClient {
|
||||
public readonly senderAddress: string;
|
||||
|
||||
private readonly signer: OfflineSigner;
|
||||
private readonly fees: FeeTable;
|
||||
private readonly fees: CosmosFeeTable;
|
||||
|
||||
/**
|
||||
* Creates a new client with signing capability to interact with a Cosmos SDK blockchain. This is the bigger brother of CosmosClient.
|
||||
@ -36,22 +38,23 @@ 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 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 gasPrice The price paid per unit of gas
|
||||
* @param gasLimits Custom overrides for gas limits related to specific transaction types
|
||||
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
|
||||
*/
|
||||
public constructor(
|
||||
apiUrl: string,
|
||||
senderAddress: string,
|
||||
signer: OfflineSigner,
|
||||
customFees?: Partial<FeeTable>,
|
||||
gasPrice: GasPrice = defaultGasPrice,
|
||||
gasLimits: Partial<GasLimits<CosmosFeeTable>> = {},
|
||||
broadcastMode = BroadcastMode.Block,
|
||||
) {
|
||||
super(apiUrl, broadcastMode);
|
||||
this.anyValidAddress = senderAddress;
|
||||
|
||||
this.senderAddress = senderAddress;
|
||||
this.signer = signer;
|
||||
this.fees = { ...defaultFees, ...(customFees || {}) };
|
||||
this.fees = buildFeeTable<CosmosFeeTable>(gasPrice, defaultGasLimits, gasLimits);
|
||||
}
|
||||
|
||||
public async getSequence(address?: string): Promise<GetSequenceResult> {
|
||||
|
||||
17
packages/launchpad/types/gas.d.ts
vendored
Normal file
17
packages/launchpad/types/gas.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
import { Decimal } from "@cosmjs/math";
|
||||
import { StdFee } from "./types";
|
||||
export declare type FeeTable = Record<string, StdFee>;
|
||||
export declare class GasPrice {
|
||||
readonly amount: Decimal;
|
||||
readonly denom: string;
|
||||
constructor(amount: Decimal, denom: string);
|
||||
static fromString(gasPrice: string): GasPrice;
|
||||
}
|
||||
export declare type GasLimits<T extends Record<string, StdFee>> = {
|
||||
readonly [key in keyof T]: number;
|
||||
};
|
||||
export declare function buildFeeTable<T extends Record<string, StdFee>>(
|
||||
gasPrice: GasPrice,
|
||||
defaultGasLimits: GasLimits<T>,
|
||||
gasLimits: Partial<GasLimits<T>>,
|
||||
): T;
|
||||
3
packages/launchpad/types/index.d.ts
vendored
3
packages/launchpad/types/index.d.ts
vendored
@ -27,6 +27,7 @@ export {
|
||||
isSearchByTagsQuery,
|
||||
} from "./cosmosclient";
|
||||
export { makeSignBytes } from "./encoding";
|
||||
export { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas";
|
||||
export {
|
||||
AuthAccountsResponse,
|
||||
AuthExtension,
|
||||
@ -95,7 +96,7 @@ export {
|
||||
} from "./pubkey";
|
||||
export { findSequenceForSignedTx } from "./sequence";
|
||||
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
|
||||
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
|
||||
export {
|
||||
AccountData,
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { Coin } from "./coins";
|
||||
import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient";
|
||||
import { FeeTable, GasLimits, GasPrice } from "./gas";
|
||||
import { BroadcastMode } from "./lcdapi";
|
||||
import { Msg } from "./msgs";
|
||||
import { StdFee } from "./types";
|
||||
import { OfflineSigner } from "./wallet";
|
||||
/**
|
||||
* Those fees are used by the higher level methods of SigningCosmosClient
|
||||
* These fees are used by the higher level methods of SigningCosmosClient
|
||||
*/
|
||||
export interface FeeTable {
|
||||
export interface CosmosFeeTable extends FeeTable {
|
||||
readonly send: StdFee;
|
||||
}
|
||||
/** Use for testing only */
|
||||
export interface PrivateSigningCosmosClient {
|
||||
readonly fees: CosmosFeeTable;
|
||||
}
|
||||
export declare class SigningCosmosClient extends CosmosClient {
|
||||
readonly senderAddress: string;
|
||||
private readonly signer;
|
||||
@ -23,14 +28,16 @@ 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 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 gasPrice The price paid per unit of gas
|
||||
* @param gasLimits Custom overrides for gas limits related to specific transaction types
|
||||
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
|
||||
*/
|
||||
constructor(
|
||||
apiUrl: string,
|
||||
senderAddress: string,
|
||||
signer: OfflineSigner,
|
||||
customFees?: Partial<FeeTable>,
|
||||
gasPrice?: GasPrice,
|
||||
gasLimits?: Partial<GasLimits<CosmosFeeTable>>,
|
||||
broadcastMode?: BroadcastMode,
|
||||
);
|
||||
getSequence(address?: string): Promise<GetSequenceResult>;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Decimal } from "./decimal";
|
||||
import { Uint32, Uint53, Uint64 } from "./integers";
|
||||
|
||||
describe("Decimal", () => {
|
||||
describe("fromAtomics", () => {
|
||||
@ -211,6 +212,87 @@ describe("Decimal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("multiply", () => {
|
||||
it("returns correct values for Uint32", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
expect(zero.multiply(new Uint32(0)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(1)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(2)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint32(4294967295)).toString()).toEqual("0");
|
||||
|
||||
const one = Decimal.fromUserInput("1", 5);
|
||||
expect(one.multiply(new Uint32(0)).toString()).toEqual("0");
|
||||
expect(one.multiply(new Uint32(1)).toString()).toEqual("1");
|
||||
expect(one.multiply(new Uint32(2)).toString()).toEqual("2");
|
||||
expect(one.multiply(new Uint32(4294967295)).toString()).toEqual("4294967295");
|
||||
|
||||
const oneDotFive = Decimal.fromUserInput("1.5", 5);
|
||||
expect(oneDotFive.multiply(new Uint32(0)).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(new Uint32(1)).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(new Uint32(2)).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(new Uint32(4294967295)).toString()).toEqual("6442450942.5");
|
||||
|
||||
// original value remain unchanged
|
||||
expect(zero.toString()).toEqual("0");
|
||||
expect(one.toString()).toEqual("1");
|
||||
expect(oneDotFive.toString()).toEqual("1.5");
|
||||
});
|
||||
|
||||
it("returns correct values for Uint53", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
expect(zero.multiply(new Uint53(0)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint53(1)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint53(2)).toString()).toEqual("0");
|
||||
expect(zero.multiply(new Uint53(9007199254740991)).toString()).toEqual("0");
|
||||
|
||||
const one = Decimal.fromUserInput("1", 5);
|
||||
expect(one.multiply(new Uint53(0)).toString()).toEqual("0");
|
||||
expect(one.multiply(new Uint53(1)).toString()).toEqual("1");
|
||||
expect(one.multiply(new Uint53(2)).toString()).toEqual("2");
|
||||
expect(one.multiply(new Uint53(9007199254740991)).toString()).toEqual("9007199254740991");
|
||||
|
||||
const oneDotFive = Decimal.fromUserInput("1.5", 5);
|
||||
expect(oneDotFive.multiply(new Uint53(0)).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(new Uint53(1)).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(new Uint53(2)).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(new Uint53(9007199254740991)).toString()).toEqual("13510798882111486.5");
|
||||
|
||||
// original value remain unchanged
|
||||
expect(zero.toString()).toEqual("0");
|
||||
expect(one.toString()).toEqual("1");
|
||||
expect(oneDotFive.toString()).toEqual("1.5");
|
||||
});
|
||||
|
||||
it("returns correct values for Uint64", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
expect(zero.multiply(Uint64.fromString("0")).toString()).toEqual("0");
|
||||
expect(zero.multiply(Uint64.fromString("1")).toString()).toEqual("0");
|
||||
expect(zero.multiply(Uint64.fromString("2")).toString()).toEqual("0");
|
||||
expect(zero.multiply(Uint64.fromString("18446744073709551615")).toString()).toEqual("0");
|
||||
|
||||
const one = Decimal.fromUserInput("1", 5);
|
||||
expect(one.multiply(Uint64.fromString("0")).toString()).toEqual("0");
|
||||
expect(one.multiply(Uint64.fromString("1")).toString()).toEqual("1");
|
||||
expect(one.multiply(Uint64.fromString("2")).toString()).toEqual("2");
|
||||
expect(one.multiply(Uint64.fromString("18446744073709551615")).toString()).toEqual(
|
||||
"18446744073709551615",
|
||||
);
|
||||
|
||||
const oneDotFive = Decimal.fromUserInput("1.5", 5);
|
||||
expect(oneDotFive.multiply(Uint64.fromString("0")).toString()).toEqual("0");
|
||||
expect(oneDotFive.multiply(Uint64.fromString("1")).toString()).toEqual("1.5");
|
||||
expect(oneDotFive.multiply(Uint64.fromString("2")).toString()).toEqual("3");
|
||||
expect(oneDotFive.multiply(Uint64.fromString("18446744073709551615")).toString()).toEqual(
|
||||
"27670116110564327422.5",
|
||||
);
|
||||
|
||||
// original value remain unchanged
|
||||
expect(zero.toString()).toEqual("0");
|
||||
expect(one.toString()).toEqual("1");
|
||||
expect(oneDotFive.toString()).toEqual("1.5");
|
||||
});
|
||||
});
|
||||
|
||||
describe("equals", () => {
|
||||
it("returns correct values", () => {
|
||||
const zero = Decimal.fromUserInput("0", 5);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import BN from "bn.js";
|
||||
|
||||
import { Uint32, Uint53, Uint64 } from "./integers";
|
||||
|
||||
// Too large values lead to massive memory usage. Limit to something sensible.
|
||||
// The largest value we need is 18 (Ether).
|
||||
const maxFractionalDigits = 100;
|
||||
@ -124,6 +126,16 @@ export class Decimal {
|
||||
return new Decimal(sum.toString(), this.fractionalDigits);
|
||||
}
|
||||
|
||||
/**
|
||||
* a.multiply(b) returns a*b.
|
||||
*
|
||||
* We only allow multiplication by unsigned integers to avoid rounding errors.
|
||||
*/
|
||||
public multiply(b: Uint32 | Uint53 | Uint64): Decimal {
|
||||
const product = this.data.atomics.mul(new BN(b.toString()));
|
||||
return new Decimal(product.toString(), this.fractionalDigits);
|
||||
}
|
||||
|
||||
public equals(b: Decimal): boolean {
|
||||
return Decimal.compare(this, b) === 0;
|
||||
}
|
||||
|
||||
7
packages/math/types/decimal.d.ts
vendored
7
packages/math/types/decimal.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
import { Uint32, Uint53, Uint64 } from "./integers";
|
||||
/**
|
||||
* A type for arbitrary precision, non-negative decimals.
|
||||
*
|
||||
@ -24,6 +25,12 @@ export declare class Decimal {
|
||||
* Both values need to have the same fractional digits.
|
||||
*/
|
||||
plus(b: Decimal): Decimal;
|
||||
/**
|
||||
* a.multiply(b) returns a*b.
|
||||
*
|
||||
* We only allow multiplication by unsigned integers to avoid rounding errors.
|
||||
*/
|
||||
multiply(b: Uint32 | Uint53 | Uint64): Decimal;
|
||||
equals(b: Decimal): boolean;
|
||||
isLessThan(b: Decimal): boolean;
|
||||
isLessThanOrEqual(b: Decimal): boolean;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user