NAV Navbar

Getting Started

What is the dAPI?

The dAPI is a package interface for communicating with the NEO blockchain. The methods are handled by an existing wallet provider, such as the O3 wallet, and help to reduce the development overhead associated with creating dApps on NEO.

By offloading the responsibility of NEO blockchain interactions to a wallet provider, dApp developers do no have to worry about managing users private keys or how to format transactions to be signed and broadcast. The developer no long has to worry about user onboarding flows related to creating and managing a users secure credentials, and can just focus on the development of their core dApp.

On the user side, since all transactions that a dApp needs to broadcast to the blockchain will be handled by the users wallet proivder, they can feel safe knowing that they never need to copy and paste their private key into a dApp again. Any transaction that a user signs will be done so in the wallet, and their private key will never be provided to the dApp.

Installation

dAPI client integrations are currently facilited via a versioned JS package, and can be imported to your application either via CDN or NPM.

Install via CDN

<script src="https://cdn.jsdelivr.net/npm/neo-dapi@2.0.4/lib/neo-dapi.min.js"></script>
window.neoDapi

Badge

When installing via CDN, it's always recommended to reference a specific version of the neo-dapi package, to protect your app from possible method interface updates. In this example the version referenced in the url is 2.0.4.

Install via NPM

npm i --save neo-dapi

or

yarn add neo-dapi
var neoDapi = require('neo-dapi');

or

import neoDapi from 'neo-dapi';

npm version

When installing via NPM, it's always advised to lockdown your package version to either the specific version only, or to patch updates.

Dev Environment

The client JS package will help to facilitate all communications with the provider wallet, and the only requirement is that you have the O3 wallet running in the background. For development purposes, we recommend using the O3 desktop application, which can be downloaded from https://o3.network.

As long as you have the O3 desktop application open in the background. You can open your dApp in any web browser, and the JS package will automatically communicate with the background wallet.

Private Net

If you are looking to develop your own smart contracts, or would like to test sending assets without having to worry about requesting assets on testnet, O3 has made a private net available for you to run on your local computer. This locally hosted private net will provide you will full controll over all the NEO and GAS in your network, and it can be reset at anytime.

For more information please see the private net repo: https://github.com/O3Labs/neo-privatenet-docker

Mobile Compatability

We suggest doing the majority of your development using O3 desktop and using the mobile device simulator in the Chrome debugger. Once your app has been tested and fully functional, the JS package should automatically connect when running in the O3 mobile wallet with no additional changes.

Tutorials

Calling dAPI methods

As a general rule, please keep in mind that all methods on the dAPI are asynchronous, and will return a Promise.

In cases where the method is clasified in the API documentation as a "read" method, the Promise will resolve with a result that was either successfully ready from the NEO blockchain or from the users wallet. In the case that it was rejected, it either means the request to the NEO blockchain failed, or the user rejected the request to provide the information from their wallet.

For "write" methods, these will require a signature from the user in the wallet, and will, in most cases, broadcast the signed transaction to the NEO blockchain on behalf of the user. In this case, a resolved Promise will return the transaction id that can be referenced by the dApp to later confirm that it was processes, and a rejected Promise indicates that either the user has denied the request to sign the transaction, or the transaction was rejected by the NEO blockchain.

For all rejected promises, an error object is returned that will describe the reason for which the rejection occured.

Get user address

One of the first basic tasks for any dapp is to get the address of the account which the user would like to utilize with a dApp. In order to do this, we can use the method getAccount. This method will prompt the user, letting them know that your dApp is requesting that they provide an address from their O3 wallet. The user can then select any account from their wallet at the time of the request, and confirm that they would like to provide it to your dApp.

Getaccounttraywindow

Assuming that you have already included the neo-dapi JS package into your application, you can call the method getAccount, as follows:

