[extensions] ethereum call decode params

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2018-10-24 14:37:04 +02:00
parent 3eda5e3bfc
commit f8ef431373
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
4 changed files with 157 additions and 5 deletions

View File

@ -264,6 +264,7 @@
:arguments {:to :string
:method :string
:params? :vector
:outputs? :vector
:on-result? :event}}}
:hooks {:commands commands/command-hook}})

View File

@ -4,7 +4,8 @@
[status-im.models.wallet :as models.wallet]
[status-im.utils.ethereum.abi-spec :as abi-spec]
[status-im.utils.fx :as fx]
[status-im.ui.screens.navigation :as navigation]))
[status-im.ui.screens.navigation :as navigation]
[clojure.string :as string]))
(handlers/register-handler-fx
:extensions/transaction-on-result
@ -29,11 +30,15 @@
(handlers/register-handler-fx
:extensions/ethereum-call
(fn [_ [_ {:keys [to method params on-result]}]]
(fn [_ [_ {:keys [to method params outputs on-result]}]]
(let [tx-object {:to to :data (when method (abi-spec/encode method params))}]
{:browser/call-rpc [{"jsonrpc" "2.0"
"method" "eth_call"
"params" [tx-object "latest"]}
#(when on-result
(re-frame/dispatch (on-result {:error %1 :result (when %2
(get (js->clj %2) "result"))})))]})))
(let [result-str (when %2
(get (js->clj %2) "result"))
result (if (and outputs result-str)
(abi-spec/decode (string/replace result-str #"0x" "") outputs)
result-str)]
(re-frame/dispatch (on-result {:error %1 :result result}))))]})))

View File

