@hlf-core/coin
hlf-core splits working with tokens into two packages, and this is the same split as everywhere
else: the vocabulary separately, the execution separately. @hlf-core/coin, which is what we are
talking about here, is the platform-neutral models, commands, and events, without any tie to Fabric.
They can be used outside the blockchain too, while execution on the ledger side is handled by
@hlf-core/coin-chaincode.
The primary model is Coin (a token with a balance field), and the owner’s state is described by
CoinAccount. An account has two states: inUse — what can be disposed of — and held — what is
locked under some operation, and the operations add, remove, hold, unhold, nullify move
funds between them. All amounts are stored as strings, and this is fundamental: financial precision
cannot be entrusted to floating-point numbers, otherwise a discrepancy inevitably accumulates on
rounding. CoinBalance extends the account with auditing — emitted, burned, total — so that
a full accounting of what has been issued and burned is visible through it.
The account address deserves a word of its own — CoinAccountObject of the form {ownerUid, id}.
It is an object, not two separate strings, and that is done deliberately: a single owner can have
several accounts for a single coin (say, OWNER and PAYOUT), and in a transfer it is important
to us that the parties cannot be accidentally swapped.
The operations are described by commands inheriting HlfTransportCommandAsync — CoinEmitCommand,
CoinBurnCommand, CoinTransferCommand, CoinHoldCommand, CoinUnholdCommand, CoinNullifyCommand,
and a pair of readonly commands, CoinGetCommand and CoinBalanceGetCommand. Each action has its
own event — CoinEmittedEvent, CoinTransferredEvent (where the recipient is named target, not
to) and so on.
const transfer = new CoinTransferCommand({
object: {ownerUid: 'alice', id: 'OWNER'}, // sender
target: {ownerUid: 'bob', id: 'OWNER'}, // recipient
value: '500',
coinUid: 'coin_issuer_8_MYTOKEN',
});
The account keys are built by CoinAccountUtil on top of StateKey,
and the order of the parts in the key is chosen here so that all of an owner’s accounts for a coin
can be enumerated with a single range query.