neoDapi.getAccount()
.then((account: Account) => {
  const {
    address,
    label,
  } = account;

  console.log('Account address: ' + address);
  console.log('Account label: ' + label);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

This method, like all methods on the dAPI return a JS Promise object. This promise will resolve or reject, based on whether the user has approve or denied your request.

In the case that the request to getAccount, resolves the Promise, you will be returned an object with the parameters address and label. The address is the NEO address for the account which the user would like to interact with your dApp. The label is the name that they have given to refer to that account in their O3 wallet. This label can be useful when presenting information about the account that will be more familiar to the user.

For example, in the O3 Ontology staking dApp, we display the label for the users account along side the shortened NEO address.

Getaccountontstake

From here, the dApp can use this address to query additional information from the dAPI that are relevant to that specific user, or simply have an address for which assets will be sent.

Getting asset balances

neoDapi.getBalance({
  params: {
    address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
  },
  network: 'MainNet',
})
.then(results => {
  Object.keys(results).forEach(address => {
    const balances = results[address];
    balances.forEach(balance => {
      const { assetID, symbol, amount } = balance

      console.log('Address: ' + address);
      console.log('Asset ID: ' + assetID);
      console.log('Asset symbol: ' + symbol);
      console.log('Amount: ' + amount);
    });
  });
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

Whether it be for the account the user has provided to your dApp via getAccount, or you would like to get asset balance information for another account, the getBalance method can be used. By default, for a provided address, the wallet provider will query the status of the NEO block for the latest asset balances for NEO, GAS, and any NEP5 tokens. This balance request does not require permission from the user, and will return the balances immidiately in the resolve of the returned Promise.

In this first example, we have requested the default balances for the address AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru on MainNet. Similarly, you can change the network from which the balances are pulled from. For example, if you are still in development, you can change the network field to TestNet, and the asset balances for that account will be returned from the NEO test net.

Advanced attributes

neoDapi.getBalance({
  params: {
    address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
    assets: ['GAS'],
    fetchUTXO: true,
  },
  network: 'MainNet',
})

Alternatively, this same getBalance method can accept additional parameters for fetching the balance for only specific assets, or even return the NEO and GAS UTXOs for these balances. This is of course a more advanced usecase, and will probably not be required for the average dApp.

Request assets from user

neoDapi.send({
  fromAddress: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
  toAddress: 'ATaWxfUAiBcQixNPq6TvKHEHPQum9bx79d',
  asset: 'GAS',
  amount: '0.01',
  network: 'MainNet',
})
.then(({txid, nodeUrl}: SendOutput) => {
  console.log('Send transaction success!');
  console.log('Transaction ID: ' + txid);
  console.log('RPC node URL: ' + nodeUrl);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case SEND_ERROR:
      console.log('There was an error when broadcasting this transaction to the network.');
      break;
    case MALFORMED_INPUT:
      console.log('The receiver address provided is not valid.');
      break;
    case CANCELED:
      console.log('The user has canceled this transaction.');
      break;
    case INSUFFICIENT_FUNDS:
      console.log('The user has insufficient funds to execute this transaction.');
      break;
  }
});

In the case where the dApp would like to request a payment or any general sending of assets from the users wallet address to another address. The dAPI provides a method called send. The send method accepts the basic parameters for from which address the assets are to be sent from, the address which wil be receiving the assets, what asset to send, and the amount to be sent.

In this basic example, we are conducting a basic send of 0.01 GAS from the address that we got from calling getAccount, to the dApp address.

Advanced attributes

neoDapi.send({
  fromAddress: 'ATaWxfUAiBcQixNPq6TvKHEHPQum9bx79d',
  toAddress: 'ATaWxfUAiBcQixNPq6TvKHEHPQum9bx79d',
  asset: 'GAS',
  amount: '0.01',
  network: 'MainNet',
  remark: 'Hash puppy clothing purchase. Invoice#abc123',
  fee: '0.0011',
})

The send method can also accept an option parameter called remark. This will allow you to attach a short message description for the send transaction. This will be recorded on the blockchain as a part of the transaction.

The dApp can also specific a network fee amount attached to the transaction under the parameter fee. This network fee will be paid by the user in GAS, and will help to aid to get the transaction processed quicker when the mempool is full with free or lower fee transactions.

Setting up a private net

In the case that you would like to start developing your own smart contracts, you will want to use a private net.

In order to get this setup, you will need to have Docker installed on your computer.

Clone down the O3 provided private net. https://github.com/O3Labs/neo-privatenet-docker

make

Once cloned, navigate to the repo in your terminal, and simply execute the command:

This will trigger the Docker image to be downloaded to your machine, and will spin up your private net.

Upon starting up, your terminal should be in the NEO Python CLI.

Neopythoncli

This is the command line interface that you will be using to compile and deploy your smart contract files.

Open up master wallet

wallet open wallets/privnet.wallet

First open up the master wallet by executing the command:

When prompted for a password, type in o3.network.

Sync up wallet

wallet rebuild

Upon opening the wallet you will need to rebuild the wallet by executing the command:

This will resync the wallet from the genesis block.

Make GAS claimable

wallet send NEO AVFobKv2y7i66gbGPAGDT67zv1RMQQj9GB 1

In order to start deploying smart contracts, you will need GAS, so to start claiming the GAS for the wallet, you need to sync the claim. The way you can do this is by sending any amount of NEO back to yourself. By executing this command, you will be sending 1 NEO back to the wallet itself, and the GAS will then become claimable.

Upon entering the command, you will need to enter the wallet password again o3.network.

It's important to node that GAS is allocated per NEO after every block, but they blocks in the private net run every 1 second so it shouldn't take long to accumulate a lot of GAS.

Claim GAS

wallet claim

This command will claim any available GAS to your wallet.

Upon entering the command, you will need to enter the wallet password again o3.network.

Private net explorer

The private net also comes with it's own instance of the O3 explorer to monitor your contracts and transactions. To access the explorer, simply navigate in your web browser to http://localhost:3333.

O3explorer

Hello World Smart Contract

Included in the neo-privatenet-docker repo is a hello world smart contract called hello.py, which can be found in the smartContracts folder. When you start up the private net the Docker instance will automatically connect to this folder, so any smart contract files that you write should be placed in this smartContract folder when working with the private net.

Contract overview

from boa.interop.Neo.Runtime import Log
from boa.builtins import concat

def Main(operation, args):
    if operation == 'hello':
        return hello(args[0])
    return False

def hello(input):
    Log(concat('Hello World! ', input))
    return True

From the neo python cli which was started up in your docker instance, you can interact with the hello.py smart contract file. This simple contract has a single method called hello. Upon invoking this smart contract, it will print log our the string Hello World!, along with an input which you provide.

From the neo python cli, there are a couple options for compiling a smart contract from python code to NEOVM code. One option will simply compile the python file to an avm file containing the NEOVM code. The second option will also compile the pythong file to the avm file, but will also allow you to simultaneously run the smart contract without deploying.

Testing & compiling

sc build_run {path} {storage} {dynamic_invoke} {payable} {params} {returntype} (inputs) (--no-parse-addr) (--from-addr) (--owners) (--tx-attr)

In most cases while developing, you will want to use the compile and run operation so that you can continuously iterate on the development of your contract methods. In this example, we will go ahead an build and run the contract using the command sc build_run. This command takes several parameters which will simulate how the contract would be deployed and the operation/arguments you would like to pass to the contract upon test invoke.

sc build_run smartContracts/hello.py false false false 0507 10 hello ['Good Bye']

Here is the full command that we will be running with a short description of each field below:

path

The relative path to the smart contract file. In our case, it will always start with smartContracts/, followed by the name of the python file. So for the hello world smart contract the path value will be smartContracts/hello.py

storage

This is a Boolean flag which wich is set on deployment to tell the platform if your contract requires storage in the VM. For this simple contract we will not use storage, so we can return the value False

dynamic_invoke

This is a Boolean flag which wich is set on deployment to tell the platform if your contract would like to be able to invoke other smart contracts, whose contract hash is not hardcoded into your contract. For this simple contract we will not use dynamic invokes, so we can return the value False.

payable

This is a Boolean flag which wich is set on deployment to tell the platform if your contract would like to be able to accept payments of NEO or GAS. For this simple contract we will not be accepting payments of NEO or GAS, so we can return the value False.

params

A list of data types which your contracts expect to be passed into the Main function of the contract. A full list of the data types can be found here. The most common pattern is to accept an operation and args, where the operation is a string, and the args are an array of parameters. So for this, we will use the value 0710, 07 for String, and 10 for Array.

returntype

The data type which the contract will return. For most contracts, this value will be 05 for Bytearry. The allows the contract to return various data types, all in the form of a hex string when returned by via RPC node.

inputs

This will be a list of the input arguments to your Main function, separated by spaces. So for our example we will be calling the operation hello, and be passing in a single argument Good Bye. Which will look like hello ['Good Bye'].

Other flags

The are some additional flags that can be set at the end of the execution, but we will go over those in a later tutorial, as they are not applicable to our example.

Upon running the command, you should get the following result: Buildtest

[test_mode][SmartContract.Contract.Create] Confirms the contract was compiled and setup with the parameters you provided.

[test_mode][SmartContract.Runtime.Log] is the output of the Log method in our hello operation (Log(concat('Hello World! ', input))). Here you can see that the test invoke of the contract operates as expected, and the following was logged {'type': 'String', 'value': 'Hello World! Good Bye'}

[test_mode][SmartContract.Execution.Success] Communicates whether the contract invoke was successful or not. In your smart contract, if the logic of the contract completes successfully, then the contract should return the value True, and False if not. In this case, we can see that the Integer value of 1 was returned, which represents the value True. So we know that the contract was test invoked successfully.

Deploying

sc deploy {path} {storage} {dynamic_invoke} {payable} {params} (returntype) (--fee)

Now that you have successfully compiled and tested your contract, it's time to actually deploy the contract onto your private net. Unlike the previous build and run, deploying the contract will acutally store your contract code on the platform, and it's operations can then be invoked by dApps.

You can use the sc deploy command, which as very similar parameters as sc build_run, except without the input arguments to test at the end. You can add an addtional network fee with the --fee parameter. But since we are on a private net, this is not required. If deploying to test net or main net, please be sure to include a network fee.

sc deploy smartContracts/hello.avm False False False 0710 05

Here an important thing to note is that you want to reference the compiled file with the extention .avm, rather than the source .py file.

Upon execution, you will be asked for some details about your contract. These will be more useful for deployments to testnet or mainnet, but for now you can just use arbitrary values.

Creating smart contract....
                 Name: hello1
              Version:
               Author:
                Email:
          Description:
        Needs Storage: False
 Needs Dynamic Invoke: False
           Is Payable: False
{
    "hash": "0xcefb4b90edd232bcebb689064c50dc1e0aac00d5",
    "script": "57c56b6a00527ac46a51527ac46a00c30568656c6c6f87640e006a51c300c3650b006c756661006c756655c56b6a00527ac40d48656c6c6f20576f726c6421206a00c37e680f4e656f2e52756e74696d652e4c6f6761516c75665ec56b6a00527ac46a51527ac46a51c36a00c3946a52527ac46a52c3c56a53527ac4006a54527ac46a00c36a55527ac461616a00c36a51c39f6433006a54c36a55c3936a56527ac46a56c36a53c36a54c37bc46a54c351936a54527ac46a55c36a54c3936a00527ac462c8ff6161616a53c36c7566",
    "parameters": [
        "String",
        "Array"
    ],
    "returntype": "ByteArray"
}

In the output upon deploying, you can find the script hash of your contract, in the hash attribute. In this case the contract hash is 0xcefb4b90edd232bcebb689064c50dc1e0aac00d5.

Invoking via CLI

sc invoke {contract} (inputs) (--attach-neo) (--attach-gas) (--no-parse-addr) (--from-addr) (--fee) (--owners) (--tx-attr)

Once the contract is deployed, you can now start to invoke the contract. We can do this directly from the neo python cli using the command sc invoke. It has very similar parameters to the sc build_run command, except that instead of referencing the contract file, we will reference the contract hash we got on deployment, and there is no need to specify the datatypes of the input and output arguments.

sc invoke 0xcefb4b90edd232bcebb689064c50dc1e0aac00d5 hello ['Good Bye']

In our case, the invoke command will look like this. Upon submitting the command, it will produce the test invoke results. These should be the same as when we tested the contract with the sc build_run command, with the exclusion of the SmartContract.Crate log.

This invoke command also will prompt you for the wallet password. If entered, this transaction will be processed in the next block and recorded on chain. For cases where you are looking to perform a "read" operation from your smart contract, there is no need to sign and broadcast the information, as the return data has already been provided. In later examples, we will go over other contracts which you will want the transaction to be executed on chain (for example a transfer of NEP5 tokens).

dAPI Testbed

After you deploy your smart contract, and would like to start building the interface to call your smart contract from your dApp, you can start by using the dAPI Testbed @ neodapitestbed.o3.app. This is a free open source testbed which allows you to quickly explore all the different methods on the dAPI. It can make all the same calls related to getting a user address, getting asset balances, and even transferring assets. However, one of the most important features of this testbed is that it allows you to test out your smart contract code by invoking it via the dAPI.

Testbedoverview

In the testbed, you have a shortcut list for all the methods available on the dAPI. Each method has a simple form for any input variables that may be required or optionally provided. The requirement to using this dAPI testbed is that you have the O3 desktop app open in the background on the same computer. The dAPI JS package included in this testbed dApp will automatically communicate with the O3 wallet in the background.

Connecting to Private Net

First, you will notice that at the top right hand corner, there is a network selector. The network will automatically be set to the network of your wallet. So in order to invoke our hello world smart contract on our private net, simply change the wallet network to "PrivateNet" from the network selector found on the bottom left of the wallet.

Menubarnetworkbutton

Networkselector

Once you have set your wallet to PrivateNet, the dAPI testbed network selector should have automatically updated it's network to PrivateNet as well. If not, please simply refresh the site.

Invoking read methods

When interacting with smart contracts there are generally two different types of interactions, one to ready information back from the smart contract, and the other to write data to the blockchain. For the first case, we can use a method on the dAPI called invokeRead. This method will create a invoke transaction, execute it against the RPC node, and return the result. This does not require any signature from the user, nor does it write any information to the blockchain.

For this case, lets call our hello world smart contract. First navigate to the dapi explorer, with your O3 wallet open in the background and your privatenet up.

neodapitestbed.o3.app

Navigate to the invokeRead method, either by scrolling down, or selecting it from the shortcut on the left side of the screen.

Testbedinvokeread

Update the input object with the details for your invoke. In this case, we will update the script hash to be the one we got for our hello world contract when we deployed.

IMPORTANT: When using the dAPI please be sure to remove the 0x prefix on your contract script hash. It will be displayed in neo-python with the 0x prefix. Please remove it when referencing your contract in the dAPI invokeRead and invoke methods.

Testbedinvokereadinput

After updating the correctly formatted script hash for your contract, you can fill in the operation and args just as we called it from the neo-python cli.

Another thing to note is that when using the dAPI you need to specify the data type of each argument that is passed into the smart contract as inputs. This is so that when the transaction is serialized, the data is correctly formatted.

Lastly, since we are executing against the PrivateNet, we need to update this input value.

Testbedinvokereadresult

If all went correct, you should see a similar result. The state value is HALT, which means that the contract executed successfully; compared to the value FAULT, which indicates that there was an error.

The stack value is what was returned from the smart contract. In some cases, this may be a value such as an account balance or token symbol, but in our case it is just the Integer value 1 to indicate a returned value of True.

If you look back into your neo-python cli you will notice there there was no log event. This is because the invokeRead method just read the result of the execution, but did not actually execute the transaction.

Invoke write methods

In the case where you want to execute a transaction on chain which would result in a change of state, you will use the invoke method on the dAPI instead. This method will require a signature from the user of the wallet, and will effect a change on your NEO private net.

Scroll down to the invoke method in the testbed, and simply copy the same input that you used for invokeRead, and click Run. This will execute the same invoke against your hello world smart contract, except that it will now broadcast the transaction to be processed in the next block after getting your signature.

Testbedinvokereadresult

Upon clicking Run, a notification should pop up from the O3 wallet. Select an account to sign the transaction with and select Connect. In the case where your account isn't already authenticated, enter the passphrase to decrypt your account.

Dapiconnectpopup

You will now be presented with the standard notification for accepting or rejecting a transaction. You can click the Show transaction details dropdown to see a breakdown of the details included in the transaction that you will be signing with you selected wallet. By clicking Approve, the transaction will then be signed and broadcast to the network for processing.

Dapitxapprove

The testbed will show you the transaction id that was sent to the network.

Invoketxid

There are two ways to look at the result of your transaction.

First you can copy this value and look it up in the private net explorer to see the result.

Invokeexploretx

For our hello world smart contract, this will not be all that useful, but for other transaction, mainly NEP5 transfers or main/test net invokes, this will start to become more useful.

The second way is to navigate back to your neo-python cli. There should be now be the same logged results just as we had invoked the smart contract from the neo-python cli in the previous tutorial.

Invokecliresult

Here you can see the log of Hello World! concatenated with our input String, and the success message with an output of the Integer 1, signifying a return of True.

Now that you know the basics of how to invoke both read and write methods on your smart contract, we can move into more advanced use cases.

Token Smart Contract

from boa.interop.Neo.Runtime import CheckWitness
from boa.interop.Neo.Storage import GetContext, Get, Put
from boa.interop.Neo.Action import RegisterAction

# AVFobKv2y7i66gbGPAGDT67zv1RMQQj9GB
TOKEN_OWNER = b'\x93\xe1\xe5\xbe\xd9\x15\x96\x84\xac\x8c.\xe4\r w\xfb\xcf\xac\xf7\n'
TOTAL_SUPPLY = 10000000 * 100000000
TOKEN_NAME = 'My First Token'
TOKEN_SYMBOL = 'MFT'
TOKEN_DECIMALS = 8

TOTAL_SUPPLY_KEY = 'TOTAL_SUPPLY'
INIT_KEY = 'DEPLOYED'

ctx = GetContext()
OnTransfer = RegisterAction('transfer', 'addr_from', 'addr_to', 'amount')

def Main(operation, args):

    if operation == 'totalSupply':
        return Get(ctx, TOTAL_SUPPLY_KEY)

    if operation == 'name':
        return TOKEN_NAME

    if operation == 'symbol':
        return TOKEN_SYMBOL

    if operation == 'decimals':
        return TOKEN_DECIMALS

    if operation == 'balanceOf':
        if len(args) == 1:
            return Get(ctx, args[0])

    if operation == 'transfer':
        if len(args) == 3:
            return transfer(args[0], args[1], args[2])

    if operation == 'init':
        return init()

    return False

def transfer(addr_from, addr_to, amount):

    if amount <= 0:
        return False

    if len(addr_to) != 20:
        print("not a valid to address")
        return False

    if not CheckWitness(addr_from):
        print("from address is not the tx sender")
        return False

    if addr_from == addr_to:
        print("transfer to self!")
        return True

    from_val = Get(ctx, addr_from)

    if from_val < amount:
        print("insufficient funds")
        return False

    if from_val == amount:
        Delete(ctx, addr_from)
    else:
        difference = from_val - amount
        Put(ctx, addr_from, difference)

    to_value = Get(ctx, addr_to)

    to_total = to_value + amount

    Put(ctx, addr_to, to_total)

    OnTransfer(addr_from, addr_to, amount)

    return True

def init():
    if not CheckWitness(TOKEN_OWNER):
        print("Must be token owner to init")
        return False

    if Get(ctx, INIT_KEY):
        print("Token already init")
        return False

    Put(ctx, INIT_KEY, 1)
    Put(ctx, TOTAL_SUPPLY_KEY, TOTAL_SUPPLY)
    Put(ctx, TOKEN_OWNER, TOTAL_SUPPLY)
    OnTransfer(None, TOKEN_OWNER, TOTAL_SUPPLY)
    return True

One of the first smart contracts that many developers want to test out deploying a a token contract. In the NEO ecosystem the standard for token contracts is called NEP5, and you will find a template of this smart contract in the neo-privatenet-docker, similar to how there was already a hello world smart contract.

You can find details about all the methods on the NEP5 standard in the NEO proposals: https://github.com/neo-project/proposals/blob/master/nep-5.mediawiki

For this basic example template, we have all the base methods, with the addition of a non-standard init method. This method will initialize the token contract, sending all 10m tokens to the contract owner (which is set to the master account on the neo-privatenet-docker). If reviewing other smart contract for NEP5 tokens in the NEO ecosystem, you will find that many of them are much more complex, as they also contain the code to facilitate the automated token sale.

For the purpose of this tutorial, we kept it to the base methods so your can test out reading and writing information to the smart contract using the dAPI.

While reviewing the contract, you can see that most of the methods are what we refer to as "read" methods. This is because they do not require any writes into contract storage or handling of native asset UTXOs (NEO & GAS). For example, when calling the name operation, it will simply return a static string, for the name of the token. When using the dAPI method invokeRead, calling the method will return the value My First Token.

On the other hand, lets look at the operation init. This method makes calls to the Put method, which will write the specific key value pair to the storage allocated to your contract. In order to have these value be persisted on the blockchain, we need to use the dAPI method invoke instead of invokeRead. This will ensure that your transaction is signed by your wallet provider and broadcast to the network for processing in the next block.

Deploying the contract

sc build smartContracts/token.py

sc deploy smartContracts/token.avm True False False 0710 05

Just like in the hello world tutorial, once you are in the neo-python cli, you can use the sc build and sc deploy commands to compile and deploy your contract.

Note that this time we set the first parameter in the sc deploy command, after the path to the .avm file, to True. This indicates that for our token contract we would like to use the contract storage feature. This will allow the contract to store data on the blockchain, but be aware that the cost of the deployment has now increased to 490 GAS.

Invoke from testbed

Once deployed, grab the contract hash (minus the 0x prefix), and head back to the dAPI testbed.

Update the input object with the reference to your new token contract script hash, and set the operation to name. We will not need any arguments, so leave the args array empty, or remove it from the object.

Tokeninvokeread

After hitting Run, you should have a result as follows.

Tokeninvokereadresult

Making sense of results

But wait a minute, what is that weird looking value in the stack field. We specified that the return type of our contract as 05 on deployment, which means ByteArray. This will allow us to return various data types, formatted as hex strings. So in the dAPI client package, we've included utilities to convert these hex string results to numbers and string.

In this case, we know that the type that we want is a String. So in copy the value, and navigate to the Hex to string method in the testbed. Here you can paste the result and convert it to a human readable string.

Tokenresultconverted

Here we can see that the value returned should match the name of our token that we hardcoded into the contract.

Init the contract

Now, in order to get rolling with making transfers we first need to initialize the contract by calling the init operation. Just like we did for the hello world smart contract, we need to use the invoke method on the dAPI. This will send all of the tokens to the private net master account, so that you can start sending your test tokens to other addresses.

Tokeninit

Once approving the transaction, you can then check the balance of the master private net account, with the invokeRead method, just like we called the name operation. This time we just need to add the input argument of the account address, with data type Address.

Tokeninitresult

You should get back a success result, which you can then convert from it's hex string value to a number in the utils at the bottom of the testbed.

Furthermore, you can even go into the private net explorer, and checkout your contract and master account balances.

Tokencontractexplorer

Tokenmasteraccount

Transferring tokens

Now that we have the master account loaded up with our test token, we can proceed to send them to another test address.

Just update the operation and args in the invoke method, and follow the same steps as the init.

Tokeninvoketransfer

One important thing to note is that for the third argument in the array, we have set the input value to 1. Since smart contracts do not handle floating point numbers, we need to keep in mind the number of decimals our token has. So if we are transferring 1, we are not transferring one whole token, but rather 0.00000001 of our token since it has 8 decimal places. This is a very important point when building your application, as you do not want to make the mistake of sending the wrong amount by a whole decimal place!

After submitting the invoke transaction, we can use the transaction (txid) in the results to look up the results in more detail in the explorer.

Tokentransferresults

Here we can see that we successfully sent 0.00000001 of our MFT token to our test address.

Sample dApp

Now that we have a token contract deployed on our private net, and we've tested it from the dAPI testbed, we can look into integrating this in to our own dApp. The code for the testbed is open source, and can be used as an example. Or you can reference the API documentation for usage examples.

Here we will use create a very simple dApp website which will interface with the token smart contract. This simple dApp will just be a basic website that imports the neo-dapi JS package from a CDN, and provides a user interface to interact with the contract.

Fetch Token Name

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/neo-dapi@2.0.4/lib/neo-dapi.min.js"></script>
  </head>
  <body>
    <h1>Token testbed</h1>

    <div class="method-container">
      <h3>script hash</h3>
      <input type="text" id="scriptHash" name="scriptHash" placeholder="scriptHash" value="">
    </div>

    <div class="method-container">
      <h3>name</h3>
      <button type="button" name="button" onclick="handleName()">Run</button>
      <div class="result" id="nameResult"></div>
      <div class="error" id="nameError"></div>
    </div>
  </body>
  <script type="text/javascript">
    const scriptHashEle = document.getElementById("scriptHash");
    const nameResultEle = document.getElementById("nameResult");
    const nameErrorEle = document.getElementById("nameError");

    function handleName() {
      neoDapi.invokeRead({
        scriptHash: scriptHashEle.value,
        operation: 'name',
      })
      .then(res => res.stack[0].value)
      .then(res => neoDapi.utils.hex2str(res))
      .then(res => nameResultEle.innerHTML = res)
      .catch(err => nameErrorEle.innerHTML = err);
    }
  </script>
</html>

To start off, we will look into making a basic app, which will take an input of the contract hash, and return the name of the token from the smart contract via the dAPI. In order to accomplish this, we will set up a plain HTML website, and import the neo-dapi package into the website.

Once importing the JS package from the CDN, we can set up a few simple UI elements to help us to interact with the dAPI code. These are mostly comprised of a single text input where the user can paste in the script hash of their smart contract, and a button to execute the invoke to fetch the name of the token from the smart contract.

In the script tag, we will have a single function which will take the input script hash, and call the dAPI method invokeRead, with the operation set to the value name.

It's important to note that on the promise resolve, we are parsing the relevant information from the response, which is the value of the first object returned in the stack array. As we saw from the previous tutorial, using the testbed, the response value is returned as a hexstring.

So in our application, we will utilize the utils provided on the api in order to parse the return value to be human readable. For this we will use the method hex2str on the utils namespace.

Lastly we set the formatted output of the promise resolve or the raw value from the promise reject to their respective result and error div elements in the UI.

Gettokenname

This is what our simple UI should look like. Upon entering the script hash for your token contract deployed on private net and clicking the Run button, you should see the following result. Where the token name My First Token is displayed just below the Run button. In the case that there is an error, make sure you have removed the 0x from the contract script hash and have your O3 wallet open in the background with the network set to PrivateNet.

Init Token Contract

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/neo-dapi@2.0.4/lib/neo-dapi.min.js"></script>
  </head>
  <body>
    <h1>Token testbed</h1>

    <div class="method-container">
      <h3>script hash</h3>
      <input type="text" id="scriptHash" name="scriptHash" placeholder="scriptHash" value="">
    </div>

    <div class="method-container">
      <h3>init</h3>
      <button type="button" name="button" onclick="handleInit()">Run</button>
      <div class="result" id="initResult"></div>
      <div class="error" id="initError"></div>
    </div>
  </body>
  <script type="text/javascript">
    const scriptHashEle = document.getElementById("scriptHash");
    const initResultEle = document.getElementById("initResult");
    const initErrorEle = document.getElementById("initError");

    function handleInit() {
      neoDapi.invoke({
        scriptHash: scriptHashEle.value,
        operation: 'init'
      })
      .then(res => JSON.stringify(res))
      .then(res => initResultEle.innerHTML = res)
      .catch(err => initErrorEle.innerHTML = err);
    }
  </script>
</html>

If you remember from the last tutorial, the sample token contract has a non-standard method called init, which allows us to distribute the entire supply of tokens to the owner of the contract. In this case, we have set the owner of the token to be the master account on the private net. So in order to call this method, we need to submit a transaction to the chain to write the new state to the blockchain. Therefore, we need to use the invoke method instead of the invokeRead method, and this will require the user to sign the transaction from the wallet interface.

In order to accomplish this, we have edited the simple dApp to call the invoke method on the dAPI as opposed to the invokeRead method, and updated some of the UI text. The handling for the promise resolve has also been updated to simply update the UI with the return object, which will contain the transaction id for the invocation which can be checked on the local explorer.

Upon entering the contract script hash and clicking Run, the user will be prompted for their signature on the transaction, and it should be signed by the master account of the privatenet. So if you haven't already added this account to your O3 wallet, please be sure to do so before running.

In the neopython cli, this can be done with the command: wallet export wif AVFobKv2y7i66gbGPAGDT67zv1RMQQj9GB

Take the return value and paste it into import section of the O3 wallet.

Inittokencontract

Upon approving the transaction, the dApp UI will update with the txid for the transaction, and on the next block, all tokens will have been issued.

At this point it would be a good time to practice implementing the integration for the remainder of the methods on your own. Don't worry, the full dApp will be linked at the end just in case.

Transferring Tokens

Now that you have initialized the token contract, all the tokens should be in your account. From here we can implement the most important method for your token contract, the transfer operation.

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/neo-dapi@2.0.4/lib/neo-dapi.min.js"></script>
  </head>
  <body>
    <h1>Token testbed</h1>

    <div class="method-container">
      <h3>script hash</h3>
      <input type="text" id="scriptHash" name="scriptHash" placeholder="scriptHash" value="">
    </div>

    <div class="method-container">
      <h3>transfer</h3>
      <input type="text" id="transfer-addr-from" name="from address" placeholder="from address" value="">
      <input type="text" id="transfer-addr-to" name="to address" placeholder="to address" value="">
      <input type="number" id="transfer-amount" name="amount" placeholder="amount" value="">
      <button type="button" name="button" onclick="handleTransfer()">Run</button>
      <div class="result" id="transferResult"></div>
      <div class="error" id="transferError"></div>
    </div>
  </body>
  <script type="text/javascript">
    const scriptHashEle = document.getElementById("scriptHash");
    const transferAddrFromEle = document.getElementById("transfer-addr-from");
    const transferAddrToEle = document.getElementById("transfer-addr-to");
    const transferAmountEle = document.getElementById("transfer-amount");
    const transferResultEle = document.getElementById("transferResult");
    const transferErrorEle = document.getElementById("transferError");

    function handleTransfer() {
      neoDapi.invoke({
        scriptHash: scriptHashEle.value,
        operation: 'transfer',
        args: [
          {
            type: neoDapi.Constants.ArgumentDataType.ADDRESS,
            value: transferAddrFromEle.value,
          },
          {
            type: neoDapi.Constants.ArgumentDataType.ADDRESS,
            value: transferAddrToEle.value,
          },
          {
            type: neoDapi.Constants.ArgumentDataType.INTEGER,
            value: transferAmountEle.value,
          }
        ]
      })
      .then(res => JSON.stringify(res))
      .then(res => transferResultEle.innerHTML = res)
      .catch(err => transferErrorEle.innerHTML = err);
    }
  </script>
</html>

In the updated simple dApp, we again updated the UI with text relevant to the transfer, and added in a few new input fields. Here the user can enter the to and from addresses, along with the amount of tokens to be transferred. For the dAPI interaction, we just updated the operation to transfer, and added in the arguments for the operation.

For the transfer operation, we need to pass in 3 arguments, the from address, to address, and amount. The first two will be of type Address, with the amount being of type Integer.

Once the user has populated all the fields and clicked Run, the wallet should notify them of a new transaction for approval. Upon successful submission, you can lookup the txid in the local explorer for your private net.

Notice anything strange?

The amount sent is actually far less that the integer amount that we specified in the UI. This is because the contract can only handle integers, so when passed a argument, it must also be formatted as such. So if you want to send 10 tokens, you would need to enter an input of 1000000000, to accommodate the amount of decimal places the token has.

Since the contract provides a method to get the number of decimal places the token has, can you update the existing dApp to automatically convert the UI input to accept decimal numbers? Meaning if a user inputs a value of 10, it should pass an argument of value 1000000000, or 3 for an user input of 0.00000003.

Full Sample dApp

Once you have completed implementing all of the methods into your sample dApp, you can check your implementation here. It has automatic parsing for the read methods, but we have left the auto conversion of the user input for the transfer method as a challenge.

API Methods

Read Methods

Read methods do not alter the state of the blockchain. It can help you query information about your user, and provide you with relevant information:

getProvider

neoDapi.getProvider()
.then((provider: Provider) => {
  const {
    name,
    website,
    version,
    compatibility,
    extra,
  } = provider;

  const {
    theme,
    currency,
  } = extra;

  console.log('Provider name: ' + name);
  console.log('Provider website: ' + website);
  console.log('Provider dAPI version: ' + version);
  console.log('Provider dAPI compatibility: ' + JSON.stringify(compatibility));
  console.log('Provider UI theme: ' + theme);
  console.log('Provider Base currency: ' + currency);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp.');
      break;
  }
});

Example Response

{
  name: 'Awesome Wallet',
  website: 'https://www.awesome.com',
  version: 'v0.0.1',
  compatibility: [
    'NEP-14',
    'NEP-23',
    'NEP-29'
  ],
  extra: {
    theme: 'Dark Mode',
    currency: 'USD',
  }
}

Returns information about the dAPI provider, including who this provider is, the version of their dAPI, and the NEP that the interface is compatible with.

Input Arguments

None

Success Response
Parameter Type Description
name String The name of the wallet provider
website String The website of the wallet provider
version String The version of the dAPI that the the wallet supports
compatibility String[] A list of all applicable NEPs which the wallet provider supports
extra Object Provider specific attributes
extra
Parameter Type Description
theme string UI theme of the provider
currency string Base currency set by user
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getNetworks

dapi.NEO.getNetworks()
.then(response => {
  const {
    networks,
    defaultNetwork,
  } = response.networks;

  console.log('Networks: ' + networks);
  // eg. ["MainNet", "TestNet", "PrivateNet"]

  console.log('Default network: ' + defaultNetwork);
  // eg. "MainNet"
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

Example Response

{
  networks: ["MainNet", "TestNet", "PrivateNet"],
  defaultNetwork: "TestNet",
}

Returns the networks the wallet provider has available to connect to, along with the default network the wallet is currently set to.

Input Arguments

None

Success Response
Parameter Type Description
networks String[] A list of all networks which this wallet provider allows access to
defaultNetwork String Network the wallet is currently set to
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getAccount

neoDapi.getAccount()
.then((account: Account) => {
  const {
    address,
    label,
  } = account;

  console.log('Account address: ' + address);
  console.log('Account label: ' + label);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

Example Response

{
  address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
  label: 'My Spending Wallet'
}

Return the Account that is currently connected to the dApp.

Success Response
Parameter Type Description
address String The address of the account that is currently connected to the dapp
label String A label the users has set to identify their wallet
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getPublicKey

neoDapi.getPublicKey()
.then((publicKeyData: PublicKeyData) => {
  const {
    address,
    publicKey,
  } = publicKeyData;

  console.log('Account address: ' + address);
  console.log('Account public key: ' + publicKey);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

Example Response

{
  address: 'ATUaTd3LA4kZiyB6it9fdb5oJpZYMBF4DX',
  publicKey: '03fa41b6ff75ebeff8464556629cfceae7402f5d815626a7a6542f786974b942e0'
}

Return the public key of the Account that is currently connected to the dApp.

Success Response
Parameter Type Description
address String The address of the account that is currently connected to the dapp
publicKey String The public key of the account that is currently connected to the dapp
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getBalance

neoDapi.getBalance({
  params: {
    address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
    assets: ['NKN']
  },
  network: 'MainNet',
})
.then((results: BalanceResults) => {
  Object.keys(results).forEach(address => {
    const balances = results[address];
    balances.forEach(balance => {
      const { assetID, symbol, amount } = balance

      console.log('Address: ' + address);
      console.log('Asset ID: ' + assetID);
      console.log('Asset symbol: ' + symbol);
      console.log('Amount: ' + amount);
    });
  });
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

Single Address with specific balances requested

// input
{
  params: {
    address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
    assets: ['NKN']
  },
  network: 'MainNet',
}

// output
{
  AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru: [
    {
      assetID: 'c36aee199dbba6c3f439983657558cfb67629599',
      symbol: 'NKN',
      amount: '0.00000233',
    }
  ],
}

Single Address with all balances requested

// input
{
  params: {
    address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
  },
  network: 'MainNet',
}

// output
{
  AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru: [
    {
      assetID: 'c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b',
      symbol: 'NEO',
      amount: '10',
    },
    {
      assetID: '602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7',
      symbol: 'GAS',
      amount: '777.0001',
    },
    {
      assetID: 'c36aee199dbba6c3f439983657558cfb67629599',
      symbol: 'NKN',
      amount: '0.00000233',
    },
    {
      assetID: 'fc732edee1efdf968c23c20a9628eaa5a6ccb934',
      symbol: 'NNC',
      amount: '2000',
    }
  ]
}

Multiple address balance queries

// input
{
  params: [
    {
      address: 'AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru',
    },
    {
      address: 'AbKNY45nRDy6B65YPVz1B6YXiTnzRqU2uQ',
      assets: ['PHX'],
    },
  ],
  network: 'MainNet',
}

// output
{
  AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru: [
    {
      assetID: 'c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b',
      symbol: 'NEO',
      amount: '10',
    },
    {
      assetID: '602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7',
      symbol: 'GAS',
      amount: '777.0001',
    },
    {
      assetID: 'c36aee199dbba6c3f439983657558cfb67629599',
      symbol: 'NKN',
      amount: '0.00000233',
    },
    {
      assetID: 'fc732edee1efdf968c23c20a9628eaa5a6ccb934',
      symbol: 'NNC',
      amount: '2000',
    }
  ],
  AbKNY45nRDy6B65YPVz1B6YXiTnzRqU2uQ: [
    {
      assetID: '1578103c13e39df15d0d29826d957e85d770d8c9',
      symbol: 'PHX',
      amount: '11000',
    }
  ]
}

Allows the DAPP to query the balance of a user, this includes both native assets (NEO/GAS) and NEP-5 tokens

Input Arguments
Parameter Type Description
params BalanceRequest or BalanceRequest[] A list of Balance Request Objects, specifying which addresses, and which assets to query
network String The call will only work for the networks available in the GetNetworks command
Balance Request
Parameter Type Description
address String The address whose balance you want to query
assets String[] A list of contract hash (or symbold on MainNet only) to query the balance for
fetchUTXO? boolean The response will fetch NEO and GAS UTXO's if this attribute is true
Success Response
Parameter Type Description
address_1 BalanceResponse[] This key is the actual address of the query eg. "AeysVbKWiLSuSDhg7DTzUdDyYYKfgjojru"
address_2 BalanceResponse[] This key is the actual address of the query eg. "AbKNY45nRDy6B65YPVz1B6YXiTnzRqU2uQ"
address_n BalanceResponse[] This key is the actual address of the query eg. "AUdawyrLMskxXMUE8osX9mSLKz8R7777kE"
BalanceResponse
Parameter Type Description
assetID String ID of the given asset
symbol String Symbol of the given asset
amount String Double Value of the balance represented as a String
unspent UTXO[]? If fetch utxo's was turned on then the utxo array will be returned for the native assets NEO and GAS
UTXO
Parameter Type Description
asset String Script hash of the native asset
createdAtBlock String Block number where this utxo was created
index Int Output index of the UTXO relative to the txid in which it was created
txid String The transaction id of this UTXO
value String The double value of this UTXO represented as a String

getStorage

neoDapi.getStorage({
  scriptHash: '505663a29d83663a838eee091249abd167e928f5',
  key: 'game.status',
  network: 'TestNet'
})
.then(res => {
  const value = res.result;
  console.log('Storage value: ' + value);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_REFUSED:
      console.log('Connection dApp not connected. Please call the "connect" function.');
      break;
    case RPC_ERROR:
      console.log('There was an error when broadcasting this transaction to the network.');
      break;
  }
});

Example Response

{
  result: 'hello world'
}

Returns the raw value located in contract storage

Input Arguments
Parameter Type Description
scriptHash String Scripthash of the contract whose storage you are querying on
key String Key of the storage value to retrieve from the contract
network String Network alias to submit this request to.
Success Response
Parameter Type Description
result String The raw value located in contract storage
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

invokeRead

neoDapi.invokeRead({
  scriptHash: '505663a29d83663a838eee091249abd167e928f5',
  operation: 'calculatorAdd',
  args: [
    {
      type: neoDapi.Constants.ArgumentDataType.INTEGER,
      value: 2
    },
    {
      type: neoDapi.Constants.ArgumentDataType.INTEGER,
      value: 10
    }
  ],
  network: 'PrivNet'
})
.then((result: Object) => {
  console.log('Read invocation result: ' + JSON.stringify(result));
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_REFUSED:
      console.log('Connection dApp not connected. Please call the "connect" function.');
      break;
   case RPC_ERROR:
    console.log('There was an error when broadcasting this transaction to the network.');
    break;
  }
});

Example Response

{
  script: '8h89fh398f42f.....89hf2894hf9834',
  state: 'HALT, BREAK',
  gas_consumed: '0.13',
  stack: [
    {
      type: 'Integer',
      value: '1337'
    }
  ]
}

Execute a contract invocation in read-only mode.

Input Arguments
Parameter Type Description
scriptHash String The script hash of the contract you want to invoke a read on
operation String The operation on the smart contract that you want to invoke a read on
args Argument[] The input arguments necessary to perform this operation
network String Network alias to submit this request to. If omitted, will default the network which the wallet is set to
Argument
Parameter Type Description
type String The type of the argument with you are using
value String String representation of the argument which you are using
Success Response

The wallet will return the direct response from the RPC node.

Parameter Type Description
script String The script which was run
state String Status of the executeion
gas_consumed String Estimated amount of GAS to be used to execute the invocation. (Up to 10 free per transaction)
stack Argument[] An array of response arguments
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

verifyMessage

neoDapi.verifyMessage({
  message: '058b9e03e7154e4db1e489c99256b7faHello World!',
  data: '0147fb89d0999e9d8a90edacfa26152fe695ec8b3770dcad522048297ab903822e12472364e254ff2e088fc3ebb641cc24722c563ff679bb1d1623d08bd5863d0d',
  publicKey: '0241392007396d6ef96159f047967c5a61f1af0871ecf9dc82afbedb68afbb949a',
})
.then(({result: bool}) => {
  console.log('Signature data matches provided message and public key: ' + result);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case CONNECTION_DENIED:
      console.log('The user rejected the request to connect with your dApp');
      break;
  }
});

Example Response

{
  result: true,
}

Returns whether the provided signature data matches the provided message and was signed by the account of the provided public key.

Input Arguments
Parameter Type Description
message String The original signed message
data String The signature data
publicKey String The public key of the account used to sign the message
Success Response
Parameter Type Description
result Boolean Whether the provided signature matches the provided message and public key
Error Response
Parameter Type Description
type String The type of error which has occurred
description String A description of the error which has occurred
data String? Any raw data associated with the error

getBlock

neoDapi.getBlock({
  blockHeight: 2619690,
  network: 'TestNet'
})
.then((result: Object) => {
  console.log('Block information: ' + JSON.stringify(result));
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
   case RPC_ERROR:
    console.log('There was an error when broadcasting this transaction to the network.');
    break;
  }
});

Get information about a specific block.

Example Response

{
  "hash": "0xc1668a114ee680597196ed402a0e0507fd8348e6090a54250d7accfadbd74b6e",
  "size": 686,
  "version": 0,
  "previousblockhash": "0xbae289c94e17ae90022673186fd6e1e48b7dd7afb89319bff0e2832db06d16b3",
  "merkleroot": "0x07d70f7337d3869a7daa538425d78a47212fb8c6130d66d84ac48526853a4e51",
  "time": 1557376153,
  "index": 2619690,
  "nonce": "8efd62ebb85ee68b",
  "nextconsensus": "AWZo4qAxhT8fwKL93QATSjCYCgHmCY1XLB",
  "script": {
    "invocation": "402a1dab9e5593d1d7d2a22a36772d4541b8053d33f8b8474b7d5a20066c1bd821e051fc252ed16146930d55ecb17fbb74972fba4c4b27af81a707999ca1313dd2401520eba2dd3b54a74a798cbb716c484ba6f6f21218f099e3d622a0fbd15989f38f9b0b344daf9b89175055d3a92f49df65118e8598735d651bedd4f1811baeb140e6491c03f3057f404d2fe7db50e40e82ade405a9dc7fccd81f4ba0b499a4a29f8570d631b8d40c5995b17d9391fe9ff8c73f28a4e1eb922b7a1ce9d1a5dc0448402cfcdede54828875d45402120aa2d8f78c7bd40df5e5d3b1873fd7e4d03672ebd0904f90c90fa519c623968f55550ae55374de66dc0db9c9d865c593bb95be5640214db0cd3cea6f4ad866df4129d482b89583805d1bdb08ce8399881e70351778a3e4a4093cf69aa7b99b83347fbfd38d85ff45d6a78ca2ab8cacffbfbc8c2d16",
    "verification": "5521030ef96257401b803da5dd201233e2be828795672b775dd674d69df83f7aec1e36210327da12b5c40200e9f65569476bbff2218da4f32548ff43b6387ec1416a231ee821025bdf3f181f53e9696227843950deb72dcd374ded17c057159513c3d0abe20b64210266b588e350ab63b850e55dbfed0feeda44410a30966341b371014b803a15af0721026ce35b29147ad09e4afe4ec4a7319095f08198fa8babbe3c56e970b143528d222103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c12103fd95a9cb3098e6447d0de9f76cc97fd5e36830f9c7044457c15a0e81316bf28f57ae"
  },
  "tx": [
    {
      "txid": "0x07d70f7337d3869a7daa538425d78a47212fb8c6130d66d84ac48526853a4e51",
      "size": 10,
      "type": "MinerTransaction",
      "version": 0,
      "attributes": [],
      "vin": [],
      "vout": [],
      "sys_fee": "0",
      "net_fee": "0",
      "scripts": [],
      "nonce": 3093227147
    }
  ],
  "confirmations": 70,
  "nextblockhash": "0x2c9d6a107b21e83e09dd1b89df344a726895147d410120c46996290692ba29aa"
}

Execute a contract invocation in read-only mode.

Input Arguments
Parameter Type Description
blockHeight integer The height of the block you would like to get information about.
network String Network alias to submit this request to. If omitted, will default the network which the wallet is set to
Success Response

The wallet will return the direct response from the RPC node.

Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getBlockHeight

neoDapi.getBlockHeight({
  network: 'TestNet'
})
.then((res: {result: number}) => {
  console.log('Block height: ' + res.result);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
   case RPC_ERROR:
    console.log('There was an error when broadcasting this transaction to the network.');
    break;
  }
});

Get the height of the current block.

Example Response

{
  "result": 2619690
}

Execute a contract invocation in read-only mode.

Input Arguments
Parameter Type Description
network String Network alias to submit this request to. If omitted, will default the network which the wallet is set to
Success Response
Parameter Type Description
result Number Height of the current block
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getTransaction

neoDapi.getTransaction({
  txid: '7e049fd7c253dabf38e4156df30c78b30d49f307196aa89b99a47d2330789bf2',
  network: 'TestNet'
})
.then((result: Object) => {
  console.log('Transaction details: ' + JSON.stringify(result));
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
   case RPC_ERROR:
    console.log('There was an error when broadcasting this transaction to the network.');
    break;
  }
});

Get information about a specific transaction.

Example Response

{
  "txid": "0x7e049fd7c253dabf38e4156df30c78b30d49f307196aa89b99a47d2330789bf2",
  "size": 556,
  "type": "InvocationTransaction",
  "version": 1,
  "attributes": [
    {
      "usage": "Script",
      "data": "296ac124021a71c449a9bad320c16429b08ad6ee"
    },
    {
      "usage": "Remark",
      "data": "cbb549adec34d741"
    }
  ],
  "vin": [],
  "vout": [],
  "sys_fee": "0",
  "net_fee": "0",
  "scripts": [
    {
      "invocation": "4072b83e8aca62c27dc36b032b895e757db00620384e26f43cd0ecc9904bff1e652dd94a03226d6dcb0b6f91104cb40be6455aa0fc3b474a8a8e5fa43ff4b10b8d40af726dc0976f15cd8a134634074c5613ab1e59979fec37b611392975c92afa11038fd9d96ddfb306df12ae200dc3c15fa17cb9530389e28f090fd8c9721c3307",
      "verification": "53c56b6c766b00527ac46c766b51527ac4616c766b00c36121022949376faacb0c6783da8ab63548926cb3a2e8d786063a449833f927fa8853f0ac642f006c766b51c361210292a25f5f0772d73d3fb50d42bb3cb443505b15e106789d19efa4d09c5ddca756ac635f006c766b00c361210292a25f5f0772d73d3fb50d42bb3cb443505b15e106789d19efa4d09c5ddca756ac642f006c766b51c36121022949376faacb0c6783da8ab63548926cb3a2e8d786063a449833f927fa8853f0ac62040000620400516c766b52527ac46203006c766b52c3616c7566"
    }
  ],
  "script": "0400e1f505147869ef9732cdf6f6d54adaa5cae3b55a9396bceb14296ac124021a71c449a9bad320c16429b08ad6ee53c1087472616e7366657267f1dfcf0051ec48ec95c8d0569e0b95075d099d84f10400e1f50514b1fdddf658ce5ff9f83e66ede2f333ecfcc0463e14296ac124021a71c449a9bad320c16429b08ad6ee53c1087472616e7366657267f1dfcf0051ec48ec95c8d0569e0b95075d099d84f1",
  "gas": "0",
  "blockhash": "0x4ea57fe267a392933d2b03fa733fbf1fa12c13f7e8ae2051e45465800e1a7cdb",
  "confirmations": 9,
  "blocktime": 1557377749
}

Execute a contract invocation in read-only mode.

Input Arguments
Parameter Type Description
txid String The id of the transaction you would like to get information about.
network String Network alias to submit this request to. If omitted, will default the network which the wallet is set to
Success Response

The wallet will return the direct response from the RPC node.

Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

getApplicationLog

neoDapi.getApplicationLog({
  txid: '7e049fd7c253dabf38e4156df30c78b30d49f307196aa89b99a47d2330789bf2',
  network: 'TestNet'
})
.then((result: Object) => {
  console.log('Application log of transaction execution: ' + JSON.stringify(result));
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
   case RPC_ERROR:
    console.log('There was an error when broadcasting this transaction to the network.');
    break;
  }
});

Get the application log for a given transaction.

Example Response

{
  "txid": "0x7e049fd7c253dabf38e4156df30c78b30d49f307196aa89b99a47d2330789bf2",
  "executions": [
    {
      "trigger": "Application",
      "contract": "0x72985e7f2cea98b89af54d8607bc6400814c4b45",
      "vmstate": "HALT",
      "gas_consumed": "5.292",
      "stack": [],
      "notifications": [
        {
          "contract": "0x849d095d07950b9e56d0c895ec48ec5100cfdff1",
          "state": {
            "type": "Array",
            "value": [
              {
                "type": "ByteArray",
                "value": "7472616e73666572"
              },
              {
                "type": "ByteArray",
                "value": "296ac124021a71c449a9bad320c16429b08ad6ee"
              },
              {
                "type": "ByteArray",
                "value": "7869ef9732cdf6f6d54adaa5cae3b55a9396bceb"
              },
              {
                "type": "ByteArray",
                "value": "00e1f505"
              }
            ]
          }
        },
        {
          "contract": "0x849d095d07950b9e56d0c895ec48ec5100cfdff1",
          "state": {
            "type": "Array",
            "value": [
              {
                "type": "ByteArray",
                "value": "7472616e73666572"
              },
              {
                "type": "ByteArray",
                "value": "296ac124021a71c449a9bad320c16429b08ad6ee"
              },
              {
                "type": "ByteArray",
                "value": "b1fdddf658ce5ff9f83e66ede2f333ecfcc0463e"
              },
              {
                "type": "ByteArray",
                "value": "00e1f505"
              }
            ]
          }
        }
      ]
    }
  ]
}

Execute a contract invocation in read-only mode.

Input Arguments
Parameter Type Description
txid String The id of the transaction you would like to get the application logs for.
network String Network alias to submit this request to. If omitted, will default the network which the wallet is set to
Success Response

The wallet will return the direct response from the RPC node.

Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

Write Methods

Write methods will alter the state on the blockchain, and require a user signature.

send

neoDapi.send({
  fromAddress: 'ATaWxfUAiBcQixNPq6TvKHEHPQum9bx79d',
  toAddress: 'ATaWxfUAiBcQixNPq6TvKHEHPQum9bx79d',
  asset: 'GAS',
  amount: '0.0001',
  remark: 'Hash puppy clothing purchase. Invoice#abc123',
  fee: '0.0001',
  network: 'MainNet',
  broadcastOverride: false,
})
.then(({txid, nodeUrl}: SendOutput) => {
  console.log('Send transaction success!');
  console.log('Transaction ID: ' + txid);
  console.log('RPC node URL: ' + nodeUrl);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case SEND_ERROR:
      console.log('There was an error when broadcasting this transaction to the network.');
      break;
    case MALFORMED_INPUT:
      console.log('The receiver address provided is not valid.');
      break;
    case CANCELED:
      console.log('The user has canceled this transaction.');
      break;
    case INSUFFICIENT_FUNDS:
      console.log('The user has insufficient funds to execute this transaction.');
      break;
  }
});

Example Response

{
  txid: 'ed54fb38dff371be6e3f96e4880405758c07fe6dd1295eb136fe15f311e9ff77',
  nodeUrl: 'http://seed7.ngd.network:10332',
}

The send API can be used for accepting payments from the user in a cryptocurrency that is located on the NEO blockchain. It requires user authentication in order for the transaction to be relayed. The transaction will be relayed by the wallet.

Input Arguments
Parameter Type Description
fromAddress String The address from where the transaction is being sent. This will be the same value as the one received from the getAccount API
toAddress String The address to where the user should send their funds
asset String The asset which is being requested for payment...e.g NEP5 scripHash, GAS or CGAS
amount String The amount which is being requested for payment
remark String? A transaction attribute remark which may be placed in the transaction, this data will appear in the transaction record on the blockchain
fee String? If a fee is specified then the wallet SHOULD NOT override it, if a fee is not specified the wallet SHOULD allow the user to attach an optional fee
network String Network alias to submit this request to.
broadcastOverride Boolean? If this flag is set to True, the wallet provider will return the signed transaction rather than broadcasting to a node.
Success Response

In the case where the "broadcastOverride" input argument is not set, or set to false.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
nodeURL String The node to which the transaction was submitted to

In the case where the "broadcastOverride" input argument is set to True.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
signedTx String The serialized signed transaction
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

invoke

neoDapi.invoke({
  scriptHash: '505663a29d83663a838eee091249abd167e928f5',
  operation: 'storeData',
  args: [
    {
      type: neoDapi.Constants.ArgumentDataType.STRING,
      value: 'hello'
    }
  ],
  attachedAssets: {
    NEO: '100',
    GAS: '0.0001',
  },
  fee: '0.001',
  network: 'TestNet',
  broadcastOverride: false,
  txHashAttributes: [
    {
      type: 'Boolean',
      value: true,
      txAttrUsage: 'Hash1'
    }
  ]
})
.then(({txid, nodeUrl}: InvokeOutput) => {
  console.log('Invoke transaction success!');
  console.log('Transaction ID: ' + txid);
  console.log('RPC node URL: ' + nodeUrl);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case RPC_ERROR:
      console.log('There was an error when broadcasting this transaction to the network.');
      break;
    case CANCELED:
      console.log('The user has canceled this transaction.');
      break;
  }
});

Example Response

{
  txid: 'ed54fb38dff371be6e3f96e4880405758c07fe6dd1295eb136fe15f311e9ff77',
  nodeUrl: 'http://seed7.ngd.network:10332',
}:

Invoke allows for the generic execution of smart contracts on behalf of the user. It is recommended to have a general understanding of the NEO blockchain, and to be able successfully use all other commands listed previously in this document before attempting a generic contract execution.

Input arguments
Parameter Type Description
scriptHash String The script hash of the contract that you wish to invoke
operation String The operation on the smart contract that you wish to call. This can be fetched from the contract ABI
args Argument[] A list of arguments necessary to perform on the operation you wish to call
fee String? If a fee is specified then the wallet SHOULD NOT override it, if a fee is not specified the wallet SHOULD allow the user to attach an optional fee
network String Network alias to submit this request to.
attachedAssets AttachedAssets? Describes the assets to attach with the smart contract, e.g. attaching assets to mint tokens during a token sale
assetIntentOverrides AssetIntentOverrides Used to specify the exact UTXO's to use for attached assets. If this is provided fee and attachedAssets will be ignored
triggerContractVerification Boolean? Adds the instruction to invoke the contract verifican trigger
broadcastOverride Boolean? If this flag is set to True, the wallet provider will return the signed transaction rather than broadcasting to a node.
txHashAttributes TxHashAttribute[]? Optional list of tx attribute hash values to be added
Argument
Parameter Type Description
type String The type of the argument with you are using
value String String representation of the argument which you are using
TxHashAttribute
Parameter Type Description
type String The type of the argument with you are using
value String String representation of the argument which you are using
txAttrUsage String Attribute usage value
AttachedAssets
Parameter Type Description
NEO String? The amount of NEO to attach to the contract invocation
GAS String? The amount of GAS to attach to the contract invocation
AssetIntentOverrides
Parameter Type Description
inputs AssetInput[] A list of UTXO inputs to use for this transaction
outputs AssetOutput[] A list of UTXO outputs to use for this transaction
AssetInput
Parameter Type Description
txid String Transaction id to be used as input
index String Index of the UTXO, can be found from transaction details
AssetOutput
Parameter Type Description
asset String Asset of the UTXO
address String Address to receive the UTXO
value String String representation of double or integer value to be used as output
Success Response

In the case where the "broadcastOverride" input argument is not set, or set to false.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
nodeURL String The node to which the transaction was submitted to

In the case where the "broadcastOverride" input argument is set to True.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
signedTx String The serialized signed transaction
Set script transaction attribute 0x20 according to the following conditions:
  • If triggerContractVerification is set to true, set 0x20 to scriptHash of the contract being invoked
  • If there is no fee, attachedAssets, or 'assetIntentOverrides', set 0x20 to the users address
  • If there are assetIntentOverrides but none of the inputs belong to the user address, set 0x20 to user address
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

invokeMulti

neoDapi.invokeMulti({
  invokeArgs: [
    {
      scriptHash: '505663a29d83663a838eee091249abd167e928f5',
      operation: 'storeData',
      args: [
        {
          type: neoDapi.Constants.ArgumentDataType.STRING,
          value: 'hello'
        }
      ],
      attachedAssets: {
        NEO: '100',
        GAS: '0.0001',
      },
      triggerContractVerification: true,
    },
    {
      scriptHash: '505663a29d83663a838eee091249abd167e928f5',
      operation: 'purchaseTicket',
      args: [
        {
          type: neoDapi.Constants.ArgumentDataType.INTEGER,
          value: '10'
        }
      ],
    }
  ],
  fee: '0.001',
  network: 'TestNet',
  broadcastOverride: false,
  txHashAttributes: [
    {
      type: neoDapi.Constants.ArgumentDataType.BOOLEAN,
      value: true,
      txAttrUsage: 'Hash1'
    }
  ]
})
.then(({txid, nodeUrl}: InvokeOutput) => {
  console.log('Invoke transaction success!');
  console.log('Transaction ID: ' + txid);
  console.log('RPC node URL: ' + nodeUrl);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case NO_PROVIDER:
      console.log('No provider available.');
      break;
    case RPC_ERROR:
      console.log('There was an error when broadcasting this transaction to the network.');
      break;
    case CANCELED:
      console.log('The user has canceled this transaction.');
      break;
  }
});

