Skip to content

Building Dapps

Andre Cronje edited this page Dec 27, 2018 · 5 revisions

Setup

Option 1: Connect directly to testnet at;

Option 2: Host own local node;

Step 0 Docker

Step 1 Installing Go (Ubuntu)

Step 1 Setup

Step 2 Clone Repo

Step 3 (Optional, hard mode)

Running the node

Example Commands

HTTP API

Get controlled accounts

Get any account

Send transactions from controlled accounts

Get Transaction receipt

Send raw signed transactions

Deploying a contract

Create bytecode from solidity file

Get Contract Address

Call the Deployed Contract

Setup

Option 1: Connect directly to testnet at;

https://vm.fantom.services

Example call; https://vm.fantom.services/account/0xFD00A5fE03CB4672e4380046938cFe5A18456Df4

Option 2: Host own local node;

Step 0 Docker

Create an 3 node lachesis cluster with:

n=3 BUILD_DIR="$PWD" ./docker/builder/scale.bash

Step 1 Installing Go (Ubuntu)

Step 1 Setup

sudo apt-get update

sudo apt-get -y upgrade

sudo curl -O https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz

sudo tar -xvf go1.9.1.linux-amd64.tar.gz

sudo mv go /usr/local

sudo nano ~/.profile

export PATH=$PATH:/usr/local/go/bin

source ~/.profile

Step 2 Clone Repo

mkdir -p $GOPATH/src/github.com/Fantom-foundation/

cd $GOPATH/src/github.com/Fantom-foundation

git clone https://github.com/Fantom-foundation/go-lachesis.git

export GOPATH=$HOME/work

cd $GOPATH/src/github.com/Fantom-foundation/go-lachesis

curl https://glide.sh/get | sh

glide install

Step 3 (Optional, hard mode)

sudo apt-get update

sudo apt-get -y upgrade

sudo curl -O https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz

sudo tar -xvf go1.9.1.linux-amd64.tar.gz

sudo mv go /usr/local

sudo nano ~/.profile

export PATH=$PATH:/usr/local/go/bin

source ~/.profile

mkdir $HOME/work

export GOPATH=$HOME/work

mkdir -p $HOME/work/src/github.com/user/

cd $HOME/work/src/github.com/user/

git clone https://github.com/Fantom-foundation/go-lachesis.git

apt-get install -y build-essential

#Lachesis

go get github.com/dgraph-io/badger

go get github.com/sirupsen/logrus

go get gopkg.in/urfave/cli.v1

make build

#Lachesis

./build/lachesis keygen

mkdir -p /root/.lachesis/

vi /root/.lachesis/priv_key.pem

vi /root/.lachesis/peers.json

[

{

"NetAddr":"ip:12000",

"PubKeyHex":"0x0448C9212873C76DE0086DA680F9329735C40674B5FA05105886548B217273B0AFA02D73157F4D96DACFE1D9E44DBB2608F5C60D037743DB18567B82D077CBAE40"

},

{

"NetAddr":"ip:12000",

"PubKeyHex":"0x04C503A238046D9095B548E61939CA58BE926C6809F7205CD2D671A88C4E8369754ADE343FBB4FBE9A46118EB549753B76B18243369E0475A319989F06879CFE19"

},

{

"NetAddr":"ip:12000",

"PubKeyHex":"0x046A584F2FBDF61A36B8D30461C9285B817F60271D41B7B7EC445EF73DD2B363B1F782D5FE96D3D08B71750F4D07CC137AE7AD9A95574791737E6E407880015B3A"

}

]

Running the node

The default data dir is currently;

$HOME/.lachesis/

In this folder it expects two files;

priv_key.pem

peers.json

