Giter Site home page Giter Site logo

thor-devkit.js's Introduction

Thor DevKit

Typescript library to aid DApp development on VeChain Thor

NPM Version Unit Test Coverage Status

Installation

npm i thor-devkit

Usage

Transaction

to build and sign a transaction

import { Transaction, secp256k1 } from 'thor-devkit'

const clauses =  [{
    to: '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
    value: 10000,
    data: '0x'
}]

// calc intrinsic gas
const gas = Transaction.intrinsicGas(clauses)
console.log(gas)
// 21000

let body: Transaction.Body = {
    chainTag: 0x9a,
    blockRef: '0x0000000000000000',
    expiration: 32,
    clauses: clauses,
    gasPriceCoef: 128,
    gas,
    dependsOn: null,
    nonce: 12345678
}

const tx = new Transaction(body)
const signingHash = tx.signingHash()
tx.signature = secp256k1.sign(signingHash, /* your private key */)

const raw = tx.encode()
const decoded = Transaction.decode(raw)

Certificate

client side self-signed certificate

import { Certificate, secp256k1, blake2b256 } from 'thor-devkit'

const cert: Certificate = {
    purpose: 'identification',
    payload: {
        type: 'text',
        content: 'fyi'
    },
    domain: 'localhost',
    timestamp: 1545035330,
    signer: <<<signer-address>>>
}

const jsonStr = Certificate.encode(cert)
const signature = secp256k1.sign(blake2b256(jsonStr), <<<private-key>>>)

cert.signature = '0x' + signature.toString('hex')

Certificate.verify(cert)

// certificate id
const id = '0x' + blake2b256(Certificate.encode(cert)).toString('hex')

ABI

import { abi } from 'thor-devkit'

