fix: withdraw/deposit converts from BZZ (10^16 base units) (#66)
* refactor: added toBZZbaseUnit function * feat: added utility for attesting value is BZZ convertible to base units * fix: conversion from 15 to 16 decimal places, added unsafe versions * refactor: withdraw modal uses the safe conversion from BZZ * refactor: added BigNumber and Token class to handle BZZ digits correctly * refactor: extract deposit and withdraw functionality into single modal * test: added tests for Token * chore: removed unused component * chore: addressed PR review, token decimal is now integer 0-18 * chore: added comment to clarify the value restriction on token amount
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { Token } from './Token'
|
||||
|
||||
describe('models/Token', () => {
|
||||
describe('Token.fromDecimal', () => {
|
||||
const values = [
|
||||
{ bzz: '0', baseUnits: '0' },
|
||||
{ bzz: '0.1', baseUnits: BigInt('1000000000000000') },
|
||||
{ bzz: '9.9', baseUnits: BigInt('99000000000000000') },
|
||||
]
|
||||
|
||||
// Test with default 16 decimal places
|
||||
values.forEach(({ bzz, baseUnits }) => {
|
||||
test(`converting ${bzz} => ${baseUnits}`, () => {
|
||||
expect(Token.fromDecimal(bzz).toBigNumber.eq(baseUnits.toString())).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// Test with 0 decimal places
|
||||
values.forEach(({ baseUnits }) => {
|
||||
test(`converting ${baseUnits} => ${baseUnits} with 0 decimals`, () => {
|
||||
expect(Token.fromDecimal(baseUnits, 0).toBigNumber.eq(baseUnits.toString())).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('new Token', () => {
|
||||
const cs = ['0', '1234567890', '99000000000000000']
|
||||
const correctValues = [...cs, ...cs.map(BigInt), ...cs.map(v => new BigNumber(v))]
|
||||
|
||||
correctValues.forEach(v => {
|
||||
test(`New Token ${v} of type ${typeof v}`, () => {
|
||||
const t = new Token(v)
|
||||
|
||||
expect(t.toBigNumber.eq(v.toString())).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { isInteger, makeBigNumber } from '../utils'
|
||||
|
||||
const POSSIBLE_DECIMALS = [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||
type digits = typeof POSSIBLE_DECIMALS[number]
|
||||
|
||||
const BZZ_DECIMALS = 16
|
||||
|
||||
export class Token {
|
||||
private amount: BigNumber // Represented in the base units, so it is always an integer value
|
||||
private readonly decimals: digits
|
||||
|
||||
constructor(amount: BigNumber | string | bigint, decimals: digits = BZZ_DECIMALS) {
|
||||
const a = makeBigNumber(amount)
|
||||
|
||||
if (!isInteger(a) || !POSSIBLE_DECIMALS.includes(decimals)) throw new TypeError('Not a valid token values')
|
||||
|
||||
this.amount = a
|
||||
this.decimals = decimals
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct new Token from a digit representation
|
||||
*
|
||||
* @param amount Amount of a token in the digits (1 token = 10^decimals)
|
||||
* @param decimals Number of decimals for the token (must be integer)
|
||||
*
|
||||
* @throws {TypeError} If the decimals is not an integer or the amount after conversion is not an integer
|
||||
*
|
||||
* @returns new Token
|
||||
*/
|
||||
static fromDecimal(amount: BigNumber | string | bigint, decimals: digits = BZZ_DECIMALS): Token | never {
|
||||
const a = makeBigNumber(amount)
|
||||
|
||||
// No need to do any validation here, it is done when the new token is created
|
||||
const t = a.multipliedBy(new BigNumber(10).pow(decimals))
|
||||
|
||||
return new Token(t, decimals)
|
||||
}
|
||||
|
||||
get toBigInt(): bigint {
|
||||
return BigInt(this.amount.toFixed(0))
|
||||
}
|
||||
|
||||
get toString(): string {
|
||||
return this.amount.toFixed(0)
|
||||
}
|
||||
|
||||
get toBigNumber(): BigNumber {
|
||||
return new BigNumber(this.amount)
|
||||
}
|
||||
|
||||
get toDecimal(): BigNumber {
|
||||
return this.amount.dividedBy(new BigNumber(10).pow(this.decimals))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user