ton3-core
Version:
TON low-level API tools
479 lines (422 loc) • 11.6 kB
text/typescript
import Decimal from 'decimal.js'
interface CoinsOptions {
isNano?: boolean
decimals?: number
}
/**
* Coins/Jettons
*
* @class Address
*/
class Coins {
private value: Decimal
private decimals: number
private multiplier: number
/**
* Creates an instance of {@link Coins}
*
* @param {(Coins | bigint | number | string)} value
* @param {CoinsOptions} [options] - Coins options to configure decimals after comma (0-18) and if value preserved as nanocoins
*
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('100')
*
* new Coins(coins)
* new Coins(BigInt('100'))
* new Coins(100)
* new Coins('100')
* new Coins('100000', { isNano: true, decimals: 3 })
* ```
*/
constructor (value: Coins | bigint | number | string, options?: CoinsOptions) {
const { isNano = false, decimals = 9 } = options || {}
Coins.checkCoinsType(value)
Coins.checkCoinsDecimals(decimals)
const decimal = new Decimal(value.toString())
if (decimal.dp() > decimals) {
throw new Error(`Invalid Coins value, decimals places "${decimal.dp()}" can't be greater than selected "${decimals}"`)
}
this.decimals = decimals
this.multiplier = (1 * 10) ** this.decimals
this.value = !isNano
? decimal.mul(this.multiplier)
: decimal
}
/**
* Add value to instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coinsA = new Coins('10')
* const coinsB = new Coins('9')
*
* coinsA.add(coinsB)
*
* console.log(coinsA.toString()) // '19'
* ```
*
* @return {this} - Current instance reference
*/
public add (coins: Coins): this {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
this.value = this.value.add(coins.value)
return this
}
/**
* Subtract value from instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coinsA = new Coins('10')
* const coinsB = new Coins('9')
*
* coinsA.sub(coinsB)
*
* console.log(coinsA.toString()) // '1'
* ```
*
* @return {this} - Current instance reference
*/
public sub (coins: Coins): this {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
this.value = this.value.sub(coins.value)
return this
}
/**
* Multiplies instance value by value
*
* @param {(bigint | number | string)} value
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10').mul(2)
*
* console.log(coins.toString()) // '20'
* ```
*
* @return {this} - Current instance reference
*/
public mul (value: bigint | number | string): this {
Coins.checkValue(value)
Coins.checkConvertability(value)
const multiplier = value.toString()
this.value = this.value.mul(multiplier)
return this
}
/**
* Divides instance value by value
*
* @param {(bigint | number | string)} value
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10').div(2)
*
* console.log(coins.toString()) // '5'
* ```
*
* @return {this} - Current instance reference
*/
public div (value: bigint | number | string): this {
Coins.checkValue(value)
Coins.checkConvertability(value)
const divider = value.toString()
this.value = this.value.div(divider)
return this
}
/**
* Checks if instance value equal another {@link Coins} instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10')
* const equal = new Coins('10')
* const notEqual = new Coins('11')
*
* console.log(equal.eq(coins), notEqual.eq(coins)) // true, false
* ```
*
* @return {boolean}
*/
public eq (coins: Coins): boolean {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
return this.value.eq(coins.value)
}
/**
* Checks if instance value greater than another {@link Coins} instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10')
* const equal = new Coins('10')
* const greater = new Coins('11')
*
* console.log(equal.gt(coins), greater.gt(coins)) // false, true
* ```
*
* @return {boolean}
*/
public gt (coins: Coins): boolean {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
return this.value.gt(coins.value)
}
/**
* Checks if instance value greater than or equal another {@link Coins} instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10')
* const equal = new Coins('10')
* const greater = new Coins('11')
*
* console.log(equal.gte(coins), greater.gte(coins)) // true, true
* ```
*
* @return {boolean}
*/
public gte (coins: Coins): boolean {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
return this.value.gte(coins.value)
}
/**
* Checks if instance value lesser than another {@link Coins} instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10')
* const equal = new Coins('10')
* const lesser = new Coins('9')
*
* console.log(equal.lt(coins), lesser.lt(coins)) // false, true
* ```
*
* @return {boolean}
*/
public lt (coins: Coins): boolean {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
return this.value.lt(coins.value)
}
/**
* Checks if instance value lesser than or equal another {@link Coins} instance value
*
* @param {Coins} coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('10')
* const equal = new Coins('10')
* const lesser = new Coins('9')
*
* console.log(equal.lte(coins), lesser.lte(coins)) // true, true
* ```
*
* @return {boolean}
*/
public lte (coins: Coins): boolean {
Coins.checkCoins(coins)
Coins.compareCoinsDecimals(this, coins)
return this.value.lte(coins.value)
}
/**
* Checks if instance value is negative
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const zero = new Coins('0')
* const negative = new Coins('-1')
*
* console.log(zero.isNegative(), negative.isNegative()) // false, true
* ```
*
* @return {boolean}
*/
public isNegative (): boolean {
return this.value.isNegative()
}
/**
* Checks if instance value is positive
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const zero = new Coins('0')
* const positive = new Coins('1')
*
* console.log(zero.isPositive(), positive.isPositive()) // true, true
* ```
*
* @return {boolean}
*/
public isPositive (): boolean {
return this.value.isPositive()
}
/**
* Checks if instance value equals zero
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const zero = new Coins('0')
*
* console.log(zero.isZero()) // true
* ```
*
* @return {boolean}
*/
public isZero (): boolean {
return this.value.isZero()
}
/**
* Returns string representation of instance in coins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('100')
*
* console.log(coins.toString()) // '100'
* ```
*
* @return {string}
*/
public toString (): string {
const value = this.value.div(this.multiplier).toFixed(this.decimals)
// Remove all trailing zeroes
const re1 = new RegExp(`\\.0{${this.decimals}}$`)
const re2 = new RegExp('(\\.[0-9]*?[0-9])0+$')
const coins = value.replace(re1, '').replace(re2, '$1')
return coins
}
/**
* Returns string representation of instance in nanocoins
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = new Coins('100')
*
* console.log(coins.toNano()) // '100000000000'
* ```
*
* @return {string}
*/
public toNano (): string {
return this.value.toFixed(0)
}
private static checkCoinsType (value: any): void {
if (Coins.isValid(value) && Coins.isConvertable(value)) return undefined
if (Coins.isCoins(value)) return undefined
throw new Error('Invalid Coins value')
}
private static checkCoinsDecimals (decimals: number): void {
if (decimals < 0 || decimals > 18) {
throw new Error('Invalid decimals value, must be 0-18')
}
}
private static compareCoinsDecimals (a: Coins, b: Coins): void {
if (a.decimals !== b.decimals) {
throw new Error("Can't perform mathematical operation of Coins with different decimals")
}
}
private static checkValue (value: any): void {
if (Coins.isValid(value)) return undefined
throw new Error('Invalid value')
}
private static checkCoins (value: any): void {
if (Coins.isCoins(value)) return undefined
throw new Error('Invalid value')
}
private static checkConvertability (value: any): void {
if (Coins.isConvertable(value)) return undefined
throw new Error('Invalid value')
}
private static isValid (value: any): boolean {
return typeof value === 'string'
|| typeof value === 'number'
|| typeof value === 'bigint'
}
private static isCoins (value: any): boolean {
return value instanceof Coins
}
private static isConvertable (value: any): boolean {
try {
new Decimal(value.toString())
return true
} catch (_err) {
return false
}
}
/**
* Creates instance of Coins from value in nano
*
* @static
* @param {(bigint | number | string)} value - Value in nanocoins
* @param {number} [decimals=9] - Decimals after comma
*
* @example
* ```ts
* import { Coins } from 'ton3-core'
*
* const coins = Coins.fromNano('100000000000', 9)
*
* console.log(coins.toString()) // 100 coins
* ```
*
* @return {Coins}
*/
public static fromNano (value: bigint | number | string, decimals: number = 9): Coins {
Coins.checkCoinsType(value)
Coins.checkCoinsDecimals(decimals)
return new Coins(value, { isNano: true, decimals })
}
}
export {
Coins,
CoinsOptions
}