Created
August 28, 2021 00:18
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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