Add fee-free token registration sample

This commit is contained in:
Sergey Chernov 2026-07-31 09:24:05 +04:00
parent 4f0f66cf19
commit b627e4f9d2
4 changed files with 101 additions and 7 deletions

View File

@ -1,6 +1,6 @@
# u-js-sample # u-js-sample
A minimal network smoke test using `universa-core2@2.0.0-alpha.1`. A minimal network and custom-token sample using `universa-core2@2.0.0-alpha.1`.
## Initialize and test ## Initialize and test
@ -9,6 +9,14 @@ npm ci
scripts/test-network.sh scripts/test-network.sh
``` ```
The test loads `src/universa.json`, initializes `Network`, connects to the The test:
available topology, and asserts that the network returns `sping=spong`. The
alpha package intentionally has no bundled default topology. 1. Loads `src/universa.json` and the network-specific whitelisted client key.
2. Initializes `Network`, connects, and asserts that it returns `sping=spong`.
3. Creates and signs a simple `SAMPLE` token contract with split/join and
change-owner permissions.
4. Packs the contract into a `TransactionPack`, registers it without a fee,
and asserts that its final network state is `APPROVED`.
The bundled client key is intended only for this test network. The alpha
package intentionally has no bundled default topology.

View File

@ -1,7 +1,7 @@
{ {
"name": "u-js-sample", "name": "u-js-sample",
"version": "0.1.0", "version": "0.1.0",
"description": "A minimal universa-core2 network smoke test.", "description": "A minimal universa-core2 network and token registration sample.",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@ -5,21 +5,52 @@ import { readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path'; import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { import {
ChangeOwnerPermission,
Contract,
Network, Network,
PrivateKey, PrivateKey,
RoleSimple,
SplitJoinPermission,
Topology, Topology,
TransactionPack,
unicryptoReady unicryptoReady
} from 'universa-core2'; } from 'universa-core2';
const sourceDirectory = dirname(fileURLToPath(import.meta.url)); const sourceDirectory = dirname(fileURLToPath(import.meta.url));
const topologyFile = resolve(sourceDirectory, 'universa.json'); const topologyFile = resolve(sourceDirectory, 'universa.json');
const clientKeyFile = resolve(sourceDirectory, 'white.private.unikey');
const sleep = (milliseconds) => new Promise((resolvePromise) => {
setTimeout(resolvePromise, milliseconds);
});
async function waitForFinalState(network, connection, contractId) {
const deadline = Date.now() + 60_000;
while (Date.now() < deadline) {
const { itemResult } = await network.getState(
contractId.composite3,
connection,
{ timeout: 5_000 }
);
const state = itemResult?.state;
if (state && state !== 'UNDEFINED' && !state.startsWith('PENDING') && !state.startsWith('LOCKED')) {
return itemResult;
}
await sleep(250);
}
throw new Error('contract registration timed out');
}
await unicryptoReady; await unicryptoReady;
const topologyData = JSON.parse(await readFile(topologyFile, 'utf8')); const topologyData = JSON.parse(await readFile(topologyFile, 'utf8'));
const topology = await Topology.load(topologyData); const topology = await Topology.load(topologyData);
const privateKey = await PrivateKey.generate({ strength: 2048 }); const clientKey = await PrivateKey.unpack(await readFile(clientKeyFile));
const network = new Network(privateKey, { const network = new Network(clientKey, {
topology, topology,
directConnection: true directConnection: true
}); });
@ -31,3 +62,58 @@ const response = await network.command('sping');
assert.equal(response?.sping, 'spong', 'unexpected network state response'); assert.equal(response?.sping, 'spong', 'unexpected network state response');
console.log(`Universa network is functioning: ${network.size()} nodes, sping=spong`); console.log(`Universa network is functioning: ${network.size()} nodes, sping=spong`);
// This key owns and signs the sample token. The white key above is used only
// to authenticate the client and authorize fee-free registration.
const tokenKey = await PrivateKey.generate({ strength: 2048 });
const issuer = new RoleSimple('issuer', {
addresses: [tokenKey.publicKey.shortAddress]
});
const splitJoinPermission = SplitJoinPermission.create('owner', {
field_name: 'amount',
min_value: '0',
min_unit: '0.01',
join_match_fields: ['state.origin']
});
const token = Contract.create(issuer, {
definitionData: {
template_name: 'UNIT_CONTRACT',
unit_name: 'Sample Token',
unit_short_name: 'SAMPLE',
description: 'A token created by the universa-core2 sample'
},
stateData: {
amount: '100'
},
permissions: [
ChangeOwnerPermission.create('owner'),
splitJoinPermission
],
expiresAt: '3m',
createdAt: new Date()
});
await token.sign(tokenKey);
const transactionPack = new TransactionPack(token.pack());
const packedTransaction = await transactionPack.pack();
const contractId = await token.hashId();
console.log(`Created and packed SAMPLE token (${packedTransaction.length} bytes)`);
const connection = await network.getRandomConnection();
await network.command(
'approve',
{ packedItem: packedTransaction },
connection,
{ timeout: 10_000 }
);
const itemResult = await waitForFinalState(network, connection, contractId);
assert.equal(
itemResult.state,
'APPROVED',
`token registration failed: ${JSON.stringify(itemResult.errors ?? [])}`
);
console.log(`SAMPLE token is ${itemResult.state}`);

BIN
src/white.private.unikey Normal file

Binary file not shown.