Navigation

Introduction

Welcome to the API documentation of the new open source Colored Coins Protocol implementation, developed by Colu.
Using this API you can
issue new assets, transfer assets between addresses and query for asset metadata and colored addresses that hold an asset.

The examples in this section use 'bitcoinjs-lib' version '1.5.8'
In order to deploy the right version, please write in your command prompt:
npm install --save bitcoinjs-lib@1.5.8

API Endpoints

Following is a description and documentation of the Coloredcoins API

Issue Asset

issueAsset API Documentation

TITLEBuild a transaction which issues a Colored-Coins asset

VERSION0.2.0

URL/issue

TYPE[POST]

Description

This API call is used to build a transaction to issue assets. it can be funded by a specifc utxo or by any spendable utxo of the issuer address (see financeOutput).
If assets are not specifically sent to other addresses from the issuance then the entire value of the issuance is considered to be sent to the last output of the transaction.

Parameters
FieldTypeDescription
issueAddress String

Base58 public key adress of asset issuer.

amount String

Amount of units for the asset you wish to issue.

divisibility Number

To how many places is the asset devisible (0-8).

fee Number

mining fee for the issuance recommended a minimum of 1000 satoshi.

pubKeyReturnMultisigDust(optional) String

encoded public key if you want to receive the multisig dust if multisig is needed for the metadata.

financeOutput(optional) Object

A vout type object to use in order to finance the issue

    value Number

Value in BTC of the output

    n Number

Output index

    scriptPubKey Object

ScriptPubKey type object

        asm String

Asm for the output

        hex String

Hex for the output

        type String

Bitcoin output type

        reqSigs(optional) Number

Number of required signatures to redeem

        adresses(optional) String[]

Addresses that can redeem

financeOutputTxid(optional) String

Txid containing the vout used for the finance

reissueable Boolean

Decides if the asset can ever be reissued

flags(optional) Object

A flag type object

    injectPreviousOutput(optional) Boolean

If true the input will contain the previous output script to make siging simpler

transfer(optional) Object[]

Array of transfer type objects (transfers amount of the issued asset to specific addresses)

    address(optional) String

Address to transfer assets to

    amount Number

Amount of Asset to transfer

    pubKeys(optional) String

Optional instead of address send the pubkeys as a string array, we create P2SH

    m(optional) String

Number] of signatures required in order to reedem the multisig

metadata(optional) Object

Metadata of the specific utxo from the transaction

    assetId(optional) String

Asset Id

    assetName(optional) String

Asset Name

    assetGenesis(optional) String

Genesis transaction where the asset was created (in case of re issue)

    issuer(optional) String

Name of the issuer

    description(optional) String

description of the asset

    urls(optional) Object[]

Array of URL type objects

        name String

Name of the url

        url String

The url

        mimeType String

Mime type of the data in the url

        dataHash(optional) String

If needed hash of the data that in the url (for proof reasons)

    encryptions(optional) Object[]

Array of encryptSection type objects

        key(optional) String

The json key of the value to encrypt within the user section

        pubKey(optional) String

Public key we will use for the encryption (rsa pubkey)

        format(optional) String

Input format of the key (accepted values: 'pem', 'der')

        type(optional) String

Type and padding of the key (accepted values: 'pkcs1', 'pkcs8')

    userData(optional) JSON

Any arbitrary json data that the previous owner of the output has enterd

rules(optional) Object

Object for the rules of the asset

    version Number

Version of the rule system

    fees Object
        items(optional) Object[]

Array of fee type items

            address String

Address to send the fee

            assetId String

Asset id to send fee (btc if none asset)

            value Number

Value to send for the fee (in satoshi or amount)

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

    expiration(optional) Object

Expiration object used to lown an asseet, when asset expires it moves back to last output

        validUntil Number

When the asset is consider expired

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

    minters(optional) Object[]

Array of mitnter objects, (addresses that can issue the asset)

        address String

Address of the minter

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type (if the minter can add minters)

    holders(optional) Object[]

Array of holder type objects, they specify in what addresses the asset is considered valid

        adress String

Address where the asset is considered valid

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

Success
FieldTypeDescription
txHex String

Unsigend transaction hex for the issuance

assetId String

Asset id for the asset generated

request for issue

var request = require('request')