peers.json is the current node list (completely permissioned system currently, it is defined as per below, the

PubKeyHex is the public key (with 0x) that corresponds to the private key found in priv_key.pem

[

{

"NetAddr":"ip:12000",

"PubKeyHex":"0x0448C9212873C76DE0086DA680F9329735C40674B5FA05105886548B217273B0AFA02D73157F4D96DACFE1D9E44DBB2608F5C60D037743DB18567B82D077CBAE40"

},

{

"NetAddr":"ip:12000",

"PubKeyHex":"0x04C503A238046D9095B548E61939CA58BE926C6809F7205CD2D671A88C4E8369754ADE343FBB4FBE9A46118EB549753B76B18243369E0475A319989F06879CFE19"

},

{

"NetAddr":"ip:12000",

"PubKeyHex":"0x046A584F2FBDF61A36B8D30461C9285B817F60271D41B7B7EC445EF73DD2B363B1F782D5FE96D3D08B71750F4D07CC137AE7AD9A95574791737E6E407880015B3A"

}

]

To run the nodes you execute;

#service node

./build/lachesis run -node_addr="ip:port" -service_addr="ip:port"

#proxy node

./build/lachesis run -node_addr="ip:port" -proxy_addr="ip:port" -client_addr="ip:port"

Example Commands

#service node

./build/lachesis run -node_addr="ip:12000" -service_addr="ip:8000"

#proxy node

./build/lachesis run -node_addr="ip:12000" -proxy_addr="ip:9000" -client_addr="ip:9000"

You can subscribe to service_addr for http requests, so in above example http://ip:8000/stats

HTTP API

Get controlled accounts

This endpoint returns all the accounts that are controlled by the evm instance. These are the accounts whose private keys are present in the keystore. example:

Get any account

This method allows retrieving the information about any account, not just the ones whose keys are included in the keystore.

Send transactions from controlled accounts

Send a transaction from an account controlled by the evm instance. The transaction will be signed by the service since the corresponding private key is present in the keystore. example: Send Ether between accounts

Get Transaction receipt

Example:

Send raw signed transactions

Most of the time, one will require to send transactions from accounts that are not controlled by the evm instance. The transaction will be assembled, signed and encoded on the client side. The resulting raw signed transaction bytes can be submitted to evm through the /rawtx endpoint. example:

Below is how to interact, otherwise standard EVM rules.

// node.js

const axios = require('axios');

const EthereumTx = require('ethereumjs-tx')

const privateKey = Buffer.from('<private key>', 'hex')

const txParams = {

nonce: '0x00',

gasPrice: '0x000000000001',

gasLimit: '0x27100',

to: '0xFD00A5fE03CB4672e4380046938cFe5A18456Df4',

value: '0x00',

data: '0x',

// EIP 155 chainId - mainnet: 1, ropsten: 3

chainId: 1

}

const tx = new EthereumTx(txParams)

tx.sign(privateKey)

const serializedTx = tx.serialize()

const hexTx = '0x' + serializedTx.toString('hex')

axios.post('http://ip:port/sendRawTransaction', hexTx)

.then(function (response) {

console.log(response.data);

})

.catch(function (error) {

console.log(error);

});

// http://ip:port/account/629007eb99ff5c3539ada8a5800847eacfc25727

// http://ip:port/sendRawTransaction

// http://ip:port/transactions

// http://ip:port/accounts

// http://ip:port/transaction/{}

// http://ip:port/account/{}

/*

http://ip:port/account/0xFD00A5fE03CB4672e4380046938cFe5A18456Df4

{"address":"0xFD00A5fE03CB4672e4380046938cFe5A18456Df4","balance":9999790000000000000000,"nonce":1}

http://ip:port/transaction/0x68a07a9dc6ff0052e42f4e7afa117e90fb896eda168211f040da69606a2aeddc

{"root":"0x7ed3e21533e05c18ded09e02d7bf6bf812c218a3a7af8c6b5cc23b5cb4951069","transactionHash":"0x68a07a9dc6ff0052e42f4e7afa117e90fb896eda168211f040da69606a2aeddc","from":"0xfd00a5fe03cb4672e4380046938cfe5a18456df4","to":"0xfd00a5fe03cb4672e4380046938cfe5a18456df4","gasUsed":21000,"cumulativeGasUsed":21000,"contractAddress":"0x0000000000000000000000000000000000000000","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","failed":false}

function getNonce(tx) {

axios.get('http://ip:port/account/'+tx.from)

.then(function (response) {

tx.nonce = response.data.nonce

generateRawTx(tx, priv)

})

.catch(function (error) {

console.log(error);

});

}

*/

Deploying a contract

Create bytecode from solidity

JSONbig = require('json-bigint');

fs = require('fs')

solc = require('solc')

Web3 = require('web3')

web3 = new Web3()

function Contract(file, name) {

this.file = file

this.name = ':'+name

this.bytecode = ''

this.abi = ''

}

Contract.prototype.compile = function() {

input = fs.readFileSync(this.file)

output = solc.compile(input.toString(), 1)

console.log('compile output', output)

this.bytecode = output.contracts[this.name].bytecode

this.abi = output.contracts[this.name].interface

this.w3 = web3.eth.contract(JSONbig.parse(this.abi)).at('');

}

const contract = new Contract('./helloWorld.sol', 'HelloWorld')

contract.compile()

const EthereumTx = require('ethereumjs-tx')

const privateKey = Buffer.from('<private_key>', 'hex')

const txParams = {

from: '0x5A7fcbE8e848E957166631E5DAbD683210f06E7c',

value: 0,

nonce: nonce,

chainId:1,

gas: 1000000,

gasPrice:0,

data: '0x' + contract.bytecode + constructorParams,

}

const tx = new EthereumTx(txParams)

tx.sign(privateKey)

const serializedTx = tx.serialize()

const hexTx = '0x' + serializedTx.toString('hex')

axios.post('https://vm.fantom.services/sendRawTransaction', hexTx)

.then(function (response) {

console.log(response.data);

})

.catch(function (error) {

console.log(error);

});

Get Contract Address

Call https://vm.fantom.services/tx/0x81163ffaa2e05720bfb772e58edec81bf5640f9dbebe0105720cfeb9066256da

{"root":"0x3c7b6e8900b625a5c0f5610ff1a6414ae078f6f9fbe3f69511074b642f7a30c8","transactionHash":"0x81163ffaa2e05720bfb772e58edec81bf5640f9dbebe0105720cfeb9066256da","from":"0x5a7fcbe8e848e957166631e5dabd683210f06e7c","to":null,"value":0,"gas":1000000,"gasUsed":242199,"gasPrice":0,"cumulativeGasUsed":242199,"contractAddress":"0x8efe306de45fe1f3cdf0338c8d5d1c30f60ba286","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","error":"","failed":false}

contractAddres contains the newly created address.

Call the Deployed Contract

const contract = new Contract('./helloWorld.sol', 'HelloWorld')

contract.compile()

const callData = contract.w3.helloWorld.getData()

//Note the function name of the solidity function

const tx = {

from: '<any from address>',

value:0,

to: '<contract_address>',

data: callData,

}

axios.post('https://vm.fantom.services/call', stx)

.then(function (response) {

console.log(response.data);

hexRes = Buffer.from(response.data.data).toString()

// If you want to parse the results <function_name>

unpacked = contract.parseOutput('helloWorld', hexRes)

})

.catch(function (error) {

console.log(error);

});

Clone this wiki locally