@ -1,7 +1,8 @@
(ns status-im.utils.ethereum.abi-spec
(:require [cljs.spec.alpha :as spec]
[clojure.string :as string]
[status-im.js-dependencies :as dependencies]))
[status-im.js-dependencies :as dependencies]
[clojure.string :as str]))
;; Utility functions for encoding
@ -33,6 +34,12 @@
(defn number-to-hex [x]
(subs (.numberToHex utils x) 2))
(defn hex-to-utf8 [x]
(.hexToUtf8 utils (str "0x" x)))
(defn hex-to-number [x]
(.hexToNumber utils (str "0x" x)))
(defn sha3 [s]
(.sha3 utils (str s)))
@ -267,3 +274,116 @@
params)]
(str method-id (enc {:type :tuple
:value params})))))
;; ======= decode
(defn substr [val s l]
(subs val s (+ s l)))
;; "[]" -> 0 , "[1]" -> 1
(defn arr-size [val]
(int (apply str (rest (butlast val)))))
;; [2] -> 2 , [1] -> 1 , [] - > 1
(defn nested-size [val]
(let [num (arr-size val)]
(if (zero? num) 1 num)))
;; '("[1]" "[]") or nil
(defn list-of-nested-types [type]
(when-let [res (re-seq #"(\[[0-9]*\])" type)]
(map first res)))
(defn nested-name [type]
(let [ntypes (list-of-nested-types type)]
(if ntypes
(subs type 0 (- (count type) (count (last ntypes))))
type)))
(defn is-arr? [type]
(boolean (list-of-nested-types type)))
(defn is-dynamic-arr? [type]
(let [ntypes (list-of-nested-types type)]
(and ntypes (zero? (arr-size (last ntypes))))))
(defn static-arr-len [type]
(let [ntypes (list-of-nested-types type)]
(if ntypes
(nested-size (last ntypes))
1)))
(defn static-part-length [type]
(apply * (conj (map nested-size (or (list-of-nested-types type) '("1"))) 32)))
(defn offset-reducer [{:keys [cnt coll]} val]
(let [cnt' (+ cnt val)]
{:cnt cnt'
:coll (conj coll cnt')}))
(defn get-offsets [types]
(let [lengths (map static-part-length types)]
(conj (butlast (:coll (reduce offset-reducer {:cnt 0 :coll []} lengths))) 0)))
(defn hex-to-bytes [hex]
(let [len (* (.toNumber (.toBN utils (subs hex 0 64))) 2)]
(substr hex 64 len)))
(defn dyn-hex-to-value [hex type]
(cond
(str/starts-with? type "bytes")
(str "0x" (hex-to-bytes hex))
(str/starts-with? type "string")
(hex-to-utf8 (hex-to-bytes hex))))
(defn hex-to-bytesM [hex type]
(let [size (int (second (re-matches #"^bytes([0-9]*)" type)))]
(subs hex 0 (* 2 size))))
(defn hex-to-value [hex type]
(cond
(= "bool" type) (= hex "0000000000000000000000000000000000000000000000000000000000000001")
(str/starts-with? type "uint") (hex-to-number hex)
(str/starts-with? type "int") (hex-to-number hex)
(str/starts-with? type "address") (str "0x" (subs hex (- (count hex) 40)))
(str/starts-with? type "bytes") (hex-to-bytesM hex type)))
(defn dec-type [bytes]
(fn [offset type]
(cond
(is-arr? type)
(let [dyn-arr? (is-dynamic-arr? type)
arr-off (js/parseInt (str "0x" (substr bytes (* offset 2) 64)))
len (if dyn-arr?
(js/parseInt (str "0x" (substr bytes (* arr-off 2) 64)))
(static-arr-len type))
arr-start (if dyn-arr? (+ arr-off 32) offset)
nname (nested-name type)
nstatpartlen (static-part-length nname)
rnstatpartlen (* (js/Math.floor (/ (+ nstatpartlen 31) 32)) 32)]
(loop [res [] i 0]
(if (>= i (* len rnstatpartlen))
res
(recur (conj res ((dec-type bytes) (+ arr-start i) nname)) (+ i rnstatpartlen)))))
(or (re-matches #"^bytes(\[([0-9]*)\])*$" type)
(str/starts-with? type "string"))
(let [dyn-off (js/parseInt (str "0x" (substr bytes (* offset 2) 64)))
len (js/parseInt (str "0x" (substr bytes (* dyn-off 2) 64)))
rlen (js/Math.floor (/ (+ len 31) 32))
val (substr bytes (* dyn-off 2) (* (+ rlen 1) 64))]
(dyn-hex-to-value val type))
:else
(let [len (static-part-length type)
val (substr bytes (* offset 2) (* len 2))]
(hex-to-value val type)))))
(defn decode [bytes types]
(let [offsets (get-offsets types)]
(map (dec-type bytes) offsets types)))

View File

@ -17,3 +17,29 @@
(is (= (abi-spec/encode "g(uint[][],string[])" [[[1 2] [3]] ["one" "two" "three"]])
"0xad6a3446000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000036f6e650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000374776f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057468726565000000000000000000000000000000000000000000000000000000")))
(deftest test-decode
(is (= (abi-spec/decode "000000000000000000000000000000000000000000000000000000005bc741cd00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000013b86dbf1a83c9e6a492914a0ee39e8a5b7eb60700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147000000000000000000000000000000000000"
["uint256" "bytes" "address" "uint256" "uint256"])
'(1539785165
"0x516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147"
"0x13b86dbf1a83c9e6a492914a0ee39e8a5b7eb607"
0
0)))
(is (= (abi-spec/decode "00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001"
["uint32" "bool"])
'(69 true)))
(is (= (map abi-spec/hex-to-utf8
(first (abi-spec/decode "61626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000"
["bytes3[2]"])))
'("abc" "def")))
(is (= (abi-spec/decode "0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"
["string" "bool" "uint256[]"])
'("dave" true [1 2 3])))
(is (= (abi-spec/decode "00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000"
["uint" "uint32[]" "bytes10" "bytes"])
'(291 [1110 1929] "31323334353637383930" "0x48656c6c6f2c20776f726c6421"))))