Factomjs
Javascript library to build applications on the Factom blockchain.
Install / Use
/learn @PaulBernier/FactomjsREADME
Factom.js
Factom.js is a library to easily build applications on the Factom blockchain.
- Provides higher level functionalities than factomd API making it a breeze to create chains or entries, send transactions...
- Provides uniform, consistent and easy to use Factom data structures.
- Provides a range of util functions to manipulate Factom addresses.
Installation
$ npm install --save factom
Documentation and changelog
The complete documentation of the library is available in Markdown format in the docs directory or online in web format at https://factomjs.luciap.ca.
A changelog is available in the file changelog.md.
Web browser
Two versions of factom.js are being bundled for usage in a web browser and can be found in the dist folder. dist/factom.js is a bundle containing all the exposed components of the library. dist/factom-struct.js is a lighter bundle that contains factom structures such as Entry, Chain and Transaction, and all the utily functions related to FCT/EC addresses and fundamental constants. factom-struct bundle doesn't include any component that makes API calls.
import { isValidPrivateAddress, isValidPrivateEcAddress } from 'factom/dist/factom-struct';
import { FactomCli } from 'factom/dist/factom';
Usage
We recommend you have a look at the tutorial on the developer portal of the Factom protocol.
Important: please note than whenever a private (Entry Credit or Factoid) address is needed in this library (typically for signing data), you can either provide a private address or a public address as an argument. If you provide a public address the library will attempt to retrieve the corresponding private address from the wallet. Thus providing private address as arguments allow you to not have to run walletd.
Instantiate FactomCli
const { FactomCli } = require('factom');
// Default factomd connection to localhost:8088
// and walletd connection to localhost:8089
const cli = new FactomCli();
// You can override factomd connection parameters and retry strategy
const cli = new FactomCli({
host: '52.202.51.228',
port: 8088,
path: '/v2', // Path to V2 API. Default to /v2
debugPath: '/debug', // Path to debug API. Default to /debug
user: 'paul', // RPC basic authentication
password: 'pwd',
protocol: 'http', // http or https. Default to http
rejectUnauthorized: true, // Set to false to allow connection to a node with a self-signed certificate
timeout: 5000, // Timeout of individual requests
retry: {
retries: 3,
factor: 2,
minTimeout: 500,
maxTimeout: 2000,
},
});
// You can also override both factomd and walletd options
const cli = new FactomCli({
factomd: {
host: '52.202.51.228',
port: 8088,
},
walletd: {
host: '52.202.51.228',
port: 8089,
},
});
Chains and Entries
Add a Chain
const { Entry, Chain } = require('factom');
const firstEntry = Entry.builder()
.extId('6d79206578742069642031') // If no encoding parameter is passed as 2nd argument, 'hex' is used
.extId('my ext id 1', 'utf8') // Explicit the encoding. Or you can pass directly a Buffer
.content('Initial content', 'utf8')
.build();
const chain = new Chain(firstEntry);
cli.add(chain, 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym');
Add an entry
const { Entry } = require('factom');
const myEntry = Entry.builder()
.chainId('9107a308f91fd7962fecb321fdadeb37e2ca7d456f1d99d24280136c0afd55f2')
.extId('6d79206578742069642031') // If no encoding parameter is passed as 2nd argument, 'hex' is used
.extId('some external ID', 'utf8')
.content('My new content', 'utf8')
.build();
cli.add(myEntry, 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym');
// Add multiples entries.
// The library will limit the number of concurrent Promises to 200 by default to avoid overwhelming the factomd instance.
cli.add([entry1, entry2], 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym');
// The concurrency limit can be customized.
cli.add([entry1, entry2], 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym', {
concurrency: 1,
});
Commit/reveal acknowledgment when submitting chains or entries
Factom protocol uses a commit/reveal commitment scheme. By default when using add the library will sequentially wait for an acknowledgment (ack) of the commit by the network and then wait for the ack of the reveal, both for up to 60s. The library allows you to customize the timeouts of those acks and also to not wait for the acks at all when creating a Chain or an Entry. Please read Factom whitepaper about commit/reveal scheme and what are the potential risks to not wait for network acknowledgments.
// Default behavior waits for both commit and reveal up to 60s
cli.add(myEntry, 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym');
// Change the timeout for commit ack to 120s and the timeout for reveal ack to 20s
cli.add(myEntry, 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym', {
commitTimeout: 120,
revealTimeout: 20,
});
// By providing a negative number the library will not wait for any acknowledgment.
// In below example the wait on reveal ack is disabled (it'll still wait up to 60s on the commit ack).
cli.add(myEntry, 'Es32PjobTxPTd73dohEFRegMFRLv3X5WZ4FXEwNN8kE2pMDfeMym', { revealTimeout: -1 });
Repeated commit
If you commit twice an entry or a chain and that the second time the fees paid are lower or equal to the first commit you are in a 'repeated commit' case and the second commit will be rejected (and you won't be charged for it). If this scenario happens the output of add will have the field repeatedCommit set to true and the field txId will be undefined. See Factom doc.
Getting entries and block context
The simplest and fastest way to retrieve an Entry is to query it by its hash.
cli.getEntry('caf017da212bb68ffee2ba645e1488e5834863743d50972dd3009eab2b93eb42');
You may notice that the entry returned doesn't have a timestamp populated. That's because the timestamp is actually stored in the Entry Block that contains this Entry. The library offers a convenient way to also retrieve that information:
cli.getEntryWithBlockContext('caf017da212bb68ffee2ba645e1488e5834863743d50972dd3009eab2b93eb42');
The entry returned will have its timestamp populated and an additional blockContext field that is structured as follow:
// Note that the timestamps here are in epoch seconds whereas the timestamp attribute of the Entry itself is in timestamp milliseconds
{ entryTimestamp: 1518286500,
directoryBlockHeight: 7042,
entryBlockTimestamp: 1518286440,
entryBlockSequenceNumber: 1,
entryBlockKeyMR: 'a13ac9df4153903f5a07093effe6434bdeb35fea0ff4bd402f323e486bea6ea4' }
Besides getEntryWithBlockContext entries returned by getFirstEntry and getAllEntriesOfChain also have a timestamp and blockContext populated.
Iterating entries of a chain
The FactomCli method getAllEntriesOfChain fetches all the entries of the chain before returning the result: in case of long chains in can be impractical. In some cases you may want to iterate only through a portion of the chain. For those cases FactomCli exposes the method rewindChainWhile(chainId, function predicate(entry) {}, function body(entry) {}) that iterates a chain from the most recent entry to the oldest one as long as the predicate function returns true and that the end of the chain has not been reached. At each iteration the body function is called with the current entry as its argument.
Example 1. Iterating a long chain entry by entry
await cli.rewindChainWhile('caf017da212bb68ffee2ba645e1488e5834863743d50972dd3009eab2b93eb42',
() => true,
entry => // Process entry
Example 2. Searching an entry in a chain
let search = true,
found;
await cli.rewindChainWhile(
'caf017da212bb68ffee2ba645e1488e5834863743d50972dd3009eab2b93eb42',
() => search,
function (entry) {
if (entry.extId[0].toString() === 'Find me!') {
search = false;
found = entry;
}
}
);
Addresses
Factom.js offers a bunch of util functions around FCT/EC addresses and cryptographic keys, namely:
const {
isValidAddress,
addressToKey, // For EC, Es, Fs addresse
addressToRcdHash, // For FA addresses
isValidPublicAddress,
isValidPrivateAddress,
isValidEcAddress,
isValidPublicEcAddress,
isValidPrivateEcAddress,
isValidFctAddress,
isValidPublicFctAddress,
isValidPrivateFctAddress,
getPublicAddress,
keyToPublicFctAddress,
rcdHashToPublicFctAddress,
seedToPrivateFctAddress,
keyToPublicEcAddress,
seedToPrivateEcAddress,
generateRandomFctAddress,
generateRandomEcAddress,
} = require('factom');
Transactions
Transaction object
Note that all the amounts are in factosh
