Transactions

Transaction Types

Each transaction type is given an integer to represent it as follows. The transaction fee is included below too, though libraries should have these set by default.

Transaction Type Name Description Fee (ZBS)
3 Issue Create a new token / asset 500.00
4 Transfer Send ZBS or Token to another address 0.05
5 ReIssue Issue more of your token 200.00
6 Burn Destroy X tokens 5.00
7 Exchange A Trade. Buy / Sell 0.20
8 Lease Start leasing to another address 5.00
9 LeaseCancel Cancel a Lease 1.00
10 CreateAlias Create an alias for your address 10
11 MassTransfer Send up to 100 Transfers in 1 transaction 0.05
12 Data Save data to the blockchain 0.03
13 SetScript Add a script to an Address 10.00
14 CustomFee Setup a Custom Fee for your Asset 50.00
15 SetAsset Set a script on an asset 10.00
16 ContractInvoke Run a contract 0.10
17 Index
18 CustomFee

Extra Fee: 0.01 (In addition to fee when operating with smart assets or accounts)

Transaction Formats

The below sections demonstrate the JSON transaction format that is required to make a transaction. When signing a transaction on a Node, you can exclude the signature/proofs field as the server will give you this back.

Within the transaction JSON, Fee's are expressed without decimal values, so ZBS has 8 decimal places, and 100000000 represents 1 ZCL within the transaction JSON.

Issue / ReIssue Transaction

Issue and ReIssue take the same format apart from the type

{
    "senderPublicKey":"2M25DqL2W4rGFLCFadgATboS8EPqyWAN3DjH12AH5Kdr",
    "quantity": 50000
    "fee": 50000000000,
    "description":"My New Token",
    "type":3 / 5,
    "version":2,
    "reissuable":true,
    "script":"base64:AQa3b8tH", // Optional Script for 'Smart Assets'
    "sender":"3Mz9N7YPfZPWGd4yYaX6H53Gcgrq6ifYiH7",
    "feeAssetId":null, // When paying using custom fees
    "chainId":84, // 84 (T) for testnet 90 (Z) for mainnet
    "proofs":["4yjVxzrLuXUq5y2QCa2LDn1Fp9P63hPBmqDLGQCqn41EB1uZ1pys79NP81h7FxRBnZSbpNGbz1xjwckHcPAQHmFX"],
    "decimals":2,
    "name":"MyTestToken"
    }

Transfer Transaction

{
    "senderPublicKey":"4c5K4kGeRdrnSYZ9wngQKSozikVgfxmDEuViirsyUHwd",
    "amount":110000000
    fee":500000,
    "type":4,
    "version":2,
    "attachment":"",
    "sender":"3MyT3r1S8xvKtiKnLgVNNiSAiQdnMbffBQy",
    "feeAssetId":null,
    "proofs":["2KFbYWi9BJwDG9dbiJQzC9qJPpu4Ug8Ybs331fQbX9FAkEpEtrj9DKvwNG7cb2m98DV6NCoKH4MBVtGGFsnQWPV6"],
    "assetId":null, // If we are sending an asset, null for ZBS
    "recipient":"3MqEisFsWdhvDMAKBwZzZv4niVsfJJtxcaw",
    "feeAsset":null
}

Burn Transaction

Burning tokens can be useful for systems where a token is used to activate or fund a service and once the service has been used the token can be burnt.

{ type: 6,
  id: 'ComWcac6FMKBT4sNNyNwDDFG8T63s3xorV4M7x8y6TVm',
  sender: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo',
  senderPublicKey: '4vR9m3vFuAWqQ6zJWDeqnPCMgLsxKkz2sQcAcEjHduU',
  fee: 500000000,
  timestamp: 1561326417304,
  proofs:
   [ '3c2ao2uF2T6sxLJqHZSdAY2brjHHeUVQqNMDDJPxfh33JAyTeocnAWGt7nw6dwTB2mBEG8CimuC4BP19DCfB98YZ' ],
  version: 2,
  assetId: 'BiuhdjnH9qxgfax52zXgJw3b5ArxCdA4q8kYECqWoEYT',
  amount: 1,
  chainId: 84
 }

Exchange Transaction

