JavaScript API


JavaScript API provides you some convenient tools to work with the keys and the contracts, when implementing web services. It can be used from all compatible browsers; currently, running it from Node.js environment is not guaranteed.

You can download it here.

All methods described are accessible from top level.

Common tools

Most of method export uses binary format (Uint8Array). It can be easily converted to base64 string, for handy storage (for ex. localStorage)

const byteArray = Universa.tools.decode64("abc="); // returns Uint8Array(2) [105, 183]
const b64Encoded = Universa.tools.encode64(byteArray); // returns "abc="

Key pairs

Create a new key pair

Create RSA Private key with given size (2048 bits, 4096 bits)

const privateKey = await Universa.PrivateKey.create(2048); // returns Promise[PrivateKey]

Public key is accessible from PrivateKey instance:

const publicKey = privateKey.publicKey;

Get the key address

You can get short or long address of key:

const shortAddress = publicKey.shortAddress; // Uint8Array
const longAddress = publicKey.longAddress; // Uint8Array

Check if an address matches the key

const isMatching = publicKey.isMatchingAddress(keyAddress); // boolean
const isMatching = privateKey.isMatchingAddress(keyAddress); // boolean
  • publicKey is a PublicKey instance
  • privateKey is PrivateKey instance
  • keyAddress is Uint8Array representing some key address (long or short)

Export/Import key

You can export any of your keys to binary

const privateKeyEncoded = privateKey.toBOSS; // Uint8Array
const publicKeyEncoded = publicKey.toBOSS; // Uint8Array

There are binary loaders for keys:

const privateKey = Universa.PrivateKey.fromBOSS(privateKeyEncoded); // PrivateKey
const publicKey = Universa.PublicKey.fromBOSS(publicKeyEncoded); // PublicKey

Contract

Create a new draft contract

To create simple draft contract provide public key:

const contract = Universa.Contract.create(publicKey);
  • publicKey is a PublicKey instance referring to issuer and owner role

You can create your personal token contract by template:

const tokenContract = Universa.Contract.createToken(
  publicKey, amount, minUnit, tokenFullName, tokenShortName
);
  • publicKey is a PublicKey instance referring to issuer and owner role
  • amount is string amount of units, ex. "1000"
  • minUnit is minimal unit amount for ex. minUnit = 0.01
  • tokenFullName is full name of token, for ex. "My Test Token"
  • tokenShortName is short name of token, for ex. "MTK"

Edit a contract

There are two versions of contract: original and temp. Original version is last approved version, temp - is current draft version to edit. Both accessible as properties: contract.temp, contract.original.

You can edit definition and state sections of temp contract by two methods:

contract.setState(path, value) 
contract.setDefinition(path, value)
  • path is a string path to property, for ex. "data.name"
  • value is any value you want to set (ex. "unit_template")

When done with altering the contract, lock your changes to prepare the contract for signing:

contract.temp.lockData();

Sign a contract

You can sign a contract with any of your private keys:

contract.temp.sign(privateKey);
  • privateKey is a PrivateKey instance to use for signature

Create a new conract revision

If you want to create a new revision of contract, just call:

contract.createRevision()

It will create new revision at temp section of contract (contract.temp).

Set a new owner

You can set a new contract owner (by the owner public key):

contract.setOwner(publicKey)
  • publicKey is PublicKey instance, representing owner's key

Alternately, you can set the owner using the key address (either long or short) of the owner:

const keyAddress = publicKey.shortAddress
contract.setOwner(keyAddress)
  • keyAddress is Uint8Array representing key address

Import/Export

To export a contract, choose temporary or original version of contract:

const tempBinary = contract.temp.currentBinary; // Uint8Array
const originalBinary = contract.original.currentBinary; // Uint8Array

You can import a contract from its binary representation:

const contract = Universa.Contract.fromBOSS(contractBinary);
  • contractBinary is Uint8Array representing packed contract

Also there's Blob option available:

const contractBlob = contract.original.toFile;

Universa Network operations

Connecting to the network

You can connect to the Universa network when you have the authorization key:

const nodeAPI = await Node.connect(privateKey)
  • privateKey is PrivateKey instance to use as authorization key

Check state of contract

State can be retrieved by common state response:

const originalState = contract.checkOriginal(nodeAPI); // to check original version
const tempState = contract.checkTemp(nodeAPI); // to check temp version
  • nodeAPI is NodeAPI instance

The state response is a ItemResult instance with the following structure:

