#!/usr/bin/env node
This script demonstrates how to deal with the different phases of compiling Sophia contracts to bytecode, deploying the bytecode to get a callable contract address and ultimately, invoke the deployed contract on the æternity blockchain.
/*
* ISC License (ISC)
* Copyright (c) 2018 aeternity developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
'use strict'
We’ll need the main client module Ae
, the Wallet
module, the Crypto
module and the contract
module from the SDK.
const { default: Ae, Wallet, Contract, Crypto } = require('@aeternity/aepp-sdk')
const program = require('commander')
const fs = require('fs')
function exec (infile, fn, args) {
const keypair = Crypto.envKeypair(process.env)
if (!infile || !fn) {
program.outputHelp()
process.exit(1)
}
const code = fs.readFileSync(infile, 'utf-8')
Most methods in the SDK return Promises, so the recommended way of
dealing with subsequent actions is then
chaining with a final catch
callback.
Ae.create
itself is asynchronous as it determines the node’s version and
rest interface automatically. Only once the Promise is fulfilled, we know
we have a working client object.
Both Wallet.create
and Contract.create
need to be initialized with
the Ae client. The Wallet object needs to be passed in because some of
the following actions requires sending signed transactions to be mined,
which implies spending reward tokens to the miner.compile
takes a raw Sophia contract in string form and sends it off to
the node for bytecode compilation. This might in the future be done
without talking to the node, but requires a bytecode compiler
implementation directly in the SDK.
Ae.create(program.host, { debug: program.debug }).then(client => {
return Contract.create(client, Wallet.create(client, keypair)).compile(code)
Invoking deploy
on the bytecode object will result in the contract
being written to the chain, once the block has been mined.
Sophia contracts always have an init
method which needs to be invoked,
even when the contract’s state
is unit
(()
). The arguments to
init
have to be provided at deployment time and will be written to the
block as well, together with the contract’s bytecode.
}).then(bytecode => {
console.log(`Obtained bytecode ${bytecode.bytecode}`)
return bytecode.deploy({ initState: program.init })
Once the contract has been successfully mined, we can attempt to invoke any public function defined within it. The miner who found the next block will not only be rewarded a fixed amount, but also an amount depending on the amount of gas spend.
}).then(deployed => {
console.log(`Contract deployed at ${deployed.address}`)
return deployed.call(fn, { args: args.join(' ') })
The execution result, if successful, will be an AEVM-encoded result value. Once type decoding will be implemented in the SDK, this value will not be a hexadecimal string, anymore.
}).then(value => {
console.log(`Execution result: ${value}`)
}).catch(e => console.log(e.message))
}
The commander
library provides maximum command line parsing convenience.
program
.version('0.1.0')
.arguments('<infile> <function> [args...]')
.option('-i, --init [state]', 'Arguments to contructor function')
.option('-H, --host [hostname]', 'Node to connect to', 'http://localhost:3013')
.option('--debug', 'Switch on debugging')
.action(exec)
.parse(process.argv)