var key = 'L4DhcqXTV3rzeeuPCeaUqzBA1KEosR8T5MMpJCyDYawSNDgKumz1'
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// asset data
var asset = {
    'issueAddress': address,
    'amount': 100,
    'divisibility': 0,
    'fee': 5000,
    'reissueable':false,
    'transfer': [{
    	'address': address,
    	'amount': 100
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            'meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// issue asset
postApi('issue', asset, function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txHex': '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc50000000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000',
    'assetId': 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
}

request for issue

var request = require('request')

var key = 'L4DhcqXTV3rzeeuPCeaUqzBA1KEosR8T5MMpJCyDYawSNDgKumz1'
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// asset data
var asset = {
    'issueAddress': address,
    'amount': 100,
    'divisibility': 0,
    'fee': 5000,
    'reissueable':false,
    'transfer': [{
    	'address': address,
    	'amount': 100
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            'meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// issue asset
postApi('issue', asset, function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txHex': '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc50000000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000',
    'assetId': 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
}

request for issue

var request = require('request')

var key = 'L4DhcqXTV3rzeeuPCeaUqzBA1KEosR8T5MMpJCyDYawSNDgKumz1'
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// asset data
var asset = {
    'issueAddress': address,
    'amount': 100,
    'divisibility': 0,
    'fee': 5000,
    'reissueable':false,
    'transfer': [{
    	'address': address,
    	'amount': 100
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            'meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// issue asset
postApi('issue', asset, function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txHex': '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc50000000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000',
    'assetId': 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
}

request for issue

var request = require('request')

var key = 'L4DhcqXTV3rzeeuPCeaUqzBA1KEosR8T5MMpJCyDYawSNDgKumz1'
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// asset data
var asset = {
    'issueAddress': address,
    'amount': 100,
    'divisibility': 0,
    'fee': 5000,
    'reissueable':false,
    'transfer': [{
    	'address': address,
    	'amount': 100
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            'meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// issue asset
postApi('issue', asset, function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txHex': '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc50000000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000',
    'assetId': 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
}

Send Asset

sendAsset API Documentation

TITLEBuild a transaction which sends an asset

VERSION0.3.0

URL/sendasset

TYPE[POST]

Description

This API call is used to transfer assets, it can be funded by a specifc utxo or by any spendable utxo of the from address (see financeOutput). sendutxo can be used to specify the specific asset you wish to send, if the transaction has more assets inputed then assets transferred then the remainder of all the assets is considered to be spent to the last output

Parameters
FieldTypeDescription
fee Number

Fee for transaction in satoshi.

pubKeyReturnMultisigDust(optional) String

encoded public key if you want to receive the multisig dust if multisig is needed for the metadata.

from(optional) String[]

Array of addresses to send the asset from. Any unspents of the specific asset held by that address will be used (optional can use sendutxo instead)

sendutxo(optional) String[]

Array of Utxos to use for sending the asset itself (: format)

financeOutput(optional) Object

A vout type object to use in order to finance the transfer (btc costs)

    value Number

Value in BTC of the output

    n Number

Output index

    scriptPubKey Object

ScriptPubKey type object

        asm String

Asm for the output

        hex String

Hex for the output

        type String

Bitcoin output type

        reqSigs(optional) Number

Number of required signatures to redeem

        adresses(optional) String[]

Addresses that can redeem

financeOutputTxid(optional) String

Txid containing the vout used for the finance

to Object[]

Array of transfer type objects (transfers amount of the specifed asset to specific addresses)

    address(optional) String

Address to transfer assets to (any base58 address)

    amount Number

Amount of Asset to transfer

    assetId String

Asset ID of Asset to transfer

    pubKeys(optional) String

Optional] instead of address send the pubkeys as a string array, we create P2SH

    m(optional) String

Number of signatures required in order to reedem the multisig

flags(optional) Object

A flag type object

    injectPreviousOutput(optional) Boolean

If true the input will contain the previous output script to make siging simpler

metadata(optional) Object

Metadata of the specific utxo from the transaction

    assetId(optional) String

Asset Id

    assetName(optional) String

Asset Name

    assetGenesis(optional) String

Genesis transaction where the asset was created (in case of re issue)

    issuer(optional) String

Name of the issuer

    description(optional) String

description of the asset

    urls(optional) Object[]

Array of URL type objects

        name String

Name of the url

        url String

The url

        mimeType String

Mime type of the data in the url

        dataHash(optional) String

If needed hash of the data that in the url (for proof reasons)

    encryptions(optional) Object[]

Array of encryptSection type objects

        key(optional) String

The json key of the value to encrypt within the user section

        pubKey(optional) String

Public key we will use for the encryption (rsa pubkey)

        format(optional) String

Input format of the key (accepted values: 'pem', 'der')

        type(optional) String

Type and padding of the key (accepted values: 'pkcs1', 'pkcs8')

    userData(optional) JSON

Any arbitrary json data that the previous owner of the output has enterd

rules(optional) Object

Object for the rules of the asset

    version Number

Version of the rule system

    fees Object
        items(optional) Object[]

Array of fee type items

            address String

Address to send the fee

            assetId String

Asset id to send fee (btc if none asset)

            value Number

Value to send for the fee (in satoshi or amount)

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

    expiration(optional) Object

Expiration object used to lown an asseet, when asset expires it moves back to last output

        validUntil Number

When the asset is consider expired

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

    minters(optional) Object[]

Array of mitnter objects, (addresses that can issue the asset)

        address String

Address of the minter

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type (if the minter can add minters)

    holders(optional) Object[]

Array of holder type objects, they specify in what addresses the asset is considered valid

        adress String

Address where the asset is considered valid

        locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

Success
FieldTypeDescription
txHex String

Unsigned hex of the send transaction.

coloredOutputIndexes Number[]

Array of indexes of outputs assets were explicitly transferred to.

multisigOutputs Number[]

Array of indexes of multisig outputs.

create a transaction that sends colored coins

var bitcoin = require('bitcoinjs-lib')
var request = require('request')

var address1 = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'
var address2 = 'mnz4Xgxg3o6XrhhYvVHCvAyXpKB5GaZ1GM'
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// send parameters
var asset = {
    'fee': 5000,
    'from': [address1],
    'to': [{
    	'address': address2,
    	'amount': 20,
        'assetId': assetId
    },{
    	'address': address1,
    	'amount': 80,
        'assetId': assetId
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            "meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// send asset
postApi('sendasset', asset, function(err, body){
if (err) console.log('error: ', err); });

response

{
    'txHex': '01000000023f69ea2eca2ef5c5b10d38723e4a8355dcfde719ba83e6bbacd5f04ed99dba540100000000ffffffffe02388be1f8f8da250d2616d5906d410ae2c033b7caaff5b83480831177a9eae0100000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21034373b8d1afe498c51f70d3b9c809731d1d83f4b81d6f9d5d58412d580630391452ae58020000000000001976a91423d0d278caaa283ecf68e4a0e33213de99ef4caf88ac00000000000000001d6a1b434301110bb73205ae80d7a439ae87cf3ddd9ed71cb0db18012012807d2f05000000001976a914eb02487d8f3624865fcc4e4fe06a69157cd19f5988ac00000000'
}

create a transaction that sends colored coins

var bitcoin = require('bitcoinjs-lib')
var request = require('request')

var address1 = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'
var address2 = 'mnz4Xgxg3o6XrhhYvVHCvAyXpKB5GaZ1GM'
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// send parameters
var asset = {
    'fee': 5000,
    'from': [address1],
    'to': [{
    	'address': address2,
    	'amount': 20,
        'assetId': assetId
    },{
    	'address': address1,
    	'amount': 80,
        'assetId': assetId
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            "meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// send asset
postApi('sendasset', asset, function(err, body){
if (err) console.log('error: ', err); });

response

{
    'txHex': '01000000023f69ea2eca2ef5c5b10d38723e4a8355dcfde719ba83e6bbacd5f04ed99dba540100000000ffffffffe02388be1f8f8da250d2616d5906d410ae2c033b7caaff5b83480831177a9eae0100000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21034373b8d1afe498c51f70d3b9c809731d1d83f4b81d6f9d5d58412d580630391452ae58020000000000001976a91423d0d278caaa283ecf68e4a0e33213de99ef4caf88ac00000000000000001d6a1b434301110bb73205ae80d7a439ae87cf3ddd9ed71cb0db18012012807d2f05000000001976a914eb02487d8f3624865fcc4e4fe06a69157cd19f5988ac00000000'
}

create a transaction that sends colored coins

var bitcoin = require('bitcoinjs-lib')
var request = require('request')

var address1 = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'
var address2 = 'mnz4Xgxg3o6XrhhYvVHCvAyXpKB5GaZ1GM'
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// send parameters
var asset = {
    'fee': 5000,
    'from': [address1],
    'to': [{
    	'address': address2,
    	'amount': 20,
        'assetId': assetId
    },{
    	'address': address1,
    	'amount': 80,
        'assetId': assetId
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            "meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// send asset
postApi('sendasset', asset, function(err, body){
if (err) console.log('error: ', err); });

response

{
    'txHex': '01000000023f69ea2eca2ef5c5b10d38723e4a8355dcfde719ba83e6bbacd5f04ed99dba540100000000ffffffffe02388be1f8f8da250d2616d5906d410ae2c033b7caaff5b83480831177a9eae0100000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21034373b8d1afe498c51f70d3b9c809731d1d83f4b81d6f9d5d58412d580630391452ae58020000000000001976a91423d0d278caaa283ecf68e4a0e33213de99ef4caf88ac00000000000000001d6a1b434301110bb73205ae80d7a439ae87cf3ddd9ed71cb0db18012012807d2f05000000001976a914eb02487d8f3624865fcc4e4fe06a69157cd19f5988ac00000000'
}

create a transaction that sends colored coins

var bitcoin = require('bitcoinjs-lib')
var request = require('request')

var address1 = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'
var address2 = 'mnz4Xgxg3o6XrhhYvVHCvAyXpKB5GaZ1GM'
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    }, 
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

// send parameters
var asset = {
    'fee': 5000,
    'from': [address1],
    'to': [{
    	'address': address2,
    	'amount': 20,
        'assetId': assetId
    },{
    	'address': address1,
    	'amount': 80,
        'assetId': assetId
    }],
    'metadata': {
        'assetId': '1',
        'assetName': 'Asset Name',
        'issuer': 'Asset Issuer',
        'description': 'My Description',
        'urls': [{name:'icon', url: 'https://pbs.twimg.com/profile_images/572390580823412736/uzfQSciL_bigger.png', mimeType: 'image/png', dataHash: ''}],
        'userData': {
            "meta' : [
                {key: 'Item ID', value: 2, type: 'Number'},
                {key: 'Item Name', value: 'Item Name', type: 'String'},
                {key: 'Company', value: 'My Company', type: 'String'},
                {key: 'Address', value: 'San Francisco, CA', type: 'String'}
            ]
        }
    }
}

// send asset
postApi('sendasset', asset, function(err, body){
if (err) console.log('error: ', err); });

response

{
    'txHex': '01000000023f69ea2eca2ef5c5b10d38723e4a8355dcfde719ba83e6bbacd5f04ed99dba540100000000ffffffffe02388be1f8f8da250d2616d5906d410ae2c033b7caaff5b83480831177a9eae0100000000ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21034373b8d1afe498c51f70d3b9c809731d1d83f4b81d6f9d5d58412d580630391452ae58020000000000001976a91423d0d278caaa283ecf68e4a0e33213de99ef4caf88ac00000000000000001d6a1b434301110bb73205ae80d7a439ae87cf3ddd9ed71cb0db18012012807d2f05000000001976a914eb02487d8f3624865fcc4e4fe06a69157cd19f5988ac00000000'
}

Broadcast Transaction

broadcasttransaction API Documentation

TITLESend the signed transaction through the API to the bitcoin network

VERSION0.2.0

URL/broadcast

TYPE[POST]

Description

This API call is used to send the signed raw transaction hex to the bitcoin network

Parameters
FieldTypeDescription
txHex String

The hex of the transaction you want to send.

Success
FieldTypeDescription
txid String

The transaction txid

brodcast transaction on the blockchain

var request = require('request')

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    },
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

var signedTxHex = '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc5000000006b483045022100eaca05895e51111c6a3a6c28488c897754ce2a3dae607f9e6da88cd1559cdd8d022062a61de3970ec1c5398e80524fb7d639f0c7d79c074954d0efc5968412666d4c012102bd1a53fbe7a1f5ac884592e2780c35b9de15fc9ecdc9b097163fd8c95b25b499ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000'

// sign tx param
var data_params = {
    'txHex': signedTxHex
}

// broadcast transaction
postApi('broadcast',data_params,function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txid': '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
}

brodcast transaction on the blockchain

var request = require('request')

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    },
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

var signedTxHex = '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc5000000006b483045022100eaca05895e51111c6a3a6c28488c897754ce2a3dae607f9e6da88cd1559cdd8d022062a61de3970ec1c5398e80524fb7d639f0c7d79c074954d0efc5968412666d4c012102bd1a53fbe7a1f5ac884592e2780c35b9de15fc9ecdc9b097163fd8c95b25b499ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000'

// sign tx param
var data_params = {
    'txHex': signedTxHex
}

// broadcast transaction
postApi('broadcast',data_params,function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txid': '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
}

brodcast transaction on the blockchain

var request = require('request')

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    },
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

var signedTxHex = '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc5000000006b483045022100eaca05895e51111c6a3a6c28488c897754ce2a3dae607f9e6da88cd1559cdd8d022062a61de3970ec1c5398e80524fb7d639f0c7d79c074954d0efc5968412666d4c012102bd1a53fbe7a1f5ac884592e2780c35b9de15fc9ecdc9b097163fd8c95b25b499ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000'

// sign tx param
var data_params = {
    'txHex': signedTxHex
}

// broadcast transaction
postApi('broadcast',data_params,function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txid': '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
}

brodcast transaction on the blockchain

var request = require('request')

function postApi(api_endpoint, json_data, callback) {
    console.log(api_endpoint+': ', JSON.stringify(json_data))
    request.post({
        url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
        headers: {'Content-Type': 'application/json'},
        form: json_data
    },
    function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status: ', response.statusCode)
        console.log('Body: ', JSON.stringify(body))
        return callback(null, body)
    })
}

var signedTxHex = '0100000001ee58da20e99a5f664bf053615d10e10d965312d7e1e708b0883f9b36e0723cc5000000006b483045022100eaca05895e51111c6a3a6c28488c897754ce2a3dae607f9e6da88cd1559cdd8d022062a61de3970ec1c5398e80524fb7d639f0c7d79c074954d0efc5968412666d4c012102bd1a53fbe7a1f5ac884592e2780c35b9de15fc9ecdc9b097163fd8c95b25b499ffffffff04580200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb2052ae58020000000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac0000000000000000206a1e4343010202fcc3d843eaba4d278ed107c0c2b56a146f66b8201201201210287b2f05000000001976a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac00000000'

// sign tx param
var data_params = {
    'txHex': signedTxHex
}

// broadcast transaction
postApi('broadcast',data_params,function(err, body){
if (err) console.log('error: ', err) })

response

{
    'txid': '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
}

Get Address Info

getaddressinfo API Documentation

TITLEGet asset information for the address

VERSION0.2.0

URL/addressinfo/:address

TYPE[GET]

Description

This API call is used to get all the assets for the address, this information is per utxo owned by the address, also retrives uncolored utxos.

Parameters
FieldTypeDescription
address String

Base58 address

Success
FieldTypeDescription
address String

Base58 address

utxos Object[]

Arry of ccUtxo items

    scriptPubKey Object

ScriptPubKey type object

        asm String

Asm for the output

        hex String

Hex for the output

        type String

Bitcoin output type

        reqSigs Number

Number of required signatures to redeem

        adresses String[]

Addresses that can redeem

assets Object[]

Array of assetInfo type objects

    amount Number

Amount of the asset in the utxo

    assetId String

Asset id

    issueTxid String

Txid that links this utxo to is genises issuance

    divisibility Number

How divisible the asset is

    lockStatus Boolean

Was the issuance locked

request to get the address info

var request = require('request')

// get address info
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status:', response.statusCode)
        console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('addressinfo', address , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
	utxos: [{
		_id: '559288db7c3454d08f7eb35c',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 1,
		value: 600,
		blockheight: -1,
		used: false,
		assets: [{
			assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
			amount: 100,
			issueTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
			divisibility: 0,
			lockStatus: null
		}],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	},
	{
		_id: '559288db7c3454d08f7eb35e',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 3,
		value: 86997800,
		blockheight: -1,
		used: false,
		assets: [ ],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	}]
}

request to get the address info

var request = require('request')

// get address info
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status:', response.statusCode)
        console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('addressinfo', address , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
	utxos: [{
		_id: '559288db7c3454d08f7eb35c',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 1,
		value: 600,
		blockheight: -1,
		used: false,
		assets: [{
			assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
			amount: 100,
			issueTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
			divisibility: 0,
			lockStatus: null
		}],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	},
	{
		_id: '559288db7c3454d08f7eb35e',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 3,
		value: 86997800,
		blockheight: -1,
		used: false,
		assets: [ ],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	}]
}

request to get the address info

var request = require('request')

// get address info
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status:', response.statusCode)
        console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('addressinfo', address , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
	utxos: [{
		_id: '559288db7c3454d08f7eb35c',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 1,
		value: 600,
		blockheight: -1,
		used: false,
		assets: [{
			assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
			amount: 100,
			issueTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
			divisibility: 0,
			lockStatus: null
		}],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	},
	{
		_id: '559288db7c3454d08f7eb35e',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 3,
		value: 86997800,
		blockheight: -1,
		used: false,
		assets: [ ],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	}]
}

request to get the address info

var request = require('request')

// get address info
var address = 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
            return callback(error)
        }
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
        console.log('Status:', response.statusCode)
        console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('addressinfo', address , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
	utxos: [{
		_id: '559288db7c3454d08f7eb35c',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 1,
		value: 600,
		blockheight: -1,
		used: false,
		assets: [{
			assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
			amount: 100,
			issueTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
			divisibility: 0,
			lockStatus: null
		}],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	},
	{
		_id: '559288db7c3454d08f7eb35e',
		txid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
		index: 3,
		value: 86997800,
		blockheight: -1,
		used: false,
		assets: [ ],
		scriptPubKey: {
			asm: 'OP_DUP OP_HASH160 fb6a7c47ea2a45debe4dc3137508ee8bf05e8e04 OP_EQUALVERIFY OP_CHECKSIG',
			hex: '76a914fb6a7c47ea2a45debe4dc3137508ee8bf05e8e0488ac',
			reqSigs: 1,
			type: 'pubkeyhash',
			addresses: ['n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj']
		}
	}]
}

Get Asset Holders

getAssetHolders API Documentation

TITLERequest asset holders

VERSION0.2.0

URL/stakeholders/:assetId/:numConfirmations

TYPE[GET]

Description

This API call is used to get all the addresses that contain any value of the specifed asset

Parameters
FieldTypeDescription
assetId String

Asset unique ID.

numConfirmations Number

Number of confiramtions for the utxos to return.

Success
FieldTypeDescription
holders Object[]

Array of holder objects.

    address Object[]

Address that has holding of the asset.

    amount Object[]

Amount of the asset in the address.

request for asset holders

var request = require('request')

// get asset holders
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	     return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('stakeholders', assetId+'/1', function(err, body) {
if (err) console.log('error: ', err) })

response

{
    assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
    holders: [{
        address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
        amount: 100
    }],
    divisibility: 0,
    lockStatus: null
}

request for asset holders

var request = require('request')

// get asset holders
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	     return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('stakeholders', assetId+'/1', function(err, body) {
if (err) console.log('error: ', err) })

response

{
    assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
    holders: [{
        address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
        amount: 100
    }],
    divisibility: 0,
    lockStatus: null
}

request for asset holders

var request = require('request')

// get asset holders
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	     return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('stakeholders', assetId+'/1', function(err, body) {
if (err) console.log('error: ', err) })

response

{
    assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
    holders: [{
        address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
        amount: 100
    }],
    divisibility: 0,
    lockStatus: null
}

request for asset holders

var request = require('request')

// get asset holders
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	     return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('stakeholders', assetId+'/1', function(err, body) {
if (err) console.log('error: ', err) })

response

{
    assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
    holders: [{
        address: 'n4SKTwh8xxNMSH7uN2xRZym7iXCZNwy8vj',
        amount: 100
    }],
    divisibility: 0,
    lockStatus: null
}

Get Asset Metadata

getAssetMetadata API Documentation

TITLERequest asset metadata and utxo metadata

VERSION0.2.0

URL/assetmetadata/:assetId/:utxo

TYPE[GET]

Description

This API call is used to get the metadata of the issuance of an asset, if a specific utxo is provided it will also get the metadata for that specific utxo which was set by the previous owner of that asset on the blockchain

Parameters
FieldTypeDescription
assetId String

Asset unique ID.

utxo String

Unspent in : format

Success
FieldTypeDescription
divisibility Number

How divisible is the asset

version String

Version of protocol as string

totalSupply Number

Total amount of the asset that was issued

numOfHolders Number

Number of addresses that have any amount of the asset

numOfTransactions Number

Number of transactions that the asset was passed in

numOfIssuance Number

Number of times an amount of the asset was issued

firstAppearsInBlock Number

First time this asset aapeard in the blockchain (first issue)

metadataOfIssuence Object

Metadata of the issuance

    data Object

Data section in the metadata file

        assetId String

Asset Id

        assetName String

Asset Name

        assetGenesis String

Genesis transaction where the asset was created (in case of re issue)

        issuer String

Name of the issuer

        description String

Description of the asset

        urls Object[]

Array of URL type objects

            name String

Name of the url

            url String

The url

            mimeType String

Mime type of the data in the url

            dataHash String

If needed hash of the data that in the url (for proof reasons)

        userData JSON

Any arbitrary json data that issuer has enterd

    rules Object

Object for the rules of the issuance

        version Number

Version of the rule system

        fees Object
            items Object[]

Array of fee type items

                address String

Address to send the fee

                assetId String

Asset id to send fee (btc if none asset)

                value Number

Value to send for the fee (in satoshi or amount)

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

        expiration Object

Expiration object used to lown an asseet, when asset expires it moves back to last output

            validUntil Number

When the asset is considered expired

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

        minters Object[]

Array of mitnter objects, (addresses that can issue the asset)

            address String

Address of the minter

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type (if the minter can add minters)

        holders Object[]

Array of holder type objects, they specify in what addresses the asset is considered valid

            adress String

Address where the asset is considered valid

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

metadataOfUtxo Object

Metadata of the specific utxo from the transaction

    data Object

Data section in the metadata file

        assetId String

Asset Id

        assetName String

Asset Name

        assetGenesis String

Genesis transaction where the asset was created (in case of re issue)

        issuer String

Name of the issuer

        description String

description of the asset

        urls Object[]

Array of URL type objects

            name String

Name of the url

            url String

The url

            mimeType String

Mime type of the data in the url

            dataHash String

If needed hash of the data that in the url (for proof reasons)

        userData JSON

Any arbitrary json data that the previous owner of the output has enterd

    rules Object

Object for the rules of the asset

        version Number

Version of the rule system

        fees Object
            items Object[]

Array of fee type items

                address String

Address to send the fee

                assetId String

Asset id to send fee (btc if none asset)

                value Number

Value to send for the fee (in satoshi or amount)

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

        expiration Object

Expiration object used to lown an asseet, when asset expires it moves back to last output

            validUntil Number

When the asset is consider expired

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

        minters Object[]

Array of mitnter objects, (addresses that can issue the asset)

            address String

Address of the minter

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type (if the minter can add minters)

        holders Object[]

Array of holder type objects, they specify in what addresses the asset is considered valid

            adress String

Address where the asset is considered valid

            locked Boolean

Failed to specify if following transaction of the asset can add to this rule type

request to get an asset metadata

var request = require('request')

// get asset metadata
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
var txid = '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
var utxo = txid+':1'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	    return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('assetmetadata', assetId+'/'+utxo , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
	divisibility: 0,
	lockStatus: null,
	totalSupply: 100,
	numOfHolders: 1,
	numOfTransfers: 0,
	numOfIssuance: 1,
	firstBlock: -1,
	issuanceTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
	metadataOfIssuence: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesOfIssuence: 'none',
	sha2Issue: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20',
	metadatOfUtxo: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesofUtxo: 'none',
	sha2Utxo: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20'
}

request to get an asset metadata

var request = require('request')

// get asset metadata
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
var txid = '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
var utxo = txid+':1'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	    return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('assetmetadata', assetId+'/'+utxo , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
	divisibility: 0,
	lockStatus: null,
	totalSupply: 100,
	numOfHolders: 1,
	numOfTransfers: 0,
	numOfIssuance: 1,
	firstBlock: -1,
	issuanceTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
	metadataOfIssuence: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesOfIssuence: 'none',
	sha2Issue: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20',
	metadatOfUtxo: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesofUtxo: 'none',
	sha2Utxo: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20'
}

request to get an asset metadata

var request = require('request')

// get asset metadata
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
var txid = '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
var utxo = txid+':1'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	    return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('assetmetadata', assetId+'/'+utxo , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
	divisibility: 0,
	lockStatus: null,
	totalSupply: 100,
	numOfHolders: 1,
	numOfTransfers: 0,
	numOfIssuance: 1,
	firstBlock: -1,
	issuanceTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
	metadataOfIssuence: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesOfIssuence: 'none',
	sha2Issue: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20',
	metadatOfUtxo: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesofUtxo: 'none',
	sha2Utxo: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20'
}

request to get an asset metadata

var request = require('request')

// get asset metadata
var assetId = 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY'
var txid = '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de'
var utxo = txid+':1'

function getApi(api_endpoint, param, callback) {
    console.log('Get from:'+api_endpoint+'/'+param)
    request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
        if (error) {
	    return callback(error)
	}
        if (typeof body === 'string') {
            body = JSON.parse(body)
        }
	console.log('Status:', response.statusCode)
	console.log('Body:', body)
        return callback(null, body)
    })
}

getApi('assetmetadata', assetId+'/'+utxo , function(err, body) {
if (err) console.log('error: ', err) })

response

{
	assetId: 'LYfzkq2KP6K5rhNR7mE9B6BmhiHTtxvMxY',
	divisibility: 0,
	lockStatus: null,
	totalSupply: 100,
	numOfHolders: 1,
	numOfTransfers: 0,
	numOfIssuance: 1,
	firstBlock: -1,
	issuanceTxid: '98a1ebf2b80eafe4cc58bb01e1eb74a09038e60a67032cacdb3dfb8bf83175de',
	metadataOfIssuence: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesOfIssuence: 'none',
	sha2Issue: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20',
	metadatOfUtxo: {
		assetId: '1',
		assetName: 'Asset Name',
		assetGenesis: '',
		issuer: 'Asset Issuer',
		description: 'My Description',
		userData: {
			meta: [{
				key: 'Item ID',
				value: '2',
				type: 'Number'
			},{
				key: 'Item Name',
				value: 'Item Name',
				type: 'String'
			},{
				key: 'Company',
				value: 'My Company',
				type: 'String'
			},{
				key: 'Address',
				value: 'San Francisco, CA',
				type: 'String'
			}]
		}
	},
	rulesofUtxo: 'none',
	sha2Utxo: '0359040d5c3bc91b5e28e014541363c0f64d9a2429541fe6cf1c568c63c85fbb20'
}

Getting Started


In this getting started guide we will walk you through the two core examples of the colored coins api:
issuing a new asset and transferring an existing asset.

The examples given in this guide are written in javascript using the popular libraries node js and bitcoinjs.

All the code snippets used in this getting started guide can be found on github.

First, we will walk you through the necessary steps needed to setup your machine in order to follow the rest of the guide.

Setup

In this section we will walk you through installing the required software in order to be able to follow the rest of our getting started guide.

 

 

 

 

NodeJS

In this section we show how to install node js so that anyone can follow the rest of the examples in this getting started guide.
Of course, node js is not mandatory for using our API, but since all the examples are written in node we present it here for completeness.

Installing Node js

For example, on Linux (Ubuntu), launch a terminal window and type this to download the package:

> curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -

Now type this to install node js

> sudo apt-get install -y nodejs

Now verify installation

Open a text editor and save the following in a file called example.js

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Colored Coins\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Now launch a terminal window and type

> node example.js

Launch a web browser and visit localhost:1337. You should see the text "Colored Coins"

 

 

 

 

BitcoinJS

Installing Bitcoin JS

Our API is currently based on bitcoinjs-lib version 1.5. Some compatibility issues exist with newer versions of bitcoinjs-lib. To install type the following:

> sudo npm install bitcoinjs-lib@1.5

Verify installation

Save the following code into a file verify_bjs.js

var bitcoin = require('bitcoinjs-lib');
key = bitcoin.ECKey.makeRandom();
address = key.pub.getAddress().toString();
console.log('new bitcoin address: ['+address+']');

Now launch a terminal window and type

> node verify_bjs.js

You should see an output of the form:

New Bitcoin Address:[1L1emXigzg7dPc92eYRHaf1TQtZ7n9ri3n]

showing the new Bitcoin address that was just generated.

 

 

 

 

Request

we also use a library to handle http requests

Installing request

> sudo npm install request

 

 

 

 

Creating a Testnet Address

In this section we will create a new TESTNET Bitcoin address and fund it with test-bitcoins. The reason that we are using TESTNET is that issuing an asset requires funding and we don't want to spend real bitcoins in this tutorial.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it address.js

First requiring the dependencies

// address.js
var bitcoin = require('bitcoinjs-lib');

Creating a new bitcoin key object and extracting the TESTNET address
// address.js
...
//var key = bitcoin.ECPair.makeRandom({network: bitcoin.networks.testnet});
var key = bitcoin.ECKey.makeRandom();
//var address = key.getAddress().toString();
var address = key.pub.getAddress(bitcoin.networks.testnet).toString();
var wif = key.toWIF();
console.log('new TESTNET address: ['+address+']');
console.log('Private Key of new address (WIF format): ['+wif+']');
...

Now launch a terminal window and type

> node address.js
new TESTNET address: [n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR]
Private Key of new address (WIF format): [KzH9zdXm95Xv3z7oNxzM6HqSPUiQbuyKoFdQBTf3HKx1B6eYdbAn]

A new TESTNET address was printed to the screen.

Let's copy the new address we just generated and launch a web browser pointing to a testnet faucet, say this one. Testnet faucets are websites that give away free test-bitcoins (which have no monetary value) so that developers can use them for testing Bitcoin software.

Paste the newly generated testnet address you just created into the faucet, fill in the captcha and hit send. You are now the owner of (valueless) test-bitcoins!

To see your balance online you can use our TESTNET explorer. Paste your TESTNET address into the search box and hit the search button. You should see the test-bitcoin balance in your address.

 

 

 

 

Issue a new asset

In this section we review the process of issuing a new asset using the colored coins API.

 

 

 

 

Issuance - 1st attempt

Issuing a new asset

In this section we will try to issue a new asset on the bitcoin TESTNET network.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it issue.js.

First requiring the dependencies

// issue.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

Creating a new bitcoin key object and extracting the TESTNET address
// issue.js
...
key = bitcoin.ECKey.makeRandom();
new_address = key.pub.getAddress(bitcoin.networks.testnet).toString();
...

Now define an auxiliary function to help us POST to the colored coins API.
This function accepts the json_data to be posted and the API end point (as well as a callback).

// issue.js
...
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};
...

The next thing to do is to prepare a JSON object specifying the new asset. The minimal information needed for issuing a new asset is


  • The Issuance address

  • The Amount of Units to be issued

  • The Transaction Fee (recommended to be at least 5000 Satoshis)


So let's first try to issue 1 unit of a new asset on our randomly generated address:
// issue.js
...
var asset = {
'issueAddress': new_address,
'amount': 1,
'fee': 5000
};
...

Finally, because we are interested in issuing a new asset the relevant API endpoint is issue, so let's call it and attempt to issue a new asset based on the above asset JSON:

// issue.js
...
postToApi('issue', asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Now launch a terminal window and type

> node issue.js
----------------
issue: {'issueAddress':'n2bhvGqphnFp7gUfm3QA2s26HDetkTvJjg','amount':1,'fee':5000}}
Status: 500
Body: '{\'error\':\'Not enough funds to cover issuence\'}'
>

As you may have expected, we are getting an error. The error is pretty self explanatory, we first need to fund the issuing address with enough bitcoins to finance the colored coins issuance transaction.

Fortunately for us, we already prepared in advance a funded TESTNET address, so let's use it.

 

 

 

 

Issuance - 2nd attempt

Generating an issuance transaction

In this section we will issue a new asset on a our previously funded TESTNET address.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it issue_funded.js.

First requiring the dependencies

// issue_funded.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

Using our funded TESTNET address
// issue_funded.js
...
funded_address = 'your funded address here'
// e.g. funded_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';
...

Now use the same code for calling the colored coins API and preparing the data

// issue_funded.js
...
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

var asset = {
'issueAddress': funded_address,
'amount': 1,
'fee': 5000
};

postToApi('issue', asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Launch a terminal window and run the new file

> node issue_funded.js
----------------------

issue: {"issueAddress":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","amount":1,"fee":5000}
Status: 200
Body: "{\"txHex\":\"0100000001e0cd69ce93aded7a8d51063ed5f7bb5c9cdcc885a93fa629574dedb2cd5b48ad0100000000ffffffff020000000000000000086a06434301050110b8820100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000\",\"assetId\":\"LHVFD2PsgiDjbUhNmbGyNgYz8fuuW4K3CuDgs\"}">

This time we successfully created a (hexadecimal representation of) a new colored transaction, specifically an issuance transaction.
Note that the second returned value (you may have to scroll to the right to see it) is the Asset ID: LHVFD2PsgiDjbUhNmbGyNgYz8fuuW4K3CuDgs. Incidentally, note that the first letter of the Asset ID is "L". This indicates that the asset we are in the process of issuing will be a Locked Asset.

What remains to be done is to sign the transaction and broadcast it to the Bitcoin (TESTNET) network

Let's first sign the transaction.

 

 

 

 

Sign Transaction

Signing the transaction

Signing the issuance transaction requires the private key of the underlying address and the Hexadecimal representation of the unsigned transaction that we got back from the 'issue' API endpoint.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it sign_transaction.js.

First require the bitcoinjs dependency

// sign_transaction.js
var bitcoin = require('bitcoinjs-lib');

Now add the following signTx function that will perform the actual signing. This function accepts the hexadecimal representation of an unsigned Bitcoin transaction and the Wallet Import Format representation (or WIF for short) of the private key controlling the underlying address. If everything is valid, we get back a signed Bitcoin transaction.

// sign_transaction.js
....
function signTx (unsignedTx, wif) {
var privateKey = bitcoin.ECKey.fromWIF(wif)
var tx = bitcoin.Transaction.fromHex(unsignedTx)
var insLength = tx.ins.length
for (var i = 0; i < insLength; i++) {
tx.sign(i, privateKey)
}
return tx.toHex()
}
...

The private key for the issuance address was obtained when we created (and funded) our TESTNET address
Let's copy the private key from there.

The hexadecimal representation of the unsigned transaction is the value of 'body.txHex' we got from the 'issue' API call.

Let's add those to our file:

// sign_transaction.js
....
var key = 'wif format of private key of your issuance address'
// e.g. var key = 'KzH9zdXm95Xv3z7oNxzM6HqSPUiQbuyKoFdQBTf3HKx1B6eYdbAn';
var txHex = 'body.txHex response to issue';
// e.g. txHex = '0100000001e0cd69ce93aded7a8d51063ed5f7bb5c9cdcc885a93fa629574dedb2cd5b48ad0100000000ffffffff020000000000000000086a06434301050110b8820100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000'
...

Finally, let's print the signed transaction to the console

// sign_transaction.js
....
var signedTxHex = signTx(txHex, key);
console.log("signedTxHex: ["+signedTxHex+"]");

Launch a console an run the file

> node sign_transaction.js
-----------------------------
signedTxHex: [0100000001e0cd69ce93aded7a8d51063ed5f7bb5c9cdcc885a93fa629574dedb2cd5b48ad010000006b483045022100a7bb72eb60e169aa298290664828670fe2dac2d76a6c5abbec278c7ee231de61022041949b59b486c12fc27ec6f8a83e1590f4102562900b55ba08569f1e22682a49012102c5ac36c34715ebf5b82abab08132ca08e90cc40ed8c2ca29620d0f00f28655d6ffffffff020000000000000000086a06434301050110b8820100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000]

There we are, this is a hexadecimal representation of the signed issuance transaction.

The next step would be to use the colored coins API to broadcast this signed transaction to the TESTNET network, so that it can be written to the blockchain.

 

 

 

 

Broadcast Transaction

Broadcasting the signed transaction

We are going to use the 'broadcast' API endpoint in order to broadcast the signed transaction.

create a new file, say broadcast.js, in the same directory.

Requiring the same dependencies and using the exact same postToApi function

// broadcast.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};
...

We now add the JSON data to be posted to the API endpoint. This API endpoint is expecting a JSON object with the key txHex, so let's create such a JSON object referencing the singedTxHex we just got

// broadcast.js
...
var signedTxHex= '0100000001e0cd69ce93aded7a8d51063ed5f7bb5c9cdcc885a93fa629574dedb2cd5b48ad010000006b483045022100a7bb72eb60e169aa298290664828670fe2dac2d76a6c5abbec278c7ee231de61022041949b59b486c12fc27ec6f8a83e1590f4102562900b55ba08569f1e22682a49012102c5ac36c34715ebf5b82abab08132ca08e90cc40ed8c2ca29620d0f00f28655d6ffffffff020000000000000000086a06434301050110b8820100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000'
;

var transaction = {
'txHex': signedTxHex
}

postToApi('broadcast', transaction, function(err, body){
if (err) {
console.log('error: ', err);
}
});

As before, Launch a terminal window and type

> node broadcast.js
-------------------

broadcast: {"txHex":"0100000001e0cd69ce93aded7a8d51063ed5f7bb5c9cdcc885a93fa629574dedb2cd5b48ad010000006b483045022100a7bb72eb60e169aa298290664828670fe2dac2d76a6c5abbec278c7ee231de61022041949b59b486c12fc27ec6f8a83e1590f4102562900b55ba08569f1e22682a49012102c5ac36c34715ebf5b82abab08132ca08e90cc40ed8c2ca29620d0f00f28655d6ffffffff020000000000000000086a06434301050110b8820100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000"}
Status: 200
Body: "{\"txid\":\"355f07ac190ac71f990f68e6153f812dcfaad6209391872d47a514d43c62b13e\"}"
>

Success! We issued our first Asset on top of the Bitcoin TESTNET network

What we got in response was the transaction ID of the transaction that was broadcasted. We can inspect the transaction with any TESTNET explorer. Note the expected structure of an Issuance Transaction. We can see the OP_RETURN output and the fact that since nothing else was specified, everything (bitcoins and assets) was transferred back to the address 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR'.

Now let's inspect the same transaction with a colored coins explorer. This explorer recognizes that 1 unit of the asset LHVFD2PsgiDjbUhNmbGyNgYz8fuuW4K3CuDgs (that we created in a previous section) was sent to the issuance address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR.

 

 

 

 

Transfer an asset

In this section we review using the colored coins API to transfer an existing asset from one address to other addresses.

Let's start by issuing 100 units of a new asset using our previously funded address.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it issue_100.js.
// issue_100.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
funded_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR'; // or your own funded address
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};
var asset = {
'issueAddress': funded_address,
'amount': 100,
'fee': 5000
};

postToApi('issue', asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Running that file we get the response:

> node issue_100.js
--------------------
issue: {"issueAddress":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","amount":100,"fee":5000}
Status: 200
Body: {"txHex":"01000000016605218c2217f94db9490cc242fbaa98125d52975f9690d3433b1bde88b4b6020100000000ffffffff020000000000000000096a074343010520121018ddf505000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000","assetId":"LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH"}

Now sign and broadcast the transaction as was demonstrated above. Here is the resulting issuance transaction for the new LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH asset, confirming that 100 units were issued on our funded address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR.

Next lets send 5 units of the LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH asset to a new address.

To send an asset to a new address we need to specify the target address. For the purpose of this tutorial we will just create a new random random address. Let's do that in a new file, call it transfer.js.

// transfer.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
var issuance_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';
var key = bitcoin.ECKey.makeRandom();
var to_address = key.pub.getAddress(bitcoin.networks.testnet).toString();
console.log('new TESTNET address: ['+to_address+']');
...

Now define the JSON specifying the source and target addresses as well as the AssetID and the amount of units to send

// transfer.js
...

var send_asset = {
'from': [issuance_address],
'fee': 5000,
'to': [{
'address': to_address,
'amount': 5,
'assetId': 'LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH'
}]
};
...

Next, let's build the transfer transaction by calling the sendasset API endpoint (using the same postToApi function)

// transfer.js
...
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

postToApi('sendasset', send_asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

And run the code by launching a terminal window and typing

> node transfer.js
--------------------
new TESTNET address: [mpeattSsFRzqYzDxXU6otxpgCzQNQbzjfM]
sendasset: {"from":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","fee":5000,"to":[{"address":"mpeattSsFRzqYzDxXU6otxpgCzQNQbzjfM","amount":5,"assetId":"LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH"}]}
Status: 200
Body: {"txHex":"01000000017aaeb19da971c550ac8b1f630262ef003f7965ddf533c9b98a425f2bded9d4ff0100000000ffffffff0358020000000000001976a914642a98ae9be32cbf8aadcd345532f21ac18f511b88ac0000000000000000086a06434301150005d8d6f505000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000","multisigOutputs":[]}

Now sign and broadcast the transaction. You can use our explorer to verify that the resulting transfer transaction transfered 5 units of the LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH asset, from the issuance address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR to the target address mpeattSsFRzqYzDxXU6otxpgCzQNQbzjfM that we created above.

 

 

 

 

Issue and Transfer

Issuing and transferring an asset in one transaction

One of the benefits of the colored coins protocol is the ability to issue an asset and send it within the same transaction. In this section we will use the colored coins API to issue & transfer an asset.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it issue_and_transfer.js.

As before, require the dependencies

// issue_and_transfer.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

Using the funded TESTNET address for issuance
// issue_and_transfer.js
...
address = 'your funded address here';
// e.g. address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';
...

Add a new Bitcoin address. This is where we are going to send some of the newly created asset.

// issue_and_transfer.js
...
key = bitcoin.ECKey.makeRandom();
new_address = key.pub.getAddress(bitcoin.networks.testnet).toString();
console.log('new TESTNET address: ['+new_address+']');
...

Use the familiar code for calling the colored coins API

// issue_and_transfer.js
...
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v2/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};
...

Now when we prepare the asset data we are going to add a transfer key, specifying the amount and target address:

// issue_and_transfer.js
...
var asset = {
'issueAddress': address,
'amount': 123,
'fee': 5000,
'transfer':[{
'address': new_address,
'amount': 33
}]
}
...

And finally add the API call

// issue_and_transfer.js
...
postToApi('issue', asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Launch a terminal window and run the new file

> node issue_and_transfer.js

with the following result:

Body:  "{\"txHex\":\"01000000011fc40634d28ba68319d87c3a0b754fba05baa2386fdcb50a73b5d9151f46c6d80000000000ffffffff0358020000000000001976a914b0ffe61e2a6daa62accc1a04e053da8e59b851a588ac00000000000000000c6a0a4343010527b000221010d6d9f505000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000\",\"assetId\":\"LKafUov95685QJ13FbwbDW6WmYExqXixankWC\"}"

This is the (hexadecimal representation) of an unsigned issuance transaction of a new asset, with assetID LKafUov95685QJ13FbwbDW6WmYExqXixankWC.

Following the same steps as before, sign and broadcast the transaction. Use our explorer to verify that resulting issuance and transfer transaction indeed issued 123 units of a new asset LKafUov95685QJ13FbwbDW6WmYExqXixankWC on address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR and sent 33 units to the address mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG.

Note that even though it was not explicitly specified, the remaining 90 units of the asset (since 123 were issued) were automatically sent back to the issuance address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR, as dictated by the transfer rules of the colored coins protocol.

 

 

 

 

Adding Metadata

In this section we describe how to include metadata when issuing or transferring an asset.

 

 

 

 

Issue with Metadata

In order to add metadata on asset issuance we need to include a metadata key to the asset definition.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it issue_with_metadata.js.

As before, we require the dependencies and use the postToApi function and our funded address (you should use the funded address created during setup.)

// issue_with_metadata.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
var funded_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

Now add a metadata key to the asset definition:

// issue_with_metadata.js
........
var asset = {
issueAddress: funded_address,
amount: 1000,
fee: 5000,
metadata: {
assetName: "Lexus Hoverboard",
issuer: "Marty McFly",
description: "Anti-Beef Hoverboard",
urls: [
{name: 'Wired Article',url: 'http://www.wired.com/2015/08/lexus-hoverboard/', mimeType: 'text/html', dataHash: '66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc'},
{name: 'utube promo',url: 'https://youtu.be/q_BYvUlDviM', mimeType: 'text/html', dataHash: '15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a'}
],
userData :{
meta: [
{key: 'Weight', value: 1234, type: 'Number'},
{key: 'Model', value: "Magneto", type: 'String'},
{key: 'In Stock', value: true, type: 'Boolean'}
],
price: 1234567,
"technical specification": 'In a weak applied field, a superconductor expels nearly all magnetic flux. It does this by setting up electric currents near its surface. The magnetic field of these surface currents cancels the applied magnetic field within the bulk of the superconductor. As the field expulsion, or cancellation, does not change with time, the currents producing this effect (called persistent currents) do not decay with time. Therefore the conductivity can be thought of as infinite: a superconductor. Near the surface, within a distance called the London penetration depth, the magnetic field is not completely cancelled. Each superconducting material has its own characteristic penetration depth. Any perfect conductor will prevent any change to magnetic flux passing through its surface due to ordinary electromagnetic induction at zero resistance. The Meissner effect is distinct from this: when an ordinary conductor is cooled so that it makes the transition to a superconducting state in the presence of a constant applied magnetic field, the magnetic flux is expelled during the transition. This effect cannot be explained by infinite conductivity alone. Its explanation is more complex and was first given in the London equations by the brothers Fritz and Heinz London. It should thus be noted that the placement and subsequent levitation of a magnet above an already superconducting material does not demonstrate the Meissner effect, while an initially stationary magnet later being repelled by a superconductor as it is cooled through its critical temperature does.'
}
}
};

postToApi('issue', asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Launch a terminal window and run the new file

> node issue_with_metadata.js
----------------------
Body: {"txHex":"0100000001706d55720506c968bdff2fcbc625bc558a4bca63a0fa747ee5af211b50350c450100000000ffffffff03ac0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21038ea9fc0be8a92d05545750375c79094aaa915bd6135fc3543a0ef8d036864d5852ae00000000000000001d6a1b43430102349b8dd84634428c83bfb755079eee288227531b2013100c800100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000","assetId":"LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE"}

resulting in the hexadecimal representation of the unsigned transaction. As before, proceed to sign and broadcast the transaction. Use our explorer to verify that resulting issuance transaction indeed issued 1000 units of the new Lexus Hoverboard asset on our funded address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR.

Clicking on the asset link 'Lexus Hoverboard' in the bottom right of the issuance transaction presents the following page where we can see all the metadata displayed (You may need to click on the METADATA header):

ASSET NAME: Lexus Hoverboard           LOCKED ASSET

DESCRIPTION
Anti-Biff Hoverboard
Total Supply 1
# Holders 1
SUMMARY
# Issuance 1
Divisibility 0
Meta Torrent Magent
utxo cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3:2
# Transfers 0
First Block 530912
METADATA
Weight
1234
Model
Magneto
In Stock
true
Name URL Type Hash
Wired Article http://www.wired.com/2015/08/lexus-hoverboard/ text/html 66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc
utube promo https://youtu.be/q_BYvUlDviM text/html 15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a

It is important to note the structure of the metadata. In principle, you can use any JSON object that you want. However, currently our explorer will recognizes and parse only the following keys structure (You may need to click on the METADATA header)

{
metadata: {
assetId: String,
assetName: String,
assetGenesis: String,
issuer: String,
description: String,
urls: [
{name_1, url_1, mimeType_1, dataHash_1},
{name_2, url_2, mimeType_2, dataHash_2},...
],
userData : {
meta: [
{key_1, value_1, type_1},
{key_2, value_2, type_2},
...
],
user_key1: user_value_1,
user_key2: user_value_2,
...
},
encryptions: [
{key: "user_key1", pubKey: 'RSA Public Key',format:'pem|der',type:'pkcs1|pkcs8' },
...
],
},
rules: {
fees: [{}],
expiration: {},
minters: [{}],
holders: [{}]
}
}

In particular, note that our explorer displays the values specified in the meta array, but does not display the other keys noted above as user_key1,... (e.g. price and technical specification in the asset definition).

In the upcoming Query an Asset|Asset Metadata section we explain how to use the API to retrieve asset metadata. Let's use that functionality without explaining how it works in detail

// query_metadata.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
function getFromApi(api_endpoint, param, callback) {
console.log('Get from:'+api_endpoint+'/'+param);
request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status:', response.statusCode);
console.log('Body:', body);
return callback(null, body);
});
};

assetid = 'LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE'
txid='cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3'
index = '2'

getFromApi('assetmetadata',assetid+'/'+txid+':'+index,function(err, body){
if (err) console.log('error: '+err);
});

we get the following respons:

{"assetId":"LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE","divisibility":0,"someUtxo":"cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3:2","totalSupply":1000,"numOfHolders":1,"numOfTransfers":0,"numOfIssuance":1,"firstBlock":-1,"issuanceTxid":"cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3","issueAddress":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","metadataOfIssuence":{"data":{"assetName":"Lexus Hoverboard","issuer":"Marty McFly","description":"Anti-Beef Hoverboard","urls":[{"name":"Wired Article","url":"http://www.wired.com/2015/08/lexus-hoverboard/","mimeType":"text/html","dataHash":"66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc"},{"name":"utube promo","url":"https://youtu.be/q_BYvUlDviM","mimeType":"text/html","dataHash":"15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a"}],"userData":{"meta":[{"key":"Weight","value":"1234","type":"Number"},{"key":"Model","value":"Magneto","type":"String"},{"key":"In Stock","value":"true","type":"Boolean"}],"price":"1234567","technical specification":"In a weak applied field, a superconductor expels nearly all magnetic flux. It does this by setting up electric currents near its surface. The magnetic field of these surface currents cancels the applied magnetic field within the bulk of the superconductor. As the field expulsion, or cancellation, does not change with time, the currents producing this effect (called persistent currents) do not decay with time. Therefore the conductivity can be thought of as infinite: a superconductor. Near the surface, within a distance called the London penetration depth, the magnetic field is not completely cancelled. Each superconducting material has its own characteristic penetration depth. Any perfect conductor will prevent any change to magnetic flux passing through its surface due to ordinary electromagnetic induction at zero resistance. The Meissner effect is distinct from this: when an ordinary conductor is cooled so that it makes the transition to a superconducting state in the presence of a constant applied magnetic field, the magnetic flux is expelled during the transition. This effect cannot be explained by infinite conductivity alone. Its explanation is more complex and was first given in the London equations by the brothers Fritz and Heinz London. It should thus be noted that the placement and subsequent levitation of a magnet above an already superconducting material does not demonstrate the Meissner effect, while an initially stationary magnet later being repelled by a superconductor as it is cooled through its critical temperature does."}}},"sha2Issue":"038ea9fc0be8a92d05545750375c79094aaa915bd6135fc3543a0ef8d036864d58"}

Unwrapping the JSON response we see under the metadataOfIssuence key the exact metadata that was put in:

{
"assetId": "LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE",
"divisibility": 0,
"someUtxo": "cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3:2",
"totalSupply": 1000,
"numOfHolders": 1,
"numOfTransfers": 0,
"numOfIssuance": 1,
"firstBlock": -1,
"issuanceTxid": "cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3",
"issueAddress": "n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR",
"metadataOfIssuence": {
"data": {
"assetName": "Lexus Hoverboard",
"issuer": "Marty McFly",
"description": "Anti-Beef Hoverboard",
"urls": [
{
"name": "Wired Article",
"url": "http://www.wired.com/2015/08/lexus-hoverboard/",
"mimeType": "text/html",
"dataHash": "66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc"
},
{
"name": "utube promo",
"url": "https://youtu.be/q_BYvUlDviM",
"mimeType": "text/html",
"dataHash": "15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a"
}
],
"userData": {
"meta": [
{
"key": "Weight",
"value": "1234",
"type": "Number"
},
{
"key": "Model",
"value": "Magneto",
"type": "String"
},
{
"key": "In Stock",
"value": "true",
"type": "Boolean"
}
],
"price": "1234567",
"technical specification": "In a weak applied field, a superconductor expels nearly all magnetic flux. It does this by setting up electric currents near its surface. The magnetic field of these surface currents cancels the applied magnetic field within the bulk of the superconductor. As the field expulsion, or cancellation, does not change with time, the currents producing this effect (called persistent currents) do not decay with time. Therefore the conductivity can be thought of as infinite: a superconductor. Near the surface, within a distance called the London penetration depth, the magnetic field is not completely cancelled. Each superconducting material has its own characteristic penetration depth. Any perfect conductor will prevent any change to magnetic flux passing through its surface due to ordinary electromagnetic induction at zero resistance. The Meissner effect is distinct from this: when an ordinary conductor is cooled so that it makes the transition to a superconducting state in the presence of a constant applied magnetic field, the magnetic flux is expelled during the transition. This effect cannot be explained by infinite conductivity alone. Its explanation is more complex and was first given in the London equations by the brothers Fritz and Heinz London. It should thus be noted that the placement and subsequent levitation of a magnet above an already superconducting material does not demonstrate the Meissner effect, while an initially stationary magnet later being repelled by a superconductor as it is cooled through its critical temperature does."
}
}
},
"sha2Issue": "038ea9fc0be8a92d05545750375c79094aaa915bd6135fc3543a0ef8d036864d58"
}

 

 

 

 

Transfer with Metadata

One of the strengths of the new colored coins protocol is the ability to add metadata to every colored transaction.

To do that we can use the same code that we created in the section Transfer an Asset and saved under the name transfer.js. The only difference being the addition of a metadata key to the send_asset definition.
So let's use that code to transfer 10 of the 1000 Lexus Hoverboards that were created in the previous section to a new random address:

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it transfer_with_metadata.js.
As before, we require the dependencies and use the postToApi function and our funded address (you should use the funded address created during setup).

// transfer_with_metadata.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
var issuance_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';
var key = bitcoin.ECKey.makeRandom();
var to_address = key.pub.getAddress(bitcoin.networks.testnet).toString();

var send_asset = {
from: [issuance_address],
to: [{
address: to_address,
amount: 10,
assetId: 'LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE'
}],
fee: 5000,
metadata: { assetName: 'laboris',
issuer: 'anim nisi consectetur',
description: 'Fugiat ipsum sunt amet reprehenderit irure.',
urls:
[ { name: 'magna',
url: 'http://D7I.com',
mimeType: 'text/html',
dataHash: '637b7a78fa119d05bde3765ac40c72d22906da7977640a79d11a291a73c9549' },
{ name: 'do',
url: 'http://2mz.com',
mimeType: 'text/html',
dataHash: 'd71ce3f84ff0a6fe78ce6448c995af6c530b7dfbdd6c5ac19b79e02183b17ab' },
{ name: 'et',
url: 'http://MLy.com',
mimeType: 'text/html',
dataHash: '1d736e670a061362b2d4ddc632ed5fe40d5554dabc8395d311f076a13848d16' } ],
userData:
{ meta:
[ { key: 'reprehenderit', value: '78584', type: 'Number' },
{ key: 'culpa', value: 'Pg2sxqj6CX', type: 'String' },
{ key: 'duis', value: true, type: 'Boolean' } ],
fookey: 'fBRiImkkFZdDt0ILvX8qJ7UmAllO8p',
barkey: '3081468253' } }
};
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};
postToApi('sendasset', send_asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Running this code we get

sendasset:  {"from":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","to":[{"address":"mgK6cNGVRhVWb2kyXzt7d4xGusbUgJ1s86","amount":10,"assetId":"LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE"}],"fee":5000,"metadata":{"assetName":"laboris","issuer":"anim nisi consectetur","description":"Fugiat ipsum sunt amet reprehenderit irure.","urls":[{"name":"magna","url":"http://D7I.com","mimeType":"text/html","dataHash":"637b7a78fa119d05bde3765ac40c72d22906da7977640a79d11a291a73c9549"},{"name":"do","url":"http://2mz.com","mimeType":"text/html","dataHash":"d71ce3f84ff0a6fe78ce6448c995af6c530b7dfbdd6c5ac19b79e02183b17ab"},{"name":"et","url":"http://MLy.com","mimeType":"text/html","dataHash":"1d736e670a061362b2d4ddc632ed5fe40d5554dabc8395d311f076a13848d16"}],"userData":{"meta":[{"key":"reprehenderit","value":"78584","type":"Number"},{"key":"culpa","value":"Pg2sxqj6CX","type":"String"},{"key":"duis","value":true,"type":"Boolean"}],"fookey":"fBRiImkkFZdDt0ILvX8qJ7UmAllO8p","barkey":"3081468253"}}}
Status: 200
Body:
{"txHex":"0100000001c3a40d0d2bf7dd03e0bc9c23d1b221e2974708bf20274ff04017727b91060ccb0200000000ffffffff04ac0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff21034c47375e6142fd37322cd96cd66727e54cf21a55860290b2cbed0d72fcada26252ae58020000000000001976a91408ba136ce59e64c5d6cb350ce7902a08d4960d0d88ac00000000000000001c6a1a43430111227b0976bfc34cb7bbab3cf1b2f0a2728a3a25da010a20770100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000","metadataSha1":"227b0976bfc34cb7bbab3cf1b2f0a2728a3a25da","multisigOutputs":"[]"}

resulting in the hexadecimal representation of the unsigned transaction. As before, proceed to sign and broadcast the transaction. Use our explorer to verify that resulting transfer transaction indeed transferred 10 units of the Lexus Hoverboard asset from the issuance address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR to a new address.

We can query for the asset metadata (more details later in the Query an Asset|Asset Metadata section):

// query_metadata.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
function getFromApi(api_endpoint, param, callback) {
console.log('Get from:'+api_endpoint+'/'+param);
request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status:', response.statusCode);
console.log('Body:', body);
return callback(null, body);
});
};

assetid = 'LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE'
txid='192d4744b63bf0c59a6e7dc947de05e015cc2ef4e70982bd5189770b0b9cbf34'
index = '1'

getFromApi('assetmetadata',assetid+'/'+txid+':'+index,function(err, body){
if (err) console.log('error: '+err);
});

The reply is:

{"assetId":"LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE","divisibility":0,"someUtxo":"192d4744b63bf0c59a6e7dc947de05e015cc2ef4e70982bd5189770b0b9cbf34:1","totalSupply":1000,"numOfHolders":2,"numOfTransfers":1,"numOfIssuance":1,"firstBlock":530912,"issuanceTxid":"cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3","issueAddress":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","metadataOfIssuence":{"data":{"assetName":"Lexus Hoverboard","issuer":"Marty McFly","description":"Anti-Beef Hoverboard","urls":[{"name":"Wired Article","url":"http://www.wired.com/2015/08/lexus-hoverboard/","mimeType":"text/html","dataHash":"66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc"},{"name":"utube promo","url":"https://youtu.be/q_BYvUlDviM","mimeType":"text/html","dataHash":"15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a"}],"userData":{"meta":[{"key":"Weight","value":"1234","type":"Number"},{"key":"Model","value":"Magneto","type":"String"},{"key":"In Stock","value":"true","type":"Boolean"}],"price":"1234567","technical specification":"In a weak applied field, a superconductor expels nearly all magnetic flux. It does this by setting up electric currents near its surface. The magnetic field of these surface currents cancels the applied magnetic field within the bulk of the superconductor. As the field expulsion, or cancellation, does not change with time, the currents producing this effect (called persistent currents) do not decay with time. Therefore the conductivity can be thought of as infinite: a superconductor. Near the surface, within a distance called the London penetration depth, the magnetic field is not completely cancelled. Each superconducting material has its own characteristic penetration depth. Any perfect conductor will prevent any change to magnetic flux passing through its surface due to ordinary electromagnetic induction at zero resistance. The Meissner effect is distinct from this: when an ordinary conductor is cooled so that it makes the transition to a superconducting state in the presence of a constant applied magnetic field, the magnetic flux is expelled during the transition. This effect cannot be explained by infinite conductivity alone. Its explanation is more complex and was first given in the London equations by the brothers Fritz and Heinz London. It should thus be noted that the placement and subsequent levitation of a magnet above an already superconducting material does not demonstrate the Meissner effect, while an initially stationary magnet later being repelled by a superconductor as it is cooled through its critical temperature does."}}},"sha2Issue":"038ea9fc0be8a92d05545750375c79094aaa915bd6135fc3543a0ef8d036864d58","metadataOfUtxo":{"data":{"assetName":"laboris","issuer":"anim nisi consectetur","description":"Fugiat ipsum sunt amet reprehenderit irure.","urls":[{"name":"magna","url":"http://D7I.com","mimeType":"text/html","dataHash":"637b7a78fa119d05bde3765ac40c72d22906da7977640a79d11a291a73c9549"},{"name":"do","url":"http://2mz.com","mimeType":"text/html","dataHash":"d71ce3f84ff0a6fe78ce6448c995af6c530b7dfbdd6c5ac19b79e02183b17ab"},{"name":"et","url":"http://MLy.com","mimeType":"text/html","dataHash":"1d736e670a061362b2d4ddc632ed5fe40d5554dabc8395d311f076a13848d16"}],"userData":{"meta":[{"key":"reprehenderit","value":"78584","type":"Number"},{"key":"culpa","value":"Pg2sxqj6CX","type":"String"},{"key":"duis","value":"true","type":"Boolean"}],"fookey":"fBRiImkkFZdDt0ILvX8qJ7UmAllO8p","barkey":"3081468253"}}},"sha2Utxo":"034c47375e6142fd37322cd96cd66727e54cf21a55860290b2cbed0d72fcada262"}

which when unwrapped gives:

{
"assetId": "LEjtW9dppmaqEEibUVKSY1uAoZCfXP1TDierE",
"divisibility": 0,
"someUtxo": "192d4744b63bf0c59a6e7dc947de05e015cc2ef4e70982bd5189770b0b9cbf34:1",
"totalSupply": 1000,
"numOfHolders": 2,
"numOfTransfers": 1,
"numOfIssuance": 1,
"firstBlock": 530912,
"issuanceTxid": "cb0c06917b721740f04f2720bf084797e221b2d1239cbce003ddf72b0d0da4c3",
"issueAddress": "n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR",
"metadataOfIssuence": {
"data": {
"assetName": "Lexus Hoverboard",
"issuer": "Marty McFly",
"description": "Anti-Beef Hoverboard",
"urls": [
{
"name": "Wired Article",
"url": "http://www.wired.com/2015/08/lexus-hoverboard/",
"mimeType": "text/html",
"dataHash": "66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc"
},
{
"name": "utube promo",
"url": "https://youtu.be/q_BYvUlDviM",
"mimeType": "text/html",
"dataHash": "15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a"
}
],
"userData": {
"meta": [
{
"key": "Weight",
"value": "1234",
"type": "Number"
},
{
"key": "Model",
"value": "Magneto",
"type": "String"
},
{
"key": "In Stock",
"value": "true",
"type": "Boolean"
}
],
"price": "1234567",
"technical specification": "In a weak applied field, a superconductor expels nearly all magnetic flux. It does this by setting up electric currents near its surface. The magnetic field of these surface currents cancels the applied magnetic field within the bulk of the superconductor. As the field expulsion, or cancellation, does not change with time, the currents producing this effect (called persistent currents) do not decay with time. Therefore the conductivity can be thought of as infinite: a superconductor. Near the surface, within a distance called the London penetration depth, the magnetic field is not completely cancelled. Each superconducting material has its own characteristic penetration depth. Any perfect conductor will prevent any change to magnetic flux passing through its surface due to ordinary electromagnetic induction at zero resistance. The Meissner effect is distinct from this: when an ordinary conductor is cooled so that it makes the transition to a superconducting state in the presence of a constant applied magnetic field, the magnetic flux is expelled during the transition. This effect cannot be explained by infinite conductivity alone. Its explanation is more complex and was first given in the London equations by the brothers Fritz and Heinz London. It should thus be noted that the placement and subsequent levitation of a magnet above an already superconducting material does not demonstrate the Meissner effect, while an initially stationary magnet later being repelled by a superconductor as it is cooled through its critical temperature does."
}
}
},
"sha2Issue": "038ea9fc0be8a92d05545750375c79094aaa915bd6135fc3543a0ef8d036864d58",
"metadataOfUtxo": {
"data": {
"assetName": "laboris",
"issuer": "anim nisi consectetur",
"description": "Fugiat ipsum sunt amet reprehenderit irure.",
"urls": [
{
"name": "magna",
"url": "http://D7I.com",
"mimeType": "text/html",
"dataHash": "637b7a78fa119d05bde3765ac40c72d22906da7977640a79d11a291a73c9549"
},
{
"name": "do",
"url": "http://2mz.com",
"mimeType": "text/html",
"dataHash": "d71ce3f84ff0a6fe78ce6448c995af6c530b7dfbdd6c5ac19b79e02183b17ab"
},
{
"name": "et",
"url": "http://MLy.com",
"mimeType": "text/html",
"dataHash": "1d736e670a061362b2d4ddc632ed5fe40d5554dabc8395d311f076a13848d16"
}
],
"userData": {
"meta": [
{
"key": "reprehenderit",
"value": "78584",
"type": "Number"
},
{
"key": "culpa",
"value": "Pg2sxqj6CX",
"type": "String"
},
{
"key": "duis",
"value": "true",
"type": "Boolean"
}
],
"fookey": "fBRiImkkFZdDt0ILvX8qJ7UmAllO8p",
"barkey": "3081468253"
}
}
},
"sha2Utxo": "034c47375e6142fd37322cd96cd66727e54cf21a55860290b2cbed0d72fcada262"
}

The original issuance metadata (the one still showing in the explorer) still appears under the metadataOfIssuence key (as it did in the previous section). However now we also have a new key, metadataOfUtxo, showing the totally independent metadata that was added during this transfer transaction.

 

 

 

 

Query an Asset

In this section we will use the API to retrieve data about an asset.

 

 

 

 

Asset Holders

In this section we cover issuing a GET request to the colored coins API for a list of an asset's holders. Asset's holders are the set of Bitcoin addresses with a non-zero balance for the asset in question.

Query Asset Holders

create a new file in the same directory where you installed BitcoinJS & Node js

save the following into that file, say we call it asset_holders.js.

First requiring the dependencies

// asset_holders.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

Now define a second auxiliary function, this time helping us with GET requests from the colored coins API.
This function accepts the API end point and a parameter (as well as a callback).

// asset_holders.js
...
function getFromApi(api_endpoint, param, callback) {
console.log('Get from:'+api_endpoint+'/'+param);
request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status:', response.statusCode);
console.log('Body:', body);
return callback(null, body);
});
};
...

Now let's store in a variable the value of the AssetID we are actually interested in. Let's use the LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH asset we issued and transferred in the Transfer an Asset section.

// asset_holders.js
....
var assetid='LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH' //your asset ID here;

In this case we are issuing a GET call to the 'stakeholders' API endpoint.

// asset_holders.js
....
getFromApi('stakeholders',assetid,function(err, body){
if (err) console.log('error: ', err);
});

Now launch a terminal window and type

> node asset_holders.js
----------------
Get from:stakeholders/LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH
Status: 200
Body: {"assetId":"LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH","holders":[{"address":"mpeattSsFRzqYzDxXU6otxpgCzQNQbzjfM","amount":5},{"address":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","amount":95}],"divisibility":0}

Let's unwrap the JSON object for easier view:

{
assetId: 'LKXjG9uMSFoDj2Z6NrEJ6nkcRGVtjUmC4zrtH',
holders: [{
'address': 'mpeattSsFRzqYzDxXU6otxpgCzQNQbzjfM',
'amount':5
},{
'address': 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR',
'amount':95
}],
divisibility: 0
}

As one would expect, the response contains a 'holders' key whose value is a list of JSON objects, each specifying a Bitcoin address and the amount of asset units currently held in that address.

 

 

 

 

Asset Metadata

In this section we cover issuing a GET request to the colored coins API for a full listing of an asset's metadata.

Query Asset Metadata

create a new file in the same directory where you installed BitcoinJS & Node js

save the following into that file, say we call it asset_metadata.js.

We use here the exact same code as was used in the previous section where we queried asset holders.

// asset_metadata.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

function getFromApi(api_endpoint, param, callback) {
console.log('Get from:'+api_endpoint+'/'+param);
request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status:', response.statusCode);
console.log('Body:', body);
return callback(null, body);
});
};

Let's try to retrieve the metadata of the Lexus Hoverboard asset we issued in the Issue with Metadata section. In this case we need more than just the AssetID.

we must specify the transaction output we are interested in.

Looking at the list of outputs in the issuance transaction we see that our asset is listed in the third place (index #2).

TRANSACTION INFORMATION
f36bb85d9f9819620c7eb0efe3af8c... Sent 0.00099 Asset Sent 1
Inputs Amount Asset
n2t19a46cBs2DdH... 0.001 tBTC

Outputs Amount Asset
mxsQRy4qbWcBXpC... 0.00000684 tBTC
Non Standard 0 tBTC
n2t19a46cBs2DdH 0.00098316 tBTC
1 Lexus Hoverboard
Raw HEX

The way to specify an transaction output is in using the format

txid:index

So in this case:
f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd:2

Let's store this information in variables

// asset_metadata.js
....
assetid = 'LCNeqPsxVSXZEZa6gm84s9pj885FTb27nEfJB'
txid='f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd'
index = '2'
....

In this case we are using the 'assetmetadata' endpoint, expecting the data in the following format

http://api..../assetmetadata/'transaction_id':'output_index'

So, let's prepare the call:


// asset_metadata.js
...
getFromApi('assetmetadata',assetid+'/'+txid+':'+index,function(err, body){
if (err) console.log('error: '+err);
});

Now launch a terminal window and type

> node asset_metadata.js
----------------
Get from:assetmetadata/LCNeqPsxVSXZEZa6gm84s9pj885FTb27nEfJB/f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd:2
Status: 200
Body: {"assetId":"LCNeqPsxVSXZEZa6gm84s9pj885FTb27nEfJB","divisibility":0,"someUtxo":"f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd:2","totalSupply":1,"numOfHolders":1,"numOfTransfers":0,"numOfIssuance":1,"firstBlock":529282,"issuanceTxid":"f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd","issueAddress":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","metadataOfIssuence":{"data":{"assetName":"Lexus Hoverboard","issuer":"Marty McFly","description":"Anti-Biff Hoverboard","urls":[{"name":"Wired Article","url":"http://www.wired.com/2015/08/lexus-hoverboard/","mimeType":"text/html","dataHash":"66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc"},{"name":"utube promo","url":"https://youtu.be/q_BYvUlDviM","mimeType":"text/html","dataHash":"15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a"}],"userData":{"meta":[{"key":"Weight","value":"1234","type":"Number"},{"key":"Model","value":"Magneto","type":"String"},{"key":"In Stock","value":"true","type":"Boolean"}]}}},"sha2Issue":"038c83f326060df69136a6566af7a430b3be93b68ce64e7d464ffdadef25b63fc2"}

let's unwrap the JSON response so that we can view it more easily:

{
"assetId": "LCNeqPsxVSXZEZa6gm84s9pj885FTb27nEfJB",
"divisibility": 0,
"someUtxo": "f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd:2",
"totalSupply": 1,
"numOfHolders": 1,
"numOfTransfers": 0,
"numOfIssuance": 1,
"firstBlock": 529282,
"issuanceTxid": "f36bb85d9f9819620c7eb0efe3af8ce8cdf1e5317cac96afd806fecfcae08acd",
"issueAddress": "n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR",
"metadataOfIssuence": {
"data": {
"assetName": "Lexus Hoverboard",
"issuer": "Marty McFly",
"description": "Anti-Biff Hoverboard",
"urls": [
{
"name": "Wired Article",
"url": "http://www.wired.com/2015/08/lexus-hoverboard/",
"mimeType": "text/html",
"dataHash": "66111ceeed2b7bb977dc2354e870a655a91f2c6909347bf53d3d7918b5743fc"
},
{
"name": "utube promo",
"url": "https://youtu.be/q_BYvUlDviM",
"mimeType": "text/html",
"dataHash": "15b7c49342a7b74dfbc98506930f84ff55cc0839a8960a991b5103a44aeb20a"
}
],
"userData": {
"meta": [
{
"key": "Weight",
"value": "1234",
"type": "Number"
},
{
"key": "Model",
"value": "Magneto",
"type": "String"
},
{
"key": "In Stock",
"value": "true",
"type": "Boolean"
}
]
}
}
},
"sha2Issue": "038c83f326060df69136a6566af7a430b3be93b68ce64e7d464ffdadef25b63fc2"
}

As expected, the metadata that we inserted during issuance is there.

 

 

 

 

Query an Address

Querying a Bitcoin Address

Let's use the colored coins API to query for the content of the address mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG, where we sent 5 units of the LKafUov95685QJ13FbwbDW6WmYExqXixankWC asset in the Issue and Transfer section.

create a new file in the same directory where you installed BitcoinJS & Node js

save the following into that file, say we call it query_address.js.

We use here the exact same code as was used in the previous sections where we queried the asset.

// query_address.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

function getFromApi(api_endpoint, param, callback) {
console.log('Get from:'+api_endpoint+'/'+param);
request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status:', response.statusCode);
console.log('Body:', body);
return callback(null, body);
});
};
...

In this case we are working against the 'addressinfo' API endpoint:

// query_address.js
....
address='mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG';
getFromApi('addressinfo',address,function(err, body){
if (err) consule.log('error: ', err);
});

Let's launch a terminal and run the file

> node query_address.js
------------------------------
Get from:addressinfo/mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG
Status: 200
Body: {"address":"mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG","utxos":[{"_id":"55c74be26c180630bc076a50","txid":"365a53533185f48c0e5dc77c1432db60b2a006f4d3b5565374cffb095524e9ab","index":0,"value":600,"blockheight":528938,"blocktime":"2015-08-09T14:48:58.000Z","used":false,"assets":[{"assetId":"LKafUov95685QJ13FbwbDW6WmYExqXixankWC","amount":33,"issueTxid":"365a53533185f48c0e5dc77c1432db60b2a006f4d3b5565374cffb095524e9ab","divisibility":0}],"scriptPubKey":{"asm":"OP_DUP OP_HASH160 b0ffe61e2a6daa62accc1a04e053da8e59b851a5 OP_EQUALVERIFY OP_CHECKSIG","hex":"76a914b0ffe61e2a6daa62accc1a04e053da8e59b851a588ac","reqSigs":1,"type":"pubkeyhash","addresses":["mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG"]}}]}

If we unwrap the JSON we get this:

{
"address": "mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG",
"utxos": [
{
"_id": "55c74be26c180630bc076a50",
"txid": "365a53533185f48c0e5dc77c1432db60b2a006f4d3b5565374cffb095524e9ab",
"index": 0,
"value": 600,
"blockheight": 528938,
"blocktime": "2015-08-09T14:48:58.000Z",
"used": false,
"assets": [
{
"assetId": "LKafUov95685QJ13FbwbDW6WmYExqXixankWC",
"amount": 33,
"issueTxid": "365a53533185f48c0e5dc77c1432db60b2a006f4d3b5565374cffb095524e9ab",
"divisibility": 0
}
],
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 b0ffe61e2a6daa62accc1a04e053da8e59b851a5 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914b0ffe61e2a6daa62accc1a04e053da8e59b851a588ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"mweqmyz5SeNv8Zgdhboz55G7xZL9f9jQVG"
]
}
}
]
}

On top of the standard Bitcoin data one can get from any TESTNET explorer (e.g. Toshi) we get here also data about the asset content of the address (in this case holding 33 units of the LKafUov95685QJ13FbwbDW6WmYExqXixankWC asset).

 

 

 

 

Advanced Topics

Metadata Encryption

The colored coins protocol supports metadata encryption.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it metadata_encryption.js.
As before, we require the dependencies and use the postToApi function and our funded address: n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR (you should use the funded address created during setup).

If this address is not funded, you should fund it with BTC.

// metadata_encryption.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
var funded_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

As we did in the Adding Metadata|Issue with Metadata section we need to add a metadata key to the asset definition.
So this time let's issue a new asset, a time machine in fact, with the following metadata:

// metadata_encryption.js
.......
metadata: {
assetName: "Time Machine",
issuer: "Dr. Emmet Brown",
description: "The flux capacitor will send us back to the future",
urls: [
{name: 'imdb',url: 'http://www.imdb.com/title/tt0088763/', mimeType: 'text/html', dataHash: '249e3e3c77d07d8fe8984a47bbbab8c89aeb8b1dadf4e2ff47db42a3e5a1c126'},
],
userData :{
meta: [
{key: 'Weight', value: 50000, type: 'Number'},
{key: 'Model', value: "Delorean", type: 'String'},
],
technology: 'flux capacitor 666',
"Undelrying Physics": 'This magnetic flux calculator calculates the magnetic flux of an object based on the magnitude of the magnetic field which the object emanates and the area of the object, according to the formula, Φ=BA, if the magnetic field is at a 90° angle (perpendicular) to the area of the object. If the magnetic field is not perpendicular to the object, then use the calculator below, which computes the magnetic flux at non-perpendicular angles. The magnetic flux is directly proportional to the magnitude of the magnetic field emanating from the object and the area of the object. The greater the magnetic field, the greater the magnetic flux. Conversely, the smaller the magnetic field, the smaller the flux. The area of the object has the same direct relationship. The greater the area of an object, the greater the flux. Conversely, the smaller the area, the smaller the magnetic flux.'
}
}

However, since our time travel technology is secret, we would like to encrypt the content of the Underlying Physics key. The colored coins protocol supports encrypting the value of userData keys (except for the ones that appear under the meta key array) using RSA public key encryption.

We can add an RSA public/private key pair by installing an additional library, called keypair.
To do that type in the console

sudo npm install keypair

RSA keys come in different formats so we need to specify if we are using the pem or der format. On top of that we need to specify which padding standard we are using for the key, pkcs1 or pkcs8.

We specify all this information in additional encryptions key in the metadata JSON, like so:

// metadata_encryption.js
.......
var keypair = require('keypair');
var pair = keypair();

var asset = {
issueAddress: funded_address,
amount: 50,
fee: 5000,
metadata: {
assetName: "Time Machine",
issuer: "Dr. Emmet Brown",
description: "The flux capacitor will send us back to the future",
urls: [
{name: 'imdb',url: 'http://www.imdb.com/title/tt0088763/', mimeType: 'text/html', dataHash: '249e3e3c77d07d8fe8984a47bbbab8c89aeb8b1dadf4e2ff47db42a3e5a1c126'},
],
encryptions: [
{key: "Undelrying Physics", pubKey: pair['public'],format:"pem",type:'pkcs1' }
],
userData :{
meta: [
{key: 'Weight', value: 50000, type: 'Number'},
{key: 'Model', value: "Delorean", type: 'String'},
],
technology: 'flux capacitor 666',
"Undelrying Physics": 'This magnetic flux calculator calculates the magnetic flux of an object based on the magnitude of the magnetic field which the object emanates and the area of the object, according to the formula, Φ=BA, if the magnetic field is at a 90° angle (perpendicular) to the area of the object. If the magnetic field is not perpendicular to the object, then use the calculator below, which computes the magnetic flux at non-perpendicular angles. The magnetic flux is directly proportional to the magnitude of the magnetic field emanating from the object and the area of the object. The greater the magnetic field, the greater the magnetic flux. Conversely, the smaller the magnetic field, the smaller the flux. The area of the object has the same direct relationship. The greater the area of an object, the greater the flux. Conversely, the smaller the area, the smaller the magnetic flux.'
}
}
};

In this example we created a keypair object by requiring the keypair library, and instructed encrypting the value of the "Undelrying Physics" userData key using the public key of the keypair, using the 'pem' format and 'pkcs1' padding standard.

Now we just need to make the API call:

// metadata_encryption.js
.......
postToApi('issue', asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

and run the code to get:

> node  metadata_encryption.js
----------------------
Body: {"txHex":"010000000173044e4c41d8f058c4fd31a8d67733908f617eb93fe792378e6615f45358729f0100000000ffffffff03ac0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2103e339927c047b00d57cea117b39b163c8bc14dfd852a1acfa6adcef50bf08d0e252ae00000000000000001d6a1b43430102c31c5f8505bde610b1fbf1634fa986410aa7064a2051100c800100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000","assetId":"LDJMbzwCBWhrrXpKS7TrCfoAWYgXQhwZg1G6R"}

resulting in the hexadecimal representation of the unsigned transaction. As before, proceed to sign and broadcast the transaction. Use our explorer to verify that resulting issuance transaction indeed issued 50 units of the new Time Machine asset on our funded address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR.

Let's verify the encryption took place by retrieving the asset metadata (more details later in the Query an Asset|Asset Metadata section):

// query_metadata.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');
function getFromApi(api_endpoint, param, callback) {
console.log('Get from:'+api_endpoint+'/'+param);
request.get('http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint+'/'+param, function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status:', response.statusCode);
console.log('Body:', body);
return callback(null, body);
});
};

assetid = 'LDJMbzwCBWhrrXpKS7TrCfoAWYgXQhwZg1G6R'
txid='66a681cf98ef2f03300c5c1aec7cd84862fe19567aa0334a424ff2a69cceebdc'
index = '2'

getFromApi('assetmetadata',assetid+'/'+txid+':'+index,function(err, body){
if (err) console.log('error: '+err);
});

The reply is:

{"assetId":"LDJMbzwCBWhrrXpKS7TrCfoAWYgXQhwZg1G6R","divisibility":0,"someUtxo":"66a681cf98ef2f03300c5c1aec7cd84862fe19567aa0334a424ff2a69cceebdc:2","totalSupply":50,"numOfHolders":1,"numOfTransfers":0,"numOfIssuance":1,"firstBlock":530925,"issuanceTxid":"66a681cf98ef2f03300c5c1aec7cd84862fe19567aa0334a424ff2a69cceebdc","issueAddress":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","metadataOfIssuence":{"data":{"assetName":"Time Machine","issuer":"Dr. Emmet Brown","description":"The flux capacitor will send us back to the future","urls":[{"name":"imdb","url":"http://www.imdb.com/title/tt0088763/","mimeType":"text/html","dataHash":"249e3e3c77d07d8fe8984a47bbbab8c89aeb8b1dadf4e2ff47db42a3e5a1c126"}],"encryptions":[{"key":"Undelrying Physics","pubKey":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA0hw6PRO9RpHRf/pdpEMfD01odzBTaheuA1JxunVTq+/X1hGSUrpRWMIM/tp8\n9DQod6K+6Bo/2CmoZxkWPOk45tbU9QE4Cb532n+MIkzsmbvmM+i49UXSqC8v44MGKTVLb7X2\nPogItSM3lqH4KpZR3cM/JDarfS1R77U/OMDZ/YECDPbcwKPdSLQHhWJ1c9cX5+0lCSDt1WXY\n4XX+hH64C+L/Ss4dMP2kpyHvbsBYpGdLu7AmcDmHtCOl2rXR1z4E0asYGiojw3PI56ATOndS\n30ABKKgQTAExjPQ24BtJYhfJ+zD5zHhztizPPfOwrID2HTfGwVTwfXinV4bpoFfwhwIDAQAB\n-----END RSA PUBLIC KEY-----\n","format":"pem","type":"pkcs1"}],"userData":{"meta":[{"key":"Weight","value":"50000","type":"Number"},{"key":"Model","value":"Delorean","type":"String"}],"technology":"flux capacitor 666","Undelrying Physics":"cv2gyjn/w9HOttpdhODnJcSGmOyhrTVsmAlnWDeIzh8VSvOGYXjM8Bkxwf9Ao5KhSf2GmFzIZNcbneNPIf1V2VnN2PBDv9oEXcZwKqf+2cJtWCK5CbwwCY4hBlSbtGT+Fs/8AWHc5blBREXZn9xMeubgUJ/H2dEiW5qK1cizlOsE/Qv8/GaRoRE7uFcCfhFqgJIFMQnYJeUtiYQoDkzsvgS84zKi1i2WjrCS6b99MQVE8mUvWP606V5nVDLDDSqe4L/pk5BCDMJ4y0ZusSvD3j7aC4jP7Z3Nyog7kcFUYFiYxVS2n6dL0vOr+gI3nvBLQda1GfQzoK3av1L8N4TkEEb9A9uJ26LUVERLoz5TRayUaGfY8a+QXnnb2Pifa69JsotTrG/qgXaltXXUo9xZwc8zKM2FkXN6iQK9g5R2Vya90t6n37ieGpC4QEFKhzvJ59VDpFNshkLhk5rxg84ulWItUILkHstGyJJblMdhNdcaecV57k2qiLbHHcz41JtbIv+mvmdHAkrc5Det+dRFvqzXN/jv6UVxk5bvX1pos+0c53m/JOgR3YIgxsHhCy8kNGj3dwy2jW8jvOll3xlDDWXbuh9fwQriJdFxh/7aknOoWc6h9g2XBSrf3p6wTEARhZ+QQKvLeY07KpzJ6w8f6lBiojcnpTqsRHOCFYjM3hsqmIcPrOPrWJG9HVk+Iwpi8XvITtuLUboKztot115CmSyat1HmJShgzmCM16W8ms7cG2qmkyUewxh0iu4jmLkVGIZRNbKRBNRJJ6t4thF0u9tNzs7PSW3ewjtppRRuqZcZvhzI5zNuOhYY9TNB+8kfrSQvUbtOGGKhjUS4T/yu4Q25oANsjTenQpEm3xxW63HIIGAGXTfzLV8pssaZtvsER37qZLacVY4XMY9NU6BiX4kyi9lb4foYJyMoqUGAOAqJJMheU+1qa4i4G0cspzzMopgCCaopzcG33eXVO5ncXVfmTc5Il8j9ZNhWVckzbFLPJRicrOrQIXW+rK9ZCfyog9jmRPupWXpgD88R4HxZWdObSN7tOTukcuKUtKhBVe8SXlE3501lMJSnwRxOt/djiUKRkQS4zbUsQGNfWVlpP0vwmTXO7jWPOKslOFVhBMVbTxr+HkzZAMGrN8a9gfTclBchNzHyJk9mSNSMcKfc6lzFKuIO2HA/IGUem2LgvlMgEmsjs7xRuFYRImlnhiajZ97P6g65n2pfsC3nE4nkP33Y6h1OlHqCtpSTKZJvlvVeNmeSvZAX811MeILunyjFcVSWDMpEaV4fIHc8bHeCYPKozUVneeYu75B5CgGZMhoqSBRUlHgSgvwFkqiEPAEWEWbSeFYFDrli/kNuOMmjKzHNZxOMXROmDQQ6Gzrcca3/JnMYiX7rEAuOZUYIrf6nX72ZovqWKsz0aI/WG8qb2nMfqfSkE2wwoG8MbxK8tvyPIZGWfySabN57zYGoyaJL5bjWuTGSFWEprgtIZyL8OIde6BTnSQONNBiwsd+S+0jvtV4CMwjNde0sBnDGzEViaqNOf1lU+5UQMKX9St6mG+foFfg3KBODN4PInw5UVuhT69ZIxXLvOazZ0ElpXIBKbgatw5IvvArplD4MJbNXi+XfMH76nQZ+1/MOy/oXtMhV5b33k0qqqB+Fn8pB0qwOVXLbC029FPP0vtW19/PKfNmlCjnnhedl4369u1U73gU="}}},"sha2Issue":"03e339927c047b00d57cea117b39b163c8bc14dfd852a1acfa6adcef50bf08d0e2"}

unwrapping the JSON we get:

{
"assetId": "LDJMbzwCBWhrrXpKS7TrCfoAWYgXQhwZg1G6R",
"divisibility": 0,
"someUtxo": "66a681cf98ef2f03300c5c1aec7cd84862fe19567aa0334a424ff2a69cceebdc:2",
"totalSupply": 50,
"numOfHolders": 1,
"numOfTransfers": 0,
"numOfIssuance": 1,
"firstBlock": -1,
"issuanceTxid": "66a681cf98ef2f03300c5c1aec7cd84862fe19567aa0334a424ff2a69cceebdc",
"issueAddress": "n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR",
"metadataOfIssuence": {
"data": {
"assetName": "Time Machine",
"issuer": "Dr. Emmet Brown",
"description": "The flux capacitor will send us back to the future",
"urls": [
{
"name": "imdb",
"url": "http://www.imdb.com/title/tt0088763/",
"mimeType": "text/html",
"dataHash": "249e3e3c77d07d8fe8984a47bbbab8c89aeb8b1dadf4e2ff47db42a3e5a1c126"
}
],
"encryptions": [
{
"key": "Undelrying Physics",
"pubKey": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA0hw6PRO9RpHRf/pdpEMfD01odzBTaheuA1JxunVTq+/X1hGSUrpRWMIM/tp8\n9DQod6K+6Bo/2CmoZxkWPOk45tbU9QE4Cb532n+MIkzsmbvmM+i49UXSqC8v44MGKTVLb7X2\nPogItSM3lqH4KpZR3cM/JDarfS1R77U/OMDZ/YECDPbcwKPdSLQHhWJ1c9cX5+0lCSDt1WXY\n4XX+hH64C+L/Ss4dMP2kpyHvbsBYpGdLu7AmcDmHtCOl2rXR1z4E0asYGiojw3PI56ATOndS\n30ABKKgQTAExjPQ24BtJYhfJ+zD5zHhztizPPfOwrID2HTfGwVTwfXinV4bpoFfwhwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"format": "pem",
"type": "pkcs1"
}
],
"userData": {
"meta": [
{
"key": "Weight",
"value": "50000",
"type": "Number"
},
{
"key": "Model",
"value": "Delorean",
"type": "String"
}
],
"technology": "flux capacitor 666",
"Undelrying Physics": "cv2gyjn/w9HOttpdhODnJcSGmOyhrTVsmAlnWDeIzh8VSvOGYXjM8Bkxwf9Ao5KhSf2GmFzIZNcbneNPIf1V2VnN2PBDv9oEXcZwKqf+2cJtWCK5CbwwCY4hBlSbtGT+Fs/8AWHc5blBREXZn9xMeubgUJ/H2dEiW5qK1cizlOsE/Qv8/GaRoRE7uFcCfhFqgJIFMQnYJeUtiYQoDkzsvgS84zKi1i2WjrCS6b99MQVE8mUvWP606V5nVDLDDSqe4L/pk5BCDMJ4y0ZusSvD3j7aC4jP7Z3Nyog7kcFUYFiYxVS2n6dL0vOr+gI3nvBLQda1GfQzoK3av1L8N4TkEEb9A9uJ26LUVERLoz5TRayUaGfY8a+QXnnb2Pifa69JsotTrG/qgXaltXXUo9xZwc8zKM2FkXN6iQK9g5R2Vya90t6n37ieGpC4QEFKhzvJ59VDpFNshkLhk5rxg84ulWItUILkHstGyJJblMdhNdcaecV57k2qiLbHHcz41JtbIv+mvmdHAkrc5Det+dRFvqzXN/jv6UVxk5bvX1pos+0c53m/JOgR3YIgxsHhCy8kNGj3dwy2jW8jvOll3xlDDWXbuh9fwQriJdFxh/7aknOoWc6h9g2XBSrf3p6wTEARhZ+QQKvLeY07KpzJ6w8f6lBiojcnpTqsRHOCFYjM3hsqmIcPrOPrWJG9HVk+Iwpi8XvITtuLUboKztot115CmSyat1HmJShgzmCM16W8ms7cG2qmkyUewxh0iu4jmLkVGIZRNbKRBNRJJ6t4thF0u9tNzs7PSW3ewjtppRRuqZcZvhzI5zNuOhYY9TNB+8kfrSQvUbtOGGKhjUS4T/yu4Q25oANsjTenQpEm3xxW63HIIGAGXTfzLV8pssaZtvsER37qZLacVY4XMY9NU6BiX4kyi9lb4foYJyMoqUGAOAqJJMheU+1qa4i4G0cspzzMopgCCaopzcG33eXVO5ncXVfmTc5Il8j9ZNhWVckzbFLPJRicrOrQIXW+rK9ZCfyog9jmRPupWXpgD88R4HxZWdObSN7tOTukcuKUtKhBVe8SXlE3501lMJSnwRxOt/djiUKRkQS4zbUsQGNfWVlpP0vwmTXO7jWPOKslOFVhBMVbTxr+HkzZAMGrN8a9gfTclBchNzHyJk9mSNSMcKfc6lzFKuIO2HA/IGUem2LgvlMgEmsjs7xRuFYRImlnhiajZ97P6g65n2pfsC3nE4nkP33Y6h1OlHqCtpSTKZJvlvVeNmeSvZAX811MeILunyjFcVSWDMpEaV4fIHc8bHeCYPKozUVneeYu75B5CgGZMhoqSBRUlHgSgvwFkqiEPAEWEWbSeFYFDrli/kNuOMmjKzHNZxOMXROmDQQ6Gzrcca3/JnMYiX7rEAuOZUYIrf6nX72ZovqWKsz0aI/WG8qb2nMfqfSkE2wwoG8MbxK8tvyPIZGWfySabN57zYGoyaJL5bjWuTGSFWEprgtIZyL8OIde6BTnSQONNBiwsd+S+0jvtV4CMwjNde0sBnDGzEViaqNOf1lU+5UQMKX9St6mG+foFfg3KBODN4PInw5UVuhT69ZIxXLvOazZ0ElpXIBKbgatw5IvvArplD4MJbNXi+XfMH76nQZ+1/MOy/oXtMhV5b33k0qqqB+Fn8pB0qwOVXLbC029FPP0vtW19/PKfNmlCjnnhedl4369u1U73gU="
}
}
},
"sha2Issue": "03e339927c047b00d57cea117b39b163c8bc14dfd852a1acfa6adcef50bf08d0e2"
}

The value of the "Underlying Physics" key is now encrypted and no longer appears in plain text as in the asset issuance definition. The encryption public key is given (and it is the user's responsibility to take care of the encryption private key so that the value can later be decrypted).

 

 

 

 

Multisig Addresses

In this section we describe the colored coins API support for multisignature Bitcoin Addresses.

The colored coins protocol lends full support for issuing an sending assets to and from multisignature addresses. Multisignature addresses (or multisig for short) are most preferably created by the client. However, the colored coins API also supports creating a multisig address for you.

We first describe how to generate a multisig address using nodejs. We proceed to issue assets on and transfer them to multisig addresses. Finally we discuss the API support for creating a multisig addresses.

 

 

 

 

Creating Multisig Address

In this section we describe how to create a multisignature addresses using nodejs

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it create_multisig_address.js.

// create_multisig_address.js
var bitcoin = require('bitcoinjs-lib');
var scripts = bitcoin.scripts;
var Address = bitcoin.Address;
var network = bitcoin.networks.testnet;

var raw_privKeys = [];
var pubKeys = [];
var redeemScript;

function newMultisigAddress(m,N){

for (var i = 1; i <= N; i++) {
pk = bitcoin.ECKey.makeRandom();
raw_privKeys.push(pk);
};

pubKeys = raw_privKeys.map(function (key) {
return key.pub.toHex()
});

var raw_pubkeys = pubKeys.map(bitcoin.ECPubKey.fromHex)
redeemScript = scripts.multisigOutput(m, raw_pubkeys)
var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash())
var multisigAddress = Address.fromOutputScript(scriptPubKey, network).toString()
return multisigAddress
}

This code creates an m out of N multisignature Address, by constructing the N underlying Public Keys.

In order to sign the transaction later we will need to know m out of the N private keys as well as the redeem script, so let's add some code to log this data:

// create_multisig_address.js
...........
console.log('redeemScript',redeemScript.toHex())
var wifs = raw_privKeys.map(function(key){return key.toWIF()})
console.log('privKeys (wif)',wifs)
console.log('pubKeys', pubKeys)

Let's take an example of 2 out of 3 multisig address

// create_multisig_address.js
...........
console.log('multisigAddress',newMultisigAddress(2,3))

Run that code by typing

> node create_multisig_address.js
----------------
multisigAddress 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9
redeemScript 522103ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2210352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946210330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b53ae
privKeys (wif) [ 'Kz6XuRHniKZfWxSLSC7YdN8AmB6oXaDSfHhxa6TPfwmcAC8URE7b',
'L3u4otRKpgBwC8JaJPGiWpYWaLQqQngVVhmG6bZXoEL6V85bywnd',
'KzaAL6uKn2zHUULhsESuDDNguS2TsjDZv3ebgiFbFzGSqg5oMZ89' ]
pubKeys [ '03ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2',
'0352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946',
'0330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b' ]
>

We see the multisig address (note it starts with 2 not with m or n like standard addresses), the redeem script and the set of 3 public and corresponding private keys.

 

 

 

 

Multisig Issuance Address

In this section we will issue an asset on the multisig address 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9 that was created in the previous section.

In fact, in terms of the colored coins protocol it makes no difference at all that the address is a multisig address, we basically use the exact same code that was used in the Getting Started|Issue a New Asset section.

first we must make sure to fund this address by sending some satoshis to it as we did in the Getting Started | Setup | Creating a Testnet Address section.

// issue_asset.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

address = '2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9';

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === String) {
body = JSON.parse(body);
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

var asset = {
"issueAddress": address,
"amount": 100,
"fee": 5000
};

postToApi('issue', asset, function(err, body){
if (err) {
consule.log('error: ', err);
}
});

Running this code

> node issue_asset.js
----------------------
Body: "{\"txHex\":\"0100000001719317d1d89cbbe89f0783053b5a6ba16ab584ac6f9a60ed25cafe94c6f9f3680000000000ffffffff020000000000000000096a074343010527b010ce8101000000000017a9141442dada3e43b037543440bad2ad9695a360a6558700000000\",\"assetId\":\"LGooTK9WEUNqSUu3i4CRJLxvejx9fbiZ2Gk1X\"}"

we get the hexadecimal representation of the unsigned transaction. As before, we need to sign and broadcast the transaction. However, and here is the first time that the fact that the issuance address is a multisig address comes into play, signing the multisig transaction requires a little more effort. We do that in the next section.

 

 

 

 

Signing a multisig transaction

In this section we sign the multisignature transaction that was generated in the previous section. We then proceed to broadcast the signed transaction to the Bitcoin testnet network.

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it sign_multisig.js.

This code uses the bitcoinjs-lib library as well as 2 of the three private keys and the redeem script that were created with the multisig address 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9 in the previous section.

// sign_multisig.js
var bitcoin = require('bitcoinjs-lib');

var privKeys = [ 'Kz6XuRHniKZfWxSLSC7YdN8AmB6oXaDSfHhxa6TPfwmcAC8URE7b','L3u4otRKpgBwC8JaJPGiWpYWaLQqQngVVhmG6bZXoEL6V85bywnd','KzaAL6uKn2zHUULhsESuDDNguS2TsjDZv3ebgiFbFzGSqg5oMZ89'];

var redeemScript = '522103ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2210352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946210330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b53ae';

var txHex = '0100000001719317d1d89cbbe89f0783053b5a6ba16ab584ac6f9a60ed25cafe94c6f9f3680000000000ffffffff020000000000000000096a074343010527b010ce8101000000000017a9141442dada3e43b037543440bad2ad9695a360a6558700000000'

var tx = bitcoin.Transaction.fromHex(txHex);

var txb = bitcoin.TransactionBuilder.fromTransaction(tx);

var rawSignedTransaction;

txb.tx.ins.forEach(function(input, i) {

var txid = bitcoin.bufferutils.reverse(input.hash).toString('hex');

txb.sign(i, bitcoin.ECKey.fromWIF(privKeys[0]), bitcoin.Script.fromHex(redeemScript));
txb.sign(i, bitcoin.ECKey.fromWIF(privKeys[1]), bitcoin.Script.fromHex(redeemScript));

try {
rawSignedTransaction = txb.build().toHex();

console.log('Successfully signed.');
} catch (e) {
if ('Transaction is missing signatures' === e.message) {
rawSignedTransaction = txb.buildIncomplete().toHex();
} else if ('Not enough signatures provided' === e.message) {
console.log('Not enough signatures provided');

rawSignedTransaction = txb.buildIncomplete().toHex();
} else {
console.log(e);
}
}
});

console.log(rawSignedTransaction);

Note how this code uses only the first and second private keys (we could have used any 2 out of the 3) and the redeem script.

Running that code we get the signed transaction which we can then broadcast.

Use our explorer to verify that resulting issuance transaction indeed issued 100 units of the new LGooTK9WEUNqSUu3i4CRJLxvejx9fbiZ2Gk1X asset on our funded multisig address 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9.

In exactly the same way, one can use multisig addresses in transferring assets. The only difference from previous code is the way the transaction is signed

 

 

 

 

Auto-generated Multisig

The colored coins API can create a multisig address for you from a list of public keys.

While this option may be easier to use, openly sending the list of public keys exposes some security and privacy concerns (at least in principle).

In this case we use the pubKeys,m API endpoints instead of the address endpoint.
The pubkeys endpoint expects an array of N public keys and the integer m<=N is the minimal number of private keys needed to sign the transaction and release the funds in the associated multisignature address.

Here is an example of transferring the asset LGGxrFrEE2MhYoDrqzuXXg136rRY6mHScFshW from the funded issuance address (you should use the funded address created during setup) to the multisig address 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9 that was created in a previous section, by providing the three underlying public keys:

[ '03ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2',
'0352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946',
'0330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b' ]

create a new file in the same directory where you installed BitcoinJS and Node js

save the following into that file, say we call it transfer_to_pubkeys_multisig.js.

As before, we require the dependencies and use the postToApi function and our funded address (you should use the funded address created during setup.)

// transfer_pubkeys_multisig.js
var bitcoin = require('bitcoinjs-lib');
var request = require('request');

var issuance_address = 'n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR';

var pubKeys = [ '03ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2',
'0352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946',
'0330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b' ]

var m =2;
var send_asset = {
from: [issuance_address],
to: [{
pubKeys:pubKeys,
m:2,
amount: 10,
assetId: 'LGGxrFrEE2MhYoDrqzuXXg136rRY6mHScFshW'
}],
fee: 5000
};
function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) {
return callback(error);
}
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};
postToApi('sendasset', send_asset, function(err, body){
if (err) {
console.log('error: ', err);
}
});

Note how the code is essentially the same as in the Getting Started | Transfer an Asset section, except for replacing the address: to_address key-value pair with pubKeys,m.

Now run the code to get:

> node  metadata_encryption.js
----------------------
sendasset: {"from":"n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR","to":[{"pubKeys":["03ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2","0352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946","0330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b"],"m":2,"amount":10,"assetId":"LGGxrFrEE2MhYoDrqzuXXg136rRY6mHScFshW"}],"fee":5000}
Status: 200
Body: {"txHex":"0100000001818ee20e62fb52f1bac564f85f468a94538bfe62aea3e7f1bea71e39540bc3cf0200000000ffffffff03580200000000000017a9141442dada3e43b037543440bad2ad9695a360a655870000000000000000086a0643430115000a4c6d0100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000","multisigOutputs":[{"index":0,"reedemScript":"522103ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2210352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946210330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b53ae","address":"2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9"}]}

Note how the JSON reply

{
"txHex": "0100000001818ee20e62fb52f1bac564f85f468a94538bfe62aea3e7f1bea71e39540bc3cf0200000000ffffffff03580200000000000017a9141442dada3e43b037543440bad2ad9695a360a655870000000000000000086a0643430115000a4c6d0100000000001976a914ea55c2430dca31e56ef5ae55c2863dae65df908688ac00000000",
"multisigOutputs": [
{
"index": 0,
"reedemScript": "522103ae3cf1075ab2c7544d973903c089295ab195af63a8f3c168c9b8901b457d9ce2210352f75a371a1331fa51a20b5e6e1e4ab8f86a1f65dd36fe44a9f7ce5d2a706946210330959f464f88f7294cc412a81f72f3cb817a2738a16e187d99b8e78c4ccf9e3b53ae",
"address": "2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9"
}
]
}

contains on top of the usual txHex key (which we will soon sign), also a multisigOutputs key whose value give the correct redeem script and the multisig address 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9 that were obtained directly in the Create a Multisig Address section.

We can proceed to sign and broadcast the transaction as usual (since we are sending from our standard issuance transaction this is the usual sign procedure, not the multisignature one). Use our explorer to verify that resulting transfer transaction sent 10 units of the LGGxrFrEE2MhYoDrqzuXXg136rRY6mHScFshW asset from our funded address n2t19a46cBs2DdHs2sqfRwPGhoQjvqmefR to the multisig address 2Mu6MbHBxSpD2CLDpQRATKf3X8L4TSapmV9.

 

 

 

 

Use Cases

Issue & Send Assets

Presented here is a detailed use-case example, using the ColoredCoins API with NodeJS, for the following use-case:

1. Create a new address.
2. Fund the address.
3. Issue 100 asset units on this address.
4. Sign the issue transaction.
5. Broadcast the signed issue transaction on the blockchain.
6. View the issue assets on the blockchain.
7. Create 3 more addresses (‘address1’, ‘address2’, and ‘address3’).
8. Send 10 asset units to ‘address1’, 20 asset units to ‘address2’, and 30 asset units to ‘address3’.
9. Sign the send transaction.
10. Broadcast the signed send transaction on the blockchain.
11. View the issue & send transaction on the blockchain, and see the asset holders info.

Ready... lets start.

1. Create a new address:


var bitcoin = require('bitcoinjs-lib');

//var key = bitcoin.ECPair.makeRandom({network: bitcoin.networks.testnet});
var key = bitcoin.ECKey.makeRandom();
var wif = key.toWIF();
var address = key.getAddress().toString();
console.log('new TESTNET address: ['+address+']');
console.log('key: ['+wif+']');

Output:


new TESTNET address: [mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE]
key: [L32oQn3Df8nvCoZiNnfTeRhC8rVjtC1tAMeDk9ovxBPWardxT5tT]

2. Fund the address:

I use this link to fund it: http://faucet.haskoin.com/

Lets see the funding.


http://testnet.api.coloredcoins.org/v3/addressinfo/mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE

Output:


{
address: 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE',
utxos:
[{
_id: '55a37dcf7c3454d08f27c563',
txid: 'a8af84f20d12658267e954275e809be2158f0bfcd6980a16b1b114e8bc102481',
index: 1,
value: 100000,
blockheight: -1,
used: false,
assets: [ ],
scriptPubKey:
{
asm: 'OP_DUP OP_HASH160 4d5bd54809f846dc6b1a14cbdd0ac87a3c66f766 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE']
}
}]
}

We can see that the address is funded by 100,000 satoshi.

3. Issue 100 asset units on this address:


var bitcoin = require('bitcoinjs-lib');
var request = require('request');

var key1 = 'L32oQn3Df8nvCoZiNnfTeRhC8rVjtC1tAMeDk9ovxBPWardxT5tT';
var address1 = 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE';

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) return callback(error);
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

var asset = {
'issueAddress': address1,
'amount': 100,
'divisibility': 0,
'fee': 5000,
'reissueable': false,
'transfer': [{
'address': address1,
'amount': 100
}],
'metadata': {
'assetId': '1',
'assetName': 'Mission Impossible 15',
'issuer': 'Fox Theater',
'description': 'Movie ticket to see the New Tom Cruise flick',
'userData': {
'meta' : [
{key: 'Item ID', value: 1, type: 'Number'},
{key: 'Item Name', value: 'Item Name', type: 'String'},
{key: 'Company', value: 'My Company', type: 'String'},
{key: 'Address', value: 'San Francisco, CA', type: 'String'}
]
}
}
};

postToApi('issue', asset, function(err, body){
if (err) console.log('error: ',err);
});

Output:


txHex:'0100000001812410bce814b1b1160a98d6fc0b8f15e29b805e2754e9678265120df284afa80100000000ffffffff04bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210314a96cd6f5a20826070173fe5b7e9797f21fc8ca4a55bcb2d2bde99f55dd352352ae58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000206a1e43430102fa9213bc243af03857d0f9165e971153586d3915201201201210087e0100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000'

assetId:'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3'

Please note, that I defined the ‘transfer’ property in the asset object where I put my own address.
If I don't do that, the coloring scheme will use all my remainder BTC balance as my ‘color’ asset holder. And so, I shall not be left with any BTC for more issuing or send transactions.
When I do define the 'transfer' property, it only uses the minimum carrier amount (~550 Satoshi, at our example - 600 Satoshi).

4. Sign the issue transaction:


var bitcoin = require('bitcoinjs-lib');

function signTx (unsignedTx, privateKey) {
var tx = bitcoin.Transaction.fromHex(unsignedTx)
var insLength = tx.ins.length
for (var i = 0; i < insLength; i++) {
tx.sign(i, privateKey)
}
return tx.toHex()
}

var unsignedTx = '0100000001812410bce814b1b1160a98d6fc0b8f15e29b805e2754e9678265120df284afa80100000000ffffffff04bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210314a96cd6f5a20826070173fe5b7e9797f21fc8ca4a55bcb2d2bde99f55dd352352ae58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000206a1e43430102fa9213bc243af03857d0f9165e971153586d3915201201201210087e0100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000';

var assetId = 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3'

//var key = bitcoin.ECPair.fromWIF('L32oQn3Df8nvCoZiNnfTeRhC8rVjtC1tAMeDk9ovxBPWardxT5tT', bitcoin.networks.testnet);
var key = bitcoin.ECKey.fromWIF('L32oQn3Df8nvCoZiNnfTeRhC8rVjtC1tAMeDk9ovxBPWardxT5tT');

var sign = signTx(unsignedTx,key);

console.log('Signed: '+sign);

Output:


Signed:0100000001812410bce814b1b1160a98d6fc0b8f15e29b805e2754e9678265120df284afa8010000006b4830450221008db3c46c58ed2c975a8a1a3b3949ade4b751bdfdbef1c6497cc48c3fccaa4492022064f91071c68942d9a5f5e0f4da326442675abfa498a733e55b3f250f96a28f26012102ed5fb18801b833c649342f5499a98db4c8b14ac2b76a1895161c86767403305cffffffff04bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210314a96cd6f5a20826070173fe5b7e9797f21fc8ca4a55bcb2d2bde99f55dd352352ae58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000206a1e43430102fa9213bc243af03857d0f9165e971153586d3915201201201210087e0100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000

5. Broadcast the signed issue transaction on the blockchain:


var bitcoin = require('bitcoinjs-lib');
var request = require('request');

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) return callback(error);
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

var signedTxHex = '0100000001812410bce814b1b1160a98d6fc0b8f15e29b805e2754e9678265120df284afa8010000006b4830450221008db3c46c58ed2c975a8a1a3b3949ade4b751bdfdbef1c6497cc48c3fccaa4492022064f91071c68942d9a5f5e0f4da326442675abfa498a733e55b3f250f96a28f26012102ed5fb18801b833c649342f5499a98db4c8b14ac2b76a1895161c86767403305cffffffff04bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff210314a96cd6f5a20826070173fe5b7e9797f21fc8ca4a55bcb2d2bde99f55dd352352ae58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000206a1e43430102fa9213bc243af03857d0f9165e971153586d3915201201201210087e0100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000';

var data_params = {
'txHex': signedTxHex
};

console.log('signed: '+signedTxHex)

postToApi('broadcast',data_params,function(err, body){
if (err) console.log('error: '+err);
});

Output:


txid:'a5ca30de4f0df2578fe544ba5b58ff90a6a40eb73378c18a4265000e087d8ca9'

6. View the issue assets on the blockchain:

Now, lets look at the address again.


http://testnet.api.coloredcoins.org/v2/addressinfo/mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE

Output:


{
address: 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE',
utxos:
[{
_id: '55a37ff57c3454d08f27cb96',
txid: 'a5ca30de4f0df2578fe544ba5b58ff90a6a40eb73378c18a4265000e087d8ca9',
index: 1,
value: 600,
blockheight: -1,
used: false,
assets:
[{
assetId: 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3',
amount: 100,
issueTxid: 'a5ca30de4f0df2578fe544ba5b58ff90a6a40eb73378c18a4265000e087d8ca9',
divisibility: 0,
lockStatus: null
}],
scriptPubKey:
{
asm: 'OP_DUP OP_HASH160 4d5bd54809f846dc6b1a14cbdd0ac87a3c66f766 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses:
['mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE']
}
},{
_id: '55a37ff57c3454d08f27cb9b',
txid: 'a5ca30de4f0df2578fe544ba5b58ff90a6a40eb73378c18a4265000e087d8ca9',
index: 3,
value: 97799.99999999999,
blockheight: -1,
used: false,
assets: [ ],
scriptPubKey:
{
asm: 'OP_DUP OP_HASH160 4d5bd54809f846dc6b1a14cbdd0ac87a3c66f766 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE']
}
}]
}

We can now see the new 100 assets at my address with a new utxo.

Let's also look at the asset holders:


http://testnet.api.coloredcoins.org/v3/stakeholders/LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3

Output:


{
assetId: 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3',
holders:
[{
address: 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE',
amount: 100
}],
divisibility: 0,
lockStatus: null,
someUtxo: 'a5ca30de4f0df2578fe544ba5b58ff90a6a40eb73378c18a4265000e087d8ca9:1'
}

We can see there is only one holder of those assets, and its our address.

7. Create 3 new addresses:

I'll create 3 new addresses by running this code:


var bitcoin = require('bitcoinjs-lib');

//var key1 = bitcoin.ECPair.makeRandom({network: bitcoin.networks.testnet});
var key1 = bitcoin.ECKey.makeRandom();
var wif1 = key1.toWIF();
var address1 = key1.getAddress().toString();
var key2 = bitcoin.ECKey.makeRandom();
var wif2 = key2.toWIF();
var address2 = key2.getAddress().toString();
var key3 = bitcoin.ECKey.makeRandom();
var wif3 = key3.toWIF();
var address3 = key3.getAddress().toString();

console.log('address1: '+address1);
console.log('address2: '+address2);
console.log('address3: '+address3);

Output:


address1: mua5wmuk75GZ9xdcR4E5T3Uoo47YqdEQYD
address2: mkcohk5KGnoMtz6sFMStjeLLJtM7GG6ce6
address3: n2L6R1JTNArkVgqBTDGCCthbuFX84iM2uf

8. Send 10 asset units to ‘address1’, 20 asset units to ‘address2’, and 30 asset units to ‘address3’:


var bitcoin = require('bitcoinjs-lib');
var request = require('request');

var address = 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE';
var assetId = 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3';
var address1 = 'mua5wmuk75GZ9xdcR4E5T3Uoo47YqdEQYD';
var address2 = 'mkcohk5KGnoMtz6sFMStjeLLJtM7GG6ce6';
var address3 = 'n2L6R1JTNArkVgqBTDGCCthbuFX84iM2uf';

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) return callback(error);
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

var asset = {
'fee': 5000,
'from': [address],
'to': [{
'address': address1,
'amount': 10,
'assetId': assetId
},{
'address': address2,
'amount': 20,
'assetId': assetId
},{
'address': address3,
'amount': 30,
'assetId': assetId
},{
'address': address,
'amount': 40,
'assetId': assetId
}],
'metadata': {
'assetId': '1',
'assetName': 'Mission Impossible 16',
'issuer': 'Fox Theater',
'description': 'Movie ticket to see the New Tom Cruise flick again',
'userData': {
'meta' : [
{key: 'Item ID', value: 2, type: 'Number'},
{key: 'Item Name', value: 'Item Name', type: 'String'},
{key: 'Company', value: 'My Company', type: 'String'},
{key: 'Address', value: 'San Francisco, CA', type: 'String'}
]
}
}
};

postToApi('sendasset', asset, function(err, body){
if (err) console.log('error: '+err);
});

Output:


txHex:'0100000002a98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa50100000000ffffffffa98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa50300000000ffffffff07bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2103c355e4a5b8863548df76d32c1b5327f3a3f8115b6189937632a850d3547a972252ae58020000000000001976a9149a296522ab7e14eb01539b943257baf0fee5f21188ac58020000000000001976a91437f3f3ebb951fa038a1e2af57573a0adb5feb43588ac58020000000000001976a914e44cd81050675d738c7136fad8f6eb47619ab4ec88ac58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000236a21434301114b737ffaab448cd4ba07246a88c9eb287124ecfd010a0214031e0420415c700100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000'

Please note, that similar to the issue transaction, I send the remainder assets to my own address.
If I don't do that, the coloring scheme will use all my remainder BTC balance as my ‘color’ asset holder. And so, I shall not be left with any BTC for more issuing or send transactions.

If I don’t remember how much assets I’ve left with, I can use the ‘addressinfo’ or the ‘stakeholders’ API methods.


http://testnet.api.coloredcoins.org/v3/addressinfo/mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE

http://testnet.api.coloredcoins.org/v3/stakeholders/LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3

9. Sign the send transaction:


var bitcoin = require('bitcoinjs-lib');

function signTx (unsignedTx, privateKey) {
var tx = bitcoin.Transaction.fromHex(unsignedTx)
var insLength = tx.ins.length
for (var i = 0; i < insLength; i++) {
tx.sign(i, privateKey)
}
return tx.toHex()
}

var unsignedTx = '0100000002a98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa50100000000ffffffffa98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa50300000000ffffffff07bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2103c355e4a5b8863548df76d32c1b5327f3a3f8115b6189937632a850d3547a972252ae58020000000000001976a9149a296522ab7e14eb01539b943257baf0fee5f21188ac58020000000000001976a91437f3f3ebb951fa038a1e2af57573a0adb5feb43588ac58020000000000001976a914e44cd81050675d738c7136fad8f6eb47619ab4ec88ac58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000236a21434301114b737ffaab448cd4ba07246a88c9eb287124ecfd010a0214031e0420415c700100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000';

var assetId = 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3'

var key = bitcoin.ECPair.fromWIF('L32oQn3Df8nvCoZiNnfTeRhC8rVjtC1tAMeDk9ovxBPWardxT5tT', bitcoin.networks.testnet);

var sign = signTx(unsignedTx,key);

console.log('Signed: '+sign);

Output:


Signed: '0100000002a98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa5010000006b483045022100fa98b9253fdde6b3050d7e4ca4123ac85eebfc17117119e2acfcf724800361f502207df568df98a519f724f02e5101389f22ae712601c74686d4853a9cdc8b94fc96012102ed5fb18801b833c649342f5499a98db4c8b14ac2b76a1895161c86767403305cffffffffa98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa5030000006b483045022100df4edf510e80b2b0fe1c51eb2e0fd33f4e20a9b70d914c26eec3f5e586d4c4e10220522e47d8cec8f769f3625a27ee59b956a881acac8009d1a9623a951f2fd15ccb012102ed5fb18801b833c649342f5499a98db4c8b14ac2b76a1895161c86767403305cffffffff07bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2103c355e4a5b8863548df76d32c1b5327f3a3f8115b6189937632a850d3547a972252ae58020000000000001976a9149a296522ab7e14eb01539b943257baf0fee5f21188ac58020000000000001976a91437f3f3ebb951fa038a1e2af57573a0adb5feb43588ac58020000000000001976a914e44cd81050675d738c7136fad8f6eb47619ab4ec88ac58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000236a21434301114b737ffaab448cd4ba07246a88c9eb287124ecfd010a0214031e0420415c700100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000'

10. Broadcast the signed send transaction on the blockchain:


var bitcoin = require('bitcoinjs-lib');
var request = require('request');

function postToApi(api_endpoint, json_data, callback) {
console.log(api_endpoint+': ', JSON.stringify(json_data));
request.post({
url: 'http://testnet.api.coloredcoins.org:80/v3/'+api_endpoint,
headers: {'Content-Type': 'application/json'},
form: json_data
},
function (error, response, body) {
if (error) return callback(error);
if (typeof body === 'string') {
body = JSON.parse(body)
}
console.log('Status: ', response.statusCode);
console.log('Body: ', JSON.stringify(body));
return callback(null, body);
});
};

var signedTxHex = '0100000002a98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa5010000006b483045022100fa98b9253fdde6b3050d7e4ca4123ac85eebfc17117119e2acfcf724800361f502207df568df98a519f724f02e5101389f22ae712601c74686d4853a9cdc8b94fc96012102ed5fb18801b833c649342f5499a98db4c8b14ac2b76a1895161c86767403305cffffffffa98c7d080e0065428ac17833b70ea4a690ff585bba44e58f57f20d4fde30caa5030000006b483045022100df4edf510e80b2b0fe1c51eb2e0fd33f4e20a9b70d914c26eec3f5e586d4c4e10220522e47d8cec8f769f3625a27ee59b956a881acac8009d1a9623a951f2fd15ccb012102ed5fb18801b833c649342f5499a98db4c8b14ac2b76a1895161c86767403305cffffffff07bc0200000000000047512103ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2103c355e4a5b8863548df76d32c1b5327f3a3f8115b6189937632a850d3547a972252ae58020000000000001976a9149a296522ab7e14eb01539b943257baf0fee5f21188ac58020000000000001976a91437f3f3ebb951fa038a1e2af57573a0adb5feb43588ac58020000000000001976a914e44cd81050675d738c7136fad8f6eb47619ab4ec88ac58020000000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac0000000000000000236a21434301114b737ffaab448cd4ba07246a88c9eb287124ecfd010a0214031e0420415c700100000000001976a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac00000000';

var data_params = {
'txHex': signedTxHex
};

console.log('signed: '+signedTxHex)

postToApi('broadcast',data_params,function(err, body){
if (err) console.log('error: '+err);
});

Output:


txid:'af1412a0c1bcfdf4ead805d705e0efbd1677066f3688a7c27d7130ff7fe76b0b'

11. View the issue & send transaction on the blockchain, and see the asset holders info:

Lets see our address again:


http://testnet.api.coloredcoins.org/v3/addressinfo/mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE

Output:


{
address: 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE',
utxos:
[{
_id: '55a383787c3454d08f27d86d',
txid: 'af1412a0c1bcfdf4ead805d705e0efbd1677066f3688a7c27d7130ff7fe76b0b',
index: 4,
value: 600,
blockheight: 502141,
used: false,
assets:
[{
assetId: 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3',
amount: 40,
issueTxid: 'a5ca30de4f0df2578fe544ba5b58ff90a6a40eb73378c18a4265000e087d8ca9',
divisibility: 0,
lockStatus: null
}],
scriptPubKey:
{
asm: 'OP_DUP OP_HASH160 4d5bd54809f846dc6b1a14cbdd0ac87a3c66f766 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE']
}
},{
_id: '55a383787c3454d08f27d870',
txid: 'af1412a0c1bcfdf4ead805d705e0efbd1677066f3688a7c27d7130ff7fe76b0b',
index: 6,
value: 94300,
blockheight: 502141,
used: false,
assets: [ ],
scriptPubKey:
{
asm: 'OP_DUP OP_HASH160 4d5bd54809f846dc6b1a14cbdd0ac87a3c66f766 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9144d5bd54809f846dc6b1a14cbdd0ac87a3c66f76688ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE']
}
}]
}

We can see we’re left with only 40 assets.

To see all our assets, lets look at the asset holders info:


http://testnet.api.coloredcoins.org/v3/stakeholders/LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3

Output:


{
assetId: 'LdGJDiQTo6k7oDT76syyiRDNJCHcZtt2K3',
holders:
[{
address: 'mua5wmuk75GZ9xdcR4E5T3Uoo47YqdEQYD',
amount: 10
},{
address: 'mkcohk5KGnoMtz6sFMStjeLLJtM7GG6ce6',
amount: 20
},{
address: 'n2L6R1JTNArkVgqBTDGCCthbuFX84iM2uf',
amount: 30
},{
address: 'mnZzLW7SFi1zfGD8RBRZkyV8EQps1rmBLE',
amount: 40
}],
divisibility: 0,
lockStatus: null,
someUtxo: 'af1412a0c1bcfdf4ead805d705e0efbd1677066f3688a7c27d7130ff7fe76b0b:1'
}

Finally, we can see all the 100 assets divided between the 4 address.