@@ -0,0 +1,982 @@
1+:: ethereum: utilities
2+::
3+=, ethereum-types
4+|%
5+:: deriving and using ethereum keys
6+::
7+++ key
8+ |%
9+ ++ address-from-pub
10+ =, keccak:crypto
11+ |= pub=@
12+ %+ end [3 20]
13+ %+ keccak-256 64
14+ (rev 3 64 pub)
15+ ::
16+ ++ address-from-prv
17+ (cork pub-from-prv address-from-pub)
18+ ::
19+ ++ pub-from-prv
20+ =, secp256k1:secp:crypto
21+ |= prv=@
22+ %- serialize-point
23+ (priv-to-pub prv)
24+ ::
25+ ++ sign-typed-transaction
26+ |= [tx=typed-transaction:rpc pk=@]
27+ ^- @ux
28+ =- (cat 3 - -.tx)
29+ ?- -.tx
30+ %0x0 (sign-transaction +.tx pk)
31+ %0x2 (sign-transaction-1559 +.tx pk)
32+ ==
33+ ::
34+ ++ sign-transaction
35+ =, crypto
36+ |= [tx=transaction:rpc pk=@]
37+ |^ ^- @ux
38+ :: hash the raw transaction data
39+ =/ hash=@
40+ %- keccak-256:keccak
41+ =+ dat=(encode chain-id.tx 0 0)
42+ =+ wid=(met 3 dat)
43+ [wid (rev 3 wid dat)]
44+ :: sign transaction hash with private key
45+ =+ (ecdsa-raw-sign:secp256k1:secp hash pk)
46+ :: complete transaction is raw data, with r and s
47+ :: taken from the signature, and v as per eip-155
48+ (encode :(add (mul chain-id.tx 2) 35 v) r s)
49+ ::
50+ ++ encode
51+ |= [v=@ r=@ s=@]
52+ %+ encode:rlp %l
53+ tx(to b+20^to.tx, chain-id [v r s ~])
54+ --
55+ ::
56+ ++ sign-transaction-1559
57+ =, crypto
58+ |= [tx=transaction-1559:rpc pk=@]
59+ |^ ^- @ux
60+ =; hash=@
61+ =+ (ecdsa-raw-sign:secp256k1:secp hash pk)
62+ ::NOTE we retrieve y's parity from the v value
63+ (encode-1559 ~ (end 0 v) r s)
64+ :: hash the raw transaction data including leading 0x2
65+ %- keccak-256:keccak
66+ =+ dat=(cat 3 (encode-1559 ~) 0x2)
67+ =+ wid=(met 3 dat)
68+ [wid (rev 3 wid dat)]
69+ ::
70+ ++ encode-1559
71+ |= sig=(unit [y=@ r=@ s=@])
72+ %+ encode:rlp %l
73+ =, tx
74+ :* chain-id
75+ nonce
76+ max-priority-gas-fee
77+ max-gas-fee
78+ gas
79+ b+20^to
80+ value
81+ data
82+ ::
83+ :- %l
84+ %+ turn ~(tap by access-list)
85+ |= [a=address b=(list @ux)]
86+ l+~[b+20^a l+(turn b |=(c=@ux b+32^c))]
87+ ::
88+ ?~ sig ~
89+ ~[y r s]:u.sig
90+ ==
91+ --
92+ --
93+::
94+:: rlp en/decoding
95+::NOTE https://eth.wiki/en/fundamentals/rlp
96+::
97+++ rlp
98+ |%
99+ ::NOTE rlp encoding doesn't really care about leading zeroes,
100+ :: but because we need to disinguish between no-bytes zero
101+ :: and one-byte zero (and also empty list) we end up with
102+ :: this awful type...
103+ +$ item
104+ $@ @
105+ $% [%l l=(list item)]
106+ [%b b=byts]
107+ ==
108+ :: +encode-atoms: encode list of atoms as a %l of %b items
109+ ::
110+ ++ encode-atoms ::NOTE deprecated
111+ |= l=(list @)
112+ ^- @
113+ (encode l+l)
114+ ::
115+ ++ encode
116+ |= in=item
117+ |^ ^- @
118+ ?- in
119+ @
120+ $(in [%b (met 3 in) in])
121+ ::
122+ [%b *]
123+ ?: &(=(1 wid.b.in) (lte dat.b.in 0x7f))
124+ dat.b.in
125+ =- (can 3 ~[b.in [(met 3 -) -]])
126+ (encode-length wid.b.in 0x80)
127+ ::
128+ [%l *]
129+ :: we +can because b+1^0x0 encodes to 0x00
130+ ::
131+ =/ l=(list byts)
132+ %+ turn l.in
133+ |= ni=item
134+ =+ (encode ni)
135+ [(max 1 (met 3 -)) -]
136+ %+ can 3
137+ %- flop
138+ =- [(met 3 -)^- l]
139+ (encode-length (roll (turn l head) add) 0xc0)
140+ ==
141+ ::
142+ ++ encode-length
143+ |= [len=@ off=@]
144+ ?: (lth len 56) (add len off)
145+ =- (cat 3 len -)
146+ :(add (met 3 len) off 55)
147+ --
148+ :: +decode-atoms: decode expecting a %l of %b items, producing atoms within
149+ ::
150+ ++ decode-atoms
151+ |= dat=@
152+ ^- (list @)
153+ =/ i=item (decode dat)
154+ ~| [%unexpected-data i]
155+ ?> ?=(%l -.i)
156+ %+ turn l.i
157+ |= i=item
158+ ~| [%unexpected-list i]
159+ ?> ?=(%b -.i)
160+ dat.b.i
161+ ::
162+ ++ decode
163+ |= dat=@
164+ ^- item
165+ =/ bytes=(list @) (flop (rip 3 dat))
166+ =? bytes ?=(~ bytes) ~[0]
167+ |^ item:decode-head
168+ ::
169+ ++ decode-head
170+ ^- [done=@ud =item]
171+ ?~ bytes
172+ ~| %rlp-unexpected-end
173+ !!
174+ =* byt i.bytes
175+ :: byte in 0x00-0x79 range encodes itself
176+ ::
177+ ?: (lte byt 0x79)
178+ :- 1
179+ [%b 1^byt]
180+ :: byte in 0x80-0xb7 range encodes string length
181+ ::
182+ ?: (lte byt 0xb7)
183+ =+ len=(sub byt 0x80)
184+ :- +(len)
185+ :- %b
186+ len^(get-value 1 len)
187+ :: byte in 0xb8-0xbf range encodes string length length
188+ ::
189+ ?: (lte byt 0xbf)
190+ =+ led=(sub byt 0xb7)
191+ =+ len=(get-value 1 led)
192+ :- (add +(led) len)
193+ :- %b
194+ len^(get-value +(led) len)
195+ :: byte in 0xc0-f7 range encodes list length
196+ ::
197+ ?: (lte byt 0xf7)
198+ =+ len=(sub byt 0xc0)
199+ :- +(len)
200+ :- %l
201+ %. len
202+ decode-list(bytes (slag 1 `(list @)`bytes))
203+ :: byte in 0xf8-ff range encodes list length length
204+ ::
205+ ?: (lte byt 0xff)
206+ =+ led=(sub byt 0xf7)
207+ =+ len=(get-value 1 led)
208+ :- (add +(led) len)
209+ :- %l
210+ %. len
211+ decode-list(bytes (slag +(led) `(list @)`bytes))
212+ ~| [%rip-not-bloq-3 `@ux`byt]
213+ !!
214+ ::
215+ ++ decode-list
216+ |= rem=@ud
217+ ^- (list item)
218+ ?: =(0 rem) ~
219+ =+ ^- [don=@ud =item] ::TODO =/
220+ decode-head
221+ :- item
222+ %= $
223+ rem (sub rem don)
224+ bytes (slag don bytes)
225+ ==
226+ ::
227+ ++ get-value
228+ |= [at=@ud to=@ud]
229+ ^- @
230+ (rep 3 (flop (swag [at to] bytes)))
231+ --
232+ --
233+::
234+:: abi en/decoding
235+::NOTE https://solidity.readthedocs.io/en/develop/abi-spec.html
236+::
237+++ abi
238+ => |%
239+ :: solidity types. integer bitsizes ignored
240+ ++ etyp
241+ $@ $? :: static
242+ %address %bool
243+ %int %uint
244+ %real %ureal
245+ :: dynamic
246+ %bytes %string
247+ ==
248+ $% :: static
249+ [%bytes-n n=@ud]
250+ :: dynamic
251+ [%array-n t=etyp n=@ud]
252+ [%array t=etyp]
253+ ==
254+ ::
255+ :: solidity-style typed data. integer bitsizes ignored
256+ ++ data
257+ $% [%address p=address]
258+ [%string p=tape]
259+ [%bool p=?]
260+ [%int p=@sd]
261+ [%uint p=@ud]
262+ [%real p=@rs]
263+ [%ureal p=@urs]
264+ [%array-n p=(list data)]
265+ [%array p=(list data)]
266+ [%bytes-n p=octs] ::TODO just @, because context knows length?
267+ [%bytes p=octs]
268+ ==
269+ --
270+ =, mimes:html
271+ |%
272+ :: encoding
273+ ::
274+ ++ encode-args
275+ :: encode list of arguments.
276+ ::
277+ |= das=(list data)
278+ ^- tape
279+ (encode-data [%array-n das])
280+ ::
281+ ++ encode-data
282+ :: encode typed data into ABI bytestring.
283+ ::
284+ |= dat=data
285+ ^- tape
286+ ?+ -.dat
287+ ~| [%unsupported-type -.dat]
288+ !!
289+ ::
290+ %array-n
291+ :: enc(X) = head(X[0]) ... head(X[k-1]) tail(X[0]) ... tail(X[k-1])
292+ :: where head and tail are defined for X[i] being of a static type as
293+ :: head(X[i]) = enc(X[i]) and tail(X[i]) = "" (the empty string), or as
294+ :: head(X[i]) = enc(len( head(X[0])..head(X[k-1])
295+ :: tail(X[0])..tail(X[i-1]) ))
296+ :: and tail(X[i]) = enc(X[i]) otherwise.
297+ ::
298+ :: so: if it's a static type, data goes in the head. if it's a dynamic
299+ :: type, a reference goes into the head and data goes into the tail.
300+ ::
301+ :: in the head, we first put a placeholder where references need to go.
302+ =+ hol=(reap 64 'x')
303+ =/ hes=(list tape)
304+ %+ turn p.dat
305+ |= d=data
306+ ?. (is-dynamic-type d) ^$(dat d)
307+ hol
308+ =/ tas=(list tape)
309+ %+ turn p.dat
310+ |= d=data
311+ ?. (is-dynamic-type d) ""
312+ ^$(dat d)
313+ :: once we know the head and tail, we can fill in the references in head.
314+ =- (weld nes `tape`(zing tas))
315+ ^- [@ud nes=tape]
316+ =+ led=(lent (zing hes))
317+ %+ roll hes
318+ |= [t=tape i=@ud nes=tape]
319+ :- +(i)
320+ :: if no reference needed, just put the data.
321+ ?. =(t hol) (weld nes t)
322+ :: calculate byte offset of data we need to reference.
323+ =/ ofs=@ud
324+ =- (div - 2) :: two hex digits per byte.
325+ %+ add led :: count head, and
326+ %- lent %- zing :: count all tail data
327+ (scag i tas) :: preceding ours.
328+ =+ ref=^$(dat [%uint ofs])
329+ :: shouldn't hit this unless we're sending over 2gb of data?
330+ ~| [%weird-ref-lent (lent ref)]
331+ ?> =((lent ref) (lent hol))
332+ (weld nes ref)
333+ ::
334+ %array :: where X has k elements (k is assumed to be of type uint256):
335+ :: enc(X) = enc(k) enc([X[1], ..., X[k]])
336+ :: i.e. it is encoded as if it were an array of static size k, prefixed
337+ :: with the number of elements.
338+ %+ weld $(dat [%uint (lent p.dat)])
339+ $(dat [%array-n p.dat])
340+ ::
341+ %bytes-n
342+ :: enc(X) is the sequence of bytes in X padded with zero-bytes to a
343+ :: length of 32.
344+ :: Note that for any X, len(enc(X)) is a multiple of 32.
345+ ~| [%bytes-n-too-long max=32 actual=p.p.dat]
346+ ?> (lte p.p.dat 32)
347+ (pad-to-multiple (render-hex-bytes p.dat) 64 %right)
348+ ::
349+ %bytes :: of length k (which is assumed to be of type uint256)
350+ :: enc(X) = enc(k) pad_right(X), i.e. the number of bytes is encoded as a
351+ :: uint256 followed by the actual value of X as a byte sequence, followed
352+ :: by the minimum number of zero-bytes such that len(enc(X)) is a
353+ :: multiple of 32.
354+ %+ weld $(dat [%uint p.p.dat])
355+ (pad-to-multiple (render-hex-bytes p.dat) 64 %right)
356+ ::
357+ %string
358+ :: enc(X) = enc(enc_utf8(X)), i.e. X is utf-8 encoded and this value is
359+ :: interpreted as of bytes type and encoded further. Note that the length
360+ :: used in this subsequent encoding is the number of bytes of the utf-8
361+ :: encoded string, not its number of characters.
362+ $(dat [%bytes (lent p.dat) (swp 3 (crip p.dat))])
363+ ::
364+ %uint
365+ :: enc(X) is the big-endian encoding of X, padded on the higher-order
366+ :: (left) side with zero-bytes such that the length is a multiple of 32
367+ :: bytes.
368+ (pad-to-multiple (render-hex-bytes (as-octs p.dat)) 64 %left)
369+ ::
370+ %bool
371+ :: as in the uint8 case, where 1 is used for true and 0 for false
372+ $(dat [%uint ?:(p.dat 1 0)])
373+ ::
374+ %address
375+ :: as in the uint160 case
376+ $(dat [%uint `@ud`p.dat])
377+ ==
378+ ::
379+ ++ is-dynamic-type
380+ |= a=data
381+ ?. ?=(%array-n -.a)
382+ ?=(?(%string %bytes %array) -.a)
383+ &(!=((lent p.a) 0) (lien p.a is-dynamic-type))
384+ ::
385+ :: decoding
386+ ::
387+ ++ decode-topics decode-arguments
388+ ::
389+ ++ decode-results
390+ :: rex: string of hex bytes with leading 0x.
391+ |* [rex=@t tys=(list etyp)]
392+ =- (decode-arguments - tys)
393+ %^ rut 9
394+ (rsh [3 2] rex)
395+ (curr rash hex)
396+ ::
397+ ++ decode-arguments
398+ |* [wos=(list @) tys=(list etyp)]
399+ =/ wos=(list @) wos :: get rid of tmi
400+ =| win=@ud
401+ =< (decode-from 0 tys)
402+ |%
403+ ++ decode-from
404+ |* [win=@ud tys=(list etyp)]
405+ ?~ tys !!
406+ =- ?~ t.tys dat
407+ [dat $(win nin, tys t.tys)]
408+ (decode-one win ~[i.tys])
409+ ::
410+ ++ decode-one
411+ ::NOTE we take (list etyp) even though we only operate on
412+ :: a single etyp as a workaround for urbit/arvo#673
413+ |* [win=@ud tys=(list etyp)]
414+ =- [nin dat]=- ::NOTE ^= regular form broken
415+ ?~ tys !!
416+ =* typ i.tys
417+ =+ wor=(snag win wos)
418+ ?+ typ
419+ ~| [%unsupported-type typ]
420+ !!
421+ ::
422+ ?(%address %bool %uint) :: %int %real %ureal
423+ :- +(win)
424+ ?- typ
425+ %address `@ux`wor
426+ %uint `@ud`wor
427+ %bool =(1 wor)
428+ ==
429+ ::
430+ %string
431+ =+ $(tys ~[%bytes])
432+ [nin (trip (swp 3 q.dat))]
433+ ::
434+ %bytes
435+ :- +(win)
436+ :: find the word index of the actual data.
437+ =/ lic=@ud (div wor 32)
438+ :: learn the bytelength of the data.
439+ =/ len=@ud (snag lic wos)
440+ (decode-bytes-n +(lic) len)
441+ ::
442+ [%bytes-n *]
443+ :- (add win +((div (dec n.typ) 32)))
444+ (decode-bytes-n win n.typ)
445+ ::
446+ [%array *]
447+ :- +(win)
448+ :: find the word index of the actual data.
449+ =. win (div wor 32)
450+ :: read the elements from their location.
451+ %- tail
452+ %^ decode-array-n ~[t.typ] +(win)
453+ (snag win wos)
454+ ::
455+ [%array-n *]
456+ (decode-array-n ~[t.typ] win n.typ)
457+ ==
458+ ::
459+ ++ decode-bytes-n
460+ |= [fro=@ud bys=@ud]
461+ ^- octs
462+ :: parse {bys} bytes from {fro}.
463+ :- bys
464+ %+ rsh
465+ :- 3
466+ =+ (mod bys 32)
467+ ?:(=(0 -) - (sub 32 -))
468+ %+ rep 8
469+ %- flop
470+ =- (swag [fro -] wos)
471+ +((div (dec bys) 32))
472+ ::
473+ ++ decode-array-n
474+ ::NOTE we take (list etyp) even though we only operate on
475+ :: a single etyp as a workaround for urbit/arvo#673
476+ ::NOTE careful! produces lists without type info
477+ =| res=(list)
478+ |* [tys=(list etyp) fro=@ud len=@ud]
479+ ^- [@ud (list)]
480+ ?~ tys !!
481+ ?: =(len 0) [fro (flop `(list)`res)]
482+ =+ (decode-one fro ~[i.tys]) :: [nin=@ud dat=*]
483+ $(res ^+(res [dat res]), fro nin, len (dec len))
484+ --
485+ --
486+::
487+:: communicating with rpc nodes
488+::NOTE https://github.com/ethereum/wiki/wiki/JSON-RPC
489+::
490+++ rpc
491+ :: types
492+ ::
493+ => =, abi
494+ =, format
495+ |%
496+ :: raw call data
497+ ++ call-data
498+ $: function=@t
499+ arguments=(list data)
500+ ==
501+ ::
502+ :: raw transaction data
503+ +$ typed-transaction
504+ $% [%0x0 transaction]
505+ [%0x2 transaction-1559]
506+ ==
507+ ::
508+ +$ transaction
509+ $: nonce=@ud
510+ gas-price=@ud
511+ gas=@ud
512+ to=address
513+ value=@ud
514+ data=@ux
515+ chain-id=@ux
516+ ==
517+ ::
518+ +$ transaction-1559
519+ $: chain-id=@ux
520+ nonce=@ud
521+ max-priority-gas-fee=@ud
522+ max-gas-fee=@ud
523+ gas=@ud
524+ to=address
525+ value=@ud
526+ data=@ux
527+ access-list=(jar address @ux)
528+ ==
529+ ::
530+ :: ethereum json rpc api
531+ ::
532+ :: supported requests.
533+ ++ request
534+ $% [%eth-block-number ~]
535+ [%eth-call cal=call deb=block]
536+ $: %eth-new-filter
537+ fro=(unit block)
538+ tob=(unit block)
539+ adr=(list address)
540+ top=(list ?(@ux (list @ux)))
541+ ==
542+ [%eth-get-block-by-number bon=@ud txs=?]
543+ [%eth-get-filter-logs fid=@ud]
544+ $: %eth-get-logs
545+ fro=(unit block)
546+ tob=(unit block)
547+ adr=(list address)
548+ top=(list ?(@ux (list @ux)))
549+ ==
550+ $: %eth-get-logs-by-hash
551+ has=@
552+ adr=(list address)
553+ top=(list ?(@ux (list @ux)))
554+ ==
555+ [%eth-get-filter-changes fid=@ud]
556+ [%eth-get-transaction-by-hash txh=@ux]
557+ [%eth-get-transaction-count adr=address =block]
558+ [%eth-get-balance adr=address =block]
559+ [%eth-get-transaction-receipt txh=@ux]
560+ [%eth-send-raw-transaction dat=@ux]
561+ ==
562+ ::
563+ ::TODO clean up & actually use
564+ ++ response
565+ $% ::TODO
566+ [%eth-new-filter fid=@ud]
567+ [%eth-get-filter-logs los=(list event-log)]
568+ [%eth-get-logs los=(list event-log)]
569+ [%eth-get-logs-by-hash los=(list event-log)]
570+ [%eth-got-filter-changes los=(list event-log)]
571+ [%eth-transaction-hash haz=@ux]
572+ ==
573+ ::
574+ ++ transaction-result
575+ $: block-hash=(unit @ux)
576+ block-number=(unit @ud)
577+ transaction-index=(unit @ud)
578+ from=@ux
579+ to=(unit @ux)
580+ input=@t
581+ ==
582+ ::
583+ ++ event-log
584+ $: :: null for pending logs
585+ $= mined %- unit
586+ $: input=(unit @ux)
587+ log-index=@ud
588+ transaction-index=@ud
589+ transaction-hash=@ux
590+ block-number=@ud
591+ block-hash=@ux
592+ removed=?
593+ ==
594+ ::
595+ address=@ux
596+ data=@t
597+ :: event data
598+ ::
599+ :: For standard events, the first topic is the event signature
600+ :: hash. For anonymous events, the first topic is the first
601+ :: indexed argument.
602+ :: Note that this does not support the "anonymous event with
603+ :: zero topics" case. This has dubious usability, and using
604+ :: +lest instead of +list saves a lot of ?~ checks.
605+ ::
606+ topics=(lest @ux)
607+ ==
608+ ::
609+ :: data for eth_call.
610+ ++ call
611+ $: from=(unit address)
612+ to=address
613+ gas=(unit @ud)
614+ gas-price=(unit @ud)
615+ value=(unit @ud)
616+ data=tape
617+ ==
618+ ::
619+ :: minimum data needed to construct a read call
620+ ++ proto-read-request
621+ $: id=(unit @t)
622+ to=address
623+ call-data
624+ ==
625+ ::
626+ :: block to operate on.
627+ ++ block
628+ $% [%number n=@ud]
629+ [%label l=?(%earliest %latest %pending)]
630+ ==
631+ --
632+ ::
633+ :: logic
634+ ::
635+ |%
636+ ++ encode-call
637+ |= call-data
638+ ^- tape
639+ ::TODO should this check to see if the data matches the function signature?
640+ =- :(weld "0x" - (encode-args arguments))
641+ %+ scag 8
642+ %+ render-hex-bytes 32
643+ %- keccak-256:keccak:crypto
644+ (as-octs:mimes:html function)
645+ ::
646+ :: building requests
647+ ::
648+ ++ json-request
649+ =, eyre
650+ |= [url=purl jon=json]
651+ ^- hiss
652+ :^ url %post
653+ %- ~(gas in *math)
654+ ~['Content-Type'^['application/json']~]
655+ (some (as-octt (en-json:html jon)))
656+ :: +light-json-request: like json-request, but for %l
657+ ::
658+ :: TODO: Exorcising +purl from our system is a much longer term effort;
659+ :: get the current output types for now.
660+ ::
661+ ++ light-json-request
662+ |= [url=purl:eyre jon=json]
663+ ^- request:http
664+ ::
665+ :* %'POST'
666+ (crip (en-purl:html url))
667+ ~[['content-type' 'application/json']]
668+ (some (as-octt (en-json:html jon)))
669+ ==
670+ ::
671+ ++ batch-read-request
672+ |= req=(list proto-read-request)
673+ ^- json
674+ a+(turn req read-request)
675+ ::
676+ ++ read-request
677+ |= proto-read-request
678+ ^- json
679+ %+ request-to-json id
680+ :+ %eth-call
681+ ^- call
682+ [~ to ~ ~ ~ `tape`(encode-call function arguments)]
683+ [%label %latest]
684+ ::
685+ ++ request-to-json
686+ =, enjs:format
687+ |= [riq=(unit @t) req=request]
688+ ^- json
689+ %- pairs
690+ =; r=[met=@t pas=(list json)]
691+ ::TODO should use request-to-json:rpc:jstd,
692+ :: and probably (fall riq -.req)
693+ :* jsonrpc+s+'2.0'
694+ method+s+met.r
695+ params+a+pas.r
696+ ::TODO would just jamming the req noun for id be a bad idea?
697+ ?~ riq ~
698+ [id+s+u.riq]~
699+ ==
700+ ?- -.req
701+ %eth-block-number
702+ ['eth_blockNumber' ~]
703+ ::
704+ %eth-call
705+ :- 'eth_call'
706+ :~ (eth-call-to-json cal.req)
707+ (block-to-json deb.req)
708+ ==
709+ ::
710+ %eth-new-filter
711+ :- 'eth_newFilter'
712+ :_ ~
713+ :- %o %- ~(gas by *(map @t json))
714+ =- (murn - same)
715+ ^- (list (unit (pair @t json)))
716+ :~ ?~ fro.req ~
717+ `['fromBlock' (block-to-json u.fro.req)]
718+ ::
719+ ?~ tob.req ~
720+ `['toBlock' (block-to-json u.tob.req)]
721+ ::
722+ ::NOTE tmi
723+ ?: =(0 (lent adr.req)) ~
724+ :+ ~ 'address'
725+ ?: =(1 (lent adr.req)) (tape (address-to-hex (snag 0 adr.req)))
726+ :- %a
727+ (turn adr.req (cork address-to-hex tape))
728+ ::
729+ ?~ top.req ~
730+ :+ ~ 'topics'
731+ (topics-to-json top.req)
732+ ==
733+ ::
734+ %eth-get-block-by-number
735+ :- 'eth_getBlockByNumber'
736+ :~ (tape (num-to-hex bon.req))
737+ b+txs.req
738+ ==
739+ ::
740+ %eth-get-filter-logs
741+ ['eth_getFilterLogs' (tape (num-to-hex fid.req)) ~]
742+ ::
743+ %eth-get-logs
744+ :- 'eth_getLogs'
745+ :_ ~
746+ :- %o %- ~(gas by *(map @t json))
747+ =- (murn - same)
748+ ^- (list (unit (pair @t json)))
749+ :~ ?~ fro.req ~
750+ `['fromBlock' (block-to-json u.fro.req)]
751+ ::
752+ ?~ tob.req ~
753+ `['toBlock' (block-to-json u.tob.req)]
754+ ::
755+ ?: =(0 (lent adr.req)) ~
756+ :+ ~ 'address'
757+ ?: =(1 (lent adr.req)) (tape (address-to-hex (snag 0 adr.req)))
758+ :- %a
759+ (turn adr.req (cork address-to-hex tape))
760+ ::
761+ ?~ top.req ~
762+ :+ ~ 'topics'
763+ (topics-to-json top.req)
764+ ==
765+ ::
766+ %eth-get-logs-by-hash
767+ :- 'eth_getLogs'
768+ :_ ~ :- %o
769+ %- ~(gas by *(map @t json))
770+ =- (murn - same)
771+ ^- (list (unit (pair @t json)))
772+ :~ `['blockHash' (tape (transaction-to-hex has.req))]
773+ ::
774+ ?: =(0 (lent adr.req)) ~
775+ :+ ~ 'address'
776+ ?: =(1 (lent adr.req)) (tape (address-to-hex (snag 0 adr.req)))
777+ :- %a
778+ (turn adr.req (cork address-to-hex tape))
779+ ::
780+ ?~ top.req ~
781+ :+ ~ 'topics'
782+ (topics-to-json top.req)
783+ ==
784+ ::
785+ %eth-get-filter-changes
786+ ['eth_getFilterChanges' (tape (num-to-hex fid.req)) ~]
787+ ::
788+ %eth-get-transaction-count
789+ :- 'eth_getTransactionCount'
790+ :~ (tape (address-to-hex adr.req))
791+ (block-to-json block.req)
792+ ==
793+ ::
794+ %eth-get-balance
795+ :- 'eth_getBalance'
796+ :~ (tape (address-to-hex adr.req))
797+ (block-to-json block.req)
798+ ==
799+ ::
800+ %eth-get-transaction-by-hash
801+ ['eth_getTransactionByHash' (tape (transaction-to-hex txh.req)) ~]
802+ ::
803+ %eth-get-transaction-receipt
804+ ['eth_getTransactionReceipt' (tape (transaction-to-hex txh.req)) ~]
805+ ::
806+ %eth-send-raw-transaction
807+ ['eth_sendRawTransaction' (tape (num-to-hex dat.req)) ~]
808+ ==
809+ ::
810+ ++ eth-call-to-json
811+ =, enjs:format
812+ |= cal=call
813+ ^- json
814+ :- %o %- ~(gas by *(map @t json))
815+ =- (murn - same)
816+ ^- (list (unit (pair @t json)))
817+ :~ ?~ from.cal ~
818+ `['from' (tape (address-to-hex u.from.cal))]
819+ ::
820+ `['to' (tape (address-to-hex to.cal))]
821+ ::
822+ ?~ gas.cal ~
823+ `['gas' (tape (num-to-hex u.gas.cal))]
824+ ::
825+ ?~ gas-price.cal ~
826+ `['gasPrice' (tape (num-to-hex u.gas-price.cal))]
827+ ::
828+ ?~ value.cal ~
829+ `['value' (tape (num-to-hex u.value.cal))]
830+ ::
831+ ?~ data.cal ~
832+ `['data' (tape data.cal)]
833+ ==
834+ ::
835+ ++ block-to-json
836+ |= dob=block
837+ ^- json
838+ ?- -.dob
839+ %number s+(crip '0' 'x' ((x-co:co 1) n.dob))
840+ %label s+l.dob
841+ ==
842+ ::
843+ ++ topics-to-json
844+ |= tos=(list ?(@ux (list @ux)))
845+ ^- json
846+ :- %a
847+ =/ ttj
848+ ;: cork
849+ (cury render-hex-bytes 32)
850+ prefix-hex
851+ tape:enjs:format
852+ ==
853+ %+ turn tos
854+ |= t=?(@ (list @))
855+ ?@ t
856+ ?: =(0 t) ~
857+ (ttj `@`t)
858+ a+(turn t ttj)
859+ ::
860+ :: parsing responses
861+ ::
862+ ::TODO ++ parse-response |= json ^- response
863+ ::
864+ ++ parse-hex-result
865+ |= j=json
866+ ^- @
867+ ?> ?=(%s -.j)
868+ (hex-to-num p.j)
869+ ::
870+ ++ parse-eth-new-filter-res parse-hex-result
871+ ::
872+ ++ parse-eth-block-number parse-hex-result
873+ ::
874+ ++ parse-transaction-hash parse-hex-result
875+ ::
876+ ++ parse-eth-get-transaction-count parse-hex-result
877+ ::
878+ ++ parse-eth-get-balance parse-hex-result
879+ ::
880+ ++ parse-event-logs
881+ (ar:dejs:format parse-event-log)
882+ ::
883+ ++ parse-event-log
884+ =, dejs:format
885+ |= log=json
886+ ^- event-log
887+ =- ((ot -) log)
888+ :~ =- ['logIndex'^(cu - (mu so))]
889+ |= li=(unit @t)
890+ ?~ li ~
891+ =- ``((ou -) log) ::TODO not sure if elegant or hacky.
892+ :~ 'logIndex'^(un (cu hex-to-num so))
893+ 'transactionIndex'^(un (cu hex-to-num so))
894+ 'transactionHash'^(un (cu hex-to-num so))
895+ 'blockNumber'^(un (cu hex-to-num so))
896+ 'blockHash'^(un (cu hex-to-num so))
897+ 'removed'^(uf | bo)
898+ ==
899+ ::
900+ address+(cu hex-to-num so)
901+ data+so
902+ ::
903+ =- topics+(cu - (ar so))
904+ |= r=(list @t)
905+ ^- (lest @ux)
906+ ?> ?=([@t *] r)
907+ :- (hex-to-num i.r)
908+ (turn t.r hex-to-num)
909+ ==
910+ ::
911+ ++ parse-transaction-result
912+ =, dejs:format
913+ |= jon=json
914+ ~| jon=jon
915+ ^- transaction-result
916+ =- ((ot -) jon)
917+ :~ 'blockHash'^_~ :: TODO: fails if maybe-num?
918+ 'blockNumber'^maybe-num
919+ 'transactionIndex'^maybe-num
920+ from+(cu hex-to-num so)
921+ to+maybe-num
922+ input+so
923+ ==
924+ ::
925+ ++ maybe-num
926+ =, dejs:format
927+ =- (cu - (mu so))
928+ |= r=(unit @t)
929+ ?~ r ~
930+ `(hex-to-num u.r)
931+ --
932+::
933+:: utilities
934+::TODO give them better homes!
935+::
936+++ num-to-hex
937+ |= n=@
938+ ^- tape
939+ %- prefix-hex
940+ ?: =(0 n)
941+ "0"
942+ %- render-hex-bytes
943+ (as-octs:mimes:html n)
944+::
945+++ address-to-hex
946+ |= a=address
947+ ^- tape
948+ %- prefix-hex
949+ (render-hex-bytes 20 `@`a)
950+::
951+++ transaction-to-hex
952+ |= h=@
953+ ^- tape
954+ %- prefix-hex
955+ (render-hex-bytes 32 h)
956+::
957+++ prefix-hex
958+ |= a=tape
959+ ^- tape
960+ ['0' 'x' a]
961+::
962+++ render-hex-bytes
963+ :: atom to string of hex bytes without 0x prefix and dots.
964+ |= a=octs
965+ ^- tape
966+ ((x-co:co (mul 2 p.a)) q.a)
967+::
968+++ pad-to-multiple
969+ |= [wat=tape mof=@ud wer=?(%left %right)]
970+ ^- tape
971+ =+ len=(lent wat)
972+ ?: =(0 len) (reap mof '0')
973+ =+ mad=(mod len mof)
974+ ?: =(0 mad) wat
975+ =+ tad=(reap (sub mof mad) '0')
976+ %- weld
977+ ?:(?=(%left wer) [tad wat] [wat tad])
978+::
979+++ hex-to-num
980+ |= a=@t
981+ (rash (rsh [3 2] a) hex)
982+--