Example Response

{
  txid: 'ed54fb38dff371be6e3f96e4880405758c07fe6dd1295eb136fe15f311e9ff77',
  nodeUrl: 'http://seed7.ngd.network:10332',
}:

Invoke Multi functions the same as Invoke, but accepts inputs to execute multiple invokes in the same transaction.

Input arguments
Parameter Type Description
fee String? If a fee is specified then the wallet SHOULD NOT override it, if a fee is not specified the wallet SHOULD allow the user to attach an optional fee
network String Network alias to submit this request to.
assetIntentOverrides AssetIntentOverrides Used to specify the exact UTXO's to use for attached assets. If this is provided fee and attachedAssets will be ignored
invokeArgs InvokeArguments[] Array of contract invoke inputs
broadcastOverride Boolean? If this flag is set to True, the wallet provider will return the signed transaction rather than broadcasting to a node.
txHashAttributes TxHashAttribute[]? Optional list of tx attribute hash values to be added
InvokeArguments
Parameter Type Description
scriptHash String The script hash of the contract that you wish to invoke
operation String The operation on the smart contract that you wish to call. This can be fetched from the contract ABI
args Argument[] A list of arguments necessary to perform on the operation you wish to call
attachedAssets AttachedAssets? Describes the assets to attach with the smart contract, e.g. attaching assets to mint tokens during a token sale
triggerContractVerification Boolean? Adds the instruction to invoke the contract verifican trigger
Argument
Parameter Type Description
type String The type of the argument with you are using
value String String representation of the argument which you are using
TxHashAttribute
Parameter Type Description
type String The type of the argument with you are using
value String String representation of the argument which you are using
txAttrUsage String Attribute usage value
AttachedAssets
Parameter Type Description
NEO String? The amount of NEO to attach to the contract invocation
GAS String? The amount of GAS to attach to the contract invocation
AssetIntentOverrides
Parameter Type Description
inputs AssetInput[] A list of UTXO inputs to use for this transaction
outputs AssetOutput[] A list of UTXO outputs to use for this transaction
AssetInput
Parameter Type Description
txid String Transaction id to be used as input
index String Index of the UTXO, can be found from transaction details
AssetOutput
Parameter Type Description
asset String A list of UTXO inputs to use for this transaction
address String A list of UTXO outputs to use for this transaction
value String String representation of double or integer value to be used as output
Success Response

