This textbook was generated automatically from a GitHub Repo. Visit the repo for more information.

IOTA Developer Essentials Banner

IOTA Developer Essentials

Languages Covered GitHub Repo GitHub Gist Interactive Lab Complete Textbook IOTA Ecosystem

These notebooks provide a self-study introduction to IOTA protocol and are designed for developers and tech enthusiasts who would like to get quickly familiar with the IOTA. Technical-related information is accompanied with interactive code snippets to help you to quickly jump on the platform and be ready to build own solutions based on it.

Motto of this tutorial: "Let's start with baby steps before we can try and fly."

Main Goal

Main goal of this guide is to provide a comprehensive yet well-balanced proportion of deep-dive technical information and essentials.

Getting Started

There are several chapters available. If you are not familiar with IOTA then it is highly recommended to follow them in the particular order to understand all important IOTA concepts properly. Otherwise you may get quickly confused later.

Code Snippets

Code snippets are currently based on Python, NodeJs and C#. Since Python is very descriptive language and easy to understand, I believe the tutorial is basically understandable to anyone. Anyway, all important IOTA concepts are described in language-agnostic way and so you would be able to use any language of your choice.

Interested to see what is the overall code base coverage breaked down to a specific language? The following page provides you such an info. Needless to say, we are working hard to be sure there is no language that would be a second-class citizen.

Languages Covered

IOTA Developer Essentials are accompanied by IOTA Developer Lab and so you can play with all code snippets directly in your web browser. Feel free to experiment with the prepared code snippets and get your hands dirty. It is a best way how to get familiar quickly.

Interactive Lab

All standalone code snippets are also maintained @ GitHub Gist. It enables you to fork them, comment them, share them, embed them and track all changes to them. See the action panel next to each code snippet.

GitHub Gist

Viewing and running

Interactive Mode

There exists a side project called IOTA Developer Lab that is built on top of IOTA Developer Essentials. The project provides infrastructure services for you to be able to get an interactive experience with all code snippets.

Interactive Lab

Static Mode

If you prefer a static experience (for printing purposes, for instance) you can reach the given notebooks as a Complete Textbook that is compiled from all source materials:

Complete Textbook

Everything is tightly linked together and so you can easily switch between different languages for instance, or you can share links to specific chapters, code snippets, etc. All links are static so feel free to share them.

Feedback and corrections

These notebooks are maintained at GitHub. Issues can be submited via issue tracker.

About

