Awesome
re-frame-web3-fx
re-frame Effectful Handlers to work with Ethereum blockchain Web3 API, using cljs-web3
Installation
Add into your project.clj
Include [district0x.re-frame.web3-fx]
in your CLJS file
(ns my.app
(:require [district0x.re-frame.web3-fx]))
Breaking changes 0.2.3 -> 1.0.0
This library was completely rewritten on version upgrade 0.2.3 -> 1.0.0. API was greatly changed (simplified). Reasons for breakage were also unerlying changes in Ethereum (fork to Byzantium) and preparations for web3 1.0.0. I deeply appologize, but this was absolutely necessary.
API Overview
- :web3/call
- :web3/get-balances
- :web3/watch-events
- :web3/watch-transactions
- :web3/watch-blocks
- :web3/stop-watching-balances
- :web3/stop-watching
- :web3/stop-watching-all
Usage
Following effect handlers are available:
:web3/call
Use it to call any function from cljs-web3 or any smart contract function. Calling cljs-web3 function:
(reg-event-fx
::load-accounts
(fn [{:keys [:db]} []]
{:web3/call {:web3 (:web3 db)
:fns [{:fn cljs-web3.eth/accounts
:args []
:on-success [::accounts-loaded]
:on-error [::error]}]}}))
Calling constant smart-contract function. In this case getting total supply of a ERC20 Token:
(reg-event-fx
::get-token-total-supply
(fn [{:keys [:db]}]
{:web3/call {:web3 (:web3 db)
:fns [{:instance (:token-contract-instance db)
:fn :total-supply
:on-success [::token-total-supply-result]
:on-error [::error]}]}}))
Calling state changing smart-contract function, aka sending a transaction to the network. In this case calling mint
function of MintableToken. Notice there's no on-success
, on-error
. Given callbacks are executed at following situations:
:on-tx-hash
When tx is successfully sent to the network. Receives tx-hash in parameters.:on-tx-hash-error
When tx wasn't send to the network. Usually user rejected to sign.:on-tx-success
When tx was processed without error. Receives receipt in parameters.:on-tx-error
When there was an error during processing a transaction. Receives receipt in parameters.:on-tx-receipt
General callback when tx was processed. Either with error or not. Receives receipt in parameters. (You don't need to use all of them, only ones you need) <br>
All of these callbacks have their respective multi-event callbacks i.e.:
:on-tx-hash-n
:on-tx-hash-error-n
:on-tx-success-n
:on-tx-error-n
:on-tx-receipt-n
(reg-event-fx
::mint-token
(fn [{:keys [:db]} [_ {:keys [:to :amount :from]}]]
{:web3/call {:web3 (:web3 db)
:fns [{:instance (:token-contract-instance db)
:fn :mint
:args [to amount]
:tx-opts {:from from
:gas 4500000}
:on-tx-hash-n [[::tx-send-success] [::extra]]
:on-tx-hash-error [::tx-send-failed]
:on-tx-success [::token-minted]
:on-tx-error [::tx-failed]
:on-tx-receipt [::tx-receipt-loaded]}]}}))
:web3/get-balances
Gets balance of Ether or ERC20 token. Optionally you can pass :watch? true
, so the callback will be fired everytime
the balance changes.
Getting and watching balance or Ether:
(reg-event-fx
::load-ether-balances
(fn [{:keys [:db]} [_ addresses]]
{:web3/get-balances {:web3 (:web3 db)
:addresses (for [address addresses]
{:address address
:watch? true
:on-success [::ether-balance-loaded address]
:on-error [::error]})}}))
Getting and watching balance of a ERC20 Token. Notice you need to pass :instance
of a ERC20 contract
(reg-event-fx
::load-token-balances
(fn [{:keys [:db]} [_ addresses]]
{:web3/get-balances {:web3 (:web3 db)
:addresses (for [address addresses]
{:address address
:instance (:token-contract-instance db)
:watch? true
:on-success [::token-balance-loaded address]
:on-error [::error]})}}))
:web3/watch-events
Listens to smart-contract events. Callback receives event :args
as first param and complete event data as a second.
In this example we watch Mint event of MintableToken
(reg-event-fx
::watch-mint
(fn [{:keys [:db]} [_ {:keys [:to]}]]
{:web3/watch-events {:events [{:id :mint-watcher
:event :Mint
:instance (:token-contract-instance db)
:block-filter-opts {:from-block 0}
:event-filter-opts {:to to}
:on-success [::token-mint-event]
:on-error [::error]}]}}))
:web3/watch-transactions
Sets up listener until transaction receipt is available. Callbacks are fired same way as in :web3/call
for
state-changing contract functions.
(reg-event-fx
::watch-transaction
interceptors
(fn [{:keys [:db]} [tx-hash]]
{:web3/watch-transactions {:web3 (:web3 db)
:transactions [{:id :my-watcher
:tx-hash tx-hash
:on-tx-success [::tx-success]
:on-tx-error [::error]
:on-tx-receipt [::tx-receipt]}]}}))
:web3/watch-blocks
Sets up listener with callback fired on each new Ethereum block.
(reg-event-fx
::watch-blocks
(fn [{:keys [:db]}]
{:web3/watch-blocks {:id :my-watcher
:web3 (:web3 db)
:on-success [::new-block]
:on-error [::error]}}))
:web3/stop-watching-balances
Stops listeners previously set up with :web3/get-balances
(reg-event-fx
::stop-watching-balances
(fn [{:keys [:db]} [_ addresses]]
{:web3/stop-watching-balances {:addresses (for [address addresses]
{:address address
:instance (:token-contract-instance db)})}}))
:web3/stop-watching
In any effect handler above, where you could provide :id
, you can use this effect handler to stop that listener.
(reg-event-fx
::stop-watching
(fn [{:keys [:db]}]
{:web3/stop-watching {:ids [:my-watcher]}}))
:web3/stop-watching-all
Stops all listeners set up by all effect handlers.
(reg-event-fx
::stop-watching
(fn [{:keys [:db]}]
{:web3/stop-watching-all true}))
Running tests
- On development machine
npx shadow-cljs watch test-browser
- Open http://d0x-vm:6502
- On CI (or headless env)
npx shadow-cljs compile test-ci
-
CHROME_BIN=`which chromium-browser` npx karma start --single-run`