The simple callback {use.case.1}

In some cases it is necessary to have a mechanism to call the contract back. For example a fond which withdraws its budget every year to specific people. But Solidity can’t call itself.

Therefore extend from usingCaptainJS and start coding…

Use a unique integer jobId to identify your callback. Invoke RingShipsBell with enough gas and receive the callback in method RingRing. That’s it.

To make sure that RingRing will only be called by the captain himself and not a pirate, add onlyCaptainsOrdersAllowed to its declaration.

pragma solidity ^0.4.25; import "https://github.com/CaptainJavaScript/Solidity/usingCaptainJS.sol";

contract SimpleCallback is usingCaptainJS { uint constant CALLBACK_ID = 3; function CallbackExample() public {

RingShipsBell(

CALLBACK_ID, /* give the job a unique ID */

60 * 24, /* 24 hours */

45000, /* gas budget for callback */

DEFAULT_GAS_PRICE /* use default gas price */

);

} function RingRing(uint UniqueIdentifier) external onlyCaptainsOrdersAllowed {

if(UniqueIdentifier == CALLBACK_ID) {

/* this will be executed 24 hours after CallbackExample

* was invoked

*/

}

} }

Simply use Mathjs {use.case.2}

Now let’s look at a simple JavaScript job. JavaScript’s mathjs library has multiple useful functions such as the conversion of centimeter to inch.

In order to use this library you need to call the Run function and hand over the JavaScript code that is necessary to convert centimeters to inch.

The JavaScript code you want to submit must be written the following way:

module.exports = function(CaptainJSIn) { /* here goes your code */ }

CaptainJS will invoke your code within a container by calling the default function. CaptainJSIn will contain your inputs of the JavaScript function. Then CaptainJS will return the result of your code. If your JavaScript code was successful: CaptainsResultwill be invoked. The return result is always a string.

If your JavaScript code was not successful or its result couldn’t be send back (it failed or there was not enough gas orwhatever happened) then CaptainsError will be invoked.

To make sure that both CaptainsResult and CaptainsError will only be called by the captain himself and not a pirate, add onlyCaptainsOrdersAllowed to its declaration.

A runtime slice has a duration of 10 seconds and it includes the download and install of all required npm modules. At the moment 6 runtime slices are the maximum.

Here’s the complete code snippet:

contract SeamansExamples is usingCaptainJS {

...

uint constant CENTIMETER_JOB = 1; function CentimeterToInchExample(string Centimeter) public {

Run(

CENTIMETER_JOB, /* give the job a unique ID */

/* JavaScript code I want to execute: */

"module.exports = function(CaptainJSIn) { var math = require('mathjs'); return math.eval(CaptainJSIn + ' cm to inch'); }",

Centimeter, /* Input parameter which will result in CaptainJSIn (see above) */

"mathjs", /* Nodejs libraries we need */

3, /* we need a maximum of 3 runtime slices */

DEFAULT_GAS_UNITS, /* use default gas units */

DEFAULT_GAS_PRICE /* we will transfer the default gas price for return */

);

} function CaptainsResult(uint UniqueJobIdentifier, string Result) external onlyCaptainsOrdersAllowed {

if(UniqueJobIdentifier == CENTIMETER_JOB) {

// OK. It worked and we got a result

...

}

}



function CaptainsError(uint UniqueJobIdentifier, string Error) external onlyCaptainsOrdersAllowed {

if(UniqueJobIdentifier == CENTIMETER_JOB) {

// OK. It didn't work :-/

}

} }

Call external resources like WolframAlpha {use.case.3}

If you want to call external resources such as WolframAlpha it’s pretty easy, too. Let’s ask WolframAlpha anything it knows about a country like France. Therefore you design your code the same way like you did in the 2nd use case.

To query WolframAlpha you use JavaScript’s axios library. The default function must be async so that you can wait for a result when you invoke axios.get(…). axios will return a JSON object but we need to flatten it to a string:

module.exports = async function(CaptainJSIn) {

const axios = require('axios');

const WAlpha = await axios.get('http://www.wolframalpha.com/queryrecognizer/query.jsp?appid=DEMO&mode=Default&i=' + CaptainJSIn + '&output=json');

return JSON.stringify(WAlpha.data);

}

Again, CaptainJS will invoke your code within a container by calling this default function. CaptainJSIn will contain your inputs such as “France”.

Again, if your JavaScript code was successful: CaptainsResult will be invoked. Otherwise CaptainsError will be invoked.

And because Solidity sometimes is such a crappy programming language you will use a very expensive concat function to make your JavaScript code more readable.

Here’s the complete code snippet.

contract SeamansExamples is usingCaptainJS {

...

uint constant WOLFRAMALPHA_JOB = 2;



function WolframAlphaExample(string Country) public {

Run(

WOLFRAMALPHA_JOB, /* give the job a unique ID */

concat ( /* JavaScript code I want to execute: */

"module.exports = async function(CaptainJSIn) { ",

" const axios = require('axios'); ",

" const WAlpha = await axios.get('http://www.wolframalpha.com/queryrecognizer/query.jsp?appid=DEMO&mode=Default&i=' + CaptainJSIn + '&output=json'); ",

" return JSON.stringify(WAlpha.data); ",

"}"

),

Country, /* Input parameter which will result in CaptainJSIn (see above) */

"axios", /* Nodejs libraries we need */

3, /* we need a maximum of 3 runtime slices */

200000, /* use 200,000 gas units */

DEFAULT_GAS_PRICE /* use default gas price */

);

}



...

}

Requsting JSON, XML/XPath or HTML/jQuery {use.case.4}

A classic oracle request is a simple JSON, XML/XPath or HTML/jQuery request. Instead of writing a full-blown JavaScript code which does the query for you the newest release has predefined queries.

To invoke a simple query just use the Run method in the same way as you did it in the previous 2 use cases. But instead of submitting JavaScript code send an URL that has either a html:, xml: or json: prefix. The input parameter of the Run method is either a JSON, XPath or jQuery expression. Typically 1 runtime slice is enough.

If your JavaScript code was successful: CaptainsResult will be invoked. Otherwise CaptainsError will be invoked.

(The client libraries now include a test module to see if your code works — before you submitted to the blockchain)

function HTMLqueryExample() public {

Run(

HTML_QUERY_EXAMPLE, /* give the job a unique ID */

/* url needs to start with html: */

"html:http://www.amazon.co.uk/gp/product/1118531647",

/* Input parameter is the jQuery. Result will be stored in QUERY_RESULT variable */

"$('span.inlineBlock-display span.a-color-price').each(function(i, element) {var el = $(this); QUERY_RESULT = el.text(); })",

"", /* no modules required */

1, /* queries are fast */

DEFAULT_GAS_UNITS, /* use default gas units */

DEFAULT_GAS_PRICE /* we will transfer the default gas price for return */

);

}



function JSONqueryExample() public {

Run(

JSON_QUERY_EXAMPLE, /* give the job a unique ID */

/* url needs to start with json: */

"json:https://api.kraken.com/0/public/Ticker?pair=ETHUSD",

/* Input parameter is the JSON path */

"result.XETHZUSD.a[0]",

"", /* no modules required */

1, /* queries are fast */

DEFAULT_GAS_UNITS, /* use default gas units */

DEFAULT_GAS_PRICE /* we will transfer the default gas price for return */

);

}

Encrypting Your Data {use.case.5}

For those seamen who like to encrypt their data which is stored in the blockchain I’ve added an encryption module to the latest release. This allows you to send encrypted data to the Captain’s NodeJS containers. There it will be decrypted and executed. (The string result will not be send encrypted back)

For example you want to send a mail after a transaction happened then you could make use of nodemailer, login to your mail account and send the mail. In this case your code will look similar to this one:

module.exports = async function(CaptainJSIn) {

var nodemailer = require("nodemailer");

var transport = nodemailer.createTransport({

host: "smtp-mail.outlook.com", // hostname

secureConnection: false, // TLS requires secureConnection to be false

port: 587, // port for secure SMTP

tls: {

ciphers:'SSLv3'

},

auth: {

user: "myaddress@outlook.com",

pass: "mysecretpassword"

}

});

var mailOptions = {

to: 'donald.trum@whitehouse.gov', // list of receivers

subject: "you're fired", // Subject line

text: "Dear Donald, ..."

};



// send mail with defined transport object

transport.sendMail(mailOptions, function(error, info){});

}

The bad side of the story is, that your account details will be stored within your code. And these account details will be stored in Ethereum’s blockchain forever. That’s bad. But there’s hope!

In the client libraries I’ve added the https://github.com/CaptainJavaScript/Seaman-Client/blob/master/CaptainJS-Encryption.jswhich help you to encrypt your data before you include them in your contract:

just simply add your module to a file like MailSample.js

encrypt the file using EncryptFile from CaptainJS-Encryption.js

add the encrypted code to your contract

Encrypt your code:

var ENC = require("./CaptainJS-Encryption.js");

async function RUN() {

await ENC.EncryptFile(false, "MailSample.js", "EncryptedMailSample.txt",

() => { console.log("Success!"); },

(ERROR) => { console.log(ERROR); }

);

}

RUN();

And add your encrypted code to the Solidity-based contract:

function HTMLqueryExample() public {

Run(

ENCRYPTED_MAIL_EXAMPLE,

"crypt:8366268bd167a9f8318f99c71d0f489d0372b545735c2e10303c47bad2507e933171f72f...",

"",

"",

1,

DEFAULT_GAS_UNITS,

DEFAULT_GAS_PRICE

);

}

The Captain will never read your code! It just gets executed in a fresh new container. In other words: if you encrypt your data then it will stay encrypted until it’s executed in a container (and the container will vanish after execution). The encryption uses the public/private key of the Captain’s contracts. Allthough the captain could read your encrypted data for private purposes, he won’t do it. That’s a promise and a proof of trust.

That’s it! :-)