Skip to content
This repository has been archived by the owner on Dec 12, 2022. It is now read-only.

district0x/cljs-web3-next

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

cljs-web3-next

CircleCI

This ClojureScript library provides a API for interacting with Ethereum nodes.

Installation

Latest released version of this library:
Clojars Project

API Overview

cljs-web3.core

Core functions which deal with creating and checking the status of Web3 connections.

websocket-provider

This function is the gateway to using the library. Most other functions will take the map it returns as their first argument, unless specified otherwise.

It creates a Web3 instance over a websocket connection. Takes an the url and a map of options as parameters. Returns a the websocket (the provider)

Example:

(ns my-district
  (:require [cljs-web3.core :as web3-core]
            [web3.impl.web3js :as web3js]))

(def web3 (web3-core/websocket-provider "ws://127.0.0.1:8545" {:client-config {:max-received-frame-size 100000000
                                                                               :max-received-message-size 100000000}}))

http-provider

Similar to websocket-provider, but creates a Web3 instance from a URL address.

NOTE some functions, notably subscriptions (see e.g. subscribe-events), will work differently with than http-provider, which is why it's generally recommended to use the websocket connections for your subscriptions.

extend

Allows for extending the Web3 object with any supported JSON RPC method, which is otherwise not a part of this library. Takes as arguments:

  • a map returned by the websocket-provider or http-provider function.
  • the module name (a keyword)
  • a colection of method maps with following keys:
    • name : Name of the method to add
    • call : The RPC method name
    • params : The number of parameters for that call (optional)

Example:

(extend web3
 :evm
 [{:name "increaseTime"
   :call "evm_increaseTime"
   :params 1})])

Returns the same web3 map as passed, but now the provider is extended with the increaseTime method in the evm module, which you can invoke like this:

(.increaseTime (aget web3 :provider "evm") 1000)

connection-url

Takes as arguments a map returned by the provider function and returns the URL of the node it is connected to.

(connection-url web3)
;; "ws://127.0.0.1:8545"

connected?

Takes as arguments a map returned by the provider function and returns the connection status as a boolean value. This function is synchronous, for an asynchronous method see is-listening?.

disconnect

Immediately disconnects the provider, returns nil.

(disconnect web3)

on-connect

Takes a provider map and a callback function as arguments, callback is executed when the connection is established.

(web3-core/on-connect web3 (fn [event] (prn "just connected")))

on-disconnect

Takes a provider map and a callback as arguments, callback is executed when the connection is dropped.

(web3-core/on-disconnect web3 (fn [event] (prn "your web3 socket has lost its connection")))

on-error

Similar as on-connect and on-disconnect but executes the callback when connection throws an error.

cljs-web3.eth

This namespace contains functions for interacting with the Ethereum blockchain and Ethereum smart contracts.

is-listening?

Asynchronous version of the connected? function, takes the provider map and a callback function. Returns a JS/Promise which returns a boolean.

You can use it to set a periodically executing connection healthcheck:

(js/setInterval (fn []
                  (is-listening? web3
                                 (fn [_ connected?]
                                   (when-not connected?
                                     (reset-connection)))))
                3000)

contract-at

Takes a provider map, contract abi interface as returned by the solc compiler (in a JSON format). Returns a Contract instance.