In the case where the "broadcastOverride" input argument is not set, or set to false.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
nodeURL String The node to which the transaction was submitted to

In the case where the "broadcastOverride" input argument is set to True.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
signedTx String The serialized signed transaction
Set script transaction attribute 0x20 according to the following conditions:
  • If triggerContractVerification is set to true, set 0x20 to scriptHash of the contract being invoked
  • If there is no fee, attachedAssets, or 'assetIntentOverrides', set 0x20 to the users address
  • If there are assetIntentOverrides but none of the inputs belong to the user address, set 0x20 to user address
Error Response
Parameter Type Description
type String The type of error which has occured
description String? A description of the error which has occured
data String? Any raw data associated with the error

signMessage

neoDapi.signMessage({
  message: 'Hello World!',
})
.then((signedMessage: SignedMessage) => {
  const {
    publicKey,
    message,
    salt,
    data,
  } = signedMessage;

  console.log('Public key used to sign:', publicKey);
  console.log('Original message:', message);
  console.log('Salt added to message:', salt);
  console.log('Signed data:', data);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case UNKNOWN_ERROR:
      console.log(description);
      break;
  }
});

Example Response

{
  publicKey: '0241392007396d6ef96159f047967c5a61f1af0871ecf9dc82afbedb68afbb949a',
  data: '0147fb89d0999e9d8a90edacfa26152fe695ec8b3770dcad522048297ab903822e12472364e254ff2e088fc3ebb641cc24722c563ff679bb1d1623d08bd5863d0d',
  salt: '058b9e03e7154e4db1e489c99256b7fa',
  message: 'Hello World!',
}

