npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@zenotta/zenotta-js

v1.2.14

Published

API module to access the Zenotta network

Downloads

22

Readme

About The Project

This module aims to ease the development of wallet applications that interact with the Zenotta network.

Specific areas of focus include:

  • Key-pair generation through the use of BIP39 mnemonic implementation.
  • Encryption and decryption of key-pairs during operations safely.
  • Transactions and other complex network interactions simplified.

Installation

Install the module to your project:

  • npm

    npm install @zenotta/zenotta-js
  • yarn

    yarn add @zenotta/zenotta-js

Getting Started

  • initNew

    import { ZenottaInstance } from '@zenotta/zenotta-js';
    
    const COMPUTE_HOST = 'example.compute.host.com'
    const INTERCOM_HOST = 'example.intercom.host.com'
    const PASSPHRASE = 'a secure passphrase'
    
    // Create the client object
    const client = new ZenottaInstance();
    
    // Initialize the client with the needed configuration
    const initResult = client.initNew({
        computeHost: COMPUTE_HOST,
        intercomHost: INTERCOM_HOST,
        passPhrase: PASSPHRASE,
      }
    );
    
    const [seedPhrase, masterKeyEncrypted] = initResult.content.initNewResponse;
    
    // Display the seed phrase to the user for safe keeping
    displaySeedPhrase(seedPhrase);
    
    // Store the encrypted master key safely
    saveMasterKey(masterKeyEncrypted);

When the client is initialized without a pre-generated seed phrase or existing master key, the initNew function is used to initialize the client. This type of initialization will in return provide a generated seed phrase as well as its corresponding master key in an encrypted format. It is then up to the developer to store this master key somewhere safe, and to display the seed phrase at least once to the user for safe-keeping. This seed phrase can be used to re-construct lost key-pairs if the need should arise.

Some arguments during the initialization are optional, such as the initOffline- which is used to initialize the client in an offline state.

The computeHost and intercomHost interface elements are used to determine the API endpoints for the Compute node, and Zenotta Intercom server the client is supposed to connect to, respectively.

A user-defined passPhrase needs to be supplied to the client during initialization, as this passphrase will be used to encrypt/decrypt data during operations.

  • initFromMasterKey

    import { ZenottaInstance } from '@zenotta/zenotta-js';
    
    const COMPUTE_HOST = 'example.compute.host.com'
    const INTERCOM_HOST = 'example.intercom.host.com'
    const PASSPHRASE = 'a secure passphrase'
    
    // Create the client object
    const client = new ZenottaInstance();
    
    // Initialize the client with the needed configuration
    client.initFromMasterKey({
        computeHost: COMPUTE_HOST,
        intercomHost: INTERCOM_HOST,
        passPhrase: PASSPHRASE,
      },
      masterKey: getMasterKey()
    );

When an existing master key exists, this type of initialization should be used. This typically occurs when the client has been initialized previously using initNew and the encrypted master key has been stored safely. Using an existing master key will ensure that BIP39 key-pair derivation is consistent. This type of initialization does not have a return value.

  • initFromSeed

    import { ZenottaInstance } from '@zenotta/zenotta-js';
    
    const COMPUTE_HOST = 'example.compute.host.com'
    const INTERCOM_HOST = 'example.intercom.host.com'
    const PASSPHRASE = 'a secure passphrase'
    const SEED_PHRASE = 'a seed phrase that should look like a bunch of random words'
    
    // Create the client object
    const client = new ZenottaInstance();
    
    // Initialize the client with the needed configuration
    const initResult = client.initFromSeed({
        computeHost: COMPUTE_HOST,
        intercomHost: INTERCOM_HOST,
        passPhrase: PASSPHRASE,
      },
      seedPhrase: SEED_PHRASE
    );
    
    const masterKey = initResult.content.initFromSeedResponse;
    
      // Store the encrypted master key safely
    saveMasterKey(masterKeyEncrypted);

Initialization of the client through the use of an existing seed phrase may happen for one of two reasons:

