Code Monkey home page Code Monkey logo

aepp-calldata-js's Introduction

Aeternity data serialization

Aeternity contract calldata encoding and results decoding standalone library.

This is Javascript implementation of data serialization specified in aeternity protocol.

While the only purpose of the library at the moment of this writing is solely to provide encoding and decoding respectively of contracts calldata and return data it may evolve to full-fledged serialization library of the full protocol specification.

Installation

npm install -P @aeternity/aepp-calldata

Quick Start

To work with contract calls with type information provided by ACI the AciContractCallEncoder class should be used. The constructor takes a single argument - Sophia ACI as string.

NodeJS example:

const {AciContractCallEncoder} = require('@aeternity/aepp-calldata')
const ACI = require('./Test.json')
const CONTRACT = 'Test'

const encoder = new AciContractCallEncoder(ACI)

Encoding calldata

The encodeCall method is used to encode calldata taking the contract name as first argument, then function name and list of contract call arguments as last argument.

Example:

const encoded = encoder.encodeCall(CONTRACT, 'test_string', ["whoolymoly"])
console.log(`Encoded data: ${encoded}`)

Expected output:

Encoded data: cb_KxHwzCuVGyl3aG9vbHltb2x5zwMSnw==

Decoding call result

The decodeResult method is used to decode contract call result based on it's type. While the first two arguments are the same as the encoding method, the third one is the actual result to be decoded and last one is the result type which defaults to 'ok'.

Example:

const decoded = encoder.decodeResult(CONTRACT, 'test_string', 'cb_KXdob29seW1vbHlGazSE')
console.log(`Decoded data: ${decoded}`)

Expected output:

Encoded data: cb_KxHwzCuVGyl3aG9vbHltb2x5zwMSnw==
Decoded data: whoolymoly

Contract call errors

FATE contract call error message is represented as encoded contract bytearray (cb_ prefixed string). However, revert messages are FATE string encoded, so the decodeResult method accepts forth argument with the result type.

Example:

// error message
const error = encoder.decodeResult(
    CONTRACT,
    'test_string',
    'cb_VHlwZSBlcnJvciBvbiBjYWxsOiBbe2J5dGVzLDw8MjQwLDIsLi4uPj59XSBpcyBub3Qgb2YgdHlwZSBbe2J5dGVzLDMyfV3EtJjU',
    'error'
)
console.log('Error: ' + error)

// revert message
const revert = encoder.decodeResult(CONTRACT, 'test_string', 'cb_OXJlcXVpcmUgZmFpbGVkarP9mg==', 'revert')
console.log('Revert: ' + revert)

Expected output:

Error: Type error on call: [{bytes,<<240,2,...>>}] is not of type [{bytes,32}]
Revert: require failed

Events

Example:

const data = encoder.decodeEvent('Test', 'cb_dHJpZ2dlcmVk1FYuYA==', [
    34853523142692495808479485503424878684430196596020091237715106250497712463899n,
    17n
])
console.log(data)

Expected output:

{EventTwo: [17n, 'triggered']}

Byte Arrays

Any contract bytearray can be decocded using the ContractByteArrayEncoder class.

Node that FATE does not carry some of the type informaton with the data:

  • Record keys are lost
  • Variant constructor names are lost
  • Any user type information is lost
  • STL type information is lost: i.e. Chain, AENS, Set, BLS12_381

Example:

const {ContractByteArrayEncoder} = require('@aeternity/aepp-calldata')

const decodedString = encoder.decode('cb_KXdob29seW1vbHlGazSE')
console.log(`Decoded string: ${decodedString}`)

const decodedMap = encoder.decode('cb_LwEOfzGit9U')
console.log('Decoded map:', decodedMap)

Expected output:

Decoded string: whoolymoly
Decoded map: Map(1) { 7n => false }

The encoder could also work with explicit type information:

Example:

const {ContractByteArrayEncoder, TypeResolver} = require('@aeternity/aepp-calldata')

const encoder = new ContractByteArrayEncoder()
const resolver = new TypeResolver()

const decodedString = encoder.decodeWithType('cb_KXdob29seW1vbHlGazSE', resolver.resolveType('string'))
console.log(`Decoded string: ${decodedString}`)

const type = resolver.resolveType({map: ['int', 'bool']})
const encodedMap = encoder.encodeWithType(new Map([[7n, false]]), type)
console.log('Encoded map:', encodedMap)