const itemResult = {
  "lockedById": null,
  "extra": {},
  "expiresAt": "2018-08-31T01:08:50.000Z",
  "state": "APPROVED",
  "haveCopy": true,
  "isTestnet": true,
  "errors": [],
  "__type": "ItemResult",
  "createdAt": "2018-05-31T01:08:53.000Z"
};

Get the cost of the contract

Before register new revision you need to know cost of registration:

response = await XChange.getCost(contract.original);
response.cost // Int
response.testnetCompatible // boolean flag, to define you can register this contract in TestNet or only in MainNet

Register a contract providing the payment

If you have U Pack contract, you can register any of your contract with payment.

const uPack = Contract.fromBOSS(U_PACK_BIN);
response = await contract.registerPaid(nodeAPI, uPack, cost, keys, testMode);
  • nodeAPI is NodeAPI instance
  • uPack is Contract instance of payment contract
  • cost is Int cost of contract registration
  • keys is array of PrivateKey, contains owners/creators keys
  • testMode is Boolean flag to register contract in MainNet or TestNet

After send to registration you'll receive ItemResult response, with errors, if there was errors, or current state of registration. You need to check state of contract until it will have some end state (APPROVED or DECLINED)

Cloud operations

Create account at CryptoCloud

To create new account in CryptoCloud simply choose any of your private keys as authorization key:

const cloudApi = await CryptoCloud.connect(APP_TOKEN, privateKey);
  • cloudApi is CryptoCloud API instance for account bound with current privateKey
  • APP_TOKEN is string, containing application token
  • privateKey is PrivateKey instance

Register nick

You can register nick to use in chats or just for nick/password authorization:

const isRegistered = await cloudApi.registerNick("myNickExample", searchable) // boolean
  • cloudApi is CryptoCloud API instance for account bound with current privateKey
  • searchable is boolean, if true - anyone can find you by this nick
  • isRegistered is boolean, false when nick already registered

Unregister nick

Any registered nick can be removed and become available for others:

const isRemoved = await cloudApi.registerNick("myNickExample"); // boolean
  • cloudApi is CryptoCloud API instance for account bound with current privateKey
  • isRemoved is boolean, false when there's no nick registered by user

Set a password for nick/password authorization

await cloudApi.setPassword("myPasswordExample");
  • cloudApi is CryptoCloud API instance for account bound with current privateKey

Login with nick and password

To login with nick and password you should have at least one registered nick and password set.

const cloudApi = CryptoCloud.connectWithPassword(APP_TOKEN, nick, password);
  • cloudApi is CryptoCloud API instance for account bound with current privateKey
  • APP_TOKEN is string, containing application token
  • nick is string, any of your nicks
  • password is string, password should be set by setPassword command after initial login with PrivateKey

Search for party by nick

const party = await cloudApi.getParty({ nick: 'exampleNick' }); // null if party not found
console.log(party.isAccepted); // boolean, shows if party is accepted
console.log(party.canBeAccepted); // boolean, shows if party can be accepted
  • cloudApi is CryptoCloud API instance for account bound with current privateKey
  • party - instance of Party, or null, if party not found

Send a message to the party

const message = await party.newMessage("Hello world!"); // constructs message instance
await message.deliver(); // delivers message to party

Load the messages

You can load the array of messages and sort them by sender (use "from" property for every message):

// Load all messages up to serial #100
const messages = cloudApi.loadItems({ tags: ["comm"], latestSerial: 100 });

// Load all messages before serial #100
const messages = cloudApi.loadItems({ tags: ["comm"], beforeSerial: 100 });

// Load all messages after serial #100
const messages = cloudApi.loadItems({ tags: ["comm"], afterSerial: 100 });

// Load all messages with limit up to 50
const messages = cloudApi.loadItems({ tags: ["comm"], limit: 500 });

Create cloud item

const item = new CryptoCloud.Item(cloudApi);

Set tag to item

item.setTag("myTag");

console.log(item.tag); // "myTag"

Set/update data to item

item.setPrivateData(data);

console.log(item.privateData); // data
  • data is js object

Save changes to cloud

await item.save(); // saves item to cloud

console.log(item.id); // item cloud id (int)

Load existing item from cloud by id

// Create empty item
const item = new CryptoCloud.Item(cloudApi);

// Load item by id
await item.loadId(itemId) // load item data by id

Remove item

await item.destroy() // removes item from cloud

Store Operations

Available products

To get the list of available products:

const products = await XChange.store.getProducts();
  • products is an object with two fields:
    • status: must be "OK"
    • products: is a list of available products

Available Hypertokens