{
    "senderPublicKey":"8QUAqtTckM5B8gvcuP7mMswat9SjKUuafJMusEoSn1Gy",
    "amount":1000000000,
    "fee":300000,
    "type":7,
    "version":2,
    "sellMatcherFee":30000,
    "buyMatcherFee":300000,
    "sender":"3N8aZG6ZDfnh8YxS6aNcteobN8eXTWHaBBd",
    "feeAssetId":null,
    "proofs":["4kBxzWXSzbM4jQbi8SmNtZeRJEwUkMzCezf8AQYeu5z124ajpKG9GVJZbHdnRWz9R1Key5opySDbKj6B4zTtZsNF"],
    "price":90,
    "id":"28biMwpgZVjAUk5iJnWvphaFgr8Tybwqe6s5JxGTdDWJ",
    "order2":
        {"version":2,
        "id":"GaCXusGNDkYZ8iciV3cuUKb7r4awESwocghJXnFyLxYk",
        "sender":"3MwBD8xZByEHAnwB69eexgVPAPD5ucw1Rna","senderPublicKey":"HHonWv97CD2XXgRNbny7aCWP8amgYatCaTEthq38P1Do","matcherPublicKey":"8QUAqtTckM5B8gvcuP7mMswat9SjKUuafJMusEoSn1Gy", //Obtained from https://matcher.testnet-0bsnetwork.com/matcher
        "assetPair":
            {
                "amountAsset":"4CFzLzP3vBHBev9GyeWm6AP7hm72qn6xELU1je5Wmq8L",
                "priceAsset":null
            },
            "orderType":"sell",
            "amount":10000000000,
            "price":90,
            "timestamp":1548657730301,
            "expiration":1551163330301,
            "matcherFee":300000,"signature":"2uX8CedW8oPXqktAwJLm9emFArUimxcXD9pqHnd2mEWJYYJuBChzk59fzB7DVeKnczmosJa4vfAvCzqY8DSoq4Rr","proofs":["2uX8CedW8oPXqktAwJLm9emFArUimxcXD9pqHnd2mEWJYYJuBChzk59fzB7DVeKnczmosJa4vfAvCzqY8DSoq4Rr"]
        },
    "order1":
        {
            "version":2,
            "id":"4EFqAQLW8S2Swo3dh5RZ5DXNtfQRDij7Y28RayN3vW7G",
            "sender":"3N5h5G6ToFR7cMMzdzR1hvUaMNuz7quCt4H","senderPublicKey":"He5xRqcQBzk1VbdH7GP3XdGbB7dLwsvEbyLfNAbVkunJ","matcherPublicKey":"8QUAqtTckM5B8gvcuP7mMswat9SjKUuafJMusEoSn1Gy", //Obtained from https://matcher.testnet-0bsnetwork.com/matcher
            "assetPair":
                {
                    "amountAsset":"4CFzLzP3vBHBev9GyeWm6AP7hm72qn6xELU1je5Wmq8L",
                    "priceAsset":null
                },
            "orderType":"buy",
            "amount":1000000000,
            "price":100,
            "timestamp":1548661139113,
            "expiration":1548661439111,
            "matcherFee":300000,"signature":"23mzQgZgjsKBCTLRLS8ktbtP6EZx3gABT9AF63UF4nuhS1o7XJgEGby2umhijo4t8yKuw1CmZ3UVab4A6vtod3H8","proofs":["23mzQgZgjsKBCTLRLS8ktbtP6EZx3gABT9AF63UF4nuhS1o7XJgEGby2umhijo4t8yKuw1CmZ3UVab4A6vtod3H8"]
            }
    }

Lease Transaction

{
    "senderPublicKey":"FB5ErjREo817duEBBQUqUdkgoPctQJEYuG3mU7w3AYjc",
    "amount":1,
    "fee":500000000,
    "type":8,
    "version":2,
    "sender":"3Mps7CZqB9nUbEirYyCMMoA7VbqrxLvJFSB",
    "feeAssetId":null,
    "proofs":["fkWj6RkV722jhv72BY6Eo5BZ6N4T8nTPcmQVmoBs14Cv53W2VfRE7C9rT1TN4CZfAW2Y2YhVFm24NGZh75eBHq4"],"recipient":"3NCKpqzSnHmXhZEmqYy4U6RUKUAJDTxWgWP"
}

Lease Cancel

{
    "senderPublicKey":"FB5ErjREo817duEBBQUqUdkgoPctQJEYuG3mU7w3AYjc",
    "amount":1
    "fee":100000000,
    "type":8,
    "version":2,
    "sender":"3Mps7CZqB9nUbEirYyCMMoA7VbqrxLvJFSB",
    "feeAssetId":null,
    "proofs":["fkWj6RkV722jhv72BY6Eo5BZ6N4T8nTPcmQVmoBs14Cv53W2VfRE7C9rT1TN4CZfAW2Y2YhVFm24NGZh75eBHq4"],"recipient":"3NCKpqzSnHmXhZEmqYy4U6RUKUAJDTxWgWP"

}