Expected output:

Decoded string: whoolymoly
Decoded map: Map(1) { 7n => false }

FATE API Encoder

Any of the following FATE API data types can be encoded and decoded:

  • key_block_hash
  • micro_block_hash
  • block_pof_hash
  • block_tx_hash
  • block_state_hash
  • contract_bytearray
  • contract_pubkey
  • account_pubkey
  • channel
  • oracle_pubkey
  • oracle_query_id
  • peer_pubkey
  • name
  • tx_hash
  • signature
  • commitment
  • bytearray

Example:

const {FateApiEncoder} = require('@aeternity/aepp-calldata')
const encoder = new FateApiEncoder()

const encoded = encoder.encode('contract_bytearray', new Uint8Array())
console.log(`Encoded: ${encoded}`)

const decoded = encoder.decode('cb_Xfbg4g==')
console.log('Decoded:', decoded)

Expected output:

Encoded: cb_Xfbg4g==
Decoded: Uint8Array(0) []

Note that the encoder work with binary data, so that strings has to be encoded as Uint8Array.

String Example:

const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()

const encoded = encoder.encode('contract_bytearray', textEncoder.encode('whoolymoly'))
console.log(`Encoded: ${encoded}`)

const decoded = textDecoder.decode(encoder.decode('cb_d2hvb2x5bW9seeO2SW0='))
console.log('Decoded:', decoded)

Excepted output:

Encoded: cb_d2hvb2x5bW9seeO2SW0=
Decoded: whoolymoly

Contract Encoder

Decodes contract metadata including bytecode.

Example:

const {ContractEncoder} = require('@aeternity/aepp-calldata')
const encoder = new ContractEncoder()

const testContract = fs.readFileSync(path.resolve(__dirname, '../build/contracts/Test.aeb'))
const contract = encoder.decode(testContract.toString())

console.log('Contract:', contract)

Expected output (trimmed):

{
  tag: 70n,
  vsn: 3n,
  source_hash: 'e50758c624dcacd485db1f9e76208c5858dd968f6218637d055fd4a3b2850baa',
  aevmTypeInfo: [],
  compilerVersion: '6.1.0',
  payable: false,
  bytecode: {
    functions: [
      {
        id: '077a0e02',
        name: 'test_nested_list',
        attributes: [],
        args: {
          name: 'tuple',
          valueTypes: [
            {
              name: 'list',
              valuesType: { name: 'list', valuesType: { name: 'int' } }
            }
          ]
        },
        returnType: {
          name: 'list',
          valuesType: { name: 'list', valuesType: { name: 'int' } }
        },
        instructions: [
          [
            { mnemonic: 'RETURNR', args: [ { mod: 'arg', arg: 0n } ] }
          ]
        ]
      },
      .....
    ],
    symbols: {
      '67419061': 'test_unit',
      ....
    },
    annotations: Map(0) {}
  }
}

Please note that the bytecode is for debugging/print purposes and it's structure WON'T be kept backward compatible

Data types

Using the library involves data types and their mappings from Sophia to JavaScript and vice versa.

Sophia Type Sophia Example Javascript type Javascript Example
int 63, -63 BigInt 63n, -63n
bool true, false Boolean true, false
string "whoolymoly" String "whoolymoly"
bytes #beef BigInt BigInt("0xbeef")
list [1, 2, 3, 5, 8, 13, 21] Array [1,2,3,5,8,13,21]
tuple (true, false) Array [true, false]
map {[7] = false} Map, Object, Array new Map([[7, false]]), {7: false}, [[7, false]]
record {x = 0, y = 0} Object (POJO) {x: 0, y: 0}
variant Some(404), None Object (POJO) {'Some': [404]}, {'None': []}, 404, undefined
bits Bits.none, Bits.all Bits.set(Bits.none, 0) BigInt 0b0n, -1n, 0b00000001n
hash #001234d BigInt BigInt("0x001234d")
signature #001234d BigInt BigInt("0x001234d")
address ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt String ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
Set.set Set.from_list([1, 2, 3]) Set, Array new Set([1,2,3]),[1,2,3]
BLS12_381.fr BLS12_381.int_to_fr(3735928559) BigInt 3735928559n
BLS12_381.fp BLS12_381.int_to_fp(3735928559) BigInt 3735928559n
  • note the fixed structure of variant object with a single key - the variant constructor (i.e. Some) and array of variant arguments as it's value.
  • while Javascript Number and primitive int types can be used as well when BigInt type is expected it's not recommended because of it's Number.MAX_SAFE_INTEGER limitation.