Signs a provided messaged with an account selected by user. A salt prefix is added to the input string, and provided as a part of the data while signing. In the example, the signed value would be 058b9e03e7154e4db1e489c99256b7faHello World!.

Input Arguments
Parameter Type Description
message String The message to sign
Success Response
Parameter Type Description
publicKey String The public key used to sign message
data String The signed data
salt String The salt prefix added to the original message before signing
message String The original message
Error Response
Parameter Type Description
type String The type of error which has occurred
description String A description of the error which has occurred
data String? Any raw data associated with the error

deploy

neoDapi.deploy({
  network: 'PrivateNet',
  name: 'Hello world!',
  version: 'v0.0.1',
  author: 'John Smith',
  email: 'info@o3.network',
  description: 'My first contract.',
  needsStorage: true,
  dynamicInvoke: false,
  isPayable: false,
  parameterList: '0710',
  returnType: '05',
  code: '53c56b0d57616b652075702c204e454f21680f4e656f2e52756e74696d652e4c6f6761006c7566',
  networkFee: '0.001',
})
.then(({txid, nodeUrl}: InvokeOutput) => {
  console.log('Deploy transaction success!');
  console.log('Transaction ID: ' + txid);
  console.log('RPC node URL: ' + nodeUrl);
})
.catch(({type: string, description: string, data: any}) => {
  switch(type) {
    case UNKNOWN_ERROR:
      console.log(description);
      break;
  }
});

