Add fee-free token registration sample
This commit is contained in:
parent
4f0f66cf19
commit
b627e4f9d2
16
README.md
16
README.md
@ -1,6 +1,6 @@
|
||||
# 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
|
||||
|
||||
@ -9,6 +9,14 @@ npm ci
|
||||
scripts/test-network.sh
|
||||
```
|
||||
|
||||
The test loads `src/universa.json`, initializes `Network`, connects to the
|
||||
available topology, and asserts that the network returns `sping=spong`. The
|
||||
alpha package intentionally has no bundled default topology.
|
||||
The test:
|
||||
|
||||
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.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "u-js-sample",
|
||||
"version": "0.1.0",
|
||||
"description": "A minimal universa-core2 network smoke test.",
|
||||
"description": "A minimal universa-core2 network and token registration sample.",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
90
src/index.js
90
src/index.js
@ -5,21 +5,52 @@ import { readFile } from 'node:fs/promises';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
ChangeOwnerPermission,
|
||||
Contract,
|
||||
Network,
|
||||
PrivateKey,
|
||||
RoleSimple,
|
||||
SplitJoinPermission,
|
||||
Topology,
|
||||
TransactionPack,
|
||||
unicryptoReady
|
||||
} from 'universa-core2';
|
||||
|
||||
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
||||
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;
|
||||
|
||||
const topologyData = JSON.parse(await readFile(topologyFile, 'utf8'));
|
||||
const topology = await Topology.load(topologyData);
|
||||
const privateKey = await PrivateKey.generate({ strength: 2048 });
|
||||
const network = new Network(privateKey, {
|
||||
const clientKey = await PrivateKey.unpack(await readFile(clientKeyFile));
|
||||
const network = new Network(clientKey, {
|
||||
topology,
|
||||
directConnection: true
|
||||
});
|
||||
@ -31,3 +62,58 @@ const response = await network.command('sping');
|
||||
assert.equal(response?.sping, 'spong', 'unexpected network state response');
|
||||
|
||||
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
BIN
src/white.private.unikey
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user