(def abi (aget (.readFileSync js/fs MyContract.json) "abi))

(contract-at web3 abi "0x98f93ed24052ceed35741beee1d75287cb297137")

get-transaction-receipt

Takes a provider map, transaction hash (a String) and an optional callback function. Returns a JS/Promise which evaluates to the receipt of that transaction.

(get-transaction-receipt web3 "0xd5000d0de00e50d6ac47fe48d11d9dc8258e74fc69b57b5c804e7ddc424af8d0" (fn [tx] (prn "I got the receipt" tx))

accounts

Returns the availiable ethereum accounts in the wallet on the node it is connected to.

(accounts web3)

get-block-number

Takes a web3 map and an optional callback as arguments. Returns a JS/Promise which evaluates to the current block number.

(get-block-number web3)

get-block

Takes as arguments:

  • a web3 map
  • a block number or hash
  • a boolean parameter specifying whether to include the transactions
  • an optional callback

Returns a JS/Promise which evaluates to the representation of the last mined block.

(get-block web3 block-number false)

encode-abi

This function returns the ABI bytecode of a transaction.

It takes as arguments:

  • the provider map
  • smart contract instance, as returned by the contract-at function
  • the kebab-cased keyword with the name of the smart contracts function
  • a vector with the arguments of the function

Returns bytecode as a string.

(encode-abi web3 my-contract :set-counter [3])

contract-call

This function executes a statless (read only) method of a smart contract.

It takes as arguments:

  • the provider map
  • smart contract instance, as returned by the contract-at function
  • the kebab-cased keyword with the name of the smart contracts function to execute
  • a vector with the arguments of the function
  • map of options:
  • :from : the account calling the contract (see accounts)

Returns a JS/Promise which evaluates to the return value of that function.

(web3-eth/contract-call web3
                        my-contract
                        :my-plus
                        [3 4]
                        {:from (first accounts)})

contract-send

This function executes a state-altering (payable) method of a smart contract.

It takes as arguments:

  • the provider map
  • smart contract instance, as returned by the contract-at function
  • the kebab-cased keyword with the name of the smart contracts function to execute
  • a vector with the arguments of the function
  • map of options:
  • :from : the account calling the contract (see accounts)
  • :gas : max amount of gas you are willing to spend

Returns a JS/Promise which evaluates to the tx-hash once the transaction is mined.

(<! (web3-eth/contract-send web3
                            my-contract
                            :set-counter
                            [3]
                            {:from (first accounts)
                             :gas 4000000}))

subscribe-events

Creates a subscription listening to a specific contracts event.

It takes as arguments:

  • the provider map
  • smart contract instance, as returned by the contract-at function
  • the CamelCased keyword with the name of the smart contracts event to listen to
  • map of options:
    • :from-block : the block number (greater than or equal to) from which to get events on.
  • a nodejs-style callback function (error is a first parameters and response the second), executed each time the event is seen (optional)

Returns a EventEmitter object which can be subsequently used with on to react to an even finer-grained control.

(web3-eth/subscribe-events web3
                           my-contract
                           :SetCounterEvent
                           {:from-block block-number}
                           (fn [error event]
                             (prn "new event" event})))

subscribe-logs

This function lets you create subscriptions listening to specific logs of things happening on the blockchain, filtered by a given list of topics.

It takes as arguments:

  • the provider map
  • map of options:
    • :from-block : the block number (greater than or equal to) from which to get events on.
    • :address : a string or a vector of strings with addresses to listen to
    • :topics : An vector of values which must each appear in the log entries. Order needs to match the order in the :address vector.
  • a nodejs-style callback function (error is a first parameters and response the second), executed each time the log is seen (optional)
(subscribe-logs web3
                {:address [address]
                 :topics [event-signature]
                 :from-block block-number}
                (fn [_ event] (prn event))

Similar to the subscribe-events function, it returns an EventEmitter which can be augmented using on.

decode-log

Use this function to decodes an ABI encoded log data and indexed topic data, such as returned by the subscribe-logs subscription.

Arguments:

  • the provider map abi : a map of interface inputs array data : the abi bytecode of the data field in the log (a string) topics : a vector of the topics of the log (see subscribe-logs)

on

This function can be used with the EventEmitter returned by the subscribe-events and subscribe-logs. It will add callbacks executed on specific events:

  • :connected: fires the callback when the subscription is created, returns the id of that subscription as the first argument of the callback
  • :data : fired on each incoming log with the log object as argument. If the subscription happens ot be listening to a smart event contract the event passed as th ecallback argument is the same as for the subscribe-events.
  • :changed : fires when the log is removed from the blockchain
  • :error : fires when an error in the subscription occurs.
(-> event-emitter
    (#(on web3 % :connected (fn [sub-id]
                              (prn "subscribed to SetCounterEvents. Subscription id :" sub-id))))
    (#(on web3 % :data (fn [event]
                         (prn "new SetCounterEvents :" event))))
    (#(on web3 % :error (fn [error]
                          (prn "Error :" error)))))

unsubscribe

Clears a subscription, takes a web3 provider an an event emitter (returned by the subscribe-events or subscribe-logs) as arguments.

(web3-eth/unsubscribe web3 event-emitter)

clear-subscriptions

Clears all created subscription.

get-past-events

Returns all past events for the specified contract.

