diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index d8a9d2c81a..bae4fefeaa 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -1411,6 +1411,31 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL return Statusgo.identicon(seed); } + @ReactMethod(isBlockingSynchronousMethod = true) + public String encodeTransfer(final String to, final String value) { + return Statusgo.encodeTransfer(to, value); + } + + @ReactMethod(isBlockingSynchronousMethod = true) + public String encodeFunctionCall(final String method, final String paramsJSON) { + return Statusgo.encodeFunctionCall(method, paramsJSON); + } + + @ReactMethod(isBlockingSynchronousMethod = true) + public String decodeParameters(final String decodeParamJSON) { + return Statusgo.decodeParameters(decodeParamJSON); + } + + @ReactMethod(isBlockingSynchronousMethod = true) + public String hexToNumber(final String hex) { + return Statusgo.hexToNumber(hex); + } + + @ReactMethod(isBlockingSynchronousMethod = true) + public String numberToHex(final String numString) { + return Statusgo.numberToHex(numString); + } + @ReactMethod public void identiconAsync(final String seed, final Callback callback) { Log.d(TAG, "identiconAsync"); @@ -1572,5 +1597,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL StatusThreadPoolExecutor.getInstance().execute(r); } + } diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index c6ccda0430..8202ada87e 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -820,6 +820,28 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(identicon:(NSString *)publicKey) { return StatusgoIdenticon(publicKey); } +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeTransfer:(NSString *)to + value:(NSString *)value) { + return StatusgoEncodeTransfer(to,value); +} + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(encodeFunctionCall:(NSString *)method + paramsJSON:(NSString *)paramsJSON) { + return StatusgoEncodeFunctionCall(method,paramsJSON); +} + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(decodeParameters:(NSString *)decodeParamJSON) { + return StatusgoDecodeParameters(decodeParamJSON); +} + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(hexToNumber:(NSString *)hex) { + return StatusgoHexToNumber(hex); +} + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(numberToHex:(NSString *)numString) { + return StatusgoNumberToHex(numString); +} + RCT_EXPORT_METHOD(validateMnemonic:(NSString *)seed callback:(RCTResponseSenderBlock)callback) { #if DEBUG diff --git a/modules/react-native-status/nodejs/status.cpp b/modules/react-native-status/nodejs/status.cpp index 2141965248..e3df3c1b32 100644 --- a/modules/react-native-status/nodejs/status.cpp +++ b/modules/react-native-status/nodejs/status.cpp @@ -386,6 +386,182 @@ void _Identicon(const FunctionCallbackInfo& args) { } +void _EncodeTransfer(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + if (args.Length() != 2) { + // Throw an Error that is passed back to JavaScript + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong number of arguments for EncodeTransfer"))); + return; + } + + // Check the argument types + + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'to'"))); + return; + } + + if (!args[1]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'value'"))); + return; + } + + + String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked()); + char *arg0 = *arg0Obj; + String::Utf8Value arg1Obj(isolate, args[1]->ToString(context).ToLocalChecked()); + char *arg1 = *arg1Obj; + + // Call exported Go function, which returns a C string + char *c = EncodeTransfer(arg0, arg1); + + Local ret = String::NewFromUtf8(isolate, c).ToLocalChecked(); + args.GetReturnValue().Set(ret); + delete c; + +} + +void _EncodeFunctionCall(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + if (args.Length() != 2) { + // Throw an Error that is passed back to JavaScript + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong number of arguments for EncodeFunctionCall"))); + return; + } + + // Check the argument types + + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'method'"))); + return; + } + + if (!args[1]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'paramsJSON'"))); + return; + } + + + String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked()); + char *arg0 = *arg0Obj; + String::Utf8Value arg1Obj(isolate, args[1]->ToString(context).ToLocalChecked()); + char *arg1 = *arg1Obj; + + // Call exported Go function, which returns a C string + char *c = EncodeFunctionCall(arg0, arg1); + + Local ret = String::NewFromUtf8(isolate, c).ToLocalChecked(); + args.GetReturnValue().Set(ret); + delete c; + +} + +void _DecodeParameters(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + if (args.Length() != 1) { + // Throw an Error that is passed back to JavaScript + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong number of arguments for DecodeParameters"))); + return; + } + + // Check the argument types + + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'decodeParamJSON'"))); + return; + } + + + String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked()); + char *arg0 = *arg0Obj; + + // Call exported Go function, which returns a C string + char *c = DecodeParameters(arg0); + + Local ret = String::NewFromUtf8(isolate, c).ToLocalChecked(); + args.GetReturnValue().Set(ret); + delete c; + +} + +void _HexToNumber(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + if (args.Length() != 1) { + // Throw an Error that is passed back to JavaScript + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong number of arguments for HexToNumber"))); + return; + } + + // Check the argument types + + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'hex'"))); + return; + } + + + String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked()); + char *arg0 = *arg0Obj; + + // Call exported Go function, which returns a C string + char *c = HexToNumber(arg0); + + Local ret = String::NewFromUtf8(isolate, c).ToLocalChecked(); + args.GetReturnValue().Set(ret); + delete c; + +} + +void _NumberToHex(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + if (args.Length() != 1) { + // Throw an Error that is passed back to JavaScript + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong number of arguments for NumberToHex"))); + return; + } + + // Check the argument types + + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'numString'"))); + return; + } + + + String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked()); + char *arg0 = *arg0Obj; + + // Call exported Go function, which returns a C string + char *c = NumberToHex(arg0); + + Local ret = String::NewFromUtf8(isolate, c).ToLocalChecked(); + args.GetReturnValue().Set(ret); + delete c; + +} + void _Logout(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); @@ -1583,6 +1759,11 @@ void init(Local exports) { NODE_SET_METHOD(exports, "initKeystore", _InitKeystore); NODE_SET_METHOD(exports, "stopCPUProfiling", _StopCPUProfiling); NODE_SET_METHOD(exports, "identicon", _Identicon); + NODE_SET_METHOD(exports, "encodeTransfer", _EncodeTransfer); + NODE_SET_METHOD(exports, "encodeFunctionCall", _EncodeFunctionCall); + NODE_SET_METHOD(exports, "decodeParameters", _DecodeParameters); + NODE_SET_METHOD(exports, "hexToNumber", _HexToNumber); + NODE_SET_METHOD(exports, "numberToHex", _NumberToHex); NODE_SET_METHOD(exports, "logout", _Logout); NODE_SET_METHOD(exports, "hashMessage", _HashMessage); NODE_SET_METHOD(exports, "resetChainData", _ResetChainData); diff --git a/src/status_im/ethereum/abi_spec.cljs b/src/status_im/ethereum/abi_spec.cljs deleted file mode 100644 index bca28806ad..0000000000 --- a/src/status_im/ethereum/abi_spec.cljs +++ /dev/null @@ -1,396 +0,0 @@ -(ns status-im.ethereum.abi-spec - (:require ["web3-utils" :as utils] - [cljs.spec.alpha :as spec] - [clojure.string :as string] - [status-im.ethereum.core :as ethereum])) - -;; Utility functions for encoding - -(defn right-pad [x] - (let [len (count x) - to-pad (- 64 (mod len 64))] - (if (= 64 to-pad) - x - (.rightPad utils x (+ len to-pad))))) - -(defn left-pad [x] - (let [len (count x) - to-pad (- 64 (mod len 64))] - (if (= 64 to-pad) - x - (.leftPad utils x (+ len to-pad))))) - -(defn to-two-complement [x] - (when x - (subs (.toTwosComplement utils x) 2))) - -(defn utf8-to-hex [x] - (when x - (subs (ethereum/utf8-to-hex x) 2))) - -(defn hex-to-boolean [x] - (= x "0x0")) - -(defn bytes-to-hex [x] - (when x - (subs (.bytesToHex utils x) 2))) - -(defn number-to-hex [x] - (when x - (subs (.numberToHex utils x) 2))) - -(defn hex-to-utf8 [x] - (ethereum/hex-to-utf8 (str "0x" x))) - -(defn hex-to-number [x] - (when x - (let [hex-x (str "0x" x)] - (try - (.hexToNumber utils hex-x) - (catch :default _ - (.hexToNumberString utils hex-x)))))) - -(defn is-hex? [value] - (when value - (string/starts-with? value "0x"))) - -;; Encoder for parsed abi spec - -(defmulti enc :type) - -;; bool: as in the uint8 case, where 1 is used for true and 0 for false -(defmethod enc :bool - [{:keys [value]}] - (left-pad (if value "1" "0"))) - -;; int: enc(X) is the big-endian two’s complement encoding of X, padded on the -;; higher-order (left) side with 0xff for negative X and with zero bytes for -;; positive X such that the length is 32 bytes. -(defmethod enc :int - [{:keys [value]}] - (to-two-complement value)) - -;; uint: enc(X) is the big-endian encoding of X, padded on the -;; higher-order (left) side with zero-bytes such that the length is 32 bytes. -(defmethod enc :uint - [{:keys [value]}] - (left-pad (number-to-hex value))) - -;; address: as in the uint160 case -(defmethod enc :address - [{:keys [value]}] - (when (string? value) - (left-pad (string/replace value "0x" "")))) - -;; bytes, of length k (which is assumed to be of type uint256): -;; enc(X) = enc(k) pad_right(X), i.e. the number of bytes is encoded as a -;; uint256 followed by the actual value of X as a byte sequence, -;; followed by the minimum number of zero-bytes such that len(enc(X)) -;; is a multiple of 32. -;; bytes: enc(X) is the sequence of bytes in X padded with trailing -;; zero-bytes to a length of 32 bytes. -(defmethod enc :bytes - [{:keys [value dynamic?]}] - ;; in the examples of the abi specifications strings are passed for - ;; bytes parameters, in our ens resolver we pass encoded bytes directly - ;; for namehash, this handles both cases by checking if the value is already - ;; hex - (let [encoded-value? (is-hex? value) - encoded-value (if encoded-value? - (subs value 2) - (utf8-to-hex value))] - (str (when dynamic? (enc {:type :int :value (/ (count encoded-value) 2)})) - (right-pad encoded-value)))) - -;; string: enc(X) = enc(enc_utf8(X)), i.e. X is utf-8 encoded and this -;; value is interpreted as of bytes type and encoded further. -;; Note that the length used in this subsequent encoding is the number -;; of bytes of the utf-8 encoded string, not its number of characters. -(defmethod enc :string - [{:keys [value dynamic?]}] - (enc {:type :bytes :value value :dynamic? dynamic?})) - -;; fixedx: enc(X) is enc(X * 10**N) where X * 10**N is -;; interpreted as a int256. -(defmethod enc :fixed - [{:keys [value power] - :or {power 18}}] - (enc {:type :int - :value (* value (Math/pow 10 power))})) - -;; ufixed: as in the ufixed128x18 case -(defmethod enc :ufixed - [{:keys [value power] - :or {power 18}}] - (enc {:type :uint - :value (* value (Math/pow 10 power))})) - -;; T[k] for any T and k: -;; enc(X) = enc((X[0], ..., X[k-1])) -;; i.e. it is encoded as if it were a tuple with k elements of the same type. - -;; T[] where X has k elements (k is assumed to be of type uint256): -;; enc(X) = enc(k) enc([X[0], ..., X[k-1]]) -;; i.e. it is encoded as if it were an array of static size k, -;; prefixed with the number of elements. -(defmethod enc :array - [{:keys [value dynamic? array-of]}] - (str (when dynamic? - (enc {:type :int - :value (count value)})) - (enc {:type :tuple - :value (map #(assoc array-of :value %) - value)}))) - -;; (T1,...,Tk) for k >= 0 and any types T1, …, Tk -;; enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k)) -;; where X = (X(1), ..., X(k)) - -;; for Ti being static type: -;; head(X(i)) = enc(X(i)) -;; tail(X(i)) = "" (the empty string) - -;; for dynamic types: -;; head(X(i)) = enc(len(head(X(1)) ... head(X(k)) -;; tail(X(1)) ... tail(X(i-1)) )) -;; tail(X(i)) = enc(X(i)) - -;; Note that in the dynamic case, head(X(i)) is well-defined since -;; the lengths of the head parts only depend on the types and not -;; the values. Its value is the offset of the beginning of tail(X(i)) -;; relative to the start of enc(X). -(defmethod enc :tuple - [{:keys [value]}] - (let [[len x] (reduce - (fn [[len acc] {:keys [dynamic?] :as x}] - (let [enc-x (enc x)] - (if dynamic? - [(+ len 32) - (conj acc (assoc x :tail enc-x))] - [(+ len (/ (count enc-x) 2)) - (conj acc (assoc x :head enc-x))]))) - [0 []] - value) - [_ heads tails] (reduce (fn [[len heads tails] {:keys [head tail]}] - (if tail - [(+ len (/ (count tail) 2)) - (conj heads (enc {:type :int :value len})) - (conj tails tail)] - [len - (conj heads head) - tails])) - [len [] []] - x)] - (apply str (concat heads tails)))) - -;; -;; Parser for method signatures -;; - -(spec/def ::params (spec/* (spec/cat ::param ::param - ::separator (spec/? ::comma)))) - -(spec/def ::param (spec/cat ::type ::string - ::size (spec/? ::number) - ::x (spec/? ::x) - ::power (spec/? ::number) - ::array (spec/* ::array))) - -(spec/def ::array (spec/cat ::open-bracket ::open-bracket - ::size (spec/? ::number) - ::close-bracket ::close-bracket)) - -(spec/def ::x #{\x}) - -(spec/def ::open-bracket #{\[}) - -(spec/def ::close-bracket #{\]}) - -(spec/def ::comma #{\,}) - -(spec/def ::number int?) - -(spec/def ::string string?) - -(defn- single-char [code] - (when-let [m (#{\, \[ \] \x} (first code))] - [1 m])) - -(defn- number [code] - (when-let [m (re-find #"^[0-9]+" code)] - [(count m) (js/parseInt m)])) - -(defn- string [s] - (when-let [m (re-find #"^[a-z]+" s)] - [(count m) m])) - -(defn tokenise [code] - (when (seq code) - (if-let [[len token] (or (string code) - (single-char code) - (number code))] - (cons token (tokenise (subs code len))) - (throw (ex-info "Unexpected token" {:code code}))))) - -;; Definition: The following types are called “dynamic”: -;; - bytes -;; - string -;; - T[] for any T -;; - T[k] for any dynamic T and any k >= 0 -;; - (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k -(defn parse-param - "Takes a parsed parameter and returns a parameter that can - be encoded by associng the :dynamic? key for dynamic parameters - and recursively defining arrays" - [{::keys [type size array power] :as param}] - (if array - (let [{::keys [size]} (last array)] - {:type :array - :dynamic? (nil? size) - :array-of (parse-param (update param ::array butlast))}) - (let [type (keyword type) - param {:type type - :dynamic? (or (= type :string) - (and (= type :bytes) - (nil? size))) - :size size}] - (if power - (assoc param :power power) - param)))) - -(defn parse-params [method-signature] - (let [tokens (tokenise (second (re-find #"\((.*)\)" method-signature))) - params (spec/conform ::params tokens)] - (if (spec/invalid? params) - (spec/explain-data ::params tokens) - (map #(parse-param (::param %)) - params)))) - -(defn signature->method-id [signature] - (apply str (take 10 (ethereum/sha3 signature)))) - -(defn encode [method params] - (let [method-id (signature->method-id method) - params (map #(assoc %1 :value %2) - (parse-params method) - 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 [number (hex-to-number (subs hex 0 64)) - len (* (if (nil? number) 0 number) 2)] - (substr hex 64 len))) - -(defn dyn-hex-to-value [hex type] - (cond - (string/starts-with? type "bytes") - (str "0x" (hex-to-bytes hex)) - - (string/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") - (string/starts-with? type "uint") (hex-to-number hex) - (string/starts-with? type "int") (hex-to-number hex) - (string/starts-with? type "address") (str "0x" (subs hex (- (count hex) 40))) - (string/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) - (string/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] - (when bytes - (let [bytes (subs bytes 2)] - (when-not (empty? bytes) - (let [offsets (get-offsets types)] - (map #(when-not (= "0x" %) %) - (map (dec-type bytes) offsets types))))))) diff --git a/src/status_im/ethereum/abi_spec_test.cljs b/src/status_im/ethereum/abi_spec_test.cljs deleted file mode 100644 index 7070d57a5c..0000000000 --- a/src/status_im/ethereum/abi_spec_test.cljs +++ /dev/null @@ -1,69 +0,0 @@ -(ns status-im.ethereum.abi-spec-test - (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.ethereum.abi-spec :as abi-spec])) - -(deftest hex-to-number - (testing "hex number is less than 53 bits, it returns a number") - (is (= (abi-spec/hex-to-number "9") - 9)) - (is (= (abi-spec/hex-to-number "99999999") - 2576980377)) - (testing "hex number is less than 53 bits, it returns a string") - (is (= (abi-spec/hex-to-number "9999999999999999") - "11068046444225730969"))) - -(deftest enc - (is (= "000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" (abi-spec/enc {:type :address :value "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"})))) - -(deftest test-encode - (is (= (abi-spec/encode "baz(uint32,bool)" [69 true]) - "0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001")) - - (is (= (abi-spec/encode "bar(bytes3[2])" [["abc" "def"]]) - "0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000")) - - (is (= (abi-spec/encode "sam(bytes,bool,uint256[])" ["dave" true [1 2 3]]) - "0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003")) - - (is (= (abi-spec/encode "f(uint256,uint32[],bytes10,bytes)" [0x123 [0x456 0x789] "1234567890" "Hello, world!"]) - "0x8be6524600000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000")) - - (is (= (abi-spec/encode "g(uint[][],string[])" [[[1 2] [3]] ["one" "two" "three"]]) - "0xad6a3446000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000036f6e650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000374776f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057468726565000000000000000000000000000000000000000000000000000000")) - - (is (= (abi-spec/encode "getExpectedRate(address,address,uint256)" ["0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" 1000]) - "0x809a9e55000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000000000000003e8"))) - -(deftest test-decode - (is (= (abi-spec/decode "0x000000000000000000000000000000000000000000000000000000005bc741cd00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000013b86dbf1a83c9e6a492914a0ee39e8a5b7eb60700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147000000000000000000000000000000000000" - ["uint256" "bytes" "address" "uint256" "uint256"]) - '(1539785165 - "0x516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147" - "0x13b86dbf1a83c9e6a492914a0ee39e8a5b7eb607" - 0 - 0))) - - (is (= (abi-spec/decode "0x0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000d7621dc58210001" - ["uint256" "uint256"]) - '("1000000000000000000" "970000000000000001"))) - - (is (= (abi-spec/decode "0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000000000000003e8" - ["address" "address" "uint256"]) - '("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" 1000))) - - (is (= (abi-spec/decode "0x00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001" - ["uint32" "bool"]) - '(69 true))) - - (is (= (map abi-spec/hex-to-utf8 - (first (abi-spec/decode "0x61626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000" - ["bytes3[2]"]))) - '("abc" "def"))) - - (is (= (abi-spec/decode "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003" - ["string" "bool" "uint256[]"]) - '("dave" true [1 2 3]))) - - (is (= (abi-spec/decode "0x00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000" - ["uint" "uint32[]" "bytes10" "bytes"]) - '(291 [1110 1929] "31323334353637383930" "0x48656c6c6f2c20776f726c6421")))) diff --git a/src/status_im/ethereum/decode.cljs b/src/status_im/ethereum/decode.cljs index b9300be306..695f746f63 100644 --- a/src/status_im/ethereum/decode.cljs +++ b/src/status_im/ethereum/decode.cljs @@ -1,10 +1,8 @@ (ns status-im.ethereum.decode - (:require [status-im.ethereum.abi-spec :as abi-spec])) + (:require [status-im.utils.money :as money])) (defn uint [hex] - (first (abi-spec/decode hex ["uint"]))) - -(defn string - [hex] - (first (abi-spec/decode hex ["string"]))) + (let [n (money/bignumber hex)] + (when n + (.toString n 10)))) diff --git a/src/status_im/ethereum/ens.cljs b/src/status_im/ethereum/ens.cljs index d477296a65..38cdf3b61e 100644 --- a/src/status_im/ethereum/ens.cljs +++ b/src/status_im/ethereum/ens.cljs @@ -1,7 +1,7 @@ (ns status-im.ethereum.ens (:require [clojure.string :as string] [status-im.ethereum.json-rpc :as json-rpc] - [status-im.ethereum.abi-spec :as abi-spec])) + [status-im.native-module.core :as status])) ;; this is the addresses of ens registries for the different networks (def ens-registries @@ -65,7 +65,7 @@ :params [chain-id ens-name] :on-success ;;NOTE: returns a timestamp in s and we want ms - #(cb (* (js/Number (abi-spec/hex-to-number %)) 1000))})) + #(cb (* (js/Number (status/hex-to-number %)) 1000))})) (defn register-prepare-tx [chain-id from ens-name pubkey cb] diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 612d3590bb..b8f272af03 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -391,6 +391,33 @@ (log/debug "[native-module] identicon") (.identicon ^js (status) seed)) +(defn encode-transfer + [to-norm amount-hex] + (log/debug "[native-module] encode-transfer") + (.encodeTransfer ^js (status) to-norm amount-hex)) + +(defn encode-function-call + [method params] + (log/debug "[native-module] encode-function-call") + (.encodeFunctionCall ^js (status) method (types/clj->json params))) + +(defn decode-parameters + [bytes-string types] + (log/debug "[native-module] decode-parameters") + (let [json-str (.decodeParameters ^js (status) (types/clj->json {:bytesString bytes-string :types types}))] + (types/json->clj json-str))) + +(defn hex-to-number + [hex] + (log/debug "[native-module] hex-to-number") + (let [json-str (.hexToNumber ^js (status) hex)] + (types/json->clj json-str))) + +(defn number-to-hex + [num] + (log/debug "[native-module] number-to-hex") + (.numberToHex ^js (status) (str num))) + (defn identicon-async "Generate a icon based on a string, asynchronously" [seed callback] diff --git a/src/status_im/signing/core.cljs b/src/status_im/signing/core.cljs index 00d1369b22..d5fb5f24b6 100644 --- a/src/status_im/signing/core.cljs +++ b/src/status_im/signing/core.cljs @@ -2,7 +2,6 @@ (:require [clojure.string :as string] [re-frame.core :as re-frame] [status-im.constants :as constants] - [status-im.ethereum.abi-spec :as abi-spec] [status-im.ethereum.core :as ethereum] [status-im.ethereum.eip55 :as eip55] [status-im.ethereum.json-rpc :as json-rpc] @@ -105,16 +104,16 @@ (sign-message cofx) (let [tx-obj-to-send (merge tx-obj (when gas - {:gas (str "0x" (abi-spec/number-to-hex gas))}) + {:gas (str "0x" (status/number-to-hex gas))}) (when gasPrice - {:gasPrice (str "0x" (abi-spec/number-to-hex gasPrice))}) + {:gasPrice (str "0x" (status/number-to-hex gasPrice))}) (when nonce - {:nonce (str "0x" (abi-spec/number-to-hex nonce))}) + {:nonce (str "0x" (status/number-to-hex nonce))}) (when maxPriorityFeePerGas - {:maxPriorityFeePerGas (str "0x" (abi-spec/number-to-hex + {:maxPriorityFeePerGas (str "0x" (status/number-to-hex (js/parseInt maxPriorityFeePerGas)))}) (when maxFeePerGas - {:maxFeePerGas (str "0x" (abi-spec/number-to-hex + {:maxFeePerGas (str "0x" (status/number-to-hex (js/parseInt maxFeePerGas)))}))] (when-not in-progress? {:db (update db :signing/sign assoc :error nil :in-progress? true) @@ -174,7 +173,7 @@ (let [{:keys [symbol decimals] :as token} (tokens/address->token (:wallet/all-tokens db) to)] (when (and token data (string? data)) (when-let [type (get-method-type data)] - (let [[address value _] (abi-spec/decode + (let [[address value _] (status/decode-parameters (str "0x" (subs data 10)) (if (= type :approve-and-call) ["address" "uint256" "bytes"] ["address" "uint256"]))] (when (and address value) @@ -449,7 +448,7 @@ {:events [:wallet.ui/sign-transaction-button-clicked-from-chat]} [{:keys [db] :as cofx} {:keys [to amount from token]}] (let [{:keys [symbol address]} token - amount-hex (str "0x" (abi-spec/number-to-hex amount)) + amount-hex (str "0x" (status/number-to-hex amount)) to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) from-address (:address from) identity (:current-chat-id db) @@ -468,9 +467,7 @@ :from from-address :chat-id identity :command? true - :data (abi-spec/encode - "transfer(address,uint256)" - [to-norm amount-hex])})})) + :data (status/encode-transfer to-norm amount-hex)})})) {:db db ::json-rpc/call [{:method "wakuext_requestAddressForTransaction" @@ -487,7 +484,7 @@ [{:keys [db] :as cofx} {:keys [amount from token]}] (let [{:keys [request-parameters chat-id]} (:wallet/prepare-transaction db) {:keys [symbol address]} token - amount-hex (str "0x" (abi-spec/number-to-hex amount)) + amount-hex (str "0x" (status/number-to-hex amount)) to-norm (:address request-parameters) from-address (:address from)] (fx/merge cofx @@ -507,15 +504,13 @@ :command? true :message-id (:id request-parameters) :chat-id chat-id - :data (abi-spec/encode - "transfer(address,uint256)" - [to-norm amount-hex])})}))))) + :data (status/encode-transfer to-norm amount-hex)})}))))) (fx/defn sign-transaction-button-clicked {:events [:wallet.ui/sign-transaction-button-clicked]} [{:keys [db] :as cofx} {:keys [to amount from token gas gasPrice maxFeePerGas maxPriorityFeePerGas]}] (let [{:keys [symbol address]} token - amount-hex (str "0x" (abi-spec/number-to-hex amount)) + amount-hex (str "0x" (status/number-to-hex amount)) to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) from-address (:address from)] (fx/merge cofx @@ -535,9 +530,7 @@ {:to to-norm :value amount-hex} {:to (ethereum/normalized-hex address) - :data (abi-spec/encode - "transfer(address,uint256)" - [to-norm amount-hex])}))})))) + :data (status/encode-transfer to-norm amount-hex)}))})))) (re-frame/reg-fx :signing/get-transaction-by-hash-fx diff --git a/src/status_im/signing/core_test.cljs b/src/status_im/signing/core_test.cljs index 45a9637985..a9fbb52f32 100644 --- a/src/status_im/signing/core_test.cljs +++ b/src/status_im/signing/core_test.cljs @@ -1,15 +1,15 @@ (ns status-im.signing.core-test (:require [cljs.test :refer-macros [deftest is testing]] [status-im.signing.core :as signing] - [status-im.ethereum.abi-spec :as abi-spec])) + [status-im.native-module.core :as status])) (deftest signing-test (testing "showing sheet" (let [to "0x2f88d65f3cb52605a54a833ae118fb1363acccd2" contract "0xc55cf4b03948d7ebc8b9e8bad92643703811d162" - amount1-hex (str "0x" (abi-spec/number-to-hex "10000000000000000000")) - amount2-hex (str "0x" (abi-spec/number-to-hex "100000000000000000000")) - data (abi-spec/encode "transfer(address,uint256)" [to amount2-hex]) + amount1-hex (str "0x" (status/number-to-hex "10000000000000000000")) + amount2-hex (str "0x" (status/number-to-hex "100000000000000000000")) + data (status/encode-transfer to amount2-hex) first-tx {:tx-obj {:to to :from nil :value amount1-hex}} diff --git a/src/status_im/signing/keycard.cljs b/src/status_im/signing/keycard.cljs index 3e427fdba5..e9b0a56a25 100644 --- a/src/status_im/signing/keycard.cljs +++ b/src/status_im/signing/keycard.cljs @@ -1,6 +1,5 @@ (ns status-im.signing.keycard (:require [re-frame.core :as re-frame] - [status-im.ethereum.abi-spec :as abi-spec] [status-im.native-module.core :as status] [status-im.utils.fx :as fx] [status-im.i18n.i18n :as i18n] @@ -36,15 +35,15 @@ :message-id message-id :command? command?} maxPriorityFeePerGas - (assoc :maxPriorityFeePerGas (str "0x" (abi-spec/number-to-hex + (assoc :maxPriorityFeePerGas (str "0x" (status/number-to-hex (js/parseInt maxPriorityFeePerGas)))) maxFeePerGas - (assoc :maxFeePerGas (str "0x" (abi-spec/number-to-hex + (assoc :maxFeePerGas (str "0x" (status/number-to-hex (js/parseInt maxFeePerGas)))) gas - (assoc :gas (str "0x" (abi-spec/number-to-hex gas))) + (assoc :gas (str "0x" (status/number-to-hex gas))) gasPrice - (assoc :gasPrice (str "0x" (abi-spec/number-to-hex gasPrice))) + (assoc :gasPrice (str "0x" (status/number-to-hex gasPrice))) data (assoc :data data) nonce diff --git a/src/status_im/ui/screens/network_info/views.cljs b/src/status_im/ui/screens/network_info/views.cljs index bebf3c1198..4bf2cb549a 100644 --- a/src/status_im/ui/screens/network_info/views.cljs +++ b/src/status_im/ui/screens/network_info/views.cljs @@ -4,8 +4,8 @@ [reagent.core :as reagent] [status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.decode :as decode] - [status-im.ethereum.abi-spec :as abi-spec] - [status-im.utils.datetime :as time])) + [status-im.utils.datetime :as time] + [status-im.native-module.core :as status])) (defn get-block [block callback] (json-rpc/call @@ -31,7 +31,7 @@ (fn [res] (reset! latest-block res) (get-block - (str "0x" (abi-spec/number-to-hex + (str "0x" (status/number-to-hex (last-loaded-block-number))) (fn [res] (reset! last-loaded-block res))))))] diff --git a/src/status_im/utils/test.cljs b/src/status_im/utils/test.cljs index fb8ff1ad33..687253e621 100644 --- a/src/status_im/utils/test.cljs +++ b/src/status_im/utils/test.cljs @@ -79,11 +79,29 @@ (.initKeystore native-status (str test-dir "/keystore/" key-uid)))) + :identicon (fn [pk] (.identicon native-status pk)) + + :encodeTransfer (fn [to-norm amount-hex] + (.encodeTransfer native-status to-norm amount-hex)) + + :encodeFunctionCall (fn [method params-json] + (.encodeFunctionCall native-status method params-json)) + + :decodeParameters (fn [decode-param-json] + (.decodeParameters native-status decode-param-json)) + + :hexToNumber (fn [hex] + (.hexToNumber native-status hex)) + + :numberToHex (fn [num-str] + (.numberToHex native-status num-str)) + :validateMnemonic (fn [json callback] (callback (.validateMnemonic native-status json))) + :startLocalNotifications identity})) diff --git a/src/status_im/utils/varint.cljs b/src/status_im/utils/varint.cljs deleted file mode 100644 index d7a673afce..0000000000 --- a/src/status_im/utils/varint.cljs +++ /dev/null @@ -1,52 +0,0 @@ -(ns ^{:doc "Implementation of varint based on https://github.com/chrisdickinson/varint"} - status-im.utils.varint - (:require [status-im.ethereum.abi-spec :as abi-spec] - ["web3-utils" :as utils])) - -(def most-significant-bit 0x80) -(def biggest-int-per-byte 0x7F) -(def biggest-int 2147483648) ;; 2^31 - -(defn encode [num] - (loop [num num - out []] - (if (>= num 128) - (recur (if (>= num biggest-int) - (/ num 128) - (bit-shift-right num 7)) - (conj out (bit-or (bit-and num 0xFF) - most-significant-bit))) - (conj out (bit-or num 0))))) - -(defn encode-hex [num] - (reduce (fn [hex current-bit] - (str hex - (.leftPad utils - (abi-spec/number-to-hex current-bit) - 2))) - "" - (encode num))) - -(defn add-b-to-res - [res b shift] - (+ res (if (< shift 28) - (bit-shift-left (bit-and b biggest-int-per-byte) - shift) - (* (bit-and b biggest-int-per-byte) - shift shift)))) - -(defn decode [buf] - (loop [res 0 - shift 0 - [b & rest-buf] buf] - (if (>= b most-significant-bit) - (recur (add-b-to-res res b shift) - (+ shift 7) - rest-buf) - (add-b-to-res res b shift)))) - -(defn decode-hex [hex] - (->> hex - (partition 2) - (mapv #(abi-spec/hex-to-number (apply str %))) - decode)) diff --git a/src/status_im/utils/varint_test.cljs b/src/status_im/utils/varint_test.cljs deleted file mode 100644 index 1fb25777e9..0000000000 --- a/src/status_im/utils/varint_test.cljs +++ /dev/null @@ -1,28 +0,0 @@ -(ns status-im.utils.varint-test - (:require [cljs.test :refer-macros [deftest is]] - [status-im.utils.varint :as varint])) - -(deftest encode - (is (= (varint/encode-hex 0x0) - "00")) - (is (= (varint/encode-hex 0x70) - "70")) - (is (= (varint/encode-hex 0xe3) - "e301"))) - -(deftest decode - (is (= (varint/decode-hex "00") - 0x0)) - (is (= (varint/decode-hex "70") - 0x70)) - (is (= (varint/decode-hex "e301") - 0xe3))) - -(defn test-roundtrip [n] - (= (varint/decode-hex (varint/encode-hex n)) - n)) - -(deftest roundtrip - (is (test-roundtrip 0)) - (is (test-roundtrip 23948)) - (is (test-roundtrip 2684453))) diff --git a/status-go-version.json b/status-go-version.json index cd9e19fa58..c8e623fa6d 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,7 +3,7 @@ "_comment": "Instead use: scripts/update-status-go.sh ", "owner": "status-im", "repo": "status-go", - "version": "v0.109.4", - "commit-sha1": "6f93913be5559852ad456827beb26a7f4ccc2509", - "src-sha256": "08aa2x38739wg6hrcv8rxmc2rhmv2nr3jnh9y5wqrf2hldvprplx" + "version": "v0.110.1", + "commit-sha1": "a182f3e699f290776a1f1b3df8e2341c67b3f814", + "src-sha256": "1hrqmm4db4vbgwjmvqzbilza6fwgb2hns1bsx4vzdpvjqfy3akrv" }