This type of initialization will return the corresponding master key (in an encrypted format) which was created using the provided seed phrase. This master key needs to be stored safely in the same manner as initialization using initNew.

import { ZenottaInstance } from '@zenotta/zenotta-js';

  const COMPUTE_HOST = 'example.compute.host.com'
  const INTERCOM_HOST = 'example.intercom.host.com'
  const PASSPHRASE = 'a secure passphrase'

  // Create the client object
  const client = new ZenottaInstance();

  // Configuration
  const config = {
      computeHost: COMPUTE_HOST,
      intercomHost: INTERCOM_HOST,
      passPhrase: PASSPHRASE,
    };

  // Initialize the client with the needed configuration
  const initResult = client.initNew(config, true);

  const [seedPhrase, masterKeyEncrypted] = initResult.content.initNewResponse;

  // Display the seed phrase to the user for safe keeping
  displaySeedPhrase(seedPhrase);

  // Store the encrypted master key safely
  saveMasterKey(masterKeyEncrypted);

  // Initialize network configuration when required
  const initNetworkResult = client.initNetwork(config);

In some cases, it might be desirable to initialize the client without a network connection. This will allow the client to be used offline, but will inadvertently prevent the client from being able to perform any operations that require interaction with the Zenotta network. The following functions are available with an offline configuration:

  • regenAddresses - Re-generate lost key-pairs from a list of given addresses.
  • getNewKeypair - Generate a new key-pair.
  • getSeedPhrase - Get the existing seed phrase from memory (requires initialization from seed phrase).
  • getMasterKey - Get the existing master key from memory.
  function saveMasterKey(masterKeyEncrypter: IMasterKeyEncrypted): void {
    // Write your I/O operations here to safely store the encrypted master key
    ...
  }

  function getMasterKey(): IMasterKeyEncrypted {
    // Write your I/O operations here to safely retrieve 
    // the encrypted master key
    ...
  }

  function saveKeypair(keyPair: IKeyPairEncrypted): void {
    // Write your I/O operations here to safely store the key pair
    ...
  }

  function getKeypairs(): IKeyPairEncrypted[] {
    // Write your I/O operations here to safely retrieve 
    // the encrypted key pairs
    ...
  }

  function getAllEncryptedTxs(): ICreateTransactionEncrypted[] {
    // Write your I/O operations here to get all encrypted 
    // transactions
    ...
  }

  function saveEncryptedTx(druid: string, encryptedTx: ICreateTransactionEncrypted): void {
    // Write your I/O operations here to save the encrypted transaction
    // with its corresponding DRUID value in a key-value format
    ...
  }

Many methods will either require or return different types of data depending on the operation. It is entirely up to the developer to store and retrieve data safely.

Usage

After the client has been correctly initialized, the methods provided by the client will allow the developer to interact with the Zenotta blockchain network.

Generating and Testing Seed Phrases

  • generateSeedPhrase

    import { generateSeedPhrase } from '@zenotta/zenotta-js';
    
    const seedPhrase = generateSeedPhrase();
  • testSeedPhrase

    import { testSeedPhrase } from '@zenotta/zenotta-js';
    
    const seedPhrase = 'a seed phrase provided by the user that looks like a bunch of random words';
    
    const testResult = testSeedPhrase(seedPhrase);

As seen previously, depending on the scenario, the client can be initialized in a number of different ways. If the client is initialized using initNew, a new seed phrase will be generated automatically. However, in some cases the client needs to be initialized using a pre-generated or provided seed phrase.

The generateSeedPhrase method is provided by the module to generate valid new seed phrases on the fly. This is especially useful in cases where UX design constraints require a valid seed phrase to be generated and displayed to the user before the client is initialized.

Since a seed phrase can be used to reconstruct lost/missing key-pairs, it is customary for the user to be able to provide their own seed phrase should the need arise. To test if the seed phrase is capable of constructing a valid master key, the testSeedPhrase method should be used.