It takes as arguments:

  • the provider map
  • smart contract instance, as returned by the contract-at function
  • the CamelCased keyword with the name of the smart contracts event to replay
  • map of options:
    • :from-block : the block number (greater than or equal to) from which to return the events
    • :to-block : the block number (less than or equal to) up to which the events are returned
  • a nodejs-style callback function (error is a first parameters and response the second), executed each time the event is seen (optional)
(web3-eth/get-past-events web3
                          my-contract
                          :SetCounterEvent
                          {:from-block 0
                           :to-block "latest"}
                          (fn [events]))

get-past-logs

A subscribe-logs equivalent of get-past-events.

(web3-eth/get-past-logs web3
                        {:address [address]
                         :topics [event-signature]
                         :from-block 0
                         :to-block "latest"}
                        (fn [logs]))

cljs-web3.utils

This namespaces provides various utility functions.

sha3

Returns a sha3 of the input.

solidity-sha3

Implementation of Solidity sha3 function. Takes a web3 provider and a variable number of arguments, returns a hash value (a string).

(solidity-sha3 web3 "0x7d10b16dd1f9e0df45976d402879fb496c114936" 6 "abc")

from-ascii

(from-ascii web3 args)

to-ascii

(to-ascii web3 arg)

number-to-hex

(number-to-hex web3 number)

from-wei

(from-wei web3 number <unit>)

to-wei

(to-wei web3 number <unit>)

address?

(address? web3 address)

cljs-web3.evm

NOTE The functions in this namespaces are not a part of the API unless you extend the evm module with these RPC calls. They will only to work with a testrpc such as ganache

increase-time!

Increases the blockchain time by the specified number of seconds.

(increase-time! web3 [seconds] callback)

mine-block!

Instantly mines a block.

(mine-block web3 callback)

snapshot!

Snapshot the state of the blockchain at the current block.

(snapshot! web3 callback)

revert!

Revert the state of the blockchain to a previous snapshot.

(revert! web3 [snapshot-id] callback)

cljs-web3.helpers

Functions in this namespace are not part of the API, rather help in turning the JS objects returned by the API funcions to the corresponding Clojure data structures. As such they are independent of the currently used implementation and do not take the web3 provider as their first argument.

js->cljkk

From JavaScript Object to Clojure map with kebab-cased keywords, e.g. :

#js {:fromBlock 0, :toBlock "latest"}
;; =>
{:from-block 0 :to-block "latest"}

cljkk->js

From Clojure with kebab-cased keywords to JavaScript e.g.

{:from-block 0 :to-block "latest"}
;; =>
#js {:fromBlock 0, :toBlock "latest"}

event-interface

Given a contract instance returned by the contract-at function and a :CamelCase key of the event, returns the abi interface of that event, which can be used e.g. with subscribe-logs.

(event-interface my-contract :SetCounterEvent)

return-values->clj

Given a returnValues field (part of the data return by subscriptions, such as subscribe-events) and an event-interface returns a edn (aka Clojure) representation of this events return values.

(return-values->clj return-values event-interface)

Running tests

lein npm install

spin up a testnet instance in a separate shell

npx truffle develop

migrate contracts

npx truffle migrate --network ganache

New build, test and release commands

As this library is meant to work both in browsers and on Node.js (server), it must be tested on both as well. Additionally CI runs the tests slightly different way, so that's the 3rd test environment.

  • it's still in browser, but CI gets success vs failure depending on the
  • browser process exit code so a bit of extra work is needed to get it out from JS (Karma is used for that)

Node.js

  1. Build: npx shadow-cljs compile test-node
  2. Tests: node out/node-tests.js

Browser

  1. Build: npx shadow-cljs watch test-browser
  2. Tests: http://d0x-vm:6502

CI (Headless Chrome, Karma)

  1. Build: npx shadow-cljs compile test-ci
  2. Tests:
    CHROME_BIN=`which chromium-browser` npx karma start karma.conf.js --single-run
    

inspect on headless chrome on another chrome instance

  1. Run headless chrome: chromium-browser --headless --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --allowed-origins="*" https://chromium.org
  2. Open chrome://inspect/#devices and configure remote target with IP ADDRESS (hostname doesn't work)

Build & release with deps.edn and tools.build

  1. Build: clj -T:build jar
  2. Release: clj -T:build deploy

About

⚠️ This code now resides at d0x monorepo

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published