Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Step by step tutorials to get started with example bots.
Obyte provides users with the ability to create Smart Contracts as well as simple prosaic contracts.
Obyte has a payment gateway, Woocommerce plugin and cashback program for merchants who would like to accept Bytes for goods or services.
Instructions how to set up headless wallet on a blank Debian machine.
git clone https://github.com/xJeneKx/Tutorial-2-source
cd Tutorial-2-source
npm installpairingCode = 'Aly99HdWoCFaxJiIHs1ldREAN/[email protected]/bb#' + code;eventBus.on('paired', (from_address, pairing_secret) => {
console.error('paired', from_address, pairing_secret);
});b = '<br>Please login using this pairing code: <a href="obyte:' + pairingCode + '">' + pairingCode + '</a>';{
"deviceName": "Obyte Denmark",
"permanent_pairing_secret": "0000",
"control_addresses": [
"DEVICE_ADDRESS_1",
"DEVICE_ADDRESS_2",
"DEVICE_ADDRESS_3"
],
"payout_address": "YOUR_WALLET_ADDRESS",
"bLight": false
}node start.js 1>log 2>errlog &{
"logToSTDOUT": false,
"LOG_FILENAME": "/dev/null"
}let dataURL = await QRCode.toDataURL("obyte:" + pairingCode);
b = '<br>Please login using this pairing code: <a href="obyte:' + pairingCode + '">' + pairingCode + '</a><br><img src="' + dataURL + '">';eventBus.on('paired', (from_address, pairing_secret) => {
let device = require('ocore/device');
assocCodeToDeviceAddress[pairing_secret] = from_address;
device.sendMessageToDevice(from_address, 'text', 'ok');
});async function amILogged(ctx) {
let url = ctx.request.url;
let match = url.match(/code=([0-9]+)/);
if (match && assocCodeToDeviceAddress[match[1]]) {
ctx.cookies.set('ac', match[1]);
ctx.body = 'true';
} else {
ctx.body = 'false';
}
}app.use(mount('/amilogged', amILogged));var step = '<%= step %>';
var code = '<%= code %>';
if (step === 'login') {
setInterval(() => {
fetch('/amilogged?code=' + code)
.then((response) => {
return response.text();
}).then(result => {
if (result === 'true') {
location.reload(true);
}
})
.catch(alert);
}, 5000);
}let assocCodeAndNumberToAddress = {};step = 'paid';
let address = await getAssocAddress(assocCode, 1);
let dataURL = await QRCode.toDataURL("obyte:" + address + '?amount=100');
b = '<br>Please pay for the article. <br>Address: ' + address + '<br>Amount: 100<br><img src="' + dataURL + '"><br>' +
'<a href="obyte:' + address + '?amount=100">Pay</a>';function getAssocAddress(assocCode, number) {
return new Promise(resolve => {
let name = assocCode + '_' + number;
if (assocCodeAndNumberToAddress[name]) {
return resolve(assocCodeAndNumberToAddress[name]);
} else {
headlessWallet.issueNextMainAddress(address => {
assocCodeAndNumberToAddress[name] = address;
return resolve(address);
});
}
});
}let assocCodeToPaid = {};async function amIPaid(ctx) {
let code = ctx.cookies.get('ac');
ctx.body = (code && itPaid(code)) ? 'true' : 'false';
}
function itPaid(code) {
return !!assocCodeToPaid[code];
}app.use(mount('/amipaid', amIPaid));eventBus.on('new_my_transactions', (arrUnits) => {
const device = require('ocore/device.js');
db.query("SELECT address, amount, asset FROM outputs WHERE unit IN (?)", [arrUnits], rows => {
rows.forEach(row => {
if (row.amount === 100 && row.asset === null) {
for (let key in assocCodeAndNumberToAddress) {
if (assocCodeAndNumberToAddress[key] === row.address) {
let assocCode = key.split('_')[0];
assocCodeToPaid[assocCode] = true;
device.sendMessageToDevice(assocCodeToDeviceAddress[assocCode], 'text', 'I received your payment');
return;
}
}
}
})
});
});
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.
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.
git clone https://github.com/byteball/bot-example
cd bot-example
npm install
cp .env.testnet .envlet arrQueue = [];if(parseInt(args[1]) <= Date.now()){
device.sendMessageToDevice(from_address, 'text', "Incorrect time");
}else {
arrQueue.push({city: args[0], time: args[1], device_address: from_address});
device.sendMessageToDevice(from_address, 'text', "ok");
}headlessWallet.readSingleAddress(address => {
my_address = address;
console.error('my_address', address)
});
eventBus.on('text', (from_address, text) => {
text = text.trim();
const device = require('ocore/device.js');
const args = text.toLowerCase().split(':');
if (args.length === 2){
switch (args[0]) {
case 'berlin':
case 'moscow':
case 'helsinki':
case 'washington':
if(parseInt(args[1]) <= Date.now()){
console.error('dateNow', Date.now());
device.sendMessageToDevice(from_address, 'text', "Incorrect time");
}else {
arrQueue.push({city: args[0], time: args[1], device_address: from_address});
device.sendMessageToDevice(from_address, 'text', "ok");
}
break;
default:
device.sendMessageToDevice(from_address, 'text', "City not support");
break;
}
}else {
device.sendMessageToDevice(from_address, 'text', "Incorrect command");
}
});function checkQueue(){
arrQueue.forEach((el, index) => {
if(el.time <= Date.now()){
let result = JSON.parse(body);
postDataFeed(el.city, el.time, result.current.temp_c);
}
});
}
setTimeout(checkQueue, 60000);function postDataFeed(city, time, temp){
const network = require('ocore/network.js');
const composer = require('ocore/composer.js');
let data_feed = {};
data_feed[city + '_' + time] = temp;
let params = {
paying_addresses: [my_address],
outputs: [{address: my_address, amount: 0}],
signer: headlessWallet.signer,
callbacks: composer.getSavingCallbacks({
ifNotEnoughFunds: console.error,
ifError: console.error,
ifOk: function(objJoint){
network.broadcastJoint(objJoint);
}
})
};
let objMessage = {
app: "data_feed",
payload_location: "inline",
payload: data_feed
};
params.messages = [objMessage];
composer.composeJoint(params);
}let data_feed = {};
data_feed[city + '_' + time] = temp;paying_addresses: [my_address],
outputs: [{address: my_address, amount: 0}]let objMessage = {
app: "data_feed",
payload_location: "inline",
payload: data_feed
};composer.composeJoint(params);

On Obyte, users can issue new assets and define rules that govern their transferability.
onSigned indicates that the contract was successfully signed and unit with contract's hash was posted into DAG."use strict";
const headlessWallet = require('headless-obyte');
const eventBus = require('ocore/event_bus.js');
const prosaic_contract = require('headless-obyte/prosaic_contract_api.js');
function onReady() {
prosaic_contract.listenForPendingContracts(headlessWallet.signWithLocalPrivateKey);
let contract_text = "I pay Tom $20, if he sends me a pair of his Air Jordans.";
let contract_title = "Air Jordans Purchase from Tom";
let contract_is_valid_until = 24; //hours
headlessWallet.readFirstAddress(my_address => {
let peer_address = "62SU7724ME5MWB7VEP64VURYZHDRSDWN";
let peer_device_address = "0PUQK5HOEKS4NONGL2ITJI3HMJ76P3LKI";
prosaic_contract.offer(contract_title,
contract_text,
my_address,
peer_address,
peer_device_address,
contract_is_valid_until,
[],
headlessWallet.signWithLocalPrivateKey, {
onOfferCreated: function(objContract) {
console.log("on offer created", objContract);
},
onResponseReceived: function(accepted) {
console.log("on response received", accepted);
},
onSigned: function(objContract) {
console.log("on signed", objContract);
},
onError: function(error) {
console.log("on error", error);
}
});
});
};
eventBus.once('headless_wallet_ready', onReady);"dependencies": {
"headless-obyte": "git+https://github.com/byteball/headless-obyte.git",
"ocore": "git+https://github.com/byteball/ocore.git"
// .....
}var headlessWallet = require('headless-obyte');
var network = require('ocore/network.js');
var composer = require('ocore/composer.js');
var my_address = ''; // set definer address.
var asset = {}; // defined asset here.
composer.composeAssetDefinitionJoint(my_address, asset, headlessWallet.signer,
{
ifError: console.error,
ifNotEnoughFunds: console.error,
ifOk: function(objJoint, assocPrivatePayloads, composer_unlock) {
network.broadcastJoint(objJoint);
console.error('==== Asset ID:'+ objJoint.unit.unit);
}
}
);var asset = {
cap: 1000000,
is_private: false,
is_transferrable: true,
auto_destroy: false,
fixed_denominations: false,
issued_by_definer_only: true,
cosigned_by_definer: false,
spender_attested: false
}var asset = {
// ....
fixed_denominations: true,
denominations: [
{denomination: 1, count_coins: 1e10},
{denomination: 2, count_coins: 2e10},
{denomination: 5, count_coins: 1e10},
{denomination: 10, count_coins: 1e10},
{denomination: 20, count_coins: 2e10},
{denomination: 50, count_coins: 1e10},
{denomination: 100, count_coins: 1e10},
{denomination: 200, count_coins: 2e10},
{denomination: 500, count_coins: 1e10},
{denomination: 1000, count_coins: 1e10},
{denomination: 2000, count_coins: 2e10},
{denomination: 5000, count_coins: 1e10},
{denomination: 10000, count_coins: 1e10},
{denomination: 20000, count_coins: 2e10},
{denomination: 50000, count_coins: 1e10},
{denomination: 100000, count_coins: 1e10}
]
};var asset = {
// ....
spender_attested: true,
attestors: ["ADDRESS1", "ADDRESS2"]
};var asset = {
// ....
issue_condition: ["and", [
["has one", {what: "output", asset: "this asset"}],
["has", {what: "output", asset: "base", address: "MO7ZZIU5VXHRZGGHVSZWLWL64IEND5K2"}],
["has one equal", {
equal_fields: ["amount"],
search_criteria: [{what: "output", asset: "base"}, {what: "output", asset: "this asset"}]
}]
]],
transfer_condition: ["and", [
["has one", {what: "output", asset: "this asset"}],
["has one equal", {
equal_fields: ["address", "amount"],
search_criteria: [{what: "output", asset: "base"}, {what: "output", asset: "this asset"}]
}]
]]
};local: (this is the default) use /usr/sbin/sendmailvar headlessWallet = require('headless-obyte');
let opts = {
asset: null,
amount: 1000, // bytes
to_address: "textcoin:[email protected]",
email_subject: "Payment in textcoin"
};
headlessWallet.issueChangeAddressAndSendMultiPayment(opts, (err, unit, assocMnemonics) => {
....
});{
"textcoin:[email protected]": "barely-album-remove-version-endorse-vocal-weasel-kitten-when-fit-elbow-crop"
}https://obyte.org/#textcoin?barely-album-remove-version-endorse-vocal-weasel-kitten-when-fit-elbow-cropnpm install https://github.com/byteball/rpcify.gitvar rpcify = require('rpcify');
var eventBus = require('ocore/event_bus.js');
// this is a module whose methods you want to expose via RPC
var headlessWallet = require('headless-obyte'); // when headless-obyte is dependency of your project
//var headlessWallet = require('../start.js'); // when this script is in headless-obyte tools folder
var balances = require('ocore/balances.js'); // another such module
// most of these functions become available only after the passphrase is entered
eventBus.once('headless_wallet_ready', function(){
// start listening on RPC port
rpcify.listen(6333, '127.0.0.1');
// expose some functions via RPC
rpcify.expose(headlessWallet.issueChangeAddressAndSendPayment);
rpcify.expose(balances.readBalance, true);
rpcify.expose(balances.readAllUnspentOutputs);
rpcify.expose([
headlessWallet.readFirstAddress,
headlessWallet.readSingleWallet,
headlessWallet.issueOrSelectAddressByIndex
], true);
// expose some events
rpcify.exposeEvent(eventBus, "my_transactions_became_stable");
rpcify.exposeEvent(eventBus, "new_my_transactions");
});var rpc = require('json-rpc2');
var client = rpc.Client.$create(6333, '127.0.0.1');
client.call('issueChangeAddressAndSendPayment', [asset, amount, to_address, device_address], function(err, unit) {
...
});var WebSocket = require('ws');
var ws = new WebSocket("ws://127.0.0.1:6333");
ws.on('open', function onWsOpen() {
console.log("ws open");
ws.send(JSON.stringify({
"jsonrpc":"2.0",
"id":1,
"method":"readBalance",
"params":["LUTKZPUKQJDQMUUZAH4ULED6FSCA2FLI"]
})); // send a command
});
ws.on('error', function onWsError(e){
console.log("websocket error"+e);
})
ws.on('message', function onWsMessage(message){ // JSON responses
console.error(message);
// {"jsonrpc":"2.0","result":{"base":{"stable":0,"pending":0}},"error":null,"id":1} // response to readBalance
// {"event":"my_transactions_became_stable","data":[["1pLZa3aVicNLE6vcClG2IvBe+tO0V7kDsxuzQCGlGuQ="]]} // event my_transactions_became_stable
});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.