Example Response

{
  txid: 'ed54fb38dff371be6e3f96e4880405758c07fe6dd1295eb136fe15f311e9ff77',
  nodeUrl: 'http://seed7.ngd.network:10332',
}:

Will deploy a compiled smart contract to the blockchain with the provided input parameters. The GAS cost for deploying the contract will be calculated by the provider, and displayed to the user upon tx acceptance or rejection.

Input Arguments
Parameter Type Description
network String Network alias to submit this request to.
name String The name of the contract to be deployed
version String The version of the contract to be deployed
author String The author of the contract to be deployed
email String The email of the contract to be deployed
description String The description of the contract to be deployed
needsStorage Boolean Whether or not the contract will use storage
dynamicInvoke Boolean Whether or not the contract will be performing dynamic invocations of other smart contracts
isPayable Boolean Whether or not the contract will be able to accept native assets
parameterList String The list of input argument types for the Main function on the contract. https://docs.neo.org/en-us/sc/Parameter.html
returnType String The list of output returnType argument types. https://docs.neo.org/en-us/sc/Parameter.html
code String The hex of the compiled smart contract avm
netowrkFee String The network fee to execute the transaction, in addition to the deploy fee which will be added automatically
broadcastOverride Boolean? If this flag is set to True, the wallet provider will return the signed transaction rather than broadcasting to a node.
Success Response

