// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.
import { getPsks } from "./keys.js";
import { duskToLux } from "./crypto.js";
import { getBalance } from "./balance.js";
import { transfer } from "./contracts/transfer.js";
import { sync, stakeInfo } from "./node.js";
import { stake, unstake, withdrawReward } from "./contracts/stake.js";
import { history } from "./history.js";
import { clearDB } from "./db.js";
import { getNetworkBlockHeight } from "./graphql.js";
import { wasmbytecode, exu } from "../deps.js";
/**
* Construct gas configuration from this class
*
* @class Gas
* @type {Object}
* @property {number} limit The gas limit of the wallet, default is 2900000000
* @property {number} price The gas price of the wallet, default is 1
*/
export class Gas {
static DEFAULT_LIMIT = 2_900_000_000;
static DEFAULT_PRICE = 1;
limit = NaN;
price = NaN;
// Passing null/undefined/0 or negative values will set the default value for price and limit
constructor({ limit, price } = {}) {
this.limit = Math.max(limit, 0) || Gas.DEFAULT_LIMIT;
this.price = Math.max(price, 0) || Gas.DEFAULT_PRICE;
Object.freeze(this);
}
}
/**
* Construct a wallet from this function, this function will load the web assembly into the buffer
* and instantiate it, it will block until the web assembly is loaded
*
* @class Wallet
* @type {Object}
* @property {Uint8Array} seed The seed of the wallet
*/
export class Wallet {
constructor(seed) {
this.wasm = new exu.Module(wasmbytecode);
this.seed = seed;
}
/**
* Get balance
* @param {string} psk - bs58 encoded public spend key of the user we want to
* @returns {Promise<BalanceInfo>} The balance info
* @memberof Wallet
*/
getBalance(psk) {
return getBalance(this.wasm, this.seed, psk);
}
/**
* Get psks for the seed
* @returns {Promise<Array<string>>} psks Psks of the first 21 address for the seed
*/
getPsks() {
return getPsks(this.wasm, this.seed);
}
/**
* Sync the wallet
*
* @param {SyncOptions} [options] Options for the sync
*
* @returns {Promise} promise that resolves after the sync is complete
*/
sync(options = {}) {
return sync(this.wasm, this.seed, options);
}
/**
* Transfer Dusk from sender psk to receiver psk
* @param {string} sender bs58 encoded Psk to send the dusk from
* @param {string} receiver bs68 encoded psk of the address who will receive the Dusk
* @param {number} amount Amount of DUSK to send
* @param {Gas} [gas] Gas settings for the transfer transaction
* @returns {Promise} promise that resolves after the transfer is accepted into blockchain
*/
transfer(sender, receiver, amount, gas = new Gas()) {
return transfer(
this.wasm,
this.seed,
sender,
receiver,
amount,
gas.limit,
gas.price,
);
}
/**
* Stake Dusk from the provided psk, refund to the same psk
* @param {string} staker bs58 encoded Psk to stake from
* @param {number} amount Amount of dusk to stake
* @param {Gas} [gas] Gas settings for the stake transaction
* @returns {Promise} promise that resolves after the stake is accepted into blockchain
*/
async stake(staker, amount, gas = new Gas()) {
const minStake = 1000;
const index = (await this.getPsks()).indexOf(staker);
if (amount < minStake) {
throw new Error(`Stake amount needs to be above a ${minStake} dusk`);
}
if (index === -1) {
throw new Error("Staker psk not found");
}
const bal = await this.getBalance(staker);
if (bal.value < minStake) {
throw new Error(
`Balance needs to be greater than min stake amount of ${minStake}`,
);
} else {
return stake(
this.wasm,
this.seed,
index,
staker,
amount,
gas.limit,
gas.price,
);
}
}
/**
* Fetches the info of the stake if the person has staked
* @param {string} psk bs58 encoded Psk of the staker
* @returns {Promise<StakeInfo>} The stake info
*/
async stakeInfo(psk) {
const index = (await this.getPsks()).indexOf(psk);
if (index < 0) {
throw new Error("Staker psk not found");
}
const info = await stakeInfo(this.wasm, this.seed, index);
if (info.amount) {
info["amount"] = await duskToLux(this.wasm, info.amount);
}
if (info.reward) {
info["reward"] = await duskToLux(this.wasm, info.reward);
}
return info;
}
/**
* Unstake dusk from the provided psk, refund to the same psk
* @param {string} unstaker bs58 encoded psk to unstake from
* @param {Gas} [gas] Gas settings for the unstake transaction
* @returns {Promise} promise that resolves after the unstake is accepted into blockchain
*/
async unstake(unstaker, gas = new Gas()) {
const index = (await this.getPsks()).indexOf(unstaker);
if (index === -1) {
throw new Error("psk not found");
}
return unstake(this.wasm, this.seed, index, unstaker, gas.limit, gas.price);
}
/**
* Withdraw reward
* @param {string} unstaker bs58 encoded psk to unstake from
* @param {Gas} [gas] Gas settings for the withdrawReward transaction
* @returns {Promise} promise that resolves after the unstake is accepted into blockchain
*/
async withdrawReward(psk, gas = new Gas()) {
const index = (await this.getPsks()).indexOf(psk);
return withdrawReward(this.wasm, this.seed, index, gas.limit, gas.price);
}
/**
* Get the history of the wallet
*
* @param {string} psk - bs58 encoded public spend key of the user we want to fetch the history of
* @returns {Array<TxData>} The history of the wallet
*/
history(psk) {
return history(this.wasm, this.seed, psk);
}
/**
* Reset the state indexedb db and localStorage
* @returns {Promise} promise that resolves after the db is reset
*/
reset() {
return clearDB();
}
/**
* Get the network block height
* @returns {Promise<number>} The network block height
*/
static get networkBlockHeight() {
return getNetworkBlockHeight();
}
}