Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
WebSocket API is a technology that makes it possible to open a communication session between the nodes. Normally you would use the Core library (ocore) or Web library (obyte.js) to make these calls.
Loading...
Loading...
Obyte has a payment gateway, Woocommerce plugin and cashback program for merchants who would like to accept Bytes for goods or services.
Loading...
Loading...
JSON-RPC is a remote procedure call protocol encoded in JSON. It allows multiple calls to be sent to the server which may be answered out of order.
Loading...
Loading...
Loading...
If you run project you will see a blog with articles. After opening any article you will see a request to log in. Let’s create an authorization. First we need a pairing code, you could see it on startup
Copy it and change our code a bit
Run and see the link. Сlick on it and bot will be added. Now let’s look at the console.
Add the link
And QR
Save our device address, we will need it later.
Now we need to tell the browser that we are logged in To implement this, we add function in start.js
And also one more routing
In article.html we will add a request to check
What’s going on here? The user scans the QR code, adds our bot. Bot associates code with his device_address. In the browser, we knock and ask: did we logged in? and adding cookies.
Now we need to create an address and associate it with the user and the article. Add a variable
Add the address and display it
And finally the function getAssocAddress
This time we will not wait for the stabilization of the unit and immediately open access. Add a variable
Add a check that the user has paid for
Add a routing
And payment processing
Hey guys! Today we will learn how to log in using a Obyte wallet, and also make paid access to the article. To do this, we need project that i have prepared for you . Install it.
That’s all, make a working second article - will be your homework:) You should get this:
All the code you can find on . Did you like the lesson? Any questions? What topics have caused you difficulties and it is worth talking about them? Write in the comments and I will help you. Also join my telegram chat - .
These clauses authenticate the author(s) of the unit.
Here is an example of the simplest address definition that defines an address controlled by a single private key:
The pubkey above is base64-encoded public key. The sig
expression evaluates to true
if the signature provided with the transaction is valid and produced by the private key that corresponds to the above public key. The address (checksummed hash in base32) corresponding to this definition is A2WWHN7755YZVMXCBLMFWRSLKSZJN3FU.
This clause evaluates to true
if the author supplies a preimage of the hash specified in the address definition.
These clauses allow to combine other conditions with logical operators.
All expressions in this language evaluate to a boolean value, and multiple boolean subexpressions can be combined using boolean operators and
and or
. For example, this is a definition that requires two signatures:
To spend funds from the address equal to the hash of the above definition, one would need to provide two signatures.
As you noticed, we use JSON to construct the language expressions. This is an unusual choice but allows to use existing well-debugged, well-supported, and well-optimized JSON parsers rather than invent our own.
“Or” condition can be used to require signatures by any one of the listed public keys:
The above is useful when you want to control the same address from any of the 3 devices: your laptop, your phone, and your tablet.
The conditions can be nested:
A definition can require a minimum number of conditions to be true out of a larger set, for example, a 2-of-3 signature:
(“r” stands for “required”) which features both the security of two mandatory signatures and the reliability, so that in case one of the keys is lost, the address is still usable and can be used to change its definition and replace the lost 3rd key with a new one, or to move the funds to another address.
Also, different conditions can be given different weights, of which a minimum is required:
Subsequent conditions can be negated with not
clause:
Since it is legal to select very old parents (that didn’t see the newer data feed posts), one usually combines negative conditions such as the above with the requirement that the timestamp is after a certain date.
sig
, hash
, address
, cosigned by
, and in merkle
cannot be negated.
These clauses redirect the evaluation of the subdefinition to something else.
A definition can contain reference to another address using address
clause:
which delegates signing to another address and is useful for building shared control addresses (addresses controlled by several users in contracts). This syntax gives the users the flexibility to change definitions of their own component addresses whenever they like, without bothering the other user.
A definition can reference a definition template:
The parameters specify values of variables to be replaced in the template. The template needs to be saved before (and as usual, be stable before use) with a special message type app=“definition_template”, the template itself is in message payload, and the template looks like normal definition but may include references to variables in the syntax @param1, @param2. Definition templates enable code reuse. They may in turn reference other templates.
These clauses inspect data outside of the current unit.
in data feed
clause can be used to make queries about data previously stored on Obyte:
This condition evaluates to true
if there is at least one previous message stored in Obyte database that has “data feed name” equal to “expected value”. Instead of =
, you can also use >
, <
, >=
, <=
, or !=
The data feed must be posted to Obyte decentralized database by one of the oracles whose addresses are “ADDRESS1”, “ADDRESS2”, … Since oracles post to the common database, we call them on-chain oracles.
On-chain oracles are a very powerful thing indeed. For example, this address definition represents a binary option:
Initially, the two parties fund the address defined by this definition by sending their respective stakes to the address. Then if the EUR/USD exchange rate published by the exchange address ever exceeds 1.2500, the first party can sweep the funds. If this doesn’t happen before timeout period, the second party can sweep all the funds stored on this address.
Another example would be a customer who buys goods from a merchant but he doesn’t quite trust that merchant and wants his money back in case the goods are not delivered. The customer pays to a shared address defined by:
The definition depends on the FedEx oracle that posts tracking numbers of all successfully delivered shipments. If the shipment is delivered, the merchant will be able to unlock the money using the first condition. If it is not delivered before the specified timeout period, the customer can take his money back. This example is somewhat crazy because it requires FedEx to post each and every shipment. See in merkle
clause below for a more practical way to achieve the same result.
in merkle
is a more economical way to query the presence of a particular data entry in a large data set. Instead of posting every data entry in a data_feed
message, only the merkle root of the large data set is posted as a data_feed
, and the signer has to provide the data entry and its merkle path:
This clause evaluates to true
if the specified address was seen as author in at least one past unit included in the last stable unit.
This clause evaluates to true
if there was an input or output in the past (before last stable unit) that satisfies the specified condition. The syntax for the search condition is the same as for has
clause below.
This clause evaluates to true
if there was a definition change of the specified address and the c-hash (checksummed hash) of the new definition is equal to the specified value.
This clause evaluates to true
if the age of all inputs spent from this address satisfies the specified condition. The age is the difference between last ball mci and the input’s mci.
This clause evaluates to true
if the specified address is attested by one of the listed attestors. The address can also be “this address”.
These clauses inspect data within the current unit
A subdefinition may require that the transaction be cosigned by another address:
A definition can also include queries about the transaction itself, which can be used for example to code limit orders on a trustless exchange. Assume that a user wants to buy 1,200 units of some asset for which he is willing to pay no more than 1,000 bytes (the native currency of Obyte). Also, he is not willing to stay online all the time while he is waiting for a seller. He would rather just post an order at an exchange and let it execute when a matching seller comes along. He can create a limit order by sending 1,000 bytes to an address defined by this definition, which makes use of has
clause:
The first or-alternative lets the user take back his bytes whenever he likes, thus cancelling the order. The second alternative delegates the exchange the right to spend the funds, provided that another output on the same transaction pays at least 1,200 units of the other asset to the user’s address. The exchange would publicly list the order, a seller would find it, compose a transaction that exchanges assets, and sign it together with the exchange. Note that the exchange does not receive arbitrary control over the user’s funds, it can spend them only if it simultaneously pays the alternative asset to the user, while the user retains full control over his funds and can withdraw them from the contract when he likes.
The has
clause evaluates to true
if the transaction has at least one input or output that satisfies all the set conditions:
what
: input
or output
, required
asset
: ID of asset (44-character string) or base
for bytes
type
: for inputs only, transfer
or issue
. If specified, it searches only transfers or only issues
amount_at_least
, amount_at_most
, amount
: amount must be at least, at most, or exactly equal to the specified value
address
: address where the output is sent to or the input is spent from, can be literal address or “this address” or “other address”
Similar clause has one
has exactly the same syntax but evaluates to true if there is exactly one input or output that satisfies the search conditions.
The following requirement can also be included in a subdefinition:
It evaluates to true
if there is at least one pair of inputs or outputs that satisfy the search criteria and the fields specified in equal_fields
are equal. The first element of the pair is searched by the first set of filters, the second by the second, and the syntax for the search conditions is the same as in has
clause.
A similar condition has one equal
requires that there is exactly one such pair.
This clause evaluates to true
if the sum of inputs or outputs that satisfy the filter is equal, at least, or at most the target value.
The syntax for the filter is the same as for has
clause.
This clause evaluates to true
if the unit has a definition change of the specified address and the c-hash (checksummed hash) of the new definition is equal to the specified value.
This clause evaluates to true
if last_ball_mci
of the current unit is greater than (other possible comparisons: >=
, <
, <=
, =
) than the specified value. It can be useful to make the address spendable only after some point in the future and not rely on any timestamp oracles.
This clause evaluates to true
If the time is longer than the specified time. You can also use "=", "<", ">=", "<=", "!="
. This example returns true 2 hours after the deployment.
Obyte provides users with the ability to create Smart Contracts as well as simple prosaic contracts.
Hi, everybody. Today we will create our own oracle. To do this, we need a bot-example. Let’s install it.
Our oracle will accept a message like City: timestamp, where timestamp is the time to publish. Let’s write a message handler
Add a variable to store the queue
Now add to the queue
Now we need to check the queue once a minute
Now we come to the main point. We need to publish data_feed
What is happening?
1) We create an object in data_feed and keep the temperature in it.
2) We send the payment to ourselves as we only need to publish data_feed
3) Create a message with data and signature
4) Publish
One of the core features that almost every existing Obyte chatbot has is sending and receiving payments. This enables bot developers immediately add payment to the service they could be providing with
When you include a valid Obyte address anywhere in the text of your response to the user, the address will be automatically highlighted in the user's chat window, and after clicking it the user will be able to pay arbitrary amount of arbitrary asset to this address.
When you want to request a specific amount of a specific asset, format your payment request this way:
Amount is in the smallest units, such as bytes. If you omit &asset=...
part, base asset (bytes) is assumed. If you want to request payment in another asset, indicate its identifier such as oj8yEksX9Ubq7lLc+p6F2uyHUuynugeVq4+ikT67X6E=
for blackbytes (don't forget to url-encode it).
You will likely want to generate a unique payment address per user, per transaction. This code sample might help:
It is possible to request that the payment would be done from any single address wallet or specific single address wallet. This works only in chat messages.
To request the user to pay multiple payments at once, create the below Javascript object objPaymentRequest
which can contain multiple addresses and multiple assets, encode the object in base64, and send it over to the user:
The user's wallet will parse this message, display the multiple payments in a user-readable form, and offer the user to pay the requested amounts. Your payment-waiting code will be called when the payment is seen on the DAG.
If you include a headless wallet
you can get notified when any of your addresses receives a payment
arrUnits
is an array of units (more accurately, unit hashes) that contained any transaction involving your addresses. The event new_my_transactions
is triggered for outgoing transactions too, you should check if the new transaction credits one of the addresses you are expecting payments to.
To get notified when any of your transactions become stable (confirmed), subscribe to my_transactions_became_stable
event:
arrUnits
is again the array of units that just became stable and they contained at least one transaction involving your addresses.
The above events work in both full and light nodes. If your node is full, you can alternatively subscribe to event mci_became_stable
which is emitted each time a new main chain index (MCI) becomes stable:
To send payments, you need to include a headless wallet
and use this function after the headless wallet becomes ready:
asset
is the asset you are paying in (null
for bytes), amount
is payment amount in the smallest units. If the payment was successful, you get its unit
in the callback and can save it or watch further events on this unit.
Textcoins are a handy way of transfering money and asset to other people, in a text form.
Textcoin is a payment sent though email or other text based media, such as chat apps. Textcoins can also be printed on paper, scratch cards, or PIN envelopes and passed on like paper wallets.
Sending textcoins from server-side (headless) wallets is easy: you use the same functions you normally use to send to Obyte addresses but instead of the recipient's Obyte address you write:
textcoin:userEmailAddress
for sending to email, or
textcoin:someUniqueIdentifierOtherThanEmail
for sending via any other text based media
Examples: textcoin:pandanation@wwfus.org
or textcoin:ASDFGHJKL
.
Sample code:
from_email
: the address your emails are sent from. Must be on your domain, otherwise the emails are likely to be non-delivered or labeled as spam
smtpTransport
: type of SMTP transport used to deliver emails:
local
: (this is the default) use /usr/sbin/sendmail
direct
: connect directly to the recipient's MX server, this minimizes the number of intermediaries who are able to see and potentially steal the textcoin, but this method is not reliable as there are no retries
relay
: send through a relay server. In this case, you need to also set its host name in smtpRelay
. If it requires authentication, you need to also set smtpUser
and smtpPassword
.
The callback of issueChangeAddressAndSendMultiPayment
and all similar functions has an optional 3rd parameter assocMnemonics
which is an associative array of all generated textcoins (BIP39 mnemonics) indexed by the address
from the input parameters.
All the keys of the array have a textcoin:
prefix, like in input parameters.
If you are sending though any media other than email, you use assocMnemonics
to find mnemonics generated for each output address (in the example above, you would find it by the key textcoin:ASDFGHJKL
) and then deliver them yourself.
For example, if you are writing a Telegram bot that rewards users for certain actions, you create a clickable link by prefixing the mnemonic with https://obyte.org/#textcoin?
:
and send it in chat.
Hello, everybody. Today we will learn how to create our first contract. We will do this on the example of a bot that accepts a bet on the weather.
First, let’s create variables.
Save our address for use in the contract
Let’s add a text handler and analyze what happens:
Now let’s talk about contract creation. We need a separate function
Let’s analyze what happens arrSeenConditionPeer - watching if there was a payment in the contract with the amount same of the user. arrSeenConditionMyInput - сhecking, did we withdraw bytes? If yes, then allow the user to pick up their bytes.
arrDefinition - this is what our contract looks like, let’s look at it in details. When we need some address to subscribe we use [‘address’, address] When we need to check the publication data_feed we use [‘in data feed’, [[conf.oracle_address], name, operator, value]]
assocSignersByPath - in this object, we prescribe the path in the array of the contract before you need to sign and who should sign it. In our example, we sign all ‘address’. The path starts with r-this is the first element in the array, in our case ‘or’ construction. Other examples with signatures will be discussed in the following lessons.
walletDefinedByAddresses.createNewSharedAddress - we create a contract address headlessWallet.issueChangeAddressAndSendPayment - we add the address of the contract and make a payment to it
This is how we create a message that contains a contract and a payment request.
Create correspondents.js
We need to tell the oracle to check and publish the weather at the specified time, for this we use this feature.
We check whether there is an oracle in our correspondents and if it is not - we add.
Autonomous agents allow to quickly create decentralized finance applications on a distributed ledger.
They operate based on code that is deployed on the ledger once and can never be changed. The code is open and is executed by all nodes on the network.
Anybody can activate an AA just by sending a transaction to its address. Here is an example of a simple AA:
messages
is a template of the response transaction that will be generated by the AA. It follows the structure of a regular Obyte transaction but its sections between curly braces {} contain code that makes the resulting transaction dependent on the triggering (activating) transaction. This is similar to how PHP code can be inserted into HTML to make the resulting page dependent on the request. The language autonomous agents are written in is called Oscript.
The above example of an AA just sends the received money less 1000 bytes back to the sender. trigger.address
is the address of the sender who activated the AA. trigger.output
allows to find the amounts in different currencies sent to the AA, trigger.output[[asset=base]]
is the amount in the base asset -- bytes.
Another example that sells tokens for bytes at 1.5 tokens per byte:
AA's response can also depend on data it receives in the triggering transaction.
In Obyte, any transaction can contain one or more messages of different types. These types are called apps. The most common app is payment
. At least one payment is mandatory in every transaction, it is necessary to at least pay fees. Another app is data
. This type of message delivers an arbitrary json object that can contain any data:
The object in payload
is the data this message delivers.
When an AA receives a data message (along with a payment message, of course) in the triggering transaction, it can access the data through trigger.data
and its action can depend on the data received:
The above AA tries to send trigger.data.withdrawal_amount
bytes back to the sender. withdrawal_amount
is a field in the data message of the triggering transaction:
In the above example, if the withdrawal_amount
is greater than the balance that the AA has (including the amount received from the triggering transaction), the AA's response will fail. When an AA fails to handle a trigger transaction for any reason, it rolls back all changes and attempts to send back to sender all the received funds, in all assets, less the bounce fees. By default, the bounce fees are 10000 bytes for "base" asset and 0 for all other assets. This is also the minimum. The default can be overridden by adding a bounce_fees
field in the autonomous agent definition:
The above AA will charge 20000 bytes and 100 units of the asset "n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY="
if it has to bounce back a triggering transaction. Sending less than 20000 bytes to the AA will result in the AA silently eating the coins (they are not enough even for the bounce fees). When non-zero amount of asset "n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY="
is sent to the AA, it should also be at least 100, otherwise the AA eats the coins.
As said above, payment
is the most common message but other messages can be sent as well by any address, AA included.
AA can act as an oracle by sending a data feed:
This example shows that object keys can also be parameterized through Oscript, like object values.
The above AA doesn't have a payment message, it will be added automatically just to pay for the fees.
An AA can also send any structured data:
Unlike data_feed
messages, data
messages:
can have any structure with any nesting depth (data feeds are flat key-value pairs);
are not indexed for search;
can be used to pass data to other AAs.
The above AA forwards the received money (less 1000 bytes) to another address, which might be an AA. It also passes data, which includes the initially received data parameters. The value of trigger.data
will be expanded into an object and added as value of initial_data
field.
If the secondary recipient (JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY
) happens to be an AA, its execution will start only after the execution of the current AA is finished. There is no reentrancy issue.
This AA will create a simple text message and store it on the DAG. ||
is the operator for string concatenation.
The above AA creates a definition of a new asset based on parameters passed in data
.
Some syntax elements that we see here for the first time:
!
is a logical NOT
operator;
otherwise
is an operator that returns the first operand if it is truthy (anything except false, 0, and empty string) or the second operand otherwise;
when an object or array value evaluates to an empty string, the corresponding object/array element is removed. This means that if cap
parameter is not passed then cap
in payload
evaluates to an empty string, therefore cap
is removed from asset definition and an asset with infinite supply will be defined;
fields whose value is an empty array/object are also removed. Therefore, if none of the attestor1
, attestor2
, attestor3
fields was set, the attestors
array becomes empty and will be excluded from the final definition.
The AA's behavior can also depend on various state variables that describe the ledger as a whole.
Balance of other AAs (but not regular addresses) can also be queried:
The above AA forwards any received payment (less 1000 bytes) to another AA JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY if its balance is less than 1,000,000 bytes, or returns to sender otherwise.
The above AA pays to 7XZSBB32I5XPIAVWUYCABKO67TZLHKZW
if Charlotte Hornets won and to FDZVVIOJZZVFAP6FLW6GMPDYYHI6W4JG
otherwise TKT4UESIKTTRALRRLWS4SENSTJX6ODCW
is the address of a sports oracle that posts the results of sports events.
Before the data feed is posted, any attempt to evaluate it will fail the AA and bounce the triggering transaction.
Note that the amount
field in the output is omitted, which means that the entire AA balance will be paid out.
The above AA pays to the triggering user 50000 bytes if his address is attested by Steem attestation bot JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725
and his Steem reputation is at least 50. Otherwise, it "pays" 0 bytes. Trying to pay 0 amount results in the output being removed. Since it is the only output in this case and it is removed, the resulting response transaction would produce no effect, therefore it is not created at all.
An autonomous agent can have its own state saved between invocations. Use var
to access and assign state variables:
The above AA saves the address that sent the previous triggering transaction, waits for the next invocation, and sends the received money less 1000 bytes to the previous user.
Assigning state variables is only allowed in the special message with app set to state
. This message must have a single other field called state
which is a set of Oscript statements. These statements are allowed to assign state variables. The state message is not included in the final response transaction, it can only affect state by modifying the state variables.
When an uninitialized var
is accessed, it evaluates to false
.
The above example has an if
field in the first message. When var['previous_address']
is not initialized yet (first call), the if
oscript evaluates to false
and the entire first message is excluded from the transaction.
State variables of other AAs can also be read, but not assigned:
Use local constants to save intermediate results during script execution.
Local constant names are prefixed with $. Unlike state variables, they are not saved between invocations.
To make it easier to argue about a local constant's value, there is one restriction that is not common in other languages: single-assignment rule. A local constant can be assigned a value only once and that value remains constant until the end of the script. An attempt to re-assign a value will fail the script and make the AA bounce.
As we've seen in the State variables example before, some parts of the response transaction generated by the AA can be excluded by adding an if
field. If the Oscript in the if
evaluates to true or any truthy value (anything except false, 0, and empty string), the enclosing object is included in the final response, otherwise, it is excluded.
The if
field itself is of course always removed from the final response.
Along with if
, any object can have an init
field that includes initialization code:
The init code contains only statements, it does not return any value unlike most other Oscripts. The other exception is state message described in State variables above, it is also statements-only. All other Oscripts can contain 0 or more statements but must end with an expression (without a ;
) whose value becomes the value of the Oscript.
Init code is evaluated immediately after if
(if it is present and evaluated to true
of course) and before everything else. It is often used to set local constants that are used later.
Local constants that are set in if
and init
are also available in all other Oscripts of the current and nested objects. In the above example, $amount1
and $amount2
were set in init
of the message object, and they are used in payload/outputs/output-0/amount of this message.
Local constants set in if
are also available in the corresponding init
.
As said above, there are two types of Oscripts: those that contain only statements and those that return a value.
There are only two statements-only Oscripts: init and state script. Here is an example of an init script that we've seen before:
All other Oscripts return a value. They either contain a single expression:
or a set of statements and an expression at the end:
The value of the last expression is the value of the Oscript.
When you need to route the code execution along several mutually exclusive paths depending on input and environment parameters, use cases
:
The regular messages
array is replaced with an object with a single element called cases
. cases
is an array that lists several mutually exclusive alternatives. Every alternative except the last must have an if
field. The first alternative whose if
evaluates to true
defines the messages
that will take the place of the higher-level messages
field.
The conditions in the if
s may not be mutually exclusive but the alternative that is listed earlier in the list takes precedence. Only one alternative is always taken.
In the above example, if trigger.data
has no define
field but has a truthy issue
field, the 2nd alternative is selected, and the original object folds into:
Cases can be nested. Everything said above about if
, init
, and local constants, applies to cases
as well.
The above examples showed how execution can be branched along different parts of json template.
Oscript code itself can be branched as well by using if/else:
If you want to interrupt a script's execution and return from it, with or without a value, use return
.
Here is an example of return
in a return-value Oscript, it must return a value:
A return
in a statements-only Oscript, such as init, doesn't return any value, it just interrupts the script:
If you find an error condition and want to prematurely stop the AA execution and bounce, use bounce
:
or require
:
In this example, timestamp
is roughly the current timestamp (number of seconds since Jan 1, 1970 00:00:00 UTC).
All applied changes will be rolled back and any received coins will be bounced back to the sender less the bounce fees.
_________________
These settings are applicable to most Obyte nodes projects that have 'headless-obyte' or 'ocore' library as dependency.
The default settings are in the core library's conf.js
file, they can be overridden in your project root's conf.js
and then in conf.json
in the app data folder. The app data folder is:
macOS: ~/Library/Application Support/<appname>
Linux: ~/.config/<appname>
Windows: %LOCALAPPDATA%\<appname>
<appname>
is name
in your package.json
, e.g. headless-obyte
.
At some point you'll surely want to have a peek into the database, or even make a modification in it. Default SQLite database file and RocksDB folder are located near the config file, you can find the correct paths for each platform above.
If you don't have .env.testnet
example file then just create a .env
file that contains this row:
This is the list of some of the settings that the library understands (your app can add more settings that only your app understands):
The port to listen on. If you don't want to accept incoming connections at all, set port to null
, which is the default. If you do want to listen, you will usually have a proxy, such as nginx, accept websocket connections on standard port 443 and forward them to your Obyte daemon that listens on port 6611 on the local interface.
To lower disk load and increase sync speed, you can optionally disable flushing to disk every transaction, instead doing it once a second. This can be done by setting innodb_flush_log_at_trx_commit=0
in your MySQL server config file (my.ini)
Work as light node (true
) or full node (false
). The default is full client. Light node only holds data that is relevant to your wallet, full node sync all the data in public database. Benefit of light node is that you can start using it immediately, but the downside is that you might not get updates about new transactions and confirmations as fast as full node does.
Whether to serve as hub on the Obyte network (store and forward e2e-encrypted messages for devices that connect to your hub). The default is false
.
If your node accepts incoming connections, this is its URL. The node will share this URL with all its outgoing peers so that they can reconnect in any direction in the future. By default the node doesn't share its URL even if it accepts connections.
Whether your node wants to learn about new peers from its current peers (true
, the default) or not (false
). Set it to false
to run your node in stealth mode so that only trusted peers can see its IP address (e.g. if you have online wallets on your server and don't want potential attackers to learn its IP).
Settings for connecting through optional SOCKS5 proxy. Use them to connect through TOR and hide your IP address from peers even when making outgoing connections. This is useful and highly recommended when you are running an online wallet on your server and want to make it harder for potential attackers to learn the IP address of the target to attack. Set socksLocalDNS
to false
to route DNS queries through TOR as well.
Most bots out there expect user's machine to have UNIX sendmail and by default sendmail
function in mail
module will try to use that, but it is possible to configure your node to use SMTP relay. This way you could use Gmail or Sendmail SMTP server or even something like Mailtrap.io (excellent for testing purposes if you don't want the actual email recipient to receive your test messages). This is how the configuration would look:
smtpTransport
can take one of three values:
local
: send email using locally installed sendmail
. Normally, sendmail
is not installed by default and when installed, it needs to be properly configured to actually send emails. If you choose this option, no other conf settings are required for email. This is the default option.
direct
: send email by connecting directly to the recipient's SMTP server. This option is not recommended.
relay
: send email through a relay server, like most email apps do. You need to also configure the server's host smtpRelay
, its port smtpPort
if it differs from the default port 25, and smtpUser
and smtpPassword
for authentication to the server.
This is an example configuration for nginx to accept websocket connections at wss://obyte.one/bb and forward them to locally running daemon that listens on port 6611:
If your server doesn't support IPv6, comment or delete the two lines containing [::] or nginx won't start
By default Node limits itself to 1.76GB the RAM it uses. If you accept incoming connections, you will likely reach this limit and get this error after some time:
To prevent this, increase the RAM limit by adding --max_old_space_size=<size>
to the launch command where size is the amount in MB you want to allocate.
For example --max-old-space-size=4096
, if your server has at least 4GB available.
If you are executing your node with node start.js --max-old-space-size=4096
command then you should also change the checkDaemonAndNotify
parameter.
If you are running a wallet-less node (Hub, Relay or Explorer) or password-less headless wallet (conf.bNoPassphrase=true
) then you can restart the node automatically when it stops. In order to do that, you could use checkDaemonAndRestart
function, instead of checkDaemonAndNotify
.
The first parameter in checkDaemonAndRestart
is the process that gets searched from ps x
command response, the second parameter is a command that will get executed when node has stopped.
If you are using NVM to manage your Node.js versions then you might need to add PATH
and LD_LIBRARY_PATH
to your crontab configuration as well, otherwise crontab might not be able to restart the node.
Prosaic contracts are the feature of Obyte platform that helps people to make an agreement on, basically, anything, as text of contracts is a simple text. The benefit of using Obyte prosaic contracts is that after both sides of the contract sign it, a unit, which in fact is a public proof of contract agreement, is posted into DAG, which contains a hash of contract text, while being signed by both peers of the contract. So anyone can check the fact of agreement on the exact same text by those two contract peers. The contract text itself is not revealed but stored in peers wallets.
To offer a prosaic contract from your bot, you have to follow these steps:
user pairs with your bot.
you ask the user to send his payment address (it will be included in the contract) in the chat.
you create a new prosaic contract using the user's address and your address as parties of the contract (also defining contract's title, text and how long the offer is valid).
you send specially formatted chat message with the contract to the user and wait for the response on it ('accept' or 'decline').
the user views the contract in his wallet and agrees or declines it.
on receiving 'accepted' response, you generate new shared address with special definition ('and' between two addresses).
then you top up this shared address to compensate fees.
you initiate posting a 'data' message from this shared address, containing contract text hash. User will then be asked to sign this transaction from the same shared address (as he is a part of its definition).
after getting the user's signature for the 'data' unit, you post it into DAG. Mission accomplished.
We have a special wrapper API for all of these steps in ocore/prosaic_contract_api.js
, which in turn is calling ocore/prosaic_contract.js
methods. For simplicity, here is an example of using *_api.js
, you can check it's code to get a better understanding of what's going on.
callbacks
object consists of four optional callback functions, which called in following sequence:
onOfferCreated
is called right after the contract was created and sent to peer. Always called.
onResponseReceived
is called when peer respond to the contract, supplying the response in accepted
bool argument.
onSigned
indicates that the contract was successfully signed and unit with contract's hash was posted into DAG.
onError
can only happen when handling the response, if your bot did receive any response, either onError
or onSigned
will be called.
If you are developing a bot that makes contract offers, here is an outline of the contract's lifecycle:
Your bot (offerer) receives a user's address in chat from a user.
The bot then sends a contract with arbitration offer to the user. In the offer, it sets the user's address as the other party of the contract, amount of the contract, and an arbiter's address of your choosing. You can also choose the role for your bot in this contract (buyer or seller).
The user reviews the offer in their wallet and accepts the contract.
Your bot receives the user's acceptance, generates a new shared address with smart contract definition, and initiates signing and posting of a special data
unit with the hash of the agreed contract text. The unit is to be signed by the shared address (shared between the bot and the user) and each party's signature means that they agree with the contract's text. Your bot signs first, then waits for the user to sign the transaction.
After the unit has been posted, the buyer side of the contract should pay to this contract's address the full amount that was specified in the contract.
As soon as the funds are locked on the contract, the seller of the goods/services starts fulfilling their obligations.
When all the work is done and the buyer is satisfied with the result, they can release the funds from the contract sending them to the seller's address. Also at any time the seller can refund the full amount back to the buyer. The smart contract ensures that in the absence of any arbiter decision each party can send the contract's funds only to the other party: buyer to the seller to release the funds after the work is done, or seller to the buyer to refund. Either outcome completes the contract.
In case of a disagreement, the user can open a dispute and invoke the arbiter by clicking the corresponding button in their wallet, or your bot can call an API method to open a dispute. The plaintiff has to pay for the arbiter's service before the arbiter starts looking into the case. After the contract status becomes in_dispute
, both parties have to wait for the arbiter to post a resolution unit. After the arbiter has studied the contract and the evidence provided by the parties, they make a decision and post the resolution unit. Then, the winning side can claim the funds from the contract.
Note that in order to open a dispute from the bot, your bot should implement the corresponding API (see below). After you open a dispute, the arbiter will set a fee for their service and the arbstore will send a payment request to your bot. Your bot should be able to handle such requests and decide about paying them, either autonomously, or forward them to a human operator.
Basically, all you need to do is calling the exported methods of arbiter_contract.js
module and reacting to arbiter_contract_*
events on the event bus.
createAndSend(contractObj, callback(contract){})
This method creates a contract from the provided object and sends it to the peer. contractObj
should contain all the fields needed to assemble a new contract offer. The function is then sends the offer to the contractObj.peer_device_address
via chat. The contract
received in the callback function has hash
field, it is the contract's hash which acts as the primary key when referencing contracts.
contractObj
required fields:
title
- contract title, string
text
- contract text, string
arbiter_address
- the address of the picked arbiter, string
amount
- amount in asset
to be paid to the seller, int
asset
- asset in which the payment should be done, can be null
or "base"
for Bytes, or any other asset ID, string
peer_address
- address of the other side of the contract, string
my_address
- your address, string
me_is_payer
- flag specifying if you are the buyer or seller, bool
peer_device_address
- the device address of the other side of the contract, string
ttl
- Time-To-Live, in hours, till which the contract can be accepted, then it expires, float
cosigners
- array of your wallet cosigners that would be used when posting contract signature unit, leave empty if not multi-sig or when you want to use all of the cosigners, array of device addresses
my_contact_info
- free-form text with your contact info so the peer and arbiter can contact you, it is important to note that if you are sending contract offers from a bot, then arbiter can only pair with your bot and not you, therefore you should put your own wallet's pairing code here or other contacts by which you (as a human) can be reached,string
contract
object supplied to your callback will mostly be identical to the contractObj that you provided to this function, but with some additional fields populated, such as hash, creation_date, my_pairing_code.
\
createSharedAddressAndPostUnit(contract_hash, walletInstance, callback(error, contract){})
This method is used to react to the peer accepting your offer. It creates a new shared address with smart contract definition and posts the unit into the DAG. walletInstance
is a reference to wallet that has sendMultiPayment()
method with an appropriate signer inside. For bots, it is usually the main headlessWallet
module. If the error
argument for the callback is null
, it means that the unit was posted successfully, the contract
object is returned with the updated fields.\
pay(contract_hash, walletInstance, arrSigningDeviceAddresses, callback(error, contract, unit){})
This method pays to the contract if you are the buyer side. If you use a multi-signature wallet, then fill the arrSigningDeviceAddresses
array with signing device addresses, otherwise provide an empty array. The unit
argument in the callback function is the unit ID in case the payment succeeded.\
complete(contract_hash, walletInstance, arrSigningDeviceAddresses, callback(error, contract, unit){})
This method sends the funds locked on the contract to the peer. Depending on your role, this action performs either a successful completion (you are a buyer), or a refund (you are the seller).\
respond(contract_hash, status, signedMessageBase64, signer, callback(error, contract){})
openDispute(contract_hash, callback(error, response, contract){})
This function raises a dispute by making an HTTPS request to the contract arbiter's current ArbStore web address. Callback is provided with an error, if any, http response and the updated contract object.\
appeal(contract_hash, callback(error, response, contract){})
This method is similar to the previous one, but is used when you are not satisfied with the arbiter's decision. Also makes an HTTPS request to the ArbStore, but this time calls to ArbStore moderator, who can penalize the arbiter, in case they find the arbiter's decision / actions inappropriate.\
getByHash(contract_hash, callback(contract){})
/ getBySharedAddress(address, callback(contract){})
/ getAllBy*()
These are all similar functions to retrieve one or many contracts by specific criteria. To get a detailed overview of all the methods you can look at exported methods of arbiter_contract.js
module.
Almost every event is supplied with additional arguments that you can use in your event handler functions. For examples, see the code snippet above.
arbiter_contract_offer : (contract_hash)
- this event is fired when core received new contract with arbitration offer from any correspondent.
arbiter_contract_response_received : (contract)
- fired when user responds to your contract offer. To get their response, check the status
field of the contract, it should be either "accepted"
or "declined"
.
arbiter_contract_update : (contract, field, value, unit, winner, isPrivate)
- this is the most common event that you will react to. It is raised in many situations, like:
changed status of a contract: field = "status"
(contract was revoked
, or put in_dispute
, in_appeal
, was paid
, completed
, etc.). This is the full list of the contract statuses: 'pending', 'revoked', 'accepted', 'signed', 'declined', 'paid', 'in_dispute', 'dispute_resolved', 'in_appeal', 'appeal_approved', 'appeal_declined', 'cancelled', 'completed'
unit with contract hash was posted: field = "unit"
Obyte is cryptocurrency platform, which is written with NodeJS. This site is for developers and should help developers to get started using Obyte platform in their projects.
Create a new node.js package for your chatbot: npm init
. You will definitely need modules from ocore
and if you are going to send payments, you will also need headless-obyte
. Your package.json
should list these dependencies:
Now run npm install
to fetch dependencies.
If you want to connect to testnet network instead of spamming mainnet for development then you can do that by adding .env
file, which should contain just one line:
Go ahead and run the code: node index.js
. You'll see initialization lines printed by ocore in console, which contain your wallet first address printed and an error saying that There are no funded addresses
. This is our failed attempt to issueChangeAddressAndSendPayment()
, as you don't have any bytes in your wallet yet.
We wrapped all of our code in 'headless_wallet_ready' event handler, at this point the private keys of your wallet are decrypted and are ready for use.
Find out in console output your first address.
You can deposit some bytes on it to be able to make outbound transactions. Right after ocore is done with initialization (onReady function
), we send one byte to some address. First argument is an asset
, its the asset you are paying in (null
for bytes), amount
is payment amount in the smallest units. If the payment was successful, you get its unit
in the callback and can save it or watch further events on this unit.
The new_my_transactions
and my_transactions_became_stable
event handlers are to be invoked after any new transaction received to any of your wallet addresses and when this transactions become stable, respectively. arrUnits
is an array of units (more accurately, unit hashes) that contained any transaction involving your addresses. The event new_my_transactions
is triggered for outgoing transactions too, you should check if the new transaction credits one of the addresses you are expecting payments to.
The above events work in both full and light nodes. If your node is full, you can alternatively subscribe to event mci_became_stable
which is emitted each time a new main chain index (MCI) becomes stable:
Every Obyte wallet can be paired with any other Obyte wallet, because every wallet has it's unique pairing code. Headless wallet will print it's pairing code to console right after the launch. Depending on what kind of app you want to build, you will or will not need this pairing code. Simplest operations on Obyte platform require no chat bot (like sending/receiving txs, for example).
For more handy interaction with your userbase, you mostly surely need to write a chat bot, to set up direct interaction between your clients Obyte apps and your wallet (this process is called 'pairing'). Chat bot is Obyte wallet which can receive chat messages and react to them. Clients can pair and chat with your chat bot inside their Obyte wallet app, your task is to handle incoming messages and act accordingly. Through chat, users can provide to your chat bot their addresses, KYC profiles, or any other information with couple of clicks.
You can use bot-example
repository as a starting point for your chat bot. You can also use the module from previous step, but it will require you to add some more config lines, so its better to git clone git@github.com:byteball/bot-example.git
, remove .git folder rm -fr bot-example/.git
and tweak some default configs there (remember to give a name to your bot, switch to light node, set up a pairing secret, etc.)
When you start your node, it will print its full pairing code:
The pairing code consists of your node's public key (device public key), hub address, and pairing secret (after #).
Publish this pairing code anywhere as a link with byteball:
scheme, users will be able to open a chat with your bot by clicking your link (the link opens in their Obyte app and starts a chat):
When a user pairs his device with your bot (e.g. by clicking the link to your bot), you receive a pairing notification and can welcome the user:
To receive chat messages, subscribe to 'text' events on event bus:
from_address
is user's device address (not to be confused with payment addresses), user_message
is their message.
Messages to user's device can be sent with sendMessageToDevice
function in device
module:
So, in case we want to echo back what user wrote, we could combine those two above examples into this:
Great success, we now have a bot, which is like a parrot (repeating everything you wrote to it).
To give access to predefined commands, format your responses this way:
Sometimes you might want to suggest the command without actually sending it immediately, so user could have the possibility to edit the command before sending, this could be done like this:
That's it. You've completed your first chat bot on Obyte platform. You can check other chat bot examples provided by our team:
Instructions how to set up headless wallet on a blank Debian machine.
Setting up a Headless wallet is required for those wanting to build their own chat bots. That is, if they want to host the bot on their own server, at least. This guide will take you through the installation in a step by step process but won’t venture into how to build the chat bot itself. There are several possibilities to rent a Virtual Private Server (VPS) and apart from a fairly fast SSD storage there are not a lot of requirements.
Most VPS providers allow you to choose an operating system, and since this guide will be based on Debian 9, you might want to find a provider that offers this as one of their options. Since I will be running a full node, I also must make sure to have enough disk space available and that it is on SSD, since HDD will be too slow. At the time of writing this guide, the required disk space for the full Obyte DAG is about 38 GB but always make sure to have enough or at least the option to add more, should you need it.
1 server (with Debian in this case)
At least 50-70 GB of fast SSD disk space (HDD is too slow for initial syncing)
1 GB RAM (hubs/relays that accept incoming connections might need up to 4GB)
First time you log on, you will see a notice that you have not connected to this host before. Click “Yes” to save the key.
You are now logged on to your server:
Running the headless wallet as root is not recommended, so the first thing I do, is create the user we will run the hub as. I chose the username “obyte”.
adduser obyte
I will be installing all prerequisites as root for this guide. Only the actual headless wallet stuff will be installed from the user we just created. For now, stay logged in as root.
First, we need some basic tools. To make sure we get the newest versions, first run an update of the apt tool:
apt update
Then install following software:
apt-get install -y git curl software-properties-common
To make sure all binaries build properly, we need the build-essentials as well:
apt-get install -y build-essential
Now log on with the user we initially created (obyte
in this example)
su -l obyte
The headless wallet is based on nodejs, and one of the easiest way to control node versions is by the script called “nvm”. Just fire these three commands, and you’re all set (make sure not to miss any of the backticks in the above):
nvm_version=`curl --silent -L https://api.github.com/repos/nvm-sh/nvm/releases/latest | /usr/bin/awk '/tag_name/ { print $2 }' | /bin/sed 's/[",]//g'`
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$nvm_version/install.sh | bash
Then exit user, log in again and install version 10 of nodejs:
exit
su -l obyte
nvm install 10
Build tools for Windows can be installed with following command (you need to run PowerShell or CMD.exe as Administrator to install this):
npm install --global --production windows-build-tools
Copy the URL from there and clone the repository
git clone https://github.com/byteball/headless-obyte.git
It creates a directory called headless-obyte.
cd headless-obyte
Then install it
npm install
In your .config folder, you should create a file called conf.json and edit it. It will contain values that is used by the headless wallet. They override the values that you can find in the file called conf.js located in your headless-obyte folder.
mkdir -p ~/.config/headless-obyte
nano ~/.config/headless-obyte/conf.json
I will briefly explain each of the entries, you should put in the file (if you’re not familiar with the structure of a json object, you could check out sites such as jsoneditoronline.org)
This is pretty much self explanatory. The name of your device. This is the name that others will see when they pair with your bot. It’s not to be confused with the bot’s name, which you will have to have the Obyte hub-operator create for you.
This is the part that goes after the # in your pairing invitation that you will use from your control device - usually a GUI wallet.
This is one of the two most important settings. This is an array of device addresses (not wallet addresses) that are allowed to control your headless wallet and request payments from it. You will most likely want to control the headless wallet from a GUI wallet on a phone or desktop. You find the device address by clicking the burger-menu and clicking “Settings” and then scrolling down to the section saying “Device Address” on the wallet(s) you want to be able to connect to the headless wallet from.
The other important setting is the payout_address which is the wallet address that the bot is allowed to send funds to. When a device with the correct device address requests the bot to send a payment of X bytes, the bot automatically pays it to the wallet you specify here. If you are installing the bot to allow users to send funds to it or to be part of smart contracts that it will potentially withdraw from, you might want to periodically send funds away from the headless wallet and into another wallet you control.
This setting determines whether your headless wallet will run as a full node or a light node. If your bot needs access to all transactions on the DAG, you will need to run a full node. Therefore, in most cases, this should be set to false
.
The final file would look something like this:
That’s it, your headless wallet is now ready to be started. If you set bLight = false; your wallet will start synchronizing the entire DAG. This can take a long time depending on the speed of your server and particularly the SSD disk.
To make sure the process doesn’t run out of memory (which can cause crashes) you should manually assign additional memory to the nodejs process:
node --max-old-space-size=4096 start.js
ctrl+z
bg
fg
ctrl+c
It might just be me, but I always found that a better way to leave processes running even after I log off the server, is to use the program screen
. It’s super easy to use. So I suggest, that even though the bot say you can send the process to the background by using ctrl+z
and bg
, i have a harder time separating the various background processes. So I suggest you simply do this:
sudo apt-get install screen
This allows you to start, detach and re-attach screens as you please.
screen
- run it when you have detached from screen and want to start a totally new screen.
ctrl+a and d
- this detaches from screens and sends it to background (be careful not to miss pressing a
before d
.
screen -r
- this reattaches to most recently started screen. If you have started multiple then use screen -list
to get a list of all started screens and then reattached with screen -r PPID
ctrl+d
- while attached to screen, this terminates the screen and stops the headless wallet
If you are unable to enter a passphrase every time the wallet starts and/or are willing to accept the security risks, set bNoPassphrase
to true
and daemonize the app when starting:
Checking if node
runs
ps -ef | { head -1; grep node; }
or ps -fp $(pgrep -d, -x node)
kill PID
- sends SIGTERM command to stop the process by PID number.
If you want to automatically stop all node processes then use pkill node
tail -f ~/headless-obyte/log.txt
By default, headless wallets are set to write logs into a file, but you make them to write to console instead by setting logToSTDOUT
configuration to true
.
If don't wish to get the logs to console and you also would like to disable writing the log file then this can be done by adding a variable like this to the configuration file:
There are two ways of making a contract between two Obyte users: either to agree on some conditions and formalize them in our Smart Contract Language, which later can be evaluated by machine code and decide who spends the money from the contract (), or just publish a hash of simple prosaic text of a contract into DAG ().
If you are looking to build more complex dApps that are as capable as Ethereum smart-contracts, then you need to use .
That’s all. Your first oracle is ready. For the experiment, you can add more cities or make paid access. All code is available on .Did you like the lesson? Any questions? What topics have caused you difficulties and it is worth talking about them? Write in the comments and I will help you. Also join my telegram chat - .
Example:
There are many other functions for sending payments, for example sending multiple payments in multiple assets at the same time, see exports
of .
The opts
object has an optional field email_subject
which is the subject of the email sent to the user. To send emails, you need to set the following fields in your :
If you are going to print a paper wallet from an ATM, you just print the mnemonic and optionally in the QR code. You could create your own too with jQuery.
More about textcoins in Obyte blog
Example code in a Telegram bot that sends users textcoins for passing a quiz:
Example code that creates a list of textcoins for subsequent mass sending (e.g. ):
We will need . Let’s install it.
That’s all. I also have a homework for you. Complete the oracle and our bot so that oracle informs us that he published and the publication has become stable. After oracle message, notify the user that he lost or won. And if he lost then take the money out of the contract. All code is available on . Did you like the lesson? Any questions? What topics have caused you difficulties and it is worth talking about them? Write in the comments and I will help you. Also join my telegram chat - .
This code needs to be deployed on the ledger before it can be used. Visit , copy/paste the above code, and click "Deploy". You'll then get the address of your new autonomous agent. Anybody can now send money to this address to trigger execution of this AA's code.
AAs can be triggered with data by manually entering the key/value pairs to GUI wallet, , making the or with the .
This was a quick introduction to programming of autonomous agents. Read the full to learn more, and enjoy your journey through decentralized finance and beyond!
Most bot repositories have example .env.testnet
file in their root folder. In order to connect use testnet network, just run cp .env.testnet .env
. Backup and delete the database if you already ran it on MAINNET. Wallet app for website.
Storage backend -- mysql or sqlite, the default is sqlite. If sqlite, the database files are stored in the app data folder. If mysql, you need to also initialize the database with and set connection params:
Obyte network works over secure WebSocket protocol wss://. To accept incoming connections, you'll need a valid TLS certificate (you can get a free one from ) and a domain name (you can get a free domain from ). Then you accept connections on standard port 443 and proxy them to your locally running Obyte daemon.
Many bots have a file named included in the repository, which looks like this
This can be executed with crontab with the command mentioned in and when the email is properly configured on the server and the node crashes, this script will notify you that the node went down.
Above example is for wallet-less node, which will log the stdout
and stderr
(>> for appending) to file (). If the command uses parameters like --max-old-space-size=4096
then these should be added to both parameters, but output directing (1>log
and 2>>err
) should not be added to first parameter.
you of the other side of the contract.
The core module for prosaic contracts is . You can directly use it's exported methods, so check the content of it first.
Contracts with arbitration are peer-to-peer free-form textual contracts coupled with simple smart contracts that safely lock the money for the duration of the contract and allow disputes to be resolved by mutually agreed arbiters. In other words, a contract with arbitration is a plus a smart contract that guarantees that the parties faithfully follow the contract.
All the API methods available for your bot are located in file.
There is an example script for a part of the API functionality in inside headless-wallet
repository, you can start by modifying it to your needs:
If you allow users to make offers, use this function after receiving a contract offer, usually as a reaction to the event arbiter_contract_offer
. To get a better idea how to construct signedMessageBase64
and signer
arguments for this function, you can take a look at an example of how GUI wallet is invoking it: \
To begin developing any app which is based on headless-obyte, first thing to do is to set up your machine, install Node.js and tools. . You can skip this step if you are familiar with Node.js development and already know how to do it in your way.
Full configuration options are described in section. As for now, in your configuration file (conf.js in project folder or conf.json in user folder) switch your app to light node and add hub URL:
There are many other functions for sending payments, for example sending multiple payments in multiple assets at the same time, see exports
of .
You also need this pairing code to add your bot to the .
The behavior of your event handler can depend on pairing_secret
, the second argument of the event handler. For example, you can have a one-time (non-permanent) pairing secret equal to session ID on your website; after the user clicks the pairing link and opens chat, you can link his chat session with his web session, see .
The user will see the text in square brackets "Command name", it will be highlighted as a link, and when the user clicks it, his app will send command code
text to your bot.
Here is how the result looks like:
: start from here
: sending payments
: sending and receiving payments
: receiving payments without storage of private keys
: offering contracts
: offering contracts
: not a bot, but you'll find code for offering contracts
Now you are ready to make something useful, go ahead to our Tutorials section and carefully read all of them to get better understanding of Obyte code API. .
The first thing you should consider, is whether you want to run your bot on a full node or a light node. When setting up the server, the choice to run on a full node will mean you need to make sure to have enough disk space available. The installation is the same whether you run a full node or a light node, but running a full node will allow your bot to have access to all units ever posted to the DAG and thereby be able to perform more complex tasks. If light wallet is enough for you and have no interest in renting a VPS then you can run a light .
Having created the server, I need to connect to the server. I use a super lightweight SSH terminal called “putty” from my Windows laptop.(available on )
(thanks to for the above two commands)
You will also need for cases where the pre-built binaries (secp256k1, sqlite, rocksdb) are not available for your system configuration. That you can install with this command:
npm install -g node-gyp
So far so good - we’re now almost ready to start actually installing the headless wallet. But first. We need to find the repository that we want to clone. Go to and click the “Clone or download” button:
There is also a detailed explanation of the values on at the section called “Customize”.
Since Obyte runs on an SQLite database by default, which is , you might want to be able to explore the data stored yourself, thereby making it easier to create the logic for the bot if you need to access data in the databases. If you wish to then that can be changed with a configuration file.
apt-get install sqlite3
Checking the log to see if things are running (by default, log file is in ):
Full documentation for headless Obyte node can be found there
Autonomous agents (AA) are special addresses (accounts) on the ledger that do not belong to anybody. An AA can send transactions only in response to a triggering transaction sent to it and strictly according to a program associated with the AA. This program is open and known in advance, it specifies the AA's reaction to triggering transactions. The reaction depends on:
coins sent by the triggering transaction: how much of which asset were sent;
data sent by the triggering transaction;
environment that exists in the ledger when the trigger is received: balances, data posted by oracles, attestations, state variables.
The AA's reaction can be one or a combination of:
sending some other coins back to the sender or to a third party (which can be another AA);
changing the environment of the ledger by posting data feeds, attestations, updating state variables, etc.
The behavior of autonomous agents is similar to vending machines: they accept coins and data entered on a keypad (the triggering action), and in response they release a cup of coffee or play a song, or do whatever they are programmed to do. What's common between them, their behavior is predictable, known in advance.
There are no private/public keys associated with AAs. Their transactions do not have signatures. Their transactions are created by all (full) nodes just by following the common protocol rules and executing the code associated with the AA. All nodes always come to the same result and produce exactly the same transaction that should be sent on behalf of the AA. As one comes to expect from a DAG, all nodes arrive at the same view of the ledger state just by following the rules, without any votings, proof-of competitions, or leaders.
The AA-generated transactions are not broadcast to peers as peers are expected to generate the same transactions on their own.
The language used to specify the AA behavior is a domain specific language that we call Oscript. It was designed specifically for AAs to make it easy to write an autonomous agent behavior, make it easy to argue about what is going on in the AA code, and make it hard to make difficult-to-track errors.
To control the resource consumption, the language does not support loops. Resource usage is controlled by setting a cap on the complexity of the program -- too resource heavy programs are simply not allowed. However, the resource caps provide enough room for the vast majority of practical applications.
AAs can "call" other AAs by sending coins to them. All the scripts in the calling AA are finished and all changes committed before passing control on to the next AA, this avoids completely the reentrancy problem common in other smart contract platforms.
Addresses of autonomous agents follow the same general rules as all other Obyte addresses: their definitions are two-element arrays and the address is a checksummed hash of the array encoded in base32.
AA address is defined as:
The second element of the above array is an object that defines a template for future units created by the AA. The template's structure follows the structure of a regular unit in general, with some elements dynamic and dependent upon the input and state parameters. The dynamic elements are designated with special markup and include code in a domain specific language called Oscript:
The concept is similar to how such languages as PHP, ASP, and JSP work. For example, a PHP script is a HTML file interspersed with fragments of PHP code enclosed between <?php
and ?>
tags. These fragments make the HTML page dynamic while keeping its HTML structure. While this mix can quickly become messy when PHP is used to generate HTML code, the ease of creating dynamic web pages by just inserting small pieces of code into HTML was one of the main selling points of these programming languages in the early days of the web. And it enabled quick prototyping, iteration, experimentation, and eventually led to the creation of some of the biggest pieces of the modern Internet (wordpress and facebook, to name a few).
Transactions, or storage units as we call them in Obyte, are similarly the basic building units of distributed ledgers. They are usually represented as JSON objects, which are the counterparts of HTML pages on the web. Now, to make it easy to create dynamic, parameterized JSON objects representing transactions, we allow to inject some code into JSON and invite the developers to apply their creativity and do the rest.
Web
HTML
PHP
Obyte
JSON
Oscript
This is an example of autonomous agent definition:
Here messages
is a template for the AA's response transaction. It has only one message -- a payment message that will send the received amount less 1000 bytes back to sender. Omitting the amount
entirely would send everything back (minus the fees). There are code fragments in strings enclosed in curly braces "{}", they are evaluated and the result is inserted in place of the code.
Before sending the resulting response transaction, the nodes on the network will also enhance it with change outputs (if necessary) and add other necessary fields in order to attach the transaction to the DAG: parents, last stable unit, authors, timestamp, fees, etc.
Below is a specification of the unit template format.
This is an optional field of the unit template that specifies the fees charged from sender if the AA execution fails. In this case, all the received money in all assets is automatically bounced back to sender, less the bounce fees. The fees are keyed by asset ID (base
for bytes).
The minimum and default bounce fee for bytes is 10000 bytes. The minimum and default bounce fee for all other assets is 0. Non-base bounce fees apply only to those assets that were actually received by the autonomous agent.
Sending to an autonomous agent anything less than the bounce fees will result in no response and the AA silently eating the coins. However this rule applies only to money sent from regular addresses. Bounce fees are not checked when the money is received from another AA.
bounce_fees
field is removed from the final unit.
Each deployed autonomous agent can have a URL that points to a JSON formatted documentation file, which content will be shown to users in wallet app. URL can contain {{aa_address}}
placeholder, which will be replaced with actual AA address before fetching the JSON file. The structure of the JSON file should look like this:
Keys in field_descriptions
object should match all the keys in trigger.data
that users can use with this AA, wallet app will add value of that fields as an explanation what it does. The value of version
should be as shown above ("1.0"
) - it should NOT used as version of AA.
This is the main field of autonomous agent definition. It specifies templates for the messages to be generated, and the templates are parameterized with oscript code.
The messages can be of any type (called app
) that Obyte supports. The most common app is payment
, it is used to send payments in any asset back to sender or to a third party. Other apps are:
data
: used to send data, this includes sending data parameters to other (secondary) AAs;
data_feed
: used to send data feeds. By doing this, the AA becomes an oracle;
profile
: used to send one's own profile. Maybe an AA wants to say something to the world about itself;
text
: used to save arbitrary text to the DAG;
definition
: used to post a definition of a new AA;
asset_attestors
: used to change the attestor list of an asset previously defined by this AA;
attestation
: used to post information about some other address. By doing this, the AA becomes an attestor;
definition_template
: used to post a template for smart contract definition;
poll
: used to create a poll;
vote
: used to vote in a poll. Every AA has voting rights after all.
There is also another, special, app called state
, which is not possible in regular units but is used only in AAs to produce state changes. More about it in a separate chapter.
It is possible to create parameterized Autonomous Agents, which are based on previously deployed templates. Their structure for that kind of Autonomous Agent would look like this:
The base AA template would need to reference these parameters as params.name1
and params.name2
.
Any string, number, or boolean in the template can be calculated by a script. The script is evaluated and the result is inserted in place of the script, the type is preserved. For example, if 20000 bytes were sent from address 2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7
to an AA, then this code
would be replaced with
Object keys can also be parameterized with Oscript:
See the Oscript language reference below.
JSON has scalars (strings, numbers, booleans) and objects (objects and arrays). Scalars can be parameterized through Oscript. Objects, on the other hand, are parameterized differently. They can have several alternative versions in the AA template, and only one version is selected based on input parameters and state. The versions and their selection criteria are specified using an object in cases
array:
The regular value of an object/array is replaced with an object whose single element is an array cases
. Each element of the cases
array is an object with up to 3 elements:
if
: an Oscript expression. If the result of its evaluation is truthy then this case
is selected. All other case
s are not evaluated. if
is required for all case
s except the last, the last one may or may not have an if
. If all previous case
s evaluated to a falsy value and the last one is without an if
, the last one is selected;
init
: an optional statements-only Oscript that is evaluated immediately after if
if this case
is selected;
a mandatory element that is named the same as the original field (messages
in the above example). If this case
is selected, the original (3 levels higher) field is replaced with the value of this element.
In the above example, if the 2nd case
were selected, the original object would fold into:
Cases can be nested.
Cases can be used for any non-scalar value inside messages
, not just messages
themselves.
Similar to the cases
above, any object can have an additional if
field. It is evaluated first, and if it is falsy, the entire object is removed from the enclosing object or array. Its internal Oscripts are not evaluated in this case.
In the above example, the payment
message will be generated only if trigger.data.withdrawal_amount
is a number greater than 0.
The if
field itself is removed from the object.
Similar to the cases
above, any object can have an additional init
field. It is evaluated immediately after if
when if
is present and truthy. If there is no if
, init
is unconditionally evaluated first.
init
must be a statements-only Oscript, it does not return a value.
Example:
The init
field itself is removed from the object.
If the value of any object field or array value evaluates to an empty string, this field or element is removed.
For example, object
will become
Array
will become
If the amount
field in an output within a payment message is omitted or evaluates to an empty string (which results in its removal per the above rules), this output receives all the remaining coins.
In the above example, 1000 bytes are sent to 2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7
, the rest of the AA's balance in bytes is sent to the address that triggered the AA.
If an object or array becomes empty as a result of various removals, it is also removed from the enclosing object or array.
A state message is a special message in the messages
array that performs state changes. It is the only oscript where state variables are assigned. Unlike regular messages that always have payload
, state message has a field named state
instead that contains a state changing script:
The state message must always be the last message in the messages
array. It is not included in the final response unit and its script (state script) is evaluated after the response unit is already prepared. It is the only oscript where response_unit variable is available. State script contains only statements, it is not allowed to return any value.
Local constant names are always prefixed with $
. If the constant name itself needs to be calculated, the expression is enclosed in curly braces.
Local constants exist only during AA evaluation. Each constant can be assigned a value only once (single-assignment rule).
Each local constant is visible only in its own oscript after it was assigned. If it was assigned in if
block, it is also available in all adjacent and enclosed oscripts. If it was assigned in init
block, it is also available in all adjacent oscripts except if
and in all oscripts enclosed in the same object.
If an unassigned local constant is referenced, it is taken as false
.
If-else blocks in curly braces do not create separate scopes for local constants:
Local constants can hold values of any type: string, number, boolean, or object (including array).
When a local constant holds an object, individual fields of the object can be accessed through dot or [] selector:
If the specified object field does not exist, the value is taken as false
.
Objects and arrays can be initialized using familiar syntax as in other languages:
Although all local constants are single-assignment, objects and arrays can be mutated by modifying, adding, or deleting their fields:
The left-hand selectors can be arbitrarily long .a.b[2].c.3.d[].e
. If some elements do not exist, an empty object or array is created automatically. []
adds to the end of an array. Array indexes cannot be skipped, i.e. if an array has length 5, you cannot assign to element 7. Once you are done mutating an object, can call freeze()
to prevent further accidental mutations.
Functions are local constants and the same rules apply to them.
The return value is the value of the last expression or a return statement.
A function sees all other local constants and functions declared before it. Because of this, recursion is impossible.
Constants declared before the function cannot be shadowed by its arguments or other local constants declared within the function.
Complexity of a function is the sum of complexities of all operations within it. Every time a function is called, its complexity is added to the total complexity of the AA. If a function is declared but never called, its complexity doesn't affect the complexity of the AA.
Getters are read-only functions available to other AAs and non-AA code.
They are meant to extract information about an AA state that is not directly available through state vars. E.g. in order to fetch this information one needs to perform some calculation over state vars or access several state vars and have good knowledge about the way information is stored in the AA.
Examples:
oswap could expose functions that would calculate the price of a future exchange. Otherwise, clients would have to do non-trivial math themselves.
the token registry could expose a single getter $getTokenDescription($asset)
for reading a token description, and a similar one for decimals. Otherwise, one has to read first $desc_hash = var['current_desc_' || $asset]
, then var['description_' || $desc_hash]
.
Getters are declared in a top-level getters
section which is evaluated before everything else.
The code in getters
section can contain only function declarations and constants. Request-specific information such as trigger.data
, trigger.outputs
, etc is not available in getters.
In the AA which declares them, getters can be accessed like normal functions.
Other AAs can call them by specifying the remote AA address before the function name using this syntax:
or
If $remote_aa
is a variable that cannot be evaluated at deploy time, you need to indicate the max complexity of the remote call either explicitly, after a #:
or as a variable that can be evaluated at deploy time:
or by indicating a base AA that the $remote_aa
is a parameterized AA of:
The complexity of a remote call is the complexity of its function, plus one.
All functions declared in the getters
section are publicly available. If some of them are not meant for public use, one can indicate this by a naming convention, e.g. by starting their names with an underscore $_callPrivateFunction()
but information hiding cannot be really enforced since all getters operate on public data anyway.
Getters can also be conveniently called from non-AAs. In node.js code:
For remote clients, there is a light/execute_getter
command in WebSocket API, hopefully it will be shortly available through obyte.js.
State variables are persisted across invocations of autonomous agents.
Accessing state variables:
Assigning state variables:
var['var_name']
reads the value of state variable var_name
stored under current AA.
var['AA_ADDRESS']['var_name']
reads the value of state variable var_name
stored under AA AA_ADDRESS
. AA_ADDRESS
is a valid address or this_address
to refer to the current AA.
If there is no such variable, false
is returned.
State vars can temporarily hold strings, numbers, objects, and booleans but when persisting, true
values are converted to 1 and false
values result in removal of the state variable from storage. AAs are required to hold a minimum balance in bytes equal to the size of their storage (length of var names + length of var values). This will also incentivize them to free up unused storage. The size of AA’s storage occupied before the current invocation is in variable storage_size
.
Internally, objects are stored as JSON strings and their length is limited. Don't try to store a structure in a state var if this structure can grow indefinitely.
In addition to regular assignment =
, state variables can also be modified in place using the following operators:
+=
: increment by;
-=
: decrement by;
*=
: multiply by;
/=
: divide by;
%=
: remainder of division by;
||=
: concatenate string/object/array.
For concatenation, the existing value of the var is converted to string.
For +=
, -=
, *=
, /=
, %=
, the existing boolean value is converted to 1 or 0, strings result in error.
If the variable didn't exist prior to one of these assignments, it is taken as false
and converted to number or string accordingly.
Each read or write operation on a state variable adds +1 to complexity. Assignment with modification also costs 1 in complexity.
Examples:
Adds a key to the response object. Response variables do not affect state, they are meant to only inform the caller, and other interested parties, about the actions performed by the AA.
Response vars can only be assigned, never read. Response vars can be assigned and reassigned multiple times in any oscript. They can hold values of types: string, number, boolean. Attempting to assign an object would result in true
being assigned.
Example: assigning these response variables
will result in the following response object:
The AAs are activated and responses are generated when the triggering unit gets stabilized. If the triggering unit triggers several AAs or there are several triggers that are included in the same MC unit and therefore get stabilized at the same time, the triggers are handled in deterministic order to ensure reproducibility on all nodes.
The first response unit has one or two parents:
the MC unit that just got stabilized and which includes the triggering unit;
the previous AA-generated unit (meaning, generated by any AA, not just the current one) if it is not already included in the first parent.
Any subsequent responses (generated by secondary AAs and in response to other triggers at the same MCI) are chained after the first response and then one after another.
After every response, 4 events are emitted:
aa_response
aa_response_to_unit-
+ trigger_unit
aa_response_to_address-
+ trigger_address
aa_response_from_aa-
+ aa_address
Applications, which are based on a full node can subscribe to these events to receive information about responses they are interested in, e.g.:
Applications, which are based on light node will also need to add the address to their watched list in order to subscribe to these events:
All 4 event handlers receive objAAResponse
object as a single argument:
The object has the following fields:
mci
: the MCI of the triggering unit. The response unit is attached to the NC unit with the same MCI;
trigger_address
: the address that sent coins to the AA and thus triggered it;
aa_address
: AA address
bounced
: true
if the trigger was bounced, false
otherwise;
response_unit
: hash of the response unit or null if there is no response;
objResponseUnit
: response unit object or null if there is no response;
response
: response from the script. The object can have up to two fields: error
for error message and responseVars
for response variables set by scripts.
The generated response unit may contain outputs to other AAs. In this case, the receiving AAs are triggered too. They receive the outputs from the previous AA and the data (if any) from its generated data
message.
Secondary AAs behave like the primary ones except that they may receive less than the minimum bounce fees.
If there are several secondary AAs triggered by a previous AA, they are handled in a deterministic order to make sure that the results are reproducible on all nodes. If a secondary AA triggers a ternary one in turn, it is handled before going on to the next secondary AA.
If any of the secondary AAs fails, the entire chain fails, all the changes produced by its AAs are rolled back, and the primary AA bounces.
The total number of secondary AAs stemming from a single primary AA cannot exceed 10, otherwise the primary AA fails and bounces.
If an AA fails for any reason (bad formula, attempt to send an invalid response, attempt to send more money than it has, etc), and attempt is made to bounce all the received coins back to sender, less bounce fees. All state changes are rolled back.
If creation of the bouncing transaction fails too, no transaction is created in response, i.e. the AA eats the coins.
The response object, which is not saved to the ledger, contains an error message explaining the reasons for failure.
Every variable assignment must be terminated by a semicolon ;
. return and bounce statements are also terminated with a semicolon.
There are two types of Oscripts that can be used in AAs:
statements-only scripts that consist only of statements (such as assignments) and don't return any value. init script and state message are the only two allowed statements-only scripts;
scripts that return a value. They can have 0 or more statements but the last evaluated expression must not be a statement and its result is the result of the script.
Example of a statements-only script:
Example of a script that returns a value:
The result of evaluation of the last expression $amount*2
is returned.
Usually, the return value of oscript is a scalar: string, number, or boolean. It is just inserted in place of the oscript.
If the return value is an object, it is similarly expanded and inserted in place of the original oscript. This can be used to send prepared objects through trigger.data
.
For example, if trigger.data
is
and an AA has this message
The resulting message will be
Interrupts the script's execution and returns the value of expr
. This syntax can be used only in oscripts that are supposed to return a value.
Interrupts the script's execution without returning any value. This syntax can be used only in statements-only oscripts: init
and state
.
Evaluates the first block of statements if the condition
is truthy, the second block otherwise. The else
part is optional.
If the block includes only one statement, enclosing it in {} is optional:
Like other smart contract definitions, AAs have a capped complexity which cannot exceed 100. Some operations involve complex computation or access to the database, such operations are counted and add to the total complexity count. Other operations such as +
, -
, etc, are relatively inexpensive and do not add to the complexity count. The language reference indicates which operations count towards complexity.
Total complexity is the sum of complexities of all oscripts. It is calculated and checked only during deployment and includes the complexity of all branches even though some of them might not be activated at run time.
If the complexity exceeds 100, validation fails and the AA cannot be deployed.
Operands are numbers or converted to numbers if possible.
Additional rules for power operator ^
:
e^x
is calculated with exact (not rounded) value of e
,
an exponent greater or equal to MAX_SAFE_INTEGER will cause an error,
for non-integer exponents, the result is calculated as x^y = e^(y * ln(x))
with rounding of the intermediary result, which causes precision loss but guaranties reproducible results;
this operator adds +1 to complexity count.
If the same key is found in both objects, the value from the right-hand one prevails.
Trying to concat an array with object results in error.
If either operand is a scalar (strings, numbers, booleans), both are converted to strings and concatenated as strings. Objects/arrays become "true".
Lowercase names and
, or
are also allowed.
Non-boolean operands are converted to booleans.
The result is a boolean.
If the first operand evaluates to true
, second operand of OR
is not evaluated.
If the first operand evaluates to false
, second operand of AND
is not evaluated.
Lowercase name not
is also allowed. The operator can be also written as !
.
Non-boolean operand is converted to boolean.
The result is a boolean.
Lowercase name otherwise
is also allowed.
If expr1
is truthy, its result is returned and expr2
is not evaluated. Otherwise, expr2
is evaluated and its result returned.
If both operands are booleans or both operands are numbers, the result is straightforward.
If both operands are strings, they are compared in lexicographical order.
If both operands are objects, only ==
and !=
are allowed, other operators will cause an error.
If operands are of different types:
if any of them is an object or boolean, it causes an error,
if any of them is a string, only ==
and !=
are allowed and non-string operand will be converted to string before comparison, other operators will cause an error,
all other combinations of types cause an error.
If condition
is truthy, expr1
is evaluated and returned, otherwise expr2
is evaluated and returned.
Operators have the following precedence in the order of decreasing "stickiness":
^
!
*
, /
, %
+
, -
, ||
==
, !=
, >
, >=
, <
, <=
AND
OR
?:
OTHERWISE
Pi constant rounded to 15 digits precision: 3.14159265358979
.
Euler's number rounded to 15 digits precision: 2.71828182845905
.
Returns "string"
, "number"
, "boolean"
or "object"
.
These functions add +1 to complexity count.
Negative numbers cause an error. Non-number inputs are converted to numbers or result in error.
Returns absolute value of a number. Non-number inputs are converted to numbers or result in error.
Rounds the input number to the specified number of decimal places (0 if omitted). round
uses ROUND_HALF_EVEN
rules. Non-number inputs are converted to numbers or result in error. Negative or non-integer decimal_places
results in error. decimal_places
greater than 15 results in error.
Returns minimum or maximum among the set of numbers. Non-number inputs are converted to numbers or result in error.
Returns the square root of the sum of squares of all arguments. Boolean parameters are converted to 1 and 0, objects are taken as 1, all other types result in error. The function returns a non-infinity result even if some intermediary results (squares) would overflow.
This function adds +1 to complexity count.
Returns part of the string. If length is not set then returns rest of the string from start index. If start_index
is negative then substring
uses it as a character index from the end of the string. If start_index
is negative and absolute of start_index
is larger than the length of the string then substring
uses 0 as the start_index
.
Returns integer index (starting from 0) of searched string position in string. If searched string is not found then -1 is returned. Use contains
if you don't need to know the index of the searched string.
Returns boolean true
if the string starts, ends or contains searched string.
Returns the string with changed case.
Replaces all occurrences of search_str
in str
with replacement
and returns the new string.
Returns true
if str
consists only of characters in allowed_chars
. allowed_chars
is a group of characters recognized by regular expressions, examples: a-z0-9
, \w
. has_only
adds +1 to complexity.
Attempts to parse the input JSON string. If the result of parsing is an object, the object is returned. If the result is a scalar (boolean, string, number), the scalar is returned.
This function adds +1 to complexity count.
If parsing fails, false
is returned.
Non-string input is converted to string.
Stringifies the input parameter into JSON. The parameter can also be a number, boolean, or string. If it is a number outside the IEEE754 range, the formula fails. Objects in the returned JSON are sorted by keys.
The function for map
, foreach
, and filter
accepts 1 or 2 arguments. If it accepts 1 argument, the value of each element is passed to it. If it accepts 2 arguments, key and value for objects or index and element for arrays are passed.
The second argument is the maximum number of elements that an array or object can have. If it is larger, the script fails. This number must be a constant so that it can be known at deploy time, and the complexity of the entire operation is the complexity of the callback function times maximum number of elements. If the function has 0 complexity, the total complexity of map/reduce/foreach/filter is assumed to be 1 independently of the max number of elements. Max number of elements cannot exceed 100.
A function is executed over each element of an array or object. The callback function can be anonymous like in the example above, or referenced by name:
reduce
has one additional argument for initial value:
The callback function for reduce
accepts 2 or 3 arguments: accumulator and value or accumulator, key, and value (accumulator, index, and element for arrays).
All these 4 functions are similar to their Javascript counterparts but unlike Javascript they can also operate on objects, not just arrays.
The functions are similar to their counterparts in other languages. join
can be applied to objects too, in this case the elements are sorted by key and their values are joined.
The function reverses an array and returns a new one. Deep copies of all elements are created.
Passing a non-array to this function results in error.
Returns the keys of an object. The keys are sorted. Passing anything but an object results in error.
Returns the length of string. number, object or array. When passed an object or array, it returns the number of elements in object or array. Scalar types are converted to strings and the length of the string is returned.
Generates a number from a seed string. The same seed always produces the same number. The numbers generated from different seed strings are uniformly distributed in the specified interval.
The first form returns a fractional number from 0 to 1.
The second form returns an integer number from 0 to max inclusive.
The third form returns an integer number from min to max inclusive.
This function is useful for generating pseudo-random numbers from a seed string. It adds +1 to complexity count.
Returns SHA-256 hash of input string/object in Base64 encoding (default), Base32 or Hex encoding. Non-string inputs are converted to strings. This function adds +1 to complexity count.
Returns a 160-bit checksummed hash of an object, it is most useful for calculating an address when you know its definition, e.g. if you programmatically define a new AA and want to know its address in order to immediately trigger it.
Returns boolean true
if the trigger data parameter with name param
exists.
Returns boolean true
if the number is without fractionals.
Returns boolean true
if the object is an array.
Returns boolean true
if the object is an associative array (dictionary).
Returns boolean true
if the string is valid Obyte wallet address.
Returns boolean true
if the string is Autonomous Agent address.
Returns boolean true
if number is positive, integer, and below MAX_CAP (maximum cap that any token can have on Obyte platform).
Returns true
if signedPackage
object is a valid signed package signed by address address
, returns false
otherwise (the formula doesn't fail even if signedPackage
doesn't have the correct format). address
must be a valid address, otherwise the expression fails with an error. This function adds +1 to complexity count.
signedPackage
object is usually passed through the trigger and has the following structure:
Here:
signed_message
is the message being signed, it can be an object, an array, or scalar;
authors
is an array of authors who signed the message (usually one), it has the same structure as unit authors and includes the signing address, authentifiers (usually signatures) and optionally definitions;
last_ball_unit
: optional unit of last ball that indicates the position on the DAG at which the message was signed. If definition is not included in author
, it must be known at this point in the ledger history. If there is no last_ball_unit
in signedPackage
, including address definition as part of each author
is required;
version
: always 2.0
.
Usually, signedPackage
is created by calling signMessage
function from signed_message
module:
The function creates a correctly structured signedPackage
object which can be added to trigger.data
.
Returns true
if signature
is a correct ECDSA signature of message
by the private key corresponding to public_key
, returns false
otherwise.
message
is a string corresponding to the message being signed, the function will hash the message with SHA-256 before verifying the signature. In case message
is not a string, the formula will fail.
public_key
is a string containing the public key in a PEM format. For example:
-----BEGIN PUBLIC KEY-----
and -----END PUBLIC KEY-----
can be omitted, spaces or carriage returns will be ignored. If public_key
is not a string, is not for a supported curve, or doesn't have the required length then the formula will fail.
signature
is a string containing the signature in Base64 or hexadecimal format. In case signature is not a string or is not Base64 nor hexadecimal format, the formula will fail.
Supported algorithms:
ECDSA
brainpoolP160r1, brainpoolP160t1, brainpoolP192r1, brainpoolP192t1, brainpoolP224r1, brainpoolP224t1, brainpoolP256r1, brainpoolP256t1, prime192v1, prime192v2, prime192v3, prime239v1, prime239v2, prime239v3, prime256v1, secp112r1, secp112r2, secp128r1, secp128r2, secp160k1, secp160r1, secp160r2, secp192k1, secp224k1, secp224r1, secp256k1, secp384r1, sect113r1, sect113r2, sect131r1, sect131r2, wap-wsg-idm-ecid-wtls1, wap-wsg-idm-ecid-wtls4, wap-wsg-idm-ecid-wtls6, wap-wsg-idm-ecid-wtls7, wap-wsg-idm-ecid-wtls8, wap-wsg-idm-ecid-wtls9
RSA
PKCS #1 - 512 to 4096 bits
Returns true
if proof
is valid, return false otherwise.
seed
is a string from which is derived a proof unique for this RSA key. The formula will fail in case seed
is not a string or is empty.
proof
is an hexadecimal string value from 128 to 1024 characters (depending of RSA key size). Can be used with number_from_seed
to obtain a verifiable random number.
pubkey
is a string containing the RSA public key in a PEM spki format. For example:
-----BEGIN PUBLIC KEY-----
and -----END PUBLIC KEY-----
can be omitted, spaces or carriage returns will be ignored. If public_key
is not a string, is not for a supported curve, or doesn't have the required length then the formula will fail.
Supported algorithm: RSAPKCS #1 - 512 to 4096 bits
Check if a given element is included in a merkle tree. The proof
has the following structure {root: string, siblings: array, index: number}
and would normally come from trigger.data
. One should check is_valid_merkle_proof
and look up the merkle hash root
from a data feed.
Aborts the script's execution with error message passed as the function's argument. The received money will be bounced to sender (less bounce fees).
Aborts the script's execution with error message error_message
if the condition
evaluates to a falsy value. The received money will be bounced to the sender (less bounce fees).
This is equivalent to
These functions are to be used in conjunction with is_valid_sig
and vrf_verify
.
Returns a signature that can be verified with is_valid_sig
message
is a string to be signed.
encoding
is either 'base64 or 'hex'
pem_key
is a string containing ECDSA private key in PEM format
Returns a signature that can be verified with is_valid_sig.
message
is a string to be signed.
encoding
is either 'base64 or 'hex'
pem_key
is a string containing RSA private key in PEM format
_**_Returns a proof that can verified with vrf_verify.
seed
is a string from which is derived the unique proof
pem_key
_**_is a string containing RSA private key in PEM format�
Key generation with openssl
Keys for the functions above can be generated with openssl.
RSA (mandatory for vrfGenerate and vrf_verify):
private key
openssl genrsa -out priv_key.pem 2048
Replace 2048 by any key length from 512 to 4096.
public key
openssl rsa -in priv_key.pem -outform PEM -pubout -out pub_key.pem
ECDSA:
private key
openssl ecparam -name prime256v1 -genkey -noout -out priv_key.pem �Replace prime256v1 by any curve identifier listed above
public key
openssl ec -in priv_key.pem -pubout -out pub_key.pem
Some functions and operators that expect strings as inputs need to convert non-string inputs to strings:
numbers are converted to strings using their decimal representation. For numbers whose exponent is greater than or equal to 21 or less than or equal to -7, exponential representation is used.
booleans are converted to strings true
and false
.
objects become strings true
.
Some functions and operators that expect numbers as inputs need to convert non-number inputs to numbers:
booleans true
and false
are converted to numbers 1
and 0
respectively.
objects become 1.
strings become numbers, +
can be used to force the conversion, e.g. +'3'
becomes 3
, +false
becomes 0
.
0 and empty string become false
, all other values become true
. Any value that would convert to true
is called truthy, otherwise falsy.
Line comments
as well as block comments are supported:
The address of the sender who sent money to this AA. If the sending unit was signed by several addresses, the first one is used.
The address of the sender who sent money to the initial AA of a chain of AAs. Same as trigger.address
if there was no chain. When an AA sends money to another AA, trigger.initial_address
remains unchanged.
The unit that sent money to this AA.
The trigger unit that started a chain of AA calls. Same as trigger.unit
if there was no chain. When an AA sends money to another AA, trigger.initial_unit
remains unchanged.
Output sent to the AA address in the specified asset.
assetID
can be base
for bytes or any expression that evaluates to asset ID.
field
can be amount
or asset
or omitted. If omitted, amount
is assumed. If the trigger unit had several outputs in the same asset to this AA address, their amounts are summed.
The search criteria can only be =
(asset=$assetID
) or !=
(asset!=$assetID
).
Examples:
If there is no output that satisfies the search criteria, the returned .amount
is 0 and the returned .asset
is a string none
. Your code should check for this string if necessary.
If there is more than one output that satisfies the search criteria (which is possible only for !=
), the returned .asset
is a string ambiguous
. Your code should check for this string if necessary. Trying to access .amount
of an ambiguous asset fails the script.
An object that stores the outputs sent to the AA address in various assets.
assetID
can be 'base'
for bytes or any expression that evaluates to asset ID.
Examples:
Data sent with the trigger unit in its data
message. trigger.data
returns the entire data object, trigger.data.field1.field2
or trigger.data.field1[expr2]
tries to access a deeper nested field:
if it is an object, object is returned;
if it is a scalar (string, number, or boolean), scalar is returned;
if it doesn't exist, false
is returned.
For example, if the trigger unit had this data message
trigger.data
would be equal to
trigger.data.field1
would be equal to
trigger.data.field1.field2
would be equal to string value2
,
trigger.data.field1['a' || 'bc']
would be equal to number 88
,
trigger.data.field1.nonexistent
would be equal to boolean false
,
trigger.data.nonexistent.anotherfield
would be equal to boolean false
.
MCI of the trigger unit, which is the same as MCI of MC unit the response unit (if any) will be attached to.
Timestamp of the MC unit that recently became stable, this is the unit whose stabilization triggered the execution of this AA. This is the same unit the response unit (if any) will be attached to. It's number of seconds since Epoch - Jan 01 1970. (UTC).
Hash of the MC unit that includes (or is equal to) the trigger unit.
The size of AA’s storage occupied before the current invocation
Built-in variable says how many responses were already generated in response to a primary trigger and might help to avoid exceeding the limit of 10 responses per primary trigger.
Built-in variable that holds an array of responses generated by previous AAs in the chain (empty array for the first AA in the chain). Each element of the array is an object with the following fields:
unit_obj
: response unit object (if any);
trigger_unit
: trigger unit for this response;
trigger_address
: trigger address for this response;
aa_address
: address of the AA that handled the trigger and generated the response.
The address of this AA.
The hash of the unit that will be generated by the AA in response to the trigger. This variable is available only in state script. Any references to this variable in any other scripts will fire an error.
It allows to inspect the definition of any address using definition['ADDRESS']
syntax.
Examples:
Extracts information about an asset. This adds +1 to complexity. expr
is base
for bytes or an expression that evaluates to an asset ID.
field
is one of the following, or field_expr
should evaluate to one of the following:
exists
: boolean, returns false
if asset ID is invalid;
cap
: number, total supply of the asset. For uncapped assets, 0 is returned;
is_private
: boolean, is the asset private?
is_transferrable
: boolean, is the asset transferrable?
auto_destroy
: boolean, does the asset gets autodestroyed when sent to definer address?
fixed_denominations
: boolean, is the asset issued in fixed denominations? Currently AAs can't send fixed denomination assets, but if issued_by_definer_only
is false
then somebody else can issue them.
issued_by_definer_only
: boolean, is the asset issued by definer only?
cosigned_by_definer
: boolean, should each transfer be cosigned by definer?
spender_attested
: boolean, should each holder be attested?
is_issued
: boolean, is any amount of the asset already issued?
definer_address
: string, returns wallet address of the definer.
Examples:
If the asset ID is valid, but does not exist then false
is returned for any field.
Finds data feed value by search criteria. This adds +1 to complexity.
There are multiple search criteria listed between the double brackets, their order is insignificant.
oracles
: string, list of oracle addresses delimited by :
(usually only one oracle). this_address
refers to the current AA;
feed_name
: string, the name of the data feed;
feed_value
: string or number, optional, search only for this specific value of the data feed;
min_mci
: number, optional, search only since the specified MCI;
ifseveral
: string, optional, last
or abort
, what to do if several values found that match all the search criteria, return the last one or abort the script with error, default is last
ifnone
: string or number or boolean, optional, the value to return if nothing is found. By default, this results in an error and aborts the script;
what
: string, optional, value
or unit
, what to return, the data feed value or the unit where it was posted, default is value
;
type
: string, optional, auto
or string
, what type to return, default is auto
. For auto
, data feed values that look like valid IEEE754 numbers are returned as numbers, otherwise they are returned as strings. If string
, the returned value is always a string. This setting affects only the values extracted from the database; if ifnone
is used, the original type of ifnone
value is always preserved.
Data feeds are searched before the MCI of the triggering unit (inclusively). If there are several AAs stemming from the same MCI, previous AA responses are also searched.
Examples:
Determines if a data feed can be found by search criteria. Returns true
or false
. This adds +1 to complexity.
There are multiple search criteria listed between the double brackets, their order is insignificant.
oracles
: string, list of oracle addresses delimited by :
(usually only one oracle). this_address
refers to the current AA;
feed_name
: string, the name of the data feed;
feed_value
: string or number, search only for values of the data feed that are =
, !=
, >
, >=
, <
, or <=
than the specified value;
min_mci
: number, optional, search only since the specified MCI.
Data feeds are searched before the MCI of the triggering unit (inclusively). If there are several AAs stemming from the same MCI, previous AA responses are also searched.
Examples:
Finds an attestation by search criteria. This adds +1 to complexity.
There are multiple search criteria listed between the double brackets, their order is insignificant.
attestors
: string, list of attestor addresses delimited by :
(usually only one attestor). this_address
refers to the current AA;
address
: string, the address that was attested;
ifseveral
: string, optional, last
or abort
, what to do if several matching attestations are found, return the last one or abort the script with error, default is last
ifnone
: string or number or boolean, optional, the value to return if nothing is found. By default, this results in an error and aborts the script;
type
: string, optional, auto
or string
, what type to return, default is auto
. For auto
, attested field values that look like valid IEEE754 numbers are returned as numbers, otherwise they are returned as strings. If string
, the returned value is always a string. This setting affects only the values extracted from the database; if ifnone
is used, the original type of ifnone
value is always preserved.
field
string or field_expr
expression are optional and they indicate the attested field whose value should be returned. Without field
or field_expr
, true
is returned if an attestation is found.
If no matching attestation is found, ifnone
value is returned (independently of field
). If there is no ifnone
, false
is returned.
If a matching attestation exists but the requested field does not, the result is as if the attestation did not exist.
Attestations are searched before the MCI of the triggering unit (inclusively). If there are several AAs stemming from the same MCI, previous AA responses are also searched.
Examples:
Returns the balance of an AA in the specified asset. If aa_address
is omitted, the current AA is assumed. asset
can be base
for bytes, asset id for any other asset, or any expression that evaluates to an asset id or base
string.
This adds +1 to complexity count.
The returned balance includes the outputs received from the current trigger.
Examples:
Tries to find an input or output in the current unit by search criteria.
These language constructs are available only in non-AA formulas in smart contracts (["formula", ...]
clause).
There are multiple search criteria listed between the double brackets, their order is insignificant. All search criteria are optional but at least one must be present.
asset
: string, asset of input or output, can be base
for bytes. Comparison operators can be only =
or !=
;
address
: string, the address receives an output or spends an input, can be this_address
. Comparison operators can be only =
or !=
(other addresses);
amount
: number, the condition for the amount of an input or output. Allowed comparison operators are: =
, !=
, >
, >=
, <
, <=
.
field
is one of amount
, address
, and asset
. It indicates which information about the input or output we are interested in.
If no input/output is found by search criteria or there is more than one matching entry, the formula fails.
Examples:
Searching and filtering
Assuming $x is a variable that contains an object, arrays within this object can be searched and filtered like this:
The above filters the ‘messages’ array by 3 filtering criteria. Like elsewhere in Oscript, double brackets indicate searching by listed criteria (similar to WHERE in SQL).
When the object is a 1-element array but the next key is a string, unwrap the array. This helps to avoid writing [0]. For example, $x.messages.payload.asset and $x.messages[0].payload.asset
are equivalent if ‘messages’ is a 1-element array.
Examples:
strings cannot be longer than 4096 characters;
state var value strings cannot be longer than 1024 characters;
state var names cannot be longer than 128 characters;
numbers must be in IEEE754 double range;
all intermediary calculations are rounded to 15 significant digits;
total complexity of all scripts cannot exceed 100;
total number of operations of all scripts cannot exceed 2000.
Any attempt to exceed these limits will result in script's failure.
There is no error if the same definition is posted twice.
The user who deployed the AA doesn't have any privileges over the AA aside from those directly coded in the AA source code.
Once deployed, the AA definition can never be changed.
Any outputs sent to the AA address before its definition was revealed ("before" means before the MCI of the first unit where the definition was revealed) are treated like regular outputs, they just increase the address'es balance but do not trigger any action, even delayed action after the definition was revealed. This might be a convenient way to fill the AA with some coins before actually launching it.
In case you wish to enter expressions that doesn't get evaluated with the deployment of new AA, but as actual expressions for a new AA, instead of "{expression}"
write an expression that outputs another expression "{'{expression}'}"
.
This URI protocol enables developers to open the wallet app in desired screen with pre-filled inputs.
Following hyperlinks will open the wallet app on Send screen. These hyperlinks (without the HTML) also work when posted as chat messages.
If you don't know user's account address, but still want them to user single-address account then you can use single_address=1
parameter like this:
In order to open a Send screen with pre-filled payment and data, the data object needs to be converted into URI encoded Base64 (using JSON.stringify
, btoa
andencodeURIComponent
) string. Many cases, when the Autonomous Agent stores data based on triggering address, we would also want the users to use single-address account, this can by adding the single_address=1
parameter. Autonomous agents can also accept nested data.
This will open Send screen, which will be pre-filled to post 10 000 bytes and data object.
This will open Send screen, which will be pre-filled to post unstructured key/value pairs as data (single-address account required). Keys need to be unique.
Like the above, this will open Send screen, which will be pre-filled to post unstructured key/value pairs as data (single-address account required) meant for temporary storage. Keys need to be unique.
The data posted this way will be available on the Obyte DAG only for 1 day, after which it will be purged but the hashes of the data will stay on the DAG forever. Unlike permanent data, temporary data costs less in fees.
This will open Send screen, which will be pre-filled to post indexable key/value pairs as data feed (single-address account required). Keys need to be unique.
This will open Send screen, which will be pre-filled to post key/value pairs as profile info (single-address account required). Keys need to be unique.
This will open Send screen, which will be pre-filled to post key/value pairs as someone else attestation profile (single-address account required). Keys need to be unique.
This will open Send screen, which will be pre-filled to post poll (single-address account required). Option keys can be named anything, but values need to be unique. Question parameter is also required.
This will open Send screen, which will be pre-filled to post Autonomous Agent definition (single-address account required). The definition
parameter needs to be URL encoded.
Because most browsers support only URLs up to 2048 characters length, it is also possible to post a AA definition by making it fetch the definition from some other other address like this:
This will open Send screen, which will be pre-filled to post short text content (single-address account required). The content
parameter needs to be URL encoded and can be maximum of 140 chars before encoding.
This will open Send screen, which will be pre-filled to post a vote for a system variable.
This will open Send screen, which will be pre-filled to post a vote-count request for a system variable.
The subject
parameter is one of op_list
, threshold_size
, base_tps_fee
, tps_interval
, tps_fee_multiplier
, it is the name of the variable whose vote count is requested.
The user pays a 1 GB fee for this request.
Bot Store on Obyte is like Apple App Store or Google Play Store, but instead of apps, Bot Store has chat bots that are written for Obyte platform. Each Obyte Hub can have each own bots that are shown for users of that app. Currently, most users are using the official wallet app and official Hub, but it makes sense that if somebody forks the wallet, they would use their own Hub for it too. This means that in the future, in order to get your chat bot exposed to maximum amount of users, it needs to get listed on all Hubs.
Luckily, adding chat bots to your wallet app is much easier on Obyte than it is to side-load apps on iOS or Android. All you need to do is add the pairing code that bot outputs to your website - everyone who has Byteball app already installed and clicks on it, will get automatically paired with your chat bot. Hyperlink to pairing code would be something like this:
Pairing codes in hyperlinks are not limited to only chat bots, you could create a pairing link to any device, all you need to do is find you pairing invitation code (Chat > Add new device > Invite other device) and add the code after obyte:
protocol inside the hyperlink.
obyte:
protocol is not only for websites, it could be used in physical world too with QR codes. This means that if users have the Obyte app installed and they scan any QR code that contains any of the above codes (just value of the href, without quotes and without the HTML around them) then the installed Obyte app will open the same way. The QR code is not just meant to be printed on paper, it can work on websites too, giving the users the ability to use your services cross-device (browse the website on a laptop, scan the QR code with a phone and complete the payment on the phone).
There are many online QR code generators to create those QR codes manually, but QR codes can be created in real-time too. Following is the example how to create a QR code with obyte:
protocol link using jQuery.
We run cp .env.testnet .env
for changing it configuration to testnet network. When you starting bot at first, he will ask you passphrase(don’t forget it, since you need to enter each time you start). When starting bot in console you will see “my pairing code”. It looks like this: Copy this pairing code.
Now add our bot, open the Obyte wallet and go to Chat (below) > Add a new device > Accept invitation from the other device then insert your pairing code (instead of an asterisk any word, for example, test) and click "PAIR”
After that you will have a chat with your bot, write to him and he will answer you with your message. All right, now let's teach the bot to give us our money back.
Opening the start.js file we see 5 events:
headless_wallet_ready - Triggered when the bot is running and ready to work
paired - Triggered when someone adds our bot
text - Triggered when we receive a message
new_my_transactions - Triggered when we receive a transaction(but it is not stable)
my_transactions_became_stable - Triggered when the transaction has become stable
First, let's create variables where the associations for addresses will be stored. We will learn how to work with the database in the following lessons.
Now, lets teach the bot to send a request to send us your address:
Here we see the function sendMessageToDevice, we will use it whenever we want to send a message to the user.
The function takes 3 parameters
device address
subject - for now we will use only ‘text’
our text message
In order to send bot our address we need to go to our chat and click "Insert my address”:
Great, now we will teach our bot to check and save the user's address, and also create an address for accepting payments and request 5000 bytes from the user.
This time we used issueNextMainAddress, this function creates a new wallet address. We need it to distinguish between payments.
Now the most important thing! First, we need to inform the user that we have received the payment and waiting until it becomes stable.
After that, we send the user his payment minus the payment fee:
Here, we use sendAllBytesFromAddress, as the name implies, we just send all the funds(minus the payment fee) from one address to another.
That's it. Now let's test it. To do this you need to add faucet bot: AxBxXDnPOzE/AxLHmidAjwLPFtQ6dK3k70zM0yKVeDzC@byteball.org/bb-test#0000
And send him your address, wait until it becomes stable(this can be seen on the main screen), that's all. Send payments to the bot and get them back.
An Autonomous Agent (AA) is a special address (account) on the ledger that acts according to a program associated with it. Its behavior is similar to that of a vending machine that receives coins and data entered on a keypad and in response, releases a cup of coffee, plays a song, or does whatever it was programmed to do.
Autonomous Agents are written in Oscript — a new programming language developed specifically for this purpose. The language is very simple and any developer who has experience in curly-brace languages such as JavaScript, PHP, Java, C/C++, etc should not have any difficulty learning it.
Some of the features of the language:
convenient access to variables that describe the state of the ledger and the triggering (requesting, activating) transaction received. That’s what makes the language domain-specific. In particular, the following variables are available:
amounts received in the triggering transaction;
data received in the triggering transaction;
who sent the triggering transaction;
state variables of the current and other AAs;
data feeds;
attestations;
balances of this and other AAs;
information about assets;
arithmetic operations;
logical operations (AND, OR, etc);
comparisons;
concatenations;
some math functions;
some cryptography functions for calculating hashes and validating signatures created off-chain;
branching with if/else;
no loops (just iteration methods for objects and arrays);
no recursive functions;
scalar and object data types;
generation of deterministic pseudo-random number from seed.
On Obyte, users can issue new assets and define rules that govern their transferability.
To start a user-defined asset on Obyte, you need to complete two steps
Define your asset. Asset definition is a special type of message (distinct from payments) that you send to the Obyte DAG
Issue the asset by sending the 1st transaction that spends it
To define a new asset you need a headless wallet. In your dependencies:
Call this function to create a new asset definition and broadcast it:
Here my_address
is your address you use to post the asset-defining message. The address must be funded to pay at least the transaction fees (about 1000 bytes).
asset
is a javascript object that describes the properties of your asset. Here is an example:
cap
is the total number of coins that can be issued (money supply). If omitted, the number is unlimited.
is_private
: indicates whether the asset is private (such as blackbytes) or publicly traceable (similar to bytes).
is_transferrable
: indicates whether the asset can be freely transferred among arbitrary parties or all transfers should involve the definer address as either sender or recipient. The latter can be useful e.g. for loyalty points that cannot be resold.
auto_destroy
: indicates whether the asset is destroyed when it is sent to the definer address.
fixed_denominations
: indicates whether the asset exists as coins (banknotes) of a limited set of denominations, similar to blackbytes. If it is true
, the definition must also include property denominations
, which is an array of all denominations and the number of coins of that denomination:
issued_by_definer_only
: indicates whether the asset can be issued only by the definer address. If false
, anyone can issue the asset, in this case cap
must be unlimited and it would make sense that issuing is limited by issue_condition
.
cosigned_by_definer
: indicates whether each operation with the asset must be cosigned by the definer address. Useful for regulated assets where the issuer (bank) wants to perform various compliance checks (such as the funds are not arrested by a court order) prior to approving a transaction. This could also be used to allow fee-less asset transfers for users (paid by definer), but wallets don't support that yet.
spender_attested
: indicates whether the spender of the asset must be attested by one of approved attestors. Also useful for regulated assets e.g. to limit the access to the asset only to KYC'ed users. If true
, the definition must also include the list of approved attestor addresses:
The above conditions stipulate that the newly defined asset can be issued if the issuer also sends equal amount of bytes to the address MO7ZZIU5VXHRZGGHVSZWLWL64IEND5K2, and it can be transferred if equal amount of bytes is transferred to the same address at the same time.
The new asset can be issued after its definition is confirmed. To issue, you need just to spend the asset from an address that is allowed to issue and it will be issued to address you transferred it.
You cannot use wallet.sendMultiPayment()
, headlessWallet.issueChangeAddressAndSendPayment()
and similar functions because they can do only transfers and they don't know about addresses that can issue new coins. Instead, you have to use lower level functions:
composeAndSaveDivisibleAssetPaymentJoint
from ocore/divisible_asset.js
composeAndSaveIndivisibleAssetPaymentJoint
from ocore/indivisible_asset.js
Step by step tutorials to get started with example bots.
syncs full database if full node.
relays storage units if full node.
inherits ocore
library.
light node or full node.
wallet functionality.
graphical user interface for Windows, MacOS, Linux, Android and iOS.
inherits ocore
library.
light node or full node.
wallet functionality.
can be controlled via chat.
inherits headless-obyte
and ocore
library.
only full node.
recommended to be behind TOR.
post new units (moves itself funds or posts oracle data) serially.
inherits ocore
library.
only full node.
accepts incoming connections.
provides new units, stable units, balances & suitable parents to light nodes.
notifies wallet apps about new witnesses.
inherits obyte-relay
and ocore
library.
relays encrypted chat messages between devices.
notifies wallet apps about new app version.
shows list of chat bots that are available to pair with.
shows metadata about assets.
inherits headless-obyte
and ocore
library.
can be used as a base of any new project that needs Obyte wallet functionality.
asset
: used to define a new asset. Different parameters that can be used are documented on ;
Structure of those messages types is documented on and .
State variables can be accessed in any oscripts, but can be assigned only in script. Only state vars of the current AA can be assigned, state vars of other AAs are read-only. State vars can be reassigned multiple times but only the final value will be saved to the database and only if the AA finishes successfully. All changes are committed atomically. If the AA fails, all changes to state vars are rolled back.
Attempts to parse string of date or date + time and returns timestamp. If you need to get seconds from UNIX Epoch of a current unit then use .
Returns string format of date + time (default), date or time from . Timezone is UTC.
Callback for map
and filter
can also be a :
Returns number of elements if the object is an array. Have to use to determine if object is an array. Use instead.
Use this function for debugging your AA. Its arguments are evaluated and are available in a logs
array within the AA response object. You can access this object and inspect the logs while running tests through . It is available even if the script bounced. The logs are not saved anywhere in the DAG but the expressions are still evaluated and might add to your script's complexity. Remove all log()
calls after debugging.
trigger.outputs
is an associative array, you can iterate it with , , , , etc.
exists
function can be used to check if param .
AA code can be deployed with and , but AA code can also be deployed by sending a unit that includes a message with app=definition
. This can be done with headless wallet or with AA itself, which has the definition of new AA in its payload.
It is possible for AA to deploy another AA, which is especially useful when creating for previously deployed AA templates:
If you want to open Obyte app for users then there is a obyte:
protocol for that. If you are not aware what it is then it's like mailto:
, ftp:
, tel:
or . When user visits a link with that protocol then it can open the wallet app in specific screen with pre-filled inputs, helping users to insert long account addresses or amounts without the need to copy-paste them.
We can and we can use the same commands as links on the website. This is how it will look as a hyperlink:
ACCOUNT_ADDRESS
should be replaced with the wallet address where the funds should be sent, amount
parameter should have bigInt
type number in bytes (not KByte, not MByte, not GByte) and asset
parameter is optional, by default it is base
for bytes, but it can be issued too.
One website that heavily relies on this feature is ().
It is also possible to request that the payment to be done from specific address in user's wallet. Since wallet version 3.3.0, this also work from .
The subject
parameter is one of op_list
, threshold_size
, base_tps_fee
, tps_interval
, tps_fee_multiplier
, it is the name of the variable being voted for. The value
parameter is the value of the variable that the voter supports. For op_list
, it must be a list of 12 valid addresses of the proposed (OPs) separated by \n
(url-encoded, of course). All other variables have positive numeric (including fractional) values.
The votes are used to govern the network and change the values of the said system-wide variables (described in , , and ). The weight of each vote depends on the balance of the voting address(es) in Bytes.
The vote-count requests are used to govern the network and commit changes to the values of the said system-wide variables (described in , , and ). All votes for the requested variable will be tallied, weighted by the voter's current balance in Bytes, and the value that received most votes will become active. For op_list
, total votes for each proposed OP will be calculated, and the top 12 will become the new active OPs.
Textcoins are funds that can be sent via text messages. These text messages contain 12 randomly picked words and by entering them into the wallet app in exact same order, user will get the funds that were added to them. If you have ever received any textcoins then you also know that you don't actually have to re-type or copy-paste those words, textcoin receivers are usually directed to , which has green "Receive funds" button, which will launch the wallet app with exactly those textcoin words. Obyte textcoin claiming page tries to check first if user has wallet installed, but basically, underneath that button is a hyperlink similar to this:
So, additionally to , you can also make clickable textcoins on your website (link either to textcoin claiming page or directly to obyte:
protocol). Just make sure each user on your webiste has access to textcoins specifically generated for them. Otherwise, if all users see the same textcoins then it can become a rush to who claims the textcoins first.
One example, where all the official Hub bots are displayed on a website can be seen on .
Once you have paired with a chat bot already, you might wonder whether it is possible to get users from your website to some specific state in chat bot (for example, by ) with a click on any hyperlink. There are 2 options how to do that, first option would be to fill the pairing_secrets
database table with all the possible commands, but if there could be indefinite number of valid commands then easiest would be to accept any pairing secret and use them as custom commands.
Websites that use this feature are and . How it can be done can be seen from Poll bot (). For example, opening a poll app with results of specific poll can be done with hyperlink like this:
Steem Attestation bot uses this method also as an alternative way how to refer other people, source code for that is more advanced that the poll bot code, but .
A website that creates QR codes this way is () and Steem Attestation bot () generates referral links this way. Alternatively, there is also code example .
If you are testing these functions with and then instead of obyte:
protocol, use obyte-tn:
. This can be easily controlled in your bot's code like this:
Hello. I’m starting a series of tutorials about creating projects on Obyte. Today we will consider the creation of ping-pong payment bot. First we will need Linux or MacOS, node.js, and . So, let's clone it and run
All code you can find on . In the next lessons we will continue, but for now I recommend to read the official . Did you like the lesson? Any questions? What topics have caused you difficulties and it is worth talking about them? Write in the comments and I will help you. Also, join my chat in telegram - .
(announcement post on Medium)
AA code can be deployed with and . There is also a and .
After an asset is defined, the hash of the unit where it was defined becomes the unique ID of the asset. The asset is referenced by this ID in future payments and definitions (smart contracts). There are no user-assignable names of assets in the protocol, but there are independent registries that link asset IDs to user-friendly names for a fee. One such registry is , another decentralized registry is
If you are not a developer, but wish to issue an asset on Obyte then you can also use to do that without writing any code. If Obyte Asset Registry is too limited for your needs or you need to issue an asset on TESTNET then continue reading.
See a sample of asset-defining transaction at .
The definition can also include two optional properties issue_condition
and transfer_condition
which specify the restrictions when the asset can be issued and transferred. They evaluate to a boolean and are coded in the same as address definitions.
See (assets without fixed denominations) and (assets with fixed denominations) for examples.
See an example of how an asset can be both defined and issued in a single script
For more details see chapter 24 of the .
Obyte Github page is located there
can be controlled via .
customized can be added.
customized actions on and can be added.
If you need to verify that a user owns a particular address, you can do it by offering to sign a message.
You can also verify ownership of address by requesting a payment from their address and this method is more appropriate if you need to receive a payment anyway.
To request the user to sign a message, send this message in chat:
where challenge
is the message to be signed. This request will be displayed in the user's wallet as a link that the user can click and confirm signing. Once the user signs it, your chat bot receives a specially formatted message:
You can parse and validate it, here is an example how to verify user's account address:
The above code also checks that the user signed the correct message and with the correct address.
objSignedMessage
received from the peer has the following fields:
signed_message
: (string) the signed message
authors
: array of objects, each object has the following structure (similar to the structure of authors
in a unit):
address
: (string) the signing address
definition
: (array) definition of the address
authentifiers
: (object) signatures for different signing paths
To validate the message, call validation.validateSignedMessage
as in the example above.
Note that the challenge that you offer to sign must be both clear to the user and sufficiently unique. The latter is required to prevent reuse of a previously saved signed message.
You can store arbitrary data (any sequence of bytes) in Obyte public DAG. Some special types of data also supported, like 'text', 'profile', 'poll', etc.
To send data
to DAG database, code below is a minimal that you can use. Since the opts.messages
is array, it means that all the examples on this page can be changed to multi-message transactions.
And last and not the least, it is possible to post plain text to DAG too.
This guide explains how to request, validate, and use the attested data in your chat bot.
Users can have some of their data verified by a trusted third party (attestor). The attestor posts an attestation record to the Obyte DAG, this record serves as a proof for relying parties that a user was attested. The attested data itself can be either posted publicly by the attestor, or only a hash of this data is posted while the plain-text data is saved in the wallet of the attested user. In the latter case, the user can disclose the attested data to selected peers, for example to your bot.
You should point the user to your privacy policy before requesting sensitive personal data.
To request a private profile, you need to send profile-request message to the user. The message includes the list of fields you require the user to disclose. The message will be displayed in the user's chat window as a clickable link, which allows them to select one of the profiles stored in his wallet and send it over to the peer (your chat bot).
Here is the full list of available fields:
first_name
: first name
last_name
: last name
dob
: date of birth in YYYY-MM-DD format_
country
: the country that issued the ID (2-letter ISO code)
us_state
: US state (only if country=US)
personal_code
: government issued personal code (not all countries)
id_number
: government issued document ID (not all documents)
id_type
: ID type
id_subtype
: ID sub-type (not all attestations)
id_expiry
: ID expires at (not all attestations)
id_issued_at
: ID issued at (not all attestations)
When a user sends you his private profile (on profile request or by choosing "Insert private profile" from the menu), you receive it in a chat message that includes:
where privateProfileJsonBase64
is a base64-encoded JSON of the private profile object. You can easily find the profile in an incoming message using regular expression:
Then, decode the profile using private_profile.js
module in ocore
:
objPrivateProfile
object has 3 fields:
Next, you need to validate this object and extract information about the attested and attestor addresses:
This function verifies that the provided profile matches the hash stored on the DAG by the attestor. It also returns the user's address address
(hence you don't need to ask the user about his address, the profile is enough) and the attestor address attestor
(make sure it is on the list of attestors you trust for each type of attestations).
The src_profile
field of objPrivateProfile
contains an associative array of attested fields. But not all fields have to be disclosed by the user.
If a field is disclosed, the value of objPrivateProfile[field]
is an array of two elements: the plain-text value of the field and blinding. Blinding is a random string generated by the attestor when creating the profile, it serves to protect the private data from brute force attacks (the profile data is too predictable and can be easily checked against known hashes).
If the field is not disclosed, the value of objPrivateProfile[field]
is a hash of plain-text value and blinding.
To extract just the disclosed data and remove all the bindings, use
You should double check if the user sent all the required fields because users can send private profiles on profile request (required fields in locked state) or by choosing "Insert private profile" from the menu (none of the fields in locked state).
To save the received private profile of previously mentioned Real Name Attestation profile request, use code like this:
It will save the profile in the tables private_profiles
and private_profile_fields
, which you can later query to read the data.
On the light node, it is also possible, but more complicated. You need to ask the Hub for all the attestation for user address and then you would need to request proofs for those attestation units:
This is a message type, which gets a reply with a response type message.
Example:
Following is a list of request
type JSON messages that are sent over the network:
This requests returns response with nodes that accept incoming connections.
This requests returns response with list of witnesses.
This requests returns response with unit data. Example shows a unit, which created a new asset.
This requests returns response whether composed unit was accepted or not. Unit object can be composed with ocore/composer.js
.
This requests returns response with chat bots of connected hub.
This requests returns response with asset metadata (unit and registry). Example gets WCG Point asset metadata.
This requests returns response with transaction history of specified addresses.
This requests returns response with attestation units for specified field and value.
This requests returns response with all the attestation data about specific address.
This requests returns response with spendable inputs for specified asset, amount and addresses.
This requests returns response with address definition (smart-contracts have address smart-contract definition only after somebody has spent from it).
This request returns a response with balances of one or more addresses.
It is possible for users to post a profile information about themselves, this request returns a response with all the profile data units.
Get information about the current state of user votes for system variables such as op_list
, threshold_size
, base_tps_fee
, tps_interval
, and tps_fee_multiplier
. Since later votes by the same user concerning the same variable overwrite their previous votes, only the latest votes are shown. The response also includes the balances of the voting addresses. They determine the weight of the votes.
This API is for merchants and payment processors who participate in the Obyte cashback program.
After you accepted payment from the customer, you use this API to request cashback for your customer. You send a POST request to:
https://byte.money/new_purchase
(for testnet use https://byte.money/test/new_purchase
)
and pass the following parameters:
partner
: (string) your partner name you were assigned when you signed up for the program.
partner_key
: (string) partner key used to authenticate your requests.
customer
: (string) any id of the customer who made a purchase such as customer name or id in your database. This field must be the same when the same customer makes another purchase.
order_id
: (string) unique id of the order associated with the purchase. You should never request cashback for the same order id again.
description
: (string) description of the purchase.
merchant
: (string) this field is used by payment processors only to identify the individual merchant. When another purchase is made from the same merchant, this fiels should be the same.
currency
: (string) currency of the purchase. Supported values: USD, EUR, RUR, GBYTE, BTC. If the purchase was paid in any other currency, you should convert its amount to any of the supported currencies except GBYTE.
currency_amount
: (number) amount of the purchase in currency
.
partner_cashback_percentage
: (number) the percentage of the amount you want to pay to the customer out of your own funds in addition to the regular cashback. Obyte will add the same percentage out of the distribution fund (merchant match). Default it 0. You have to deposit the funds in advance in order to fund this option.
purchase_unit
: (string) unit (transaction id) of the customer's purchase if it was paid in GBYTE.
The response is in JSON format. It always contains a result
field, which is either 'ok' or 'error'.
If the cashback was successfully sent to the customer, the response has "result": "ok"
and also indicates the cashback amount and the unit it was sent in:
In case of an error, the response has "result": "error"
and error description in error
field:
Obyte can accept Byte payments for you and notify you when the Bytes have arrived to your account. There is also an easy-to-setup Wordpress/Woocommerce plugin.
Step 2: We notify you (upon your choice), either by mail and/or by a GET or a POST to your server, when a payment for your sale has arrived, we simultaneously send you the payment to your merchant address, minus our fee. At this stage you receive a first notification with an unconfirmed "result" field. When the payment is confirmed by the network you receive a second notification with a ok "result" field. Such notification of a confirmed payment typically takes between 5 to 15 minutes from the customer click to pay (live mode) or 1 minute (in test mode). You then have to handle the notification on your end so that you actualy process your customer order. You can process the delivery as soon as you received the ok notification (processing on unconfirmed notification is at your own risk!). In any case always check on your end that received_amount does match amount_asked_in_B before processing the order or contact your customer for missing Bytes (the event of an incomplete payment is not an error case on our end but it must be one on your end).
order_UID (mandatory): Your merchant order ID (should match /^[a-zA-Z0-9-]{4,20}$/).
merchant_return_url (optional): The full URL we will POST or GET to notify you a payment arrived.
mode_notif (optional, default is get): Shall we GET or POST the notification to your server? (should match get | post).
merchant_email (optional): Fill in your email address if you want to be notified by email when payment arrives OR to get an alert email if we were not able to GET or POST to notify you on your merchant_return_url. Take care to whitelist emails from "noreply@obyte-for-merchants.com" (you can usually do that by adding this email to your contacts).
amount (mandatory): The amount charged to your customer in currency (should match /^[0-9]+.?[0-9]*$/). Minimum amount is 1500 Bytes (or its equivalent in currency) Maximum amount is 1000 GBytes.
byteball_merchant_address (mandatory): Your unique Obyte merchant address to receive your payment.
mode (mandatory): live | test. Are you testing the implementation or are you running the Obyte payment button from a production server? (should match live or test and will be returned as is on merchant_return_url).
attach (optional): free text field (255 chars). Will be returned as is for your own convenience (must match /^[a-zA-Z0-9_.@,=?& -]{0,255}$/).
mode: live | test
WARNING! WARNING! WARNING! Always check this field as in test mode the notification of payment received is always sent for integration and test purpose without any effective payment. Do not use the test mode on a production merchant server.
result: nok | incoming | unconfirmed | ok
'nok': check error_msg for text error.
'incoming': the payment has been sent to us by your customer but it is still waiting for network confirmation.
'unconfirmed': the payment has been sent to you but it is still waiting for network confirmation.
'ok': final step. The payment unit we sent to you has been confirmed by the network. Process the order on your end.
error_msg (occurence: 1 on result=nok or 0): human readable text field
order_UID: Your merchant order ID
currency: B (for Bytes, see rest above).
amount_asked_in_currency (occurence: 0 on currency=B or 1): decimal
WARNING! WARNING! WARNING! Always check amount_asked_in_currency does match the amount of the order in your own database and in your expected currency.
currency_B_rate (occurence: 0 on currency=B or 1): decimal
amount_asked_in_B: integer (in Bytes)
fee: our fee (in Bytes)
received_amount: the amount we received (in Bytes).
WARNING! WARNING! WARNING! check on your end that received_amount does match amount_asked_in_B before processing the order. Contact your customer if some Bytes are missing from what you expected (the event of an incomplete payment is not an error case on our end but it must be one on your end).
receive_unit: the DAG unit that shows the payment from your customer to our Obyte address.
amount_sent: the amount sent to your byteball_merchant_address (received_amount minus fee)
unit: the DAG unit that shows the payment from our Obyte address to your byteball_merchant_address
sha256_digest: the digest of the notification is computed as sha256(secret_key+order_UID+merchant_return_url+merchant_email) where '+' means concatenate.
WARNING! WARNING! WARNING! You SHOULD re-calculate on your end the sha256 digest on each notification to check if it does match sha256_digest. An attacker could POST or GET to your merchant server a false notification but he will never be able to compute a correct sha256_digest. In test mode secret_key is 134c8nfWK4XrrfkgN1UJ9ytqae76d7uDbs.
attach: free text field (255 chars) returned "as is" from step 1 for your own convenience.
Note: if you filled in the optional merchant_email on step 1 you will receive the same information in an email plus possible alert email when - for any reason - if we were not able to notify you on merchant_return_url (for example a server time out on your end). We advise you to provide the merchant_email in order to receive such alert emails. We strongly advise you to whitelist noreply@obyte-for-merchants.com
If you are developing a service on Obyte and your programming language is node.js, your best option is to just require() the ocore modules that you need.
If you are programming in another language, or you'd like to run your Obyte node in a separate process, you can still access many of the functions of headless-obyte
and ocore
by creating a thin RPC wrapper around the required functions and then calling them via JSON-RPC.
To get started, we can add RPCify to existing project or directly to headless-obyte with this command:
From another Node.js app, calling the function would look something like this:
From another Node.js app, sending function calls and listening to events would look something like this:
If you run an exchange, you will likely want to interact with your Obyte node via RPC interface.
By default, RPC service is not enabled for security reasons. To enable it, you should start your headless node differently: instead of node start.js
, cd to tools
folder and start RPC-enabled node:
The command returns information about the current state of the DAG.
The command has no parameters and returns an object with 4 fields:
connections
: number of incoming and outgoing connections
last_mci
: the highest known main chain index (MCI)
last_stable_mci
: last stable MCI (stability point)
count_unhandled
: number of unhandled units in the queue. Large number indicates that sync is still in progress, 0 or small number means that the node is synced (it can occasionally go above 0 when new units are received out of order).
This command generates a new address in your wallet. You will likely want to use it to create a new deposit address and bind it to a user account.
Example usage:
The command has no parameters and the response is a newly generated wallet address (32-character string).
This command validates a wallet address and returns true
or false
.
You will likely want to use it before saving a withdrawal address for a customer. There is verifyaddress
alias for this method too.
Returns the balance of the specified address or the entire wallet.
Example usage for querying wallet balance:
Querying balance of an individual address:
To query the balance of the entire wallet, parameters must be empty. To query the balance of an individual address, pass it as the only element of the params array.
The response is an object, keyed by asset ID ("base" for Bytes). For each asset, there is another nested object with keys stable
and pending
for stable and pending balances respectively. Balances are in the smallest units (bytes for the native currency), they are always integers.
If the queried address is invalid, you receive error "invalid address". If the address does not belong to your wallet, you receive error "address not found".
Use it to get the list of transactions on the wallet or on a specific address.
Example request for transactions on the entire wallet (all addresses):
On individual address:
To query the transactions on an individual address, pass it as the only element of the params
array. In this case, only transactions in bytes are returned. If the passed address is invalid, you receive error "invalid address".
Using params
as an object
gives more flexibility of parameter ordering, but if address
is set in a object or first element of array, all other parameters are ignored.
To get the list of transactions in a particular asset, set an asset
parameter in params
. If asset
is null
or omitted, transactions in bytes will be returned.
To query the list of transactions since a particular main chain index (MCI), specify since_mci
property in the params
object, e.g. "params": {"since_mci":254000}
or "params": {"since_mci":254000; "asset": "f2TMkqij/E3qx3ALfVBA8q5ve5xAwimUm92UrEribIE="}
. The full list of matching transactions will be returned, however large it is.
To query an individual transaction, specify its unit in the params
object: "params": {"unit":"vlt1vzMtLCIpb8K+IrvqdpNLA9DkkNAGABJ420NvOBs="}
.
The response is an array of transactions in reverse chronological order.
Each transaction is described by an object with the following fields:
action
: string, one of invalid
, received
, sent
, moved
.
amount
: integer, amount of the transaction in the smallest units
my_address
: string, the address that belongs to your wallet and received funds (for received
and moved
actions only)
addressTo
: string, the address where the funds were moved (for moved
and sent
actions only)
arrPayerAddresses
: array of payer addresses (for received
only)
confirmations
: integer 0 (pending) or 1 (final), shows confirmation status of the transaction
unit
: string, unit of the transaction (also known as transaction id)
fee
: integer, fee in bytes
time
: integer, seconds since the Epoch
level
: integer, level of the unit in the DAG
mci
: integer, MCI of the unit. It can change while the unit is still pending and becomes immutable after the unit gets final
To operate an exchange, you'll want to wait for new deposits using the following scenario:
call getinfo
and remember last_stable_mci
, you'll use it in the following (not this one) iteration;
call listtransactions
with since_mci
set to last_stable_mci
remembered from the previous (not this one!) iteration;
look for transactions with action
s received
and moved
(you need moved
in case one user withdraws to a deposit address of another user) and identify the user by my_address
;
wait;
repeat the cycle.
In each cycle, save last_stable_mci
in persistent storage and start from it after the wallet is restarted.
If you support several Obyte-issued assets, you'll need to call listtransactions
with that asset parameter and store last_stable_mci
from getinfo
method for each asset individually.
Use this command to withdraw bytes or another asset. Example usage:
There are 3 parameters to this command:
destination address (32-character string);
amount in bytes or smallest indivisible units (integer);
(optional) asset (44-character string or null
). If missing or null
, the asset is assumed to be bytes.
On success, the response is the unit of the spending transaction (string). If the command failed, an error message is returned. It is possible that the command returns error due to lack of confirmed funds, in this case you should retry the command in a few minutes. If the request timed out without returning an error, do not retry automatically, the funds might be already sent!
Event bus provides a way to subscribe to events from any of the services running.
Headless wallet is needed for bots that hold private keys and can send transactions. This event is emitted when passphrase has been entered by bot admin and single wallet is loaded. Before that event, bot outputs device address, device pubkey and pairing key.
This event is emitted when bot executes series database queries to upgrade the database to new version.
This event is emitted when bot finishes database upgrade.
The url
parameter is protocol + domain + path (for example: wss://obyte.org/bb
).
This event is emitted when Hub sends message about new wallet version.
This event is emitted when wallet updated rates that they got from Hub.
This event is emitted on successful pairing (also when user removed and re-added the bot).
This event is emitted when text message is received by bot.
This event is emitted when object message is received by bot.
This is a message type, which doesn't require response back.
Example:
Following is a list of justsaying
type JSON messages that are sent over the network:
This will subscribe you to updates of the currently active system variables and votes for them.
Immediately after this request, you'll get the current state of all system variables and their past values with the MCIs when they were voted in:
Similar data will be pushed to you whenever any system variable changes as a result of vote count.
Also, user votes will be pushed to you as soon as they are sent by users (first, with is_stable=0
, then with is_stable=1
as soon as the voting transaction becomes final):
AAs can be by manually entering the key/value pairs to GUI wallet, , making the or with the .
Previous example shows how to send any data to DAG, but if you would want to store data that is searchable and can be used in or then you would need to post it as key-value pairs and with app type data_feed
. If we also send it always from the same first address in our wallet then we have basically become an oracle.
Anybody on Obyte network can post some data about themselves, which will be linked to their address. This app type is called profile
. Payload for that message can contain any keys. For example, web service lets you post a profile with keys like this:
Attestation profile is similar to previous one with one difference that attestation
type message are posted by somebody and they describe somebody else. These profiles are used to do KYC by .
bot creates an attestation
profile, which keeps all the data private on user device while makes it possible for the others to recognize unique users by user_id
(it's a salted hash of first_name
, last_name
, dob
and country
) and also makes it possible to validate with profile_hash
if user provides them a private profile.
In case user would want to make their profile public like with and , true
could be sent as a second parameter of hideProfile()
function. To avoid people doxxing themselves, the possibility to make public attestation with Real Name Attestation has not been included. This choice is only on Email Attestation and Steem Attestation.
In order for the user to share private profile with others, your bot should also .
If there is no plan to provide private attestations and all the attestation will always be public then there is no need for user_id
. Then the attestation profile can be as simple as this, but the attestator needs to make sure that the same username attestation is not done to multiple users. One example of that kind is .
For public attestation, there is not private profile to send to user's wallet because the bots who need information about the user could simply just .
Anybody can post a poll and let others vote on it, this can be done like this data and it will also appear on web service from where it will direct users to .
Shorter code examples using composer library are available in .
If you wish to send data to DAG database periodically then full code examples for , or can be found on Github.
This can be used to KYC your users prior to providing a service, see . You may need it e.g. to comply with regulations or protect against fraud.
ICO bot already uses the attested private profiles in order to allow only verified users to invest and to collect their personal data. You can use its code as reference.
This is a profile request message that asks the fields that all profiles have:
This is a profile request message that asks the email field that all profiles have:
This is a profile request message that asks the fields that all profiles have:
If your bot is setting up between 2 users then they need to get each others private profiles. All you need to do is first request the profiles from both users, then receive and save the profiles from both users and then send the first user and second user's profile and the second user a first user's profile.
profiles are all private, but and bots let user to choose whether they like to publish the data publicly or privately and bot publishes attestations only publicly. So, if you are asking attested email address from the user, you should let them do it with public attestation too.
For full nodes that is easy, you ask user to , after which you get a wallet address that they own. Then you just query your full node database for that data:
Above examples here show how to use different existing profiles from other attestation bots, but if you would like to create your own attestation bot, it is recommended to click on the links of these bots on this article and take a look how they have been done from the source code. There is also article page.
You can add your own communication protocol on top of the Obyte one. See event .
This is API documentation to integrate cashback to your point of sale device, but you could also , where this API has already been implemented. The first thing you need in order to get partner name and partner key.
address
: (string) Wallet address or email address of the customer. The cashback will be sent to this address. If it is email address, a will be sent. If the textcoin is not claimed within 1 week, it will be taken back.
A Obyte light wallet to provide us with your merchant Obyte address (you do not need to run the heavy full wallet, we are running it for you). Download the wallet from .
If you want to use our Woocommerce plugin skip reading now and go to:
Upon going live a merchant_id and a secret_key. Contact us on #merchants channel to get your merchant_id
and you secret_key
.
Step 1: Either in test mode or in live mode, customize the Obyte quick payment button script with your merchant parameters and insert it in your checkout web page. Obyte quick payment button init script has to be inserted in the header section of your checkout page and the button itself can be displayed anywhere on the page in a Div container. The test mode triggers the whole notification process except there is no real payment. Once you are done with your tests, switch to live mode by changing the "mode" parameter from "test" to "live" in the button init script. See example.html:
currency (optional, default B): Currency from which convert the amount into Bytes (should be B (for Bytes), BTC, ETH, USD, EUR, PLN, KRW, GBP, CAD, JPY, RUB, TRY, NZD, AUD, CHF, UAH, HKD, SGD, NGN, PHP, MXN, BRL, THB, CLP, CNY, CZK, DKK, HUF, IDR, ILS, INR, MYR, NOK, PKR, SEK, TWD, ZAR, VND, BOB, COP, PEN, ARS, ISK). Conversion is done automaticaly in real time at rates from .
merchant_id (mandatory): In test mode merchant_id is test_merchant. Contact us on #merchants channel to get a merchant id when you first go live.
qrcode (optional): you can here indicate your own qrcode url mask to use the qrcode api of your choice, using {text} as url placeholder. By default, the Google QRCode API is used, that is to say the value is: '' But you can change it and choose for instance the Chinese liantu API by setting the value: ''
For exchanges and other custody wallets, there is a specialized . However it is limited and exposes only those functions that the exchanges need.
If you are developing a service on Obyte and your programming language is node.js, your best option is to just require()
the ocore modules that you need (most likely you need and various modules inside ). This way, you'll also be running a Obyte node in-process.
See the documentation about for more details.
To expose the required functions via JSON-RPC, create a project that has headless-obyte
, ocore
(and any other ocore modules) and as dependencies:
This headless node works as usual, plus it listens to port 6332 of loop-back interface (configured in or conf.json) for JSON-RPC commands. Here are some of the commands that are usually needed by custody wallet solutions, like exchanges (these are similar to older Bitcoin node RPC methods): getinfo
, getnewaddress
, validateaddress
, getbalance
, listtransactions
, sendtoaddress
.
There are more methods available, which documentation can be read from , which is generated from JSDoc code comments.
These are all simple HTTP request, but you can build your own too and even make it to listen events.
If the destination address is invalid, the command returns error "invalid address". To avoid this, it is recommended to validate user-entered withdrawal address using above or or in NodeJs:
More detailed documentation of all RPC service methods can be found there
This event is emitted when there is a pairing attempt, this enables bot to decide with the code if the pairing code is valid or not. If you would like to accept any pairing code then .
You can add your own communication protocol on top of the Obyte one. See Request example .
You can add your own communication protocol on top of the Obyte one. See JustSaying example .
You can add your own communication protocol on top of the Obyte one. See event .
- official core networking and consensus library for node.js
- official wallet and messaging library for node.js
- official messaging library for node.js and browsers
- Lightweight wallet library for working with Obyte from browser and node.js
- Lightweight networking-only JS library (predecessor of obyte.js)
- Lightweight wallet library JS library (based on obyte.js)
- Instant Obyte devnet network set up and testing
- Script for parsing variables from AA
- Node.js library for interacting with Autonomous Agents and tracking their state
- Helper library for AA data and events
- a general purpose library for payment channels
- pay-as-you-go payment library for API use
- A very light C/C++ implementation of Obyte for Arduino ESP8266 and ESP32
- A Multisig HD Obyte Light Wallet API Service
- A Simple Command Line Interface Wallet for Obyte using OWS/OWC
- API for headless wallet, which can be used as internal exchange ledger with its own tokens.
- JSON-RPC client on Python
Smart contracts on Obyte are expressions that evaluate to true or false, the money stored on the contract can be spent only when it evaluates to true.
The smart contract language is declarative, meaning that it expresses what conditions must be met to allow movement of money, rather than how the decisions are made. This makes it easy to see that the implementation of the contract matches its intent, and hard to make mistakes (which cannot be undone in distributed ledgers).
When you want to create a new smart contract with a user, your sequence is usually as follows:
you ask the user to send his payment address (it will be included in the contract)
you define a new contract using the user's address and your address as parties of the contract
you pay your share to the contract
at the same time, you send a specially formatted payment request (different from the payment request above) to the user to request his share. You start waiting for the user's payment
the user views the contract definition in his wallet and agrees to pay
you receive the payment notification and wait for it to get confirmed
after the payment is confirmed, the contract is fully funded
We'll discuss creating and offering contracts below. These two steps are similar to what happens in the GUI wallet when a user designs a new contract.
Create a JSON object that defines the contract:
Create another object that describes the positions of your and user addresses in the above definition:
The keys of this object are r
(from "root") followed by indexes into arrays in the definition's or
and and
conditions. Since the conditions can be nested, there can be many indexes, they are separated by dot.
Then you create the smart contract address:
If the address was successfully created, it was also already automatically sent to the user, so the user's wallet will know it.
To request the user to pay his share to the contract, create the below Javascript object objPaymentRequest
which contains both the payment request and the definition of the contract, encode the object in base64, and send it over to the user:
The user's wallet will parse this message, display the definition of the contract in a user-readable form, and offer the user to pay the requested amount. Your payment-waiting code will be called when the payment is seen on the DAG.
Since smart-contracts are private until they have been spent, users might loose them if they probably didn't backup their wallet or restored from seed words. Some attestation bots lock user's reward for months or years into smart-contracts and it would be more user-friendly if users could still restore the smart-contract even after they have lost it. Since all parties (2 or more) have the definition of the smart-contract in their wallet, one could ask to resend it from the other. With chat bots, this could be done like this:
However, the language is not as powerful as Ethereum’s Solidity, it is not Turing-complete, it doesn’t allow to code any program, rather it is a domain specific language for money on the distributed ledger. They are more like for Bitcoin. If you are looking to build more complex dApps that are as capable as Ethereum smart-contracts, then you need to use .
Money on Obyte is stored on addresses. Address is just a hash (plus checksum) of an , and the address definition is an expression in the Obyte smart contract language that evaluates to either true
or false
.
Example of vesting contracts: , , . Examples with oracles: , . Examples with tokens: , .
More definition examples can be seen on .