In the case where the "broadcastOverride" input argument is not set, or set to false.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
nodeURL String The node to which the transaction was submitted to

In the case where the "broadcastOverride" input argument is set to True.

Parameter Type Description
txid String The transaction id of the send request which can be queried on the blockchain
signedTx String The serialized signed transaction
Error Response
Parameter Type Description
type String The type of error which has occurred
description String A description of the error which has occurred
data String? Any raw data associated with the error

Event Methods

addEventListener

neoDapi.addEventListener(neoDapi.Constants.EventName.ACCOUNT_CHANGED, data => {
  console.log(`Connected Account: ${data.address}`);
});

Method is used to add a callback method to be triggered on a specified event.

removeEventListener

neoDapi.removeEventListener(neoDapi.Constants.EventName.ACCOUNT_CHANGED);

Method is to remove existing callback event listeners.

Events

Events are a way for the wallet to asynchronously with the DAPP when certain changes occur to the state of the wallet that might be relevant for the

READY

On a READY event, the callback will fire with a single argument with information about the wallet provider. At any time a READY event listener is added, it will immidiately be called if the provider is already in a ready state. This provides a single flow for dapp developers since this listener should start any and all interactions with the dapi protocol.

Parameter Type Description
name String The name of the wallet provider
website String The website of the wallet provider
version String The version of the dAPI that the the wallet supports
compatibility String[] A list of all applicable NEPs which the wallet provider supports
extra Object Provider specific attributes
extra
Parameter Type Description
theme string UI theme of the provider