Generating Key-pairs

  • getNewKeypair

    import { ZenottaInstance } from '@zenotta/zenotta-js';
    
    const client = new ZenottaInstance();
    
    // Initialize the client correctly
    ...
    
    const newKeypairResult = client.getNewKeypair();
    
    const newKeypair: IKeypairEncrypted = newKeypairResult.content.newKeypairResponse;
    
    // Save the key-pair safely
    saveKeypair(newKeypair);

Updating the Balance

  • fetchBalance

    import { ZenottaInstance } from '@zenotta/zenotta-js';
    
    const client = new ZenottaInstance();
    
    // Initialize the client correctly
    ...
    
    const allKeypairs = getAllKeypairs();
    
    // We only need the 'address' field of the key-pairs
    const addressList = allKeypairs.map(keypair => keypair.address);
    
    const balanceResult = await client.fetchBalance(addressList);
    
    const balance: IFetchBalanceResponse = balanceResult.content.fetchBalanceResponse;
    {
        "total": {
            "tokens": 0,
            "receipts": {
                "default_drs_tx_hash": 1000,
                "g7d07...6704b": 1000
            }
        },
        "address_list": {
            "a0b08...c02e5": [
                {
                    "out_point": {
                        "t_hash": "g3b13...3353f",
                        "n": 0
                    },
                    "value": {
                        "Receipt": {
                            "amount": 1000,
                            "drs_tx_hash": "default_drs_tx_hash"
                        }
                    }
                },
                {
                    "out_point": {
                        "t_hash": "g7d07...6704b",
                        "n": 0
                    },
                    "value": {
                        "Receipt": {
                            "amount": 1000,
                            "drs_tx_hash": "g7d07...6704b"
                        }
                    }
                },
                {
                    "out_point": {
                        "t_hash": "ga070...4df62",
                        "n": 0
                    },
                    "value": {
                        "Token": 60000
                    }
                }
            ]
        }
    }
    • total: The total balance of all addresses provided
    • address_list: A list of addresses and their previous out-points along with their associated assets

Creating Receipt Assets

Receipts are the NFTs of the Zenotta blockchain, but unlike NFTs don't require you to write any Smart Contracts or complex logic to create.

  • createReceipts

| Argument | Type | Default | Required | Description | |------------------|---------------------|-------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | address | IKeypairEncrypted | | yes | The keypair to generate the address that the Receipt assets will be sent to once generated | | defaultDrsTxHash | boolean | true | no | Setting this to true will create generic Receipts, while setting it to false will generate a genesis transaction hash unique to these Receipts. Use false if you want to create NFTs | | amount | number | 1000 | no | The number of Receipt assets to mint | | metadata | string | null | no | Optional metadata that you can attach to the asset |

import { ZenottaInstance } from '@zenotta/zenotta-js';

const client = new ZenottaInstance();

// Initialize the client correctly
...

// Address / key-pair to assign the `Receipt` assets to
const keyPair = getAllKeypairs()[0];

// Create `Receipt` assets that have the default DRS identifier
const createReceiptResponse = await client.createReceipts(keyPair).content.createReceiptResponse;

<!-- --------------------------------- OR ---------------------------------- -->

// Create `Receipt` assets that have a unique DRS identifier
const createReceiptResponse = await client.createReceipts(keyPair, false).content.createReceiptResponse;

<!-- --------------------------------- ALL ARGUMENTS VERSION ---------------------------------- -->

const createReceiptResponse = await client.createReceipts(
  keyPair,
  false,
  10000,
  "{ 'imageURL': '...', 'description': '...' }"
).content
.createReceiptResponse;

Receipt assets can either be assigned to the default digital rights signature (DRS) or a unique DRS. When assets have different DRS identifiers they are not mutually interchangeable with each other.

{
    "asset": {
        "asset": {
            "Receipt": {
                "amount": 1000,
                "drs_tx_hash": "g7d07...6704b"
            }
        },
        "metadata": null
    },
    "to_address": "a0b08...c02e5",
    "tx_hash": "g7d07...6704b"
}
  • drs_tx_hash: The DRS identifier associated with the created Receipt assets.

