One of the core features of Avalanche is the ability to create new blockchains. Avalanche currently supports the creation of new instances of the Avalanche Virtual Machine (AVM) and the Timestamp VM.
In this tutorial, we’ll create a blockchain by creating a new instance of the AVM.
You will need a running node, a user on the node, and some AVAX in the address controlled by the user. All of that is covered in the Run an Avalanche Node tutorial.
Next, you need to have your node be a validator on the Primary Network. You can find out how to do that in the Add a Validator tutorial. It is recommended you do that with API calls, since that is the way you will be interacting with your node in the rest of this tutorial.
Every blockchain is validated by a subnet. Before you can create a blockchain, you’ll need a subnet to validate it. You can also use a subnet that already exists if you have a sufficient number of its control keys.
{% page-ref page="create-a-subnet.md" %}
The subnet needs validators in it to, well, validate blockchains.
Make sure the subnet that will validate your blockchain has at least snow-sample-size
validators in it. (Recall that snow-sample-size
is one of the command-line arguments when starting a node. Its default value is 20.)
{% page-ref page="../nodes-and-staking/add-a-validator.md" %}
Each blockchain has some genesis state when it’s created. Each Virtual Machine has a static API method named buildGenesis
that takes in a JSON representation of a blockchain’s genesis state and returns the byte representation of that state. (This isn’t true for some VMs, like the Platform VM, because we disallow the creation of new instances.)
The AVM’s documentation specifies that the argument to avm.buildGenesis
should look like this:
{
"genesisData":
{
"assetAlias1": { // Each object defines an asset
"name": "human readable name",
"symbol":"AVAL", // Symbol is between 0 and 4 characters
"initialState": {
"fixedCap" : [ // Choose the asset type.
{ // Can be "fixedCap", "variableCap"
"amount":1000, // At genesis, address A has
"address":"A" // 1000 units of asset
},
{
"amount":5000, // At genesis, address B has
"address":"B" // 1000 units of asset
},
... // Can have many initial holders
]
}
},
"assetAliasCanBeAnythingUnique": { // Asset alias can be used in place of assetID in calls
"name": "human readable name", // names need not be unique
"symbol": "AVAL", // symbols need not be unique
"initialState": {
"variableCap" : [ // No units of the asset exist at genesis
{
"minters": [ // The signature of A or B can mint more of
"A", // the asset.
"B"
],
"threshold":1
},
{
"minters": [ // The signatures of 2 of A, B and C can mint
"A", // more of the asset
"B",
"C"
],
"threshold":2
},
... // Can have many minter sets
]
}
},
... // Can list more assets
}
}
To create the byte representation of this genesis state, call avm.buildGenesis
. Your call should look like the one below. Note that this call is made to the AVM’s static API endpoint, /ext/vm/avm
.
curl -X POST --data '{
"jsonrpc": "2.0",
"id" : 1,
"method" : "avm.buildGenesis",
"params" : {
"genesisData": {
"asset1": {
"name": "myFixedCapAsset",
"symbol":"MFCA",
"initialState": {
"fixedCap" : [
{
"amount":100000,
"address": "8UeduLccQuSmYiY3fGQEyotM9uXxoHoQQ"
},
{
"amount":100000,
"address": "AgVkHvvDShLumJrzXzkwuHa7rYpewj9Kg"
},
{
"amount":50000,
"address": "AwBDGsUwNdXgVc8XG2E8A8dL3bkoVbkL9"
},
{
"amount":50000,
"address": "AATN8YjgmFjC2jQRq45sEeGcBFXNYPcM8"
}
]
}
},
"asset2": {
"name": "myVarCapAsset",
"symbol":"MVCA",
"initialState": {
"variableCap" : [
{
"minters": [
"AATN8YjgmFjC2jQRq45sEeGcBFXNYPcM8",
"FNqMDYafoDVYQ2o4a7Zd9maJAxcUEieQb"
],
"threshold":1
},
{
"minters": [
"JJSiKQfha9Z2TiMxBZ8XdW9F6KFw8aKS4",
"7jJHY1vZL6AAbCFb97KMLY8nqMQVyd5JG",
"58pM5cEf1wMSncPdCwtQ8tbHs2xdMA4eo"
],
"threshold":2
}
]
}
}
}
}
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/vm/avm
This returns the byte representation of your blockchain’s genesis state:
{
"jsonrpc": "2.0",
"result": {
"bytes": "111TNWzUtHKoSvxohjyfEwE2X228ZDGBngZ4mdMUVMnVnjtnawW1b1zbAhzyAM1v6d7ECNj6DXsT7qDmhSEf3DWgXRj7ECwBX36ZXFc9tWVB2qHURoUfdDvFsBeSRqatCmj76eZQMGZDgBFRNijRhPNKUap7bCeKpHDtuCZc4YpPkd4mR84dLL2AL1b4K46eirWKMaFVjA5btYS4DnyUx5cLpAq3d35kEdNdU5zH3rTU18S4TxYV8voMPcLCTZ3h4zRsM5jW1cUzjWVvKg7uYS2oR9qXRFcgy1gwNTFZGstySuvSF7MZeZF4zSdNgC4rbY9H94RVhqe8rW7MXqMSZB6vBTB2BpgF6tNFehmYxEXwjaKRrimX91utvZe9YjgGbDr8XHsXCnXXg4ZDCjapCy4HmmRUtUoAduGNBdGVMiwE9WvVbpMFFcNfgDXGz9NiatgSnkxQALTHvGXXm8bn4CoLFzKnAtq3KwiWqHmV3GjFYeUm3m8Zee9VDfZAvDsha51acxfto1htstxYu66DWpT36YT18WSbxibZcKXa7gZrrsCwyzid8CCWw79DbaLCUiq9u47VqofG1kgxwuuyHb8NVnTgRTkQASSbj232fyG7YeX4mAvZY7a7K7yfSyzJaXdUdR7aLeCdLP6mbFDqUMrN6YEkU2X8d4Ck3T"
},
"id": 1
}
Now let’s create the new blockchain. To do so, we call platform.createBlockchain
. Your call should look like the one below. You have to change subnetID
to the subnet that will validate your blockchain, and supply a username
that controls a sufficient number of the subnet’s control keys. As a reminder, you can find out what a subnet’s threshold and control keys are by calling platform.getSubnets
.
curl -X POST --data '{
"jsonrpc": "2.0",
"method": "platform.createBlockchain",
"params" : {
"subnetID": "KL1e8io1Zi2kr8cTXxvi321pAzfQuUa8tmBfadqpf9K2dc2TT",
"vmID":"avm",
"name":"My new AVM",
"genesisData": "111TNWzUtHKoSvxohjyfEwE2X228ZDGBngZ4mdMUVMnVnjtnawW1b1zbAhzyAM1v6d7ECNj6DXsT7qDmhSEf3DWgXRj7ECwBX36ZXFc9tWVB2qHURoUfdDvFsBeSRqatCmj76eZQMGZDgBFRNijRhPNKUap7bCeKpHDtuCZc4YpPkd4mR84dLL2AL1b4K46eirWKMaFVjA5btYS4DnyUx5cLpAq3d35kEdNdU5zH3rTU18S4TxYV8voMPcLCTZ3h4zRsM5jW1cUzjWVvKg7uYS2oR9qXRFcgy1gwNTFZGstySuvSF7MZeZF4zSdNgC4rbY9H94RVhqe8rW7MXqMSZB6vBTB2BpgF6tNFehmYxEXwjaKRrimX91utvZe9YjgGbDr8XHsXCnXXg4ZDCjapCy4HmmRUtUoAduGNBdGVMiwE9WvVbpMFFcNfgDXGz9NiatgSnkxQALTHvGXXm8bn4CoLFzKnAtq3KwiWqHmV3GjFYeUm3m8Zee9VDfZAvDsha51acxfto1htstxYu66DWpT36YT18WSbxibZcKXa7gZrrsCwyzid8CCWw79DbaLCUiq9u47VqofG1kgxwuuyHb8NVnTgRTkQASSbj232fyG7YeX4mAvZY7a7K7yfSyzJaXdUdR7aLeCdLP6mbFDqUMrN6YEkU2X8d4Ck3T",
"username":"USERNAME",
"password":"PASSWORD"
},
"id": 1
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/P
The response contains the transaction ID:
{
"jsonrpc": "2.0",
"result": {
"txID": "xAd5n5PQFV6RRo8UgH54Gf5tJs8oQdctQS2ygp5F2dKZDckYH",
"changeAddr": "P-avax103y30cxeulkjfe3kwfnpt432ylmnxux8r73r8u"
},
"id": 1
}
After a few seconds, the transaction to create our blockchain should have been accepted and the blockchain should exist (assuming the request was well-formed, etc.)
To check, call platform.getBlockchains
. This returns a list of all blockchains that exist.
curl -X POST --data '{
"jsonrpc":"2.0",
"id" :1,
"method" :"platform.getBlockchains",
"params" :{}
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/P
The response confirms that the blockchain was created:
{
"jsonrpc": "2.0",
"result": {
"blockchains": [
{
"id": "AXerNQX7voY2AABaRdTAyXcawBURBR6thPRJp43LtPpZZi6Qz",
"name": "X-Chain",
"subnetID": "11111111111111111111111111111111LpoYY",
"vmID": "jvYyfQTxGMJLuGWa55kdP2p2zSUYsQ5Raupu4TW34ZAUBAbtq"
},
{
"id": "tZGm6RCkeGpVETUTp11DW3UYFZmm69zfqxchpHrSF7wgy8rmw",
"name": "C-Chain",
"subnetID": "11111111111111111111111111111111LpoYY",
"vmID": "mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6"
},
{
"id": "CqhF97NNugqYLiGaQJ2xckfmkEr8uNeGG5TQbyGcgnZ5ahQwa",
"name": "Simple DAG Payments",
"subnetID": "11111111111111111111111111111111LpoYY",
"vmID": "sqjdyTKUSrQs1YmKDTUbdUhdstSdtRTGRbUn8sqK8B6pkZkz1"
},
{
"id": "VcqKNBJsYanhVFxGyQE5CyNVYxL3ZFD7cnKptKWeVikJKQkjv",
"name": "Simple Chain Payments",
"subnetID": "11111111111111111111111111111111LpoYY",
"vmID": "sqjchUjzDqDfBPGjfQq2tXW1UCwZTyvzAWHsNzF2cb1eVHt6w"
},
{
"id": "2SMYrx4Dj6QqCEA3WjnUTYEFSnpqVTwyV3GPNgQqQZbBbFgoJX",
"name": "Simple Timestamp Server",
"subnetID": "11111111111111111111111111111111LpoYY",
"vmID": "tGas3T58KzdjLHhBDMnH2TvrddhqTji5iZAMZ3RXs2NLpSnhH"
},
{
"id": "zpFTwJwzPh3b9N6Ahccy4fXdJFHJJdhGah5z731J6ZspcYKpK",
"name": "My new AVM",
"subnetID": "KL1e8io1Zi2kr8cTXxvi321pAzfQuUa8tmBfadqpf9K2dc2TT",
"vmID": "jvYyfQTxGMJLuGWa55kdP2p2zSUYsQ5Raupu4TW34ZAUBAbtq"
}
]
},
"id": 1
}
You can interact with this new instance of the AVM almost the same way you’d interact with the X-Chain. There are two small differences:
- The API endpoint of your blockchain is
127.0.0.1:9650/ext/bc/zpFTwJwzPh3b9N6Ahccy4fXdJFHJJdhGah5z731J6ZspcYKpK
. - Addresses are prepended with
zpFTwJwzPh3b9N6Ahccy4fXdJFHJJdhGah5z731J6ZspcYKpK-
rather thanX-
.
In the genesis data we specified that address 8UeduLccQuSmYiY3fGQEyotM9uXxoHoQQ
has 100,000 units of the asset with alias asset1
. Let’s verify that:
curl -X POST --data '{
"jsonrpc":"2.0",
"id" :1,
"method" :"avm.getBalance",
"params" :{
"address":"zpFTwJwzPh3b9N6Ahccy4fXdJFHJJdhGah5z731J6ZspcYKpK-8UeduLccQuSmYiY3fGQEyotM9uXxoHoQQ",
"assetID":"asset1"
}
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/zpFTwJwzPh3b9N6Ahccy4fXdJFHJJdhGah5z731J6ZspcYKpK
```json
{
"jsonrpc": "2.0",
"result": {
"balance": "100000"
},
"id": 1
}