const headlessWallet = require('headless-obyte');
const eventBus = require('ocore/event_bus.js');
function postData() {
let json_data = {
age: 78.90,
props: {
sets: [
'0bbb',
'zzz',
1/3
]
}
};
let objMessage = {
app: 'data',
payload_location: 'inline',
payload: json_data
};
let opts = {
messages: [objMessage]
};
headlessWallet.issueChangeAddressAndSendMultiPayment(opts, (err, unit) => {
if (err){
/*
something went wrong,
maybe put this transaction on a retry queue
*/
return;
}
// handle successful payment
});
}
eventBus.on('headless_wallet_ready', postData);

const headlessWallet = require('headless-obyte');
const eventBus = require('ocore/event_bus.js');
function postDataFeed(first_address) {
let json_data = {
time: new Date().toString(),
timestamp: Date.now()
};
let objMessage = {
app: 'data_feed',
payload_location: 'inline',
payload: json_data
};
let opts = {
paying_addresses: [first_address],
messages: [objMessage]
};
headlessWallet.issueChangeAddressAndSendMultiPayment(opts, (err, unit) => {
if (err){
/*
something went wrong,
maybe put this transaction on a retry queue
*/
return;
}
// handle successful payment
});
}
eventBus.once('headless_wallet_ready', () => {
headlessWallet.readFirstAddress(postDataFeed);
});let json_data = {
name: 'a',
about: 'b',
location: 'c',
website: 'd',
email: 'e'
};
let objMessage = {
app: 'profile',
payload_location: 'inline',
payload: json_data
};const conf = require('ocore/conf.js');
const objectHash = require('ocore/object_hash.js');
let bPublic = false; // ask user if they want public or private profile
function hideProfile(profile, bPublic = false){
const composer = require('ocore/composer.js');
let hidden_profile = {};
let src_profile = {};
for (let field in profile){
let value = profile[field];
let blinding = composer.generateBlinding();
let hidden_value = objectHash.getBase64Hash([value, blinding], true);
hidden_profile[field] = hidden_value;
src_profile[field] = [value, blinding];
}
let shortProfile = {
first_name: profile.first_name,
last_name: profile.last_name,
dob: profile.dob,
country: profile.country,
};
let public_profile;
if (bPublic) {
public_profile = profile;
public_profile.user_id = objectHash.getBase64Hash([shortProfile, conf.salt]);
return [public_profile, null];
}
else {
public_profile = {
profile_hash: objectHash.getBase64Hash(hidden_profile, true),
user_id: objectHash.getBase64Hash([shortProfile, conf.salt])
};
return [public_profile, src_profile];
}
}
let profile = {
first_name: "", // required
last_name: "", // required
dob: "", // required
country: "", // required
us_state: "", // optional
id_type: "" // optional
};
for (let field in profile){
if (typeof profile[field] === 'undefined' || profile[field] === null || profile[field] === '') {
// remove negative values, except number 0
delete profile[field];
}
else {
// convert everything else to string
profile[field] = String(profile[field]);
}
}
let [public_data, src_profile] = hideProfile(profile, bPublic);
let json_data = {
address: 'ABCDEF...',
profile: public_data
};
let objMessage = {
app: 'attestation',
payload_location: 'inline',
payload: json_data
};let json_data = {
address: 'ABCDEF...',
profile: {
username: ""
}
};
let objMessage = {
app: 'attestation',
payload_location: 'inline',
payload: json_data
};let json_data = {
question: 'abc?',
choices: [
'a',
'b',
'c',
]
};
let objMessage = {
app: 'poll',
payload_location: 'inline',
payload: json_data
};let text_data = '';
let objMessage = {
app: 'text',
payload_location: 'inline',
payload: text_data
};
"use strict";
const headlessWallet = require('../start.js');
const eventBus = require('ocore/event_bus.js');
const arbiter_contract = require('ocore/arbiter_contract.js');
const device = require('ocore/device.js');
const validationUtils = require('ocore/validation_utils');
function onReady() {
let contract_text = "Bill pays Tom $20, if Tom sends Bill a pair of his Air Jordans.";
let contract_title = "Air Jordans Purchase from Tom";
let amount = 10000; // in bytes, min 10000
let asset = null;
let ttl = 24; //hours
let arbiter_address = "VYDZPZABPIYNNHCPQKTMIKAEBHWEW3SQ";
let my_contacts = `Email me at: [email protected]`;
let me_is_payer = true;
let my_address;
headlessWallet.readFirstAddress(address => {
my_address = address;
});
eventBus.on('paired', from_address => {
device.sendMessageToDevice(from_address, 'text', `My address: ${my_address}, now send me your's.`);
});
/* ================ OFFER ================ */
eventBus.on('text', (from_address, text) => {
text = text.trim();
if (!validationUtils.isValidAddress(text))
return device.sendMessageToDevice(from_address, 'text', `does not look like an address`);
let contract = {
title: contract_title,
text: contract_text,
arbiter_address: arbiter_address,
amount: amount,
asset: asset,
peer_address: text,
my_address: my_address,
me_is_payer: me_is_payer,
peer_device_address: from_address,
ttl: ttl,
cosigners: [],
my_contact_info: my_contacts
};
arbiter_contract.createAndSend(contract, contract => {
console.log('contract offer sent', contract);
});
});
/* ================ OFFER ACCEPTED ================ */
eventBus.on("arbiter_contract_response_received", contract => {
if (contract.status != 'accepted') {
console.warn('contract declined');
return;
}
arbiter_contract.createSharedAddressAndPostUnit(contract.hash, headlessWallet, (err, contract) => {
if (err)
throw err;
console.log('Unit with contract hash was posted into DAG\nhttps://explorer.obyte.org/#' + contract.unit);
/* ================ PAY TO THE CONTRACT ================ */
if (contract.me_is_payer) {
arbiter_contract.pay(contract.hash, headlessWallet, [], (err, contract, unit) => {
if (err)
throw err;
console.log('Unit with contract payment was posted into DAG\nhttps://explorer.obyte.org/#' + unit);
setTimeout(() => {completeContract(contract)}, 3 * 1000); // complete the contract in 3 seconds
});
}
});
});
/* ================ CONTRACT FULFILLED - UNLOCK FUNDS ================ */
function completeContract(contract) {
arbiter_contract.complete(contract.hash, headlessWallet, [], (err, contract, unit) => {
if (err)
throw err;
console.log(`Contract completed. Funds locked on contract with hash ${contract.hash} were sent to peer, unit: https://explorer.obyte.org/#${unit}`);
});
}
/* ================ CONTRACT EVENT HANDLERS ================ */
eventBus.on("arbiter_contract_update", (contract, field, value, unit) => {
if (field === "status" && value === "paid") {
// do something usefull here
console.log(`Contract was paid, unit: https://explorer.obyte.org/#${unit}`);
}
});
};
eventBus.once('headless_wallet_ready', onReady);const network = require('ocore/network.js');
const eventBus = require('ocore/event_bus.js');
const ws = network.getInboundDeviceWebSocket(device_address);
// function parameters: websocket, subject, body
network.sendJustsaying(ws, 'custom', 'some data');
eventBus.on('custom_justsaying', function(ws, content) {
console.log(content);
};{
type: 'justsaying',
content: {
subject: 'version',
body: {
protocol_version: protocol_version,
alt: alt,
library: name,
library_version: version,
program: program,
program_version: program_version
}
}
}{
type: 'justsaying',
content: {
subject: 'free_joints_end',
body: null
}
}{
type: 'justsaying',
content: {
subject: 'private_payment',
body: privateElement
}
}{
type: 'justsaying',
content: {
subject: 'my_url',
body: my_url
}
}{
type: 'justsaying',
content: {
subject: 'want_echo',
body: random_echo_string
}
}{
type: 'justsaying',
content: {
subject: 'your_echo',
body: echo_string
}
}{
type: 'justsaying',
content: {
subject: 'hub/login',
body: {
challenge: challenge,
pubkey: pubkey,
signature: signature
}
}
}{
type: 'justsaying',
content: {
subject: 'hub/refresh',
body: null
}
}{
type: 'justsaying',
content: {
subject: 'hub/delete',
body: message_hash
}
}{
type: 'justsaying',
content: {
subject: 'hub/challenge',
body: challenge
}
}{
type: 'justsaying',
content: {
subject: 'hub/message',
body: {
message_hash: message_hash,
message: message
}
}
}{
type: 'justsaying',
content: {
subject: 'hub/message_box_status',
body: 'has_more'
}
}{
type: 'justsaying',
content: {
subject: 'light/have_updates',
body: null
}
}{
type: 'justsaying',
content: {
subject: 'light/sequence_became_bad',
body: arrUniqueUnits
}
}{
type: 'justsaying',
content: {
subject: 'light/new_address_to_watch',
body: address
}
}{
type: 'justsaying',
content: {
subject: 'bugreport',
body: {
message: message,
exception: exception
}
}
}{
type: 'justsaying',
content: {
subject: 'hub/push_project_number',
body: {
projectNumber: projectNumber
}
}
}{
type: 'justsaying',
content: {
subject: 'new_version',
body: {
version: version
}
}
}{
type: 'justsaying',
content: {
subject: 'exchange_rates',
body: exchangeRates
}
}{
type: 'justsaying',
content: {
subject: 'upgrade_required',
body: null
}
}{
type: 'justsaying',
content: {
subject: 'watch_system_vars',
body: null
}
}{
type: 'justsaying',
content: {
subject: 'system_vars',
body: {
op_list: [
{
vote_count_mci: 123,
is_emergency: 0,
value: [
"2FF7PSL7FYXVU5UIQHCVDTTPUOOG75GX",
....
]
},
.....
],
threshold_size: [
{
vote_count_mci: 456,
is_emergency: 0,
value: 10000
},
.....
],
.....
}
}
}{
type: "justsaying",
content: {
subject: "system_var_vote",
body: {
subject: "op_list",
value: [
"2FF7PSL7FYXVU5UIQHCVDTTPUOOG75GX",
....
],
author_addresses: ["EJC4A7WQGHEZEKW6RLO7F26SAR4LAQBU"],
unit: "LpxhHEfxbOyj0sPlp6UC6XrRABvRJiH4qKEsOcMd1Bc=",
is_stable: 1
}
}
}{
type: 'justsaying',
content: {
tag: tag,
subject: 'custom',
body: body
}
}git clone https://github.com/byteball/bot-example
cd bot-example
npm install
cp .env.testnet .envconst eventBus = require('ocore/event_bus.js');eventBus.on('headless_wallet_ready', () => {
});eventBus.on('started_db_upgrade', () => {
});eventBus.on('finished_db_upgrade', () => {
});eventBus.on('connected', () => {
});eventBus.on('open-' + url, (error) => {
});eventBus.on('connected_to_source', (new_ws) => {
});eventBus.on('nonfatal_error', (msg, Error) => {
});eventBus.on("validated-" + unit, (bValid) => {
});eventBus.on('new_joint', (objJoint) => {
});eventBus.on('mci_became_stable', (mci) => {
});eventBus.on('new_my_transactions', (arrNewUnits) => {
});eventBus.on('my_transactions_became_stable', (arrUnits) => {
});eventBus.on('sequence_became_bad', (arrUniqueUnits) => {
});eventBus.on('first_history_received', () => {
});eventBus.on('maybe_new_transactions', () => {
});eventBus.on("new_my_unit-" + unit, (objJoint) => {
});eventBus.on("received_payment", (payer_device_address, assocAmountsByAsset, asset, message_counter, bToSharedAddress) => {
});eventBus.on('my_stable-' + unit , () => {
});eventBus.on('sync_idle' , () => {
});eventBus.on('catching_up_done' , () => {
});eventBus.on('catching_up_started' , () => {
});eventBus.on('catchup_next_hash_tree' , () => {
});eventBus.on('new_direct_private_chains' , (arrChains) => {
});eventBus.on('unhandled_private_payments_left' , (rowsLength) => {
});eventBus.on('peer_version', (ws, body) => {
});eventBus.on('new_version', (ws, body) => {
});eventBus.on('receivedPushProjectNumber', (ws, body) => {
});eventBus.on('client_logged_in', (ws) => {
});eventBus.on("message_from_hub", (ws, subject, body) => {
});eventBus.on("message_for_light", (ws, subject, body) => {
});const network = require('ocore/network.js');
eventBus.on("rates_updated", () => {
console.log(JSON.stringify(network.exchangeRates, null, 2));
});eventBus.on('peer_sent_new_message', (ws, objDeviceMessage) => {
});eventBus.on("enableNotification", (device_address, params) => {
});eventBus.on("disableNotification", (device_address, params) => {
});eventBus.on("create_new_wallet", (walletId, wallet_definition_template, arrDeviceAddresses, wallet_name, other_cosigners, is_single_address) => {
});eventBus.on('wallet_completed', (walletId) => {
});eventBus.on('wallet_declined', (wallet, rejector_device_address) => {
});eventBus.on('wallet_approved', (wallet, device_address) => {
});eventBus.on('new_wallet_address', (address) => {
});eventBus.on("new_address-" + address, () => {
});eventBus.on('saved_unit-' + unit, (objJoint) => {
});eventBus.on("pairing_attempt", (from_address, pairing_secret) => {
if (pairing_secret == "SOME_SECRET") {
eventBus.emit("paired", from_address, pairing_secret);
}
});eventBus.on("paired", (from_address, pairing_secret) => {
let device = require('ocore/device.js');
// say hi to anyone who pair with bot
device.sendMessageToDevice(from_address, 'text', 'Hi!');
});eventBus.on('paired_by_secret-' + body, (from_address) => {
});eventBus.on("removed_paired_device", (from_address) => {
});eventBus.on('refresh_light_started', () => {
});eventBus.on('refresh_light_done', () => {
});eventBus.on("text", (from_address, body, message_counter) => {
let device = require('ocore/device.js');
// echo back the same message
device.sendMessageToDevice(from_address, 'text', 'ECHO: '+ body);
});eventBus.on("object", (from_address, body, message_counter) => {
let device = require('ocore/device.js');
// echo back the same object back
device.sendMessageToDevice(from_address, 'object', body);
});eventBus.on("chat_recording_pref", (from_address, body, message_counter) => {
});eventBus.on("create_new_shared_address", (address_definition_template, assocMemberDeviceAddressesBySigningPaths) => {
});eventBus.on("signing_request", (objAddress, address, objUnit, assocPrivatePayloads, from_address, signing_path) => {
});eventBus.on("signature-" + device_address + "-" + address + "-" + signing_path + "-" + buf_to_sign.toString("base64"), (sig) => {
});eventBus.on('refused_to_sign', (device_address) => {
});eventBus.on('confirm_on_other_devices', () => {
});eventBus.on('all_private_payments_handled', (from_address) => {
});eventBus.on('all_private_payments_handled-' + first_chain_unit, () => {
});const network = require('ocore/network.js');
eventBus.on('custom_request', function(ws, params, tag) {
var response = 'put response here';
return network.sendResponse(ws, tag, response);
}eventBus.on('custom_justsaying', function(ws, content) {
};eventEmitter.once('ready', () => {
console.log("db is now ready");
});ws.on('error', (error) => {
});const request = require('request');
const correspondents = require('./correspondents');
let steps = {}; // store user steps
let assocDeviceAddressToCurrentCityAndTemp = {};
let assocDeviceAddressToAddress = {};
let bets = [];
let KEY_API = '50ace22603c84daba1780432180111'; // apixu.com
let my_address;headlessWallet.readSingleWallet(walletId => {
db.query("SELECT address FROM my_addresses WHERE wallet=?", [walletId], function (rows) {
if (rows.length === 0)
throw Error("no addresses");
my_address = rows[0].address;
});
});eventBus.on('text', (from_address, text) => {
text = text.trim().toLowerCase();
if (!steps[from_address]) steps[from_address] = 'start';
let step = steps[from_address];
const device = require('ocore/device.js');
if (validationUtils.isValidAddress(text.toUpperCase())) { // If address sent to us - save it.
assocDeviceAddressToAddress[from_address] = text.toUpperCase();
return device.sendMessageToDevice(from_address, 'text', 'I saved your address. Send me the name of the city');
} else if (!assocDeviceAddressToAddress[from_address]) { // If we do not know the address - asking it to send for us.
return device.sendMessageToDevice(from_address, 'text', 'Please send me your byteball address(... > Insert my address)');
} else if (step === 'city' && (text === 'more' || text === 'less')) { // Create a contract and ask to pay for it
let time = Date.now() + 20 * 60 * 1000;
let name = assocDeviceAddressToCurrentCityAndTemp[from_address].city + '_' + time;
let operator = text === 'more' ? '>=' : '<=';
let value = assocDeviceAddressToCurrentCityAndTemp[from_address].temp;
createContract(my_address, assocDeviceAddressToAddress[from_address], 2001, 2000, from_address, name, operator, value, time,
(err, paymentRequestText, shared_address, timeout) => {
if (err) throw err;
findOracleAndSendMessage(assocDeviceAddressToCurrentCityAndTemp[from_address].city + ':' + time, () => {
bets.push({
myAmount: 2001,
peerAmount: 2000,
myAddress: my_address,
peerAddress: assocDeviceAddressToAddress[from_address],
sharedAddress: shared_address,
peerDeviceAddress: from_address,
name,
operator,
value,
timeout
});
device.sendMessageToDevice(from_address, 'text', paymentRequestText);
setTimeout(() => { // If the payment was not made for 10 minutes-we take the money
headlessWallet.sendAllBytesFromAddress(shared_address, my_address, from_address, (err, unit) => {
if (!err) console.error('unit:: ', unit);
});
}, 15 * 60 * 1000);
});
});
} else if (step === 'start') { // Check the weather and specify what rate the user wants to make
switch (text) {
case 'berlin':
case 'moscow':
case 'helsinki':
case 'washington':
request('https://api.apixu.com/v1/current.json?key=' + KEY_API + '&q=' + text, function (error, response, body) {
if (error) {
console.error(error);
device.sendMessageToDevice(from_address, 'text', 'An error occurred. Try again later.');
} else {
let result = JSON.parse(body);
let temp = result.current.temp_c;
device.sendMessageToDevice(from_address, 'text', 'Want to bet that in an hour the weather in ' + text + ' will be\n[more ' + temp + 'c](command:more) or [less ' + temp + 'c](command:less)?');
steps[from_address] = 'city';
assocDeviceAddressToCurrentCityAndTemp[from_address] = {city: text, temp};
}
});
break;
default:
device.sendMessageToDevice(from_address, 'text', "City not support");
break;
}
} else {
device.sendMessageToDevice(from_address, 'text', "unknown command");
}
});
});function createContract(myAddress, peerAddress, myAmount, peerAmount, peerDeviceAddress, name, operator, value, time, cb) {
const device = require('ocore/device');
let timeout = Date.now() + 10 * 60 * 1000;
let inverseOperator = operator === '>=' ? '<' : '>';
let arrSeenConditionPeer = ['seen', {
what: 'output',
address: 'this address',
asset: 'base',
amount: peerAmount
}];
let arrSeenConditionMyInput = ['seen', {
what: 'input',
address: 'this address',
asset: 'base'
}];
let arrDefinition = ['or', [
['or', [
['and', [
['address', peerAddress],
arrSeenConditionPeer,
arrSeenConditionMyInput
]],
['or', [
['and', [
['address', peerAddress],
['in data feed', [[conf.oracle_address], name, operator, value]],
]],
['and', [
['address', myAddress],
['in data feed', [[conf.oracle_address], name, inverseOperator, value]]
]]
]],
]],
['and', [
['address', myAddress],
['not', arrSeenConditionPeer],
['in data feed', [[conf.TIMESTAMPER_ADDRESS], 'timestamp', '>', timeout]]
]]
]];
let assocSignersByPath = {
'r.0.0.0': {
address: peerAddress,
member_signing_path: 'r',
device_address: peerDeviceAddress
}, 'r.0.1.0.0': {
address: peerAddress,
member_signing_path: 'r',
device_address: peerDeviceAddress
},
'r.1.0': {
address: myAddress,
member_signing_path: 'r',
device_address: device.getMyDeviceAddress()
},
'r.0.1.1.0': {
address: myAddress,
member_signing_path: 'r',
device_address: device.getMyDeviceAddress()
}
};
let walletDefinedByAddresses = require('ocore/wallet_defined_by_addresses.js');
walletDefinedByAddresses.createNewSharedAddress(arrDefinition, assocSignersByPath, {
ifError: (err) => {
cb(err);
},
ifOk: (shared_address) => {
headlessWallet.issueChangeAddressAndSendPayment('base', myAmount, shared_address, peerDeviceAddress, (err, unit) => {
if (err) return cb(err);
let arrPayments = [{
address: shared_address,
amount: peerAmount,
asset: 'base'
}];
let assocDefinitions = {};
assocDefinitions[shared_address] = {
definition: arrDefinition,
signers: assocSignersByPath
};
let objPaymentRequest = {payments: arrPayments, definitions: assocDefinitions};
let paymentJson = JSON.stringify(objPaymentRequest);
let paymentJsonBase64 = Buffer.from(paymentJson).toString('base64');
let paymentRequestCode = 'payment:' + paymentJsonBase64;
let paymentRequestText = '[...](' + paymentRequestCode + ')';
cb(null, paymentRequestText, shared_address, timeout);
});
}
});
}let arrPayments = [{
address: shared_address,
amount: peerAmount,
asset: 'base'
}];
let assocDefinitions = {};
assocDefinitions[shared_address] = {
definition: arrDefinition,
signers: assocSignersByPath
};
let objPaymentRequest = {payments: arrPayments, definitions: assocDefinitions};
let paymentJson = JSON.stringify(objPaymentRequest);
let paymentJsonBase64 = Buffer(paymentJson).toString('base64');
let paymentRequestCode = 'payment:' + paymentJsonBase64;
let paymentRequestText = '[your share of payment to the contract](' + paymentRequestCode + ')';const db = require('ocore/db');
exports.addCorrespondent = (code, name, cb) => {
let device = require('ocore/device');
function handleCode(code) {
let matches = code.match(/^([\w\/+]+)@([\w.:\/-]+)#([\w\/+-]+)$/);
if (!matches)
return cb("Invalid pairing code");
let pubkey = matches[1];
let hub = matches[2];
let pairing_secret = matches[3];
if (pubkey.length !== 44)
return cb("Invalid pubkey length");
acceptInvitation(hub, pubkey, pairing_secret, cb);
}
function acceptInvitation(hub_host, device_pubkey, pairing_secret, cb) {
if (device_pubkey === device.getMyDevicePubKey())
return cb("cannot pair with myself");
if (!device.isValidPubKey(device_pubkey))
return cb("invalid peer public key");
// the correspondent will be initially called 'New', we'll rename it as soon as we receive the reverse pairing secret back
device.addUnconfirmedCorrespondent(device_pubkey, hub_host, name, (device_address) => {
device.startWaitingForPairing((reversePairingInfo) => {
device.sendPairingMessage(hub_host, device_pubkey, pairing_secret, reversePairingInfo.pairing_secret, {
ifOk: () =>{
cb(null, device_address);
},
ifError: cb
});
});
});
}
handleCode(code);
};
exports.findCorrespondentByPairingCode = (code, cb) => {
let matches = code.match(/^([\w\/+]+)@([\w.:\/-]+)#([\w\/+-]+)$/);
if (!matches)
return cb("Invalid pairing code");
let pubkey = matches[1];
let hub = matches[2];
db.query("SELECT * FROM correspondent_devices WHERE pubkey = ? AND hub = ?", [pubkey, hub], (rows) => {
return cb(rows.length ? rows[0] : null);
});
};function findOracleAndSendMessage(value, cb) {
const device = require('ocore/device');
correspondents.findCorrespondentByPairingCode(conf.oracle_pairing_code, (correspondent) => {
if (!correspondent) {
correspondents.addCorrespondent(conf.oracle_pairing_code, 'flight oracle', (err, device_address) => {
if (err)
throw new Error(err);
device.sendMessageToDevice(device_address, 'text', value);
cb();
});
} else {
device.sendMessageToDevice(correspondent.device_address, 'text', value);
cb();
}
});
}["sig", {"pubkey": "Ald9tkgiUZQQ1djpZgv2ez7xf1ZvYAsTLhudhvn0931w"}]["hash", {"hash": "value of sha256 hash in base64"}]["and", [
["sig", {pubkey: "one pubkey in base64"}],
["sig", {pubkey: "another pubkey in base64"}]
]]["or", [
["sig", {pubkey: "laptop pubkey"}],
["sig", {pubkey: "smartphone pubkey"}],
["sig", {pubkey: "tablet pubkey"}]
]]["and", [
["or", [
["sig", {pubkey: "laptop pubkey"}],
["sig", {pubkey: "tablet pubkey"}]
]],
["sig", {pubkey: "smartphone pubkey"}]
]]["r of set", {
required: 2,
set: [
["sig", {pubkey: "laptop pubkey"}],
["sig", {pubkey: "smartphone pubkey"}],
["sig", {pubkey: "tablet pubkey"}]
]
}]["weighted and", {
required: 50,
set: [
{weight: 40, value: ["sig", {pubkey: "CEO pubkey"}] },
{weight: 20, value: ["sig", {pubkey: "COO pubkey"}] },
{weight: 20, value: ["sig", {pubkey: "CFO pubkey"}] },
{weight: 20, value: ["sig", {pubkey: "CTO pubkey"}] }
]
}]["not", ["in data feed", [["NOAA ADDRESS"], "wind_speed", ">", "200"]]]["and", [
["address", "ADDRESS 1 IN BASE32"],
["address", "ADDRESS 2 IN BASE32"]
]]["definition template", [
"hash of unit where the template was defined",
{param1: "value1", param2: "value2"}
]]["in data feed", [
["ADDRESS1", "ADDRESS2", …],
"data feed name",
"=",
"expected value"
]]["or", [
["and", [
["address", "ADDRESS 1"],
["in data feed", [["EXCHANGE ADDRESS"], "EURUSD", ">", "1.2500"]]
]],
["and", [
["address", "ADDRESS 2"],
["timestamp", [">", Math.round(Date.now()/1000 + timeout_seconds)]]
]]
]]["or", [
["and", [
["address", "MERCHANT ADDRESS"],
["in data feed", [["FEDEX ADDRESS"], "tracking", "=", "123456"]]
]],
["and", [
["address", "BUYER ADDRESS"],
["timestamp", [">", Math.round(Date.now()/1000 + timeout_seconds)]]
]]
]]["in merkle", [
["ADDRESS1", "ADDRESS2", ...],
"data feed name",
"expected value"
]]["seen address", "ANOTHER ADDRESS IN BASE32"]["seen", {
what: "output",
address: "ADDRESS",
asset: "asset or base",
amount: 12345
}]["seen definition change", ["ADDRESS", "NEW DEFINITION CHASH"] ]["age", [">", 1234]]["attested", ["ADDRESS", ["ATTESTOR1", "ATTESTOR2", ...]]]["cosigned by", "ANOTHER ADDRESS IN BASE32"]["or", [
["address", "USER ADDRESS"],
["and", [
["address", "EXCHANGE ADDRESS"],
["has", {
what: "output",
asset: "ID of alternative asset",
amount_at_least: 1200,
address: "USER ADDRESS"
}]
]]
]]["has equal", {
equal_fields: ["address", "amount"],
search_criteria: [
{what: "output", asset: "asset1", address: "ADDRESS IN BASE32"},
{what: "input", asset: "asset2", type: "issue", address: "ANOTHER ADDRESS IN BASE32"}
]
}]["sum", {
filter: {
what: "input"|"output", asset: "asset or base", type: "transfer"|"issue",
address: "ADDRESS IN BASE32"
},
at_least: 120,
at_most: 130,
equals: 123
}]["has definition change", ["ADDRESS", "NEW DEFINITION CHASH"] ]["mci", ">", 123456]['timestamp', ['>', Math.round(Date.now() / 1000 + 2 * 3600)]]These settings are applicable to most Obyte nodes projects that have 'headless-obyte' or 'ocore' library as dependency.
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.
If you need to verify that a user owns a particular address, you can do it by offering to sign a message.
[...](sign-message-request:challenge)[...](signed-message:base64_encoded_text)This API is for merchants and payment processors who participate in the Obyte cashback program.
"dependencies": {
"headless-obyte": "git+https://github.com/byteball/headless-obyte.git",
"ocore": "git+https://github.com/byteball/ocore.git"
}// verify posted addresss
const validationUtils = require('ocore/validation_utils');
const device = require('ocore/device.js');
var sessionData = [];
eventBus.on('text', (from_address, text) => {
let arrSignedMessageMatches = text.match(/\(signed-message:(.+?)\)/);
if (validationUtils.isValidAddress(text)) {
sessionData[from_address] = text;
let challenge = 'My address is '+text;
return device.sendMessageToDevice(from_address, 'text',
'[...](sign-message-request:'+ challenge +')');
}
else if (arrSignedMessageMatches){
let signedMessageBase64 = arrSignedMessageMatches[1];
let validation = require('ocore/validation.js');
let signedMessageJson = Buffer.from(signedMessageBase64, 'base64').toString('utf8');
try{
var objSignedMessage = JSON.parse(signedMessageJson);
}
catch(e){
return null;
}
validation.validateSignedMessage(objSignedMessage, err => {
let user_address = sessionData[from_address];
let challenge = 'My address is '+user_address;
if (err)
return device.sendMessageToDevice(from_address, 'text', err);
if (objSignedMessage.signed_message !== challenge)
return device.sendMessageToDevice(from_address, 'text',
"You signed a wrong message: "+objSignedMessage.signed_message+", expected: "+challenge);
if (objSignedMessage.authors[0].address !== user_address)
return device.sendMessageToDevice(from_address, 'text',
"You signed the message with a wrong address: "+objSignedMessage.authors[0].address+", expected: "+user_address);
// all is good, address proven, continue processing
});
}
});address: (string) the signing addresstestnet=1{
"storage": "mysql",
"database": {
"host" : "localhost",
"user" : "DATABASE_USER",
"password" : "DATABASE_PASSWORD",
"name" : "DATABASE_NAME"
}
}{
"smtpTransport": "relay",
"smtpRelay": "smtp.mailtrap.io",
"smtpUser": "MAILTRAP_INBOX_USER",
"smtpPassword": "MAILTRAP_INBOX_PASSWORD",
"smtpSsl": false,
"smtpPort": 2525,
"admin_email": "[email protected]",
"from_email": "[email protected]"
}server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate "/etc/letsencrypt/live/obyte.one/fullchain.pem";
ssl_certificate_key "/etc/letsencrypt/live/obyte.one/privkey.pem";
if ($host != "obyte.one") {
rewrite ^(.*)$ https://obyte.one$1 permanent;
}
if ($https != "on") {
rewrite ^(.*)$ https://obyte.one$1 permanent;
}
location = /bb {
proxy_pass http://localhost:6611;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
root /var/www/html;
server_name _;
}FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
1: node::Abort() [node]
...
...
12: 0x3c0f805c7567
Out of memoryconst check_daemon = require('ocore/check_daemon.js');
check_daemon.checkDaemonAndNotify('node start.js');const check_daemon = require('ocore/check_daemon.js');
check_daemon.checkDaemonAndRestart('node start.js', 'node start.js 1>log 2>>err');exports.bLight = true;
exports.hub = process.env.testnet ? 'obyte.org/bb-test' : 'obyte.org/bb';testnet=1var headlessWallet = require('headless-obyte');
var eventBus = require('ocore/event_bus.js');
function onReady() {
let amount = 1; //in bytes
let user_address = "62SU7724ME5MWB7VEP64VURYZHDRSDWN"; // account address
let user_device_address = null; // device address
headlessWallet.issueChangeAddressAndSendPayment(
null /*asset, null for bytes*/,
amount,
user_address,
user_device_address,
(err, unit) => {
if (err){
return console.error(err);
}
// handle successful payment
});
// received new payment
eventBus.on('new_my_transactions', (arrUnits) => {
// handle new unconfirmed units here
// at this stage, payment is not yet confirmed
});
// payment is confirmed
eventBus.on('my_transactions_became_stable', (arrUnits) => {
// handle payments becoming confirmed
});
};
eventBus.once('headless_wallet_ready', onReady);====== my first address: 2PJHQKMP7NH6MUYUQPZ4S46C2Q7CQI32eventBus.on('mci_became_stable', function(mci){
// check if there are any units you are interested in
// that had this MCI and react to their becoming stable
});====== my pairing code: [email protected]/bb#0000<a href="byteball:[email protected]/bb#0000">Chat with my test bot</a>eventBus.on('paired', function(from_address, pairing_secret){
var device = require('ocore/device.js');
device.sendMessageToDevice(from_address, 'text', 'Hi! I am bot.');
});eventBus.on('text', function(from_address, user_message){
// your code here
});var device = require('ocore/device.js');
var message_to_user = 'Message from bot to user';
device.sendMessageToDevice(from_address, 'text', message_to_user);var eventBus = require('ocore/event_bus.js');
var device = require('ocore/device.js');
eventBus.on('text', function(from_address, user_message){
device.sendMessageToDevice(from_address, 'text', user_message);
});click this link: [Command name](command:command code)Example command: we suggest to [buy (number) apples](suggest-command:buy 5 apples){
"result": "ok",
"cashback_amount": 101318,
"unit": "kYbc5KGKB/KnjynYePEF4cpumKmOz22xVKSVWbmtZ0M="
}{
"result": "error",
"error": "authentication failed"
}git clone https://github.com/byteball/bot-example
cd bot-example
npm install
cp .env.testnet .env
node start.jslet assocDeviceAddressToPeerAddress = {};
let assocDeviceAddressToMyAddress = {};
let assocMyAddressToDeviceAddress = {};eventBus.on('paired', (from_address, pairing_secret) => {
const device = require('ocore/device.js');
device.sendMessageToDevice(from_address, 'text', "Please send me your address");
});
eventBus.on('text', (from_address, text) => {
text = text.trim();
const device = require('ocore/device.js');
device.sendMessageToDevice(from_address, 'text', "Please send me your address");
});eventBus.on('text', (from_address, text) => {
const device = require('ocore/device.js');
text = text.trim();
if (validationUtils.isValidAddress(text)) {
assocDeviceAddressToPeerAddress[from_address] = text;
device.sendMessageToDevice(from_address, 'text', 'Saved your wallet address');
headlessWallet.issueNextMainAddress((address) => {
assocMyAddressToDeviceAddress[address] = from_address;
assocDeviceAddressToMyAddress[from_address] = address;
device.sendMessageToDevice(from_address, 'text', '[balance](byteball:' + address + '?amount=5000)');
})
} else if (assocDeviceAddressToMyAddress[from_address]) {
device.sendMessageToDevice(from_address, 'text', '[balance](byteball:' + assocDeviceAddressToMyAddress[from_address] + '?amount=5000)');
} else {
device.sendMessageToDevice(from_address, 'text', "Please send me your address");
}
});eventBus.on('new_my_transactions', (arrUnits) => {
const device = require('ocore/device.js');
db.query("SELECT address, amount, asset FROM outputs WHERE unit IN (?)", [arrUnits], rows => {
rows.forEach(row => {
let deviceAddress = assocMyAddressToDeviceAddress[row.address];
if (row.asset === null && deviceAddress) {
device.sendMessageToDevice(deviceAddress, 'text', 'I received your payment: ' + row.amount + ' bytes');
return true;
}
})
});
});eventBus.on('my_transactions_became_stable', (arrUnits) => {
const device = require('ocore/device.js');
db.query("SELECT address, amount, asset FROM outputs WHERE unit IN (?)", [arrUnits], rows => {
rows.forEach(row => {
let deviceAddress = assocMyAddressToDeviceAddress[row.address];
if (row.asset === null && deviceAddress) {
headlessWallet.sendAllBytesFromAddress(row.address, assocDeviceAddressToPeerAddress[deviceAddress], deviceAddress, (err, unit) => {
if(err) device.sendMessageToDevice(deviceAddress, 'text', 'Oops, there\'s been a mistake. : ' + err);
device.sendMessageToDevice(deviceAddress, 'text', 'I sent back your payment! Unit: ' + unit);
return true;
})
}
})
});
});var arrDefinition = ['or', [
['and', [
['address', user_address],
// conditions when user can unlock the contract
]],
['and', [
['address', bot_address],
// conditions when I can unlock the contract
]]
]];var device = require('ocore/device.js');
var assocSignersByPath = {
'r.0.0': {
address: user_address,
member_signing_path: 'r', // unused, should be always 'r'
device_address: from_address
},
'r.1.0': {
address: bot_address,
member_signing_path: 'r', // unused, should be always 'r'
device_address: device.getMyDeviceAddress()
}
};var walletDefinedByAddresses = require('ocore/wallet_defined_by_addresses.js');
walletDefinedByAddresses.createNewSharedAddress(arrDefinition, assocSignersByPath, {
ifError: function(err){
// handle error
},
ifOk: function(shared_address){
// new shared address created
}
});var payments = [
{address: shared_address, amount: peer_amount, asset: peerAsset}
];
var definitions = {};
definitions[shared_address] = {
definition: arrDefinition,
signers: assocSignersByPath
};
var objPaymentRequest = {payments, definitions};
var paymentJson = JSON.stringify(objPaymentRequest);
var paymentJsonBase64 = Buffer.from(paymentJson).toString('base64');
var paymentRequestCode = 'payment:'+paymentJsonBase64;
var paymentRequestText = '[...]('+paymentRequestCode+')';
device.sendMessageToDevice(from_address, 'text', paymentRequestText);const eventBus = require('ocore/event_bus.js');
eventBus.on('text', function(from_address, text) {
const device = require('ocore/device.js');
if (text === 'resend') {
const walletDefinedByAddresses = require('ocore/wallet_defined_by_addresses');
walletDefinedByAddresses.sendToPeerAllSharedAddressesHavingUnspentOutputs(from_address, 'base', {
ifFundedSharedAddress: function(numberOfContracts) {
device.sendMessageToDevice(from_address, "text",
`Found and resent ${numberOfContracts} smart contracts that have Bytes on them to your wallet.`
);
},
ifNoFundedSharedAddress: function() {
device.sendMessageToDevice(from_address, "text",
`No smart contracts with Bytes on it were found.`
);
}
});
}
});This guide explains how to request, validate, and use the attested data in your chat bot.
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.
[...](profile-request:first_name,last_name,dob,country,id_type)[...](profile-request:email)[...](profile-request:steem_username,reputation)[...](profile:privateProfileJsonBase64)let arrProfileMatches = text.match(/\(profile:(.+?)\)/);const privateProfile = require('ocore/private_profile.js');
let privateProfileJsonBase64 = arrProfileMatches[1];
let objPrivateProfile = privateProfile.getPrivateProfileFromJsonBase64(privateProfileJsonBase64);objPrivateProfile {
unit: "...", // attestation unit
payload_hash: "...", // pointer to attestation payload in this unit
src_profile: object // attested fields
}privateProfile.parseAndValidatePrivateProfile(objPrivateProfile, (err, address, attestor) => {
if (err) {
device.sendMessageToDevice(from_address, 'text', err);
}
// validated
// ...
});let assocPrivateData = privateProfile.parseSrcProfile(objPrivateProfile.src_profile);let required_fields = ['first_name', 'last_name', 'dob', 'country', 'id_type'];
if (required_fields.every(key => key in assocPrivateData)) {
privateProfile.savePrivateProfile(objPrivateProfile, address, attestor);
}
else {
device.sendMessageToDevice(from_address, 'text',
'Please share all the required fields.');
}db.query("SELECT unit, payload_hash, src_profile \
FROM private_profiles \
WHERE address = ? AND attestor_address = ? \
LIMIT 1;", [wallet_address_1, attestor], (rows) => {
if (rows.length === 0) {
return device.sendMessageToDevice(device_address_1, 'text',
'Please provide your private email \
[...](profile-request:first_name,last_name)');
}
let objPrivateProfile = {
unit: rows[0].unit,
payload_hash: rows[0].payload_hash,
src_profile: JSON.parse(rows[0].src_profile)
};
let privateProfileJsonBase64 = Buffer.from(JSON.stringify(objPrivateProfile)).toString('base64');
device.sendMessageToDevice(device_address_2, 'text',
'Save the private profile of the other side of the contract. \
[...](profile:'+ privateProfileJsonBase64 +')');
});const device = require('ocore/device.js');
let challenge = 'This is my address.';
device.sendMessageToDevice(from_address, 'text', 'Please provide your private email [...](profile-request:email) \
or insert the wallet address, which has publicly attested email address [...](sign-message-request:'+ challenge +'). \
\nIf you haven\'t verified your email address yet then please do it with Email Attestation bot from Bot Store.');const email_attestor = 'H5EZTQE7ABFH27AUDTQFMZIALANK6RBG';
/*
convert base64 signed message into JSON object
like it is described in Wallet address verification documentation.
*/
validation.validateSignedMessage(objSignedMessage, err => {
if (err)
return device.sendMessageToDevice(from_address, 'text', err);
if (objSignedMessage.signed_message !== challenge)
return device.sendMessageToDevice(from_address, 'text',
"You signed a wrong message: "+objSignedMessage.signed_message+", expected: "+challenge);
db.query("SELECT unit, field, value FROM attested_fields \
WHERE attestor_address = ? AND address = ?;",
[email_attestor, objSignedMessage.authors[0].address], (rows) => {
if (rows.length === 0) return;
rows.forEach((row) => {
if (row.field == 'email') {
device.sendMessageToDevice(from_address, 'text',
'Found: '+ row.value);
}
});
});
});const email_attestor = 'H5EZTQE7ABFH27AUDTQFMZIALANK6RBG';
/*
convert base64 signed message into JSON object
like it is described in Wallet address verification documentation.
*/
validation.validateSignedMessage(objSignedMessage, err => {
if (err)
return device.sendMessageToDevice(from_address, 'text', err);
if (objSignedMessage.signed_message !== challenge)
return device.sendMessageToDevice(from_address, 'text',
"You signed a wrong message: "+objSignedMessage.signed_message+", expected: "+challenge);
const network = require('ocore/network.js');
network.requestFromLightVendor('light/get_attestations', {
address: objSignedMessage.authors[0].address
},
(ws, request, response) => {
if (response.error){
console.error('light/get_attestations failed: '+response.error);
return;
}
let arrAttestations = response;
if (!Array.isArray(arrAttestations)){
console.error('light/get_attestations response is not an array: '+response);
return;
}
if (arrAttestations.length === 0){
return device.sendMessageToDevice(from_address, 'text',
"Not an attested address.");
}
console.log('attestations', arrAttestations);
let arrUnits = arrAttestations.map(attestation => attestation.unit);
network.requestProofsOfJointsIfNewOrUnstable(arrUnits, err => {
if (err){
console.log('requestProofsOfJointsIfNewOrUnstable failed: '+err);
return;
}
arrAttestations.forEach( (attestation) => {
if (attestation.attestor_address === email_attestor) {
device.sendMessageToDevice(from_address, 'text',
'Found: '+ attestation.profile.email);
}
});
});
});
});




This URI protocol enables developers to open the wallet app in desired screen with pre-filled inputs.
If you run an exchange, you will likely want to interact with your Obyte node via RPC interface.

sendtoaddress<a href="obyte:ACCOUNT_ADDRESS?amount=123000&asset=base">
open Send screen with bytes
</a><a href="obyte:ACCOUNT_ADDRESS?amount=123000&from_address=USER_ACCOUNT_ADDRESS">
open Send screen with bytes
</a><a href="obyte:ACCOUNT_ADDRESS?amount=123000&single_address=1">
open Send screen with bytes
</a>var base64 = global.btoa || require('btoa');
var data = {
number: 1,
foo: "bar",
foobar: {
foo: "bar",
},
"?<?>?": ["a",1,2,3],
};
var json_string = JSON.stringify(data);
var base64_string = base64(json_string);
var uri_encoded_base64data = encodeURIComponent(base64_string);<a href="obyte:AA_ADDRESS?amount=10000&single_address=1&base64data=eyJudW1iZXIiOjEsImZvbyI6ImJhciIsImZvb2JhciI6eyJmb28iOiJiYXIifSwiPzw%2FPj8iOlsiYSIsMSwyLDNdfQ%3D%3D">
open Send screen with bytes and data
</a><a href="obyte:data?app=data&anykey1=anyvalue&anykey2=anyvalue">
open Send screen with data
</a><a href="obyte:data?app=temp_data&anykey1=anyvalue&anykey2=anyvalue">
open Send screen with temp data
</a><a href="obyte:data?app=data_feed&anykey1=anyvalue&anykey2=anyvalue">
open Send screen with data feed
</a><a href="obyte:data?app=profile&anykey1=anyvalue&anykey2=anyvalue">
open Send screen with profile
</a><a href="obyte:data?app=attestation&address=ACCOUNT_ADDRESS&anykey1=anyvalue&anykey2=anyvalue">
open Send screen with attestation
</a><a href="obyte:data?app=poll&question=How%20are%20you&option1=fine&option2=tres%20bien">
open Send screen with poll
</a><a href="obyte:data?app=definition&definition=%7B%0A%09messages%3A%20%5B%0A%09%09%7B%0A%09%09%09app%3A%20'payment'%2C%0A%09%09%09payload%3A%20%7B%0A%09%09%09%09asset%3A%20'base'%2C%0A%09%09%09%09outputs%3A%20%5B%0A%09%09%09%09%09%7B%0A%09%09%09%09%09%09address%3A%20%22%7Btrigger.address%7D%22%0A%09%09%09%09%09%7D%0A%09%09%09%09%5D%0A%09%09%09%7D%0A%09%09%7D%0A%09%5D%0A%7D">
open Send screen with AA definition
</a><a href="obyte:data?app=definition&definition=https%3A%2F%2Fexample.com%2Fdefinition.json">
open Send screen with AA definition
</a><a href="obyte:data?app=text&content=Lorem%20ipsum%20dolor%20sit%20amet%2C%20consectetur%20adipiscing%20elit.%0AQuisque%20venenatis%20elementum%20dolor%20in%20malesuada.%20Cras%20ultrices%20ultrices%20fermentum.%20Nullam%20ac%20commodo%20ante.%20Pellentesque%20quis%20nibh%20laoreet%2C%20sagittis%20augue%20in%2C%20dapibus%20nunc.%20Etiam%20vel%20eleifend%20urna%2C%20in%20tincidunt%20turpis%20nullam.">
open Send screen with text content
</a><a href="obyte:data?app=system_vote&subject=base_tps_fee&value=11.5">
open Send screen with the system vote
</a><a href="obyte:data?app=system_vote_count&subject=base_tps_fee">
open Send screen with the system vote count request
</a><a href="obyte:textcoin?RANDOMLY-GENERATED-TWELVE-WORDS">
Recieve funds
</a><a href="obyte:[email protected]/bb#0000">
Add Sports Betting bot
</a>exports.permanent_pairing_secret = '*';<a href="obyte:AhMVGrYMCoeOHUaR9v/[email protected]/bb#stats-HAXKXC1EBn8GtiAiW0xtYLAJiyV4V1jTt1rc2TDJ7p4=">
see stats
</a><div class="container">
<div id="qr_code"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.qrcode/1.0/jquery.qrcode.min.js"
integrity="sha256-9MzwK2kJKBmsJFdccXoIDDtsbWFh8bjYK/C7UjB1Ay0="
crossorigin="anonymous"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#qr_code').html('').qrcode({
width: 512,
height: 512,
text: 'obyte:ACCOUNT_ADDRESS?amount=123000&asset=base'
});
});
</script>const pairingProtocol = process.env.testnet ? 'obyte-tn:' : 'obyte:';cd tools
node rpc_service.js$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"getinfo", "params":{} }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": {
"connections": 5,
"last_mci": 253151,
"last_stable_mci": 253120,
"count_unhandled": 0
},
"id": 1
}$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"getnewaddress", "params":{} }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": "QZEM3UWTG5MPKYZYRMUZLNLX5AL437O3",
"id": 1
}$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"validateaddress", "params":["QZEM3UWTG5MPKYZYRMUZLNLX5AL437O3"] }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": true,
"id": 1
}$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"getbalance", "params":{} }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": {
"base": {
"stable": 8000,
"pending": 0
}
},
"id": 1
}$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"getbalance", "params":["QZEM3UWTG5MPKYZYRMUZLNLX5AL437O3"] }' http://127.0.0.1:6332 | json_pp$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"listtransactions", "params":{"since_mci": 1234} }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": [
{
"action": "received",
"amount": 3000,
"my_address": "YA3RYZ6FEUG3YEIDIJICGVPD6PPCTIZK",
"arrPayerAddresses": [
"EENED5HS2Y7IJ5HACSH4GHSCFRBLA6CN"
],
"confirmations": 0,
"unit": "sALugOU8fjVyUvtfKPP0pxlE74GlPqOJxMbwxA1B+eE=",
"fee": 588,
"time": "1490452729",
"level": 253518
},
{
"action": "received",
"amount": 5000,
"my_address": "QZEM3UWTG5MPKYZYRMUZLNLX5AL437O3",
"arrPayerAddresses": [
"UOOHQW4ZKPTII4ZEE4ENAM5PC6LWAQHQ"
],
"confirmations": 1,
"unit": "vlt1vzMtLCIpb8K+IrvqdpNLA9DkkNAGABJ420NvOBs=",
"fee": 541,
"time": "1490452322",
"level": 253483
}
],
"id": 1
}$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"listtransactions", "params":["QZEM3UWTG5MPKYZYRMUZLNLX5AL437O3"] }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": [
{
"action": "received",
"amount": 5000,
"my_address": "QZEM3UWTG5MPKYZYRMUZLNLX5AL437O3",
"arrPayerAddresses": [
"UOOHQW4ZKPTII4ZEE4ENAM5PC6LWAQHQ"
],
"confirmations": 0,
"unit": "vlt1vzMtLCIpb8K+IrvqdpNLA9DkkNAGABJ420NvOBs=",
"fee": 541,
"time": "1490452322",
"level": 253483
}
],
"id": 1
}$ curl -s --data '{"jsonrpc":"2.0", "id":1, "method":"sendtoaddress", "params":["BVVJ2K7ENPZZ3VYZFWQWK7ISPCATFIW3", 1000] }' http://127.0.0.1:6332 | json_pp
{
"jsonrpc": "2.0",
"result": "vuudtbL5ASwr0LJZ9tuV4S0j/lIsotJCKifphvGATmU=",
"id": 1
}var validationUtils = require("ocore/validation_utils.js");
if (!validationUtils.isValidAddress(address)){
// notify user that the entered wallet address is invalid
}Any text before [payment description, will be ignored](obyte:ACCOUNT_ADDRESS?amount=123000&asset=base) any text aftervar headlessWallet = require('headless-obyte');
eventBus.once('headless_wallet_ready', () => {
eventBus.on('text', function(from_address, text) {
const device = require('ocore/device.js');
if (text === 'resend') {
headlessWallet.issueNextMainAddress((deposit_address) => {
// send it over to the user
device.sendMessageToDevice(from_address, 'text',
'[...](obyte:'+ deposit_address +'?amount=123000&asset=base)');
});
});
}
});
});[...](obyte:ACCOUNT_ADDRESS?amount=1&single_address=1)
[...](obyte:ACCOUNT_ADDRESS?amount=1&single_address=1&from_address=UEPO3OD2TUUJUOECVMKVSRHRDJ4ST2FS)var payments = [
{address: address1, amount: amount1, asset: asset1},
{address: address1, amount: amount2, asset: asset2},
{address: address2, amount: amount3, asset: asset1}
];
var objPaymentRequest = {payments};
var paymentJson = JSON.stringify(objPaymentRequest);
var paymentJsonBase64 = Buffer.from(paymentJson).toString('base64');
var paymentRequestCode = 'payment:'+paymentJsonBase64;
var paymentRequestText = '[...]('+paymentRequestCode+')';
device.sendMessageToDevice(from_address, 'text', paymentRequestText);var headlessWallet = require('headless-obyte');eventBus.on('new_my_transactions', function(arrUnits){
// fetch more data about my addresses
db.query("SELECT outputs.address, amount, asset FROM outputs \
JOIN my_addresses USING (address) \
WHERE unit IN(?);", [arrUnits], (rows) => {
if (rows.length === 0) return;
rows.forEach((row) => {
// react to each unconfirmed payment
});
});
});eventBus.on('my_transactions_became_stable', function(arrUnits){
// fetch more data about my addresses
db.query("SELECT outputs.address, amount, asset FROM outputs \
JOIN my_addresses USING (address) \
WHERE unit IN(?);", [arrUnits], (rows) => {
if (rows.length === 0) return;
rows.forEach((row) => {
// react to each confirmed payment
});
});
});eventBus.on('mci_became_stable', function(mci){
// check if there are any units you are interested in
// that had this MCI and react to their becoming stable
});var headlessWallet = require('headless-obyte');eventBus.once('headless_wallet_ready', () => {
headlessWallet.issueChangeAddressAndSendPayment(asset, amount, account_address, from_address, (err, unit) => {
if (err){
// something went wrong, maybe put this payment on a retry queue
return;
}
// handle successful payment
});
});
Autonomous agents allow to quickly create decentralized finance applications on a distributed ledger.
["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{trigger.output[[asset=base]] - 1000}"
}
]
}
}
]
}]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;["autonomous agent", {
messages: [
{
app: "payment",
payload: {
// this is the token being sold
asset: "n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=",
outputs: [
{
address: "{trigger.address}",
amount: "{ round(trigger.output[[asset=base]] * 1.5) }"
}
]
}
}
]
}]{
app: "data",
payload: {
withdrawal_amount: 25000,
nested_array: [
99,
{another_field: 44}
],
nested_object: {yet_another_field: "value2"}
},
...
}["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{trigger.data.withdrawal_amount}"
}
]
}
}
]
}]{
withdrawal_amount: 3000
}["autonomous agent", {
bounce_fees: {
base: 20000,
"n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=": 100
},
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{trigger.data.withdrawal_amount}"
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
app: "data_feed",
payload: {
"{trigger.data.feed_name}": "{trigger.data.feed_value}"
}
}
]
}]["autonomous agent", {
messages: [
{
app: "data",
payload: {
forwarded: 1,
initial_data: "{trigger.data}"
}
},
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY",
amount: "{trigger.output[[asset=base]] - 1000}"
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
app: "text",
payload: "{'Hello, ' || trigger.data.name || '!'}"
}
]
}]["autonomous agent", {
messages: [
{
app: "asset",
payload: {
cap: "{trigger.data.cap otherwise ''}",
is_private: false,
is_transferrable: true,
auto_destroy: "{!!trigger.data.auto_destroy}",
fixed_denominations: false,
issued_by_definer_only: "{!!trigger.data.issued_by_definer_only}",
cosigned_by_definer: false,
spender_attested: false,
attestors: [
"{trigger.data.attestor1 otherwise ''}",
"{trigger.data.attestor2 otherwise ''}",
"{trigger.data.attestor3 otherwise ''}",
]
}
}
]
}]["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{ round(balance[base]/2) }"
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{ (balance['JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY'][base] < 1e6) ? 'JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY' : trigger.address }",
amount: "{ trigger.output[[asset=base]] - 1000 }"
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{(data_feed[[oracles='TKT4UESIKTTRALRRLWS4SENSTJX6ODCW', feed_name='BROOKLYNNETS_CHARLOTTEHORNETS_2019-07-21']] == 'CHARLOTTEHORNETS') ? '7XZSBB32I5XPIAVWUYCABKO67TZLHKZW' : 'FDZVVIOJZZVFAP6FLW6GMPDYYHI6W4JG'}"
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{ (attestation[[attestors='JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725', address=trigger.address]].reputation >= 50) ? 50000 : 0 }"
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
if: "{ var['previous_address'] }",
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{ var['previous_address'] }",
amount: "{ trigger.output[[asset=base]] - 1000 }"
}
]
}
},
{
app: "state",
state: `{
var['previous_address'] = trigger.address;
}`
}
]
}]var['JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY']['var_name']["autonomous agent", {
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{ trigger.address }",
amount: `{
$amount1 = trigger.output[[asset=base]] - 5000;
$amount2 = round(balance[base] * 0.9);
max($amount1, $amount2)
}`
}
]
}
}
]
}]["autonomous agent", {
messages: [
{
if: "{ trigger.data.pay }",
init: `{
$amount1 = trigger.output[[asset=base]] - 5000;
$amount2 = round(balance[base] * 0.9);
}`,
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{ trigger.address }",
amount: "{max($amount1, $amount2)}"
}
]
}
}
]
}]{
init: `{
$amount1 = trigger.output[[asset=base]] - 5000;
$amount2 = round(balance[base] * 0.9);
}`,
...
}{
address: "{ trigger.address }",
...
}{
amount: `{
$amount1 = trigger.output[[asset=base]] - 5000;
$amount2 = round(balance[base] * 0.9);
max($amount1, $amount2)
}`,
...
}["autonomous agent", {
messages: {
cases: [
{
if: "{trigger.data.define}",
messages: [
// first version of messages
....
]
},
{
if: "{trigger.data.issue}",
init: "{$amount = trigger.output[[asset=base]];}",
messages: [
// second version of messages
....
]
},
{
messages: [
// default version of messages
....
]
}
]
}
}]["autonomous agent", {
messages: [
// second version of messages
....
]
}]if (var['started']){
$amount = balance[base];
$current_winner = var['current_winner'];
}
else{
$amount = trigger.output[[asset=base]];
$current_winner = trigger.address;
}$amount = trigger.output[[asset=base]];
if ($amount > 1e6)
return $amount;
// here comes some other code that will not be executed if $amount > 1e6$amount = trigger.output[[asset=base]];
if ($amount > 1e6)
return;
// here comes some other code that will not be executed if $amount > 1e6$maturity = var['maturity_timestamp'];
if (timestamp < $maturity) {
bounce("too early");
}$maturity = var['maturity_timestamp'];
require(timestamp >= $maturity, "too early");This is a message type, which gets a reply with a response type message.
const network = require('ocore/network.js');
const ws = network.getInboundDeviceWebSocket(device_address);
// function parameters: websocket, command, params, bReroutable, callback
var tag = network.sendRequest(ws, 'get_witnesses', null, false, getParentsAndLastBallAndWitnessListUnit);
function getParentsAndLastBallAndWitnessListUnit(ws, req, witnesses) {
var params = {
witnesses: witnesses
};
network.sendRequest(ws, 'light/get_parents_and_last_ball_and_witness_list_unit', params, false,
function(ws, req, response) {
console.log(response);
}
);
}
//you can have your own timeout logic and delete a pending request like this
setTimeout(function() {
var deleted = network.deletePendingRequest(ws, tag); // true if request was deleted
}, 30*1000);{
type: 'request',
content: {
tag: tag,
command: 'get_peers'
}
}{
type: 'response',
content: {
tag: tag,
response: [
"wss://relay.bytes.cash/bb",
"wss://hub.obytechina.org/bb",
"wss://hub.obyte.connectory.io/bb",
"wss://odex.ooo/bb",
...
]
}
}{
type: 'request',
content: {
tag: tag,
command: 'get_witnesses'
}
}{
type: 'response',
content: {
tag: tag,
response: [
"4GDZSXHEFVFMHCUCSHZVXBVF5T2LJHMU",
"BVVJ2K7ENPZZ3VYZFWQWK7ISPCATFIW3",
"DJMMI5JYA5BWQYSXDPRZJVLW3UGL3GJS",
...
]
}
}{
type: 'request',
content: {
tag: tag,
command: 'get_joint',
params: '0xXOuaP5e3z38TF5ooNtDhmwNkh1i21rBWDvrrxKt0U='
}
}{
type: 'response',
content: {
tag: tag,
response: {
"joint": {
"unit": {
"unit": "0xXOuaP5e3z38TF5ooNtDhmwNkh1i21rBWDvrrxKt0U=",
"version": "1.0",
"alt": "1",
"witness_list_unit": "oj8yEksX9Ubq7lLc+p6F2uyHUuynugeVq4+ikT67X6E=",
"last_ball_unit": "AV4qvQ5JuI7J3EO5pFD6UmMu+j0crPbH17aJfigozrc=",
"last_ball": "Ogq4+uR6LaDspK89MmyydxRchgdN/BYwXvWXwNCMubQ=",
"headers_commission": 344,
"payload_commission": 607,
"main_chain_index": 2336026,
"timestamp": 1524844417,
"parent_units": [
"IFiHjYbzuAUC9TwcTrmpvezjTKBl6nIXjgjoKuVyCz0="
],
"authors": [
{
"address": "AM6GTUKENBYA54FYDAKX2VLENFZIMXWG",
"authentifiers": {
"r": "MBWlL31OkibUXhoxmwIcWB/fAx1uWdNTE8PYTBNeN3w+tev4N1anOXjGtXiGW4whW3PfJeTO9fA5WxwyqK/m2w=="
}
}
],
"messages": [
{
"app": "data",
"payload_hash": "MiuMtTSgP0brPQijsRc0igb+dxcrkLjXWRE253AE/S8=",
"payload_location": "inline",
"payload": {
"asset": "IYzTSjJg4I3hvUaRXrihRm9+mSEShenPK8l8uKUOD3o=",
"decimals": 0,
"name": "WCG Point by Byteball",
"shortName": "WCG Point",
"issuer": "Byteball",
"ticker": "WCG",
"description": "WCG Point is a honorific token, a recognition of contributing to World Community Grid projects. The token is not transferable, therefore, it cannot be sold and the balance reflects a lifetime contribution to WCG. Some services might choose to offer a privilege to users with large balance of this token."
}
},
{
"app": "payment",
"payload_hash": "FaVxPdeRKTgcO/LCDtu/YUZego8xyrnpUMF3V/sujr8=",
"payload_location": "inline",
"payload": {
"inputs": [
{
"unit": "1p85M8VSRDaAkNoCAdtMrv6cVGpPNNeVJpLzOkzWLk8=",
"message_index": 1,
"output_index": 0
}
],
"outputs": [
{
"address": "AM6GTUKENBYA54FYDAKX2VLENFZIMXWG",
"amount": 17890
}
]
}
}
]
},
"ball": "Ef36OjK5m6lQMII4S9MN3iGtQcsvTzzQFLKQZ44UBCg="
}
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'post_joint',
params: {Object}
}
}{
type: 'response',
content: {
tag: tag,
response: 'accepted'
}
}{
type: 'request',
content: {
tag: tag,
command: 'heartbeat'
}
}{
type: "response",
content: {
tag: tag,
response: null
}
}{
type: 'response',
content: {
tag: tag,
response: 'sleep'
}
}{
type: "request",
content: {
tag: tag,
command: "subscribe",
params: {
subscription_id: crypto.randomBytes(30).toString('base64'),
last_mci: 0,
library_version: "0.1"
}
}
}{
type: "response",
content: {
tag: tag,
response: "subscribed"
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light, cannot subscribe you to updates",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'catchup',
params: {
witnesses: witnesses,
last_stable_mci: last_stable_mci,
last_known_mci: last_known_mci
}
}
}{
type: 'response',
content: {
tag: tag,
response: {
status: 'current'
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'get_hash_tree',
params: {
from_ball: from_ball,
to_ball: to_bal
}
}
}{
type: 'response',
content: {
tag: tag,
response: {
"balls": [
{
"unit": "c2teVop7xa1BmH1LPvytvOKU8HHTrprGWQw+uHlWPHo=",
"ball": "ht1QP48paWg5hpjx+Nbd/DSRFT8WlbQKk9+Uum0/tso=",
"parent_balls": [
"aEU1WiY9FQ9ihv9cKkX/EHWDxYYVaWYs2AL1yxyYZAQ="
]
},
{
"unit": "tPbC6QLeweGuiGYRPsIhfd0TQWXWASYBJJUPGa9AOfw=",
"ball": "ba5wFB+gewdRX6nSpxt6+Nt8PaRen4pLIYg3jC6EvIw=",
"parent_balls": [
"ht1QP48paWg5hpjx+Nbd/DSRFT8WlbQKk9+Uum0/tso="
]
},
{
"unit": "J7quEBEZ5ksq+0vK4cku7NtWkZ4Kxb8aHCu66RkN2eU=",
"ball": "4B+R+gW91BPmTqxF3G10oWmF4tg5LiHdpMQcL1ezqCo=",
"parent_balls": [
"ba5wFB+gewdRX6nSpxt6+Nt8PaRen4pLIYg3jC6EvIw="
]
},
...
]
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'get_last_mci'
}
}{
type: 'response',
content: {
tag: tag,
response: 3795741
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/deliver',
params: {
to: device_address,
pubkey: pubkey,
signature: signature,
encrypted_package: encrypted_message
}
}
}{
type: 'response',
content: {
tag: tag,
response: 'accepted'
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/get_temp_pubkey',
params: permanent_pubkey
}
}{
type: 'response',
content: {
tag: tag,
response: 'TEMP_PUBKEY_PACKAGE'
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/temp_pubkey',
params: {
temp_pubkey: temp_pubkey,
pubkey: permanent_pubkey,
signature: signature
}
}
}{
type: 'response',
content: {
tag: tag,
response: 'updated'
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/enable_notification',
params: registrationId
}
}{
type: 'response',
content: {
tag: tag,
response: 'ok'
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/disable_notification',
params: registrationId
}
}{
type: 'response',
content: {
tag: tag,
response: 'ok'
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/get_bots'
}
}{
type: 'response',
content: {
tag: tag,
response: [
{
"id": 29,
"name": "Buy Bytes with Visa or Mastercard",
"pairing_code": "A1i/[email protected]/bb#0000",
"description": "This bot helps to buy Bytes with Visa or Mastercard. The payments are processed by Indacoin. Part of the fees paid is offset by the reward you receive from the undistributed funds."
},
{
"id": 31,
"name": "World Community Grid linking bot",
"pairing_code": "A/JWTKvgJQ/[email protected]/bb#0000",
"description": "Donate your device’s spare computing power to help scientists solve the world’s biggest problems in health and sustainability, and earn some Bytes in the meantime. This bot allows you to link your Byteball address and WCG account in order to receive daily rewards for your contribution to WCG computations.\n\nWCG is an IBM sponsored project, more info at https://www.worldcommunitygrid.org"
},
{
"id": 36,
"name": "Username registration bot",
"pairing_code": "[email protected]/bb#0000",
"description": "Buy a username and receive money to your @username instead of a less user-friendly cryptocurrency address.\n\nProceeds from the sale of usernames go to Byteball community fund and help fund the development and promotion of the platform."
},
...
]
}
}{
type: 'request',
content: {
tag: tag,
command: 'hub/get_asset_metadata',
params: 'IYzTSjJg4I3hvUaRXrihRm9+mSEShenPK8l8uKUOD3o='
}
}{
type: 'response',
content: {
tag: tag,
response: {
"metadata_unit": "0xXOuaP5e3z38TF5ooNtDhmwNkh1i21rBWDvrrxKt0U=",
"registry_address": "AM6GTUKENBYA54FYDAKX2VLENFZIMXWG",
"suffix": null
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_history',
params: {
witnesses: witnesses,
requested_joints: joints,
addresses: addresses
}
}
}{
type: 'response',
content: {
tag: tag,
response: {Object}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_link_proofs',
params: units
}
}{
type: 'response',
content: {
tag: tag,
response: [Array]
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_parents_and_last_ball_and_witness_list_unit',
params: {
witnesses: witnesses
}
}
}{
type: 'response',
content: {
tag: tag,
response: {
"parent_units": [
"QEdpDamxpez8dlOE4nF8TWwHs+efokBXlxQHK27/y4g="
],
"last_stable_mc_ball": "xF0K/NKO6CGMU6XeBGXzurEcCajomfZMOAU4XmAy+6o=",
"last_stable_mc_ball_unit": "R/RosYwuXJ/mXw5TTfWLVLHdtnnzaKn5EgJkDfzagcs=",
"last_stable_mc_ball_mci": 3795817,
"witness_list_unit": "J8QFgTLI+3EkuAxX+eL6a0q114PJ4h4EOAiHAzxUp24="
}
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_attestation',
params: {
attestor_address: 'FZP4ZJBMS57LYL76S3J75OJYXGTFAIBL',
field: 'name',
value: 'tarmo888'
}
}
}{
type: 'response',
content: {
tag: tag,
response: 'jeiBABcZI5fjyIPHkpb2PipLHzjUgafoPd0b6bdsGUI='
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_attestations',
params: {
address: 'MNWLVYTQL5OF25DRRCR5DFNYXLSFY43K'
}
}
}{
type: 'response',
content: {
tag: tag,
response: [
{
"unit": "jeiBABcZI5fjyIPHkpb2PipLHzjUgafoPd0b6bdsGUI=",
"attestor_address": "FZP4ZJBMS57LYL76S3J75OJYXGTFAIBL",
"profile": {
"name": "tarmo888"
}
}
]
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/pick_divisible_coins_for_amount',
params: {
asset: '',
addresses: addresses,
last_ball_mci: last_ball_mci,
amount: amount,
spend_unconfirmed: 'own'
}
}
}{
type: 'response',
content: {
tag: tag,
response: {Object}
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_definition',
params: 'JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725'
}
}{
type: 'response',
content: {
tag: tag,
response: [
"sig",
{
"pubkey": "Aiy5z+jM1ySTZl1Qz1YZJouF7tU6BU++SYc/xe0Rj5OZ"
}
]
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_balances',
params: [
'JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725',
'UENJPVZ7HVHM6QGVGT6MWOJGGRTUTJXQ'
]
}
}{
type: 'response',
content: {
tag: tag,
response: {
"JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725": {
"base": {
"stable": 1454617324,
"pending": 2417784
}
},
"UENJPVZ7HVHM6QGVGT6MWOJGGRTUTJXQ": {
"base": {
"stable": 0,
"pending": 6
}
}
}
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'light/get_profile_units',
params: [
'MNWLVYTQL5OF25DRRCR5DFNYXLSFY43K'
]
}
}{
type: 'response',
content: {
tag: tag,
response: [
"gUrJfmdDeYhTKAHM5ywydHXpvcensbkv8TPQLuPG3b0="
]
}
}{
type: "response",
content: {
tag: tag,
response: {
error: "I'm light myself, can't serve you",
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'get_system_var_votes'
}
}{
type: "response",
content: {
tag: tag,
response: {
votes: {
op_list: [
{
address: "EJC4A7WQGHEZEKW6RLO7F26SAR4LAQBU",
unit: "LpxhHEfxbOyj0sPlp6UC6XrRABvRJiH4qKEsOcMd1Bc=",
timestamp: 1727794413,
value: [
"2FF7PSL7FYXVU5UIQHCVDTTPUOOG75GX",
....
],
is_stable: 1
},
....
],
threshold_size: [
{
address: "EJC4A7WQGHEZEKW6RLO7F26SAR4LAQBU",
unit: "LpxhHEfxbOyj0sPlp6UC6XrRABvRJiH4qKEsOcMd1Bc=",
timestamp: 1727794413,
value: 10000,
is_stable: 1
},
....
],
....
},
balances: {
"EJC4A7WQGHEZEKW6RLO7F26SAR4LAQBU": 188576687136,
....
}
}
}
}{
type: 'request',
content: {
tag: tag,
command: 'custom',
params: params
}
}{
type: 'response',
content: {
tag: tag,
response: 0 || 'some response' || {Object} || [Array]
}
}%last_ball_unitauthorlast_ball_unitsignedPackageauthor-----END PUBLIC KEY-----public_keytrue+ can be used to force the conversion, e.g. +'3' becomes 3, +false becomes 0.is_private: boolean, is the asset private?feed_value: string or number, optional, search only for this specific value of the data feed;feed_value: string or number, search only for values of the data feed that are =, !=, >, >=, <, or <= than the specified value;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!=-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANOJ1Y6/6uzcHDa7Q1gLO9z0KGOM51sO
Pc2nfBF4RTobSVVpFnWtZF92r8iWCebwgSRSS9dEX6YIMIWNg11LbQ8CAwEAAQ==
-----END PUBLIC KEY-----["autonomous agent", {
// here goes the AA code
}]{
address: "{trigger.address}",
amount: "{trigger.output[[asset=base]] - 1000}"
}["autonomous agent", {
bounce_fees: { base: 10000 },
doc_url: "https://example.com/doc_urls/{{aa_address}}.json",
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{trigger.output[[asset=base]] - 1000}"
}
]
}
}
]
}]["autonomous agent", {
bounce_fees: {
base: 10000,
"n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=": 100
},
...
}]{
"version": "1.0",
"description": "Description shown to users",
"homepage_url": "https://example.com",
"source_url": "https://github.com/byteball/ocore",
"field_descriptions": {
"some_field_name": "Description how to use this parameter",
"some_other_field_name": "Description how to use this other parameter",
...
}
}["autonomous agent", {
base_aa: "ADDRESS_OF_BASE_AA",
params: {
name1: "value1",
name2: "value2",
...
}
}]{
address: "{trigger.address}",
amount: "{trigger.output[[asset=base]] - 1000}"
}{
address: "2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7",
amount: 19000
}{
"{trigger.data.key}": "value"
}{
messages: {
cases: [
{
if: "{trigger.data.define}",
messages: [
// first version of messages
....
]
},
{
if: "{trigger.data.issue}",
init: "{$amount = trigger.output[[asset=base]];}",
messages: [
// second version of messages
....
]
},
{
messages: [
// default version of messages
....
]
}
]
}
}{
messages: [
// second version of messages
....
]
}{
messages: [
{
app: "data",
payload: {
timestamp: "{timestamp}",
subscriber: "{trigger.address}"
}
},
{
if: "{trigger.data.withdrawal_amount > 0}",
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{trigger.data.withdrawal_amount}"
}
]
}
}
]
}{
messages: [
{
init: "{ $addr = trigger.address; }",
app: "data",
payload: {
timestamp: "{timestamp}",
subscriber: "{$addr}"
}
},
{
if: "{trigger.data.withdrawal_amount > 1000}",
init: "{ $amount = trigger.data.withdrawal_amount - 1000; }",
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{$amount}"
}
]
}
}
]
}{
field1: "{ (1 == 2) ? 'value1' : '' }",
field2: "value2"
}{
field2: "value2"
}[ `{ (1 == 2) ? "value1" : "" }`, "value2" ][ "value2" ]{
messages: [
{
if: "{trigger.data.send_all}",
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7",
amount: 1000
},
{
address: "{trigger.address}"
}
]
}
}
]
}{
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{trigger.address}",
amount: "{trigger.output[[asset=base]] - 1000}"
}
]
}
},
{
app: "state",
state: `{
var['responded'] = 1;
var['total_balance_sent_back'] += trigger.output[[asset=base]] - 1000;
var[trigger.address || '_response_unit'] = response_unit;
}`
}
]
}$name1 = 1;
$name2 = 'value';
${'name' || 3} = $name1 + 10;[
{
if: `{
$amount = trigger.output[[asset=base]];
// the result of the last expression is the result of if
$amount == 10000
}`,
init: `{
// here we can reference $amount set in if
$half_amount = round($amount / 2);
}`,
messages: [
{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "{ trigger.address }",
amount: `{
// we are in an enclosed oscript
// and can reference $half_amount set in init
$half_amount
}`
}
]
}
},
{
app: "state",
state: `{
// we are in an enclosed oscript
// and can reference $amount set in if
var['received'] = $amount;
// we are in an enclosed oscript
// and can reference $half_amount set in init
var['sent_back'] = $half_amount;
}`
}
]
},
{
if: "{trigger.data.payout}",
init: `{
// here we cannot reference $amount nor $half_amount from the above
// we can even assign other values to them without breaking the single-assignment rule
$amount = 10;
}`,
...
}
]if (trigger.data.deposit){
$amount = trigger.output[[asset=base]];
}
$fee = round($amount * 0.01); // we can reference the $amount from above$data = trigger.data;
$action = $data.params.action;
$player_score = $data.params[$player_name || '_score'];$obj = {a: 3, b: 7 };
$arr = [7, 2, 's', {a: 6}];$obj = {a: 3, b: 7 };
$obj.a = 4; // modifies an existing field
$obj.c = 10; // adds a new field
delete($obj, 'b'); // deletes a field
freeze($obj); // prohibits further mutations of the object
$arr = [7, 2, 's', {a: 6}];
$arr[0] = 8; // modifies an existing element
$arr[] = 5; // adds a new element
delete($arr, 1); // removes element 1
freeze($arr); // prohibits further mutations of the array$f = ($x) => {
$a = var['a'];
$x * $a
};
// one-line syntax for functions that have only one expression
$sq = $x => $x^2;
$res = $f(2);
$nine = $sq(3);['autonomous agent', {
getters: `{
$sq = $x => $x^2;
$g = ($x, $y) => $x + 2*$y;
$h = ($x, $y) => $x||$y;
$r = ($acc, $x) => $x + $acc;
}`,
init: `{
// uncomment if the AA serves as library only
// bounce("library only");
...
}`,
...
}]$nine = MXMEKGN37H5QO2AWHT7XRG6LHJVVTAWU.$sq(3);$remote_aa = "MXMEKGN37H5QO2AWHT7XRG6LHJVVTAWU";
$nine = $remote_aa.$sq(3);$nine = $remote_aa#5.$sq(3);$max_complexity = 5;
$nine = $remote_aa#$max_complexity.$sq(3);$base_aa = '3DGWRKKWWSC6SV4ZQDWEHYFRYB4TGPKX';
$nine = $remote_aa#$base_aa.$sq(3);const { executeGetter } = require('ocore/formula/evaluation.js');
const db = require('ocore/db.js');
const args = ["arg1", "arg2"];
const res = await executeGetter(db, aa_address, getter, args);// assigning current AAs state variable to local constant
$my_var_name1 = var['var_name1'];
// assigning other AAs state variable to local constant
$their_var_name1 = var['JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY']['var_name1'];var['var_name1'] = 'var_value';
var['var_name2'] = 10;
var['var_name3'] += 10;
var['var_name4'] = false;
var['var_name5'] = {a:8, b:2};
var['var_name5'] ||= {c:6}; // concat an objectvar['sent_back'] = $half_amount;
var['count_investors'] += 1;
var['amount_owed'] += trigger.output[[asset=base]];
var['pending'] = false;
$x = var['JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY']['var_name1'];response['key'] = 'text';response['message'] = "set exchange rate to 0.123 tokens/byte";
response['deposit'] = 2250000;{
"responseVars": {
"message": "set exchange rate to 0.123 tokens/byte",
"deposit": 2250000
}
}var trigger_address = '';
const eventBus = require('ocore/event_bus.js');
eventBus.on('aa_response_to_address-' + trigger_address, (objAAResponse) => {
// handle event
});var aa_address = '';
const walletGeneral = require('ocore/wallet_general.js');
const eventBus = require('ocore/event_bus.js');
walletGeneral.addWatchedAddress(aa_address, () => {
eventBus.on('aa_response_from_aa-' + aa_address, (objAAResponse) => {
// handle event
});
});{
mci: 2385,
trigger_address: '2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7',
trigger_unit: 'f2S6Q3ufjzDyl9YcB51JUj2z9nE1sL4XL2VoYOrVRgQ=',
aa_address: 'JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY',
bounced: false,
response_unit: 'JCJ1ZGkl2BtUlYoeu6U2yshp97pen/fIkTHvaKYjZa4=',
objResponseUnit: {
version: '2.0dev',
alt: '3',
timestamp: 1560939440,
messages: [ ... ],
authors: [ ... ],
last_ball_unit: 'cxjDfHzWWgW8LqsC8yoDhgCYmwThmFfkdygGnDrxiFg=',
last_ball: 'gjg8W2cE4WGFAIIsU2BvLOQKOpH/03Oo1SDS3/2SQDs=',
witness_list_unit: '3gLI9EnI2xe3WJVPwRg8s4CB24ruetuddS0wYa2EI3c=',
parent_units: [ 'f2S6Q3ufjzDyl9YcB51JUj2z9nE1sL4XL2VoYOrVRgQ=' ],
headers_commission: 267,
payload_commission: 157,
unit: 'JCJ1ZGkl2BtUlYoeu6U2yshp97pen/fIkTHvaKYjZa4='
},
response: {}
}$amount = trigger.output[[asset=base]];
var['sent_back'] = round($half_amount/2);
response['message'] = "set exchange rate to 0.123 tokens/byte";
if ($amount >= 42000)
bounce("amount too large");
if (balance[base] < 20000)
return;$amount = trigger.output[[asset=base]];
$action = trigger.data.action;$amount = trigger.output[[asset=base]];
$action = trigger.data.action;
$amount*2{
output: {address: "BSPVULUCOVCNXQERIHIBUDLD7TIBIUHU", amount: 2e5}
}{
app: "payment",
payload: {
asset: "base",
outputs: [
`{trigger.data.output}`
]
}
}{
app: "payment",
payload: {
asset: "base",
outputs: [
{
address: "BSPVULUCOVCNXQERIHIBUDLD7TIBIUHU",
amount: 2e5
}
]
}
}return expr;return;if (condition){
$x = 1;
$y = 2 * $x;
}
else{
$x = 2;
$z = $x^3;
}if (condition)
$x = 1;$amount - 1000
$amount^2 / 4'abc' || 'def' // 'abcdef'
[4, 6] || [3, 1] // [4, 6, 3, 1]
{x: 1, y: 7} || {y: 8, a:9} // {x: 1, y: 8, a:9}expr1 OTHERWISE expr2condition ? expr1 : expr2typeof(anything)sqrt(number)
ln(number)abs(number)round(number [, decimal_places])
ceil(number [, decimal_places])
floor(number [, decimal_places])min(number1, [number2[, number3[, ...]]])
max(number1, [number2[, number3[, ...]]])hypot(number1, [number2[, number3[, ...]]])substring(string, start_index)
substring(string, start_index, length)index_of(string, search_string)starts_with(string, prefix)
ends_with(string, suffix)
contains(string, search_string)to_upper(string)
to_lower(string)replace(str, search_str, replacement)has_only(str, allowed_chars)parse_date(ISO8601_date)
parse_date(ISO8601_datetime)timestamp_to_string(timestamp)
timestamp_to_string(timestamp, 'datetime')
timestamp_to_string(timestamp, 'date')
timestamp_to_string(timestamp, 'time')json_parse(string)json_stringify(object)$ar = [2, 5, 9];
$ar2 = map($ar, 3, $x => $x^2);$f = $x => $x^2;
$ar = [2, 5, 9];
$ar2 = map($ar, 3, $f);$ar2 = map($ar, 3, $remote_aa.$f);$c = 3;
$ar = [2, 5, 9];
// sums all elements, will return 16
$acc = reduce($ar, $c, ($acc, $x) => $acc + $x, 0);split("let-there-be-light", "-") // ["let", "there", "be", "light"]
join(["let", "there", "be", "light"], "-") // "let-there-be-light"
split("let-there-be-light", "-", 2) // ["let", "there"]reverse([4, 8, 3]) // [3, 8, 4]keys({b: 3, a: 8}) // ['a', 'b']length(string|number|object|array)array_length(object)number_from_seed(string)
number_from_seed(string, max)
number_from_seed(string, min, max)sha256(string|number|boolean|object)
sha256(string|number|boolean|object, 'base64')
sha256(string|number|boolean|object, 'base32')
sha256(string|number|boolean|object, 'hex')$definition = ["sig", {
"pubkey": "Ald9tkgiUZQQ1djpZgv2ez7xf1ZvYAsTLhudhvn0931w"
}];
$address = chash160($definition);
$aa_definition = ['autonomous agent', {
...
}];
$aa_address = chash160($aa_definition);exists(trigger.data.param)is_integer(number)is_array(object)is_assoc(object)is_valid_address(string)is_aa(string)is_valid_amount(number)is_valid_signed_package(signedPackage, address){
"signed_message": {
"field1": "value1",
"field2": "value2",
...
},
"authors": [
{
"address": "2QHG44PZLJWD2H7C5ZIWH4NZZVB6QCC7",
"authentifiers": {
"r": "MFZ0eFJeLAgAmm6BJdvbEzNt7x0H2Fb5RQBBpMSmyVFMLM2r2SX5chU9hbEWXExkz/T2hXAk1qHmxkAbbpZw8w=="
}
}
],
"last_ball_unit": "izgjyn9bpbJjwpKQV7my0Dq1VUHbzrLpWLrdR0fDydw=",
"version": "2.0"
}var headlessWallet = require('headless-obyte');
var signed_message = require('ocore/signed_message.js');
signed_message.signMessage(message, address, headlessWallet.signer, true, function (err, signedPackage) {
// handle result here
trigger.data.signedPackage = signedPackage;
});is_valid_sig(message, public_key, signature)-----BEGIN PUBLIC KEY-----
MEowFAYHKoZIzj0CAQYJKyQDAwIIAQEEAzIABG7FrdP/Kqv8MZ4A097cEz0VuG1P\n\ebtdiWNfmIvnMC3quUpg3XQal7okD8HuqcuQCg==
-----END PUBLIC KEY-----vrf_verify(seed, proof, pubkey)is_valid_merkle_proof(element, proof);bounce(string);require(condition, error_message);if (!condition) {
bounce(error_message);
}log(expr1, expr2, ...);const sig = require(‘ocore/signature.js’)
var signature = sig.signMessageWithEcPemPrivKey(message, encoding, pem_key)const sig = require(‘ocore/signature.js’)
var signature = sig.signMessageWithRsaPemPrivKey(message, encoding, pem_key)const sig = require(‘ocore/signature.js’)
var proof = vrfGenerate(seed, pem_key)// this is a comment line
$x = 1; // this part of line is a comment/*
A comment block
*/trigger.output[[asset=assetID]].field
trigger.output[[asset!=assetID]].fieldtrigger.output[[asset=base]]
trigger.output[[asset=base]].amount
trigger.output[[asset='j52n7Bfec9jW']]
trigger.output[[asset=$asset]]
trigger.output[[asset!=base]]
trigger.output[[asset!=base]].amount
if (trigger.output[[asset!=base]].asset == 'ambiguous'){
...
}trigger.outputs[assetID]$asset = 'base';
trigger.outputs['base']
trigger.outputs[$asset]
trigger.outputs.base{
"app": "data",
"payload": {
"field1": {
"field2": "value2",
"abc": 88
},
"abc": "def"
},
"payload_hash": "..."
}{
"field1": {
"field2": "value2",
"abc": 88
},
"abc": "def"
}{
"field2": "value2",
"abc": 88
}definition[trigger.address]definition[trigger.address][0] == 'autonomous agent'
definition[trigger.address][1].base_aa == 'EXPECTED_BASE_AA'.asset[expr].field
asset[expr][field_expr]asset[base].cap
asset['base'].cap
asset['abc'].exists
asset['n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY='].is_issued
asset['n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=']['is_' || 'issued']
asset['n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=']['is_' || 'private']data_feed[[oracles=listOfOracles, feed_name=nameOfDataFeed, ...]]data_feed[[oracles='JPQKPRI5FMTQRJF4ZZMYZYDQVRD55OTC', feed_name='BTC_USD']]
data_feed[[oracles=this_address, feed_name='score']]
data_feed[[oracles='JPQKPRI5FMTQRJF4ZZMYZYDQVRD55OTC:I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT', feed_name='timestamp']]in_data_feed[[oracles=listOfOracles, feed_name=nameOfDataFeed, feed_value>feedValue, ...]]in_data_feed[[oracles='JPQKPRI5FMTQRJF4ZZMYZYDQVRD55OTC', feed_name='BTC_USD', feed_value>12345.67]]
in_data_feed[[oracles=this_address, feed_name='score', feed_value=$score]]
in_data_feed[[oracles='JPQKPRI5FMTQRJF4ZZMYZYDQVRD55OTC:I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT', feed_name='timestamp', feed_value>=1.5e9]]attestation[[attestors=listOfAttestors, address=attestedAddress, ...]].field
attestation[[attestors=listOfAttestors, address=attestedAddress, ...]][field_expr]attestation[[attestors='UOYYSPEE7UUW3KJAB5F4Y4AWMYMDDB4Y', address='BI2MNEVU4EFWL4WSBILFK7GGMVNS2Q3Q']].email
attestation[[attestors=this_address, address=trigger.address]]
attestation[[attestors='JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725', address='TSXOWBIK2HEBVWYTFE6AH3UEAVUR2FIF', ifnone='anonymous']].steem_username
attestation[[attestors='JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725', address='TSXOWBIK2HEBVWYTFE6AH3UEAVUR2FIF']].reputationbalance[asset]
balance[aa_address][asset]balance[base]
balance['n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=']
balance['JVUJQ7OPBJ7ZLZ57TTNFJIC3EW7AE2RY'][base]output[[asset=assetID, amount>minAmount, address=outputAddress]].field
input[[asset=assetID, amount=amountValue, address=inputAddress]].fieldinput[[asset=base]].amount
output[[asset = base, address=GFK3RDAPQLLNCMQEVGGD2KCPZTLSG3HN]].amount
output[[asset = base, address='GFK3RDAPQLLNCMQEVGGD2KCPZTLSG3HN']].amount
output[[asset = 'n9y3VomFeWFeZZ2PcSEcmyBb/bI7kzZduBJigNetnkY=', amount>=100]].addressunit[unit_hash] // returns an object that represents the requested unit
unit[response_unit] // returns an object of response unit generated by the current AA invocation$x.messages[[.app = 'payment', .payload.asset != 'ff', .payload.outputs.0.amount > 10]].payload.assetunit['kGQZkIj3gRclCp2vw9LMg0jVnPKYCebXHxm8XYPxbQ4='].mci
unit['kGQZkIj3gRclCp2vw9LMg0jVnPKYCebXHxm8XYPxbQ4='].timestamp
unit['kGQZkIj3gRclCp2vw9LMg0jVnPKYCebXHxm8XYPxbQ4='].messages[[.app='payment']].payload.outputs[[.address='MYESSCFGDERS3YIEGNZDOG2BI5HKQHLU']].amountconst headlessWallet = require('headless-obyte');
const eventBus = require('ocore/event_bus.js');
const objectHash = require('ocore/object_hash.js');
function postAA() {
let definition = ["autonomous agent", {
bounce_fees: { base: 10000 },
messages: [
...
]
}];
let payload = {
address: objectHash.getChash160(definition),
definition: definition
};
let objMessage = {
app: 'definition',
payload_location: 'inline',
payload_hash: objectHash.getBase64Hash(payload),
payload: payload
};
let opts = {
messages: [objMessage]
};
headlessWallet.issueChangeAddressAndSendMultiPayment(opts, (err, unit) => {
if (err){
/*
something went wrong,
maybe put this transaction on a retry queue
*/
return;
}
// handle successful payment
});
}
eventBus.on('headless_wallet_ready', postAA);["autonomous agent", {
bounce_fees: { base: 10000 },
messages: [
{
app: "definition",
payload: {
definition: ["autonomous agent", {
base_aa: "BASE_AA_TEMPLATE_ADDRESS",
params: {
name1: "{trigger.data.name1}",
name2: "{trigger.data.name2}",
...
}
}]
}
},
...
]
}]