From 58163ff75a1ef54e703b1999972160f0079c0145 Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 4 Apr 2019 14:22:21 +0300 Subject: [PATCH 01/14] refactor(Http): Handle no response in http stamp error handler --- es/utils/http.js | 1 + 1 file changed, 1 insertion(+) diff --git a/es/utils/http.js b/es/utils/http.js index 139793322e..5bec3702ac 100644 --- a/es/utils/http.js +++ b/es/utils/http.js @@ -42,6 +42,7 @@ const processResponse = async (res) => { try { return (await res).data } catch (e) { + if (!e.response) throw e throw Object.assign( Error(`Http request for ${e.config.url} failed with status code ${e.response.status}. Status: ${e.response.statusText}. \nError data: ${JSON.stringify(e.response.data)}`), { data: e.response.data } From 68ae9e1883000b50c3f72fd8639bfc0330da5e7b Mon Sep 17 00:00:00 2001 From: naz_dou <41945483+nduchak@users.noreply.github.com> Date: Fri, 5 Apr 2019 13:45:57 +0300 Subject: [PATCH 02/14] Fix/rename contract methods in docs (#309) * docs(Contract): Rename contract function in docs * docs(Regenerate DOCS): --- docs/api/ae/contract.md | 36 +++++++-------- docs/api/channel/index.md | 93 +++++++++++++++++++++++++++++++++++++++ docs/api/utils/crypto.md | 30 +++++++++++++ es/ae/contract.js | 27 ++++++------ 4 files changed, 154 insertions(+), 32 deletions(-) diff --git a/docs/api/ae/contract.md b/docs/api/ae/contract.md index 4b315dcb52..6446d218b4 100644 --- a/docs/api/ae/contract.md +++ b/docs/api/ae/contract.md @@ -20,12 +20,12 @@ import { Contract } from '@aeternity/aepp-sdk' (Using bundle) * [Contract([options])](#exp_module_@aeternity/aepp-sdk/es/ae/contract--Contract) ⇒ `Object` ⏏ * _async_ * [handleCallError(result)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--handleCallError) ⇒ `Promise.<void>` ⏏ - * [encodeCall(source, name, args)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--encodeCall) ⇒ `Promise.<String>` ⏏ - * [decode(type, data)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--decode) ⇒ `Promise.<String>` ⏏ - * [callStatic(source, address, name, args, options, top, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--callStatic) ⇒ `Promise.<Object>` ⏏ - * [call(source, address, name, args, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--call) ⇒ `Promise.<Object>` ⏏ - * [deploy(code, source, initState, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--deploy) ⇒ `Promise.<Object>` ⏏ - * [compile(source, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--compile) ⇒ `Promise.<Object>` ⏏ + * [contractEncodeCall(source, name, args)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractEncodeCall) ⇒ `Promise.<String>` ⏏ + * [contractDecodeData(type, data)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractDecodeData) ⇒ `Promise.<String>` ⏏ + * [contractCallStatic(source, address, name, args, options, top, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractCallStatic) ⇒ `Promise.<Object>` ⏏ + * [contractCall(source, address, name, args, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractCall) ⇒ `Promise.<Object>` ⏏ + * [contractDeploy(code, source, initState, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractDeploy) ⇒ `Promise.<Object>` ⏏ + * [contractCompile(source, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractCompile) ⇒ `Promise.<Object>` ⏏ @@ -59,9 +59,9 @@ Handle contract call error | --- | --- | --- | | result | `Object` | call result object | - + -### encodeCall(source, name, args) ⇒ `Promise.<String>` ⏏ +### contractEncodeCall(source, name, args) ⇒ `Promise.<String>` ⏏ Encode call data for contract call **Kind**: Exported function @@ -73,9 +73,9 @@ Encode call data for contract call | name | `String` | Name of function to call | | args | `Array` | Argument's for call | - + -### decode(type, data) ⇒ `Promise.<String>` ⏏ +### contractDecodeData(type, data) ⇒ `Promise.<String>` ⏏ Decode contract call result data **Kind**: Exported function @@ -87,9 +87,9 @@ Decode contract call result data | type | `String` | Data type (int, string, list,...) | | data | `String` | call result data (cb_iwer89fjsdf2j93fjews_(ssdffsdfsdf...) | - + -### callStatic(source, address, name, args, options, top, options) ⇒ `Promise.<Object>` ⏏ +### contractCallStatic(source, address, name, args, options, top, options) ⇒ `Promise.<Object>` ⏏ Static contract call(using dry-run) **Kind**: Exported function @@ -106,9 +106,9 @@ Static contract call(using dry-run) | top | `String` | [options.top] Block hash on which you want to call contract | | options | `String` | [options.options] Transaction options (fee, ttl, gas, amount, deposit) | - + -### call(source, address, name, args, options) ⇒ `Promise.<Object>` ⏏ +### contractCall(source, address, name, args, options) ⇒ `Promise.<Object>` ⏏ Call contract function **Kind**: Exported function @@ -123,9 +123,9 @@ Call contract function | args | `Array` | Argument's for call function | | options | `Object` | Transaction options (fee, ttl, gas, amount, deposit) | - + -### deploy(code, source, initState, options) ⇒ `Promise.<Object>` ⏏ +### contractDeploy(code, source, initState, options) ⇒ `Promise.<Object>` ⏏ Deploy contract to the node **Kind**: Exported function @@ -139,9 +139,9 @@ Deploy contract to the node | initState | `Array` | Arguments of contract constructor(init) function | | options | `Object` | Transaction options (fee, ttl, gas, amount, deposit) | - + -### compile(source, options) ⇒ `Promise.<Object>` ⏏ +### contractCompile(source, options) ⇒ `Promise.<Object>` ⏏ Compile contract source code **Kind**: Exported function diff --git a/docs/api/channel/index.md b/docs/api/channel/index.md index 5af8e6e861..246d6f384a 100644 --- a/docs/api/channel/index.md +++ b/docs/api/channel/index.md @@ -21,6 +21,9 @@ import Channel from '@aeternity/aepp-sdk/es/channel/index' * [~shutdown(sign)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..shutdown) ⇒ `Promise.<string>` * [~withdraw(amount, sign, [callbacks])](#module_@aeternity/aepp-sdk/es/channel/index--Channel..withdraw) ⇒ `Promise.<object>` * [~deposit(amount, sign, [callbacks])](#module_@aeternity/aepp-sdk/es/channel/index--Channel..deposit) ⇒ `Promise.<object>` + * [~createContract(options, sign)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..createContract) ⇒ `Promise.<object>` + * [~callContract(options, sign)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..callContract) ⇒ `Promise.<object>` + * [~getContractCall(options)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..getContractCall) ⇒ `Promise.<object>` * [~sendMessage(message, recipient)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..sendMessage) @@ -256,6 +259,96 @@ channel.deposit( } }) ``` + + +#### Channel~createContract(options, sign) ⇒ `Promise.<object>` +Create a contract + +**Kind**: inner method of [`Channel`](#exp_module_@aeternity/aepp-sdk/es/channel/index--Channel) + +| Param | Type | Description | +| --- | --- | --- | +| options | `object` | | +| [options.code] | `string` | Api encoded compiled AEVM byte code | +| [options.callData] | `string` | Api encoded compiled AEVM call data for the code | +| [options.deposit] | `number` | Initial amount the owner of the contract commits to it | +| [options.vmVersion] | `number` | Version of the AEVM | +| [options.abiVersion] | `number` | Version of the ABI | +| sign | `function` | Function which verifies and signs create contract transaction | + +**Example** +```js +channel.createContract({ + code: 'cb_HKtpipK4aCgYb17wZ...', + callData: 'cb_1111111111111111...', + deposit: 10, + vmVersion: 3, + abiVersion: 1 +}).then(({ accepted, state, address }) => { + if (accepted) { + console.log('New contract has been created') + console.log('Contract address:', address) + } else { + console.log('New contract has been rejected') + } +}) +``` + + +#### Channel~callContract(options, sign) ⇒ `Promise.<object>` +Call a contract + +**Kind**: inner method of [`Channel`](#exp_module_@aeternity/aepp-sdk/es/channel/index--Channel) + +| Param | Type | Description | +| --- | --- | --- | +| options | `object` | | +| [options.amount] | `string` | Amount the caller of the contract commits to it | +| [options.callData] | `string` | ABI encoded compiled AEVM call data for the code | +| [options.contract] | `number` | Address of the contract to call | +| [options.abiVersion] | `number` | Version of the ABI | +| sign | `function` | Function which verifies and signs contract call transaction | + +**Example** +```js +channel.callContract({ + contract: 'ct_9sRA9AVE4BYTAkh5RNfJYmwQe1NZ4MErasQLXZkFWG43TPBqa', + callData: 'cb_1111111111111111...', + amount: 0, + abiVersion: 1 +}).then(({ accepted, state }) => { + if (accepted) { + console.log('Contract called succesfully') + console.log('The new state is:', state) + } else { + console.log('Contract call has been rejected') + } +}) +``` + + +#### Channel~getContractCall(options) ⇒ `Promise.<object>` +Get contract call result + +**Kind**: inner method of [`Channel`](#exp_module_@aeternity/aepp-sdk/es/channel/index--Channel) + +| Param | Type | Description | +| --- | --- | --- | +| options | `object` | | +| [options.caller] | `string` | Address of contract caller | +| [options.contract] | `string` | Address of the contract | +| [options.round] | `number` | Round when contract was called | + +**Example** +```js +channel.getContractCall({ + caller: 'ak_Y1NRjHuoc3CGMYMvCmdHSBpJsMDR6Ra2t5zjhRcbtMeXXLpLH', + contract: 'ct_9sRA9AVE4BYTAkh5RNfJYmwQe1NZ4MErasQLXZkFWG43TPBqa', + round: 3 +}).then(({ returnType, returnValue }) => { + if (returnType === 'ok') console.log(returnValue) +}) +``` #### Channel~sendMessage(message, recipient) diff --git a/docs/api/utils/crypto.md b/docs/api/utils/crypto.md index 023d603664..f25d968574 100644 --- a/docs/api/utils/crypto.md +++ b/docs/api/utils/crypto.md @@ -25,6 +25,8 @@ import * as Crypto from '@aeternity/aepp-sdk/es/utils/crypto' * [.encodeBase58Check(input)](#module_@aeternity/aepp-sdk/es/utils/crypto.encodeBase58Check) ⇒ `Buffer` * [.decodeBase58Check(str)](#module_@aeternity/aepp-sdk/es/utils/crypto.decodeBase58Check) ⇒ `Buffer` * [.hexStringToByte(str)](#module_@aeternity/aepp-sdk/es/utils/crypto.hexStringToByte) ⇒ `Uint8Array` + * [.encodeUnsigned(value)](#module_@aeternity/aepp-sdk/es/utils/crypto.encodeUnsigned) ⇒ `Buffer` + * [.encodeContractAddress(owner, nonce)](#module_@aeternity/aepp-sdk/es/utils/crypto.encodeContractAddress) ⇒ `String` * [.generateKeyPairFromSecret(secret)](#module_@aeternity/aepp-sdk/es/utils/crypto.generateKeyPairFromSecret) ⇒ `Object` * [.generateKeyPair(raw)](#module_@aeternity/aepp-sdk/es/utils/crypto.generateKeyPair) ⇒ `Object` * [.encryptPublicKey(password, binaryKey)](#module_@aeternity/aepp-sdk/es/utils/crypto.encryptPublicKey) ⇒ `Uint8Array` @@ -238,6 +240,34 @@ Conver hex string to Uint8Array | --- | --- | --- | | str | `String` | Data to conver | + + +### @aeternity/aepp-sdk/es/utils/crypto.encodeUnsigned(value) ⇒ `Buffer` +Converts a positive integer to the smallest possible +representation in a binary digit representation + +**Kind**: static method of [`@aeternity/aepp-sdk/es/utils/crypto`](#module_@aeternity/aepp-sdk/es/utils/crypto) +**Returns**: `Buffer` - - Encoded data +**rtype**: `(value: Number) => Buffer` + +| Param | Type | Description | +| --- | --- | --- | +| value | `Number` | Value to encode | + + + +### @aeternity/aepp-sdk/es/utils/crypto.encodeContractAddress(owner, nonce) ⇒ `String` +Compute contract address + +**Kind**: static method of [`@aeternity/aepp-sdk/es/utils/crypto`](#module_@aeternity/aepp-sdk/es/utils/crypto) +**Returns**: `String` - - Contract address +**rtype**: `(owner: String, nonce: Number) => String` + +| Param | Type | Description | +| --- | --- | --- | +| owner | `String` | Address of contract owner | +| nonce | `Number` | Round when contract was created | + ### @aeternity/aepp-sdk/es/utils/crypto.generateKeyPairFromSecret(secret) ⇒ `Object` diff --git a/es/ae/contract.js b/es/ae/contract.js index 5d1e74ad68..4e49443b8f 100644 --- a/es/ae/contract.js +++ b/es/ae/contract.js @@ -63,7 +63,7 @@ async function handleCallError (result) { * @param {Array} args Argument's for call * @return {Promise} */ -async function encodeCall (source, name, args) { +async function contractEncodeCall (source, name, args) { return this.contractEncodeCallDataAPI(source, name, args) } @@ -76,9 +76,8 @@ async function encodeCall (source, name, args) { * @param {String} data call result data (cb_iwer89fjsdf2j93fjews_(ssdffsdfsdf...) * @return {Promise} Result object */ -async function decode (type, data) { - const result = await this.contractDecodeDataAPI(type, data) - return result +async function contractDecodeData (type, data) { + return this.contractDecodeDataAPI(type, data) } /** @@ -95,7 +94,7 @@ async function decode (type, data) { * @param {String} options [options.options] Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object */ -async function callStatic (source, address, name, args = [], { top, options = {} } = {}) { +async function contractCallStatic (source, address, name, args = [], { top, options = {} } = {}) { const opt = R.merge(this.Ae.defaults, options) // Prepare `call` transaction @@ -140,7 +139,7 @@ async function callStatic (source, address, name, args = [], { top, options = {} * @param {Object} options Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object */ -async function call (source, address, name, args = [], options = {}) { +async function contractCall (source, address, name, args = [], options = {}) { const opt = R.merge(this.Ae.defaults, options) const tx = await this.contractCallTx(R.merge(opt, { @@ -175,7 +174,7 @@ async function call (source, address, name, args = [], options = {}) { * @param {Object} options Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object */ -async function deploy (code, source, initState = [], options = {}) { +async function contractDeploy (code, source, initState = [], options = {}) { const opt = R.merge(this.Ae.defaults, options) const callData = await this.contractEncodeCall(source, 'init', initState) const ownerId = await this.address() @@ -210,7 +209,7 @@ async function deploy (code, source, initState = [], options = {}) { * @param {Object} options Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object */ -async function compile (source, options = {}) { +async function contractCompile (source, options = {}) { const bytecode = await this.compileContractAPI(source, options) return Object.freeze(Object.assign({ encodeCall: async (name, args) => this.contractEncodeCall(source, name, args), @@ -231,12 +230,12 @@ async function compile (source, options = {}) { */ const Contract = Ae.compose(ContractACI, ContractCompilerAPI, { methods: { - contractCompile: compile, - contractCallStatic: callStatic, - contractDeploy: deploy, - contractCall: call, - contractEncodeCall: encodeCall, - contractDecodeData: decode, + contractCompile, + contractCallStatic, + contractDeploy, + contractCall, + contractEncodeCall, + contractDecodeData, handleCallError }, deepProps: { From 9c725210705121071713f44e81bf87d95240455d Mon Sep 17 00:00:00 2001 From: naz_dou <41945483+nduchak@users.noreply.github.com> Date: Tue, 9 Apr 2019 17:43:15 +0300 Subject: [PATCH 03/14] feat(Aepp): Add Compiler to Aepp rpc methods. Update example app (#312) * feat(Aepp): Add Compiler to Aepp rpc methods. Update example app * fix(Account): Move address formatter to MemoryAccount due to rpc issue * force Jenkins * fix(Crypto): Fix crypto `formatAddress` * fix(Crypto): Move ADDRESS_FORMAT to crypto * fix(Account): Fix imports --- es/account/index.js | 10 ---------- es/account/memory.js | 4 ++-- es/ae/aepp.js | 2 +- es/ae/contract.js | 7 +++++-- es/contract/index.js | 6 ++++-- es/utils/crypto.js | 7 ++++++- es/utils/http.js | 1 - .../connect-two-ae/aepp/src/components/Home.vue | 16 +++++----------- .../identity/src/components/Home.vue | 6 ++++-- test/integration/index.js | 1 + 10 files changed, 28 insertions(+), 32 deletions(-) diff --git a/es/account/index.js b/es/account/index.js index 802efae919..2f64b9c4a2 100644 --- a/es/account/index.js +++ b/es/account/index.js @@ -30,11 +30,6 @@ import { TX_TYPE } from '../tx/builder/schema' const DEFAULT_NETWORK_ID = `ae_mainnet` -export const ADDRESS_FORMAT = { - sophia: 1, - api: 2, - raw: 3 -} /** * Sign encoded transaction * @instance @@ -74,11 +69,6 @@ const Account = stampit({ if (!this.networkId && networkId) { this.networkId = networkId } - // Add address formatter - this.getAddress = this.address - this.address = async function (format) { - return Crypto.formatAddress(format, await this.getAddress()) - } }, methods: { signTransaction }, deepConf: { diff --git a/es/account/memory.js b/es/account/memory.js index 60a66666ee..5f3abbb23e 100644 --- a/es/account/memory.js +++ b/es/account/memory.js @@ -31,8 +31,8 @@ async function sign (data) { return Promise.resolve(Crypto.sign(data, secrets.get(this).secretKey)) } -async function address () { - return Promise.resolve(secrets.get(this).publicKey) +async function address (format = Crypto.ADDRESS_FORMAT.api) { + return Promise.resolve(Crypto.formatAddress(format, secrets.get(this).publicKey)) } /** diff --git a/es/ae/aepp.js b/es/ae/aepp.js index c911f75e73..268e5c2aee 100644 --- a/es/ae/aepp.js +++ b/es/ae/aepp.js @@ -24,7 +24,7 @@ import Ae from './' import Aens from './aens' -import Contract from './contract' +import { Contract } from './contract' import Rpc from '../rpc/client' /** diff --git a/es/ae/contract.js b/es/ae/contract.js index 4e49443b8f..da08c3c52d 100644 --- a/es/ae/contract.js +++ b/es/ae/contract.js @@ -32,6 +32,7 @@ import * as R from 'ramda' import { isBase64 } from '../utils/crypto' import ContractCompilerAPI from '../contract/compiler' import ContractACI from '../contract/aci' +import ContractBase from '../contract' /** * Handle contract call error @@ -228,7 +229,7 @@ async function contractCompile (source, options = {}) { * @param {Object} [options={}] - Initializer object * @return {Object} Contract instance */ -const Contract = Ae.compose(ContractACI, ContractCompilerAPI, { +export const Contract = Ae.compose(ContractBase, ContractACI, { methods: { contractCompile, contractCallStatic, @@ -252,4 +253,6 @@ const Contract = Ae.compose(ContractACI, ContractCompilerAPI, { } }) -export default Contract +export const ContractWithCompiler = Contract.compose(ContractCompilerAPI) + +export default ContractWithCompiler diff --git a/es/contract/index.js b/es/contract/index.js index f0318476d0..cdfcc7a40f 100644 --- a/es/contract/index.js +++ b/es/contract/index.js @@ -43,7 +43,8 @@ const ContractBase = stampit({ methods: [ 'contractEncodeCallDataAPI', 'contractDecodeDataAPI', - 'compileContractAPI' + 'compileContractAPI', + 'contractGetACI' ] } } @@ -51,7 +52,8 @@ const ContractBase = stampit({ methods: { contractEncodeCallDataAPI: required, contractDecodeDataAPI: required, - compileContractAPI: required + compileContractAPI: required, + contractGetACI: required } })) diff --git a/es/utils/crypto.js b/es/utils/crypto.js index 2e2afad73b..41472579b1 100644 --- a/es/utils/crypto.js +++ b/es/utils/crypto.js @@ -28,7 +28,6 @@ import nacl from 'tweetnacl' import aesjs from 'aes-js' import { leftPad, rightPad, toBytes } from './bytes' import shajs from 'sha.js' -import { ADDRESS_FORMAT } from '../account' import { decode as decodeNode } from '../tx/builder/helpers' const Ecb = aesjs.ModeOfOperation.ecb @@ -46,6 +45,12 @@ export function isBase64 (str) { return !!(index === -1 || str.slice(index).match(/={1,2}/)) } +export const ADDRESS_FORMAT = { + sophia: 1, + api: 2, + raw: 3 +} + /** * Format account address * @rtype (format: String, address: String) => tx: Promise[String] diff --git a/es/utils/http.js b/es/utils/http.js index 5bec3702ac..812bbf6c0c 100644 --- a/es/utils/http.js +++ b/es/utils/http.js @@ -52,7 +52,6 @@ const processResponse = async (res) => { const Http = stampit({ init ({ baseUrl }) { - if (!baseUrl) console.warn('You need to provider base url.') this.baseUrl = baseUrl }, methods: { diff --git a/examples/connect-two-ae/aepp/src/components/Home.vue b/examples/connect-two-ae/aepp/src/components/Home.vue index 88fa5d9a7b..28caf24457 100644 --- a/examples/connect-two-ae/aepp/src/components/Home.vue +++ b/examples/connect-two-ae/aepp/src/components/Home.vue @@ -114,7 +114,7 @@ export default { type state = () function main(x : int) = x`, byteCode: null, - contractInitState: '()', + contractInitState: [], deployInfo: null } }, @@ -134,19 +134,19 @@ export default { async deploy (code, options = {}) { console.log(`Deploying contract...`) try { - return await this.client.contractDeploy(this.byteCode, this.abi, { initState: this.contractInitState, options }) + return await this.client.contractDeploy(this.byteCode, this.contractCode, this.contractInitState, options) } catch (err) { this.deployErr = err console.error(err) } }, - async call (code, abi, contractAddress, method = 'main', returnType = 'int', args = '(5)', options = {}) { + async call (code, abi, contractAddress, method = 'main', returnType = 'int', args = ['5'], options = {}) { console.log(`Deploying contract...`) try { - const { result } = await this.client.contractCall(this.byteCode, this.abi, this.deployInfo.address, method, { args: args, options }) + const result = await this.client.contractCall(this.contractCode, this.deployInfo.address, method, args, options) return Object.assign( result, - { decodedRes: await this.client.contractDecodeData(returnType, result.returnValue) } + { decodedRes: await result.decode(returnType) } ) } catch (err) { this.deployErr = err @@ -181,12 +181,6 @@ export default { this.pub = address }) .catch(e => { this.pub = `Rejected: ${e}` }) - // // AENS - // ae.aensPreclaim(`test${Math.floor(Math.random() * 101)}.test`) - // .then(name => { return name.claim() }) - // .then(name => { return name.update('ak_bAxmQLxXv8UnVEKfHpvDwp8qEsrVE3tnezBffLdtVMVKP6GC2') }) - // .then(name => { return name.revoke() }) - // .catch(e => {debugger}) }) } } diff --git a/examples/connect-two-ae/identity/src/components/Home.vue b/examples/connect-two-ae/identity/src/components/Home.vue index 4554a89320..2196ae4fb3 100644 --- a/examples/connect-two-ae/identity/src/components/Home.vue +++ b/examples/connect-two-ae/identity/src/components/Home.vue @@ -54,8 +54,9 @@ export default { wallet: null, balance: null, height: null, - url: 'https://sdk-mainnet.aepps.com', - internalUrl: 'https://sdk-mainnet.aepps.com', + url: 'http://localhost:3013', + internalUrl: 'http://localhost:3113', + compilerUrl: 'https://compiler.aepps.com', aeppUrl: '//0.0.0.0:9001' } }, @@ -73,6 +74,7 @@ export default { Wallet({ url: this.url, internalUrl: this.internalUrl, + compilerUrl: this.compilerUrl, accounts: [MemoryAccount({keypair: {secretKey: this.priv, publicKey: this.pub}})], address: this.pub, onTx: this.confirmDialog, diff --git a/test/integration/index.js b/test/integration/index.js index 23c36b19c1..1d84426463 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -14,6 +14,7 @@ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ + import Ae from '../../es/ae/universal' import * as Crypto from '../../es/utils/crypto' import { BigNumber } from 'bignumber.js' From 683761fc1f8c1ad74e62425df30756a64b9c55e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Powaga?= Date: Tue, 9 Apr 2019 21:00:49 +0100 Subject: [PATCH 04/14] Add getContractState method (#316) --- es/channel/index.js | 29 ++++++++++++++++++++++++++++- test/integration/channel.js | 17 +++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/es/channel/index.js b/es/channel/index.js index e1706ab970..4e8105489f 100644 --- a/es/channel/index.js +++ b/es/channel/index.js @@ -36,6 +36,13 @@ import { } from './internal' import * as R from 'ramda' +function snakeToPascalObjKeys (obj) { + return Object.entries(obj).reduce((result, [key, val]) => ({ + ...result, + [snakeToPascal(key)]: val + }), {}) +} + /** * Register event listener function * @@ -418,6 +425,25 @@ async function getContractCall ({ caller, contract, round }) { ) } +/** + * Get contract latest state + * + * @param {string} contract - Address of the contract + * @return {Promise} + * @example channel.getContractState( + * 'ct_9sRA9AVE4BYTAkh5RNfJYmwQe1NZ4MErasQLXZkFWG43TPBqa', + * ).then(({ contract }) => { + * console.log('deposit:', contract.deposit) + * }) + */ +async function getContractState (contract) { + const result = await call(this, 'channels.get.contract', { pubkey: contract }) + return snakeToPascalObjKeys({ + ...result, + contract: snakeToPascalObjKeys(result.contract) + }) +} + /** * Send generic message * @@ -505,7 +531,8 @@ const Channel = AsyncInit.compose({ deposit, createContract, callContract, - getContractCall + getContractCall, + getContractState } }) diff --git a/test/integration/channel.js b/test/integration/channel.js index 532ca6e70b..0adaf0933b 100644 --- a/test/integration/channel.js +++ b/test/integration/channel.js @@ -389,6 +389,23 @@ describe('Channel', function () { value.should.eql({ type: 'word', value: 42 }) }) + it('can get contract state', async () => { + const result = await initiatorCh.getContractState(contractAddress) + result.should.eql({ + contract: { + abiVersion: 1, + active: true, + deposit: 1000, + id: contractAddress, + ownerId: await initiator.address(), + referrerIds: [], + vmVersion: 3, + }, + contractState: result.contractState + }) + // TODO: contractState deserialization + }) + describe('throws errors', function () { async function update ({ from, to, amount, sign }) { return initiatorCh.update( From 7bbcbfbd9702b4d10df3f2f0c13d2f686f0cc0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Powaga?= Date: Tue, 9 Apr 2019 21:16:30 +0100 Subject: [PATCH 05/14] Add contractCallStatic method (#315) --- es/channel/index.js | 29 +++++++++++++++++++++++++++++ test/integration/channel.js | 22 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/es/channel/index.js b/es/channel/index.js index 4e8105489f..610aa3ca36 100644 --- a/es/channel/index.js +++ b/es/channel/index.js @@ -399,6 +399,34 @@ function callContract ({ amount, callData, contract, abiVersion }, sign) { }) } +/** + * Call contract using dry-run + * + * @param {object} options + * @param {string} [options.amount] - Amount the caller of the contract commits to it + * @param {string} [options.callData] - ABI encoded compiled AEVM call data for the code + * @param {number} [options.contract] - Address of the contract to call + * @param {number} [options.abiVersion] - Version of the ABI + * @return {Promise} + * @example channel.callContractStatic({ + * contract: 'ct_9sRA9AVE4BYTAkh5RNfJYmwQe1NZ4MErasQLXZkFWG43TPBqa', + * callData: 'cb_1111111111111111...', + * amount: 0, + * abiVersion: 1 + * }).then(({ returnValue, gasUsed }) => { + * console.log('Returned value:', returnValue) + * console.log('Gas used:', gasUsed) + * }) + */ +async function callContractStatic ({ amount, callData, contract, abiVersion }) { + return snakeToPascalObjKeys(await call(this, 'channels.dry_run.call_contract', { + amount, + call_data: callData, + contract, + abi_version: abiVersion + })) +} + /** * Get contract call result * @@ -531,6 +559,7 @@ const Channel = AsyncInit.compose({ deposit, createContract, callContract, + callContractStatic, getContractCall, getContractState } diff --git a/test/integration/channel.js b/test/integration/channel.js index 0adaf0933b..a1b6e374d5 100644 --- a/test/integration/channel.js +++ b/test/integration/channel.js @@ -368,6 +368,28 @@ describe('Channel', function () { result.should.eql({ accepted: false }) }) + it('can call a contract using dry-run', async () => { + const result = await initiatorCh.callContractStatic({ + amount: 0, + callData: await contractEncodeCall('main', ['42']), + contract: contractAddress, + abiVersion: 1 + }) + result.should.eql({ + callerId: await initiator.address(), + callerNonce: result.callerNonce, + contractId: contractAddress, + gasPrice: result.gasPrice, + gasUsed: result.gasUsed, + height: result.height, + log: result.log, + returnType: 'ok', + returnValue: result.returnValue + }) + const value = await initiator.contractDecodeDataAPI('int', result.returnValue) + value.should.eql({ type: 'word', value: 42 }) + }) + it('can get contract call', async () => { const result = await initiatorCh.getContractCall({ caller: await initiator.address(), From fcab48a19d24b8f1d4ed57b49324c1edb4ed7ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Powaga?= Date: Tue, 9 Apr 2019 23:04:39 +0100 Subject: [PATCH 06/14] Change state method to return offchain state from node (#307) --- es/channel/handlers.js | 12 ++++++------ es/channel/index.js | 17 +++++++---------- test/integration/channel.js | 16 ++++++++-------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/es/channel/handlers.js b/es/channel/handlers.js index 77cc430560..498c1b030b 100644 --- a/es/channel/handlers.js +++ b/es/channel/handlers.js @@ -162,7 +162,7 @@ export async function awaitingOffChainTx (channel, message, state) { export function awaitingOffChainUpdate (channel, message, state) { if (message.method === 'channels.update') { changeState(channel, message.params.data.state) - state.resolve({ accepted: true, state: message.params.data.state }) + state.resolve({ accepted: true, signedTx: message.params.data.state }) return { handler: channelOpen } } if (message.method === 'channels.conflict') { @@ -224,7 +224,7 @@ export function awaitingShutdownOnChainTx (channel, message, state) { export function awaitingLeave (channel, message, state) { if (message.method === 'channels.leave') { - state.resolve({ channelId: message.params.channel_id, state: message.params.data.state }) + state.resolve({ channelId: message.params.channel_id, signedTx: message.params.data.state }) return { handler: channelClosed } } if (message.method === 'channels.error') { @@ -262,7 +262,7 @@ export function awaitingWithdrawCompletion (channel, message, state) { } if (message.method === 'channels.update') { changeState(channel, message.params.data.state) - state.resolve({ accepted: true, state: message.params.data.state }) + state.resolve({ accepted: true, signedTx: message.params.data.state }) return { handler: channelOpen } } if (message.method === 'channels.conflict') { @@ -300,7 +300,7 @@ export function awaitingDepositCompletion (channel, message, state) { } if (message.method === 'channels.update') { changeState(channel, message.params.data.state) - state.resolve({ accepted: true, state: message.params.data.state }) + state.resolve({ accepted: true, signedTx: message.params.data.state }) return { handler: channelOpen } } if (message.method === 'channels.conflict') { @@ -329,7 +329,7 @@ export function awaitingNewContractCompletion (channel, message, state) { state.resolve({ accepted: true, address: encodeContractAddress(owner, round), - state: message.params.data.state + signedTx: message.params.data.state }) return { handler: channelOpen } } @@ -350,7 +350,7 @@ export async function awaitingCallContractUpdateTx (channel, message, state) { export function awaitingCallContractCompletion (channel, message, state) { if (message.method === 'channels.update') { changeState(channel, message.params.data.state) - state.resolve({ accepted: true, state: message.params.data.state }) + state.resolve({ accepted: true, signedTx: message.params.data.state }) return { handler: channelOpen } } if (message.method === 'channels.conflict') { diff --git a/es/channel/index.js b/es/channel/index.js index 610aa3ca36..e3229ee6fa 100644 --- a/es/channel/index.js +++ b/es/channel/index.js @@ -28,7 +28,6 @@ import * as handlers from './handlers' import { eventEmitters, status as channelStatus, - state as channelState, initialize, enqueueAction, send, @@ -67,8 +66,8 @@ function status () { * * @return {object} */ -function state () { - return channelState.get(this) +async function state () { + return snakeToPascalObjKeys(await call(this, 'channels.get.offchain_state', {})) } /** @@ -84,7 +83,7 @@ function state () { * 'ak$Gi42jcRm9DcZjk72UWQQBSxi43BG3285C9n4QSvP5JdzDyH2o', * 10, * async (tx) => await account.signTransaction(tx) - * ).then({ accepted, state } => + * ).then({ accepted, signedTx } => * if (accepted) { * console.log('Update has been accepted') * } @@ -157,7 +156,7 @@ async function balances (accounts) { * Leave channel * * @return {Promise} - * @example channel.leave().then(({channelId, state}) => + * @example channel.leave().then(({ channelId, signedTx }) => * console.log(channelId) * console.log(state) * ) @@ -219,10 +218,9 @@ function shutdown (sign) { * 100, * async (tx) => await account.signTransaction(tx), * { onOnChainTx: (tx) => console.log('on_chain_tx', tx) } - * ).then(({ accepted, state }) => { + * ).then(({ accepted, signedTx }) => { * if (accepted) { * console.log('Withdrawal has been accepted') - * console.log('The new state is:', state) * } else { * console.log('Withdrawal has been rejected') * } @@ -312,7 +310,7 @@ function deposit (amount, sign, { onOnChainTx, onOwnDepositLocked, onDepositLock * deposit: 10, * vmVersion: 3, * abiVersion: 1 - * }).then(({ accepted, state, address }) => { + * }).then(({ accepted, signedTx, address }) => { * if (accepted) { * console.log('New contract has been created') * console.log('Contract address:', address) @@ -365,10 +363,9 @@ function createContract ({ code, callData, deposit, vmVersion, abiVersion }, sig * callData: 'cb_1111111111111111...', * amount: 0, * abiVersion: 1 - * }).then(({ accepted, state }) => { + * }).then(({ accepted, signedTx }) => { * if (accepted) { * console.log('Contract called succesfully') - * console.log('The new state is:', state) * } else { * console.log('Contract call has been rejected') * } diff --git a/test/integration/channel.js b/test/integration/channel.js index a1b6e374d5..62d25e4814 100644 --- a/test/integration/channel.js +++ b/test/integration/channel.js @@ -119,7 +119,7 @@ describe('Channel', function () { async (tx) => await initiator.signTransaction(tx) ) result.accepted.should.equal(true) - result.state.should.be.a('string') + result.signedTx.should.be.a('string') sinon.assert.notCalled(initiatorSign) sinon.assert.calledOnce(responderSign) sinon.assert.calledWithExactly(responderSign, sinon.match('update_ack'), sinon.match.string) @@ -188,7 +188,7 @@ describe('Channel', function () { async (tx) => initiator.signTransaction(tx), { onOnChainTx, onOwnWithdrawLocked, onWithdrawLocked } ) - result.should.eql({ accepted: true, state: initiatorCh.state() }) + result.should.eql({ accepted: true, signedTx: (await initiatorCh.state()).signedTx }) sinon.assert.calledOnce(onOnChainTx) sinon.assert.calledWithExactly(onOnChainTx, sinon.match.string) sinon.assert.calledOnce(onOwnWithdrawLocked) @@ -227,7 +227,7 @@ describe('Channel', function () { async (tx) => initiator.signTransaction(tx), { onOnChainTx, onOwnDepositLocked, onDepositLocked } ) - result.should.eql({ accepted: true, state: initiatorCh.state() }) + result.should.eql({ accepted: true, signedTx: (await initiatorCh.state()).signedTx }) sinon.assert.calledOnce(onOnChainTx) sinon.assert.calledWithExactly(onOnChainTx, sinon.match.string) sinon.assert.calledOnce(onOwnDepositLocked) @@ -278,9 +278,9 @@ describe('Channel', function () { await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)]) const result = await initiatorCh.leave() result.channelId.should.be.a('string') - result.state.should.be.a('string') + result.signedTx.should.be.a('string') existingChannelId = result.channelId - offchainTx = result.state + offchainTx = result.signedTx }) it('can reestablish a channel', async () => { @@ -327,7 +327,7 @@ describe('Channel', function () { vmVersion: 3, abiVersion: 1 }, async (tx) => await initiator.signTransaction(tx)) - result.should.eql({ accepted: true, address: result.address, state: initiatorCh.state() }) + result.should.eql({ accepted: true, address: result.address, signedTx: (await initiatorCh.state()).signedTx }) contractAddress = result.address contractEncodeCall = (method, args) => initiator.contractEncodeCallDataAPI(identityContract, method, args) }) @@ -353,8 +353,8 @@ describe('Channel', function () { contract: contractAddress, abiVersion: 1 }, async (tx) => await initiator.signTransaction(tx)) - result.should.eql({ accepted: true, state: initiatorCh.state() }) - callerNonce = Number(unpackTx(initiatorCh.state()).tx.encodedTx.tx.round) + result.should.eql({ accepted: true, signedTx: (await initiatorCh.state()).signedTx }) + callerNonce = Number(unpackTx((await initiatorCh.state()).signedTx).tx.encodedTx.tx.round) }) it('can call a contract and reject', async () => { From 5da69c28fe8b009c204f4a7eca52ccfce631aa11 Mon Sep 17 00:00:00 2001 From: naz_dou <41945483+nduchak@users.noreply.github.com> Date: Wed, 10 Apr 2019 16:00:49 +0300 Subject: [PATCH 07/14] Adjust docs (#321) * feat(ACI): Add transform decoded data for 'address' type * docs(Contract and ACI examples): * docs(Usage): Adjust Usage --- docs/api/ae/contract.md | 68 ++++++++++++++++++++++++++++++++++++++-- docs/api/contract/aci.md | 8 +++++ docs/usage.md | 5 +-- es/account/index.js | 2 +- es/ae/contract.js | 51 ++++++++++++++++++++++++++++++ es/contract/aci.js | 15 ++++++--- 6 files changed, 139 insertions(+), 10 deletions(-) diff --git a/docs/api/ae/contract.md b/docs/api/ae/contract.md index 6446d218b4..262e8dff49 100644 --- a/docs/api/ae/contract.md +++ b/docs/api/ae/contract.md @@ -17,7 +17,7 @@ import { Contract } from '@aeternity/aepp-sdk' (Using bundle) ``` * [@aeternity/aepp-sdk/es/ae/contract](#module_@aeternity/aepp-sdk/es/ae/contract) - * [Contract([options])](#exp_module_@aeternity/aepp-sdk/es/ae/contract--Contract) ⇒ `Object` ⏏ + * [exports.Contract([options])](#exp_module_@aeternity/aepp-sdk/es/ae/contract--exports.Contract) ⇒ `Object` ⏏ * _async_ * [handleCallError(result)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--handleCallError) ⇒ `Promise.<void>` ⏏ * [contractEncodeCall(source, name, args)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractEncodeCall) ⇒ `Promise.<String>` ⏏ @@ -27,9 +27,9 @@ import { Contract } from '@aeternity/aepp-sdk' (Using bundle) * [contractDeploy(code, source, initState, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractDeploy) ⇒ `Promise.<Object>` ⏏ * [contractCompile(source, options)](#exp_module_@aeternity/aepp-sdk/es/ae/contract--contractCompile) ⇒ `Promise.<Object>` ⏏ - + -### Contract([options]) ⇒ `Object` ⏏ +### exports.Contract([options]) ⇒ `Object` ⏏ Contract Stamp Provide contract implementation @@ -43,6 +43,25 @@ Provide contract implementation | --- | --- | --- | --- | | [options] | `Object` | {} | Initializer object | +**Example** +```js +import Transaction from '@aeternity/aepp-sdk/es/tx/tx +import MemoryAccount from '@aeternity/aepp-sdk/es/account/memory +import ChainNode from '@aeternity/aepp-sdk/es/chain/node +import ContractCompilerAPI from '@aeternity/aepp-sdk/es/contract/compiler +// or using bundle +import { + Transaction, + MemoryAccount, + ChainNode, + ContractCompilerAPI +} from '@aeternity/aepp-sdk + +const ContractWithAE = await Contract + .compose(Transaction, MemoryAccount, ChainNode) // AE implementation + .compose(ContractCompilerAPI) // ContractBase implementation +const client = await ContractWithAe({ url, internalUrl, compilerUrl, keypair, ... }) +``` ### handleCallError(result) ⇒ `Promise.<void>` ⏏ @@ -87,6 +106,10 @@ Decode contract call result data | type | `String` | Data type (int, string, list,...) | | data | `String` | call result data (cb_iwer89fjsdf2j93fjews_(ssdffsdfsdf...) | +**Example** +```js +const decodedData = await client.contractDecodeData('string' ,'cb_sf;ls43fsdfsdf...') +``` ### contractCallStatic(source, address, name, args, options, top, options) ⇒ `Promise.<Object>` ⏏ @@ -106,6 +129,14 @@ Static contract call(using dry-run) | top | `String` | [options.top] Block hash on which you want to call contract | | options | `String` | [options.options] Transaction options (fee, ttl, gas, amount, deposit) | +**Example** +```js +const callResult = await client.contractCallStatic(source, address, fnName, args = [], { top, options = {} }) +{ + result: TX_DATA, + decode: (type) => Decode call result +} +``` ### contractCall(source, address, name, args, options) ⇒ `Promise.<Object>` ⏏ @@ -123,6 +154,15 @@ Call contract function | args | `Array` | Argument's for call function | | options | `Object` | Transaction options (fee, ttl, gas, amount, deposit) | +**Example** +```js +const callResult = await client.contractCall(source, address, fnName, args = [], options) +{ + hash: TX_HASH, + result: TX_DATA, + decode: (type) => Decode call result +} +``` ### contractDeploy(code, source, initState, options) ⇒ `Promise.<Object>` ⏏ @@ -139,6 +179,19 @@ Deploy contract to the node | initState | `Array` | Arguments of contract constructor(init) function | | options | `Object` | Transaction options (fee, ttl, gas, amount, deposit) | +**Example** +```js +const deployed = await client.contractDeploy(bytecode, source, init = [], options) +{ + owner: OWNER_PUB_KEY, + transaction: TX_HASH, + address: CONTRACT_ADDRESS, + createdAt: Date, + result: DEPLOY_TX_DATA, + call: (fnName, args = [], options) => Call contract function, + callStatic: (fnName, args = [], options) => Static all contract function +} +``` ### contractCompile(source, options) ⇒ `Promise.<Object>` ⏏ @@ -153,3 +206,12 @@ Compile contract source code | source | `String` | Contract sourece code | | options | `Object` | Transaction options (fee, ttl, gas, amount, deposit) | +**Example** +```js +const compiled = await client.contractCompile(SOURCE_CODE) +{ + bytecode: CONTRACT_BYTE_CODE, + deploy: (init = [], options = {}) => Deploy Contract, + encodeCall: (fnName, args = []) => Prepare callData +} +``` diff --git a/docs/api/contract/aci.md b/docs/api/contract/aci.md index b7815df3ea..eafb666711 100644 --- a/docs/api/contract/aci.md +++ b/docs/api/contract/aci.md @@ -71,6 +71,14 @@ Generate contract ACI object with predefined js methods for contract usage | [options] | `Object` | Options object | | [options.aci] | `Object` | Contract ACI | +**Example** +```js +const contractIns = await client.getContractInstance(sourceCode) +await contractIns.compile() +await contractIns.deploy([321]) +const callResult = await contractIns.call('setState', [123]) +const staticCallResult = await contractIns.call('setState', [123], { callStatic: true }) +``` ### ContractACI() ⇒ `Object` ⏏ diff --git a/docs/usage.md b/docs/usage.md index 095d124997..f16a196ac2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -144,7 +144,7 @@ code, such as the ones provided in the `bin/` directory of the project. ```js const {Universal: Ae} = require('@aeternity/aepp-sdk') -Ae({ url: 'https://sdk-testnet.aepps.com', internalUrl: 'https://sdk-testnet.aepps.com' }).then(ae => { +Ae({ url: 'https://sdk-testnet.aepps.com', internalUrl: 'https://sdk-testnet.aepps.com', compilerUrl: 'COMPILER_URL', keypair: 'YOUR_KEYPAIR_OBJECT' }).then(ae => { ae.height().then(height => { console.log('Current Block', height) }) @@ -152,7 +152,7 @@ Ae({ url: 'https://sdk-testnet.aepps.com', internalUrl: 'https://sdk-testnet.aep // same with async const main = async () => { - const client = await Ae({url: 'https://sdk-testnet.aepps.com', internalUrl: 'https://sdk-testnet.aepps.com'}) + const client = await Ae({url: 'https://sdk-testnet.aepps.com', internalUrl: 'https://sdk-testnet.aepps.com', compilerUrl: 'COMPILER_URL', keypair: 'YOUR_KEYPAIR_OBJECT'}) const height = await client.height() console.log('Current Block', height) } @@ -228,6 +228,7 @@ Example spend function, using aeternity's SDK abstraction Wallet({ url: 'HOST_URL_HERE', internalUrl: 'HOST_URL_HERE', + compilerUrl: 'COMPILER_URL_HERE', accounts: [MemoryAccount({keypair: {secretKey: 'PRIV_KEY_HERE', publicKey: 'PUB_KEY_HERE'}, networkId: 'NETWORK_ID_HERE'})], address: 'PUB_KEY_HERE', onTx: confirm, // guard returning boolean diff --git a/es/account/index.js b/es/account/index.js index 2f64b9c4a2..5e2fed8dc7 100644 --- a/es/account/index.js +++ b/es/account/index.js @@ -73,7 +73,7 @@ const Account = stampit({ methods: { signTransaction }, deepConf: { Ae: { - methods: ['sign', 'address', 'signTransaction', 'getNetworkId'] + methods: ['sign', 'address', 'signTransaction'] } } }, required({ methods: { diff --git a/es/ae/contract.js b/es/ae/contract.js index da08c3c52d..cd2378f05b 100644 --- a/es/ae/contract.js +++ b/es/ae/contract.js @@ -76,6 +76,8 @@ async function contractEncodeCall (source, name, args) { * @param {String} type Data type (int, string, list,...) * @param {String} data call result data (cb_iwer89fjsdf2j93fjews_(ssdffsdfsdf...) * @return {Promise} Result object + * @example + * const decodedData = await client.contractDecodeData('string' ,'cb_sf;ls43fsdfsdf...') */ async function contractDecodeData (type, data) { return this.contractDecodeDataAPI(type, data) @@ -94,6 +96,12 @@ async function contractDecodeData (type, data) { * @param {String} top [options.top] Block hash on which you want to call contract * @param {String} options [options.options] Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object + * @example + * const callResult = await client.contractCallStatic(source, address, fnName, args = [], { top, options = {} }) + * { + * result: TX_DATA, + * decode: (type) => Decode call result + * } */ async function contractCallStatic (source, address, name, args = [], { top, options = {} } = {}) { const opt = R.merge(this.Ae.defaults, options) @@ -139,6 +147,13 @@ async function contractCallStatic (source, address, name, args = [], { top, opti * @param {Array} args Argument's for call function * @param {Object} options Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object + * @example + * const callResult = await client.contractCall(source, address, fnName, args = [], options) + * { + * hash: TX_HASH, + * result: TX_DATA, + * decode: (type) => Decode call result + * } */ async function contractCall (source, address, name, args = [], options = {}) { const opt = R.merge(this.Ae.defaults, options) @@ -174,6 +189,17 @@ async function contractCall (source, address, name, args = [], options = {}) { * @param {Array} initState Arguments of contract constructor(init) function * @param {Object} options Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object + * @example + * const deployed = await client.contractDeploy(bytecode, source, init = [], options) + * { + * owner: OWNER_PUB_KEY, + * transaction: TX_HASH, + * address: CONTRACT_ADDRESS, + * createdAt: Date, + * result: DEPLOY_TX_DATA, + * call: (fnName, args = [], options) => Call contract function, + * callStatic: (fnName, args = [], options) => Static all contract function + * } */ async function contractDeploy (code, source, initState = [], options = {}) { const opt = R.merge(this.Ae.defaults, options) @@ -209,6 +235,13 @@ async function contractDeploy (code, source, initState = [], options = {}) { * @param {String} source Contract sourece code * @param {Object} options Transaction options (fee, ttl, gas, amount, deposit) * @return {Promise} Result object + * @example + * const compiled = await client.contractCompile(SOURCE_CODE) + * { + * bytecode: CONTRACT_BYTE_CODE, + * deploy: (init = [], options = {}) => Deploy Contract, + * encodeCall: (fnName, args = []) => Prepare callData + * } */ async function contractCompile (source, options = {}) { const bytecode = await this.compileContractAPI(source, options) @@ -228,6 +261,24 @@ async function contractCompile (source, options = {}) { * @rtype Stamp * @param {Object} [options={}] - Initializer object * @return {Object} Contract instance + * @example + * import Transaction from '@aeternity/aepp-sdk/es/tx/tx + * import MemoryAccount from '@aeternity/aepp-sdk/es/account/memory + * import ChainNode from '@aeternity/aepp-sdk/es/chain/node + * import ContractCompilerAPI from '@aeternity/aepp-sdk/es/contract/compiler + * // or using bundle + * import { + * Transaction, + * MemoryAccount, + * ChainNode, + * ContractCompilerAPI + * } from '@aeternity/aepp-sdk + * + * const ContractWithAE = await Contract + * .compose(Transaction, MemoryAccount, ChainNode) // AE implementation + * .compose(ContractCompilerAPI) // ContractBase implementation + * const client = await ContractWithAe({ url, internalUrl, compilerUrl, keypair, ... }) + * */ export const Contract = Ae.compose(ContractBase, ContractACI, { methods: { diff --git a/es/contract/aci.js b/es/contract/aci.js index 60d6c58c18..482c25f968 100644 --- a/es/contract/aci.js +++ b/es/contract/aci.js @@ -23,7 +23,7 @@ * @example import ContractACI from '@aeternity/aepp-sdk/es/contract/aci' */ import AsyncInit from '../utils/async-init' -import { decode } from '../tx/builder/helpers' +import { decode, encode } from '../tx/builder/helpers' const SOPHIA_TYPES = [ 'int', @@ -115,6 +115,8 @@ function transformDecodedData (aci, result, { skipTransformDecoded = false } = { switch (t) { case SOPHIA_TYPES.bool: return !!result.value + case SOPHIA_TYPES.address: + return encode(Buffer.from(result.value, 'hex'), 'ak') case SOPHIA_TYPES.map: const [keyT, ...valueT] = generic.split(',') return result.value @@ -178,7 +180,12 @@ function getFunctionACI (aci, name) { * @param {Object} [options] Options object * @param {Object} [options.aci] Contract ACI * @return {ContractInstance} JS Contract API - * + * @example + * const contractIns = await client.getContractInstance(sourceCode) + * await contractIns.compile() + * await contractIns.deploy([321]) + * const callResult = await contractIns.call('setState', [123]) + * const staticCallResult = await contractIns.call('setState', [123], { callStatic: true }) */ async function getContractInstance (source, { aci, contractAddress } = {}) { aci = aci || await this.contractGetACI(source) @@ -231,11 +238,11 @@ function call (self) { params = !options.skipArgsConvert ? prepareArgsForEncode(fnACI, params) : params const result = options.callStatic - ? await self.contractCallStatic(this.interface, this.deployInfo.address, fn, params, { + ? await self.contractCallStatic(this.source, this.deployInfo.address, fn, params, { top: options.top, options }) - : await self.contractCall(this.interface, this.deployInfo.address, fn, params, options) + : await self.contractCall(this.source, this.deployInfo.address, fn, params, options) return { ...result, From 1642c6c73e748fe5889e005046af7e94f45d6aab Mon Sep 17 00:00:00 2001 From: nduchak Date: Wed, 10 Apr 2019 18:17:01 +0300 Subject: [PATCH 08/14] chore(package): Bump version to 2.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d1866c089a..5e4356d9ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aeternity/aepp-sdk", - "version": "2.4.0", + "version": "2.4.1", "description": "SDK for the æternity blockchain", "main": "dist/aepp-sdk.js", "browser": "dist/aepp-sdk.browser.js", From d0b2dce179a1dd76eaa33b934f1f468a5d8ab89f Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 11 Apr 2019 13:45:24 +0300 Subject: [PATCH 09/14] chore(Adjust CHANGELOG): --- CHANGELOG.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2b759024e..667b2c8470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). +## [2.4.1] +### Added +- feat(ACI): Add transform decoded data for 'address' type +- feat(Aepp): Add Compiler to Aepp rpc methods. Update example app +- Add getContractState method +- feat(Channel): Add call contract static support +- feat(Channel): Add get contract state support +- feat(Channel): Get full channel state support +- docs(*): Adjust ACI, Contract nad Usage +### Changed +- refactor(Http): Handle no response in http stamp error handler +- fix(Crypto): Fix crypto `formatAddress` +- fix(Crypto): Move ADDRESS_FORMAT to crypto + + ### Removed +- none + + ### Breaking Changes +- State Channel: + - `channel.state()` now returns offchain state instead of last co-signed offchain transaction + - `channel.update(...).state` has been renamed to `signedTx` + - `channel.withdraw(...).state` has been renamed to `signedTx` + - `channel.deposit(...).state` has been renamed to `signedTx` + - `channel.leave().state` has been renamed to `signedTx` + - `channel.createContract(...).state` has been renamed to `signedTx` + - `channel.callContract(...).state` has been renamed to `signedTx` + + ### Notes and known Issues +- none + + ## [2.4.0] ### Added - Install and configure `commitizen` @@ -628,4 +659,5 @@ await account.address(format: ADDRESS_FORMAT) // default ADDRESS_FORMAT.api [2.3.0]: https://github.com/aeternity/aepp-sdk-js/compare/2.3.0-next...2.3.0 [2.3.1]: https://github.com/aeternity/aepp-sdk-js/compare/2.3.0...2.3.1 [2.3.2]: https://github.com/aeternity/aepp-sdk-js/compare/2.3.1...2.3.2 -[2.4.0.]: https://github.com/aeternity/aepp-sdk-js/compare/2.3.2...2.4.0 +[2.4.0]: https://github.com/aeternity/aepp-sdk-js/compare/2.3.2...2.4.0 +[2.4.1]: https://github.com/aeternity/aepp-sdk-js/compare/2.4.0...2.4.1 From fa4fdd3e89ae9de834f9f38691dab9dade9ca42f Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 11 Apr 2019 13:48:02 +0300 Subject: [PATCH 10/14] chore(CAHNGELOG): Fix typo --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 667b2c8470..a6c5a9faac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). ### Added - feat(ACI): Add transform decoded data for 'address' type - feat(Aepp): Add Compiler to Aepp rpc methods. Update example app -- Add getContractState method - feat(Channel): Add call contract static support - feat(Channel): Add get contract state support - feat(Channel): Get full channel state support From 5914b229b04b8c0a757227006bc2e95b2c238022 Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 11 Apr 2019 14:14:38 +0300 Subject: [PATCH 11/14] docs(USAGE): Fix networkId in Wallet example --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index f16a196ac2..cdd0561741 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -235,7 +235,7 @@ Example spend function, using aeternity's SDK abstraction onChain: confirm, // guard returning boolean onAccount: confirm, // guard returning boolean onContract: confirm, // guard returning boolean - networkId: 'aet_ua' // or any other networkId your client should connect to + networkId: 'ae_uat' // or any other networkId your client should connect to }).then(ae => ae.spend(parseInt(amount), receiver_pub_key)) ``` From 31d4955a091560acf00d9fa4bfba9dd64c402a83 Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 11 Apr 2019 14:30:48 +0300 Subject: [PATCH 12/14] fix(Http): Remove userAgent from axios --- es/utils/http.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/es/utils/http.js b/es/utils/http.js index 812bbf6c0c..b3e75dbab7 100644 --- a/es/utils/http.js +++ b/es/utils/http.js @@ -1,15 +1,8 @@ -import ax from 'axios' -import https from 'https' +import axios from 'axios' import JSONbig from 'json-bigint' import * as R from 'ramda' import stampit from '@stamp/it' -const axios = ax.create({ - httpsAgent: new https.Agent({ - rejectUnauthorized: true // For develop - }) -}) - async function get (url, options) { return processResponse( axios.get(`${this.baseUrl}${url}`, R.merge(this.httpConfig, options)) From 14f03642819f5af981b30f85aff58cdda5ec6860 Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 11 Apr 2019 14:33:41 +0300 Subject: [PATCH 13/14] chore(CHANGELOG): Fix typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c5a9faac..daa98f540c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). - feat(Channel): Add call contract static support - feat(Channel): Add get contract state support - feat(Channel): Get full channel state support -- docs(*): Adjust ACI, Contract nad Usage +- docs(*): Adjust ACI, Contract and Usage ### Changed - refactor(Http): Handle no response in http stamp error handler - fix(Crypto): Fix crypto `formatAddress` From cb69a29b798a993d985703d192eea7edc646db14 Mon Sep 17 00:00:00 2001 From: nduchak Date: Thu, 11 Apr 2019 14:45:21 +0300 Subject: [PATCH 14/14] docs(*): Regenrate docs --- docs/api/channel/index.md | 60 ++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/docs/api/channel/index.md b/docs/api/channel/index.md index 246d6f384a..7219ee5d6f 100644 --- a/docs/api/channel/index.md +++ b/docs/api/channel/index.md @@ -23,7 +23,9 @@ import Channel from '@aeternity/aepp-sdk/es/channel/index' * [~deposit(amount, sign, [callbacks])](#module_@aeternity/aepp-sdk/es/channel/index--Channel..deposit) ⇒ `Promise.<object>` * [~createContract(options, sign)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..createContract) ⇒ `Promise.<object>` * [~callContract(options, sign)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..callContract) ⇒ `Promise.<object>` + * [~callContractStatic(options)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..callContractStatic) ⇒ `Promise.<object>` * [~getContractCall(options)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..getContractCall) ⇒ `Promise.<object>` + * [~getContractState(contract)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..getContractState) ⇒ `Promise.<object>` * [~sendMessage(message, recipient)](#module_@aeternity/aepp-sdk/es/channel/index--Channel..sendMessage) @@ -117,7 +119,7 @@ channel.update( 'ak$Gi42jcRm9DcZjk72UWQQBSxi43BG3285C9n4QSvP5JdzDyH2o', 10, async (tx) => await account.signTransaction(tx) -).then({ accepted, state } => +).then({ accepted, signedTx } => if (accepted) { console.log('Update has been accepted') } @@ -175,7 +177,7 @@ Leave channel **Kind**: inner method of [`Channel`](#exp_module_@aeternity/aepp-sdk/es/channel/index--Channel) **Example** ```js -channel.leave().then(({channelId, state}) => +channel.leave().then(({ channelId, signedTx }) => console.log(channelId) console.log(state) ) @@ -219,10 +221,9 @@ channel.withdraw( 100, async (tx) => await account.signTransaction(tx), { onOnChainTx: (tx) => console.log('on_chain_tx', tx) } -).then(({ accepted, state }) => { +).then(({ accepted, signedTx }) => { if (accepted) { console.log('Withdrawal has been accepted') - console.log('The new state is:', state) } else { console.log('Withdrawal has been rejected') } @@ -284,7 +285,7 @@ channel.createContract({ deposit: 10, vmVersion: 3, abiVersion: 1 -}).then(({ accepted, state, address }) => { +}).then(({ accepted, signedTx, address }) => { if (accepted) { console.log('New contract has been created') console.log('Contract address:', address) @@ -316,15 +317,41 @@ channel.callContract({ callData: 'cb_1111111111111111...', amount: 0, abiVersion: 1 -}).then(({ accepted, state }) => { +}).then(({ accepted, signedTx }) => { if (accepted) { console.log('Contract called succesfully') - console.log('The new state is:', state) } else { console.log('Contract call has been rejected') } }) ``` + + +#### Channel~callContractStatic(options) ⇒ `Promise.<object>` +Call contract using dry-run + +**Kind**: inner method of [`Channel`](#exp_module_@aeternity/aepp-sdk/es/channel/index--Channel) + +| Param | Type | Description | +| --- | --- | --- | +| options | `object` | | +| [options.amount] | `string` | Amount the caller of the contract commits to it | +| [options.callData] | `string` | ABI encoded compiled AEVM call data for the code | +| [options.contract] | `number` | Address of the contract to call | +| [options.abiVersion] | `number` | Version of the ABI | + +**Example** +```js +channel.callContractStatic({ + contract: 'ct_9sRA9AVE4BYTAkh5RNfJYmwQe1NZ4MErasQLXZkFWG43TPBqa', + callData: 'cb_1111111111111111...', + amount: 0, + abiVersion: 1 +}).then(({ returnValue, gasUsed }) => { + console.log('Returned value:', returnValue) + console.log('Gas used:', gasUsed) +}) +``` #### Channel~getContractCall(options) ⇒ `Promise.<object>` @@ -349,6 +376,25 @@ channel.getContractCall({ if (returnType === 'ok') console.log(returnValue) }) ``` + + +#### Channel~getContractState(contract) ⇒ `Promise.<object>` +Get contract latest state + +**Kind**: inner method of [`Channel`](#exp_module_@aeternity/aepp-sdk/es/channel/index--Channel) + +| Param | Type | Description | +| --- | --- | --- | +| contract | `string` | Address of the contract | + +**Example** +```js +channel.getContractState( + 'ct_9sRA9AVE4BYTAkh5RNfJYmwQe1NZ4MErasQLXZkFWG43TPBqa', +).then(({ contract }) => { + console.log('deposit:', contract.deposit) +}) +``` #### Channel~sendMessage(message, recipient)