Create Alias

{
    "senderPublicKey":"BVv1ZuE3gKFa6krwWJQwEmrLYUESuUabNCXgYTmCoBt6",
    "sender":"3N8S4UtauvDAzpLiaRyDdHn9muexWHhBP4D",
    "feeAssetId":null,
    "proofs":["22QJfRKX7kUQt4qjdnUqZAnhqukqhnofE27uvP8Q5xnBf8M6PCNtWVGq2ngm6m7Voe7duys59D1yU9jhKrmdXDCe"],
    "fee":1000000000,
    "alias":"91f452553298770f",
    "id":"AD7KmXwoVNc2fXsmaxsHsrnT1tfPF3HsWYtfjFijVsvM",
    "type":10,
    "version":2
}

Mass Transfer

{ type: 11,
  id: '48Dn8uK95jL8iZqv81gqv7PxJtbrZihjmg1Zdcs1428P',
  sender: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo',
  senderPublicKey: '4vR9m3vFuAWqQ6zJWDeqnPCMgLsxKkz2sQcAcEjHduU',
  fee: 10000000,
  timestamp: 1561326259593,
  proofs:
   [ 'YxmbijFFmNM9goWMoxxcPtHM36TKFVrMfC5i9yZEPtZiqoDZ13M3kQJBw557bi728vmR9ydFYZ847gdoisbCVnM' ],
  version: 1,
  assetId: null,
  attachment: '',
  transferCount: 2,
  totalAmount: 2,
  transfers:
   [ { recipient: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo', amount: 1 },
     { recipient: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo', amount: 1 } ] }

Data

{ type: 12,
  id: '3d8fQKQ8Va43vvVzbKJChMrPzQDNgLBH9GaQw23YhdZC',
  sender: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo',
  senderPublicKey: '4vR9m3vFuAWqQ6zJWDeqnPCMgLsxKkz2sQcAcEjHduU',
  fee: 3000000,
  timestamp: 1561326312175,
  proofs:
   [ 'mWVbzSuomwiaowrNLBhEFcZE6dfHhK1tYWrqQZ124mum8MJuoksuqVZUjaRUW7rwLw4cEc33unKTzYP4X9ftRu9' ],
  version: 1,
  data:
   [ { key: 'integerVal', type: 'integer', value: 1 },
     { key: 'booleanVal', type: 'boolean', value: true },
     { key: 'stringVal', type: 'string', value: 'hello' },
     { key: 'binaryVal', type: 'binary', value: 'base64:AQIDBA==' } ] }

SetScript

Set Asset

Invoke Script

Index

{ type: 4,
  id: '8LjTjtAqQscTk5o3VuzViyp5Dk3dPZ9W26ezHUEyi2LW',
  sender: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo',
  senderPublicKey: '4vR9m3vFuAWqQ6zJWDeqnPCMgLsxKkz2sQcAcEjHduU',
  fee: 5000000,
  timestamp: 1561326556314,
  proofs:
   [ '5H4KDmjYh5tfe7iurVxyZZnKGwcVrproBJs83kLCgvxUi9bn6S1yiLmnrCsD5Zaijm7eJgvTaTZnS6fdx7aW8xKp' ],
  version: 2,
  recipient: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo',
  assetId: null,
  feeAssetId: null,
  feeAsset: null,
  amount: 1,
  attachment: '' }

Custom Fees

{ type: 14,
  id: 'HfEnBX9PBkTpNLs5BgxvqmrPQkowb9dogMcSFSSZg9Vq',
  sender: '3NCGfpFCVCmMSgCSct8BhExjRZ3E8i83Goo',
  senderPublicKey: '4vR9m3vFuAWqQ6zJWDeqnPCMgLsxKkz2sQcAcEjHduU',
  fee: 5000000000,
  timestamp: 1561328515891,
  proofs:
   [ 'Qe81c41WbSDwV1wXRVKZK9ze7kPhE5B5wSRB96ZsZ21ZbZ6hSq89accLdHH1gsZXQvC2WphL1VLKNYG1DVkWZwF' ],
  version: 1,
  assetId: 'BiuhdjnH9qxgfax52zXgJw3b5ArxCdA4q8kYECqWoEYT',
  minSponsoredAssetFee: 100 }

Signing Transactions

Signing transactions manually is quite complex, and usually its best to use a library to do so, however if you find you dont have a library available for your platform of choice, you may need to do it manually. The process involves concatenting the byte values of fields from the transaction to create a long byte array, and signing this using Curve25519 and your private key, and inserting this into the transaction payload.

Rather than include excessive details here on how to do that, the below code sample in C Sharp should be readable enough to understand how the process works, and you can also examine the code of our other libraries as they are all open source. If you need specific details or guidance, please do contact us using the details on the Front Page

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Numerics;
using DictionaryObject = System.Collections.Generic.Dictionary<string, object>;
using org.whispersystems.curve25519.csharp;
using System.Reflection;
using org.whispersystems.curve25519;
using System.IO;
using Program;

namespace ZbsSignAndBroadcastDataTransaction
{
    class Program
    {


        /// <summary>
        ///  This is a raw demonstration of a Data transaction. It's purpose is to save data to the blockchain (Variable TransactionData) - The signing procedure is demonstrated below.
        /// </summary>


        // 3MqSRBfLsTzXssscX8crUH88zJegvyrd5WK
        // Seed Phrase: despair olive coconut design rubber proud silver rotate glow gloom extra income effort about summer
        // Private Key: Bq6dvj1v48EAyS34Nu6bZS7tWtaxgQnChQrJvxr5x1pM
        // Public Key: 7cgFXWXTGXemR2yHq1MYx8o7jVKq6Y4Y5bTyaJ478aeG
        // Replenish Balance: https://explorer.testnet-0bsnetwork.com/faucet

        private static readonly Curve25519 Cipher = Curve25519.getInstance(Curve25519.BEST);

        static void Main(string[] args)
        {

            byte Version = 1;
            byte TxType = 12;
            byte[] SenderPrivateKey;
            byte[] SenderPublicKey;
            byte[] transactionBytes;

            DateTime Timestamp = DateTime.UtcNow;


            SenderPrivateKey = Base58.Decode("Bq6dvj1v48EAyS34Nu6bZS7tWtaxgQnChQrJvxr5x1pM"); // These are obtained from the client. X25519 Keys
            SenderPublicKey = Base58.Decode("7cgFXWXTGXemR2yHq1MYx8o7jVKq6Y4Y5bTyaJ478aeG");



            var TransactionData = new DictionaryObject
            {
                { "test num", (long)1 },
                { "test true", true },
                { "test bytes", new byte[] { 1, 2, 3, 4, 5}},
                { "test string", "Hello!"}
            };


            // To create a signature, we create a byte array.

            using (var stream = new MemoryStream())
            using (var writer = new BinaryWriter(stream))
            {
                writer.Write(TxType);  // First we add the Transaction Type (Its a number.. 12 is Data transaction)
                writer.Write(Version);  // Transaction version is set to 1
                writer.Write(SenderPublicKey);
                writer.WriteShort((short)TransactionData.Count); // Next we set the number of data items we are sending, in this case, theres 4 (TransactionData)

                foreach (var pair in TransactionData)  // For each data item we need to add its Length, Key and its value
                {
                    var key = Encoding.UTF8.GetBytes(pair.Key);
                    writer.WriteShort((short)key.Length);
                    writer.Write(key);
                    writer.WriteObject(pair.Value);
                }

                writer.WriteLong(Timestamp.ToLong());
                writer.WriteLong(3000000); //Arbitary fee

                transactionBytes = stream.ToArray();

            }


            // Next we take the byte array and sign it to generate a Proof.
            var proof = Cipher.calculateSignature(SenderPrivateKey, transactionBytes);


            // And finally, create a JSON payload to broadcast the transaction to the network


            var finalTransaction = new DictionaryObject
            {
                {"type", (byte) 12},
                {"version", Version},
                {"senderPublicKey", SenderPublicKey.ToBase58() },
                {"sender", "3MqSRBfLsTzXssscX8crUH88zJegvyrd5WK" },
                {"data", TransactionData.Select(pair => new DictionaryObject // Add each data entry to the JSON payload
                {
                    {"key", pair.Key},
                    {"type", pair.Value is long ? "integer" : (pair.Value is bool ? "boolean" : (pair.Value is string ? "string"  : "binary"))},
                    {"value", pair.Value is byte[] bytes ? bytes.ToBase64() : pair.Value }
                })},
                {"fee", 3000000}, //Arbitary fee
                {"timestamp", Timestamp.ToLong()},
                {"proofs", new string[] { proof.ToBase58()} } // Add the signature to the JSON

            };



            String TestNetHost = "https://node1.testnet-0bsnetwork.com";

            var response = Http.Post(TestNetHost + "/transactions/broadcast", finalTransaction); // Broadcast the transaction to a public node on the network

            Console.Write(response); // Output the response
            Console.Read();


    }
    }
}