The BLOCKv Platform gives vAtom publishers and developers the ability to choose whether or not to leverage a blockchain for each of the vAtom templates they create. Publishers may wish to keep certain vAtoms off-chain for vAtoms which have no inherent value or scarcity and are not intended for use outside of the BLOCKv platform. For vAtoms that are intended to be used on-chain (and therefore viewable in external wallets), publishers may choose to enable this functionality in their templates.
The platform also allows publishers to choose which blockchain network to leverage (Bitcoin, Ethereum, EOS, etc.), giving them the freedom to choose a network that best meets their requirements, including performance targets and processing costs.
In this tutorial, we are going to learn how to create a template, template variation, and emit an off-chain vAtom that can be held in your wallet or transferred to another user.
To execute this tutorial you need an App-Id
, pub_fqdn
, and a valid BLOCKv user with publisher rights. To request these credentials click here.
Steps 2 & 3 in this tutorial are completely optional. You can safely skip them.
API Reference: Login API
{
"token" : "john.doe@example.com",
"token_type" : "email",
"auth_data" : {
"password": "secret"
}
}
From the response payload, save access_token
& pub_fqdn
for future use. access_token
value is sent as an Authorization
HTTP Header. See Getting Started for details.
{
"status": "success",
"error": 0,
"message": "",
"payload": {
"user": {
"id": "6e0...",
"meta": {
"when_created": "2018-02-26T20:47:31Z",
"when_modified": "2018-03-12T17:43:49Z",
"data_type": "blockv::user"
},
"properties": {
"first_name": "John",
"last_name": "Doe",
"name_public": false,
"password": "",
"avatar_uri": "http://example.com/mypic.jpg",
"avatar_public": false,
"birthday": "1970-01-01",
"guest_id": "",
"nonpush_notification": false,
"language": "en"
},
"system_properties": {
"pub_fqdn": "blockv.demo",
"is_admin": true,
"activated": false,
"last_login": "2018-03-12T17:43:49Z",
"is_merchant": false
}
},
"asset_provider": [
{
"name": "blockv",
"type": "Cloudfront",
"uri": "https://cdndev.blockv.net",
"descriptor": {
"Policy": "eyJTdGF0ZW...",
"Signature": "gSMwEjr7...",
"Key-Pair-Id": "A..."
}
}
],
"access_token": {
"token": "ey...",
"token_type": "Bearer",
"expires_in": 5000
},
"refresh_token": {
"token": "ey...",
"token_type": "Bearer",
"expires_in": 43200
}
}
}
There are several types of public Templates. Most commonly used templates are:
vatomic.templates::3D-Object::v1
)vatomic.templates::Image::v1
)vatomic.templates::MediaPlayer::v1
)vatomic.templates::WebView::v1
)Each of these templates (and many others) are already supported by the SDK for iOS and Android. Nothing is stopping you from creating your own Templates that are unique to your own custom viewer, but using an existing viewer will help you get started.
API Reference: Get Templates API
GET /v1/templates?public=true&page[size]=50&fields=name
Let's see what an existing (image) template looks like. Retrieve an pre-existing Image Template (vatomic.templates::Image::v1
).
API Reference: Get Template API
GET /v1/templates/vatomic.templates::Image::v1
{
"payload": {
"name": "vatomic.templates::Image::v1",
"public": true,
"meta": {
"created_by": "557d18d9-259a-4c44-a088-a9601a05ab22",
"when_created": "2017-03-29T20:41:52Z",
"modified_by": "557d18d9-259a-4c44-a088-a9601a05ab22",
"when_modified": "2017-03-29T20:41:52Z",
"data_type": "vAtom::vAtomTemplateType"
},
"properties": {
"publisher_fqdn": "vatomic",
"template": {
"vAtom::vAtomType": {
"parent_id": ".",
"publisher_fqdn": "vatomic",
"root_type": "vAtom::vAtomType",
"owner": "557d18d9-259a-4c44-a088-a9601a05ab22",
"author": "557d18d9-259a-4c44-a088-a9601a05ab22",
"template": "vatomic.templates::Image::v1",
"template_variation": "",
"notify_msg": "",
"title": "Image",
"description": "This is a Template for a single image vAtom",
"disabled": false,
"category": "",
"tags": [],
"transferable": true,
"acquirable": false,
"tradeable": false,
"transferred_by": "",
"cloned_from": "",
"cloning_score": 0,
"in_contract": false,
"redeemable": false,
"in_contract_with": "",
"commerce": {
"pricing": {
"pricingType": "",
"value": {
"currency": "",
"price": "",
"valid_from": "",
"valid_through": "",
"vat_included": false
}
}
},
"states": [
{
"name": "Activated",
"value": {
"type": "boolean",
"value": "true"
},
"on_state_change": {
"reactor": ""
}
}
],
"resources": [
{
"name": "ActivatedImage",
"resourceType": "ResourceType::Image::PNG",
"value": {
"resourceValueType": "ResourceValueType::URI",
"value": "https://cdn.blockv.io/vatomic.templates/vatomic.templates::Image::v1/image_icon.png"
}
}
],
"visibility": {
"type": "owner",
"value": "*"
},
"num_direct_clones": 0,
"geo_pos": {
"$reql_type$": "GEOMETRY",
"coordinates": [
0,
0
],
"type": "Point"
},
"dropped": false
},
"private": {},
"in_sync": false,
"is_syncing": false,
"version": ""
},
"cloneable": false,
"unpublished": false
}
}
}
We are going to create a new Template just like the one above in our publisher workspace.
Read Template basics for more information on Templates and Template Variation.
Attribute template
is a unique id used to reference the template. It always starts with your pub_fqdn
, in this example it is blockv.demo
.
The ActivatedImage
and CardImage
resources specified below are used by the viewer to display the vAtom. The ActivatedImage
is shown in the inventory view and the CardImage
is displayed when the vAtom is "opened". As an example, imagine that the CardImage
is a Rare Pepe image. The viewer has essentially become a Rare Pepe wallet!
API Reference: Create Template API
{
"template": "blockv.demo::demo::Image::v1",
"public": false,
"cloneable": false,
"unpublished": true,
"vatom": {
"vAtom::vAtomType": {
"root_type": "vAtom::vAtomType",
"description": "This is a demo Image Template",
"title": "Image",
"redeemable": true,
"states": [
{
"name": "Activated",
"value": {
"type": "boolean",
"value": "true"
}
}
],
"resources": [
{
"name": "ActivatedImage",
"resourceType": "ResourceType::Image::PNG",
"value": {
"resourceValueType": "ResourceValueType::URI",
"value": ""
}
},
{
"name": "CardImage",
"resourceType": "ResourceType::Image::JPEG",
"value": {
"resourceValueType": "ResourceValueType::URI",
"value": ""
}
}
]
},
"private": {}
}
}
{
"payload": {
"success_message": "created Template successfully"
}
}
At this point, you can run the Get Template
call with this newly registered name
to verify.
GET /v1/templates/blockv.demo::demo::Image::v1
You should see something like -
{
"payload": {
"name": "blockv.demo::demo::Image::v1",
"public": false,
"meta": {
"created_by": "6e0ed730-26ff-45c0-b558-0df979d754ca",
"when_created": "2018-03-12T19:10:24Z",
"modified_by": "6e0ed730-26ff-45c0-b558-0df979d754ca",
"when_modified": "2018-03-12T19:10:24Z",
"data_type": "vAtom::vAtomTemplateType",
"in_sync": false,
"when_synced": "",
"is_syncing": false
},
"properties": {
"publisher_fqdn": "blockv",
"template": {
"vAtom::vAtomType": {
"parent_id": ".",
"publisher_fqdn": "blockv",
"root_type": "vAtom::vAtomType",
"owner": "6e0ed730-26ff-45c0-b558-0df979d754ca",
"author": "6e0ed730-26ff-45c0-b558-0df979d754ca",
"template": "blockv.demo::demo::Image::v1",
"template_variation": "",
"notify_msg": "",
"title": "Image",
"description": "This is a demo Image Template",
"disabled": false,
"category": "",
"tags": [],
"transferable": true,
"acquirable": false,
"tradeable": false,
"transferred_by": "",
"cloned_from": "",
"cloning_score": 0,
"in_contract": false,
"redeemable": true,
"in_contract_with": "",
"commerce": {
"pricing": {
"pricingType": "",
"value": {
"currency": "",
"price": "",
"valid_from": "",
"valid_through": "",
"vat_included": false
}
}
},
"states": [
{
"name": "Activated",
"value": {
"type": "boolean",
"value": "true"
},
"on_state_change": {
"reactor": ""
}
}
],
"resources": [
{
"name": "ActivatedImage",
"resourceType": "ResourceType::Image::PNG",
"value": {
"resourceValueType": "",
"value": ""
}
},
{
"name": "CardImage",
"resourceType": "ResourceType::Image::JPEG",
"value": {
"resourceValueType": "",
"value": ""
}
}
],
"visibility": {
"type": "owner",
"value": "*"
},
"num_direct_clones": 0,
"geo_pos": {
"$reql_type$": "GEOMETRY",
"coordinates": [
0,
0
],
"type": "Point"
},
"dropped": false
},
"private": {},
"in_sync": false,
"is_syncing": false,
"version": ""
},
"cloneable": false,
"unpublished": true
}
}
}
This step informs the viewer (iOS only - set under constraints
) to use a native image renderer to display the ActivatedImage
.
Read Face basics for more information on Resource Types and View Modes.
API Reference: Register Face API
{
"template": "blockv.demo::demo::Image::v1",
"display_url": "native://image",
"package_url": "native://image",
"constraints": {
"bluetooth_le": false,
"contact_list": false,
"gps": false,
"view_mode": "icon",
"platform": "generic",
"three_d": false,
"quality": "high"
},
"resources": [
"ActivatedImage"
]
}
{
"template": "blockv.demo::demo::Image::v1",
"display_url": "native://image",
"package_url": "native://image",
"constraints": {
"bluetooth_le": false,
"contact_list": false,
"gps": false,
"view_mode": "card",
"platform": "generic",
"three_d": false,
"quality": "high"
},
"resources": [
"CardImage"
]
}
{
"payload": {
"successMessage": "Registered face for template=blockv.demo::demo::Image::v1, id=7d2aaa60-a686-4013-b269-989376d5fee1"
}
}
The platform provides many action options to the developers. For this guide, we will register transfer
action with the template. This action allows vAtom to be transferred to another user.
name
of the action SHOULD always start with the template name
. In this case, it is blockv.demo::demo::Image::v1
.
API Reference: Register Action API
{
"name": "blockv.demo::demo::Image::v1::Action::Transfer",
"reactor": "blockv://v1/Transfer",
"wait": true,
"rollback": false,
"abort_on_pre_error": true,
"abort_on_post_error": false,
"abort_on_main_error": true,
"timeout": 10000,
"guest_user": true,
"config": {
"auto_create_landing_page": "http://viewer.blockv.io/#",
"auto_create_mode": "claim",
"auto_create_non_existing_recipient": true
},
"action_notification": {
"on": true,
"msg": "You received a vAtom",
"custom": {}
}
}
{
"payload": {
"successMessage": "Registered action: name=blockv.demo::demo::Image::v1::Action::Transfer"
}
}
In this step, we will further refine the template by setting values for resources
, for example ActivatedImage
and CardImage
.
API Reference: Create Variation API
{
"template": "blockv.demo::demo::Image::v1",
"template_variation": "blockv.demo::demo::Image::v1::tutorial::v1",
"public": false,
"cloneable": false,
"max_clones_per_instance": 0,
"max_clones_total": 0,
"max_num": 100,
"per_clone_gain": 1,
"auto_create_on_acquire": false,
"unpublished": true,
"vatom": {
"vAtom::vAtomType": {
"transferable": true,
"acquirable": false,
"redeemable": false,
"title": "Tutorial",
"category": "education",
"description": "This is just a demo",
"resources": [
{
"name": "ActivatedImage",
"resourceType": "ResourceType::Image::PNG",
"value": {
"resourceValueType": "ResourceValueType::URI",
"value": "https://demo.blockv.io/developer-portal/tutorial-first-vatom/vee.png"
}
},
{
"name": "CardImage",
"resourceType": "ResourceType::Image::JPEG",
"value": {
"resourceValueType": "ResourceValueType::URI",
"value": "https://demo.blockv.io/developer-portal/tutorial-first-vatom/popup-card.jpg"
}
}
],
"visibility": {
"type": "owner",
"value": "*"
}
},
"private": {}
}
}
{
"payload": {
"success_message": "created TemplateVariation successfully"
}
}
Once the Template Variation is created, you can now emit a vAtom:
Note that both the template
and variation
are unpublished
. There is a limit on total number of vatoms (10
vatoms) that can be emitted.
API Reference: Emit vAtom API
{
"template_variation": "blockv.demo::demo::Image::v1::tutorial::v1",
"num": 1
}
{
"payload": {
"num_created": 1,
"num_errors": 0,
"ids": [
"7c45926a-5a54-4632-b1bb-9f2bbc65ffbc"
]
}
}
Congratulations! You have just created a vAtom. You can find this vatom in your wallet (next step).
As a user you can easily get an inventory of your vAtoms which allows you to easily build a wallet of virtual goods or tokens.
API Reference: Get Inventory API
{
"parent_id": ".",
"limit": 10,
"page": 0
}
{
"payload": {
"vatoms": [
{
"id": "7c45926a-5a54-4632-b1bb-9f2bbc65ffbc",
"in_sync": false,
"is_syncing": false,
"private": {},
"unpublished": true,
"vAtom::vAtomType": {
"acquirable": false,
"author": "6e0ed730-26ff-45c0-b558-0df979d754ca",
"category": "education",
"cloned_from": "",
"cloning_score": 0,
"commerce": {
"pricing": {
"pricingType": "",
"value": {
"currency": "",
"price": "",
"valid_from": "",
"valid_through": "",
"vat_included": false
}
}
},
"description": "This is just a demo",
"disabled": false,
"dropped": false,
"geo_pos": {
"$reql_type$": "GEOMETRY",
"coordinates": [
0,
0
],
"type": "Point"
},
"in_contract": false,
"in_contract_with": "",
"notify_msg": "",
"num_direct_clones": 0,
"owner": "6e0ed730-26ff-45c0-b558-0df979d754ca",
"parent_id": ".",
"publisher_fqdn": "blockv",
"redeemable": false,
"resources": [
{
"name": "ActivatedImage",
"resourceType": "",
"value": {
"resourceValueType": "",
"value": "https://cdn.blockv.io/blockv.templates/publisher_fqdn/Template_Name/v1/Variation_Name/v1/icon.png"
}
},
{
"name": "CardImage",
"resourceType": "",
"value": {
"resourceValueType": "",
"value": "https://cdn.blockv.io/blockv.templates/publisher_fqdn/Template_Name/v1/Variation_Name/v1/example.jpg"
}
}
],
"root_type": "vAtom::vAtomType",
"states": [
{
"name": "Activated",
"on_state_change": {
"reactor": ""
},
"value": {
"type": "boolean",
"value": "true"
}
}
],
"tags": [],
"template": "blockv.demo::demo::Image::v1",
"template_variation": "blockv.demo::demo::Image::v1::tutorial::v1",
"title": "Tutorial",
"tradeable": false,
"transferable": true,
"transferred_by": "",
"visibility": {
"type": "owner",
"value": "*"
}
},
"version": "v1::vAtomType",
"when_created": "2018-03-13T19:32:45Z",
"when_modified": "2018-03-13T19:32:45Z"
}
],
"faces": [
{
"id": "7d2aaa60-a686-4013-b269-989376d5fee1",
"template": "blockv.demo::demo::Image::v1",
"meta": {
"created_by": "Appdriver Backend",
"when_created": "2018-03-12T22:18:19Z",
"modified_by": "",
"when_modified": "2018-03-12T22:18:19Z",
"data_type": "v1::FaceType",
"in_sync": false,
"when_synced": "",
"is_syncing": false
},
"properties": {
"display_url": "native://image",
"package_url": "native://image",
"constraints": {
"bluetooth_le": false,
"contact_list": false,
"gps": false,
"three_d": false,
"view_mode": "icon",
"platform": "generic",
"quality": "high"
},
"resources": [
"ActivatedImage"
]
}
}
],
"actions": [
{
"name": "blockv.demo::demo::Image::v1::Action::Transfer",
"meta": {
"created_by": "Appdriver Backend",
"when_created": "2018-03-12T22:16:48Z",
"modified_by": "",
"when_modified": "2018-03-12T22:16:48Z",
"data_type": "v1::ActionType",
"in_sync": false,
"when_synced": "",
"is_syncing": false
},
"properties": {
"name": "blockv.demo::demo::Image::v1::Action::Transfer",
"reactor": "blockv://v1/Transfer",
"wait": true,
"rollback": false,
"abort_on_pre_error": true,
"abort_on_post_error": false,
"abort_on_main_error": true,
"timeout": 10000,
"guest_user": true,
"state_impact": [],
"policy": {
"pre": [],
"rule": "",
"post": []
},
"params": {
"input": [],
"output": []
},
"config": {
"auto_create_landing_page": "http://viewer.blockv.io/#",
"auto_create_mode": "claim",
"auto_create_non_existing_recipient": true
},
"limit_per_user": 0,
"action_notification": {
"on": true,
"msg": "You received a vAtom",
"custom": {}
}
}
}
]
}
}
You can now simply invoke the Transfer action to send the vAtom to someone based on their phone number or email address.
API Reference: Perform Actions API
Being able to transfer virtual goods (which can include tokens etc) on the blockchain using a phone number or email address greatly simplifies a highly complex process — making the blockchain networks much more accessible to developers. When transferring a vAtom on BlockV, the vAtom is simultaneously transferred from the publisher’s blockchain wallet to the target’s blockchain wallet.
As an excercise, take the existing template add additional actions drop
and acquire
. Once these actions are available at the template level, you should be able to drop
an atom at a geo-location for others to pickup (acquire
).