To get the list of available Hypertokens:

const hypertokens = await XChange.store.getConversions();
  • hypertokens is an object with two fields:
    • status: must be "OK"
    • hypertokens: is a list of available hypertokens. Contains names, commissions and min/max amounts to convert

UTNP2UTN conversion details

const response = await XChange.utnp.getCommission();
  • response is an object with few fields:
    • status: must be "OK"
    • commission: amount of tokens that is taken as commission.
    • cost: amount of U to use for conversion
    • max_amount: max amount of tokens to convert
    • min_amount: min amount of tokens to convert

That means to get X amount of UTN, you need to send amount of UTNP equal to (X + X*commission + cost)

UTN2UTNP conversion details

const response = await XChange.utn.getLimits();
  • response is an object with two fields:
    • status: must be "OK"
    • limits:
      • commission: amount of tokens that is taken as commission.
      • cost: amount of U to use for conversion
      • max: max amount to convert
      • min: min amount to convert

Final amount of tokens to receive can be calculated by the same formula above.

Orders

To create an order in the system (for UTN-UTNP orders scroll down a bit):

const order = XChange.Order.create(publicKey, { type, productCode, currency, quantity, amount, returnAddress, promocode, compound });
const response = await order.send();
  • publicKey: your public key (see "Key pair" section). Can be null for UTN_UTNP conversion.
  • type: order type. Currently can be one of the following: "product", "conversion", "UTNP-UTN", "UTN-UTNP"
  • productCode: code of a product, can be found from getProducts call. Required for types: "product"
  • currency: currency to pay the order, can be found from getProducts call. Required for types: "product", "conversion"
  • amount: amount of currency to get. Required for types: "conversion", "UTN-UTNP"
  • quantity: amount of products to order (integer). Required for types: "product"
  • returnAddress: the address to return funds in the case of errors or refund. Required for types: "product", "conversion", "UTN-UTNP"
  • promocode: promocode to use with order (might be discount or bonus). Optional, can be applied for types: "product"
  • compound: Binary representation of UTN contract. Required for types: "UTN-UTNP"

The response depends on the order type. For types "product" and "conversion" the result is orderState field that contains the following fields:

  • orderCode: unique code of your order
  • products: products to buy. Is and object containing productCode, quantity, currency, amount
  • price: expected payment amount. Is an object containing currency and amount
  • paymentAddress: the address the payment should be sent to
  • status: order state, see below
  • statusNote: some explanation of the current state in a form readable by the end user
  • validUntil: the latest instant until then the payment will be accepted in seconds since unix epoch, UTC
  • returnAddress: the return address specified in the order

For the type "UTNP-UTN" the result is utnpImport field that contains the following fields:

  • code: unique code of your order
  • ethereumAddress: address to send UTNP
  • netAmount: amount (without comission) to convert
  • status: order state, see below
  • totalAmount: total amount to pay

UTN-UTNP order creation is more complicated as order needs to get binary UTN contract. So process is:

const order = XChange.Order.createExport({ type, returnAddress, compound });
const response = await order.prepare();
// if no errors, sign and register
const tpack = order.sign(privateKeyBIN);
// save tpack!!! encode64(tpack.toBOSS)
const response = await order.send(); 

The result is conversion field that contains the following fields:

  • id - conversion order id
  • status - order state, see below

To check order state:

const stateResponse = await order.getState();

Return object is the same as in previous call (orderState: OrderStateRecord)

To download paid and executed order:

const downloaded = await order.download();
  • downloaded: an object with two fields:
    • status: must be "OK" if order is executed
    • purchasedProducts: list, each element of it contains productCode and base64 fields. The latter is base64-encoded binary for the product

There is also possibility to store an order in the cloud and get it from there. To store the order:

const itemId = await Universa.OrderToCloud(order, cloudApi);
  • cloudApi: CryptoCloud API instance for account bound with current privateKey
  • order: order to save
  • itemId: number, id of the stored element in cloud

To download saved order from cloud:

const order = await Universa.OrderFromCloud(itemId, cloudApi);
  • cloudApi: CryptoCloud API instance for account bound with current privateKey
  • itemId: id of the stored element in cloud
  • order: downloaded order

Contract cost

There is a special method to get contract cost and testnet compatibility:

const costs = await XChange.getTransactionCost(transactionPack);
  • transactionPack: contract to check, fully packed (as transaction pack) in base64 form

The result contains two fields:

  • costInTu: (integer) amount in TU
  • testnetCompatible: (boolean) shows testnet compatibility