Skip to content

Instantly share code, notes, and snippets.

@matthewdowney
Created August 28, 2021 00:18
Show Gist options
  • Save matthewdowney/df20919a2040c8c80a0f2ca98002e2af to your computer and use it in GitHub Desktop.
Save matthewdowney/df20919a2040c8c80a0f2ca98002e2af to your computer and use it in GitHub Desktop.
Call a smart contract from Clojure to query data from the Ethereum blockchain.
(ns web3
"Call an Ethereum smart contract (Uniswap v2) from Clojure.
Requires https://github.com/clj-python/libpython-clj for python interop. Also
make sure to $ pip install web3."
(:require [libpython-clj2.python :as py]
[libpython-clj2.require :refer [require-python]]))
(comment ;; deps.edn
{:deps {org.clojure/clojure {:mvn/version "1.10.3"}
clj-python/libpython-clj {:mvn/version "2.000"}}})
;; Initialize the python bridge and import the python web3 library
(defonce _
(do
;; Optionally, point it explicitly at an executable
(py/initialize! #_#_:python-executable "/usr/lib/python3.8")
(require-python '[web3.Web3 :as w3])
(require-python '[web3 :as w])))
(defn web3
"Create a python Web3 instance."
[provider-ws-url]
(w/Web3 (w3/WebsocketProvider provider-ws-url)))
(comment
;;; E.g. using the web3 library's python bindings to get the most recent
;;; block number and timestamp
(let [w3 (web3 "wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID")
; equivalent to web3.eth.get_block("latest")
block (-> w3 (py/py.- "eth") (py/py. "get_block" "latest") py/->jvm)]
{:time (java.util.Date. (* 1000 (get block "timestamp")))
:number (get block "number")})
;=> {:time #inst"2021-08-28T00:10:10.000-00:00", :number 13110600}
)
(defn get-uniswap-v2-reserves
"Call the Uniswap V2 getReserves() function on the given contract `addr`."
[w3 addr]
;; The ABI spec of the function to call. Browse the smart contract code
;; e.g. here[1] to figure out the ABI spec for different functions.
;; [1] https://etherscan.io/address/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc#code
(let [abi {"constant" true,
"inputs" [],
"name" "getReserves",
"outputs" [{"name" "reserve0", "type" "uint112"}
{"name" "reserve1", "type" "uint112"}
{"name" "blockTimestampLast", "type" "uint32"}],
"payable" false,
"stateMutability" "view",
"type" "function"}]
;; Similar to:
;; w3.eth.contract(addr, abi=[abi]).functions.getReserves().call()
(-> (py/py.- w3 "eth")
(py/py* "contract" [addr] {:abi [abi]})
(py/py.- "functions")
(py/py. "getReserves")
(py/py* "call" [] {})
;; Hack to deal with https://github.com/clj-python/libpython-clj/issues/164
pr-str
read-string)))
(comment
;;; E.g. calling getReserves() on the ETH/USDC contract
(let [contract "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"
w3 (web3 "wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID")]
; returns [reserve0, reserve1, blockTimestampLast]
(get-uniswap-v2-reserves w3 contract))
;=> [118842086514944 36316707705482760723575N 1630109309]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment