The "Hello World!" Smart Contract example for neo is storing a key/value pair "Hello", "World" inside a smart. To get neon-js to invoke this contract, it takes a few steps. Here I want to document a how to invoke a smart contract and write to something on the neo blockchain.
First let's start with the smart contract:
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using System;
using System.Numerics;
namespace ContractStorage
{
public class ContractStorage : SmartContract {
public static object Main(string operation, object[] args) {
if (operation == "addStorageRequest")
return AddStorageRequest(args);
if (operation == "getPendingRequest")
return GetPendingRequest(args);
else
return "Error: Invalid Parameter";
}
private static object AddStorageRequest(object[] args) {
byte[] key = (byte[])args[0];
byte[] value = (byte[])args[1];
byte[] storedRequests = Storage.Get(Storage.CurrentContext, key);
if (storedRequests.Length == 0) {
Storage.Put(Storage.CurrentContext, key, value);
return true;
}
else
return false;
}
private static object GetPendingRequest(object[] args) {
byte[] key = (byte[])args[0];
byte[] storageValue = Storage.Get(Storage.CurrentContext, key);
return storageValue;
}
}
}
So pretty basic. Two methods, one for saving a key/value pair, one for getting a value based on the key. When we deploy the smart contract, we have to make sure, to click the Need Storage
checkbox. This is how my Deploy contract dialog looked: https://imgur.com/Y2sz1Jg (image embeddding didn't wokr for me :( ). For testing I used the following private docker setup: https://gist.github.com/slipo/f18f1a0b5e6adb7b0bf172b93379d891
Now what do we need to invoke the transcation:
scriptHash
of your smart contract. This can be found in theContractStorage.abi.json
in the build folder. Important: without the 0x prefix.neon-js
expects 40 chars, else it throws an error without explanation (stand 3.2.1)privatekey
&password
from our wallet. We can generate these from the WIF inneon
see: https://imgur.com/BfeB4te . The used values should match the docker image.
What do we need to do to invoke the smart contract:
- ask for the balance of the account
- define our intents
- define our invokes (what we want to do)
- create the script
- create the transaction
- sign the transaction
- send the transaction
The following code is written in angular 4
and typescript
. I will leave the angular parts out.
The first method stores something in the Smart Contract:
SMARTCONTRACTSCRIPTHASH = '2b57048da0deb5cdd138302ca40b82f660bf5060'; // without 0x!
PRIVATEKEY: string = "6PYKN6jd6wGE4dR2FPz7BehZfZ7qZoSHDYzwF6QCMAGAAD2q6B5XjnvNVm"; // from the neon wallet
PASSWORD: string = 'passpharse'; // from the neon wallet
setKey: string = "Stefan";
setValue: string = "Test";
public addStorageRequest() {
// create the account object
let net = 'http://neo-privnet:5000'; // this is a different net (part) then the one rpc where we query the
let account = new Neon.wallet.Account(this.PRIVATEKEY);
account.decrypt(this.PASSWORD);
let fromAddrScriptHash = Neon.wallet.getScriptHashFromAddress(account.address);
// get our balnce (needed for transaction)
Neon.api.neonDB.getBalance(net, account.address)
.then((balance: Neon.wallet.Balance) => {
// create or intents (someone got a link for a good explanation?)
let intents: Neon.tx.TransactionOutput[] = [{
assetId: Neon.CONST.ASSET_ID.GAS,
value: new Neon.u.Fixed8(1), // I gueesed this :)
scriptHash: this.SMARTCONTRACTSCRIPTHASH
}];
// the interesting part: what do we want to do :)
let invoke: Neon.sc.scriptParams = {
scriptHash: this.SMARTCONTRACTSCRIPTHASH,
operation: 'addStorageRequest',
args: [
Neon.sc.ContractParam.string(this.setKey),
Neon.sc.ContractParam.string(this.setValue)
]
}
// create a script from our parameters
let sb = new Neon.sc.ScriptBuilder();
sb.emitAppCall(invoke.scriptHash, invoke.operation, invoke.args, false);
let script = sb.str;
// create a transaction object
let unsignedTx = Neon.tx.Transaction.createInvocationTx(balance, intents, script, 3, { version: 1 });
// sing the transaction object (we write something to the blockchain!)
let signedTx = Neon.tx.signTransaction(unsignedTx, account.privateKey);
// convert the transaction to hx so we can send it in an query
let hexTx = Neon.tx.serializeTransaction(signedTx);
// send the transaction to our net
return Neon.rpc.queryRPC('http://neo-privnet:30333', {
method: 'sendrawtransaction',
params: [hexTx],
id: 1
}); // here we could listen to the response with then/catch
});
That covers the addStorageRequest
part. Now what do we need to do to get the stored value back out again. luckily this is a bit easier, since now we don't need to invoke a transaction (because we don't save anything to the blockchain). There might be some usecases where we also sign get
requests to the blockchain, so we have prove that we requested this data, here I don't find it necessary.
Now we just need the scripthash
of our contract and can use a simpler scriptbuilder
method.
getKey: string = "Stefan";
hideGetDetails = true;
getRequestResult: any = undefined;
getRequestResultDetails: any = undefined;
public getPendingRequest() {
// create our script
let props: Neon.sc.scriptParams = {
scriptHash: this.SMARTCONTRACTSCRIPTHASH,
operation: 'getPendingRequest',
args: [
Neon.sc.ContractParam.string(this.getKey),
],
useTailCall: true
}
let vmScript = Neon.sc.createScript(props);
// invoke the script
Neon.rpc.Query.invokeScript(vmScript)
.execute('http://neo-privnet:30333')
.then((res: any) => {
this.getRequestResultDetails = res;
if (res.result.state === "HALT, BREAK" && // "HALT, BREAK" means it was ok (source?)
!!res.result.stack["0"]) {
// if we stacked the parameters correctly we get the result on postion 0
// if you e.g. provided to many input paramters they get returned to you and are on pos 0, 1, ...
let hexValue = res.result.stack["0"].value;
let result = "";
for (var i = 0; i < hexValue.length; i += 2) {
result += String.fromCharCode(parseInt(hexValue.substr(i, 2), 16));
}
this.getRequestResult = result;
}
}, (errpr) => {
debugger;
});
}
Calling this should return you the value, you just saved in your smart contract! (it takes a block to be registerd, so up to 15sec (Source?))
The full code will be publish later in a github repository (along with other demos). However this question got some interest in the discord group so i kinda rushed it. And it's currently 2am and I have to get up again at 7 for work, so please excuse typos and other mistakes.
If you like to help, please post sources of the marked parts in the comments or add other suggestions. Anything is welcome :)
Update Question: how can I make the code not have a sace after each line, deleting it doesn't work :(
Cool writeup! You can switch from the markdown editor to raw html on the top right when editing, and delete the extra <br> tags
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Thanks, but if I copy the HTML form my editor, it says I have to remove the spans... and it generates a lot fo spans... so won't update it anymore :D
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
The code above sends 1 Gas to the smart contract each time. After some comments in discord the intent should look like this:
const intents: Neon.tx.TransactionOutput[] = [{
assetId: Neon.CONST.ASSET_ID.GAS,
value: new Neon.u.Fixed8(0.00000001),
scriptHash: account.scriptHash
}];
It sends the minuman amount of Gas to itself, so the smartcontract gets executed
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Congratulations @kern.pro! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
You made your First Comment
Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit