diff --git a/src/__integrationtests__/utils.ts b/src/__integrationtests__/utils.ts index 70146e565..ca898778b 100644 --- a/src/__integrationtests__/utils.ts +++ b/src/__integrationtests__/utils.ts @@ -9,9 +9,6 @@ import CType from '../ctype/CType' import { getOwner } from '../ctype/CType.chain' import Identity from '../identity/Identity' -// FIXME: check with weights -// export const GAS = new BN(1_000_000) -export const GAS = new BN(125_000_000) export const MIN_TRANSACTION = new BN(100_000_000) export const ENDOWMENT = MIN_TRANSACTION.mul(new BN(100)) diff --git a/src/balance/Balance.chain.ts b/src/balance/Balance.chain.ts index ba5ca72e5..d426d4e5f 100644 --- a/src/balance/Balance.chain.ts +++ b/src/balance/Balance.chain.ts @@ -15,6 +15,7 @@ import BN from 'bn.js' import { getCached } from '../blockchainApiConnection' import Identity from '../identity/Identity' import IPublicIdentity from '../types/PublicIdentity' +import BalanceUtils from './Balance.utils' /** * Fetches the current balance of the account with [accountAddress]. @@ -89,11 +90,12 @@ export async function listenToBalanceChanges( /** * Transfer Kilt [amount] tokens to [toAccountAddress] using the given [[Identity]]. - * Note that balance amount is in Femto-Kilt (1e-15) and must be translated to Kilt-Coin. + * Note that the value of the transferred currency and the balance amount reported by the chain is in Femto-Kilt (1e-15), and must be translated to Kilt-Coin. * * @param identity Identity to use for token transfer. * @param accountAddressTo Address of the receiver account. - * @param amount Amount of Femto-Kilt (1e-15) to transfer. + * @param amount Amount of Units to transfer. + * @param exponent Magnitude of the amount. Default magnitude of Femto-Kilt. * @returns Promise containing the transaction status. * * @example @@ -117,9 +119,17 @@ export async function listenToBalanceChanges( export async function makeTransfer( identity: Identity, accountAddressTo: IPublicIdentity['address'], - amount: BN + amount: BN, + exponent = -15 ): Promise { const blockchain = await getCached() - const transfer = blockchain.api.tx.balances.transfer(accountAddressTo, amount) + const cleanExponent = + (exponent >= 0 ? 1 : -1) * Math.floor(Math.abs(exponent)) + const transfer = blockchain.api.tx.balances.transfer( + accountAddressTo, + cleanExponent === -15 + ? amount + : BalanceUtils.convertToTxUnit(amount, cleanExponent) + ) return blockchain.submitTx(identity, transfer) } diff --git a/src/balance/Balance.spec.ts b/src/balance/Balance.spec.ts index 299724069..4ed7137b7 100644 --- a/src/balance/Balance.spec.ts +++ b/src/balance/Balance.spec.ts @@ -9,6 +9,7 @@ import { makeTransfer, } from './Balance.chain' import TYPE_REGISTRY from '../blockchainApiConnection/__mocks__/BlockchainQuery' +import BalanceUtils from './Balance.utils' jest.mock('../blockchainApiConnection/BlockchainApiConnection') @@ -16,6 +17,8 @@ const BALANCE = 42 const FEE = 30 describe('Balance', () => { + let alice: Identity + let bob: Identity const blockchainApi = require('../blockchainApiConnection/BlockchainApiConnection') .__mocked_api @@ -41,9 +44,11 @@ describe('Balance', () => { return accountInfo(BALANCE - FEE) } ) - + beforeAll(async () => { + alice = await Identity.buildFromURI('//Alice') + bob = await Identity.buildFromURI('//Bob') + }) it('should listen to balance changes', async (done) => { - const bob = await Identity.buildFromURI('//Bob') const listener = (account: string, balance: BN, change: BN): void => { expect(account).toBe(bob.address) expect(balance.toNumber()).toBe(BALANCE) @@ -58,11 +63,23 @@ describe('Balance', () => { }) it('should make transfer', async () => { - const alice = await Identity.buildFromURI('//Alice') - const bob = await Identity.buildFromURI('//Bob') - const status = await makeTransfer(alice, bob.address, new BN(100)) expect(status).toBeInstanceOf(SubmittableResult) expect(status.isFinalized).toBeTruthy() }) + it('should make transfer of amount with arbitrary exponent', async () => { + const amount = new BN(10) + const exponent = -6 + const expectedAmount = BalanceUtils.convertToTxUnit( + amount, + (exponent >= 0 ? 1 : -1) * Math.floor(Math.abs(exponent)) + ) + const status = await makeTransfer(alice, bob.address, amount, exponent) + expect(blockchainApi.tx.balances.transfer).toHaveBeenCalledWith( + bob.address, + expectedAmount + ) + expect(status).toBeInstanceOf(SubmittableResult) + expect(status.isFinalized).toBeTruthy() + }) }) diff --git a/src/balance/Balance.utils.spec.ts b/src/balance/Balance.utils.spec.ts new file mode 100644 index 000000000..ccaf803b4 --- /dev/null +++ b/src/balance/Balance.utils.spec.ts @@ -0,0 +1,95 @@ +import BN from 'bn.js' +import { + formatKiltBalance, + convertToTxUnit, + asFemtoKilt, + TRANSACTION_FEE, +} from './Balance.utils' + +describe('formatKiltBalance', () => { + const TESTVALUE = new BN('123456789000') + const baseValue = new BN('1') + it('formats the given balance', async () => { + expect(formatKiltBalance(TESTVALUE)).toEqual('123.456 micro KILT') + expect(formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(3))))).toEqual( + '1.000 pico KILT' + ) + expect(formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(6))))).toEqual( + '1.000 nano KILT' + ) + expect(formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(9))))).toEqual( + '1.000 micro KILT' + ) + expect( + formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(12)))) + ).toEqual('1.000 milli KILT') + expect( + formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(15)))) + ).toEqual('1.000 KILT') + expect( + formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(18)))) + ).toEqual('1.000 Kilo KILT') + expect( + formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(21)))) + ).toEqual('1.000 Mega KILT') + expect( + formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(24)))) + ).toEqual('1.000 Giga KILT') + expect( + formatKiltBalance(baseValue.mul(new BN(10).pow(new BN(27)))) + ).toEqual('1.000 Tera KILT') + }) +}) +describe('convertToTxUnit', () => { + it('converts given value with given power to femto KILT', () => { + expect(new BN(convertToTxUnit(new BN(1), -15).toString())).toEqual( + new BN(1) + ) + expect(new BN(convertToTxUnit(new BN(1), -12).toString())).toEqual( + new BN('1000') + ) + expect(new BN(convertToTxUnit(new BN(1), -9).toString())).toEqual( + new BN('1000000') + ) + expect(new BN(convertToTxUnit(new BN(1), -6).toString())).toEqual( + new BN('1000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), -3).toString())).toEqual( + new BN('1000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 0).toString())).toEqual( + new BN('1000000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 3).toString())).toEqual( + new BN('1000000000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 6).toString())).toEqual( + new BN('1000000000000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 9).toString())).toEqual( + new BN('1000000000000000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 12).toString())).toEqual( + new BN('1000000000000000000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 15).toString())).toEqual( + new BN('1000000000000000000000000000000') + ) + expect(new BN(convertToTxUnit(new BN(1), 18).toString())).toEqual( + new BN('1000000000000000000000000000000000') + ) + }) +}) +describe('asFemtoKilt', () => { + it('converts whole KILT to femtoKilt using convertToTxUnit', () => { + expect(new BN(asFemtoKilt(new BN(1000)).toString())).toEqual( + new BN('1000000000000000000') + ) + }) +}) + +describe('TRANSACTION_FEE', () => { + it('equals 125 nano KILT', () => { + expect(formatKiltBalance(TRANSACTION_FEE)).toEqual('125.000 nano KILT') + }) +}) diff --git a/src/balance/Balance.utils.ts b/src/balance/Balance.utils.ts new file mode 100644 index 000000000..5a0c1a750 --- /dev/null +++ b/src/balance/Balance.utils.ts @@ -0,0 +1,36 @@ +/** + * @packageDocumentation + * @module BalanceUtils + * @preferred + */ + +import BN from 'bn.js' +import { formatBalance } from '@polkadot/util' + +export const KILT_COIN = new BN(1) + +export function formatKiltBalance(amount: BN): string { + return formatBalance(amount, { + decimals: 15, + withSiFull: true, + withUnit: 'KILT', + }) +} + +export function convertToTxUnit(balance: BN, power: number): BN { + return new BN(balance).mul(new BN(10).pow(new BN(15 + power))) +} + +export function asFemtoKilt(balance: BN): BN { + return convertToTxUnit(balance, 0) +} + +export const TRANSACTION_FEE = convertToTxUnit(new BN(125), -9) + +export default { + KILT_COIN, + TRANSACTION_FEE, + formatKiltBalance, + asFemtoKilt, + convertToTxUnit, +} diff --git a/src/balance/index.ts b/src/balance/index.ts index 518794606..19e00bd4d 100644 --- a/src/balance/index.ts +++ b/src/balance/index.ts @@ -3,4 +3,8 @@ * @ignore */ -export * from './Balance.chain' +import BalanceUtils from './Balance.utils' +import * as Balance from './Balance.chain' + +export { Balance, BalanceUtils } +export default Balance diff --git a/src/index.ts b/src/index.ts index c147d30e8..936b6d336 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ import { Accumulator, CombinedPresentation } from '@kiltprotocol/portablegabi' import { Attester, Claimer, Verifier } from './actor' import Attestation, { AttestationUtils } from './attestation' import AttestedClaim, { AttestedClaimUtils } from './attestedclaim' -import * as Balance from './balance' +import { Balance, BalanceUtils } from './balance' import Blockchain, { IBlockchainApi } from './blockchain' import * as BlockchainApiConnection from './blockchainApiConnection' import Claim, { ClaimUtils } from './claim' @@ -66,6 +66,7 @@ export { IBlockchainApi, BlockchainApiConnection, Balance, + BalanceUtils, Crypto, Identity, AttesterIdentity,