Spending Tokens

  • makeTokenPayment

| Argument | Type | Default | Required | Description | |----------------|------------------------|-------------|--------------|----------------------------------------------------------------------------------------------------------------------------------| | paymentAddress | string | | yes | Address to make the token payment to | | paymentAmount | number | | yes | Amount of tokens to pay | | allKeypairs | IKeypairEncrypted [] | | yes | Keypairs to use to make the payment. Must have token balance associated with these keypairs in order to process the transaction | | excessKeypair | IKeypairEncrypted | | yes | Excess keypair to send any remaining balance to |

import { ZenottaInstance } from '@zenotta/zenotta-js';

const client = new ZenottaInstance();

// Initialize the client correctly
...

// All key-pairs
const allKeypairs = getAllKeypairs();

// Change/excess key-pair
const changeKeyPair = allKeypairs[0];

await makeTokenPayment(
      "d0e72...85b46", // Payment address
      10,              // Payment amount
      allKeypairs,     // All key-pairs
      changeKeyPair,   // Excess/change address
  );

NB: The makeTokenPayment method will not check validity of the payment address. It is therefore crucial to ensure a valid payment address is used before the payment gets made.

Spending Receipts

  • makeReceiptPayment

| Argument | Type | Default | Required | Description | |----------------|------------------------|-------------|--------------|----------------------------------------------------------------------------------------------------------------------------------| | paymentAddress | string | | yes | Address to make the token payment to | | paymentAmount | number | | yes | Amount of tokens to pay | | drsTxHash | string | | yes | The genesis transaction hash of the Receipt asset to spend. This is the unique identifier of the Receipt asset | | allKeypairs | IKeypairEncrypted [] | | yes | Keypairs to use to make the payment. Must have token balance associated with these keypairs in order to process the transaction | | excessKeypair | IKeypairEncrypted | | yes | Excess keypair to send any remaining balance to |

import { ZenottaInstance } from '@zenotta/zenotta-js';

const client = new ZenottaInstance();

// Initialize the client correctly
...

// All key-pairs
const keyPairs = getAllKeypairs();

// Change/excess key-pair
const changeKeyPair = keyPairs[0];

// DRS identifier (the default DRS identifier or a unique DRS identifier)
const drsTxHash = "default_drs_tx_hash";

await makeReceiptPayment(
        "d0e72...85b46", // Payment address
        10,              // Payment amount
        drsTxHash,       // DRS identifier
        allKeypairs,     // All key-pairs
        changeKeyPair,   // Excess/change address
    );

NB: The makeReceiptPayment method is similar to the makeTokenPayment in many regards, one of which being the fact that this method will send Receipt assets to a payment address in a unidirectional fashion and does not expect any assets in return. It should not be confused with receipt-based payments!

Making Receipt-based Payments

  • makeRbPayment

| Argument | Type | Default | Required | Description | |----------------|--------------------------------|-------------|--------------|----------------------------------------------| | paymentAddress | string | | yes | Address to make the token payment to | | sendingAsset | IAssetReceipt \| IAssetToken | | yes | The asset to pay | | receivingAsset | IAssetReceipt \| IAssetToken | | yes | The asset to receive | | allKeypairs | IKeypairEncrypted[] | | yes | A list of all existing key-pairs (encrypted) | | receiveAddress | IKeypairEncrypted | | yes | A keypair to assign the "receiving" asset to |

import { ZenottaInstance } from '@zenotta/zenotta-js';

const client = new ZenottaInstance();

// Initialize the client correctly
...

// All key-pairs
const allKeypairs = getAllKeypairs();

// Receive address (which is also the excess/change address)
const receivingAddress = allKeypairs[0];

// The asset we want to send
const sendingAsset = initIAssetToken({"Token": 10});

// The asset we want to receive
const receivingAsset = initIAssetReceipt({
  "Receipt": {
      "amount": 10,
      "drs_tx_hash": "default_drs_tx_hash"
  }});