const fn = new abi.Function({
    "constant": false,
    "inputs": [
        {
            "name": "a1",
            "type": "uint256"
        },
        {
            "name": "a2",
            "type": "string"
        }
    ],
    "name": "f1",
    "outputs": [
        {
            "name": "r1",
            "type": "address"
        },
        {
            "name": "r2",
            "type": "bytes"
        }
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
})

const data = fn.encode(1, 'foo')

RLP

import { RLP } from 'thor-devkit'

// define the profile for tx clause structure
const profile: RLP.Profile = {
    name: 'clause',
    kind: [
        { name: 'to', kind: new RLP.NullableFixedBlobKind(20) },
        { name: 'value', kind: new RLP.NumericKind(32) },
        { name: 'data', kind: new RLP.BlobKind() }
    ]
}

const clause = {
    to: '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
    value: 10,
    data: '0x'
}

const rlp = new RLP(profile)

const data = rlp.encode(clause)
console.log(data.toString('hex'))
// d7947567d83b7b8d80addcb281a71d54fc7b3364ffed0a80

const obj = rlp.decode(data)
// `obj` should be identical to `clause`

Crypto methods

Hash functions

import { blake2b256, keccak256 } from 'thor-devkit'

const hash = blake2b256('hello world')
console.log(hash.toString('hex'))
// 256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610

hash = keccak256('hello world')
console.log(hash.toString('hex'))
// 47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad

Secp256k1

import { secp256k1, keccak256, address } from 'thor-devkit'

const privKey = secp256k1.generatePrivateKey()
const pubKey = secp256k1.derivePublicKey(privKey)
const addr = address.fromPublicKey(pubKey)
const signature = secp256k1.sign(keccak256('hello world'), privKey)
const recoveredPubKey = secp256k1.recover(keccak256('hello world'), signature)

Mnemonic & Keystore

import { mnemonic, Keystore, HDNode } from 'thor-devkit'

// generate BIP39 mnemonic words, default to 12 words(128bit strength)
const words = mnemonic.generate()

// derive private key from mnemonic words according to BIP32, using the path `m/44'/818'/0'/0`.
// defined for VET at https://github.com/satoshilabs/slips/blob/master/slip-0044.md
const privateKey = mnemonic.derivePrivateKey(words)

// in recovery process, validation is recommended
let ok = mnemonic.validate(words)

// encrypt/decrypt private key using Ethereum's keystore scheme
const keystore = await Keystore.encrypt(privateKey, 'your password')

// throw for wrong password
const recoveredPrivateKey = await Keystore.decrypt(keystore, 'your password')

// roughly check keystore format
ok = Keystore.wellFormed(keystore)

// create BIP32 HD node from mnemonic words
const hdnode = HDNode.fromMnemonic(words)

// derive 5 child private keys
for (let i = 0; i < 5; i++) {
    let child = hdnode.derive(i)
    // get child private key
    // child.privateKey
}

// or create HD node from xpub
const pub = Buffer.from('04dc40b4324626eb393dbf77b6930e915dcca6297b42508adb743674a8ad5c69a046010f801a62cb945a6cb137a050cefaba0572429fc4afc57df825bfca2f219a', 'hex')
const chainCode = Buffer.from('105da5578eb3228655a8abe70bf4c317e525c7f7bb333634f5b7d1f70e111a33', 'hex')
hdnode = HDNode.fromPublicKey(pub, chainCode)
// derive 5 child public keys
for (let i = 0; i < 5; i++) {
    let child = hdnode.derive(i)
    // get child public key
    // child.publicKey
}

License

thor-devkit is licensed under the GNU Lesser General Public License v3.0, also included in LICENSE file in repository.

thor-devkit.js's People

Contributors

bkawk avatar decent-dev avatar dependabot[bot] avatar fabiorigam avatar libotony avatar mvayngrib avatar qianbin avatar rodolfopietro97 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

thor-devkit.js's Issues

Support latest connex

Hi
I'm getting this with Typescript

Argument of type 'Connex.Thor.Clause[]' is not assignable to parameter of type 'import("/home/some/Code/connex-ts/node_modules/thor-devkit/dist/transaction").Transaction.Clause[]'.
  Type 'Connex.Thor.Clause' is not assignable to type 'import("/home/some/Code/connex-ts/node_modules/thor-devkit/dist/transaction").Transaction.Clause'.
    Property 'data' is optional in type 'Clause' but required in type 'Clause'

data needs to be optional in thor-devkit

Thanks
Rogelio

sendRawTransaction Error: Bad Request

自签名交易时发生错误:

Transaction body= {
chainTag: 0xc7,
blockRef: '0x0000000000000000',
expiration: 20,
clauses: clauses,
gasPriceCoef: 128,
gas: gas,
dependsOn: null,
nonce: new Date().getTime(),
}

返回错误信息如下:
Response body: "body: rlp: non-canonical integer (leading zero bytes) for uint64, decoding into (*tx.Transaction)(tx.body).BlockRef"

求教原因 ?

@vechain/ethers dependency is breaking Jest testing

When running Jest tests on a package that includes thor-devkit, the tests break with the following error...

Jest has detected the following 1 open handle potentially keeping Jest from exiting:

  ●  RANDOMBYTESREQUEST
      at Object.<anonymous>.module.exports (../../node_modules/@vechain/ethers/node_modules/uuid/rng.js:3:10)
      at Object.<anonymous> (../../node_modules/@vechain/ethers/node_modules/uuid/uuid.js:57:18)
      at Object.<anonymous> (../../node_modules/@vechain/ethers/utils/secret-storage.js:15:30)

Encoding BigNumber produces Error

Hi!

Noticed this issue with the latest version (0.11.0) of devkit, here are steps to reproduce:

let { abi } = require("thor-devkit")
let BigNumber = require('bignumber.js')
let fn = new abi.Function({
    "constant": false,
    "inputs": [
        {
            "name": "a1",
            "type": "uint256"
        },
        {
            "name": "a2",
            "type": "string"
        }
    ],
    "name": "f1",
    "outputs": [
        {
            "name": "r1",
            "type": "address"
        },
        {
            "name": "r2",
            "type": "bytes"
        }
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
})

let data = fn.encode(1, 'foo') // works
data = fn.encode(new BigNumber(1), 'foo') // errors
"dependencies": {
    "bignumber.js": "^9.0.0",
    "thor-devkit": "^0.11.0"
  }

handle large value in clause

Hi, thank you for your work on this helpful library, I've been using it for a while now and so far so good. Except for one thing that I noticed:

if I set a large number in the value field of a clause like this:

let clauses = [
  {
    to: '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
    value: 1e18, // HERE
    data: '0x'
  }
];

Then errors show up

/tmp/node_modules/thor-devkit/dist/rlp.js:222
        throw new RLPError(`${ctx}: ${msg}`);
        ^

RLPError: tx.clauses.#0.value: expected non-negative safe integer
    at assert (/tmp/node_modules/thor-devkit/dist/rlp.js:222:15)
    at NumericKind.data (/tmp/node_modules/thor-devkit/dist/rlp.js:64:17)
    at pack (/tmp/node_modules/thor-devkit/dist/rlp.js:191:21)
    at kind.map.k (/tmp/node_modules/thor-devkit/dist/rlp.js:194:30)
    at Array.map (<anonymous>)
    at pack (/tmp/node_modules/thor-devkit/dist/rlp.js:194:21)
    at obj.map (/tmp/node_modules/thor-devkit/dist/rlp.js:198:33)
    at Array.map (<anonymous>)
    at pack (/tmp/node_modules/thor-devkit/dist/rlp.js:198:16)
    at kind.map.k (/tmp/node_modules/thor-devkit/dist/rlp.js:194:30)

but if I set the value less than 1e15, it works. I understand this is due to typescript's definition of the largest number (2^53-1), however, it's quite annoying if I can't build a transfer with more than one coin. Any suggestions or workaround? Thank you.

How to create a private key

Any help is appreciated. I can't find a way to generate a private key from a SYNC wallet. All I can get is the keystore.
I'm trying to use connex repl. which requires the private key to import a wallet.

How do I get this private key?

got "0xNaN" when rlp decode number field

Description

When I try to decode a raw tx and then retrive its transaction id or singer, I just got null. After that I found it's caused by rlp decode.

Actual behavior


let clauses = [{
    to: '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
    value: 0,
    data: '0x'
}]

let body = {
    chainTag: 0x9a,
    blockRef: '0x0000000000000000',
    expiration: 32,
    clauses: clauses,
    gasPriceCoef: 0,
    gas: 21000,
    dependsOn: null,
    nonce: 12345678
}

let tx = new Transaction(body)
let signingHash = cry.blake2b256(tx.encode())
tx.signature = cry.secp256k1.sign(signingHash, /* private key here */)

let raw = tx.encode()
let decoded = Transaction.decode(raw)

Decoded Transactin:

{
  "body": {
    "chainTag": 154,
    "blockRef": "0x0000000000000000",
    "expiration": 32,
    "clauses": [
      {
        "to": "0x7567d83b7b8d80addcb281a71d54fc7b3364ffed",
        "value": "0xNaN", // wrong
        "data": "0x"
      }
    ],
    "gasPriceCoef": "0xNaN", // wrong
    "gas": 21000,
    "dependsOn": null,
    "nonce": 12345678,
    "reserved": []
  },
  "signature": / * sig buffer */
  }
}

Suggestion

update rlp.ts file:

#89 const bn = new BigNumber('0x' + buf.toString('hex'))

to:

#89 const bn = new BigNumber(buf.toString('hex'), 16)

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.