Versioning

This project follows the semantic versioning guidelines. Refer to the CHANGELOG for more information about releases.

Public API

The backward compatibility promise signaled with semantic versioning above is only applied to public API of this library, that is only the module exports and data types listed above.

The public API namely consist of below classes:

The public API is also defined in TypeScript declation file.

Errors

Error names are also part of the public API and it is guaranteed to get the same error name between compatible versions. Since error classes are not exported as public API, the library users should rely only on Error.name property to handle exceptions. Please also note that error messages are NOT part of the public API and they may change any time between versions without notice.

Development

Please make sure you get familiar with Contributing Guidelines first.

Install

npm install

Tests

Unit tests can be run with:

make tests

Integration tests:

make integration-tests

One can use the benchmarks to do relative comparison on performance for a given change:

make benchmark-tests

Verify browser compatibility with:

make browser-tests

To see the test coverage run:

make coverage

aepp-calldata-js's People

Contributors

davidyuk avatar dependabot[bot] avatar dincho avatar loxs avatar marc0olo avatar mradkov avatar renovate[bot] avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

aepp-calldata-js's Issues

Support of template variables

Example:

    datatype reallyType('a) = Nope | No | Yep('a) | Yes
    payable entrypoint test_variants (a: reallyType(int)) = a

CLI tool

Implement CLI tool with main idea to support QuickCheck tests

Setup CI

Let's try very simple setup with Travis

  • build
  • test

support encoding/decoding of contracts (`ct_...`)

it should be possible to decode contract addresses like ct_... which is returned when returning e.g. a remote contract

Example Contract

contract interface OtherSophiaTypes =
    entrypoint testInt: (int) => int

main contract SophiaTypes =

    entrypoint testRemoteInt(remote : OtherSophiaTypes, intValue: int) :  int =
        remote.testInt(intValue)

    entrypoint testRemoteContract(remote : OtherSophiaTypes) : OtherSophiaTypes =
        remote

This trying to encode the calldata or decode the result leads to following error:

Rejected promise returned by test. Reason:

  Error {
    message: 'Cannot resolve type: "OtherSophiaTypes"',
  }

Code refactoring

The structure should be easy to maintain, work in parallel and have better extensibility.

  • split serializers in their own files
  • factor-out the arguments resolver from encoder object

Change current testing framework

Currently using AVA which is very nice, but it does not run in a browser, and that's a must as we have to validate any browser incompatibilities, or not ?!

Custom error types

The library throws here and there a generic Error exceptions.

Custom Error objects would allow clients to handle error workflows in a more reliable way.

Use better types

Like build-in Map and ByteArrays.

Also create custom types/structs to represent FATE types to carry data between calls.

Now it's mostly array based structs

Better error checking

For example Encoder:serialize accept any function regardless it's part of the ACI

Improve tests:

  • add encode test for Tuple with all value types
  • add decode test for Tuple with all value types
  • add encode test for List with all value types
  • add decode test for List with all value types
  • add encode test for Map with all value types
  • add decode test for Map with all value types

Replace Some and None variants with the exact value and null accordingly

As I see Some and None are needed for Sophia's strict typing. JS is not so strict, so it can't be converted in a native way. For developer convenience, I propose to make it look native by omitting Sophia's variants in this case by replacing Some and None variants with the exact value and null accordingly.

Internal representation Public interface
{ variant: 'Some', values: [123] } 123
{ variant: 'None', values: [] } null

Test Coverage

  • Install test coverage tool
  • Increase the actual coverage to sane maximum

Add documentation

  • installation
  • quick start
  • explain serialization vs encoder
  • datatype support in serializer
  • contributing
  • mention semver
  • license

Improve encoder/decoder return values

The decoder should return:

  • contract address should use the ct_ formatted address.
  • Lists should be arrays
  • Tuples should be arrays
  • Maps should use JS maps ?
  • Variants should be objects (e.g. {Yep: [3]}

The encoder should accept the above types.

maybe optional JS vs DTO data

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.