const paymentResult = await makeRbPayment(
      "18f70...caeda",  // Payment address
      sendingAsset,     // Payment asset
      receivingAsset,   // Receiving asset
      allKeypairs,      // All key-pairs
      receivingAddress, // Receive address
  );

  const { druid, encryptedTx } = paymentResult.content.makeRbPaymentResponse;

  // Save the encrypted transaction along 
  // with it's corresponding DRUID value 
  saveEncryptedTx(druid, encryptedTx);

NB: This type of transaction is a Dual-Double-Entry (DDE) transaction, and requires all parties to reach common consent before their respective transactions are sent to the compute node for processing.

Fetching Pending Receipt-based Payments

  • fetchPendingRbTransactions

    import { ZenottaInstance } from '@zenotta/zenotta-js';
    
    const client = new ZenottaInstance();
    
    // Initialize the client correctly
    ...
    
    // ALl key-pairs
    const allKeypairs = getAllKeypairs();
    
    // All encrypted transactions
    const allEncryptedTxs = getAllEncryptedTxs();
    
    // Fetch pending receipt-based payments
    const pendingRbTransactionsResult = await client.fetchPendingRbTransactions(
          allKeypairs,
          allEncryptedTxs:,
      )
    
    const pendingRbTransactions: IResponseIntercom<IPendingRbTxDetails> = pendingRbTransactionsResult.content.fetchPendingRbResponse;
    
    {
        "2a646...f8b98": {
            "timestamp": 1655117144145,
            "value": {
                "druid": "DRUID0xd0f407436f7f1fc494d7aee22939090e",
                "senderExpectation": {
                    "from": "",
                    "to": "2a646...f8b98",
                    "asset": {
                        "Receipt": {
                            "amount": 1,
                            "drs_tx_hash": "default_drs_tx_hash"
                        }
                    }
                },
                "receiverExpectation": {
                    "from": "295b2...8d4fa",
                    "to": "18f70...caeda",
                    "asset": {
                        "Token": 25200
                    }
                },
                "status": "pending",
                "computeHost": "http://127.0.0.1:3003"
            }
        }
    }

    From this data structure we're able to obtain specific details about the receipt-based payment, such as the unique identifier DRUID0xd0f407436f7f1fc494d7aee22939090e, the status of the transaction status, the timestamp of the transaction timestamp, as well as the address that made the receipt-based payment request- 2a646...f8b98.

    We are also able to see that in this specific request, the sender expects 1 Receipt asset in exchange for 25200 Token assets.

Responding to Pending Receipt-based Payments

  • acceptRbTx and rejectRbTx

    import { ZenottaInstance } from '@zenotta/zenotta-js';
      
    const client = new ZenottaInstance();
      
    // Initialize the client correctly
    ...
      
    // Fetch the pending receipt-based payments from the network
    ...
    const pendingRbTransactions: IFetchPendingRbResponse = pendingRbTransactionsResult.content.fetchPendingRbResponse;
      
    // Fetch all existing key-pairs
    ...
    const allKeypairs = getAllKeypairs();
      
    // Accept a receipt-based payment using its unique `DRUID` identifier
    await client.acceptRbTx('DRUID0xd0f407436f7f1fc494d7aee22939090e', pendingRbTransactions, allKeypairs);
      
    <!-- --------------------------------- OR ---------------------------------- -->
      
    // Reject a receipt-based payment using its unique `DRUID` identifier
    await client.rejectRbTx('DRUID0xd0f407436f7f1fc494d7aee22939090e', pendingRbTransactions, allKeypairs);

    Receipt-based transactions are accepted or rejected by passing their unique DRUID identifier as an argument to the corresponding methods.

Client Response Type

All methods provided by the client have a return value corresponding to the following interface:

export type IClientResponse = {
    id?: string;
    status: 'success' | 'error' | 'pending' | 'unknown';
    reason?: string;
    content?: IContentType;
};

, where each field represents the following:

  • status: A general indication of success or failure for the method being used

  • id: A unique identifier used for network interactions

  • reason: Detailed feedback corresponding to the status field

  • content: Data structures or values returned from the client object