Move abi-spec to status-go

6f93913b...a182f3e6
This commit is contained in:
frank 2022-08-26 16:43:04 +08:00 committed by Andrea Maria Piana
parent 3e7f7232fb
commit 00a503bf84
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
16 changed files with 306 additions and 587 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -386,6 +386,182 @@ void _Identicon(const FunctionCallbackInfo<Value>& args) {
}
void _EncodeTransfer(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> 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<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _EncodeFunctionCall(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> 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<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _DecodeParameters(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> 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<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _HexToNumber(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> 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<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _NumberToHex(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> 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<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _Logout(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
@ -1583,6 +1759,11 @@ void init(Local<Object> 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);

View File

@ -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<M>: enc(X) is the big-endian twos 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<M>: 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<M>: 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?}))
;; fixed<M>x<N>: 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)))))))

View File

@ -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"))))

View File

@ -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))))

View File

@ -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]

View File

@ -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]

View File

@ -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

View File

@ -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}}

View File

@ -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

View File

@ -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))))))]

View File

@ -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}))

View File

@ -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))

View File

@ -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)))

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"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"
}