These notebooks are developed and maintained by Petr Zizka (petr@zizkovi.name). It has been supported by the EDF Grant (https://blog.iota.org/the-5th-cohort-of-iota-ecosystem-development-fund-grantees-5cbf05227525).

It is ongoing process since IOTA is still under heavy development and rapidly emerging every day. So stay tuned - the plan is to keep adding fresh content on regular basis.

Feel free to follow me at Twitter or IOTA Discord Channel (@hribek25#2683). (New to Discord? Invitation link: https://discord.gg/fNGZXvh)

Disclaimer: I am not associated with the IOTA foundation. I am IOTA supporter.


Thank You

The IOTA Developer Essentials and IOTA Developer Lab are long-term projects. The main goal is to provide a comprehensive onboarding information to anybody interested in the IOTA protocol. It will cover much more chapters, more code snippets and it will also cover more languages. Your kind donations will support the vision pushing forward. Thank you.

Donations (IOTA): DSZRO9TCIJIKZOKUPVNOJFKVAHFCKL9YMLPVZUAEVZPOFXLIUWLPRQWBMVVSFTKGMGPPHXCYE9MIZEVBXQNFYKYUA9

qr code


IOTA 101: Essentials and IOTA Terminology

IOTA is an open-source Distributed Ledger Technology (DLT) with the following characteristics: permissionless, feeless and trustless. In contrary to the blockchain technology, the IOTA transactions are stored in a form of Directed Acyclic Graph (DAG) called the Tangle. Blockchain and Tangle comparison

Transactions

Transactions (TXs) in the tangle can be of two types:

  • Value transactions (IOTA token represents funds in case of value transactions)
  • Zero-value transactions (aka meta transactions)

Every transaction is attached to a specific IOTA Address. There are no miners with IOTA protocol to validate transactions. The consensus is achieved by network participants themselves. Each participant that wants to broadcast a new transaction has to approve 2 past transactions and basically attach transaction as a children node. Transactions without children transactions (waiting to be confirmed) are called tips.

Seed, private key and address

Pair of unique IOTA address and corresponding Private Key is determistically generated from a Seed. You can generate (253 - 1) different addresses/private keys based on one Seed. Anybody can send anything to the given address however only Seed owner can send funds from IOTA address since only the Seed owner is able to generate corresponding Private Key.

IOTA protocol uses One-Time Signature (OTS) for "verifying" whether private key matches to the given address, specifically Winternitz OTS (WOTS). So Private Key (specifically Key Fragments of the private key) is the key component while signing transactions using WOTS.

That's the reason why nobody should use IOTA address that have been already funds spent from. New IOTA address (of the given seed) should be generated and used instead. Please note, it applies only to fund-spending. Receiving funds/data is totaly fine even multiple times.

To summarize the key concept here. Only Seed is needed to be stored somewhere. Everything else can be safely generated from it: private key as well as address.

IOTA nodes

The Tangle runs on a distributed network of IOTA nodes called mainnet. There are also other separate "Tangles" serving for specific purposes: Devnet, SPAMnet, etc. You can interact with the network via public nodes using collection of public API calls. You can interact with any public node of the given network since all nodes "gossiping" all TXs across the whole network. Nodes are powered by a reference piece of software called IRI (IOTA Reference Implementation) as of now.

It is also important to mention that you do not exchange any sensitive information such as Seed with a node. Every sensitive operation such as TX signing is done on client's side and node is just a "messenger" that delivers your TX to the IOTA network. Centralized, decentralized and distributed models

Custom node

You can even run own IOTA node and actively participate to the whole network. There is a big advantage for developers in that case. Actual implementation of IRI provides an usefull real-time information via messaging platform called ZeroMQ and so developers can leverage that.

API calls

There are ready-made libraries for major programming languages available and so you have more developer-friendly way how to interact with the IOTA network. Anyway, you can interact with IOTA network using pure API calls without any library. It is up to you.

Ternary

It is difficult to avoid mentioning Ternary numeral system while discussing IOTA at all. IOTA is based on ternary system under the hood and so you have got to be aware of some basic terminology in this respect. You are going to face this fact in any developer-related IOTA guide out there.

Ternary

Ternary has three as its base (aka base 3) as opposted to binary which is base 2. Instead of two states (0 and 1) it recognizes three states (for ex. -1, 0, 1).

Trit

One Trite is analogous to one bit. It is the smallest digit and has three states. Trit = Trinary digit.

Tryte

Tryte consists of three trits meaning 27 combinations. $$3^3=27$$

You will recognize soon that almost everything is encoded in Trytes in the IOTA world. Tryte is visually represented using [A..Z] alphabets and number 9. One character represents one tryte (here comes 27 possible characters again). Don't get mad, IOTA libraries are going to help us.

Small example:




 


var Converter = require('@iota/converter') //loading helping converter module of iota.js library. More info: https://github.com/iotaledger/iota.js/tree/next/packages/converter#module_converter.trits

var Trytes = "YZJEATEQ9JKLZ" //some data encoded in Trytes
console.log(Trytes)
console.log("Number of Trytes: %s" , Trytes.length)

var Trits = Converter.trits(Trytes) //converting Trytes to Trits

console.log(Trits.join(","))
console.log("Number of trits: %s", Trits.length) //Number of trits is three times the number of trytes obviously
YZJEATEQ9JKLZ
Number of Trytes: 13
1,-1,0,-1,0,0,1,0,1,-1,-1,1,1,0,0,-1,1,-1,-1,-1,1,-1,0,-1,0,0,0,1,0,1,-1,1,1,0,1,1,-1,0,0
Number of trits: 39

IOTA 101: Basic network interactions

The tutorial is based on IOTA libraries called PyOTA for Python, IOTA Javascript Library (@iota/core) for JavaScript and Tangle.Net for C# (.NET 4.x and .NET Standard). It encapsulates all official IRI API calls and it can be installed via pip in Python environment, via npm in JS environment and via NuGet in case of .NET environment.

Reader is also encouraged to try a ready-made environment via IOTA Developer Lab project (at lab.iota101.info) where anyone can run a code directly in a web browser. When viewing it interactively then just select a code snippet and hit ctrl + enter which executes it.

Anyway, most of the information in the tutorial is language-agnostic and so only particular interfaces slightly differ across different IOTA libraries. Top-level details are the same regardless programming language you are going to use.

A few words about specific IOTA libraries

Python

PyOTA is well maintained and understandable IOTA library that can be reached at https://github.com/iotaledger/iota.lib.py. It is very good starting point for beginners. It can be installed via pip: pip install pyota

JavaScipt

Official IOTA Javascript library (v1.0) is currently in beta however all main functions are working just fine, please see https://github.com/iotaledger/iota.js/. It is very modular and so you can install only components you really need.

Main component is @iota/core and can be installed via npm: npm install @iota/core

As with everything in NodeJS, most of the tasks are asynchronous and they are chained via callbacks or promise. Snippets below leverage promise chaining.

C# (.NET)

Tangle.Net is well maintained IOTA library for .NET and can be reached at https://github.com/Felandil/tangle-.net. It can be installed via NuGet: nuget.exe install tangle.net

Snippets below uses synchronous functions however the library provides also asynchronous (async postfix) version.

Conneting to IOTA nodes

Before you can interact with the IOTA network you need an URL of the running IOTA node. There are dozen of directories of public nodes available, feel free to google them.

For a sake of this tutorial I use a project thetangle.org. It is a load ballancer that provides you an access to a pool of participanting nodes effortlessly.

  • Advantage: you do not have to care about public nodes and whether they are running or not
  • Disadvantage: you do not know in advance which specific node will process your API call. So in some respect results of API calls may differ since some API calls depend on specific node, such as GetNodeInfo().

Node is not synced

Even if a node is up and running it may not be fully prepared to process your API calls correctly. The node should be "synced", meaning should be aware of all TXs in the Tangle. It is better to avoid not fully synced nodes.

Health checking

Once you connect to an IOTA node you should be able to send API calls. However it is better to do some checks in advance in order to find out whether the given node is in good condition or not. IOTA nodes can be run by anyone and so health check is always a good practice.

Basic healthcheck can be done via GetNodeInfo() API call. It returns a basic information about the given IOTA node.




 


var iotalib = require('@iota/core'); // loading iota.js core module. More info: https://github.com/iotaledger/iota.js/tree/next/packages/core

// composerAPI initialization of the iota.js library
var iota = iotalib.composeAPI({
    'provider': 'https://nodes.thetangle.org:443'
});

// basic API call to double check health conditions
var promise = iota.getNodeInfo()
                    .then(info => {
                        console.log(info);
                        // Basic check whether node is in sync or not
                        // Elementary rule is that "latestMilestoneIndex" should equal to "latestSolidSubtangleMilestoneIndex" or be very close
                        if (Math.abs(info['latestMilestoneIndex'] - info['latestSolidSubtangleMilestoneIndex']) > 3) {
                            console.log('\r\nNode is probably not synced!');
                        } else {
                            console.log('\r\nNode is probably synced!');
                        }
                    })
                    .catch(error => {
                        console.log('Request error: ${error.message}')
                    });
{ appName: 'IRI',
  appVersion: '1.8.1',
  jreAvailableProcessors: 16,
  jreFreeMemory: 1551948936,
  jreVersion: '1.8.0_191',
  jreMaxMemory: 20997734400,
  jreTotalMemory: 3582733719,
  latestMilestone:
   'QBUS9SXTQTJWRTWHQWTEPBZBFFXJANQGZQSDSICRZQAD9PTKGTFKV9NEPZCLXLRIPIMWJ9OCTCKZZ9999',
  latestMilestoneIndex: 1173627,
  latestSolidSubtangleMilestone:
   'QBUS9SXTQTJWRTWHQWTEPBZBFFXJANQGZQSDSICRZQAD9PTKGTFKV9NEPZCLXLRIPIMWJ9OCTCKZZ9999',
  latestSolidSubtangleMilestoneIndex: 1173627,
  milestoneStartIndex: 1157888,
  lastSnapshottedMilestoneIndex: 1173525,
  neighbors: 24,
  packetsQueueSize: 0,
  time: 1568362132124,
  tips: 7924,
  transactionsToRequest: 0,
  features: [ 'loadBalancer', 'snapshotPruning', 'RemotePOW' ],
  coordinatorAddress:
   'EQSAUZXULTTYZCLNJNTXQTQHOMOFZERHTCGTXOLTVAHKSA9OGAZDEKECURBRIXIJWNPFCQIOVFVVXJVD9',
  duration: 0 }

Node is probably synced!

Please note: When using node load balancers then this type of check is quite useless since other API calls of yours may be served by different node that may have not been checked. You should be aware of it and trust that the load balancer participates only with nodes that are in a good condition.

Generating IOTA seed and IOTA address

Since the IOTA network is permissionless type of network, anybody is able to use it and interact with it. No central authority is required at any stage. So anybody is able to generate own seed and respective private key/address anytime. It is highly recommended to NOT use online generators at all.

The Seed is the only key to the given addresses. Anyone who owns the Seed owns also all funds related to respective IOTA addresses (all of them).

Seed and addresses only consists of characters [A-Z] and number 9. Length is always 81 charaters. There are usually also used additional 9 characters in IOTA addresses (so total lenght is 90 then) which is a checksum. It provides a way how to prevent typos while manipulating with IOTA addresses.

Seed

A seed generation is a process that is based on random quessing of each character in the seed. Of course, you should always use cryptographically secure pseudo-random generator!




 


//based on https://gist.github.com/SteveFromTheOffice/c8448a09352337386f135a16bbb20d93
//modified by Petr Zizka
    
var GenerateSeed = function () {
    const length       = 81;                            // The length of the seed and int array.
    const chars        = "ABCDEFGHIJKLMNOPQRSTUVWXYZ9"; // The allowed characters in the seed.
    var result       = new Array(length);               // An empty array to store the seed characters.
    var randomValues = Buffer.alloc(length)
    
    // Generate random values and store them to array.
    crypto.randomFillSync(randomValues);
    
    var cursor = 0;                                     // A cursor is introduced to remove modulus bias.
    for (var i = 0; i < randomValues.length; i++) {     // Loop through each of the 81 random values.
        cursor += randomValues[i];                      // Add them to the cursor.
        result[i] = chars[cursor % chars.length];       // Assign a new character to the seed based on cursor mod 27.
    }
    return result.join('');                             // Merge the array into a single string and return it.
};

var NewSeed = GenerateSeed();
console.log(NewSeed);
console.log("Length: %s", NewSeed.length)
IPNYMDAUYFXLBAIICBSNODJZNYWKPCORLQVYRFIKTKTLGKUXLINB99EUZRKKPOKS9MGJBYTJTRNNRXPZD
Length: 81

Alternatively you can leverage also PyOTA library since it has own implementation of the pseudo-random Seed generator (Python 2.7+ compatible). Difference between both methods is very subtle. I just wanted to illustrate both ways: Fully independent and IOTA-library-dependant. Please note, IOTA Javascript library does not support a seed generation.



 


// No code snippet available for the selected language: javascript

Address

There is a deterministic function how to get an address from a seed. It is a basically an indexed collection of addresses starting with address at index 0, 1, 2, etc.

While generating addresses you should be also aware there are three different so called Security Levels (1, 2 and 3). Each security level generates totally independent pool of addresses. Addresses of Security Level 1 are totally different from addresses of Security Level 2, etc.

In a nutshell, Security Level indicates how difficult is to generate Private key from the Seed and also what is its size. The higher security level, the more secure (longer) is the given private key. Default Security Level is 2. Most of IOTA wallets uses Security Level=2 and users are usually not allowed to change it. So be carefull while designing your app.

As long as seed, address index and security level are the same, you always get the same address and corresponding private key:

$$PrivateKey/Address = fce(Seed, AddressIndex, SecurityLevel)$$

General example: $$PrivateKey/Address1 = fce(Seed, 1, 2)$$ $$PrivateKey/Address2 = fce(Seed, 2, 2)$$ $$PrivateKey/Address3 = fce(Seed, 3, 2)$$

Anatomy of IOTA address




 


var iotalib = require('@iota/core');

var NodeURL = "https://nodes.thetangle.org:443";
var MySeed = "WKQDUZTGFKSSLACUCHHLZRKZBHSDSCEBHKUPDLKFBQALEBKDMFRPUQGZRXAADPG9TSRTZGGBZOFRJCFMM";

var iota = iotalib.composeAPI({
    'provider': NodeURL
});

//Let's generate 3 addresses using default security level=2.
//It is a deterministic function - it always generates same addresses as long as the Seed, Security Level and Index are the same
//In case of iota.js library, it additionally checks generated addresses against the Tangle whether the address was spent from
//So the following snippet leverages also API calls against a node
//Please also note that generating addresses can take quite long
var promise = iota.getNewAddress(MySeed, { index: 0, total: 3, returnAll: true } )
                .then(address => {
                    console.log();
                    console.log(address); //returned addresses are printed out    
                })
                .catch(error => {
                    console.log("Error occured: %s", error);                
                });
`GetNewAddressOptions`: 3,true options are deprecated and will be removed in v.2.0.0. 

[ 'HRLKBQUZAEB9HIVWJEWVDYQ9G9VRQXQAXR9ZWGBFQJKRPOPJYHGAT9LBEIE9RWRMUFSNLCWYHQGYAECHD',
  'XEXIDJJTANADOUBPWTCSPPRYYRTITRAHDEOZAEXWDPCYKUPTFMKVQM9KCPPLOCESFRGRVSIYZHXQZNYKC',
  'KY9DLZCHET9ATLMADPXGDVDYMPHKRKQPJZ9MB9HEIMMFCRRTNJIJIHPKGZNKKDTFMYPZRRQYAQKVAHMYX' ]

Please note, some IOTA libraries may communicate with a IOTA network while generating addresses to double check whether the given address was used or not.

So it is sometimes better to use directly an address generator component. Outputs are equivalent in both cases but the latter one usually only generates pool of addresses locally:




 


var iotalib = require('@iota/core');

var MySeed = "WKQDUZTGFKSSLACUCHHLZRKZBHSDSCEBHKUPDLKFBQALEBKDMFRPUQGZRXAADPG9TSRTZGGBZOFRJCFMM";

//Please note, it is a sync function call
//Please also note that generating addresses can take quite long
//The given addresses are generated locally regardless the Tangle state
for (var i = 0; i < 3; i++ ) {
    var address = iotalib.generateAddress (MySeed, i, 2, false); //seed, index, security, checksum
    console.log(address);
}
HRLKBQUZAEB9HIVWJEWVDYQ9G9VRQXQAXR9ZWGBFQJKRPOPJYHGAT9LBEIE9RWRMUFSNLCWYHQGYAECHD
XEXIDJJTANADOUBPWTCSPPRYYRTITRAHDEOZAEXWDPCYKUPTFMKVQM9KCPPLOCESFRGRVSIYZHXQZNYKC
KY9DLZCHET9ATLMADPXGDVDYMPHKRKQPJZ9MB9HEIMMFCRRTNJIJIHPKGZNKKDTFMYPZRRQYAQKVAHMYX

Validating IOTA address

As mentioned earlier, IOTA address consists of 81 trytes ([A..Z9] characters) or 90 trytes including checksum. Checksum is useful when you want to make sure a valid address was given (no typos, etc.) and so it is a good practise to encourage user of your app using IOTA addresses including checksums = 90 trytes.

IOTA libraries are able to help you to deal with some basic patterns such as validating addresses, generating checksums, etc.




 


var iotalib = require('@iota/checksum'); //this package is also useful when validating an address

//some IOTA address
var Adr = "CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW";

console.log("Original input excl. checksum address:");
console.log(Adr);
console.log("Length: %s", Adr.length)

var AdrInclCheckSum = iotalib.addChecksum(Adr); //generate checksum for the given address
console.log("Input address including checksum:")
console.log(AdrInclCheckSum) // the last 9 trytes is the checksum
console.log("Length incl checksum: %s", AdrInclCheckSum.length)
Original input excl. checksum address:
CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW
Length: 81
Input address including checksum:
CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RWTHBIRSXTA
Length incl checksum: 90

You should always make sure your app is dealing with valid IOTA address. Please note, you should also make sure an address is of correct length and consists only of allowed characters. IOTA libraries may slightly differ in their approaches but differences are very subtle and both libraries provide enough functions to validate IOTA address.

General steps should be:

  • Checks length
  • Checks valid characters
  • Checks address against its checksum



 


var iotalib = require('@iota/checksum');
var iotaValidators = require('@iota/validators');

//address including checksum
var InputAddr = "CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RWTHBIRSXTA";

if (!iotaValidators.isAddress(InputAddr) || InputAddr.length!=90) {
    console.log("Not valid input address given.");    
} else {
    console.log("Input address incl checksum:");
    console.log(InputAddr);

    console.log("Is it valid addr based on checksum? %s", iotalib.isValidChecksum(InputAddr));
    console.log("Input address excl checksum:");
    console.log(iotalib.removeChecksum(InputAddr));
}
Input address incl checksum:
CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RWTHBIRSXTA
Is it valid addr based on checksum? true
Input address excl checksum:
CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW

IOTA 101: Transactions

A transaction is a smallest piece of data that can be broadcasted to the network. Each transaction consists of fixed 2673 trytes. Transactions (TXs) are basically of two types:

  • Value TXs: those transactions change ownership of IOTA tokens. Two possible states: spending TXs or receiving TXs
  • Non-Value TXs: those transactions include piece of data that can be broadcasted and read from the Tangle

Transactions are broadcasted in an envelope called Bundle. Bundle may consist of multiple TXs. Bundle is processed by the Tangle as a single entity; meaning only the whole bundle is confirmed by the network (and all TXs within) or nothing. You have to prepare a bundle even if you broadcast single transaction only. Bundle is also described as atomic since every bundle includes all information needed to process it antonomously.

Each transaction includes several fields, let's name the most important ones:

  • Value: Value can be positive number = receiving IOTA tokens, negative number = spending IOTA tokens or zero = broadcasting only piece of data
  • Address: It is IOTA address that is associated with the given transaction. Value indicates whether it is an address of a receiver or a sender
  • Tag: It is an user-defined tag that can be used for searching, etc.
  • SignatureMessageFragment: This is the most lenghty attribute (2187 trytes) and could include: transaction signature based on private key in case it is a spending TX or piece of data (message) in case it is non-value TX. Alternatively it can be also left blank (in case of non-value TXs)
  • TrunkTransaction / branchTransaction: Those two fields refer to some previous transactions in the Tangle (tips) that the given bundle is going to approve
  • Timestamp: TX timestamp. It is actually UNIX timestamp (UTC) Creating TX in 5 steps

A process of sending IOTA transaction can be summarized in 5 steps:

  • To create transation(s) with the given attributes: value, address and potentially also tag or message
  • To finalize a bundle that includes all transactions. Once the bundle is finalized no new transactions can be added. It basically means to generate a bundle hash
  • To search for two tips in the Tangle that you are going to validate. There is not a strict rule how to search them but it is generally recommended to leave it on a tip selection algorithm implemented on node's side
  • To do a Proof-of-Work for each transaction in the bundle. Result of POW is Nonce and Transaction hash stored with each transaction
  • Finally you need to broadcast the whole bundle to the network

A secret of a bundle

There is a secret to be shared. You have to understand one important thing. Bundle is a top level construct that link all related transactions under one entity however the bundle itself is not broadcasted in fact. You still broadcast "only" collection of individual transactions instead. All transactions are recognized to be part of a bundle by the IOTA protocol however any data peering is based on individual transactions (in trytes).

In other words, a bundle can be reconstructed anytime from a collection of transactions via fields bundle hash, current index and last index.

Non-value transactions

Let's start with non-value transactions (meta transactions) first since they are a bit easier for starters.

Please note, the chapter is divided in two separate sections. In the first section, examples are really close to bare metal (still not too close:) to illustrate the whole process and describing implementation details wherever possible. It is also useful when you want to experiment with different settings at each step (hey "non-existing tips", looking at you).

In the second section, IOTA-facing libraries are fully levearged since they are capable to hide all implementation details behind a curtain. Needless to say, if you are not interested in such details then you would probably like to start with the second section.

Creating and broadcasting transaction in 5 steps (you want to understand the process)

Create a transaction

First of all let's create transactions (offline; in memory) and set main fields.

BTW: Timestamp field is defined automatically by the IOTA-facing libraries in the process. It is important field while creating bundle hash.

In the examples within this chapter we are going to broadcast meta transactions (data only) to two different IOTA addresses.




 


var iotalib = require('@iota/core');
var Converter = require('@iota/converter')
var NodeURL = "https://nodes.thetangle.org:443";

var iota = iotalib.composeAPI({
    'provider': NodeURL
});

var MySeed = "HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA";
var TargetAddress1 = "CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9";
var TargetAddress2 = "CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW";

var NowIs = new Date() //get a actual date & time - just to have some meaningfull info

// preparing transactions
var pt = {
    'address': TargetAddress1, //81 trytes long address
    'value': 0,
    'message': Converter.asciiToTrytes('Here comes a first message. Now is ' + NowIs),
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var pt2 = {
    'address': TargetAddress2, //81 trytes long address
    'value': 0,
    'message': Converter.asciiToTrytes('Here comes a second message. Now is ' + NowIs),
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

console.log("Created transaction objects - mandatory fields only:")
console.log(pt)
console.log(pt2)
Created transaction objects - mandatory fields only:
{ address:
   'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
  value: 0,
  message:
   'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBWAVADBWAZAEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA',
  tag: 'HRIBEK999IOTA999TUTORIAL' }
{ address:
   'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW',
  value: 0,
  message:
   'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBWAVADBWAZAEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA',
  tag: 'HRIBEK999IOTA999TUTORIAL' }

Finalizing bundle

Once all individual transactions are created it is time to prepare and finalize the bundle. While preparing the bundle you need at least to specify list of prepared transaction(s). In case of non-value transactions it is quite straightforward process. However, it is a bit more complicated in case of value transactions - please see later.

Finalizing the bundle consists of several tasks under the hood:

  • Each transaction is indexed. Attributes current_index / last_index are set
  • Bundle hash is generated (Sponge function + normalization) and assigned to each transaction
  • signatureMessageFragment is bascially copy of message field in case of non-value transactions
  • PyOTA library also checks whether the data in message field is larger than transaction allows (2187 trytes). If this is the case then it takes care of it and split your data into several transactions

Please note, finalizing the bundle also means you are no longer able to add new transactions to the bundle post the finalization process.

Simply put, bundle hash is a cryptographic "fingerprint" of all transactions in the bundle. It uniquely represents them and so as long as transactions are the same (incl their particular order) the bundle hash is also the same.

$$BundleHash = fce(address, value, legacy tag, timestamp, current index, last index)$$

You may be wondering what is a difference between tag and legacy_tag. Tag includes actualy the tag that was defined during the transaction creation. Legacy_tag is also based on it however it is modified during the normalization process while bundle hashing to be sure that bundle hash is can be securely used while TX signing. That's why the bundle hash is sometimes refered as normalized bundle hash.

The bundle refers to the first transaction in collection as tail_transaction. You can perceive tail transaction as an official representative of the whole bundle while checking for confirmation status, etc. Remember, the bundle is an atomic entity and so whatever is done to bundle it applies to ALL transactions within.



 


var iotalib = require('@iota/core');
var Converter = require('@iota/converter')
var TransactionConverter = require('@iota/transaction-converter')

var NodeURL = "https://nodes.thetangle.org:443";

var iota = iotalib.composeAPI({
    'provider': NodeURL
});

var MySeed = "HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA";
var TargetAddress1 = "CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9";
var TargetAddress2 = "CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW";

var NowIs = new Date() //get a actual date & time - just to have some meaningfull info

// preparing transactions
var pt = {
    'address': TargetAddress1, //81 trytes long address
    'value': 0, //zero transaction - only data in the message field will be sent
    'message': Converter.asciiToTrytes('Here comes a first message. Now is ' + NowIs), //data needs to be encoded in trytes
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var pt2 = {
    'address': TargetAddress2, //81 trytes long address
    'value': 0, //zero transaction - only data in the message field will be sent
    'message': Converter.asciiToTrytes('Here comes a second message. Now is ' + NowIs), //data needs to be encoded in trytes
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var transfers = [pt,pt2]; // a list of transactions to be sent
var bundle;

var promise = iota
              .prepareTransfers(MySeed, transfers) //this call prepare a bundle including all transactions
              .then(trytes => { //trytes to be sent
                    console.log("Generated bundle hash: %s", TransactionConverter.asTransactionObject(trytes[0]).bundle);
                    console.log("List of all individual transactions in the bundle:");
                    for (var i in trytes){
                        console.log(TransactionConverter.asTransactionObject(trytes[i]))
                    }
                    bundle = trytes;
              })   
              .catch(err => {
                    console.log("Something went wrong: %s", err);    
              })
Generated bundle hash: YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9
List of all individual transactions in the bundle:
{ hash:
   'YAMUKFWJ9CBIKGRCIZFIHTEGBJGFAQRZEJKYUACFJDXDDRVCRKRKCIDTBGRAWPMWJDQHWZVFHFXPJ9KOE',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW',
  value: 0,
  obsoleteTag: 'HRIBEK999IOTA999TUTORIAL999',
  timestamp: 1568364528,
  currentIndex: 1,
  lastIndex: 1,
  bundle:
   'YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9',
  trunkTransaction:
   '999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  branchTransaction:
   '999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 0,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 0,
  nonce: '999999999999999999999999999' }
{ hash:
   'IT9CRSDOVGOSWLSKGVNHLIFRFFKSPKQRFTVG9S9UJM9JLNEWOFSJIKGUJLINVRJCQMBPACZJGWAGRFFYT',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
  value: 0,
  obsoleteTag: 'RTIBEK999IOTA999TUTORIAL999',
  timestamp: 1568364528,
  currentIndex: 0,
  lastIndex: 1,
  bundle:
   'YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9',
  trunkTransaction:
   '999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  branchTransaction:
   '999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 0,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 0,
  nonce: '999999999999999999999999999' }

At this stage you can also see how does our finalized bundle look encoded in Trytes so far. It is also a proof that there is no bundle to be broadcasted itself, only list of transations matter.



 


console.log(bundle);
[ 'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999CREDHAD99A99999999A99999999YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999HRIBEK999IOTA999TUTORIAL999999999999999999999999999999999999999999999999999999999',
  'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999RTIBEK999IOTA999TUTORIAL999CREDHAD99999999999A99999999YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999HRIBEK999IOTA999TUTORIAL999999999999999999999999999999999999999999999999999999999' ]

Selecting two tips

As mentioned earlier you need to find two tips that you are going to validate together with your bundle for a sake of network participation. This will be outsourced to an IOTA node and so it will be for the first time we interact with the network (via API call get_transactions_to_approve).

There is a depth parameter needed. It instructs a node how many milestones it should go in the past while confirming tips. The higher value the better to network however more resources needed. Depth=3 is considered to be well-balanced compromise. Higher value than depth=10 will probably throw an exception depending on node's configuration.

It should return two selected tips as branch and trunk transactions. Those transactions will be used while broadcasting the bundle.



 


var gta;
var depth = 3;

var promise = iota.getTransactionsToApprove(depth)
                     .then(transactionsToApprove => {
                         console.log(transactionsToApprove);
                         gta = transactionsToApprove;
                     })
                     .catch(err => {
                         console.log("Something went wrong: %s", err);
                     })
{ trunkTransaction:
   'UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999',
  branchTransaction:
   'HZKIFVHGAMIAVICOYZWFMYKINKPH9HTDAPQNHPEEIALRUPQSXIUWRZQTYMNKQVJMYZBTXQCQF9MLZ9999' }

Performin' POW

Proof of work is a relatively simple cryptograhic puzzle to be solved. It represents energy-based costs of your transaction. It also helps to minimize risks of some attack vectors to the network.

This task can be also outsourced to IOTA nodes. In order to perform it you need selected tips, finalized bundle (in trytes) and also Minimum Weight Magnitude parameter. This parameter defines how diffciult the given cryptographic puzzle should be to be accepted by the network. As of now, you should set at least min_weight_magnitude=14 in case of mainnet.

Please note, POW is performed per each transaction and so it can take some time. That's why it is not recommended to have more than 30 transactions in a bundle.

Once succesfully performed, the modified bundle (list of all transactions in trytes) is returned in order to be broadcasted.

Specifically:

  • Fields nonce and transaction hash are calculated per each transaction
  • Trunk and Branch tips are correctly mapped among transactions within the bundle. Check that trunk of the first TX refers to the second TX, etc.


 


console.log("Performing POW...Wait please...");
var minWeightMagnitude = 14;

var att;
var promise = iota.attachToTangle(
                       gta['trunkTransaction'], //trunk transaction gotten from previous step
                       gta['branchTransaction'], //branch transaction gotten from previous step
                       minWeightMagnitude,
                       bundle // finalized bundle in trytes from previous steps
                   ) 
                   .then(attachedTrytes => {
                       console.log(attachedTrytes);
                       att=attachedTrytes;
                   })
                   .catch(err => {
                        console.log("Something went wrong: %s", err);
                   });
Performing POW...Wait please...
[ 'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999RTIBEK999IOTA999TUTORIAL999CREDHAD99999999999A99999999YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9XOZZNEFNL9ESUDQGUPRBESDBBVRWAEHEWLPIKXGEVGSZVEWASTTEDRNRGRKKUEDHQQSOBOGCKMGZA9999UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999HRIBEK999IOTA999TUTORIAL999HNAIBFYOF999999999MMMMMMMMMMPMZBUZOZRT9HUVNMNRGIBTJQCQ',
  'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999CREDHAD99A99999999A99999999YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999HZKIFVHGAMIAVICOYZWFMYKINKPH9HTDAPQNHPEEIALRUPQSXIUWRZQTYMNKQVJMYZBTXQCQF9MLZ9999HRIBEK999IOTA999TUTORIAL999VY9IBFYOF999999999MMMMMMMMMYQEGDUMCTKAQVSNQCGGUPL9MT9I' ]

You can eventually also preview what specific fields have been modified using transaction objects. The bundle is ready to be broadcasted and so it also show you all fields/values that will be stored in the network.



 


//show what has been changed: hash transaction + nonce (POW) + trunk + branch
console.log("Final bundle including POW and branch/trunk transactions:");
for (var i in att) {
    console.log(TransactionConverter.asTransactionObject(att[i])) //Let's print attributes of each TX
}
Final bundle including POW and branch/trunk transactions:
{ hash:
   'DZHB9SKKUOHB9RUODHYTVIHDGNOG9UGKBBOOIXFXFBGFYIZGGNIZGIHCZVFRQPNHETHHWL9WNSFEZ9999',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
  value: 0,
  obsoleteTag: 'RTIBEK999IOTA999TUTORIAL999',
  timestamp: 1568364528,
  currentIndex: 0,
  lastIndex: 1,
  bundle:
   'YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9',
  trunkTransaction:
   'XOZZNEFNL9ESUDQGUPRBESDBBVRWAEHEWLPIKXGEVGSZVEWASTTEDRNRGRKKUEDHQQSOBOGCKMGZA9999',
  branchTransaction:
   'UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 1568365473329,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 3812798742493,
  nonce: 'MPMZBUZOZRT9HUVNMNRGIBTJQCQ' }
{ hash:
   'XOZZNEFNL9ESUDQGUPRBESDBBVRWAEHEWLPIKXGEVGSZVEWASTTEDRNRGRKKUEDHQQSOBOGCKMGZA9999',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW',
  value: 0,
  obsoleteTag: 'HRIBEK999IOTA999TUTORIAL999',
  timestamp: 1568364528,
  currentIndex: 1,
  lastIndex: 1,
  bundle:
   'YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9',
  trunkTransaction:
   'UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999',
  branchTransaction:
   'HZKIFVHGAMIAVICOYZWFMYKINKPH9HTDAPQNHPEEIALRUPQSXIUWRZQTYMNKQVJMYZBTXQCQF9MLZ9999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 1568365472884,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 3812798742493,
  nonce: 'YQEGDUMCTKAQVSNQCGGUPL9MT9I' }

Broadcasting

Now it is time to broadcast the given bundle to the network. List of all transations in trytes returned from the previous step is only thing needed.

Once succesfully broadcasted it returns the same input bundle as a confirmation.



 


//broadcast and store
console.log("Broadcasting transaction...");
var promise = iota.storeAndBroadcast(att)
                        .then(r => console.log(r)
                        )
                        .catch(err => {
                            console.log("Something went wrong: %s", err);
                        });
Broadcasting transaction...
[ 'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999RTIBEK999IOTA999TUTORIAL999CREDHAD99999999999A99999999YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9XOZZNEFNL9ESUDQGUPRBESDBBVRWAEHEWLPIKXGEVGSZVEWASTTEDRNRGRKKUEDHQQSOBOGCKMGZA9999UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999HRIBEK999IOTA999TUTORIAL999HNAIBFYOF999999999MMMMMMMMMMPMZBUZOZRT9HUVNMNRGIBTJQCQ',
  'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAUADBYABBDBYABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999CREDHAD99A99999999A99999999YEKXZBCXRJCAZZJAWLBCCWVYFXFTXHPSLLGHOAGPADDEADLWVGWRWN9FVIFKVAPIWY9LPZZBYXFUEECB9UHFMVKVBPBDGQFVVEXMYBAVW9WZCGGORSNOOPDTBIWVBL9GVPPIVWDQYC9PZTDIVFBHVGIZZDJVV99999HZKIFVHGAMIAVICOYZWFMYKINKPH9HTDAPQNHPEEIALRUPQSXIUWRZQTYMNKQVJMYZBTXQCQF9MLZ9999HRIBEK999IOTA999TUTORIAL999VY9IBFYOF999999999MMMMMMMMMYQEGDUMCTKAQVSNQCGGUPL9MT9I' ]

Creating and broadcasting transaction in a single call (you want send a transaction quickly)

As mentioned above, IOTA-facing libraries are able to encapsulate all implementation details and so you do not need to care of them anymore. It is usually based on extended API calls that were proposed to be included by IOTA Foundation to make developer's life easier (https://github.com/iotaledger/wiki/blob/master/api-proposal.md).

Even if using extended API calls you can still slightly control a level of your involvement in the whole process. From almost no involvement and fully automatic (send_transfer()) to semi-manual approach discussed above.

Different API calls

There are some specifics you should consider while designing your app:

  • You are loosing a bit of control of individual steps of the process and you have to rely of some predefined parameters (tip selection algo, etc.)
  • If there is an exception raised during the process then you need to go back at beginning and restart the whole process again

So it is basically your call to decide what works best for you.

Now back to our exercise. Here comes a code that basically broadcasts transactions in a single call (send_transfer()) and you do not have to care of any implemenation details that are hidden under the hood.




 


var iotalib = require('@iota/core');
var Converter = require('@iota/converter')
var TransactionConverter = require('@iota/transaction-converter')

var NodeURL = "https://nodes.thetangle.org:443";

var iota = iotalib.composeAPI({
    'provider': NodeURL
});

var MySeed = "HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA";
var TargetAddress1 = "CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9";
var TargetAddress2 = "CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW";

var NowIs = new Date() //get a actual date & time - just to have some meaningfull info

// preparing transactions
var pt = {
    'address': TargetAddress1, //81 trytes long address
    'value': 0, //zero transaction - only data in the message field will be sent
    'message': Converter.asciiToTrytes('Here comes a first message. Now is ' + NowIs), //data needs to be encoded in trytes
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var pt2 = {
    'address': TargetAddress2, //81 trytes long address
    'value': 0, //zero transaction - only data in the message field will be sent
    'message': Converter.asciiToTrytes('Here comes a second message. Now is ' + NowIs), //data needs to be encoded in trytes
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var transfers = [pt,pt2]; // a list of transactions to be sent
var depth = 3;
var minWeightMagnitude = 14;

console.log("Preparing/Broadcasting... Wait please...");

var promise = iota.prepareTransfers(MySeed, transfers) //prepare a bundle in trytes
                            .then(trytes => {
                                return iota.sendTrytes(trytes, depth, minWeightMagnitude); //perform GTTA + POW + BROADCASTING
                            })
                            .then(bundle => {
                                console.log("Transactions sent!");
                                console.log("\nList of all transactions in the bundle:");
                                bundle.map(tx => console.log(tx))
                            })
                             .catch(err => {
                                console.log("Something went wrong: %s", err);
                            })
Preparing/Broadcasting... Wait please...
Transactions sent!

List of all transactions in the bundle:
{ hash:
   'WZXSIHOLGQMXOEIBDMODHFNESEVQVKGEYEWNWN9HFYSGENRTGCOIIOWEEVZQLQPSGTWBUPSDHSHLA9999',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAVADBXAABDBZABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
  value: 0,
  obsoleteTag: 'OYIBEK999IOTA999TUTORIAL999',
  timestamp: 1568367478,
  currentIndex: 0,
  lastIndex: 1,
  bundle:
   'GIPZYTSDGWQ9KKHYQVULDKFE9FDPAEUKABU9CZCNTHKAISCWRICOJKNNJGTOY9DIFESDDCNGKKPQJQLLZ',
  trunkTransaction:
   'UQWLQHZZNYDDEVIHAPPEQNXOEEXHDZMHRVXQKDBZKXEDOWLFAKZYFHLIMYEWXF9ZHVUTCEVZXEFW99999',
  branchTransaction:
   'XOHHXLT9WMAROEVLUBDRIZMY9PNQP9HEZQAJAZXTIEJZWYZNWOUWKCVFFTLWAMHT9ALMDOBHVWY999999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 1568367481436,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 3812798742493,
  nonce: 'SXXQGKNGUPLZRGSRTQXU99O9ORZ' }
{ hash:
   'UQWLQHZZNYDDEVIHAPPEQNXOEEXHDZMHRVXQKDBZKXEDOWLFAKZYFHLIMYEWXF9ZHVUTCEVZXEFW99999',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAVADBXAABDBZABBEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW',
  value: 0,
  obsoleteTag: 'HRIBEK999IOTA999TUTORIAL999',
  timestamp: 1568367478,
  currentIndex: 1,
  lastIndex: 1,
  bundle:
   'GIPZYTSDGWQ9KKHYQVULDKFE9FDPAEUKABU9CZCNTHKAISCWRICOJKNNJGTOY9DIFESDDCNGKKPQJQLLZ',
  trunkTransaction:
   'XOHHXLT9WMAROEVLUBDRIZMY9PNQP9HEZQAJAZXTIEJZWYZNWOUWKCVFFTLWAMHT9ALMDOBHVWY999999',
  branchTransaction:
   'HVGTFLPMQHNURZNWZETWELDAJVSBZHEHDNWVWHZAKMYDNGTAEAPWKGQVIBEKKLZEGIBGOQWMNASE99999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 1568367481152,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 3812798742493,
  nonce: 'ZJHPXAJI9TMSKXPQN9POQZYGNMQ' }

And to complete the picture here is addtional code that uses prepare_transfer() and send_trytes() combo.




 


var iotalib = require('@iota/core');

var NodeURL = "https://nodes.thetangle.org:443";

var iota = iotalib.composeAPI({
    'provider': NodeURL
});

var MySeed = "HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA";
var TargetAddress1 = "CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9";
var TargetAddress2 = "CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW";

var NowIs = new Date() //get a actual date & time - just to have some meaningfull info

// preparing transactions
var pt = {
    'address': TargetAddress1, //81 trytes long address
    'value': 0, //zero transaction - only data in the message field will be sent
    'message': Converter.asciiToTrytes('Here comes a first message. Now is ' + NowIs), //data needs to be encoded in trytes
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var pt2 = {
    'address': TargetAddress2, //81 trytes long address
    'value': 0, //zero transaction - only data in the message field will be sent
    'message': Converter.asciiToTrytes('Here comes a second message. Now is ' + NowIs), //data needs to be encoded in trytes
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
}

var transfers = [pt,pt2]; // a list of transactions to be sent
var depth = 3;
var minWeightMagnitude = 14;

console.log("Preparing/Broadcasting... Wait please...");

var promise = iota.prepareTransfers(MySeed, transfers) //prepare a bundle in trytes
                            .then(trytes => {                                
                                console.log("Almost prepared bundle - tips and POW are still missing");
                                console.log(trytes);
                                console.log("Searching for tips and performing POW... Wait please...");
                                return iota.sendTrytes(trytes, depth, minWeightMagnitude); //perform GTTA + POW + BROADCASTING
                            })
                            .then(bundle => {
                                console.log("Bundle was broadcasted.");
                                console.log("Final transactions were returned - including nonce (POW)");
                                bundle.map(tx => console.log(tx))
                            })
                            .catch(err => {
                                console.log("Something went wrong: %s", err);
                            })
Preparing/Broadcasting... Wait please...
Almost prepared bundle - tips and POW are still missing
[ 'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAVADBYAABDBZA9BEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999NOJDHAD99A99999999A99999999XLIGKSOQJNFIIXBJDLOSFZKFXPTHKCWIYKWDSVZKEKQJPBHTQQVRCFBYYLDGRAEKNJASAINIZWPPTJGLC999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999HRIBEK999IOTA999TUTORIAL999999999999999999999999999999999999999999999999999999999',
  'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAVADBYAABDBZA9BEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999IVIBEK999IOTA999TUTORIAL999NOJDHAD99999999999A99999999XLIGKSOQJNFIIXBJDLOSFZKFXPTHKCWIYKWDSVZKEKQJPBHTQQVRCFBYYLDGRAEKNJASAINIZWPPTJGLC999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999HRIBEK999IOTA999TUTORIAL999999999999999999999999999999999999999999999999999999999' ]
Searching for tips and performing POW... Wait please...
Bundle was broadcasted.
Final transactions were returned - including nonce (POW)
{ hash:
   'LFMCNVUBXBRAT9TWSYHSMMTPLMTUPQSKZAGDXYGCUGZYNMWAWYOQKCRELBYPJJMSR9DTXQTARDEYZ9999',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAVADBYAABDBZA9BEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
  value: 0,
  obsoleteTag: 'IVIBEK999IOTA999TUTORIAL999',
  timestamp: 1568368076,
  currentIndex: 0,
  lastIndex: 1,
  bundle:
   'XLIGKSOQJNFIIXBJDLOSFZKFXPTHKCWIYKWDSVZKEKQJPBHTQQVRCFBYYLDGRAEKNJASAINIZWPPTJGLC',
  trunkTransaction:
   'UR9QHJXQJHVUK9TQHUZIWFFXGPYICSEQDN9RGHMORTJKRWXDRVAQQTPGFTXVFGVRKRMAX9KZNLKFZ9999',
  branchTransaction:
   'EPVYMGGEQDFAG9HYJXUFHHPPVRCRSXHTLHIULPVZXBRDOOZABXALMPZSLWJJURDAVLPCZQQCOHPOZ9999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 1568368078476,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 3812798742493,
  nonce: 'ZEVHDNDFMLKCPCPIKUGDAYJBZNP' }
{ hash:
   'UR9QHJXQJHVUK9TQHUZIWFFXGPYICSEQDN9RGHMORTJKRWXDRVAQQTPGFTXVFGVRKRMAX9KZNLKFZ9999',
  signatureMessageFragment:
   'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAPBFDXCEABCTCDDEAVAXAEAWAUAVACBEAVAVADBYAABDBZA9BEAQBWBCCPAUAWAUAUAEAMAMBTCBDHDFDPC9DEAOBIDFDCDDDTCPCBDEABCIDADADTCFDEACCXCADTCNA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  address:
   'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW',
  value: 0,
  obsoleteTag: 'HRIBEK999IOTA999TUTORIAL999',
  timestamp: 1568368076,
  currentIndex: 1,
  lastIndex: 1,
  bundle:
   'XLIGKSOQJNFIIXBJDLOSFZKFXPTHKCWIYKWDSVZKEKQJPBHTQQVRCFBYYLDGRAEKNJASAINIZWPPTJGLC',
  trunkTransaction:
   'EPVYMGGEQDFAG9HYJXUFHHPPVRCRSXHTLHIULPVZXBRDOOZABXALMPZSLWJJURDAVLPCZQQCOHPOZ9999',
  branchTransaction:
   'EPVYMGGEQDFAG9HYJXUFHHPPVRCRSXHTLHIULPVZXBRDOOZABXALMPZSLWJJURDAVLPCZQQCOHPOZ9999',
  tag: 'HRIBEK999IOTA999TUTORIAL999',
  attachmentTimestamp: 1568368078376,
  attachmentTimestampLowerBound: 0,
  attachmentTimestampUpperBound: 3812798742493,
  nonce: 'HVANLGSKZQEZVYUSKVGISFOSRMH' }

Value transactions

Meta transactions were discussed in the previous chapter. This chapter describes value transactions which are transactions that transfer IOTA tokens between IOTA addresses. Tokens are fully premined and so number of tokens in the network is given. So transfering tokens means changing their ownership only.

While preparing a bundle with value transations it is important to distinguish three different transaction types within a bundle:

  • Input TXs: those are transactions that deducting (spending) value from address(es). Those transactions have negative value field. They usually refer to senders
  • Output TXs: those are transactions that adding (receiving) value to address(es). Those transactions have positive value field. They usually refer to recipients
  • Unspent/change TX: technically speaking it is also output transaction that adding value however it usually refers to senders and so it is better to consider it separated from other output TXs

All three types have to be part of each bundle otherwise you are under a risk of loosing IOTA tokens. In other words, each bundle should consists of at least one input TX, one output TX and one unspent TX. Total balance of the bundle should be zero since the number of tokens is the same, just token owners have been "changed".

Example:

  • Alice owns 100 tokens (input = 100)
  • Alice is going to send 20 tokens to John from the address that includes 100 tokens of her (Alice -100)
  • John is going to receive 20 tokens from Alice (output = 20; John +20)
  • Alice still owns 80 tokens after the transaction (unspent = 80; Alice +80)

Alice's bundle should look like: \begin{align} Input - Output & = \ Unspent \\ Input - Output - Unspent & = \ 0 \\ 100 - 20 - 80 & = \ 0 \end{align}

Why Alice has to "put" all 100 tokens in the bundle while she is going to actually spend only 20 tokens? The reason is that IOTA protocol uses one-time signature scheme (OTS) for transaction signing while spending tokens. And with every signing process a half of a private key of the given address is revealed. That's why no one should ever spend tokens from the same address again. Never reuse address

Spending tokens

Please note: for a purpose of this chapter we are going to switch to a test network called devnet. It is the testing Tangle. Tokens are not real and everything you do have no implication to the mainnet at all. However it is stil powered by standard node software and so technicaly speaking it behaves like the mainnet. If you need more info regarding devnet please refer to the official documentation.

Everything what has been mentioned in the previous chapter focused on non-value transations is still valid also in case of value transactions however there are several additional tasks you should take care of. Needless to say, IOTA libraries (PyOTA and others) are ready to help you and are able to hide all implementation details in case you would like to. In this case I would encourage everyone to prefer a functionality that is offered by extended API calls, such as send_transfer. Experimenting with value transactions and trying to tweak them does not seem to be a good idea (on the mainnet).

Anyway, even in this chapter I am going to describe main tasks that are performed under the hood to be able to understand the whole concept.

Let's have the following setup:



 


// Sender's side ***********
// Sender's seed
var SeedSender = "HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA"

// Receiver's side ***********
// Recipient's seed - this is actually not needed for the exercise - it is just to have corresponding seed somewhere written
var SeedReceiver = "NVPDKGLESTYNJEH9DTWD9DUBYWHZJDDWTBLKWEYLVZKBKKAZTIZ9CFFJMHPEKIFUWQTXRGAVRXAQZCPHL"

// This is our target address to which we are going to send tokens to(BTW: it is a first generated address based on recipient's seed)
var AddressReceiver = "BMFSMZMNBGKHAWPIZIOMJGRBXZETVSAYDSTDQCHLYTBWZMIXLNXF9XHLTMOCATFVFOMBQF9IOQGPEBPDC"

// DevNet node - Devnet Tangle that is used only for testing / dev purposes - no real tokens here: (
var DevnetNode = "https://nodes.devnet.iota.org:443"

console.log("Variables were initialized.")
Variables were initialized.

You may be wondering why only the seed for sender's side is defined. As mentioned above the reason is that the address with tokens is changing with every spending exercise and so you have to find address with tokens that left in a bucket programatically (if any tokens left:).

Let's find out. For this purpose you can use extended API call get_account_data which returns a list of used addresses and total sum of all tokens available accross all of them:



 


var iotalib = require('@iota/core');

var iota = iotalib.composeAPI({
    'provider': DevnetNode
});

console.log("Checking for total balance.")
console.log("Please note, this may take some time if many addresses has been used already...");
var usedadr;

// now we can find out whether there are any tokens left
var promise = iota.getAccountData(//Get the total available balance for the given seed and all related used addresses
                    SeedSender, // Sender's seed - library will do all hard work based on the seed
                    {
                        "start": 29, // Library will start from the address at index 0. please note I increased it to start from index 29 to save time
                        "security": 2 // Default security level
                    })
                    .then(accountData => {
                        usedadr = accountData;
                        console.log(accountData);
                        if (accountData["balance"]) {
                            console.log("\nYes, there are some tokens available! hurray");
                            // there are quite usefull info returned - all addresses used plus first unused address. See latestAddress
                        }
                    })
                    .catch(err => {
                        console.log("Something went wrong: %s", err);
                    })
Checking for total balance.
Please note, this may take some time if many addresses has been used already...
`AccountData.transfers` field is deprecated, and `AccountData.transactions` field should be used instead.
Fetching of full bundles should be done lazily.
{ latestAddress:
   'NDQMBGBQIBAQURVAQEUVONVMECKXSNYZZNKMMMOHOLPEYHJHDBTFWOZMAPFUAHLHIQUAV9GVZPEMBKL9Z',
  transfers:
   [ [ [Object], [Object], [Object], [Object] ],
     [ [Object], [Object], [Object], [Object] ],
     [ [Object], [Object], [Object], [Object] ],
     [ [Object], [Object], [Object], [Object] ] ],
  transactions:
   [ 'YDTTBOSKIURVYNSCKQQTTKREXMDEPHUXXCUHAERTZUCCPCPIWR9TJIWCFZLKLNOKWVLODQCBFEPXAH999',
     'YYWNCRKDU9FWDHLZKGVJSRTFRBHGEDDBZ9QOEZOGGIDWQKOLG9XHRBGAXFSBJRPINEKSRMBLXMIEFT999',
     'CXIDFISEYSKVESHDDKGAYSOEKS9QTXOYYQ9TRKHH9QJ9WEILNYJVLAOWKMMTBRWVNBTQVBABQLWL99999',
     '99LUPKNVOKORUMEPDJEWRYLPJOTUMMRO9M9MXTWURUYQKJUOVFBAP9OMAEDUYUAJWTRLYIXSMS99A9999' ],
  inputs:
   [ { address:
        'VZITIWJF99TH9REMKLYZUWELFLEW9FVYXPZPVMVFZVIQHAOEMEDABUUZGFROKKIVMT999PWGGLICKJOAD',
       keyIndex: 31,
       security: 2,
       balance: 1999 } ],
  addresses:
   [ 'SUYDJCMS9XMBKOPIPOXKGXMFVYXQ9YRNZLDYLRFARZFOFJDDH9JZLCQVO9GBRQKBCXHSPHOAPPWFFLMBW',
     'NHVGPRAXYTDZJLMLSUMZGVBBMICDQGGAAZ9UJGLIPPOTUEZJXABCSDFUTRTBFOLDOQZIBS9QTRXAB9IUX',
     'VZITIWJF99TH9REMKLYZUWELFLEW9FVYXPZPVMVFZVIQHAOEMEDABUUZGFROKKIVMT999PWGGLICKJOAD',
     'XPV9CTUC9JEKATOVABXXLICTDAQEOHR9HXXFLHKN9OZFMFAZKKUDZMREOBZJONZAHXCGNN9FHGNFBAKMC',
     'NDQMBGBQIBAQURVAQEUVONVMECKXSNYZZNKMMMOHOLPEYHJHDBTFWOZMAPFUAHLHIQUAV9GVZPEMBKL9Z' ],
  balance: 1999 }

Yes, there are some tokens available! hurray

Please note: API call get_account_data consider only confirmed transactions while calculating total balance. So it is better to wait until all transactions are cleared before you send additional tokens.

Are there any tokens left (value in balance field)? If yes then continue - no action is needed. If not, then you need to obtain some. It can be done visiting IOTA faucet and entering an unused address of the given seed:



 


// Now let's find a first unused address that can be used as a destination address for unspent/new tokens
// This step is not required in case of NodeJS library, please see result above

console.log("Please note, this may take some time if many addresses have been used already...");

//please note the index below starts from 29 to save some time. If you woudl like to start from beginning it should be 0
var promise = iota.getNewAddress(SeedSender, { index: 29, total: null} ) //If null is specified, then it identifies a first non-used address
                .then(address => {
                    console.log("This is the first unused address that can be used for new tokens or unspent tokens:");
                    console.log(address); //returned addresses are printed out    
                })
                .catch(error => {
                    console.log("Something went wrong: %s", error);
                });
Please note, this may take some time if many addresses have been used already...
`GetNewAddressOptions`:  options are deprecated and will be removed in v.2.0.0. 

This is the first unused address that can be used for new tokens or unspent tokens:
NDQMBGBQIBAQURVAQEUVONVMECKXSNYZZNKMMMOHOLPEYHJHDBTFWOZMAPFUAHLHIQUAV9GVZPEMBKL9Z

What have just happened behind the curtain?

  • The PyOTA library started to generating addresses from the index=0 of the given seed and at the same time it also checked whether there are any transactions registered for each address
  • If yes, then in case we are after balances, the balance is saved. In case we are after new unused address then the given address is skipped. First address for which there are not registered transactions is returned
  • That's why it may be quite time consuming. And now you also understand all issues regarding zero balances after the snapshot

You may be also wondering how to get individual balances for each used address? This can be checked using the core API call get_balances which provides you with a balance for any address you specify. Let's query for used addresses from the previous exercise:



 


// only if there is any used addresses from the previous steps

if (usedadr['addresses'].length > 0){
    // it returns the balances as a list in the same order as the addresses were provided as input.       
    var promise = iota.getBalances(
                       usedadr['addresses'], // list of addresses
                       100, // Confirmation threshold; official docs recommend to set it to 100
                      )
                      .then(({ balances }) => {
                            console.log("Some positive balance identified. Individual confirmed balances per used addresses:");
                            console.log(balances);
                      })
                      .catch(err => {
                            console.log("Something went wrong: %s", err);
                      })           
}          
else
{
    console.log("\nNo positive balance identified.");
}
Some positive balance identified. Individual confirmed balances per used addresses:
[ 0, 0, 1999, 0, 0 ]

It returns balances and you can see how the remaining tokens move to new unused address every time you spend some tokens. Needless to say, used addresses with zero balances should not be used anymore since tokens were already spent from them. So let's spend some tokens:



 


var tx1 = {
    'address': AddressReceiver, //81 trytes long address
    'value': 10,    
    'tag': 'HRIBEK999IOTA999TUTORIAL' //Up to 27 trytes
};

// Sending the iotas
console.log("\nSending iotas... Please wait...");

var promise = iota.prepareTransfers(SeedSender, [tx1]) //let's prepare a bundle first. If you do not add any other options then everything is taken care by library
                        .then(trytes => {
                            console.log("Bundle was created. Sending it...")
                            return iota.sendTrytes(trytes,
                                                   3, //trytes
                                                   9) // MWM; in case of Devnet it is 9
                        })
                        .then(response => {
                            console.log("It has been sent. Now let's see all transaction(s) that were sent:");
                            console.log(response);
                        })
                        .catch(err=>{
                            console.log("Something went wrong: %s", err);
                        })
Sending iotas... Please wait...
Bundle was created. Sending it...
It has been sent. Now let's see all transaction(s) that were sent:
[ { hash:
     'SGJWWHQZJKGAWY9PDM9ASRNIRVIRZQZHYZJCCVZYMTRAKNCIJFJJEUSC9OQHOCMIZLFCPSYREXUPIB999',
    signatureMessageFragment:
     '999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
    address:
     'BMFSMZMNBGKHAWPIZIOMJGRBXZETVSAYDSTDQCHLYTBWZMIXLNXF9XHLTMOCATFVFOMBQF9IOQGPEBPDC',
    value: 10,
    obsoleteTag: 'ZSIBEK999IOTA999TUTORIAL999',
    timestamp: 1568375569,
    currentIndex: 0,
    lastIndex: 3,
    bundle:
     'SBOLDCVCHALOYRKBCUJLQXTFAVVPVRBYJUOZTYWDIJIDUOVK9HKTNLGKSJABIICUZDBQRABWLTSWSOCBA',
    trunkTransaction:
     'CXUFSCMYZCLKUIFOQAGIXQKTKBDKFWSEHTSHOSM9MQLPJBACKGKEIISSTAXJRZRO9FXAHTVKHUDXIX999',
    branchTransaction:
     'B9SHRBPVQZYSYJCBMAENTHFCSBMWTFIQ9O9VRNYNCHTMAWYGBDL9TUZTPQFVTBBKWXPWBXNNSNL9HE999',
    tag: 'HRIBEK999IOTA999TUTORIAL999',
    attachmentTimestamp: 1568375730058,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 3812798742493,
    nonce: 'ZPT9PHZTHBXAOMZSDLUDKUPMEJO' },
  { hash:
     'CXUFSCMYZCLKUIFOQAGIXQKTKBDKFWSEHTSHOSM9MQLPJBACKGKEIISSTAXJRZRO9FXAHTVKHUDXIX999',
    signatureMessageFragment:
     'SXOQFDSJXYZMEVAGKYAGDWIZ9GMFAELZXXCLIRZTZ9YOTDBE9LLY9PEUVTNOJSZDA9NICI9MPCWUCMMSDFGTZENVPGQQZACMBHZXRNTTGXYKCTXK9RHDGGJIGMLMYHQIOHYKMZONFGQI9SHNJJZNN9DRUC9UI9TUKDDSNKMWIEVJMHKGS9QIQOEBZ9X9GCLSWDD9LMHINADZWJFK9XIWMBKXS9CLZMLDLKKXPSEBMHOHFVYPWEZZNWJARZMOAWOJE9DPBSUXIKW9AGVFVEXHULNKHCZKSHNQUQJKXOYCA9RADZPKRCAAUGTEFCQKLYFXAMVXLWLYEBHGOF9FLCSDFOGGAGRQCWVWGNMQEIWFBCIWWF9ZDHDQWSBSSFVWV9XHWKSTZEZFHMOKT9MGHBTYYMHECJNOZPWWOZONWXIOPMRWLKVJU9IZ9MMFC9PLBJUPHCYBKYAOZPXOIHNQSQMRYEOJQCCQTDCYKPXBJ9IDAOMLPUUFZOEUCXLXZGPUFSC9TIPMFPLAWTT9MMONRZRWDZIZRKTJXHULZANYWORHLUINMKRSUACGT9XJUORNWKTXHNLEOITFWUGDQOICZZMCQWJPSNBRIDI99ALFHYIUPVZJKQNTTLCSKDZWPIGUZNPTKKNCCKCBN9CJNTICDYRNYJBAPEDCCWCKKRSZJZPKJQQLLEGWVTBVUNNOIYWWPA9ZGGHBQOWZHKAIPC9Z9UPHLFJPZBUV9HPNVGYZVNOACJBSBUERPNPUEWZEJVUIFCCATTKKLUUPJ9HHORWOOTNFMSYVYQXENHNOZCJBZZQHPDXLBGGPCZIKTCPSNUXVZ9WMRCBBUQXOTMSMSRQHUKONMPWMSOMORXWNKQL9IBRYQHQNLJCRPZYDOGWCHZCRHSOYYYAAOZZORBG9OIYFZXUFVVZWETRCDVSJQPYBWCMOA9HLDHIMGMHYS9FEJXQGPS9PNADWZBBFWICBW9RSJAAPTZPFHZOOEGLBMUHGJVSNISBVCYYHBJRQBXH9KYCCUB9VGBAYXUTGUTXGUZWWNFWWEXOJNHWJDHPOEMMSQJCBRBTSWEBQDQTVNDULI99GAYRPVOIMNN9KKNKPKBTWAUJZNXHMGHVCMZZRWIPSTIHJBDFGSBNCMBYDNLG9JEMDZBTKPNKMTMLANTZHHKDDIYOBVLJTQGHPPLNWFNEPTSZMVT9RRGCKYXC99SABLWIZVYAEMKZUSSGEHNVLXFKKFFTDGTHACHFEAULNPFPUKIRKZJTUCZFDFASOLFJPQJPDSXOBSIVWGSQASYRSKDBCJTSXPPXYBFLBFDGTKKGXNXFFPRGSTZODAIXLCGQDWWCTHPUMQHQWHIPCGMYFGBOVS9QCJVRWMPUWAYFACQGPOUSEVTKESYHYZGDLYWWDTETTOREBO9HCVUSPTZIYIXSEQMCFSMRTXSCYKKY9KFPGLVNYXQDGRINJJDHHLUOUJLCIDODG9NSBSUNFTKVDRKZNKRXULU9MILNMYYGTMDUUICPQVO9LIAF9FQBFM9GSNESZVXHVHBBQABYVAKQHTMJTOTDELHDLCC9OWKMFAJMMU9XDFLMJAXGYIHNAXYRQXQWUPLYVXUWXIXEBTALYH9WUHQCDDPGZCRVOPSUZHESAK9BOYZTWLAYSIKSFDRCUIDWZTAGSCCZNEGPEMSVZDCLEVPOUOVJPWGF9DBZSDYECWLBEPIXOZNAEXVALZHJLZWJQZYSCPVYEV9ZOW9CCMAPROMWARZTZNKWCAQDOB99DABUFCNNMBGHRGMZEYDGDZCJFRPLYJFLXIUSHFHQYUHTNWSBGUSDAQJYKGBEIZNPKJMUSL9J9ABXMKMU9CJFWTFC9JDIEGNTXJIDATWJKNYYULZWTVNUADGULKK9WCMRYVFXMCXGZMJ9SJNWHRHAKQUADJLHHHYSNIZLS9WOSTL9ZHUYNDUXWLEOKNUSHCDOHVIPAFRCWQZQZQWV9WB9PHFXINUPBBOGXAGULQ9OJIWOQTZX9YDNRQHBELIFSZOHVCUUAARYDFFWGBAKJBDMONWNTL9G9FUUACUVSJSGKCMWOXBHPYHKDKYPGPNDEMUBVKNIQEKTQYYZKITHY9SVHLC9STXKNBLMLKET9NPFPPQFI9UJPAZJUEVGSZCXRACWLROZQCSCVQCNJZF9DHDBTPCI9DNGYSJMAEQVMZTB',
    address:
     'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
    value: -1000,
    obsoleteTag: '999999999999999999999999999',
    timestamp: 1568375569,
    currentIndex: 1,
    lastIndex: 3,
    bundle:
     'SBOLDCVCHALOYRKBCUJLQXTFAVVPVRBYJUOZTYWDIJIDUOVK9HKTNLGKSJABIICUZDBQRABWLTSWSOCBA',
    trunkTransaction:
     'CSKEVBJIFUZGNOTZJESXNXIMAM9YRPWCSGOJVMOPQV9NWTJBWB9BNQKVIMRBRHUZNRN9EYQVGTENZL999',
    branchTransaction:
     'B9SHRBPVQZYSYJCBMAENTHFCSBMWTFIQ9O9VRNYNCHTMAWYGBDL9TUZTPQFVTBBKWXPWBXNNSNL9HE999',
    tag: '999999999999999999999999999',
    attachmentTimestamp: 1568375730023,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 3812798742493,
    nonce: 'BIXTK9DNMNNXLFFXWISJVV9GZLW' },
  { hash:
     'CSKEVBJIFUZGNOTZJESXNXIMAM9YRPWCSGOJVMOPQV9NWTJBWB9BNQKVIMRBRHUZNRN9EYQVGTENZL999',
    signatureMessageFragment:
     'ESSLXVQYVCAVVQUWCBFOWHFWCOZZYHMA9UQOYLITNOADMIAUVK9V9MIZNFZSFMHBPMLLJSFTW9FTGCLQ9NRGTVKGRL9DSAIJIBXEZCTOEGBECIMMDTNU9JUUQBXMJQJXKELIVEEUUMUVJCXQXPAZZTWBE9FYYQXDEDWTWB9URRWTKYRIMNARVJSFKCGELPTQLJXHUIAWVAVMSSMVIXQIEORCPIUIFD9FYJMOUOGEGY99KQDIK9ATWZSEMVGCUYDTIVWKJHPWSEYQHBSMB9LXAGUPWVEIRKXUSXTBLNAZIMSKWZLBVSESMRNETYOOVSRQFVP9IIJDTMWRPKXLGHXYQVBQFIRRKEAZOAVWMNOGAG9GGNLTPDSLWYPMMSZDIAUZQUC9GYXBWOWGROBIYHZHZADJSIMUCXJJGDVEKXLJVAKBAMBSHDUOLVNMHLVPMUKGSAELLPRAIZVQP9EOABTWERBTTXFNBRTSXKBZSZWHMFYXWABEWGACQZQTNEDNRDZBVSUJLZKCYGADMNFHHXNFIMILMVQDUNBEIYJFOZDMRASGFMY9QJKRKXWAGOLZASZJNEJ9FUM9LETMOKKZJ9CQCWNIINVVBLBAPCWVXXVGJHUDBTTTJAKDYVLYJISKPVGXMUUTFGTWAUWCSSKBOWFOKHVLMRDBZQ9VVXHBYTLXDVEVUPIZCRGRIJVRBSRAVRSCUNHPATHEIPHEXTVVPIMLNEA9XRQNEVNTNBXQAJBD9FCPHGJVJ9ETNBIWNVJQUKNFSXQCUIXJKJEEFNALYHEJLG9HXAYTACXTSTDEECCBCDKHWXNEPGFCDFZWDRYXBOEJ9ZGTQPIBBIXXPQAOQMJZBRO99AGJPRXYGZZK9YCDCYJMQFBSZKL9XOOZECDGTSFCQSSHJCEDGMKORHKD9VTREYROYNECLQNYMBXZFUMYOJTZQYSEJAWUIIWBQ9GMHGXGOSIFISZVQFYCKRUVPVCTILZVH9RIBIEFBVNFK9QKZDJ9RHGMHSQJORLWDLXFYISXGKTOBMZPWXWXVXIHSNXTXOWMNRHTDOWISPLQKSFDSBUNCJPUBWADRLAHQLFO9VWK9XHU9CGUZXFBHZIFXWIJZZM9YZEVYEPRWQMXWIVMKYWKCCXMTGZWWHEWJYHWFRRBBGKXHGBGJWIBMNOEHUPSCPCPTJDQVJRCEGPQOEUKPIURONJI9JKPQHJXHEVTWZZPDEUIOAKWFMZTIAUZVYHSVWIK9TBBHWOEBZPDAZHZSYGWRVRFSBYRRUNFQTDAJBJNWQZHNIKIAIQMEL9ZKJJFZJBXANCBLXOXATYSDMXQQBJMPYJIDXEGPDILZTOYODLSGXQPYAJCPMRKWZSPEFVUYECEJBDVQQKIYPMFEO99HAXCUXABAFMWNOYWGGAIBQVXCLLXWJ9SQADIQMCNYDMBZGIYOOIAWZNNGXTWGZPLNWFNIJIGA9JXGABHONIEDAEFFJWASFITZHFJPAKCFSMBVPLXCLFFEDAOMWCMCYDDGBGJUY9VTTELMSHUP9J9SECNHRXAHITDHGZPJPUUETXIYXOGWBJPZOTYVRTGIOYLUDPGELBOPIOZK9WELGTVTXMCRKXZQACGINYHKYVOUQZDYMPFWMZJERHCVSAIHXPVYHITKGWDZQXHVVERISKPMBTTRFE9NGSAHTQZCFFQMRUMNGUFUQSOAWFQDKIIAAINUMCECNQURUFWADTB9XWESZANVLWSARRJNHDM9OUXICCBSAZXWZTY99JUADOSWHIJAUFCSUGNZVZAKCVJXBKJ9ATLUNASYALDS9SGAUCHJWCJOOPUJZLWNFYCB9SRLAMYGQPRWKGHQRZLDOTFCSCHSYNUEHEBFO9HISZ9FZBYAUUMACYELCGB9T9VNXOSOECIHO9NGYDXNA9AEWZS9JVNPIMLHNVNQEZSDCMZRXXMJLFSNDP9DQMPCCUVERKI9UBRJYYMLXREQAKSHRLDKXAXIHUAHIJ9D9LHSKTMDJBCIOTNKFQDBUYNJWTYIQ9P9TYPXH9MOAVKQUVKWZDZSLGKSHFVCS9HHCLVHCMMPDASFC9XETLHVUPWABHISFXPLOSGHLSOCASKIRSYCOZL99P9SNNSTOTLUCHLRASGPIANFEWE9RSGPUZTRHLGQSXBWJSCLBRGRPDMAMIUYOQSXPIWYRRZILEJKUBZKZZ',
    address:
     'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9',
    value: 0,
    obsoleteTag: '999999999999999999999999999',
    timestamp: 1568375569,
    currentIndex: 2,
    lastIndex: 3,
    bundle:
     'SBOLDCVCHALOYRKBCUJLQXTFAVVPVRBYJUOZTYWDIJIDUOVK9HKTNLGKSJABIICUZDBQRABWLTSWSOCBA',
    trunkTransaction:
     'CSWMPIKZCTCNACHNMJYZDBHFREPWCEZMLSZVHBNNJ9SSPMWGVMM9MZCONKPRVJXRJZPKSLNTIBZJAE999',
    branchTransaction:
     'B9SHRBPVQZYSYJCBMAENTHFCSBMWTFIQ9O9VRNYNCHTMAWYGBDL9TUZTPQFVTBBKWXPWBXNNSNL9HE999',
    tag: '999999999999999999999999999',
    attachmentTimestamp: 1568375729985,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 3812798742493,
    nonce: 'QSOBTMACZXFAFNLW99EVSCBAQAN' },
  { hash:
     'CSWMPIKZCTCNACHNMJYZDBHFREPWCEZMLSZVHBNNJ9SSPMWGVMM9MZCONKPRVJXRJZPKSLNTIBZJAE999',
    signatureMessageFragment:
     '999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
    address:
     'NDQMBGBQIBAQURVAQEUVONVMECKXSNYZZNKMMMOHOLPEYHJHDBTFWOZMAPFUAHLHIQUAV9GVZPEMBKL9Z',
    value: 990,
    obsoleteTag: '999999999999999999999999999',
    timestamp: 1568375569,
    currentIndex: 3,
    lastIndex: 3,
    bundle:
     'SBOLDCVCHALOYRKBCUJLQXTFAVVPVRBYJUOZTYWDIJIDUOVK9HKTNLGKSJABIICUZDBQRABWLTSWSOCBA',
    trunkTransaction:
     'B9SHRBPVQZYSYJCBMAENTHFCSBMWTFIQ9O9VRNYNCHTMAWYGBDL9TUZTPQFVTBBKWXPWBXNNSNL9HE999',
    branchTransaction:
     'B9SHRBPVQZYSYJCBMAENTHFCSBMWTFIQ9O9VRNYNCHTMAWYGBDL9TUZTPQFVTBBKWXPWBXNNSNL9HE999',
    tag: '999999999999999999999999999',
    attachmentTimestamp: 1568375729966,
    attachmentTimestampLowerBound: 0,
    attachmentTimestampUpperBound: 3812798742493,
    nonce: 'DBYYBUDC9OZDRG9KKFFFVOSBXMZ' } ]

Let's review all transactions that has been prepared and sent eventually:

  • The first transaction current_index=0 is the output TX that basically adds tokens to some address
  • Transactions current_index=1 and current_index=2 are two parts of the same transaction - Input TX. This transaction deducts tokens from the address(es). You can see negative value in the transaction current_index=1
  • Why there are two parts? Since the transaction spends tokens it has to be signed in order to validate the ownership. Signature is generated from the private key and the signature can be observed in the field signature_message_fragment. Here is the thing. Do you remember when different security levels were discussed? There are three security levels available and the given level has the direct impact on length of the signature. Since signature_message_fragment is of the fixed size, if SL>1 then the transaction has to be split to be able to accommodate full signature.
  • The last transaction current_index=3 moves unspent tokens to the first unused address of the given seed.

The following tasks have to be done behind the curtain in addition to the standard transaction preparation process:

  • Search for address(es) with some balance until total amount of tokens available is large enough to be spent. You can use more input addresses if needed
  • If there would be some unspent amount of tokens then also search for the first unused address
  • Spending transactions (Input TX) has to be properly signed and split in case of SL>1
  • There is also some quick check performed whether resulting number of tokens is zero (no more tokens, no less tokens. Number of tokens has to be balanced).

So basically all steps we tried in the chapter had to be done also behind the curtain while using send_transfer extended API call. You can also see that some of those operations may be quite expensive in terms of time while being completely stateless. In real world applications it would be better to save some states (index of last address, used addresses, etc.) to avoid too many expensive operations.

You can review the given broadcasted transactions also in the tangle explorer (for devnet):