@unique-nft/api
Version:
Definitely typed JS API for the Unique Network blockchain
140 lines (116 loc) • 4.13 kB
text/typescript
export interface CoinOptions {
decimals: number
symbol: string
weiSymbol: string
}
export class Coin {
public readonly decimals: number
public readonly symbol: string
public readonly weiSymbol: string
public readonly oneCoinInWei: bigint
// half of decimals wei
// for 18 decimals will be gigawei or gwei
public readonly hwei: bigint
constructor(options: CoinOptions) {
this.symbol = options.symbol
this.weiSymbol = options.weiSymbol
this.decimals = options.decimals
const decimalsBigInt = BigInt(options.decimals)
this.oneCoinInWei = 10n ** decimalsBigInt
this.hwei = 10n ** (decimalsBigInt / 2n)
}
static createUnknown18DecimalsCoin() {
return new Coin({
symbol: 'Unit',
weiSymbol: 'wei',
decimals: 18
})
}
getOptions() {
return {
symbol: this.symbol,
decimals: this.decimals,
weiSymbol: this.weiSymbol,
}
}
getFractionalPart(wei: bigint | string): bigint {
const str = wei.toString()
if (str.length <= this.decimals) return this.bigintFromWei(wei)
return BigInt(str.slice(str.length - this.decimals))
}
getIntegerPart(wei: bigint | string): bigint {
const str = wei.toString()
if (str.length <= this.decimals) return 0n
return BigInt(str.slice(0, str.length - this.decimals))
}
bigintFromWei(wei: string | bigint): bigint {
if (typeof wei === "bigint") {
return wei
} else if (typeof wei === 'string') {
if (!wei.match(/^\d+$/)) {
throw new Error('wei should be string of digits')
}
return BigInt(wei)
} else {
throw new Error('wei should be bigint or string')
}
}
format(wei: string | bigint, decimalPoints: number = 6): string {
return this.formatWithoutCurrency(wei, decimalPoints) + ` ${this.symbol}`
}
formatFullLength(wei: string | bigint): string {
return this.format(wei, this.decimals)
}
// always rounds down insignificant part
// because it's better to show '5 UNQ' on balance 5.09 UNQ instead of '5.1 UNQ'
formatWithoutCurrency(wei: string | bigint, decimalPoints: number = 6): string {
if (typeof decimalPoints !== 'number') {
throw new Error(`decimalPoints should be number`)
}
if (!(Math.round(decimalPoints) === decimalPoints)) {
throw new Error('decimalPoints should be integer number')
}
if (decimalPoints < 0) {
throw new Error(`decimal points should be in range GTE 0`)
}
if (decimalPoints > this.decimals) {
decimalPoints = this.decimals
}
const intPart = this.getIntegerPart(wei)
const fracPart = this.getFractionalPart(wei)
const nonSignificantPartLength = this.decimals - decimalPoints
const fracPartIsTooSmall = fracPart < (10n ** BigInt(nonSignificantPartLength))
if (!decimalPoints || fracPartIsTooSmall) {
return intPart.toString()
}
const fracPartStr = fracPart.toString()
.padStart(18, '0')
.slice(0, decimalPoints)
.replace(/\.?0*$/, '')
return `${intPart}.${fracPartStr}`
}
/**
* @description Dangerously because for the lossless conversion, strings should be used, not numbers.
* Please use the `coinsToWei` method instead.
*/
dangerouslyCoinsToWei(coins: number): bigint {
if (coins < 0) {
throw new Error(`coins to wei: coins should be >= 0, received ${coins}`)
}
const intPart = Math.floor(coins)
if (intPart !== 0) {
return this.coinsToWei(coins.toString())
} else {
return this.coinsToWei((coins + 1).toString()) - this.oneCoinInWei
}
}
coinsToWei(coins: string): bigint {
const parts = coins.trim().match(/^(\d*(\.\d*)?)/)
if (!parts || !parts[1]) {
throw new Error(`coinsToWei: could not parse input: ${coins}`)
}
const [intPart, fracPart] = parts[1].split('.')
const finalFracPart = fracPart ? BigInt(fracPart.padEnd(18, '0')) : 0n
return BigInt(intPart) * this.oneCoinInWei + finalFracPart
}
}