ACCOUNT_CHANGED

On a ACCOUNT_CHANGED event, the callback will fire with a single argument of the new account. This occurs when an account is already connected to the dapp, and the user has changed the connected account from the dapi provider side.

Parameter Type Description
address String Address of the new account
label String A label the users has set to identify their wallet

CONNECTED

On a CONNECTED event, the user has approved the connection of the dapp with one of their accounts. This will fire the first time any of one of the following methods are called from the dapp: getAccount, invoke, send.

Parameter Type Description
address String Address of the new account
label String A label the users has set to identify their wallet

DISCONNECTED

On a DISCONNECTED event, the account connected to the dapp via the dapi provider has been disconnected (logged out).

NETWORK_CHANGED

On a NETWORK_CHANGED event, the user has changed the network their provider wallet is connected to. The event will return the updated network details.

Parameter Type Description
networks String[] A list of all networks which this wallet provider allows access to
defaultNetwork String Network the wallet is currently set to

BLOCK_HEIGHT_CHANGED

On a BLOCK_HEIGHT_CHANGED event, the block has advanced to the next.

Parameter Type Description
network String Network of the block which changed
blockHeight Number Height of the new block
blockTime Number Timestamp of the new block
blockHash String Hash of the new block
tx String[] List of transaction ids executed in the new block

TRANSACTION_CONFIRMED

On a TRANSACTION_CONFIRMED event, a previously broadcast transaction via the dapi has been confirmed by the blockchain.

Parameter Type Description
txid String Transaction id which was confirmed on chain
blockHeight Number Height of the new block
blockTime Number Timestamp of the new block

Errors

The NEO dAPI will provide these basic errors. It is up to the wallet provider to provide additional information if they choose:

Error Type Meaning
NO_PROVIDER Could not find an instance of the dAPI in the webpage
CONNECTION_DENIED The dAPI provider refused to process this request
RPC_ERROR An RPC error occured when submitting the request
MALFORMED_INPUT An input such as the address is not a valid NEO address
CANCELED The user cancels, or refuses the dapps request
INSUFFICIENT_FUNDS The user does not have a sufficient balance to perform the requested action

Utils

These are a collection of commonly used utilities for parsing responses from smart contracts.

hex2str

const hex2strInput = '68656c6c6f';
const hex2strExpected = 'hello';

const hex2strResult = neoDapi.utils.hex2str(hex2strInput);

console.log('hex2str', hex2strExpected === hex2strResult);

Converts a hex string to a string.

str2hex

const str2hexInput = 'hello';
const str2hexExpected = '68656c6c6f';

const str2hexResult = neoDapi.utils.str2hex(str2hexInput);

console.log('str2hex', str2hexExpected === str2hexResult);

Converts a string to a hex string.

hex2int

const hex2intInput = '00e1f505';
const hex2intExpected = 100000000;

const hex2intResult = neoDapi.utils.hex2int(hex2intInput);

console.log('hex2int', hex2intExpected === hex2intResult);

Converts a hex string to an integer.

int2hex

const int2hexInput = 100000000;
const int2hexExpected = '00e1f505';

const int2hexResult = neoDapi.utils.int2hex(int2hexInput);

console.log('int2hex', int2hexExpected === int2hexResult);

Converts an integer to a hex string.

reverseHex

const reverseHexInput = 'bc99b2a477e28581b2fd04249ba27599ebd736d3';
const reverseHexExpected = 'd336d7eb9975a29b2404fdb28185e277a4b299bc';
const reverseHexResult = neoDapi.utils.reverseHex(reverseHexInput);

console.log('reverseHex', reverseHexExpected === reverseHexResult);

Converts the endian of a hex string, big to little, or little to big.

address2scriptHash

const address2scriptHashInput = 'Ab2fvZdmnM4HwDgVbdBrbTLz1wK5TcEyhU';
const address2scriptHashExpected = 'd336d7eb9975a29b2404fdb28185e277a4b299bc';

const address2scriptHashResult = neoDapi.utils.address2scriptHash(address2scriptHashInput);

console.log('address2scriptHash', address2scriptHashExpected === address2scriptHashResult);

Converts an address to a script hash.

scriptHash2address

const scriptHash2addressInput = 'd336d7eb9975a29b2404fdb28185e277a4b299bc';
const scriptHash2addressExpected = 'Ab2fvZdmnM4HwDgVbdBrbTLz1wK5TcEyhU';

const scriptHash2addressResult = neoDapi.utils.scriptHash2address(scriptHash2addressInput);

console.log('scriptHash2address', scriptHash2addressExpected === scriptHash2addressResult);

Converts a script hash to an address.