From 61845e605ef8cceb915215a3bc222e62412d3643 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 17 Jun 2021 14:33:11 +1000 Subject: [PATCH 01/64] aqua: refine pill types Introduces a %boot-pill mark and associated types, to distinguish pills that can be used to boot a ship and the ivory pill. Changes aqua to receive this new %boot-pill mark --- pkg/arvo/app/aqua.hoon | 15 ++++++--------- pkg/arvo/gen/brass.hoon | 4 ++-- pkg/arvo/gen/solid.hoon | 4 ++-- pkg/arvo/lib/pill.hoon | 16 ++++++++++------ pkg/arvo/mar/boot-pill.hoon | 15 +++++++++++++++ 5 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 pkg/arvo/mar/boot-pill.hoon diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index b530003ae6..6659b16fc2 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -38,11 +38,7 @@ == :: XX temporarily shadowed, fix and remove :: - +$ pill - $: boot-ova=* - kernel-ova=(list unix-event) - userspace-ova=(list unix-event) - == + +$ pill boot-pill:pill-lib :: +$ fleet [ships=(map ship pier) azi=az-state] +$ pier @@ -86,7 +82,7 @@ =^ cards state ?+ mark ~|([%aqua-bad-mark mark] !!) %aqua-events (poke-aqua-events:ac !<((list aqua-event) vase)) - %pill (poke-pill:ac !<(pill vase)) + %boot-pill (poke-pill:ac !<(pill vase)) %noun (poke-noun:ac !<(* vase)) %azimuth-action (poke-azimuth-action:ac !<(azimuth-action vase)) == @@ -411,10 +407,11 @@ :: ?+ val ~|(%bad-noun-arg !!) [%swap-vanes vs=*] - ?> ?=([[%7 * %1 installed=*] ~] boot-ova.pil) - =. installed.boot-ova.pil + ?> ?=(^ boot-ova.pil) + ?> ?=([%7 * %1 installed=*] i.boot-ova.pil) + =. installed.i.boot-ova.pil %+ roll (,(list term) vs.val) - |= [v=term =_installed.boot-ova.pil] + |= [v=term =_installed.i.boot-ova.pil] %^ slum installed now.hid =/ vane ?+ v ~|([%unknown-vane v] !!) diff --git a/pkg/arvo/gen/brass.hoon b/pkg/arvo/gen/brass.hoon index b6aa3e4cbd..74f02651a8 100644 --- a/pkg/arvo/gen/brass.hoon +++ b/pkg/arvo/gen/brass.hoon @@ -12,8 +12,8 @@ arg=$@(~ [top=path ~]) ~ == -:- %noun -^- pill:pill +:- %boot-pill +^- boot-pill:pill :: :: sys: root path to boot system, `/~me/[desk]/now/sys` :: diff --git a/pkg/arvo/gen/solid.hoon b/pkg/arvo/gen/solid.hoon index 1e12d3cce2..52440c3ff7 100644 --- a/pkg/arvo/gen/solid.hoon +++ b/pkg/arvo/gen/solid.hoon @@ -15,8 +15,8 @@ arg=$@(~ [top=path ~]) dub=_| == -:- %pill -^- pill:pill +:- %boot-pill +^- boot-pill:pill :: sys: root path to boot system, `/~me/[desk]/now/sys` :: =/ sys=path diff --git a/pkg/arvo/lib/pill.hoon b/pkg/arvo/lib/pill.hoon index fcde0e5986..26990eaf11 100644 --- a/pkg/arvo/lib/pill.hoon +++ b/pkg/arvo/lib/pill.hoon @@ -5,12 +5,16 @@ :: +$ pill $% [%ivory p=(list)] - $: %pill - nam=term - boot-ova=(list) - kernel-ova=(list unix-event) - userspace-ova=(list unix-event) - == == + boot-pill + == +:: ++$ boot-pill + $: %pill + nam=term + boot-ova=(list) + kernel-ova=(list unix-event) + userspace-ova=(list unix-event) + == :: +$ unix-event %+ pair wire diff --git a/pkg/arvo/mar/boot-pill.hoon b/pkg/arvo/mar/boot-pill.hoon new file mode 100644 index 0000000000..dc582c1a91 --- /dev/null +++ b/pkg/arvo/mar/boot-pill.hoon @@ -0,0 +1,15 @@ +:: +:::: /hoon/pill/mar + :: +/+ *pill +|_ pil=boot-pill +++ grow + |% + ++ noun pil + -- +++ grab + |% + ++ noun boot-pill + -- +++ grad %mime +-- From 4ec5e8af864fbbef6abfe3df5e530254740b3677 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 17 Jun 2021 16:49:56 +1000 Subject: [PATCH 02/64] aqua: upgrade for arvo 240 --- pkg/arvo/app/aqua.hoon | 35 ++++++++++++++++++++++++----------- pkg/arvo/lib/pill.hoon | 2 ++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index 6659b16fc2..7f46852075 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -179,7 +179,7 @@ ?. processing-events ..abet-pe =^ ue next-events ~(get to next-events) - =/ poke-arm (mox +47.snap) + =/ poke-arm (mox +23.snap) ?> ?=(%0 -.poke-arm) =/ poke p.poke-arm =. tym (max +(tym) now.hid) @@ -198,7 +198,7 @@ :: ++ peek |= p=* - =/ res (mox +46.snap) + =/ res (mox +22.snap) ?> ?=(%0 -.res) =/ peek p.res =/ pax (path p) @@ -211,7 +211,7 @@ :: ++ wish |= txt=@t - =/ res (mox +22.snap) + =/ res (mox +10.snap) ?> ?=(%0 -.res) =/ wish p.res ~& [who=who %wished (slum wish txt)] @@ -508,24 +508,37 @@ :: should be deleted now that aqua is capable of managing azimuth state :: internally. Its been left this way for now until all the ph tests :: can be rewritten - =/ keys=dawn-event:jael (dawn who.ae) + =/ keys=dawn-event:jael + ?~ keys.ae *dawn-event:jael + (dawn who.ae) =. this abet-pe:(publish-effect:(pe who.ae) [/ %sleep ~]) =/ initted =< plow %- push-events:apex:(pe who.ae) ^- (list unix-event) :~ [/ %wack 0] :: eny - [/ %whom who.ae] :: eny - [//newt/0v1n.2m9vh %born ~] - [//behn/0v1n.2m9vh %born ~] - :^ //term/1 %boot & + :: [/ %verb `|] possible verb + :^ / %wyrd [%urbit /test] :: negotiate XX what is .non and .rev + ^- (list (pair term @)) + :~ zuse+zuse + lull+lull + arvo+arvo + hoon+hoon-version + nock+4 + == + [/ %whom who.ae] :: who + -.kernel-ova.pil :: load compiler + +<:kernel-ova.pil :: load arvo XX how to work with solid? + :^ /d/term/1 %boot & ?~ keys.ae [%fake who.ae] [%dawn keys] -.userspace-ova.pil - [//http-client/0v1n.2m9vh %born ~] - [//http-server/0v1n.2m9vh %born ~] - [//http-server/0v1n.2m9vh %live 8.080 `8.445] + [/b/behn/0v1n.2m9vh %born ~] + [/i/http-client/0v1n.2m9vh %born ~] + [/e/http-server/0v1n.2m9vh %born ~] + [/e/http-server/0v1n.2m9vh %live 8.080 `8.445] + [/a/newt/0v1n.2m9vh %born ~] == =. this abet-pe:initted (pe who.ae) diff --git a/pkg/arvo/lib/pill.hoon b/pkg/arvo/lib/pill.hoon index 26990eaf11..30fcbcbad7 100644 --- a/pkg/arvo/lib/pill.hoon +++ b/pkg/arvo/lib/pill.hoon @@ -22,6 +22,8 @@ [%what p=(list (pair path (cask)))] [%whom p=ship] [%boot ? $%($>(%fake task:jael) $>(%dawn task:jael))] + [%wyrd p=vere] + [%verb p=(unit ?)] unix-task == :: +boot-ovum: boostrap kernel filesystem load From 1aae4490b739aeb8bfe64857b53bb9ce6dd96606 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 17 Jun 2021 16:53:49 +1000 Subject: [PATCH 03/64] aqua: upgrade wire format in threads and generators --- pkg/arvo/gen/aqua/dojo.hoon | 8 ++++---- pkg/arvo/gen/aqua/file.hoon | 2 +- pkg/arvo/gen/aqua/init.hoon | 6 ++++-- pkg/arvo/lib/ph/util.hoon | 12 ++++++------ pkg/arvo/ted/aqua/ames.hoon | 4 ++-- pkg/arvo/ted/aqua/behn.hoon | 4 ++-- pkg/arvo/ted/aqua/eyre-azimuth.hoon | 2 +- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pkg/arvo/gen/aqua/dojo.hoon b/pkg/arvo/gen/aqua/dojo.hoon index 9850f34bf1..03d63f5058 100644 --- a/pkg/arvo/gen/aqua/dojo.hoon +++ b/pkg/arvo/gen/aqua/dojo.hoon @@ -5,10 +5,10 @@ :- %aqua-events %+ turn ^- (list unix-event) - :~ [//term/1 %belt %ctl `@c`%e] - [//term/1 %belt %ctl `@c`%u] - [//term/1 %belt %txt ((list @c) command)] - [//term/1 %belt %ret ~] + :~ [/d/term/1 %belt %ctl `@c`%e] + [/d/term/1 %belt %ctl `@c`%u] + [/d/term/1 %belt %txt ((list @c) command)] + [/d/term/1 %belt %ret ~] == |= ue=unix-event [%event her ue] diff --git a/pkg/arvo/gen/aqua/file.hoon b/pkg/arvo/gen/aqua/file.hoon index 6486b19112..4b0958c5c1 100644 --- a/pkg/arvo/gen/aqua/file.hoon +++ b/pkg/arvo/gen/aqua/file.hoon @@ -7,5 +7,5 @@ :+ %event her ?> ?=([@ @ @ *] pax) =/ file [/text/plain (as-octs:mimes:html .^(@ %cx pax))] -:- //sync/0v1n.2m9vh +:- /c/sync/0v1n.2m9vh [%into `desk`i.t.pax | `mode:clay`[t.t.t.pax `file]~] diff --git a/pkg/arvo/gen/aqua/init.hoon b/pkg/arvo/gen/aqua/init.hoon index 2eec2fba25..0476a159e4 100644 --- a/pkg/arvo/gen/aqua/init.hoon +++ b/pkg/arvo/gen/aqua/init.hoon @@ -1,6 +1,8 @@ +:: Start an aqua ship +:: /- aquarium =, aquarium :- %say -|= [* [her=ship ~] ~] +|= [* [her=ship fake=? ~] ~] :- %aqua-events -[%init-ship her `*dawn-event:jael]~ +[%init-ship her ?:(fake ~ `*dawn-event:jael)]~ diff --git a/pkg/arvo/lib/ph/util.hoon b/pkg/arvo/lib/ph/util.hoon index 09e7ff623d..e7982fe84c 100644 --- a/pkg/arvo/lib/ph/util.hoon +++ b/pkg/arvo/lib/ph/util.hoon @@ -28,10 +28,10 @@ %+ send-events-to who ^- (list unix-event) :~ - [//term/1 %belt %ctl `@c`%e] - [//term/1 %belt %ctl `@c`%u] - [//term/1 %belt %txt ((list @c) what)] - [//term/1 %belt %ret ~] + [/d/term/1 %belt %ctl `@c`%e] + [/d/term/1 %belt %ctl `@c`%u] + [/d/term/1 %belt %txt ((list @c) what)] + [/d/term/1 %belt %ret ~] == :: :: Control character @@ -40,7 +40,7 @@ |= [who=ship what=term] ^- (list ph-event) %+ send-events-to who - :~ [//term/1 %belt %ctl (,@c what)] + :~ [/d/term/1 %belt %ctl (,@c what)] == :: :: Inject a file into a ship @@ -54,7 +54,7 @@ [path ~ /text/plain (as-octs:mimes:html txt)] %+ send-events-to who :~ - [//sync/0v1n.2m9vh %into des | input] + [/c/sync/0v1n.2m9vh %into des | input] == :: :: Checks whether the given event is a dojo output blit containing the diff --git a/pkg/arvo/ted/aqua/ames.hoon b/pkg/arvo/ted/aqua/ames.hoon index e609353a43..80125cbc78 100644 --- a/pkg/arvo/ted/aqua/ames.hoon +++ b/pkg/arvo/ted/aqua/ames.hoon @@ -18,7 +18,7 @@ |= [our=ship who=@p] ^- (list card:agent:gall) %+ emit-aqua-events our - [%event who [//newt/0v1n.2m9vh %born ~]]~ + [%event who [/a/newt/0v1n.2m9vh %born ~]]~ :: ++ handle-send |= [our=ship now=@da sndr=@p way=wire %send lan=lane:ames pac=@] @@ -26,7 +26,7 @@ =/ rcvr=ship (lane-to-ship lan) =/ hear-lane (ship-to-lane sndr) %+ emit-aqua-events our - [%event rcvr //newt/0v1n.2m9vh %hear hear-lane pac]~ + [%event rcvr /a/newt/0v1n.2m9vh %hear hear-lane pac]~ :: +lane-to-ship: decode a ship from an aqua lane :: :: Special-case one comet, since its address doesn't fit into a lane. diff --git a/pkg/arvo/ted/aqua/behn.hoon b/pkg/arvo/ted/aqua/behn.hoon index 517d2f3c72..1b5ac20922 100644 --- a/pkg/arvo/ted/aqua/behn.hoon +++ b/pkg/arvo/ted/aqua/behn.hoon @@ -40,7 +40,7 @@ ^+ ..abet-pe =. this %- emit-aqua-events - [%event who [//behn/0v1n.2m9vh %born ~]]~ + [%event who [/b/behn/0v1n.2m9vh %born ~]]~ ..abet-pe :: ++ handle-doze @@ -82,7 +82,7 @@ :_ ~ ^- aqua-event :+ %event who - [//behn/0v1n.2m9vh [%wake ~]] + [/b/behn/0v1n.2m9vh [%wake ~]] ..abet-pe -- -- diff --git a/pkg/arvo/ted/aqua/eyre-azimuth.hoon b/pkg/arvo/ted/aqua/eyre-azimuth.hoon index 1700c5d614..a2e77354aa 100644 --- a/pkg/arvo/ted/aqua/eyre-azimuth.hoon +++ b/pkg/arvo/ted/aqua/eyre-azimuth.hoon @@ -179,7 +179,7 @@ :_ ~ :* %event her - //http-client/0v1n.2m9vh + /i/http-client/0v1n.2m9vh %receive num.u.ask [%start [200 ~] `(as-octs:mimes:html resp) &] From 169d14f4ba53a7f9e6dad008ea4a56800a95230f Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 18 Jun 2021 10:27:17 +1000 Subject: [PATCH 04/64] aqua: allow solid pills --- pkg/arvo/app/aqua.hoon | 54 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index 7f46852075..bfc2bcde06 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -516,29 +516,37 @@ =< plow %- push-events:apex:(pe who.ae) ^- (list unix-event) - :~ [/ %wack 0] :: eny - :: [/ %verb `|] possible verb - :^ / %wyrd [%urbit /test] :: negotiate XX what is .non and .rev - ^- (list (pair term @)) - :~ zuse+zuse - lull+lull - arvo+arvo - hoon+hoon-version - nock+4 - == - [/ %whom who.ae] :: who - -.kernel-ova.pil :: load compiler - +<:kernel-ova.pil :: load arvo XX how to work with solid? - :^ /d/term/1 %boot & - ?~ keys.ae - [%fake who.ae] - [%dawn keys] - -.userspace-ova.pil - [/b/behn/0v1n.2m9vh %born ~] - [/i/http-client/0v1n.2m9vh %born ~] - [/e/http-server/0v1n.2m9vh %born ~] - [/e/http-server/0v1n.2m9vh %live 8.080 `8.445] - [/a/newt/0v1n.2m9vh %born ~] + %- zing + :~ + :~ [/ %wack 0] :: eny + :: [/ %verb `|] possible verb + :^ / %wyrd [%urbit /test] :: negotiate XX what is .non and .rev + ^- (list (pair term @)) + :~ zuse+zuse + lull+lull + arvo+arvo + hoon+hoon-version + nock+4 + == + [/ %whom who.ae] :: who + == + :: + kernel-ova.pil :: load compiler + :: + :_ ~ + :^ /d/term/1 %boot & + ?~ keys.ae + [%fake who.ae] + [%dawn keys] + :: + userspace-ova.pil :: load os + :: + :~ [/b/behn/0v1n.2m9vh %born ~] + [/i/http-client/0v1n.2m9vh %born ~] + [/e/http-server/0v1n.2m9vh %born ~] + [/e/http-server/0v1n.2m9vh %live 8.080 `8.445] + [/a/newt/0v1n.2m9vh %born ~] + == == =. this abet-pe:initted (pe who.ae) From e36f631e46dc60cde32cbf599e3f422296f4817d Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 18 Jun 2021 15:53:07 +1000 Subject: [PATCH 05/64] aqua: cleanup api, fix scry handling --- pkg/arvo/app/aqua.hoon | 20 +++----- pkg/arvo/lib/aqua-azimuth.hoon | 2 +- pkg/arvo/lib/ph/io.hoon | 85 +++++----------------------------- pkg/arvo/lib/ph/util.hoon | 4 +- pkg/arvo/sur/aquarium.hoon | 2 +- 5 files changed, 22 insertions(+), 91 deletions(-) diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index bfc2bcde06..ebea9e8484 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -204,9 +204,10 @@ =/ pax (path p) ?> ?=([@ @ @ @ *] pax) =. i.t.t.t.pax (scot %da tym) - =/ pek (slum peek [tym pax]) - pek - :: + =/ pek (slum peek [[~ ~] & pax]) + =+ ;;(res=(unit (cask)) pek) + (bind res tail) + :: :: Wish :: ++ wish @@ -504,13 +505,6 @@ ?- -.ae :: %init-ship - :: XX Note that the keys that get passed in are unused. The keys field - :: should be deleted now that aqua is capable of managing azimuth state - :: internally. Its been left this way for now until all the ph tests - :: can be rewritten - =/ keys=dawn-event:jael - ?~ keys.ae *dawn-event:jael - (dawn who.ae) =. this abet-pe:(publish-effect:(pe who.ae) [/ %sleep ~]) =/ initted =< plow @@ -519,7 +513,7 @@ %- zing :~ :~ [/ %wack 0] :: eny - :: [/ %verb `|] possible verb + :: [/ %verb `|] :: possible verb :^ / %wyrd [%urbit /test] :: negotiate XX what is .non and .rev ^- (list (pair term @)) :~ zuse+zuse @@ -535,9 +529,9 @@ :: :_ ~ :^ /d/term/1 %boot & - ?~ keys.ae + ?: fake.ae [%fake who.ae] - [%dawn keys] + [%dawn (dawn who.ae)] :: userspace-ova.pil :: load os :: diff --git a/pkg/arvo/lib/aqua-azimuth.hoon b/pkg/arvo/lib/aqua-azimuth.hoon index 7a2a46db57..4aec08d1c6 100644 --- a/pkg/arvo/lib/aqua-azimuth.hoon +++ b/pkg/arvo/lib/aqua-azimuth.hoon @@ -122,7 +122,7 @@ :_ ~ :* %event her - //http-client/0v1n.2m9vh + /i/http-client/0v1n.2m9vh %receive num.u.ask [%start [200 ~] `(as-octs:mimes:html resp) &] diff --git a/pkg/arvo/lib/ph/io.hoon b/pkg/arvo/lib/ph/io.hoon index e41bb9f48c..d39cdbbd7c 100644 --- a/pkg/arvo/lib/ph/io.hoon +++ b/pkg/arvo/lib/ph/io.hoon @@ -23,17 +23,14 @@ :: ++ start-simple (start-test %aqua-ames %aqua-behn %aqua-dill %aqua-eyre ~) -++ end-simple - (end-test %aqua-ames %aqua-behn %aqua-dill %aqua-eyre ~) :: ++ start-azimuth - =/ m (strand ,tid:spider) + =/ m (strand ,~) ^- form:m - ;< ~ bind:m (start-test %aqua-ames %aqua-behn %aqua-dill ~) - (start-thread %aqua-eyre-azimuth) + ;<(~ bind:m start-simple init) :: -++ end-azimuth - (end-test %aqua-ames %aqua-behn %aqua-dill %aqua-eyre-azimuth ~) +++ end + (end-test %aqua-ames %aqua-behn %aqua-dill %aqua-eyre ~) :: ++ start-test |= vane-threads=(list term) @@ -91,77 +88,32 @@ ^- form:m (pure:m ~) :: -:: XX +spawn-aqua and +breach-aqua mean do these actions using aqua's internal -:: azimuth management system, eventually these should just replace +spawn -:: +breach :: -++ init-azimuth +++ init =/ m (strand ,~) ^- form:m (send-azimuth-action %init-azimuth ~) :: -++ spawn-aqua +++ spawn |= =ship ~& > "spawning {}" =/ m (strand ,~) ^- form:m (send-azimuth-action %spawn ship) :: -++ breach-aqua +++ breach |= =ship ~& > "breaching {}" =/ m (strand ,~) ^- form:m (send-azimuth-action %breach ship) :: -++ spawn - |= [=tid:spider =ship] - ~& > "spawning {}" - =/ m (strand ,~) - =/ =vase !>(`input:spider`[tid %azimuth-command !>([%spawn ship])]) - (poke-our %spider %spider-input vase) -:: -++ breach - |= [=tid:spider who=ship] - =/ m (strand ,~) - ~& > "breaching {}" - =/ =vase - !>([tid %azimuth-command !>([%breach who])]) - (poke-our %spider %spider-input vase) -:: :: who: breachee :: her: wait until hears about breach :: ++ breach-and-hear - |= [=tid:spider who=ship her=ship] - =/ m (strand ,~) - ~& > "breaching {} for {}" - ;< =bowl:spider bind:m get-bowl - =/ aqua-pax - :- %i - /(scot %p her)/j/(scot %p her)/rift/(scot %da now.bowl)/(scot %p who)/noun - =/ old-rut ;;((unit @) (scry-aqua:util noun our.bowl now.bowl aqua-pax)) - =/ new-rut - ?~ old-rut - 1 - +(+.old-rut) - =/ =vase - !>([tid %azimuth-command !>([%breach who])]) - ;< ~ bind:m (poke-our %spider %spider-input vase) - |- ^- form:m - =* loop $ - ;< [him=ship =unix-effect] bind:m take-unix-effect - ;< =bowl:spider bind:m get-bowl - =/ aqua-pax - :- %i - /(scot %p her)/j/(scot %p her)/rift/(scot %da now.bowl)/(scot %p who)/noun - =/ rut (scry-aqua:util noun our.bowl now.bowl aqua-pax) - ?: =([~ new-rut] rut) - (pure:m ~) - loop -:: -++ breach-and-hear-aqua |= [who=ship her=ship] + ~& > "breaching {} for {}" =/ m (strand ,~) ;< =bowl:spider bind:m get-bowl =/ aqua-pax @@ -186,27 +138,11 @@ loop :: ++ init-ship - |= =ship + |= [=ship fake=?] =/ m (strand ,~) ^- form:m ~& > "starting {}" - ;< ~ bind:m (send-events (init:util ship `*dawn-event:jael)) - (check-ship-booted ship) -:: -++ real-ship - |= [=tid:spider =ship] - ~& > "booting real {}" - =/ m (strand ,~) - =/ =vase !>([tid %azimuth-command !>([%create-ship ship])]) - ;< ~ bind:m (poke-our %spider %spider-input vase) - (check-ship-booted ship) -:: -++ raw-ship - |= [=ship keys=(unit dawn-event:jael)] - =/ m (strand ,~) - ^- form:m - ~& > "starting {}" - ;< ~ bind:m (send-events (init:util ship keys)) + ;< ~ bind:m (send-events (init:util ship fake)) (check-ship-booted ship) :: ++ check-ship-booted @@ -258,6 +194,7 @@ :: ++ send-hi-not-responding |= [from=@p to=@p] + ~& > 'sending hi not responding' =/ m (strand ,~) ;< ~ bind:m (dojo from "|hi {(scow %p to)}") (wait-for-output from "{(scow %p to)} not responding still trying") diff --git a/pkg/arvo/lib/ph/util.hoon b/pkg/arvo/lib/ph/util.hoon index e7982fe84c..53979b4258 100644 --- a/pkg/arvo/lib/ph/util.hoon +++ b/pkg/arvo/lib/ph/util.hoon @@ -16,9 +16,9 @@ :: Start a ship (low-level; prefer +raw-ship) :: ++ init - |= [who=ship keys=(unit dawn-event:jael)] + |= [who=ship fake=?] ^- (list aqua-event) - [%init-ship who keys]~ + [%init-ship who fake]~ :: :: Send dojo command :: diff --git a/pkg/arvo/sur/aquarium.hoon b/pkg/arvo/sur/aquarium.hoon index fbf0f5b97f..d71d9f5197 100644 --- a/pkg/arvo/sur/aquarium.hoon +++ b/pkg/arvo/sur/aquarium.hoon @@ -28,7 +28,7 @@ +$ pill pill:pill-lib :: +$ aqua-event - $% [%init-ship who=ship keys=(unit dawn-event:jael)] + $% [%init-ship who=ship fake=?] [%pause-events who=ship] [%snap-ships lab=term hers=(list ship)] [%restore-snap lab=term] From ff2c7f8d687811f61bafdc9e29c044ba16b454d3 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 18 Jun 2021 15:54:17 +1000 Subject: [PATCH 06/64] ph: migrate tests to new api --- pkg/arvo/gen/aqua/init.hoon | 2 +- pkg/arvo/ted/aqua/eyre-azimuth.hoon | 4 +-- pkg/arvo/ted/aqua/eyre.hoon | 4 +-- pkg/arvo/ted/ph/add.hoon | 4 +-- pkg/arvo/ted/ph/boot-az.hoon | 8 +++--- pkg/arvo/ted/ph/boot-planet.hoon | 8 +++--- pkg/arvo/ted/ph/breach-hi-aqua.hoon | 18 ++++++------- pkg/arvo/ted/ph/breach-hi-cousin.hoon | 25 +++++++++--------- pkg/arvo/ted/ph/breach-hi.hoon | 17 ++++++------ pkg/arvo/ted/ph/breach-multiple.hoon | 30 +++++++++++----------- pkg/arvo/ted/ph/breach-sudden.hoon | 28 ++++++++++---------- pkg/arvo/ted/ph/breach-sync.hoon | 16 ++++++------ pkg/arvo/ted/ph/change-file.hoon | 4 +-- pkg/arvo/ted/ph/child-sync.hoon | 6 ++--- pkg/arvo/ted/ph/child-update.hoon | 6 ++--- pkg/arvo/ted/ph/group-rejoin.hoon | 25 +++++++++--------- pkg/arvo/ted/ph/hi-az.hoon | 13 +++++----- pkg/arvo/ted/ph/hi-comet-az.hoon | 21 ++++++++------- pkg/arvo/ted/ph/hi-cousin-az.hoon | 21 ++++++++------- pkg/arvo/ted/ph/hi-linnup-az-backward.hoon | 17 ++++++------ pkg/arvo/ted/ph/hi-linnup-az.hoon | 17 ++++++------ pkg/arvo/ted/ph/hi-marbud-az.hoon | 13 +++++----- pkg/arvo/ted/ph/hi-nephew-az.hoon | 17 ++++++------ pkg/arvo/ted/ph/hi-uncle-az.hoon | 17 ++++++------ pkg/arvo/ted/ph/hi.hoon | 8 +++--- pkg/arvo/ted/ph/lib-hooks.hoon | 17 ++++++------ pkg/arvo/ted/ph/migrate/breach.hoon | 4 +-- pkg/arvo/ted/ph/migrate/init.hoon | 15 +++++------ pkg/arvo/ted/ph/moon-az.hoon | 23 ++++++++--------- pkg/arvo/ted/ph/second-cousin-hi.hoon | 14 +++++----- 30 files changed, 205 insertions(+), 217 deletions(-) diff --git a/pkg/arvo/gen/aqua/init.hoon b/pkg/arvo/gen/aqua/init.hoon index 0476a159e4..4d7f922a2c 100644 --- a/pkg/arvo/gen/aqua/init.hoon +++ b/pkg/arvo/gen/aqua/init.hoon @@ -5,4 +5,4 @@ :- %say |= [* [her=ship fake=? ~] ~] :- %aqua-events -[%init-ship her ?:(fake ~ `*dawn-event:jael)]~ +[%init-ship her fake]~ diff --git a/pkg/arvo/ted/aqua/eyre-azimuth.hoon b/pkg/arvo/ted/aqua/eyre-azimuth.hoon index a2e77354aa..4a098961b1 100644 --- a/pkg/arvo/ted/aqua/eyre-azimuth.hoon +++ b/pkg/arvo/ted/aqua/eyre-azimuth.hoon @@ -274,7 +274,7 @@ =/ clan (clan:title who) ?- clan ?(%czar %king %duke) - ;< ~ bind:m (raw-ship:ph-io who `(dawn who ~)) + ;< ~ bind:m (init-ship:ph-io who |) (pure:m state) :: ?(%earl %pawn) @@ -294,7 +294,7 @@ =/ rank ?:(=(%earl clan) "moon" "comet") "|{rank} {(scow %p who)}, =public-key {(scow %uw pass)}" ;< ~ bind:m (dojo:ph-io spon com) - ;< ~ bind:m (raw-ship:ph-io who `(dawn who `seed)) + ;< ~ bind:m (init-ship:ph-io who |) (pure:m state) == :: diff --git a/pkg/arvo/ted/aqua/eyre.hoon b/pkg/arvo/ted/aqua/eyre.hoon index 6c1d85c1d7..b8a7b7616c 100644 --- a/pkg/arvo/ted/aqua/eyre.hoon +++ b/pkg/arvo/ted/aqua/eyre.hoon @@ -39,7 +39,7 @@ ^+ ..abet-pe =. this %- emit-aqua-events - [%event who [//http/0v1n.2m9vh %born ~]]~ + [%event who [/i/http/0v1n.2m9vh %born ~]]~ ..abet-pe :: ++ handle-thus @@ -81,7 +81,7 @@ ..abet-pe =. http-requests (~(del in http-requests) num) =. this - (emit-aqua-events [%event who [//http/0v1n.2m9vh %receive num [%start [p.res q.res] r.res &]]]~) + (emit-aqua-events [%event who [/i/http/0v1n.2m9vh %receive num [%start [p.res q.res] r.res &]]]~) ..abet-pe :: :: Got error in HTTP response diff --git a/pkg/arvo/ted/ph/add.hoon b/pkg/arvo/ted/ph/add.hoon index e5507cbee9..3a0004c05c 100644 --- a/pkg/arvo/ted/ph/add.hoon +++ b/pkg/arvo/ted/ph/add.hoon @@ -5,8 +5,8 @@ |= args=vase =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) +;< ~ bind:m (init-ship ~bud &) ;< ~ bind:m (dojo ~bud "[%test-result (add 2 3)]") ;< ~ bind:m (wait-for-output ~bud "[%test-result 5]") -;< ~ bind:m end-simple +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/boot-az.hoon b/pkg/arvo/ted/ph/boot-az.hoon index a430d620ff..44a012e64e 100644 --- a/pkg/arvo/ted/ph/boot-az.hoon +++ b/pkg/arvo/ted/ph/boot-az.hoon @@ -4,8 +4,8 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m end-azimuth +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/boot-planet.hoon b/pkg/arvo/ted/ph/boot-planet.hoon index 2b49371544..c5042c8725 100644 --- a/pkg/arvo/ted/ph/boot-planet.hoon +++ b/pkg/arvo/ted/ph/boot-planet.hoon @@ -5,8 +5,8 @@ |= vase =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) -;< ~ bind:m (raw-ship ~marbud ~) -;< ~ bind:m (raw-ship ~linnup-torsyx ~) -;< ~ bind:m end-simple +;< ~ bind:m (init-ship ~bud &) +;< ~ bind:m (init-ship ~marbud &) +;< ~ bind:m (init-ship ~linnup-torsyx &) +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-hi-aqua.hoon b/pkg/arvo/ted/ph/breach-hi-aqua.hoon index 473ddb846b..8b8d70f3c1 100644 --- a/pkg/arvo/ted/ph/breach-hi-aqua.hoon +++ b/pkg/arvo/ted/ph/breach-hi-aqua.hoon @@ -1,19 +1,19 @@ /- spider -/+ *ph-io, *ph-util +/+ *ph-io, *ph-util, strandio =, strand=strand:spider ^- thread:spider |= vase =/ m (strand ,vase) ;< =bowl:spider bind:m get-bowl -;< ~ bind:m start-simple -;< ~ bind:m init-azimuth -;< ~ bind:m (spawn-aqua ~bud) -;< ~ bind:m (spawn-aqua ~dev) -;< ~ bind:m (init-ship ~bud) -;< ~ bind:m (init-ship ~dev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~dev |) +;< ~ bind:m (sleep:strandio ~s10) ;< ~ bind:m (send-hi ~bud ~dev) -;< ~ bind:m (breach-and-hear-aqua ~dev ~bud) +;< ~ bind:m (breach-and-hear ~dev ~bud) ;< ~ bind:m (send-hi-not-responding ~bud ~dev) -;< ~ bind:m (init-ship ~dev) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (wait-for-output ~bud "hi ~dev successful") (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-hi-cousin.hoon b/pkg/arvo/ted/ph/breach-hi-cousin.hoon index 5984913e7b..d2f4dd6584 100644 --- a/pkg/arvo/ted/ph/breach-hi-cousin.hoon +++ b/pkg/arvo/ted/ph/breach-hi-cousin.hoon @@ -8,20 +8,19 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m - start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~mardev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~dev) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~mardev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~mardev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~dev |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~mardev |) ;< ~ bind:m (send-hi ~marbud ~mardev) -;< ~ bind:m (breach-and-hear az ~mardev ~marbud) +;< ~ bind:m (breach-and-hear ~mardev ~marbud) ;< ~ bind:m (send-hi-not-responding ~marbud ~mardev) -;< ~ bind:m (real-ship az ~mardev) +;< ~ bind:m (init-ship ~mardev |) ;< ~ bind:m (wait-for-output ~marbud "hi ~mardev successful") -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-hi.hoon b/pkg/arvo/ted/ph/breach-hi.hoon index 2c484a6029..e255530886 100644 --- a/pkg/arvo/ted/ph/breach-hi.hoon +++ b/pkg/arvo/ted/ph/breach-hi.hoon @@ -4,16 +4,15 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m - start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (send-hi ~bud ~dev) -;< ~ bind:m (breach-and-hear az ~dev ~bud) +;< ~ bind:m (breach-and-hear ~dev ~bud) ;< ~ bind:m (send-hi-not-responding ~bud ~dev) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (wait-for-output ~bud "hi ~dev successful") -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-multiple.hoon b/pkg/arvo/ted/ph/breach-multiple.hoon index d486f9d43c..33eca2ea8b 100644 --- a/pkg/arvo/ted/ph/breach-multiple.hoon +++ b/pkg/arvo/ted/ph/breach-multiple.hoon @@ -6,19 +6,19 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< file=@t bind:m (touch-file ~bud %kids %foo) -;< ~ bind:m (check-file-touched ~marbud %home file) -;< ~ bind:m (breach-and-hear az ~bud ~marbud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (breach-and-hear az ~marbud ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< file=@t bind:m (touch-file ~bud %kids %bar) -;< file=@t bind:m (touch-file ~bud %kids %baz) -;< ~ bind:m (check-file-touched ~marbud %home file) -;< ~ bind:m end-azimuth +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< file=@t bind:m (touch-file ~bud %kids %foo) +;< ~ bind:m (check-file-touched ~marbud %home file) +;< ~ bind:m (breach-and-hear ~bud ~marbud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (breach-and-hear ~marbud ~bud) +;< ~ bind:m (init-ship ~marbud |) +;< file=@t bind:m (touch-file ~bud %kids %bar) +;< file=@t bind:m (touch-file ~bud %kids %baz) +;< ~ bind:m (check-file-touched ~marbud %home file) +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-sudden.hoon b/pkg/arvo/ted/ph/breach-sudden.hoon index ba03c2168a..6e1c91ece4 100644 --- a/pkg/arvo/ted/ph/breach-sudden.hoon +++ b/pkg/arvo/ted/ph/breach-sudden.hoon @@ -8,19 +8,19 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< file=@t bind:m (touch-file ~bud %kids %foo) -;< ~ bind:m (check-file-touched ~marbud %home file) -;< ~ bind:m (breach az ~bud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< file=@t bind:m (touch-file ~bud %kids %foo) +;< ~ bind:m (check-file-touched ~marbud %home file) +;< ~ bind:m (breach ~bud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (dojo ~bud "|merge %home ~marbud %kids, =gem %only-this") -;< file=@t bind:m (touch-file ~bud %kids %bar) -;< file=@t bind:m (touch-file ~bud %kids %baz) -;< ~ bind:m (check-file-touched ~marbud %home file) -;< ~ bind:m end-azimuth +;< file=@t bind:m (touch-file ~bud %kids %bar) +;< file=@t bind:m (touch-file ~bud %kids %baz) +;< ~ bind:m (check-file-touched ~marbud %home file) +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-sync.hoon b/pkg/arvo/ted/ph/breach-sync.hoon index 5464594bca..e40cf9f68c 100644 --- a/pkg/arvo/ted/ph/breach-sync.hoon +++ b/pkg/arvo/ted/ph/breach-sync.hoon @@ -6,23 +6,23 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) ;< file=@t bind:m (touch-file ~bud %kids %foo) ;< ~ bind:m (check-file-touched ~marbud %home file) :: Merge so that when we unify history with the %only-this merge later, we :: don't get a spurious conflict in %home :: ;< ~ bind:m (dojo ~marbud "|merge %kids our %home") -;< ~ bind:m (breach-and-hear az ~bud ~marbud) -;< ~ bind:m (real-ship az ~bud) +;< ~ bind:m (breach-and-hear ~bud ~marbud) +;< ~ bind:m (init-ship ~bud |) ;< ~ bind:m (dojo ~bud "|merge %kids ~marbud %kids, =gem %only-this") ;< file=@t bind:m (touch-file ~bud %kids %bar) ;< file=@t bind:m (touch-file ~bud %kids %baz) ;< ~ bind:m (check-file-touched ~marbud %home file) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/change-file.hoon b/pkg/arvo/ted/ph/change-file.hoon index cfbf3ab243..34b3b4d4ee 100644 --- a/pkg/arvo/ted/ph/change-file.hoon +++ b/pkg/arvo/ted/ph/change-file.hoon @@ -5,8 +5,8 @@ |= vase =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) +;< ~ bind:m (init-ship ~bud &) ;< file=@t bind:m (touch-file ~bud %home %foo) ;< ~ bind:m (check-file-touched ~bud %home file) -;< ~ bind:m end-simple +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/child-sync.hoon b/pkg/arvo/ted/ph/child-sync.hoon index 381a67064b..1d67a4e516 100644 --- a/pkg/arvo/ted/ph/child-sync.hoon +++ b/pkg/arvo/ted/ph/child-sync.hoon @@ -5,10 +5,10 @@ |= vase =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) -;< ~ bind:m (raw-ship ~marbud ~) +;< ~ bind:m (init-ship ~bud &) +;< ~ bind:m (init-ship ~marbud &) ;< file=@t bind:m (touch-file ~bud %home %foo) ;< ~ bind:m (dojo ~bud "|merge %kids our %home") ;< ~ bind:m (check-file-touched ~marbud %home file) -;< ~ bind:m end-simple +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/child-update.hoon b/pkg/arvo/ted/ph/child-update.hoon index d505e74ea7..e377aed6e2 100644 --- a/pkg/arvo/ted/ph/child-update.hoon +++ b/pkg/arvo/ted/ph/child-update.hoon @@ -6,12 +6,12 @@ |^ =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) -;< ~ bind:m (raw-ship ~marbud ~) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) ;< [path @t] bind:m (modify ~bud %home) ;< [=path file=@t] bind:m (modify ~bud %kids) ;< ~ bind:m (check-touched ~marbud %kids path file) -;< ~ bind:m end-simple +;< ~ bind:m end (pure:m *vase) :: ++ modify diff --git a/pkg/arvo/ted/ph/group-rejoin.hoon b/pkg/arvo/ted/ph/group-rejoin.hoon index 9fcf2e8ac1..c2e7c272d6 100644 --- a/pkg/arvo/ted/ph/group-rejoin.hoon +++ b/pkg/arvo/ted/ph/group-rejoin.hoon @@ -42,27 +42,26 @@ ^- thread:spider |= args=vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~zod) -;< ~ bind:m (spawn az ~marzod) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~zod) +;< ~ bind:m (spawn ~marzod) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) ;< ~ bind:m (wait-for-goad ~marbud) -;< ~ bind:m (real-ship az ~zod) -;< ~ bind:m (real-ship az ~marzod) +;< ~ bind:m (init-ship ~zod |) +;< ~ bind:m (init-ship ~marzod |) ;< ~ bind:m (wait-for-goad ~marzod) ;< ~ bind:m (start-group-agents ~marbud) ;< ~ bind:m (start-group-agents ~marzod) ;< ~ bind:m (dojo ~marbud ":group-store|create 'test-group'") ;< ~ bind:m (wait-for-output ~marbud ">=") ;< ~ bind:m (sleep ~s1) -;< ~ bind:m (breach-and-hear az ~marzod ~marbud) -;< ~ bind:m (real-ship az ~marzod) +;< ~ bind:m (breach-and-hear ~marzod ~marbud) +;< ~ bind:m (init-ship ~marzod |) ;< ~ bind:m (wait-for-goad ~marzod) ;< ~ bind:m (start-group-agents ~marzod) ;< ~ bind:m (sleep ~s3) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-az.hoon b/pkg/arvo/ted/ph/hi-az.hoon index b5354cdea0..5133e1222e 100644 --- a/pkg/arvo/ted/ph/hi-az.hoon +++ b/pkg/arvo/ted/ph/hi-az.hoon @@ -4,12 +4,11 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (send-hi ~bud ~dev) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-comet-az.hoon b/pkg/arvo/ted/ph/hi-comet-az.hoon index 5b82b5e718..9e4eb737b9 100644 --- a/pkg/arvo/ted/ph/hi-comet-az.hoon +++ b/pkg/arvo/ted/ph/hi-comet-az.hoon @@ -5,22 +5,21 @@ |= vase =/ m (strand ,vase) =/ comet ~bosrym-podwyl-magnes-dacrys--pander-hablep-masrym-marbud -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (real-ship az ~bud) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (init-ship ~bud |) :: -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (real-ship az ~marbud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (init-ship ~marbud |) :: -;< ~ bind:m (real-ship az comet) -;< ~ bind:m (send-hi comet ~bud) +;< ~ bind:m (init-ship comet |) +;< ~ bind:m (send-hi comet ~bud) :: -;< ~ bind:m (spawn az ~linnup-torsyx) -;< ~ bind:m (real-ship az ~linnup-torsyx) +;< ~ bind:m (spawn ~linnup-torsyx) +;< ~ bind:m (init-ship ~linnup-torsyx |) :: ;< ~ bind:m (send-hi comet ~linnup-torsyx) ;< ~ bind:m (send-hi ~linnup-torsyx comet) :: -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-cousin-az.hoon b/pkg/arvo/ted/ph/hi-cousin-az.hoon index 7facac1aac..c7010cbdcf 100644 --- a/pkg/arvo/ted/ph/hi-cousin-az.hoon +++ b/pkg/arvo/ted/ph/hi-cousin-az.hoon @@ -4,16 +4,15 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (spawn az ~mardev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~dev) -;< ~ bind:m (real-ship az ~mardev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (spawn ~mardev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~dev |) +;< ~ bind:m (init-ship ~mardev |) ;< ~ bind:m (send-hi ~mardev ~marbud) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-linnup-az-backward.hoon b/pkg/arvo/ted/ph/hi-linnup-az-backward.hoon index e072f801ab..a7052138de 100644 --- a/pkg/arvo/ted/ph/hi-linnup-az-backward.hoon +++ b/pkg/arvo/ted/ph/hi-linnup-az-backward.hoon @@ -4,14 +4,13 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~linnup-torsyx) -;< ~ bind:m (real-ship az ~linnup-torsyx) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~bud) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~linnup-torsyx) +;< ~ bind:m (init-ship ~linnup-torsyx |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~bud |) ;< ~ bind:m (send-hi ~linnup-torsyx ~marbud) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-linnup-az.hoon b/pkg/arvo/ted/ph/hi-linnup-az.hoon index abcfd4151b..cdea3c006a 100644 --- a/pkg/arvo/ted/ph/hi-linnup-az.hoon +++ b/pkg/arvo/ted/ph/hi-linnup-az.hoon @@ -4,14 +4,13 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~linnup-torsyx) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~linnup-torsyx) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~linnup-torsyx) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~linnup-torsyx |) ;< ~ bind:m (send-hi ~linnup-torsyx ~marbud) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-marbud-az.hoon b/pkg/arvo/ted/ph/hi-marbud-az.hoon index a53d81a852..010e274458 100644 --- a/pkg/arvo/ted/ph/hi-marbud-az.hoon +++ b/pkg/arvo/ted/ph/hi-marbud-az.hoon @@ -4,12 +4,11 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) ;< ~ bind:m (send-hi ~bud ~marbud) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-nephew-az.hoon b/pkg/arvo/ted/ph/hi-nephew-az.hoon index 286367eddf..dbe1a5fbae 100644 --- a/pkg/arvo/ted/ph/hi-nephew-az.hoon +++ b/pkg/arvo/ted/ph/hi-nephew-az.hoon @@ -4,14 +4,13 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (send-hi ~dev ~marbud) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi-uncle-az.hoon b/pkg/arvo/ted/ph/hi-uncle-az.hoon index c73b10cb43..f3a004044d 100644 --- a/pkg/arvo/ted/ph/hi-uncle-az.hoon +++ b/pkg/arvo/ted/ph/hi-uncle-az.hoon @@ -4,14 +4,13 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (send-hi ~marbud ~dev) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/hi.hoon b/pkg/arvo/ted/ph/hi.hoon index 1c2b0db91e..02ecd1b8f2 100644 --- a/pkg/arvo/ted/ph/hi.hoon +++ b/pkg/arvo/ted/ph/hi.hoon @@ -5,9 +5,9 @@ |= vase =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) -;< ~ bind:m (raw-ship ~dev ~) -;< ~ bind:m (raw-ship ~dev ~) +;< ~ bind:m (init-ship ~bud &) +;< ~ bind:m (init-ship ~dev &) +;< ~ bind:m (init-ship ~dev &) ;< ~ bind:m (send-hi ~bud ~dev) -;< ~ bind:m end-simple +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/lib-hooks.hoon b/pkg/arvo/ted/ph/lib-hooks.hoon index 1f2aa5966c..77d357a799 100644 --- a/pkg/arvo/ted/ph/lib-hooks.hoon +++ b/pkg/arvo/ted/ph/lib-hooks.hoon @@ -1,6 +1,7 @@ /- spider -/+ *ph-io, *strandio +/+ io=ph-io, *strandio => +=, io |% ++ strand strand:spider ++ start-agents @@ -27,12 +28,11 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider - bind:m start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (start-agents ~bud) ;< ~ bind:m (start-agents ~dev) ;< ~ bind:m (send-hi ~bud ~dev) @@ -61,5 +61,6 @@ ;< ~ bind:m (dojo ~dev ":graph-store +dbug") ;< ~ bind:m (dojo ~bud ":graph-push-hook +dbug %bowl") ;< ~ bind:m (dojo ~bud ":graph-store +dbug") -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) +::(pure:m *vase) diff --git a/pkg/arvo/ted/ph/migrate/breach.hoon b/pkg/arvo/ted/ph/migrate/breach.hoon index 4678690cc2..56b7eb43fa 100644 --- a/pkg/arvo/ted/ph/migrate/breach.hoon +++ b/pkg/arvo/ted/ph/migrate/breach.hoon @@ -17,6 +17,6 @@ :: ;< ~ bind:m (send-hi ~web ~zod) :: ;< ~ bind:m (send-hi ~web ~bus) (pure:m *vase) -;< ~ bind:m (breach-aqua i.who) -;< ~ bind:m (init-ship i.who) +;< ~ bind:m (breach i.who) +;< ~ bind:m (init-ship i.who |) loop(who t.who) diff --git a/pkg/arvo/ted/ph/migrate/init.hoon b/pkg/arvo/ted/ph/migrate/init.hoon index 3433e80abb..9f7771dbe3 100644 --- a/pkg/arvo/ted/ph/migrate/init.hoon +++ b/pkg/arvo/ted/ph/migrate/init.hoon @@ -4,15 +4,14 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< ~ bind:m start-simple -;< ~ bind:m init-azimuth -;< ~ bind:m (spawn-aqua ~zod) -;< ~ bind:m (spawn-aqua ~bus) -;< ~ bind:m (spawn-aqua ~web) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~zod) +;< ~ bind:m (spawn ~bus) +;< ~ bind:m (spawn ~web) :: -;< ~ bind:m (init-ship ~zod) -;< ~ bind:m (init-ship ~bus) -;< ~ bind:m (init-ship ~web) +;< ~ bind:m (init-ship ~zod |) +;< ~ bind:m (init-ship ~bus |) +;< ~ bind:m (init-ship ~web |) :: ;< ~ bind:m (send-hi ~zod ~web) ;< ~ bind:m (send-hi ~zod ~bus) diff --git a/pkg/arvo/ted/ph/moon-az.hoon b/pkg/arvo/ted/ph/moon-az.hoon index 4cfcbb601b..22a077941f 100644 --- a/pkg/arvo/ted/ph/moon-az.hoon +++ b/pkg/arvo/ted/ph/moon-az.hoon @@ -4,20 +4,19 @@ ^- thread:spider |= vase =/ m (strand ,vase) -;< az=tid:spider bind:m - start-azimuth -;< ~ bind:m (spawn az ~bud) -;< ~ bind:m (spawn az ~marbud) -;< ~ bind:m (spawn az ~linnup-torsyx) -;< ~ bind:m (spawn az ~dev) -;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (real-ship az ~marbud) -;< ~ bind:m (real-ship az ~linnup-torsyx) -;< ~ bind:m (real-ship az ~linnup-torsyx-linnup-torsyx) +;< ~ bind:m start-azimuth +;< ~ bind:m (spawn ~bud) +;< ~ bind:m (spawn ~marbud) +;< ~ bind:m (spawn ~linnup-torsyx) +;< ~ bind:m (spawn ~dev) +;< ~ bind:m (init-ship ~bud |) +;< ~ bind:m (init-ship ~marbud |) +;< ~ bind:m (init-ship ~linnup-torsyx |) +;< ~ bind:m (init-ship ~linnup-torsyx-linnup-torsyx |) ;< ~ bind:m (send-hi ~bud ~linnup-torsyx-linnup-torsyx) ;< ~ bind:m (send-hi ~linnup-torsyx-linnup-torsyx ~marbud) -;< ~ bind:m (real-ship az ~dev) +;< ~ bind:m (init-ship ~dev |) ;< ~ bind:m (send-hi ~linnup-torsyx-linnup-torsyx ~dev) ;< ~ bind:m (send-hi ~dev ~linnup-torsyx-linnup-torsyx) -;< ~ bind:m end-azimuth +;< ~ bind:m end (pure:m *vase) diff --git a/pkg/arvo/ted/ph/second-cousin-hi.hoon b/pkg/arvo/ted/ph/second-cousin-hi.hoon index 60f18b96c7..02e7a3b22c 100644 --- a/pkg/arvo/ted/ph/second-cousin-hi.hoon +++ b/pkg/arvo/ted/ph/second-cousin-hi.hoon @@ -5,12 +5,12 @@ |= vase =/ m (strand ,vase) ;< ~ bind:m start-simple -;< ~ bind:m (raw-ship ~bud ~) -;< ~ bind:m (raw-ship ~marbud ~) -;< ~ bind:m (raw-ship ~linnup-torsyx ~) -;< ~ bind:m (raw-ship ~dev ~) -;< ~ bind:m (raw-ship ~mardev ~) -;< ~ bind:m (raw-ship ~mitnep-todsut ~) +;< ~ bind:m (init-ship ~bud &) +;< ~ bind:m (init-ship ~marbud &) +;< ~ bind:m (init-ship ~linnup-torsyx &) +;< ~ bind:m (init-ship ~dev &) +;< ~ bind:m (init-ship ~mardev &) +;< ~ bind:m (init-ship ~mitnep-todsut &) ;< ~ bind:m (send-hi ~linnup-torsyx ~mitnep-todsut) -;< ~ bind:m end-simple +;< ~ bind:m end (pure:m *vase) From 73e9056d483cb1e46c0039d6687b1182cdda43f0 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 18 Jun 2021 16:53:00 +1000 Subject: [PATCH 07/64] mar/boot-pill: build mark correctly --- pkg/arvo/mar/boot-pill.hoon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/arvo/mar/boot-pill.hoon b/pkg/arvo/mar/boot-pill.hoon index dc582c1a91..bf97f8c156 100644 --- a/pkg/arvo/mar/boot-pill.hoon +++ b/pkg/arvo/mar/boot-pill.hoon @@ -1,5 +1,5 @@ :: -:::: /hoon/pill/mar +:::: /hoon/boot-pill/mar :: /+ *pill |_ pil=boot-pill @@ -11,5 +11,5 @@ |% ++ noun boot-pill -- -++ grad %mime +++ grad %noun -- From b0bfa500d45285f1ddf0bb4086a8c5b3fd27fc0f Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 22 Jun 2021 06:13:26 +1000 Subject: [PATCH 08/64] aqua: clarify structure of $vere --- pkg/arvo/app/aqua.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index ebea9e8484..00cb0d39f8 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -514,7 +514,7 @@ :~ :~ [/ %wack 0] :: eny :: [/ %verb `|] :: possible verb - :^ / %wyrd [%urbit /test] :: negotiate XX what is .non and .rev + :^ / %wyrd [%nonce /a-version] :: dummy runtime version + nonce ^- (list (pair term @)) :~ zuse+zuse lull+lull From 69c123c22e28e56c81ba252feabfff8ac5b80609 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 16:16:02 -0500 Subject: [PATCH 09/64] Add hooks --- pkg/btc-wallet/src/js/hooks/useSettings.js | 301 +++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 pkg/btc-wallet/src/js/hooks/useSettings.js diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js new file mode 100644 index 0000000000..5506583ba5 --- /dev/null +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -0,0 +1,301 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import _ from 'lodash'; +import { api } from '../api'; +import { reduceHistory } from '../lib/util'; + +export const SettingsContext = createContext({ + network: 'bitcoin', + loadedBtc: false, + setLoadedBtc: () => {}, + loadedSettings: false, + setLoadedSettings: () => {}, + loaded: false, + setLoaded: () => {}, + providerPerms: {}, + setProviderPerms: () => {}, + shipWallets: {}, + setShipWallets: () => {}, + provider: null, + setProvider: () => {}, + wallet: null, + setWallet: () => {}, + confirmedBalance: 0, + setConfirmedBalance: () => {}, + unconfirmedBalance: 0, + setUnconfirmedBalance: () => {}, + btcState: null, + setBtcState: () => {}, + history: [], + setHistory: () => {}, + psbt: '', + setPsbt: () => {}, + address: null, + setAddress: () => {}, + currencyRates: { + BTC: { last: 1, symbol: 'BTC' }, + }, + denomination: 'BTC', + showWarning: true, + setShowWarning: () => {}, + error: '', + setError: () => {}, + broadcastSuccess: false, + setBroadcastSuccess: () => {}, +}); + +export const SettingsProvider = ({ channel, children }) => { + const [channelData, setChannelData] = useState(null); + const [loadedBtc, setLoadedBtc] = useState(false); + const [loadedSettings, setLoadedSettings] = useState(false); + const [loaded, setLoaded] = useState(false); + const [providerPerms, setProviderPerms] = useState({}); + const [shipWallets, setShipWallets] = useState({}); + const [provider, setProvider] = useState(null); + const [wallet, setWallet] = useState(null); + const [confirmedBalance, setConfirmedBalance] = useState(0); + const [unconfirmedBalance, setUnconfirmedBalance] = useState(0); + const [btcState, setBtcState] = useState(null); + const [history, setHistory] = useState([]); + const [psbt, setPsbt] = useState(''); + const [address, setAddress] = useState(null); + const [currencyRates, setCurrencyRates] = useState({ + BTC: { last: 1, symbol: 'BTC' }, + }); + const [denomination, setDenomination] = useState('BTC'); + const [showWarning, setShowWarning] = useState(false); + const [error, setError] = useState(''); + const [broadcastSuccess, setBroadcastSuccess] = useState(false); + + const { Provider } = SettingsContext; + + const success = (event) => { + console.log({ event }); + setChannelData(event); + }; + const fail = (error) => console.log({ error }); + + const initializeBtcWallet = () => { + api.bind('/all', 'PUT', api.ship, 'btc-wallet', success, fail); + }; + + const initializeSettings = () => { + let app = 'settings-store'; + let path = '/bucket/btc-wallet'; + + fetch(`/~/scry/${app}${path}.json`) + .then((res) => res.json()) + .then((n) => { + let data = _.get(n, 'initial', false); + let bucketData = _.get(n, 'bucket', false); + if (data) { + setChannelData(n); + } + if (bucketData) { + let bucketWarning = _.get(n, 'bucket.warning', -1); + if (bucketWarning !== -1) { + setShowWarning(bucketWarning); + } + let bucketCurrency = _.get(n, 'bucket.currency', -1); + if (bucketCurrency !== -1) { + setDenomination(bucketCurrency); + } + setLoadedSettings(true); + if (loadedBtc) { + setLoaded(true); + } + } + }); + + api.bind(path, 'PUT', api.ship, app, success, fail); + }; + + const initializeCurrencyPoll = () => { + fetch('https://blockchain.info/ticker') + .then((res) => res.json()) + .then((n) => { + const newCurrencyRates = currencyRates; + for (let c in n) { + newCurrencyRates[c] = n[c]; + } + setCurrencyRates(newCurrencyRates); + setTimeout(() => initializeCurrencyPoll(), 1000 * 60 * 15); + }); + }; + + const start = () => { + if (api.ship) { + initializeBtcWallet(); + initializeSettings(); + initializeCurrencyPoll(); + } + }; + + const handleNewTx = ({ txid, recvd }) => { + let old = _.findIndex(history, (h) => { + return h.txid.dat === txid.dat && h.txid.wid === txid.wid; + }); + if (old !== -1) { + delete history.splice(old, 1); + } + if (recvd === null) { + history.unshift({ txid, recvd }); + } else { + // we expect history to have null recvd values first, and the rest in + // descending order + let insertionIndex = _.findIndex(history, (h) => { + return h.recvd < recvd && h.recvd !== null; + }); + history.splice(insertionIndex, 0, { txid, recvd }); + } + }; + + const handleCancelTx = ({ wid, dat }) => { + let entryIndex = _.findIndex(history, (h) => { + return wid === h.txid.wid && dat === h.txid.dat; + }); + if (entryIndex > -1) { + history[entryIndex].failure = true; + } + }; + + useEffect(() => { + const initialData = channelData?.data?.initial; + const putEntryData = channelData?.data?.['settings-event']?.['put-entry']; + const btcStateData = channelData?.data?.['btc-state']; + const changeProvider = channelData?.data?.['change-provider']; + const newTx = channelData?.data?.['new-tx']; + const providerStatus = channelData?.data?.providerStatus; + const checkPayee = channelData?.data?.checkPayee; + const changeWallet = channelData?.data?.changeWallet; + const psbtData = channelData?.data.psbt; + const cancelTx = channelData?.data['cancel-tx']; + const addressData = channelData?.data?.address; + const balanceData = channelData?.data?.balance; + const errorData = channelData?.data?.error; + const broadcastSuccessData = channelData?.data?.['broadcast-success']; + const broadcastFailData = channelData?.data?.['broadcast-fail']; + if (initialData) { + setProvider(initialData.provider); + setWallet(initialData.wallet); + setConfirmedBalance(_.get(initialData.balance, 'confirmed', null)); + setUnconfirmedBalance(_.get(initialData.balance, 'unconfirmed', null)); + setBtcState(initialData['btc-state']); + setHistory(reduceHistory(initialData.history)); + setAddress(initialData.address); + setLoadedBtc(true); + if (loadedSettings) { + setLoaded(true); + } + } + if (putEntryData && putEntryData?.['entry-key'] === 'currency') { + setDenomination(putEntryData.value); + } + if (putEntryData && putEntryData?.['entry-key'] === 'warning') { + setShowWarning(putEntryData.value); + } + if (btcStateData) { + setBtcState(btcStateData); + } + if (changeProvider) { + setProvider(changeProvider); + } + if (newTx) { + handleNewTx(newTx); + } + if (providerStatus) { + let newProviderPerms = providerPerms; + for (let c in providerStatus) { + newProviderPerms[c] = providerStatus[c]; + } + setProviderPerms(newProviderPerms); + } + if (checkPayee) { + let newShipWallets = shipWallets; + + for (let c in checkPayee) { + newShipWallets[c] = checkPayee[c]; + } + console.log({ newShipWallets }); + setShipWallets(newShipWallets); + } + if (changeWallet) { + setWallet(changeWallet); + } + if (psbtData) { + setPsbt(psbtData.pb); + } + if (cancelTx) { + handleCancelTx(cancelTx); + } + if (addressData) { + setAddress(addressData); + } + if (balanceData) { + setUnconfirmedBalance(balanceData.unconfirmed); + setConfirmedBalance(balanceData.confirmed); + } + if (errorData) { + setError(errorData); + } + if (broadcastSuccessData) { + setBroadcastSuccess(true); + } + if (broadcastFailData) { + setBroadcastSuccess(false); + } + }, [channelData]); + + useEffect(() => { + channel.setOnChannelError(() => { + start(); + }); + start(); + }, []); + + return ( + + {children} + + ); +}; + +export const useSettings = () => useContext(SettingsContext); From f269be8ba96d2f48f0025e77f09a2017f46a04fd Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 16:17:31 -0500 Subject: [PATCH 10/64] Convert components, remove old store --- pkg/btc-wallet/src/index.js | 16 +- .../src/js/components/lib/balance.js | 270 +++--- pkg/btc-wallet/src/js/components/lib/body.js | 115 +-- .../src/js/components/lib/bridgeInvoice.js | 392 ++++----- .../src/js/components/lib/currencyPicker.js | 71 +- pkg/btc-wallet/src/js/components/lib/error.js | 20 +- .../src/js/components/lib/feePicker.js | 166 ++-- .../src/js/components/lib/header.js | 139 +-- .../src/js/components/lib/invoice.js | 435 +++++---- .../src/js/components/lib/providerModal.js | 278 +++--- pkg/btc-wallet/src/js/components/lib/send.js | 833 +++++++++--------- pkg/btc-wallet/src/js/components/lib/sent.js | 63 +- .../src/js/components/lib/settings.js | 221 +++-- .../src/js/components/lib/signer.js | 81 +- .../src/js/components/lib/startupModal.js | 80 +- .../src/js/components/lib/transaction.js | 175 ++-- .../src/js/components/lib/transactions.js | 95 +- .../src/js/components/lib/tx-action.js | 130 ++- .../src/js/components/lib/tx-counterparty.js | 83 +- .../src/js/components/lib/walletModal.js | 432 ++++----- .../src/js/components/lib/warning.js | 141 ++- pkg/btc-wallet/src/js/components/root.js | 88 +- pkg/btc-wallet/src/js/lib/util.js | 96 +- pkg/btc-wallet/src/js/reducers/currency.js | 20 - pkg/btc-wallet/src/js/reducers/initial.js | 29 - pkg/btc-wallet/src/js/reducers/settings.js | 30 - pkg/btc-wallet/src/js/reducers/update.js | 116 --- pkg/btc-wallet/src/js/store.js | 54 -- 28 files changed, 2099 insertions(+), 2570 deletions(-) delete mode 100644 pkg/btc-wallet/src/js/reducers/currency.js delete mode 100644 pkg/btc-wallet/src/js/reducers/initial.js delete mode 100644 pkg/btc-wallet/src/js/reducers/settings.js delete mode 100644 pkg/btc-wallet/src/js/reducers/update.js delete mode 100644 pkg/btc-wallet/src/js/store.js diff --git a/pkg/btc-wallet/src/index.js b/pkg/btc-wallet/src/index.js index d4852ef264..09b51a1384 100644 --- a/pkg/btc-wallet/src/index.js +++ b/pkg/btc-wallet/src/index.js @@ -1,8 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Root } from './js/components/root.js'; +import Root from './js/components/root.js'; import { api } from './js/api.js'; -import { subscription } from "./js/subscription.js"; +import { SettingsProvider } from './js/hooks/useSettings'; import './css/indigo-static.css'; import './css/fonts.css'; @@ -13,11 +13,13 @@ import './css/custom.css'; const channel = new window.channel(); api.setChannel(window.ship, channel); - if (module.hot) { - module.hot.accept() + module.hot.accept(); } -ReactDOM.render(( - -), document.querySelectorAll("#root")[0]); +ReactDOM.render( + + + , + document.querySelectorAll('#root')[0] +); diff --git a/pkg/btc-wallet/src/js/components/lib/balance.js b/pkg/btc-wallet/src/js/components/lib/balance.js index 7d2e8eb300..a963a3dac2 100644 --- a/pkg/btc-wallet/src/js/components/lib/balance.js +++ b/pkg/btc-wallet/src/js/components/lib/balance.js @@ -1,170 +1,140 @@ -import React, { Component } from 'react'; +import React, { useState } from 'react'; import { Row, Text, Button, Col } from '@tlon/indigo-react'; import Send from './send.js'; import CurrencyPicker from './currencyPicker.js'; import { satsToCurrency } from '../../lib/util.js'; -import { store } from '../../store.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; -export default class Balance extends Component { - constructor(props) { - super(props); +const Balance = () => { + const { + address, + confirmedBalance: sats, + unconfirmedBalance: unconfirmedSats, + denomination, + currencyRates, + setPsbt, + setFee, + setError, + } = useSettings(); + const [sending, setSending] = useState(false); + const [copiedButton, setCopiedButton] = useState(false); + const [copiedString, setCopiedString] = useState(false); - this.state = { - sending: false, - copiedButton: false, - copiedString: false, - }; - - this.copyAddress = this.copyAddress.bind(this); - } - - copyAddress(arg) { - let address = this.props.state.address; + const copyAddress = (arg) => { navigator.clipboard.writeText(address); - this.props.api.btcWalletCommand({ 'gen-new-address': null }); + api.btcWalletCommand({ 'gen-new-address': null }); if (arg === 'button') { - this.setState({ copiedButton: true }); + setCopiedButton(true); setTimeout(() => { - this.setState({ copiedButton: false }); + setCopiedButton(false); }, 2000); } else if (arg === 'string') { - this.setState({ copiedString: true }); + setCopiedString(true); setTimeout(() => { - this.setState({ copiedString: false }); + setCopiedString(false); }, 2000); } - } + }; - render() { - const sats = this.props.state.confirmedBalance || 0; - const unconfirmedSats = this.props.state.unconfirmedBalance; + const unconfirmedString = unconfirmedSats ? ` (${unconfirmedSats}) ` : ''; - const unconfirmedString = unconfirmedSats ? ` (${unconfirmedSats}) ` : ''; + const value = satsToCurrency(sats, denomination, currencyRates); + const sendDisabled = sats === 0; + const addressText = + address === null ? '' : address.slice(0, 6) + '...' + address.slice(-6); - const denomination = this.props.state.denomination; - const value = satsToCurrency( - sats, - denomination, - this.props.state.currencyRates - ); - const sendDisabled = sats === 0; - const addressText = - this.props.state.address === null - ? '' - : this.props.state.address.slice(0, 6) + - '...' + - this.props.state.address.slice(-6); + const conversion = currencyRates[denomination]?.last; - const conversion = this.props.state.currencyRates[denomination].last; - - return ( - <> - {this.state.sending ? ( - { - this.setState({ sending: false }); - store.handleEvent({ - data: { psbt: '', fee: 0, error: '', 'broadcast-fail': null }, - }); - }} - /> - ) : ( - - - - Balance - - { - this.copyAddress('string'); - }} - > - {this.state.copiedString ? 'copied' : addressText} - - - - - - {value} - - {`${sats}${unconfirmedString} sats`} - - - - - + return ( + <> + {sending ? ( + { + setSending(false); + setPsbt(''); + setFee(0); + setError(''); + }} + /> + ) : ( + + + + Balance + + copyAddress('string')} + > + {copiedString ? 'copied' : addressText} + + + + + + {value} + + {`${sats}${unconfirmedString} sats`} - )} - - ); - } -} + + + + + + )} + + ); +}; + +export default Balance; diff --git a/pkg/btc-wallet/src/js/components/lib/body.js b/pkg/btc-wallet/src/js/components/lib/body.js index eff8c8d8ef..334a622a32 100644 --- a/pkg/btc-wallet/src/js/components/lib/body.js +++ b/pkg/btc-wallet/src/js/components/lib/body.js @@ -1,72 +1,49 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - LoadingSpinner, - Col, -} from '@tlon/indigo-react'; -import { - Switch, - Route, -} from 'react-router-dom'; -import Balance from './balance.js'; +import React from 'react'; +import { Box, LoadingSpinner, Col } from '@tlon/indigo-react'; +import { Switch, Route } from 'react-router-dom'; +import Balance from './balance.js'; import Transactions from './transactions.js'; -import Warning from './warning.js'; -import Header from './header.js'; -import Settings from './settings.js'; +import Warning from './warning.js'; +import Header from './header.js'; +import Settings from './settings.js'; +import { useSettings } from '../../hooks/useSettings.js'; -export default class Body extends Component { - constructor(props) { - super(props); - } +const Body = () => { + const { loaded, showWarning: warning } = useSettings(); + const cardWidth = window.innerWidth <= 475 ? '350px' : '400px'; + return !loaded ? ( + + + + ) : ( + + + +
+ + + + + +
+ {!warning ? null : } + + + + + + ); +}; - render() { - - const cardWidth = window.innerWidth <= 475 ? '350px' : '400px' - - if (!this.props.loaded) { - return ( - - - - ); - } else { - return ( - - - -
- - - - - -
- { (!this.props.warning) ? null : } - - - - - - ); - } - } -} +export default Body; diff --git a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js b/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js index acc147a8e1..3b3a2df34b 100644 --- a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js +++ b/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Box, Icon, @@ -9,231 +9,203 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; - import { Sigil } from './sigil.js'; - import * as bitcoin from 'bitcoinjs-lib'; -import * as kg from 'urbit-key-generation'; import { isValidPatp } from 'urbit-ob'; - -import Sent from './sent.js' -import Error from './error.js' - +import Sent from './sent.js'; +import Error from './error.js'; import { satsToCurrency } from '../../lib/util.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; -export default class BridgeInvoice extends Component { - constructor(props) { - super(props); +const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { + const { error, currencyRates, fee, broadcastSuccess, denomination, psbt } = + useSettings(); + const [txHex, setTxHex] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(''); + const [broadcasting, setBroadcasting] = useState(false); + const invoiceRef = useRef(); - this.state = { - txHex: '', - ready: false, - error: this.props.state.error, - broadcasting: false, - }; - - this.checkTxHex = this.checkTxHex.bind(this); - this.broadCastTx = this.broadCastTx.bind(this); - this.sendBitcoin = this.sendBitcoin.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setInvoiceRef = this.setInvoiceRef.bind(this); - } - - broadCastTx(hex) { - let command = { - 'broadcast-tx': hex + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); } - return this.props.api.btcWalletCommand(command) - } - - componentDidMount() { - window.open('https://bridge.urbit.org/?kind=btc&utx=' + this.props.psbt); - document.addEventListener("click", this.clickDismiss); - } - - componentWillUnmount(){ - document.removeEventListener("click", this.clickDismiss); - } - - setInvoiceRef(n){ - this.invoiceRef = n; - } - - clickDismiss(e){ - if (this.invoiceRef && !(this.invoiceRef.contains(e.target))){ - this.props.stopSending(); - } - } - - componentDidUpdate(prevProps){ - if (this.state.broadcasting) { - if (this.state.error !== '') { - this.setState({broadcasting: false}); - } - } - - if (prevProps.state.error !== this.props.state.error) { - this.setState({error: this.props.state.error}); - } - } - - sendBitcoin(hex) { - try { - bitcoin.Transaction.fromHex(hex) - this.broadCastTx(hex) - this.setState({broadcasting: true}); - } - - catch(e) { - this.setState({error: 'invalid-signed', broadcasting: false}); - } - } - - checkTxHex(e){ - let txHex = e.target.value; - let ready = (txHex.length > 0); - let error = ''; - this.setState({txHex, ready, error}); - } - - render() { - const { stopSending, payee, denomination, satsAmount, psbt, currencyRates, fee } = this.props; - const { error, txHex } = this.state; - - let inputColor = 'black'; - let inputBg = 'white'; - let inputBorder = 'lightGray'; - if (error !== '') { - inputColor = 'red'; - inputBg = 'veryLightRed'; - inputBorder = 'red'; + setLocalError(error); } + }, [error, broadcasting, setBroadcasting]); - const isShip = isValidPatp(payee); + useEffect(() => { + window.open('https://bridge.urbit.org/?kind=btc&utx=' + psbt); + }); - const icon = (isShip) - ? - : ; + const broadCastTx = (hex) => { + let command = { + 'broadcast-tx': hex, + }; + return api.btcWalletCommand(command); + }; - return ( - <> - { this.props.state.broadcastSuccess ? - : + const sendBitcoin = (hex) => { + try { + bitcoin.Transaction.fromHex(hex); + broadCastTx(hex); + setBroadcasting(true); + } catch (e) { + setLocalError('invalid-signed'); + setBroadcasting(false); + } + }; + + const checkTxHex = (e) => { + setTxHex(e.target.value); + setReady(txHex.length > 0); + setLocalError(''); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (localError !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; + } + + const isShip = isValidPatp(payee); + + const icon = isShip ? ( + + ) : ( + + + + ); + + return ( + <> + {broadcastSuccess ? ( + + ) : ( + - - - {satsToCurrency(satsAmount, denomination, currencyRates)} - - - {`${satsAmount} sats`} - - - {`Fee: ${satsToCurrency(fee, denomination, currencyRates)} (${fee} sats)`} - - - You are paying - - - {icon} - {payee} - - - - - Bridge signed transaction + + + {satsToCurrency(satsAmount, denomination, currencyRates)} - - - - Copy the signed transaction from Bridge + + + {`${satsAmount} sats`} + + + {`Fee: ${satsToCurrency( + fee, + denomination, + currencyRates + )} (${fee} sats)`} + + + + You are paying + + + + {icon} + + {payee} - - - { (error !== '') && - - - - } - - + {broadcasting ? : null} + + + )} + + ); +}; + +export default BridgeInvoice; diff --git a/pkg/btc-wallet/src/js/components/lib/currencyPicker.js b/pkg/btc-wallet/src/js/components/lib/currencyPicker.js index 053201bf61..30dc627560 100644 --- a/pkg/btc-wallet/src/js/components/lib/currencyPicker.js +++ b/pkg/btc-wallet/src/js/components/lib/currencyPicker.js @@ -1,50 +1,37 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, - LoadingSpinner, -} from '@tlon/indigo-react'; -import _ from 'lodash'; +import React from 'react'; +import { Icon, Row, Text } from '@tlon/indigo-react'; +import { api } from '../../api'; +import { useSettings } from '../../hooks/useSettings'; -import { satsToCurrency } from '../../lib/util.js' -import { store } from '../../store'; - -export default class CurrencyPicker extends Component { - constructor(props) { - super(props); - this.switchCurrency = this.switchCurrency.bind(this); - } - - switchCurrency(){ +const CurrencyPicker = () => { + const { denomination, currencyRates } = useSettings(); + const switchCurrency = () => { let newCurrency; - if (this.props.denomination === 'BTC') { - if (this.props.currencies['USD']) { - newCurrency = "USD"; + if (denomination === 'BTC') { + if (currencyRates['USD']) { + newCurrency = 'USD'; } - } else if (this.props.denomination === 'USD') { - newCurrency = "BTC"; + } else if (denomination === 'USD') { + newCurrency = 'BTC'; } let setCurrency = { - "put-entry": { + 'put-entry': { value: newCurrency, - "entry-key": "currency", - "bucket-key": "btc-wallet", - } - } - this.props.api.settingsEvent(setCurrency); - } + 'entry-key': 'currency', + 'bucket-key': 'btc-wallet', + }, + }; + api.settingsEvent(setCurrency); + }; + return ( + switchCurrency()}> + + + {denomination} + + + ); +}; - render() { - return ( - - - {this.props.denomination} - - ); - } -} +export default CurrencyPicker; diff --git a/pkg/btc-wallet/src/js/components/lib/error.js b/pkg/btc-wallet/src/js/components/lib/error.js index f7c9ec473a..42d114d182 100644 --- a/pkg/btc-wallet/src/js/components/lib/error.js +++ b/pkg/btc-wallet/src/js/components/lib/error.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Text } from '@tlon/indigo-react'; const errorToString = (error) => { @@ -26,16 +26,12 @@ const errorToString = (error) => { if (error === 'invalid-signed') { return 'Invalid signed bitcoin transaction'; } -} +}; -export default function Error(props) { - const error = errorToString(props.error); +const Error = ({ error, ...rest }) => ( + + {errorToString(error)} + +); - return( - - {error} - - ); -} +export default Error; diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js index ee419dd7ab..00e91c198d 100644 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ b/pkg/btc-wallet/src/js/components/lib/feePicker.js @@ -1,99 +1,99 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { Box, - Icon, - Row, Text, - Button, Col, StatelessRadioButtonField as RadioButton, Label, } from '@tlon/indigo-react'; +const feeLevels = { + low: 'low', + mid: 'mid', + high: 'high', +}; -export default class FeePicker extends Component { - constructor(props) { - super(props); +const FeePicker = ({ feeChoices, feeSelect, feeDismiss }) => { + const [feeSelected, setFeeSelected] = useState(feeLevels.mid); + const [modalElement, setModalElement] = useState(); + const modalRef = useRef(); - this.state = { - selected: 'mid' - } + // const clickDismiss = (e) => { + // console.log(modalElement, e); + // // if (modalRef && !modalRef.contains(e.target)) { + // // feeDismiss(); + // // } + // }; - this.select = this.select.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setModalRef = this.setModalRef.bind(this); - } + const select = (which) => { + setFeeSelected(which); + feeSelect(which); + feeDismiss(); + }; - componentDidMount() { - document.addEventListener("click", this.clickDismiss); - } + // useEffect(() => { + // document.addEventListener('click', (e) => clickDismiss(e)); + // setModalElement(modalRef.current); + // console.log(modalRef.current); + // return () => document.addEventListener('click', clickDismiss); + // }, []); - componentWillUnount() { - document.removeEventListener("click", this.clickDismiss); - } + return ( + feeDismiss()} + position="absolute" + p={4} + border="1px solid green" + zIndex={10} + backgroundColor="white" + borderRadius={3} + > + + Transaction Speed + + + { + select('low'); + }} + > + + - setModalRef(n) { - this.modalRef = n; - } + { + select('mid'); + }} + > + + - clickDismiss(e) { - if (this.modalRef && !(this.modalRef.contains(e.target))){ - this.props.feeDismiss(); - } - } + { + select('high'); + }} + > + + + + + ); +}; - select(which) { - this.setState({selected: which}); - this.props.feeSelect(which); - } - - render() { - return ( - - - Transaction Speed - - - { - this.select('low'); - }} - > - - - - { - this.select('mid'); - }} - > - - - - { - this.select('high'); - }} - > - - - - - - ); - } -} +export default FeePicker; diff --git a/pkg/btc-wallet/src/js/components/lib/header.js b/pkg/btc-wallet/src/js/components/lib/header.js index 9b6225effb..88f4687fff 100644 --- a/pkg/btc-wallet/src/js/components/lib/header.js +++ b/pkg/btc-wallet/src/js/components/lib/header.js @@ -1,78 +1,81 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Icon, Row, Text } from '@tlon/indigo-react'; import { Link } from 'react-router-dom'; +import { useSettings } from '../../hooks/useSettings'; -export default class Header extends Component { - constructor(props) { - super(props); +const Header = ({ settings }) => { + const { provider } = useSettings(); + let icon = settings ? 'X' : 'Adjust'; + let iconColor = settings ? 'black' : 'orange'; + let iconLink = settings ? '/~btc' : '/~btc/settings'; + + let connection = null; + let badge = null; + if (!(provider && provider.connected)) { + connection = ( + + Provider Offline + + ); + + if (!settings) { + badge = ( + + ); + } } - - render() { - let icon = this.props.settings ? "X" : "Adjust"; - let iconColor = this.props.settings ? "black" : "orange"; - let iconLink = this.props.settings ? "/~btc" : "/~btc/settings"; - - let connection = null; - let badge = null; - if (!(this.props.state.provider && this.props.state.provider.connected)) { - connection = - - Provider Offline + return ( + + + + + + + Bitcoin - - if (!this.props.settings) { - badge = - - } - } - - - - return ( - - - + + {connection} + + - + {badge} + - - Bitcoin - - - - {connection} - - - {badge} - - - - + - ); - } -} + + ); +}; + +export default Header; diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/js/components/lib/invoice.js index 6850fb7f21..771a876698 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/js/components/lib/invoice.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; import { Box, Icon, @@ -9,18 +9,15 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; - -import { Sigil } from './sigil.js' - +import { Sigil } from './sigil.js'; import * as bitcoin from 'bitcoinjs-lib'; import * as kg from 'urbit-key-generation'; -import * as bip39 from 'bip39'; - -import Sent from './sent.js' +import Sent from './sent.js'; import { patp2dec, isValidPatq, isValidPatp } from 'urbit-ob'; - import { satsToCurrency } from '../../lib/util.js'; import Error from './error.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; const BITCOIN_MAINNET_INFO = { messagePrefix: '\x18Bitcoin Signed Message:\n', @@ -46,246 +43,234 @@ const BITCOIN_TESTNET_INFO = { wif: 0xef, }; -export default class Invoice extends Component { - constructor(props) { - super(props); +const Invoice = ({ stopSending, payee, satsAmount }) => { + const { + error, + currencyRates, + psbt, + fee, + broadcastSuccess, + network, + denomination, + } = useSettings(); + const [masterTicket, setMasterTicket] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(error); + const [broadcasting, setBroadcasting] = useState(false); + const invoiceRef = useRef(); - this.state = { - masterTicket: '', - ready: false, - error: this.props.state.error, - sent: false, - broadcasting: false, - }; - - this.checkTicket = this.checkTicket.bind(this); - this.broadCastTx = this.broadCastTx.bind(this); - this.sendBitcoin = this.sendBitcoin.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setInvoiceRef = this.setInvoiceRef.bind(this); - } - - componentDidMount(){ - document.addEventListener("click", this.clickDismiss); - } - - componentWillUnMount(){ - document.removeEventListener("click", this.clickDismiss); - } - - setInvoiceRef(n){ - this.invoiceRef = n; - } - - clickDismiss(e){ - if (this.invoiceRef && !(this.invoiceRef.contains(e.target))) { - this.props.stopSending(); + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); } - } + }, [error, broadcasting, setBroadcasting]); - componentDidUpdate(prevProps, prevState) { - if (this.state.broadcasting) { - if (this.state.error !== '') { - this.setState({broadcasting: false}); - } + const clickDismiss = (e) => { + if (invoiceRef && !invoiceRef.contains(e.target)) { + stopSending(); } - } + }; - broadCastTx(psbtHex) { + useEffect(() => { + document.addEventListener('click', clickDismiss); + return () => document.removeEventListener('click', clickDismiss); + }, []); + + const broadCastTx = (psbtHex) => { let command = { - 'broadcast-tx': psbtHex - } - return this.props.api.btcWalletCommand(command) - } + 'broadcast-tx': psbtHex, + }; + return api.btcWalletCommand(command); + }; - sendBitcoin(ticket, psbt) { + const sendBitcoin = (ticket, psbt) => { const newPsbt = bitcoin.Psbt.fromBase64(psbt); - this.setState({broadcasting: true}); - kg.generateWallet({ ticket, ship: parseInt(patp2dec('~' + window.ship)) }) - .then(urbitWallet => { - const { xpub } = this.props.network === 'testnet' - ? urbitWallet.bitcoinTestnet.keys - : urbitWallet.bitcoinMainnet.keys; + setBroadcasting(true); + kg.generateWallet({ + ticket, + ship: parseInt(patp2dec('~' + window.ship)), + }).then((urbitWallet) => { + // const { xpub } = + // network === 'testnet' + // ? urbitWallet.bitcoinTestnet.keys + // : urbitWallet.bitcoinMainnet.keys; - const { xprv: zprv } = urbitWallet.bitcoinMainnet.keys; - const { xprv: vprv } = urbitWallet.bitcoinTestnet.keys; + const { xprv: zprv } = urbitWallet.bitcoinMainnet.keys; + const { xprv: vprv } = urbitWallet.bitcoinTestnet.keys; - const isTestnet = (this.props.network === 'testnet'); - const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/"; + const isTestnet = network === 'testnet'; + const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/"; - const btcWallet = (isTestnet) - ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO) - : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO); + const btcWallet = isTestnet + ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO) + : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO); - try { - const hex = newPsbt.data.inputs - .reduce((psbt, input, idx) => { - // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0 - const path = input.bip32Derivation[0].path - .split(derivationPrefix) - .join(''); - const prv = btcWallet.derivePath(path).privateKey; - return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv)); - }, newPsbt) - .finalizeAllInputs() - .extractTransaction() - .toHex(); + try { + const hex = newPsbt.data.inputs + .reduce((psbt, input, idx) => { + // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0 + const path = input.bip32Derivation[0].path + .split(derivationPrefix) + .join(''); + const prv = btcWallet.derivePath(path).privateKey; + return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv)); + }, newPsbt) + .finalizeAllInputs() + .extractTransaction() + .toHex(); - this.broadCastTx(hex); - } - catch(e) { - this.setState({error: 'invalid-master-ticket', broadcasting: false}); - } - }); + broadCastTx(hex); + } catch (e) { + setLocalError('invalid-master-ticket'); + setBroadcasting(false); + } + }); + }; - } - - - checkTicket(e){ + const checkTicket = (e) => { // TODO: port over bridge ticket validation logic - let masterTicket = e.target.value; - let ready = isValidPatq(masterTicket); - let error = (ready) ? '' : 'invalid-master-ticket'; - this.setState({masterTicket, ready, error}); + setMasterTicket(e.target.value); + setReady(isValidPatq(e.target.value)); + setLocalError(isValidPatq(e.target.value) ? '' : 'invalid-master-ticket'); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (error !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; } - render() { - const broadcastSuccess = this.props.state.broadcastSuccess; - const { stopSending, payee, denomination, satsAmount, psbt, currencyRates, fee } = this.props; - const { sent, error } = this.state; + const isShip = isValidPatp(payee); - let inputColor = 'black'; - let inputBg = 'white'; - let inputBorder = 'lightGray'; + const icon = isShip ? ( + + ) : ( + + + + ); - if (error !== '') { - inputColor = 'red'; - inputBg = 'veryLightRed'; - inputBorder = 'red'; - } - - const isShip = isValidPatp(payee); - - const icon = (isShip) - ? - : ; - - return ( - <> - { broadcastSuccess ? - : + return ( + <> + {broadcastSuccess ? ( + + ) : ( + stopSending()} + > - - - {satsToCurrency(satsAmount, denomination, currencyRates)} - - - {`${satsAmount} sats`} - - - {`Fee: ${satsToCurrency(fee, denomination, currencyRates)} (${fee} sats)`} - - - You are paying - - - {icon} - {payee} - - - - - Ticket + + + {satsToCurrency(satsAmount, denomination, currencyRates)} - value.replace(/[^~-]+/g, '••••••')} - placeholder="••••••-••••••-••••••-••••••" - autoCapitalize="none" - autoCorrect="off" - color={inputColor} - backgroundColor={inputBg} - borderColor={inputBorder} - onChange={this.checkTicket} - /> - {(error !== '') && - - - - } - - + {broadcasting ? : null} + + + )} + + ); +}; + +export default Invoice; diff --git a/pkg/btc-wallet/src/js/components/lib/providerModal.js b/pkg/btc-wallet/src/js/components/lib/providerModal.js index 9b85b449fd..ddb7dc58dd 100644 --- a/pkg/btc-wallet/src/js/components/lib/providerModal.js +++ b/pkg/btc-wallet/src/js/components/lib/providerModal.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Text, @@ -8,159 +8,157 @@ import { Row, LoadingSpinner, } from '@tlon/indigo-react'; - import { isValidPatp } from 'urbit-ob'; +import { api } from '../../api'; +import { useSettings } from '../../hooks/useSettings'; -export default class ProviderModal extends Component { - constructor(props) { - super(props); +const providerStatuses = { + checking: 'checking', + failed: 'failed', + ready: 'ready', + initial: '', +}; - this.state = { - potentialProvider: null, - checkingProvider: false, - providerFailed: false, - ready: false, - provider: null, - connecting: false, - }; +const ProviderModal = () => { + const { providerPerms } = useSettings(); + const [providerStatus, setProviderStatus] = useState( + providerStatuses.initial + ); + const [potentialProvider, setPotentialProvider] = useState(null); + const [provider, setProvider] = useState(null); + const [connecting, setConnecting] = useState(false); - this.checkProvider = this.checkProvider.bind(this); - this.submitProvider = this.submitProvider.bind(this); - } - - checkProvider(e) { + const checkProvider = (e) => { // TODO: loading states - let provider = e.target.value; - let ready = false; - let checkingProvider = false; - let potentialProvider = this.state.potentialProvider; - - if (isValidPatp(provider)) { + setProviderStatus(providerStatuses.initial); + let givenProvider = e.target.value; + if (isValidPatp(givenProvider)) { let command = { - 'check-provider': provider, + 'check-provider': givenProvider, }; - potentialProvider = provider; - checkingProvider = true; - this.props.api.btcWalletCommand(command); + setPotentialProvider(givenProvider); + setProviderStatus(providerStatuses.checking); + api.btcWalletCommand(command); setTimeout(() => { - this.setState({ providerFailed: true, checkingProvider: false }); + setProviderStatus(providerStatuses.failed); }, 5000); } - this.setState({ provider, ready, checkingProvider, potentialProvider }); - } + setProvider(givenProvider); + }; - componentDidUpdate() { - if (!this.state.ready) { - if (this.props.providerPerms[this.state.provider]) { - this.setState({ - ready: true, - checkingProvider: false, - providerFailed: false, - }); + const submitProvider = () => { + if (providerStatus === providerStatuses.ready) { + let command = { + 'set-provider': provider, + }; + api.btcWalletCommand(command); + setConnecting(true); + } + }; + + useEffect(() => { + if (providerStatus !== providerStatuses.ready) { + if (providerPerms.provider === provider && providerPerms.permitted) { + setProviderStatus(providerStatuses.ready); } } - } + }, [providerStatus, providerPerms, provider, setProviderStatus]); - submitProvider() { - if (this.state.ready) { - let command = { - 'set-provider': this.state.provider, - }; - this.props.api.btcWalletCommand(command); - this.setState({ connecting: true }); - } - } - - render() { - let workingNode = null; - let workingColor = null; - let workingBg = null; - if (this.state.ready) { - workingColor = 'green'; - workingBg = 'veryLightGreen'; - workingNode = ( - - - {this.state.provider} is a working provider node - - - ); - } else if (this.state.providerFailed) { - workingColor = 'red'; - workingBg = 'veryLightRed'; - workingNode = ( - - - {this.state.potentialProvider} is not a working provider node - - - ); - } - - return ( - - - - - Step 1 of 2: Set up Bitcoin Provider Node - - - - - In order to perform Bitcoin transaction in Landscape, you'll - need to set a provider node. A provider node is an urbit which - maintains a synced Bitcoin ledger. - - {' '} - Learn More - - - - - - Provider Node - - - - - {this.state.checkingProvider ? : null} - - {workingNode} - - - {this.state.connecting ? : null} - + let workingNode = null; + let workingColor = null; + let workingBg = null; + if (providerStatus === providerStatuses.ready) { + workingColor = 'green'; + workingBg = 'veryLightGreen'; + workingNode = ( + + + {provider} is a working provider node + + + ); + } else if (providerStatus === providerStatuses.failed) { + workingColor = 'red'; + workingBg = 'veryLightRed'; + workingNode = ( + + + {potentialProvider} is not a working provider node + ); } -} + + return ( + + + + + Step 1 of 2: Set up Bitcoin Provider Node + + + + + In order to perform Bitcoin transaction in Landscape, you'll need + to set a provider node. A provider node is an urbit which maintains a + synced Bitcoin ledger. + + {' '} + Learn More + + + + + + Provider Node + + + + checkProvider(e)} + /> + {providerStatus === providerStatuses.checking ? ( + + ) : null} + + {workingNode} + + + {connecting ? : null} + + + ); +}; + +export default ProviderModal; diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index df5eef8f5c..9bb6cbbd8a 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Icon, @@ -8,450 +8,443 @@ import { Button, Col, LoadingSpinner, - StatelessRadioButtonField as RadioButton, } from '@tlon/indigo-react'; - -import Invoice from './invoice.js' -import BridgeInvoice from './bridgeInvoice.js' -import FeePicker from './feePicker.js' -import Error from './error.js' -import Signer from './signer.js' - +import Invoice from './invoice.js'; +import BridgeInvoice from './bridgeInvoice.js'; +import FeePicker from './feePicker.js'; +import Error from './error.js'; +import Signer from './signer.js'; import { validate } from 'bitcoin-address-validation'; - import * as ob from 'urbit-ob'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; -export default class Send extends Component { - constructor(props) { - super(props); +const focusFields = { + empty: '', + payee: 'payee', + currency: 'currency', + sats: 'sats', + note: 'note', +}; - this.state = { - signing: false, - denomAmount: '0.00', - satsAmount: '0', - payee: '', - checkingPatp: false, - payeeType: '', - ready: false, - validPayee: false, - focusPayee: true, - focusCurrency: false, - focusSats: false, - focusNote: false, - submitting: false, - feeChoices: { - low: [10, 1], - mid: [10, 1], - high: [10, 1], - }, - feeValue: "mid", - showModal: false, - note: '', - choosingSignMethod: false, - signMethod: 'bridge', - }; +const Send = ({ stopSending, value, conversion }) => { + const { error, setError, network, psbt, denomination, shipWallets } = + useSettings(); + const [signing, setSigning] = useState(false); + const [denomAmount, setDenomAmount] = useState('0.00'); + const [satsAmount, setSatsAmount] = useState('0'); + const [payee, setPayee] = useState(''); + const [checkingPatp, setCheckingPatp] = useState(false); + const [payeeType, setPayeeType] = useState(''); + const [ready, setReady] = useState(false); + const [validPayee, setValidPayee] = useState(false); + const [focusedField, setFocusedField] = useState(focusFields.empty); + const [feeChoices, setFeeChoices] = useState({ + low: [10, 1], + mid: [10, 1], + high: [10, 1], + }); + const [feeValue, setFeeValue] = useState('mid'); + const [showModal, setShowModal] = useState(false); + const [note, setNote] = useState(''); + const [choosingSignMethod, setChoosingSignMethod] = useState(false); + const [signMethod, setSignMethod] = useState('bridge'); - this.initPayment = this.initPayment.bind(this); - this.checkPayee = this.checkPayee.bind(this); - this.feeSelect = this.feeSelect.bind(this); - this.feeDismiss = this.feeDismiss.bind(this); - this.toggleSignMethod = this.toggleSignMethod.bind(this); - this.setSignMethod = this.setSignMethod.bind(this); - } + const feeDismiss = () => { + setShowModal(false); + }; - feeDismiss() { - this.setState({showModal: false}); - } + const feeSelect = (which) => { + setFeeValue(which); + }; - feeSelect(which) { - this.setState({feeValue: which}); - } - - componentDidMount(){ - if (this.props.network === 'bitcoin'){ - let url = "https://bitcoiner.live/api/fees/estimates/latest"; - fetch(url).then(res => res.json()).then(n => { - let estimates = Object.keys(n.estimates); - let mid = Math.floor(estimates.length/2) - let high = estimates.length - 1; - this.setState({ - feeChoices: { - high: [30, n.estimates[30]["sat_per_vbyte"]], - mid: [180, n.estimates[180]["sat_per_vbyte"]], - low: [360, n.estimates[360]["sat_per_vbyte"]], - } - }); - }) - } - } - - setSignMethod(signMethod) { - this.setState({signMethod, choosingSignMethod: false}); - } - - checkPayee(e){ - store.handleEvent({data: {error: ''}}); - - let payee = e.target.value; - let isPatp = ob.isValidPatp(payee); - let isAddress = validate(payee); + const handleSetSignMethod = (signMethod) => { + setSignMethod(signMethod); + setChoosingSignMethod(false); + }; + const checkPayee = (e) => { + console.log('checkPayee', { e }); + setError(''); + let payeeReceived = e.target.value; + let isPatp = ob.isValidPatp(payeeReceived); + let isAddress = validate(payeeReceived); + console.log({ payeeReceived, isPatp, isAddress }); if (isPatp) { - let command = {'check-payee': payee} - this.props.api.btcWalletCommand(command) + console.log('isPatp', isPatp); + let command = { 'check-payee': payeeReceived }; + api.btcWalletCommand(command); setTimeout(() => { - this.setState({checkingPatp: false}); + setCheckingPatp(false); }, 5000); - this.setState({ - checkingPatp: true, - payeeType: 'ship', - payee, - }); + setCheckingPatp(true); + setPayeeType('ship'); + setPayee(payeeReceived); } else if (isAddress) { - this.setState({ - payee, - ready: true, - checkingPatp: false, - payeeType: 'address', - validPayee: true, - }); + setPayee(payeeReceived); + setReady(true); + setCheckingPatp(false); + setPayeeType('address'); + setValidPayee(true); } else { - this.setState({ - payee, - ready: false, - checkingPatp: false, - payeeType: '', - validPayee: false, - }); + setPayee(payeeReceived); + setReady(false); + setCheckingPatp(false); + setPayeeType(''); + setValidPayee(false); } - } + }; - componentDidUpdate(prevProps, prevState) { - if ((prevProps.error !== this.props.error) && - (this.props.error !== '') && (this.props.error !== 'broadcast-fail')) { - this.setState({signing: false}); - } + const toggleSignMethod = () => { + setChoosingSignMethod(!choosingSignMethod); + }; - if (!this.state.ready && this.state.checkingPatp) { - if (this.props.shipWallets[this.state.payee.slice(1)]) { - this.setState({ready: true, checkingPatp: false, validPayee: true}); - } - } - } - - toggleSignMethod(toggle) { - this.setState({choosingSignMethod: !toggle}); - } - - initPayment() { - if (this.state.payeeType === 'ship') { + const initPayment = () => { + if (payeeType === 'ship') { let command = { 'init-payment': { - 'payee': this.state.payee, - 'value': parseInt(this.state.satsAmount), - 'feyb': this.state.feeChoices[this.state.feeValue][1], - 'note': (this.state.note || null), - } - } - this.props.api.btcWalletCommand(command).then(res => this.setState({signing: true})); - } else if (this.state.payeeType === 'address') { + payee, + value: parseInt(satsAmount), + feyb: feeChoices[feeValue][1], + note: note || null, + }, + }; + + api.btcWalletCommand(command).then(() => setSigning(true)); + } else if (payeeType === 'address') { let command = { 'init-payment-external': { - 'address': this.state.payee, - 'value': parseInt(this.state.satsAmount), - 'feyb': 1, - 'note': (this.state.note || null), - } - } - this.props.api.btcWalletCommand(command).then(res => this.setState({signing: true})); + address: payee, + value: parseInt(satsAmount), + feyb: 1, + note: note || null, + }, + }; + api.btcWalletCommand(command).then(() => setSigning(true)); } + }; + + useEffect(() => { + if (network === 'bitcoin') { + let url = 'https://bitcoiner.live/api/fees/estimates/latest'; + fetch(url) + .then((res) => res.json()) + .then((n) => { + // let estimates = Object.keys(n.estimates); + // let mid = Math.floor(estimates.length / 2); + // let high = estimates.length - 1; + setFeeChoices({ + high: [30, n.estimates[30]['sat_per_vbyte']], + mid: [180, n.estimates[180]['sat_per_vbyte']], + low: [360, n.estimates[360]['sat_per_vbyte']], + }); + }); + } + }, []); + + useEffect(() => { + if (!ready && checkingPatp) { + console.log({ ready, checkingPatp, shipWallets, payee }); + if (shipWallets.payee === payee.slice(1) && shipWallets.hasWallet) { + console.log('good'); + setReady(true); + setCheckingPatp(false); + setValidPayee(true); + } + } + }, [ready, checkingPatp, shipWallets]); + + let payeeColor = 'black'; + let payeeBg = 'white'; + let payeeBorder = 'lightGray'; + if (error) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } else if (focusedField === focusFields.payee && validPayee) { + payeeColor = 'green'; + payeeBorder = 'green'; + payeeBg = 'veryLightGreen'; + } else if (!focusedField === focusFields.payee && validPayee) { + payeeColor = 'blue'; + payeeBorder = 'white'; + payeeBg = 'white'; + } else if (focusedField !== focusFields.payee && !validPayee) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } else if ( + focusedField === 'payee' && + !validPayee && + !checkingPatp && + payeeType === 'ship' + ) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; } - render() { - let payeeColor = "black"; - let payeeBg = "white"; - let payeeBorder = "lightGray"; - if (this.props.error) { - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } else if (this.state.focusPayee && this.state.validPayee) { - payeeColor = "green"; - payeeBorder = "green"; - payeeBg = "veryLightGreen"; - } else if (!this.state.focusPayee && this.state.validPayee){ - payeeColor="blue"; - payeeBorder = "white"; - payeeBg = "white"; - } else if (!this.state.focusPayee && !this.state.validPayee) { - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } else if (this.state.focusPayee && - !this.state.validPayee && - !this.state.checkingPatp && - this.state.payeeType === 'ship'){ - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } + const signReady = ready && parseInt(satsAmount) > 0 && !signing; - - const { api, value, conversion, stopSending, denomination, psbt, currencyRates, error, network, fee } = this.props; - const { denomAmount, satsAmount, signing, payee, choosingSignMethod, signMethod } = this.state; - - const signReady = (this.state.ready && (parseInt(this.state.satsAmount) > 0)) && !signing; - - let invoice = null; - if (signMethod === 'masterTicket') { - invoice = - - } else if (signMethod === 'bridge') { - invoice = - - } - - return ( - <> - { (signing && psbt) ? invoice : - - - - Send BTC - {value} - stopSending()} - /> - - - - To - {this.state.checkingPatp ? - : null - } - - {this.setState({focusPayee: true})}} - onBlur={() => {this.setState({focusPayee: false})}} - color={payeeColor} - backgroundColor={payeeBg} - borderColor={payeeBorder} - ml={2} - flexGrow="1" - fontSize='14px' - placeholder='~sampel-palnet or BTC address' - value={payee} - fontFamily="mono" - disabled={signing} - onChange={this.checkPayee} - /> - - {error && - - {/* yes this is a hack */} - - - - } - - Amount - {this.setState({focusCurrency: true})}} - onBlur={() => {this.setState({focusCurrency: false})}} - fontSize='14px' - width='100%' - type='number' - borderColor={this.state.focusCurrency ? "lightGray" : "none"} - disabled={signing} - value={denomAmount} - onChange={e => { - this.setState({ - denomAmount: e.target.value, - satsAmount: Math.round(parseFloat(e.target.value) / conversion * 100000000) - }); - }} - /> - {denomination} - - - {/* yes this is a hack */} - - {this.setState({focusSats: true})}} - onBlur={() => {this.setState({focusSats: false})}} - fontSize='14px' - width='100%' - type='number' - borderColor={this.state.focusSats ? "lightGray" : "none"} - disabled={signing} - value={satsAmount} - onChange={e => { - this.setState({ - denomAmount: parseFloat(e.target.value) * (conversion / 100000000), - satsAmount: e.target.value - }); - }} - /> - sats - - - Fee - - - {this.state.feeChoices[this.state.feeValue][1]} sats/vbyte - - {if (!this.state.showModal) this.setState({showModal: true}); }} - cursor="pointer"/> - - - - {!this.state.showModal ? null : - - } - - - Note - {this.setState({focusNote: true})}} - onBlur={() => {this.setState({focusNote: false})}} - fontSize='14px' - width='100%' - placeholder="What's this for?" - type='text' - borderColor={this.state.focusNote ? "lightGray" : "none"} - disabled={signing} - value={this.state.note} - onChange={e => { - this.setState({ - note: e.target.value, - }); - }} - /> - - - - - { (!(signing && !error)) ? null : - - } - + + {signMethod === 'masterTicket' && ( + + + + We recommend that you sign transactions using Bridge to protect + your master ticket. + + + )} + + )} + + ); +}; + +export default Send; diff --git a/pkg/btc-wallet/src/js/components/lib/sent.js b/pkg/btc-wallet/src/js/components/lib/sent.js index 994b02b97f..be4a8e1093 100644 --- a/pkg/btc-wallet/src/js/components/lib/sent.js +++ b/pkg/btc-wallet/src/js/components/lib/sent.js @@ -1,59 +1,36 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - StatelessTextInput as Input, - Row, - Center, - Text, - Button, - Col, -} from '@tlon/indigo-react'; - +import React from 'react'; +import { Icon, Row, Col, Center, Text } from '@tlon/indigo-react'; import { satsToCurrency } from '../../lib/util.js'; +import { useSettings } from '../../hooks/useSettings'; -export default function Sent(props) { - const { payee, denomination, satsAmount, stopSending, currencyRates } = props; +const Sent = ({ payee, stopSending, satsAmount }) => { + const { denomination, currencyRates } = useSettings(); return ( - - + +
{`You sent BTC to ${payee}`} + style={{ display: 'block', 'overflow-wrap': 'anywhere' }} + color="white" + >{`You sent BTC to ${payee}`}
-
- +
+ {satsToCurrency(satsAmount, denomination, currencyRates)} - - {`${satsAmount} sats`} - + {`${satsAmount} sats`}
); -} +}; + +export default Sent; diff --git a/pkg/btc-wallet/src/js/components/lib/settings.js b/pkg/btc-wallet/src/js/components/lib/settings.js index 445e527504..352cb5500f 100644 --- a/pkg/btc-wallet/src/js/components/lib/settings.js +++ b/pkg/btc-wallet/src/js/components/lib/settings.js @@ -1,121 +1,114 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Row, Text, Button, Col } from '@tlon/indigo-react'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../api'; -export default class Settings extends Component { - constructor(props) { - super(props); - this.changeProvider = this.changeProvider.bind(this); - this.replaceWallet = this.replaceWallet.bind(this); - } +const Settings = () => { + const { wallet, provider } = useSettings(); - changeProvider(){ - this.props.api.btcWalletCommand({'set-provider': null}); - } + const changeProvider = () => { + api.btcWalletCommand({ 'set-provider': null }); + }; - replaceWallet(){ - this.props.api.btcWalletCommand({ - 'delete-wallet': this.props.state.wallet, + const replaceWallet = () => { + api.btcWalletCommand({ + 'delete-wallet': wallet, }); - } + }; - render() { - let connColor = "red"; - let connBackground = "veryLightRed"; - let conn = 'Offline' - let host = ''; - if (this.props.state.provider){ - if (this.props.state.provider.connected) conn = 'Connected'; - if (this.props.state.provider.host) host = this.props.state.provider.host; - if (this.props.state.provider.connected && this.props.state.provider.host) { - connColor = "orange"; - connBackground = "lightOrange"; - } + let connColor = 'red'; + let connBackground = 'veryLightRed'; + let conn = 'Offline'; + let host = ''; + if (provider) { + if (provider.connected) conn = 'Connected'; + if (provider.host) host = provider.host; + if (provider.connected && provider.host) { + connColor = 'orange'; + connBackground = 'lightOrange'; } - - return ( - - - - XPub Derivation - - - - - {this.props.state.wallet} - - - - + + + + BTC Node Provider + + + + + ~{host} + + + {conn} + + + + + + + ); +}; + +export default Settings; diff --git a/pkg/btc-wallet/src/js/components/lib/signer.js b/pkg/btc-wallet/src/js/components/lib/signer.js index 094a6e8dd4..df371f76f5 100644 --- a/pkg/btc-wallet/src/js/components/lib/signer.js +++ b/pkg/btc-wallet/src/js/components/lib/signer.js @@ -1,52 +1,55 @@ -import React, { Component } from 'react'; +import React from 'react'; +import { Box, Button } from '@tlon/indigo-react'; -import { - Box, - Button, -} from '@tlon/indigo-react'; - -export default function Signer(props) { - const { signReady, initPayment, choosingSignMethod, signMethod, setSignMethod } = props; - - return ( - choosingSignMethod ? - +const Signer = ({ + signReady, + initPayment, + choosingSignMethod, + signMethod, + setSignMethod, +}) => { + return choosingSignMethod ? ( + - : + ) : ( + ); +}; + +export default Signer; diff --git a/pkg/btc-wallet/src/js/components/lib/startupModal.js b/pkg/btc-wallet/src/js/components/lib/startupModal.js index 9507452ebc..c4ee50c0a7 100644 --- a/pkg/btc-wallet/src/js/components/lib/startupModal.js +++ b/pkg/btc-wallet/src/js/components/lib/startupModal.js @@ -1,52 +1,44 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Box } from '@tlon/indigo-react'; +import WalletModal from './walletModal.js'; +import ProviderModal from './providerModal.js'; +import { useSettings } from '../../hooks/useSettings.js'; -import WalletModal from './walletModal.js' -import ProviderModal from './providerModal.js' +const StartupModal = () => { + const { wallet, provider } = useSettings(); + let modal = null; - -export default class StartupModal extends Component { - constructor(props) { - super(props); + if (wallet && provider) { + return null; + } else if (!provider) { + modal = ; + } else if (!wallet) { + modal = ; } - - - render() { - let modal = null; - - if (this.props.state.wallet && this.props.state.provider) { - return null; - } else if (!this.props.state.provider){ - modal = - - } else if (!this.props.state.wallet){ - modal = - } - return ( + return ( + - - {modal} - + {modal} - ); - } -} + + ); +}; + +export default StartupModal; diff --git a/pkg/btc-wallet/src/js/components/lib/transaction.js b/pkg/btc-wallet/src/js/components/lib/transaction.js index ba18a32aca..10b9a50951 100644 --- a/pkg/btc-wallet/src/js/components/lib/transaction.js +++ b/pkg/btc-wallet/src/js/components/lib/transaction.js @@ -1,105 +1,96 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, - LoadingSpinner, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Row, Text, Col } from '@tlon/indigo-react'; import _ from 'lodash'; +import TxAction from './tx-action.js'; +import TxCounterparty from './tx-counterparty.js'; +import { satsToCurrency } from '../../lib/util.js'; +import { useSettings } from '../../hooks/useSettings.js'; -import { Sigil } from './sigil.js' -import TxAction from './tx-action.js' -import TxCounterparty from './tx-counterparty.js' -import { satsToCurrency } from '../../lib/util.js' +const Transaction = ({ tx }) => { + const { denomination, currencyRates } = useSettings(); + const pending = !tx.recvd; -export default class Transaction extends Component { - constructor(props) { - super(props); - } + let weSent = _.find(tx.inputs, (input) => { + return input.ship === window.ship; + }); + let weRecv = tx.outputs.every((output) => { + return output.ship === window.ship; + }); - render() { - const pending = (!this.props.tx.recvd); + let action = weRecv ? 'recv' : weSent ? 'sent' : 'recv'; - let weSent = _.find(this.props.tx.inputs, (input) => { - return (input.ship === window.ship); + let counterShip = null; + let counterAddress = null; + let value; + let sign; + + if (action === 'sent') { + let counter = _.find(tx.outputs, (output) => { + return output.ship !== window.ship; }); - let weRecv = this.props.tx.outputs.every((output) => { - return (output.ship === window.ship) - }); - - let action = - (weRecv) ? "recv" : - (weSent) ? "sent" : "recv"; - - let counterShip = null; - let counterAddress = null; - let value; - let sign; - - if (action === "sent") { - let counter = _.find(this.props.tx.outputs, (output) => { - return (output.ship !== window.ship); - }); - counterShip = _.get(counter, 'ship', null); - counterAddress = _.get(counter, 'val.address', null); - value = _.get(counter, 'val.value', null); - sign = '-' - } - else if (action === "recv") { - value = _.reduce(this.props.tx.outputs, (sum, output) => { + counterShip = _.get(counter, 'ship', null); + counterAddress = _.get(counter, 'val.address', null); + value = _.get(counter, 'val.value', null); + sign = '-'; + } else if (action === 'recv') { + value = _.reduce( + tx.outputs, + (sum, output) => { if (output.ship === window.ship) { return sum + output.val.value; } else { return sum; } - }, 0); - - - if (weSent && weRecv) { - counterAddress = _.get(_.find(this.props.tx.inputs, (input) => { - return (input.ship === window.ship); - }), 'val.address', null); - } else { - let counter = _.find(this.props.tx.inputs, (input) => { - return (input.ship !== window.ship); - }); - counterShip = _.get(counter, 'ship', null); - counterAddress = _.get(counter, 'val.address', null); - } - sign = ''; - } - - let currencyValue = sign + satsToCurrency(value, this.props.denom, this.props.rates); - - const failure = Boolean(this.props.tx.failure); - if (failure) action = "fail"; - - const txid = this.props.tx.txid.dat.slice(2).replaceAll('.',''); - - - return ( - - - - - {sign}{value} sats - - - - - - - {currencyValue} - - + }, + 0 ); + + if (weSent && weRecv) { + counterAddress = _.get( + _.find(tx.inputs, (input) => { + return input.ship === window.ship; + }), + 'val.address', + null + ); + } else { + let counter = _.find(tx.inputs, (input) => { + return input.ship !== window.ship; + }); + counterShip = _.get(counter, 'ship', null); + counterAddress = _.get(counter, 'val.address', null); + } + sign = ''; } -} + + let currencyValue = sign + satsToCurrency(value, denomination, currencyRates); + + const failure = Boolean(tx.failure); + if (failure) action = 'fail'; + + const txid = tx.txid.dat.slice(2).replaceAll('.', ''); + + return ( + + + + + {sign} + {value} sats + + + + + + {currencyValue} + + + ); +}; + +export default Transaction; diff --git a/pkg/btc-wallet/src/js/components/lib/transactions.js b/pkg/btc-wallet/src/js/components/lib/transactions.js index a038cd0296..2bd7619956 100644 --- a/pkg/btc-wallet/src/js/components/lib/transactions.js +++ b/pkg/btc-wallet/src/js/components/lib/transactions.js @@ -1,62 +1,43 @@ import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, -} from '@tlon/indigo-react'; - +import { Box, Text, Col } from '@tlon/indigo-react'; import Transaction from './transaction.js'; +import { useSettings } from '../../hooks/useSettings.js'; - -export default class Transactions extends Component { - constructor(props) { - super(props); +const Transactions = () => { + const { history } = useSettings(); + if (!history || history.length <= 0) { + return ( + + + No Transactions Yet + + + ); + } else { + return ( + + {history.map((tx, i) => { + return ; + })} + + ); } +}; - - render() { - if (!this.props.state.history || this.props.state.history.length <= 0) { - return ( - - No Transactions Yet - - ); - } else { - return ( - - { - this.props.state.history.map((tx, i) => { - return( - - ); - }) - } - - ); - } - } -} +export default Transactions; diff --git a/pkg/btc-wallet/src/js/components/lib/tx-action.js b/pkg/btc-wallet/src/js/components/lib/tx-action.js index a8434f5155..12405f4934 100644 --- a/pkg/btc-wallet/src/js/components/lib/tx-action.js +++ b/pkg/btc-wallet/src/js/components/lib/tx-action.js @@ -1,74 +1,72 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Box, Icon, Row, Text, LoadingSpinner } from '@tlon/indigo-react'; +import { useSettings } from '../../hooks/useSettings'; -export default class TxAction extends Component { - constructor(props) { - super(props); - } +const TxAction = ({ action, pending, txid }) => { + const { network } = useSettings(); + const leftIcon = + action === 'sent' + ? 'ArrowSouth' + : action === 'recv' + ? 'ArrowNorth' + : action === 'fail' + ? 'X' + : 'NullIcon'; - render() { - const leftIcon = - this.props.action === 'sent' - ? 'ArrowSouth' - : this.props.action === 'recv' - ? 'ArrowNorth' - : this.props.action === 'fail' - ? 'X' - : 'NullIcon'; + const actionColor = + action === 'sent' + ? 'sentBlue' + : action === 'recv' + ? 'recvGreen' + : action === 'fail' + ? 'gray' + : 'red'; - const actionColor = - this.props.action === 'sent' - ? 'sentBlue' - : this.props.action === 'recv' - ? 'recvGreen' - : this.props.action === 'fail' - ? 'gray' - : 'red'; + const actionText = + action === 'sent' && !pending + ? 'Sent BTC' + : action === 'sent' && pending + ? 'Sending BTC' + : action === 'recv' && !pending + ? 'Received BTC' + : action === 'recv' && pending + ? 'Receiving BTC' + : action === 'fail' + ? 'Failed' + : 'error'; - const actionText = - this.props.action === 'sent' && !this.props.pending - ? 'Sent BTC' - : this.props.action === 'sent' && this.props.pending - ? 'Sending BTC' - : this.props.action === 'recv' && !this.props.pending - ? 'Received BTC' - : this.props.action === 'recv' && this.props.pending - ? 'Receiving BTC' - : this.props.action === 'fail' - ? 'Failed' - : 'error'; + const pendingSpinner = !pending ? null : ( + + ); - const pending = !this.props.pending ? null : ( - - ); + const url = + network === 'testnet' + ? `http://blockstream.info/testnet/tx/${txid}` + : `http://blockstream.info/tx/${txid}`; - const url = - this.props.network === 'testnet' - ? `http://blockstream.info/testnet/tx/${this.props.txid}` - : `http://blockstream.info/tx/${this.props.txid}`; + return ( + + + + + + {actionText} + + + + + {pendingSpinner} + + ); +}; - return ( - - - - - - {actionText} - - - - - {pending} - - ); - } -} +export default TxAction; diff --git a/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js b/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js index 27bf40d573..76cf2fd286 100644 --- a/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js +++ b/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js @@ -1,53 +1,36 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Icon, Row, Text } from '@tlon/indigo-react'; +import { Sigil } from './sigil.js'; -import { Sigil } from './sigil.js' -import TxAction from './tx-action.js' +const TxCounterparty = ({ ship, address }) => { + const icon = ship ? ( + + ) : ( + + + + ); + const addressText = !address + ? '' + : address.slice(0, 6) + '...' + address.slice(-6); + const text = ship ? `~${ship}` : addressText; -export default class TxCounterparty extends Component { - constructor(props) { - super(props); - } + return ( + + {icon} + + {text} + + + ); +}; - - render() { - const icon = (this.props.ship) - ? - : - - - const addressText = (!this.props.address) ? '' : - this.props.address.slice(0, 6) + '...' + - this.props.address.slice(-6); - const text = (this.props.ship) ? - `~${this.props.ship}` : addressText; - - return ( - - {icon} - {text} - - ); - } -} +export default TxCounterparty; diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/js/components/lib/walletModal.js index 49796cd11e..a34a596c03 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/js/components/lib/walletModal.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useState } from 'react'; import { Box, Text, @@ -6,247 +6,251 @@ import { StatelessTextInput, Icon, Row, - Input, LoadingSpinner, } from '@tlon/indigo-react'; - import { patp2dec, isValidPatq } from 'urbit-ob'; +import * as kg from 'urbit-key-generation'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../api'; -const kg = require('urbit-key-generation'); -const bitcoin = require('bitcoinjs-lib'); -const bs58check = require('bs58check') -import { Buffer } from 'buffer'; +const WalletModal = () => { + const { network } = useSettings(); + const [mode, setMode] = useState('xpub'); + const [masterTicket, setMasterTicket] = useState(''); + const [confirmedMasterTicket, setConfirmedMasterTicket] = useState(''); + const [xpub, setXpub] = useState(''); + const [readyToSubmit, setReadyToSubmit] = useState(false); + const [processingSubmission, setProcessingSubmission] = useState(false); + const [confirmingMasterTicket, setConfirmingMasterTicket] = useState(false); + const [error, setError] = useState(false); -export default class WalletModal extends Component { - constructor(props) { - super(props); - - this.state = { - mode: 'xpub', - masterTicket: '', - confirmedMasterTicket: '', - xpub: '', - readyToSubmit: false, - processingSubmission: false, - confirmingMasterTicket: false, - error: false, - } - this.checkTicket = this.checkTicket.bind(this); - this.checkXPub = this.checkXPub.bind(this); - this.submitMasterTicket = this.submitMasterTicket.bind(this); - this.submitXPub = this.submitXPub.bind(this); - - } - - checkTicket(e){ + const checkTicket = ({ + event: { + target: { value }, + }, + }) => { // TODO: port over bridge ticket validation logic - if (this.state.confirmingMasterTicket) { - let confirmedMasterTicket = e.target.value; - let readyToSubmit = isValidPatq(confirmedMasterTicket); - this.setState({confirmedMasterTicket, readyToSubmit}); + if (confirmingMasterTicket) { + setConfirmedMasterTicket(value); + setReadyToSubmit(isValidPatq(value)); } else { - let masterTicket = e.target.value; - let readyToSubmit = isValidPatq(masterTicket); - this.setState({masterTicket, readyToSubmit}); + setMasterTicket(value); + setReadyToSubmit(isValidPatq(value)); } - } + }; - checkXPub(e){ - let xpub = e.target.value; - let readyToSubmit = (xpub.length > 0); - this.setState({xpub, readyToSubmit}); - } + const checkXPub = ({ + event: { + target: { value: xpubGiven }, + }, + }) => { + setXpub(xpubGiven); + setReadyToSubmit(xpubGiven.length > 0); + }; - submitMasterTicket(ticket){ - this.setState({processingSubmission: true}); - kg.generateWallet({ ticket, ship: parseInt(patp2dec('~' + window.ship)) }) - .then(urbitWallet => { - const { xpub } = this.props.network === 'testnet' - ? urbitWallet.bitcoinTestnet.keys : - urbitWallet.bitcoinMainnet.keys - - this.submitXPub(xpub); - }); - - } - - submitXPub(xpub){ + const submitXPub = (givenXpub) => { const command = { - "add-wallet": { - "xpub": xpub, - "fprint": [4, 0], - "scan-to": null, - "max-gap": 8, - "confs": 1 - } - } + 'add-wallet': { + xpub: givenXpub, + fprint: [4, 0], + 'scan-to': null, + 'max-gap': 8, + confs: 1, + }, + }; api.btcWalletCommand(command); - this.setState({processingSubmission: true}); - } + setProcessingSubmission(true); + }; - render() { - const buttonDisabled = (!this.state.readyToSubmit || this.state.processingSubmission ); - const inputDisabled = this.state.processingSubmission; - const processingSpinner = (!this.state.processingSubmission) ? null : - + const submitMasterTicket = (ticket) => { + setProcessingSubmission(true); + kg.generateWallet({ + ticket, + ship: parseInt(patp2dec('~' + window.ship)), + }).then((urbitWallet) => { + const { xpub: xpubFromWallet } = + network === 'testnet' + ? urbitWallet.bitcoinTestnet.keys + : urbitWallet.bitcoinMainnet.keys; - if (this.state.mode === 'masterTicket'){ - return ( - - - - - Step 2 of 2: Import your extended public key - - - - - - We recommend that you import your wallet using Bridge to protect your master ticket. - - - - {this.state.confirmingMasterTicket && - this.setState({ - confirmingMasterTicket: false, - masterTicket: '', - confirmedMasterTicket: '', - error: false - })}/>} - - {this.state.confirmingMasterTicket ? 'Confirm Master Ticket' : 'Master Ticket'} - - - - value.replace(/[^~-]+/g, '••••••')} - placeholder="••••••-••••••-••••••-••••••" - autoCapitalize="none" - autoCorrect="off" - onChange={this.checkTicket} - /> - {(!inputDisabled) ? null : } - - {this.state.error && - - - Master tickets do not match - - - } - - + + + + ); + } else if (mode === 'xpub') { + return ( + + + + + Step 2 of 2: Import your extended public key + + + + + Visit{' '} + + bridge.urbit.org + {' '} + to obtain your key + - ); - } + + + Extended Public Key (XPub) + + + checkXPub(e)} + /> + + { + if (inputDisabled) return; + setMode('masterTicket'); + setXpub(''); + setMasterTicket(''); + setReadyToSubmit(false); + }} + > + Import using master ticket -> + + + + + ); } -} +}; + +export default WalletModal; diff --git a/pkg/btc-wallet/src/js/components/lib/warning.js b/pkg/btc-wallet/src/js/components/lib/warning.js index 0feaf75ada..d0183b7e50 100644 --- a/pkg/btc-wallet/src/js/components/lib/warning.js +++ b/pkg/btc-wallet/src/js/components/lib/warning.js @@ -1,79 +1,72 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, - Anchor, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Text, Button, Col, Anchor } from '@tlon/indigo-react'; +import { api } from '../../api'; +import { useSettings } from '../../hooks/useSettings'; -import { store } from '../../store' - - - -export default class Warning extends Component { - constructor(props) { - super(props); - - this.understand = this.understand.bind(this); - } - - understand(){ - store.handleEvent({ data: { bucket: { warning: false}}}); +const Warning = () => { + const { setWarning } = useSettings(); + const understand = () => { + setWarning(false); let removeWarning = { - "put-entry": { + 'put-entry': { value: false, - "entry-key": "warning", - "bucket-key": "btc-wallet", - } - } - this.props.api.settingsEvent(removeWarning); - } + 'entry-key': 'warning', + 'bucket-key': 'btc-wallet', + }, + }; + api.settingsEvent(removeWarning); + }; - render() { - return ( - + + + Warning! + +
+ + Be safe while using this wallet, and be sure to store responsible + amounts of BTC. + + + Always ensure that the checksum of the wallet matches that of the + wallet's repo. + +
+ + + Learn more on urbit.org + + + + +
+ ); +}; + +export default Warning; diff --git a/pkg/btc-wallet/src/js/components/root.js b/pkg/btc-wallet/src/js/components/root.js index 082ffe68b4..e3b6288cf0 100644 --- a/pkg/btc-wallet/src/js/components/root.js +++ b/pkg/btc-wallet/src/js/components/root.js @@ -1,67 +1,39 @@ -import React, { Component } from 'react'; +import React from 'react'; import { BrowserRouter } from 'react-router-dom'; -import { api } from '../api.js'; -import { store } from '../store.js'; import { ThemeProvider } from 'styled-components'; import light from './themes/light'; -// import dark from './themes/dark'; import { Box, Reset } from '@tlon/indigo-react'; import StartupModal from './lib/startupModal.js'; import Body from './lib/body.js'; -import { subscription } from '../subscription.js'; +import { useSettings } from '../hooks/useSettings.js'; -const network = 'bitcoin'; +const Root = () => { + const { loaded, wallet, provider } = useSettings(); + const blur = !loaded ? false : !(wallet && provider); -export class Root extends Component { - constructor(props) { - super(props); - this.ship = window.ship; - this.state = store.state; - store.setStateHandler(this.setState.bind(this)); - } + return ( + + + + {loaded ? : null} + + + + + + ); +}; - componentDidMount() { - this.props.channel.setOnChannelError(() => { - subscription.start(); - }); - subscription.start(); - } - - render() { - const loaded = this.state.loaded; - const warning = this.state.showWarning; - const blur = !loaded ? false : !(this.state.wallet && this.state.provider); - - return ( - - - - {loaded ? ( - - ) : null} - - - - - - ); - } -} +export default Root; diff --git a/pkg/btc-wallet/src/js/lib/util.js b/pkg/btc-wallet/src/js/lib/util.js index 2bc6f7a404..131ec4012c 100644 --- a/pkg/btc-wallet/src/js/lib/util.js +++ b/pkg/btc-wallet/src/js/lib/util.js @@ -1,21 +1,17 @@ -import _ from 'lodash'; -import classnames from 'classnames'; - - export function uuid() { - let str = "0v" - str += Math.ceil(Math.random()*8)+"." + let str = '0v'; + str += Math.ceil(Math.random() * 8) + '.'; for (var i = 0; i < 5; i++) { - let _str = Math.ceil(Math.random()*10000000).toString(32); - _str = ("00000"+_str).substr(-5,5); - str += _str+"."; + let _str = Math.ceil(Math.random() * 10000000).toString(32); + _str = ('00000' + _str).substr(-5, 5); + str += _str + '.'; } - return str.slice(0,-1); + return str.slice(0, -1); } export function isPatTa(str) { - const r = /^[a-z,0-9,\-,\.,_,~]+$/.exec(str) + const r = /^[a-z,0-9,\-,.,_,~]+$/.exec(str); return !!r; } @@ -26,13 +22,15 @@ export function isPatTa(str) { (javascript Date object) */ export function daToDate(st) { - var dub = function(n) { - return parseInt(n) < 10 ? "0" + parseInt(n) : n.toString(); + var dub = function (n) { + return parseInt(n) < 10 ? '0' + parseInt(n) : n.toString(); }; var da = st.split('..'); var bigEnd = da[0].split('.'); var lilEnd = da[1].split('.'); - var ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub(lilEnd[0])}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`; + var ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub( + lilEnd[0] + )}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`; return new Date(ds); } @@ -44,18 +42,18 @@ export function daToDate(st) { */ export function dateToDa(d, mil) { -  var fil = function(n) { -    return n >= 10 ? n : "0" + n; -  }; -  return ( -    `~${d.getUTCFullYear()}.` + -    `${(d.getUTCMonth() + 1)}.` + -    `${fil(d.getUTCDate())}..` + -    `${fil(d.getUTCHours())}.` + -    `${fil(d.getUTCMinutes())}.` + -    `${fil(d.getUTCSeconds())}` + - `${mil ? "..0000" : ""}` -  ); + var fil = function (n) { + return n >= 10 ? n : '0' + n; + }; + return ( + `~${d.getUTCFullYear()}.` + + `${d.getUTCMonth() + 1}.` + + `${fil(d.getUTCDate())}..` + + `${fil(d.getUTCHours())}.` + + `${fil(d.getUTCMinutes())}.` + + `${fil(d.getUTCSeconds())}` + + `${mil ? '..0000' : ''}` + ); } export function deSig(ship) { @@ -64,49 +62,59 @@ export function deSig(ship) { // trim patps to match dojo, chat-cli export function cite(ship) { - let patp = ship, shortened = ""; - if (patp.startsWith("~")) { + let patp = ship, + shortened = ''; + if (patp.startsWith('~')) { patp = patp.substr(1); } // comet if (patp.length === 56) { - shortened = "~" + patp.slice(0, 6) + "_" + patp.slice(50, 56); + shortened = '~' + patp.slice(0, 6) + '_' + patp.slice(50, 56); return shortened; } // moon if (patp.length === 27) { - shortened = "~" + patp.slice(14, 20) + "^" + patp.slice(21, 27); + shortened = '~' + patp.slice(14, 20) + '^' + patp.slice(21, 27); return shortened; } return `~${patp}`; } -export function satsToCurrency(sats, denomination, rates){ +export function satsToCurrency(sats, denomination, rates) { if (!rates) { - throw "nonexistent currency table" + throw 'nonexistent currency table'; } - if (!rates[denomination]){ - denomination = "BTC"; + if (!rates[denomination]) { + denomination = 'BTC'; } let rate = rates[denomination]; - let val = parseFloat(((sats * rate.last) * 0.00000001).toFixed(8)); + let val = rate ? parseFloat((sats * rate.last * 0.00000001).toFixed(8)) : 0; let text; - if (denomination === 'BTC'){ - text = val + ' ' + rate.symbol - } else { - text = rate.symbol + val.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') + if (denomination === 'BTC' && rate) { + text = val + ' ' + rate.symbol; + } else if (rate) { + text = + rate.symbol + val.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); } - return text + return text; } -export function currencyToSats(val, denomination, rates){ +export function currencyToSats(val, denomination, rates) { if (!rates) { - throw "nonexistent currency table" + throw 'nonexistent currency table'; } - if (!rates[denomination]){ - throw 'currency not in table' + if (!rates[denomination]) { + throw 'currency not in table'; } let rate = rates[denomination]; let sats = (parseFloat(val) / rate.last) * 100000000; return sats; } + +export function reduceHistory(history) { + return Object.values(history).sort((hest1, hest2) => { + if (hest1.recvd === null) return -1; + if (hest2.recvd === null) return +1; + return hest2.recvd - hest1.recvd; + }); +} diff --git a/pkg/btc-wallet/src/js/reducers/currency.js b/pkg/btc-wallet/src/js/reducers/currency.js deleted file mode 100644 index 80470bce42..0000000000 --- a/pkg/btc-wallet/src/js/reducers/currency.js +++ /dev/null @@ -1,20 +0,0 @@ -import _ from 'lodash'; - -export class CurrencyReducer { - reduce(json, state) { - if (!json) { - return; - } - if (json.currencyRates) { - for (var c in json.currencyRates) { - state.currencyRates[c] = json.currencyRates[c]; - } - } - - if (json.denomination) { - if (state.currencyRates[json.denomination]) { - state.denomination = json.denomination - } - } - } -} diff --git a/pkg/btc-wallet/src/js/reducers/initial.js b/pkg/btc-wallet/src/js/reducers/initial.js deleted file mode 100644 index 2445eae6b8..0000000000 --- a/pkg/btc-wallet/src/js/reducers/initial.js +++ /dev/null @@ -1,29 +0,0 @@ -import _ from 'lodash'; - -export class InitialReducer { - reduce(json, state) { - let data = _.get(json, 'initial', false); - if (data) { - state.provider = data.provider; - state.wallet = data.wallet; - state.confirmedBalance = _.get(data.balance, 'confirmed', null); - state.unconfirmedBalance = _.get(data.balance, 'unconfirmed', null); - state.btcState = data['btc-state']; - state.history = this.reduceHistory(data.history); - state.address = data.address; - - state.loadedBtc = true; - if (state.loadedSettings) { - state.loaded = true; - } - } - } - - reduceHistory(history) { - return Object.values(history).sort((hest1, hest2) => { - if (hest1.recvd === null) return -1; - if (hest2.recvd === null) return +1; - return (hest2.recvd - hest1.recvd) - }) - } -} diff --git a/pkg/btc-wallet/src/js/reducers/settings.js b/pkg/btc-wallet/src/js/reducers/settings.js deleted file mode 100644 index dce62613bb..0000000000 --- a/pkg/btc-wallet/src/js/reducers/settings.js +++ /dev/null @@ -1,30 +0,0 @@ -import _ from 'lodash'; - -export class SettingsReducer { - reduce(json, state) { - let data = _.get(json, 'bucket', false); - if (data) { - let warning = _.get(json, 'bucket.warning', -1); - if (warning !== -1) { - state.showWarning = warning - } - let currency = _.get(json, 'bucket.currency', -1); - if (currency !== -1) { - state.denomination = currency; - } - - state.loadedSettings = true; - if (state.loadedBtc) { - state.loaded = true; - } - } - let entry = _.get(json, 'settings-event.put-entry.entry-key', false); - if (entry === 'currency') { - let value = _.get(json, 'settings-event.put-entry.value', false); - state.denomination = value; - } else if (entry === 'warning') { - let value = _.get(json, 'settings-event.put-entry.value', false); - state.showWarning = value; - } - } -} diff --git a/pkg/btc-wallet/src/js/reducers/update.js b/pkg/btc-wallet/src/js/reducers/update.js deleted file mode 100644 index b47ced0b5d..0000000000 --- a/pkg/btc-wallet/src/js/reducers/update.js +++ /dev/null @@ -1,116 +0,0 @@ -import _ from 'lodash'; - - -export class UpdateReducer { - reduce(json, state) { - if (!json) { - return; - } - if (json.providerStatus) { - this.reduceProviderStatus(json.providerStatus, state); - } - if (json.checkPayee) { - this.reduceCheckPayee(json.checkPayee, state); - } - if ("change-provider" in json) { - this.reduceChangeProvider(json["change-provider"], state); - } - if (json["change-wallet"]) { - this.changeWallet(json["change-wallet"], state); - } - if (json.hasOwnProperty('psbt')) { - this.reducePsbt(json.psbt, state); - } - if (json["btc-state"]) { - this.reduceBtcState(json["btc-state"], state); - } - if (json["new-tx"]) { - this.reduceNewTx(json["new-tx"], state); - } - if (json["cancel-tx"]) { - this.reduceCancelTx(json["cancel-tx"], state); - } - if (json.address) { - this.reduceAddress(json.address, state); - } - if (json.balance) { - this.reduceBalance(json.balance, state); - } - if (json.hasOwnProperty('error')) { - this.reduceError(json.error, state); - } - if (json.hasOwnProperty('broadcast-success')){ - state.broadcastSuccess = true; - } - if (json.hasOwnProperty('broadcast-fail')){ - state.broadcastSuccess = false; - } - } - - reduceProviderStatus(json, state) { - state.providerPerms[json.provider] = json.permitted; - } - - reduceCheckPayee(json, state) { - state.shipWallets[json.payee] = json.hasWallet; - } - - reduceChangeProvider(json, state) { - state.provider = json; - } - - reduceChangeWallet(json, state) { - state.wallet = json; - } - - reducePsbt(json, state) { - state.psbt = json.pb; - state.fee = json.fee; - } - - reduceBtcState(json, state) { - state.btcState = json; - } - - reduceNewTx(json, state) { - let old = _.findIndex(state.history, (h) => { - return ( h.txid.dat === json.txid.dat && - h.txid.wid === json.txid.wid ); - }); - if (old !== -1) { - delete state.history.splice(old, 1); - } - if (json.recvd === null) { - state.history.unshift(json); - } else { - // we expect history to have null recvd values first, and the rest in - // descending order - let insertionIndex = _.findIndex(state.history, (h) => { - return ((h.recvd < json.recvd) && (h.recvd !== null)); - }); - state.history.splice(insertionIndex, 0, json); - } - } - - reduceCancelTx(json, state) { - let entryIndex = _.findIndex(state.history, (h) => { - return ((json.wid === h.txid.wid) && (json.dat === h.txid.dat)); - }); - if (entryIndex > -1) { - state.history[entryIndex].failure = true; - } - } - - reduceAddress(json, state) { - state.address = json; - } - - reduceBalance(json, state) { - state.unconfirmedBalance = json.unconfirmed; - state.confirmedBalance = json.confirmed; - } - - reduceError(json, state) { - state.error = json; - } -} diff --git a/pkg/btc-wallet/src/js/store.js b/pkg/btc-wallet/src/js/store.js deleted file mode 100644 index 40cd3270fe..0000000000 --- a/pkg/btc-wallet/src/js/store.js +++ /dev/null @@ -1,54 +0,0 @@ -import { InitialReducer } from './reducers/initial'; -import { UpdateReducer } from './reducers/update'; -import { CurrencyReducer } from './reducers/currency'; -import { SettingsReducer } from './reducers/settings'; - -class Store { - constructor() { - this.state = { - loadedBtc: false, - loadedSettings: false, - loaded: false, - providerPerms: {}, - shipWallets: {}, - provider: null, - wallet: null, - confirmedBalance: null, - unconfirmedBalance: null, - btcState: null, - history: [], - psbt: '', - address: null, - currencyRates: { - BTC: { last: 1, symbol: 'BTC' } - }, - denomination: 'BTC', - showWarning: true, - error: '', - broadcastSuccess: false, - }; - - this.initialReducer = new InitialReducer(); - this.updateReducer = new UpdateReducer(); - this.currencyReducer = new CurrencyReducer(); - this.settingsReducer = new SettingsReducer(); - this.setState = () => { }; - } - - setStateHandler(setState) { - this.setState = setState; - } - - handleEvent(data) { - let json = data.data; - this.initialReducer.reduce(json, this.state); - this.updateReducer.reduce(json, this.state); - this.currencyReducer.reduce(json, this.state); - this.settingsReducer.reduce(json, this.state); - - this.setState(this.state); - } -} - -export let store = new Store(); -window.store = store; From 8e7379871397c9f5dd56896bfe6fbf07d2176212 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 16:58:05 -0500 Subject: [PATCH 11/64] Fix patp check in send --- pkg/btc-wallet/src/js/components/lib/send.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index 9bb6cbbd8a..c55bc1fd77 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -64,15 +64,12 @@ const Send = ({ stopSending, value, conversion }) => { }; const checkPayee = (e) => { - console.log('checkPayee', { e }); setError(''); let payeeReceived = e.target.value; let isPatp = ob.isValidPatp(payeeReceived); let isAddress = validate(payeeReceived); - console.log({ payeeReceived, isPatp, isAddress }); if (isPatp) { - console.log('isPatp', isPatp); let command = { 'check-payee': payeeReceived }; api.btcWalletCommand(command); setTimeout(() => { @@ -144,16 +141,14 @@ const Send = ({ stopSending, value, conversion }) => { }, []); useEffect(() => { - if (!ready && checkingPatp) { - console.log({ ready, checkingPatp, shipWallets, payee }); + if (!ready && !checkingPatp) { if (shipWallets.payee === payee.slice(1) && shipWallets.hasWallet) { - console.log('good'); setReady(true); setCheckingPatp(false); setValidPayee(true); } } - }, [ready, checkingPatp, shipWallets]); + }, [ready, checkingPatp, shipWallets, payee]); let payeeColor = 'black'; let payeeBg = 'white'; From 078d4991b3173b0eeec07054d958b5bccf04e0ec Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 17:23:10 -0500 Subject: [PATCH 12/64] Fix feepicker --- .../src/js/components/lib/feePicker.js | 45 +++++-------------- pkg/btc-wallet/src/js/components/lib/send.js | 17 ++++--- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js index 00e91c198d..9c7c77a542 100644 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ b/pkg/btc-wallet/src/js/components/lib/feePicker.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useRef } from 'react'; +import React from 'react'; import { Box, Text, @@ -6,38 +6,15 @@ import { StatelessRadioButtonField as RadioButton, Label, } from '@tlon/indigo-react'; +import { feeLevels } from './send'; -const feeLevels = { - low: 'low', - mid: 'mid', - high: 'high', -}; - -const FeePicker = ({ feeChoices, feeSelect, feeDismiss }) => { - const [feeSelected, setFeeSelected] = useState(feeLevels.mid); - const [modalElement, setModalElement] = useState(); - const modalRef = useRef(); - - // const clickDismiss = (e) => { - // console.log(modalElement, e); - // // if (modalRef && !modalRef.contains(e.target)) { - // // feeDismiss(); - // // } - // }; - +const FeePicker = ({ feeChoices, feeValue, setFeeValue, feeDismiss }) => { const select = (which) => { - setFeeSelected(which); - feeSelect(which); + console.log(which); + setFeeValue(feeLevels[which]); feeDismiss(); }; - // useEffect(() => { - // document.addEventListener('click', (e) => clickDismiss(e)); - // setModalElement(modalRef.current); - // console.log(modalRef.current); - // return () => document.addEventListener('click', clickDismiss); - // }, []); - return ( { { - select('low'); + select(feeLevels.low); }} >
@@ -168,7 +168,7 @@ const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { color={inputColor} backgroundColor={inputBg} borderColor={inputBorder} - style={{ 'line-height': '4' }} + style={{ lineHeight: '4' }} onChange={(e) => checkTxHex(e)} /> {localError !== '' && ( diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/js/components/lib/invoice.js index 771a876698..4d9c6563c1 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/js/components/lib/invoice.js @@ -214,7 +214,7 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { mono color="gray" fontSize="14px" - style={{ display: 'block', 'overflow-wrap': 'anywhere' }} + style={{ display: 'block', overflowWrap: 'anywhere' }} > {payee} diff --git a/pkg/btc-wallet/src/js/components/lib/sent.js b/pkg/btc-wallet/src/js/components/lib/sent.js index be4a8e1093..fe570f0fb3 100644 --- a/pkg/btc-wallet/src/js/components/lib/sent.js +++ b/pkg/btc-wallet/src/js/components/lib/sent.js @@ -19,7 +19,7 @@ const Sent = ({ payee, stopSending, satsAmount }) => {
{`You sent BTC to ${payee}`}
From 40f80a94b84aca3676be3726c29d9f5fb3175fcc Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 17:30:33 -0500 Subject: [PATCH 15/64] Remove more console logs --- pkg/btc-wallet/src/js/components/lib/feePicker.js | 1 - pkg/btc-wallet/src/js/hooks/useSettings.js | 1 - 2 files changed, 2 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js index 9c7c77a542..7fc5153a23 100644 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ b/pkg/btc-wallet/src/js/components/lib/feePicker.js @@ -10,7 +10,6 @@ import { feeLevels } from './send'; const FeePicker = ({ feeChoices, feeValue, setFeeValue, feeDismiss }) => { const select = (which) => { - console.log(which); setFeeValue(feeLevels[which]); feeDismiss(); }; diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 81a9f39701..8e35d52345 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -216,7 +216,6 @@ export const SettingsProvider = ({ channel, children }) => { for (let c in checkPayee) { newShipWallets[c] = checkPayee[c]; } - console.log({ newShipWallets }); setShipWallets(newShipWallets); } if (changeWallet) { From 6badb91b6f4780c62ad3fcef75462a47a935059f Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 17:44:45 -0500 Subject: [PATCH 16/64] Add fee to useSettings hook --- pkg/btc-wallet/src/js/components/lib/send.js | 2 -- pkg/btc-wallet/src/js/hooks/useSettings.js | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index abec450436..f3b050643a 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -125,7 +125,6 @@ const Send = ({ stopSending, value, conversion }) => { }; useEffect(() => { - console.log({ network }); if (network === 'bitcoin') { let url = 'https://bitcoiner.live/api/fees/estimates/latest'; fetch(url) @@ -134,7 +133,6 @@ const Send = ({ stopSending, value, conversion }) => { // let estimates = Object.keys(n.estimates); // let mid = Math.floor(estimates.length / 2); // let high = estimates.length - 1; - console.log('hello'); setFeeChoices({ high: [30, n.estimates[30]['sat_per_vbyte']], mid: [180, n.estimates[180]['sat_per_vbyte']], diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 8e35d52345..02393afcb2 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -27,6 +27,8 @@ export const SettingsContext = createContext({ setBtcState: () => {}, history: [], setHistory: () => {}, + fee: 0, + setFee: () => {}, psbt: '', setPsbt: () => {}, address: null, @@ -58,6 +60,7 @@ export const SettingsProvider = ({ channel, children }) => { const [btcState, setBtcState] = useState(null); const [history, setHistory] = useState([]); const [psbt, setPsbt] = useState(''); + const [fee, setFee] = useState(0); const [address, setAddress] = useState(null); const [currencyRates, setCurrencyRates] = useState({ BTC: { last: 1, symbol: 'BTC' }, @@ -223,6 +226,7 @@ export const SettingsProvider = ({ channel, children }) => { } if (psbtData) { setPsbt(psbtData.pb); + setFee(psbtData.fee); } if (cancelTx) { handleCancelTx(cancelTx); @@ -281,6 +285,8 @@ export const SettingsProvider = ({ channel, children }) => { setHistory, psbt, setPsbt, + fee, + setFee, address, setAddress, currencyRates, From 12c5306ac0f0c39a3e3f59eb235c09ff38fe1f47 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 1 Jul 2021 11:38:18 -0500 Subject: [PATCH 17/64] Fix newTx handler --- pkg/btc-wallet/src/js/hooks/useSettings.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 02393afcb2..55e7038e48 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -134,22 +134,28 @@ export const SettingsProvider = ({ channel, children }) => { } }; - const handleNewTx = ({ txid, recvd }) => { + const handleNewTx = (newTx) => { + const { txid, recvd } = newTx; let old = _.findIndex(history, (h) => { return h.txid.dat === txid.dat && h.txid.wid === txid.wid; }); if (old !== -1) { - delete history.splice(old, 1); + const newHistory = history.filter((o, i) => i !== old); + setHistory(newHistory); } - if (recvd === null) { - history.unshift({ txid, recvd }); - } else { + if (recvd === null && old === -1) { + const newHistory = [...history, newTx]; + setHistory(newHistory); + } else if (recvd !== null && old === -1) { // we expect history to have null recvd values first, and the rest in // descending order let insertionIndex = _.findIndex(history, (h) => { return h.recvd < recvd && h.recvd !== null; }); - history.splice(insertionIndex, 0, { txid, recvd }); + const newHistory = history.map((o, i) => + i === insertionIndex ? newTx : o + ); + setHistory(newHistory); } }; From 6608c04186c06c06a45b56a0c16f51dc8eeb1e0f Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 1 Jul 2021 11:39:12 -0500 Subject: [PATCH 18/64] Cleanup walletModal, transaction, invoice --- .../src/js/components/lib/invoice.js | 27 +++++-------------- .../src/js/components/lib/transactions.js | 2 +- .../src/js/components/lib/walletModal.js | 6 +---- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/js/components/lib/invoice.js index 4d9c6563c1..9b843d617e 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/js/components/lib/invoice.js @@ -1,4 +1,4 @@ -import React, { useRef, useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { Box, Icon, @@ -57,7 +57,6 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { const [ready, setReady] = useState(false); const [localError, setLocalError] = useState(error); const [broadcasting, setBroadcasting] = useState(false); - const invoiceRef = useRef(); useEffect(() => { if (broadcasting && localError !== '') { @@ -65,17 +64,6 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { } }, [error, broadcasting, setBroadcasting]); - const clickDismiss = (e) => { - if (invoiceRef && !invoiceRef.contains(e.target)) { - stopSending(); - } - }; - - useEffect(() => { - document.addEventListener('click', clickDismiss); - return () => document.removeEventListener('click', clickDismiss); - }, []); - const broadCastTx = (psbtHex) => { let command = { 'broadcast-tx': psbtHex, @@ -90,6 +78,7 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { ticket, ship: parseInt(patp2dec('~' + window.ship)), }).then((urbitWallet) => { + // this wasn't being used, not clear why it was pulled out. // const { xpub } = // network === 'testnet' // ? urbitWallet.bitcoinTestnet.keys @@ -127,11 +116,11 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { }); }; - const checkTicket = (e) => { + const checkTicket = ({ target: { value } }) => { // TODO: port over bridge ticket validation logic - setMasterTicket(e.target.value); - setReady(isValidPatq(e.target.value)); - setLocalError(isValidPatq(e.target.value) ? '' : 'invalid-master-ticket'); + setMasterTicket(value); + setReady(isValidPatq(value)); + setLocalError(isValidPatq(value) ? '' : 'invalid-master-ticket'); }; let inputColor = 'black'; @@ -168,13 +157,11 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { ) : ( stopSending()} > { color={inputColor} backgroundColor={inputBg} borderColor={inputBorder} - onChange={() => checkTicket()} + onChange={(e) => checkTicket(e)} /> {error !== '' && ( diff --git a/pkg/btc-wallet/src/js/components/lib/transactions.js b/pkg/btc-wallet/src/js/components/lib/transactions.js index 2bd7619956..c968320bcd 100644 --- a/pkg/btc-wallet/src/js/components/lib/transactions.js +++ b/pkg/btc-wallet/src/js/components/lib/transactions.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Box, Text, Col } from '@tlon/indigo-react'; import Transaction from './transaction.js'; import { useSettings } from '../../hooks/useSettings.js'; diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/js/components/lib/walletModal.js index a34a596c03..be0bc6ef07 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/js/components/lib/walletModal.js @@ -39,11 +39,7 @@ const WalletModal = () => { } }; - const checkXPub = ({ - event: { - target: { value: xpubGiven }, - }, - }) => { + const checkXPub = ({ target: { value: xpubGiven } }) => { setXpub(xpubGiven); setReadyToSubmit(xpubGiven.length > 0); }; From 031d9d5a7de5902a5eae01ed53a0e06f5e80641f Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 07:21:52 -0500 Subject: [PATCH 19/64] Fix destructured param in checkTicket --- pkg/btc-wallet/src/js/components/lib/walletModal.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/js/components/lib/walletModal.js index be0bc6ef07..f8687441d5 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/js/components/lib/walletModal.js @@ -24,11 +24,7 @@ const WalletModal = () => { const [confirmingMasterTicket, setConfirmingMasterTicket] = useState(false); const [error, setError] = useState(false); - const checkTicket = ({ - event: { - target: { value }, - }, - }) => { + const checkTicket = ({ target: { value } }) => { // TODO: port over bridge ticket validation logic if (confirmingMasterTicket) { setConfirmedMasterTicket(value); From 6bc8d247bd65f13c693c6bfd4b1ca0b5c9bf5f2e Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 08:27:49 -0500 Subject: [PATCH 20/64] Map dollar sign to USD --- pkg/btc-wallet/src/js/hooks/useSettings.js | 3 ++- pkg/btc-wallet/src/js/lib/util.js | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 55e7038e48..c6e4917536 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -1,7 +1,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; import _ from 'lodash'; import { api } from '../api'; -import { reduceHistory } from '../lib/util'; +import { mapDenominationToSymbol, reduceHistory } from '../lib/util'; export const SettingsContext = createContext({ network: 'bitcoin', @@ -120,6 +120,7 @@ export const SettingsProvider = ({ channel, children }) => { const newCurrencyRates = currencyRates; for (let c in n) { newCurrencyRates[c] = n[c]; + newCurrencyRates[c].symbol = mapDenominationToSymbol(c); } setCurrencyRates(newCurrencyRates); setTimeout(() => initializeCurrencyPoll(), 1000 * 60 * 15); diff --git a/pkg/btc-wallet/src/js/lib/util.js b/pkg/btc-wallet/src/js/lib/util.js index 131ec4012c..2c133559a6 100644 --- a/pkg/btc-wallet/src/js/lib/util.js +++ b/pkg/btc-wallet/src/js/lib/util.js @@ -118,3 +118,12 @@ export function reduceHistory(history) { return hest2.recvd - hest1.recvd; }); } + +export function mapDenominationToSymbol(denomination) { + switch (denomination) { + case 'USD': + return '$'; + default: + return denomination; + } +} From e35eb52033d8ad79d97cd66dc20d1b9a9af2eeed Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 09:44:18 -0500 Subject: [PATCH 21/64] Add sig to valid patp in send component --- pkg/btc-wallet/src/js/components/lib/send.js | 31 +++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index f3b050643a..d5b6756311 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -68,18 +68,22 @@ const Send = ({ stopSending, value, conversion }) => { const checkPayee = (e) => { setError(''); - let payeeReceived = e.target.value; - let isPatp = ob.isValidPatp(payeeReceived); - let isAddress = validate(payeeReceived); - if (isPatp) { - let command = { 'check-payee': payeeReceived }; + const validPatPCommand = (validPatP) => { + let command = { 'check-payee': validPatP }; api.btcWalletCommand(command); setTimeout(() => { setCheckingPatp(false); }, 5000); setCheckingPatp(true); setPayeeType('ship'); - setPayee(payeeReceived); + setPayee(validPatP); + }; + + let payeeReceived = e.target.value; + let isPatp = ob.isValidPatp(payeeReceived); + let isAddress = validate(payeeReceived); + if (isPatp) { + validPatPCommand(payeeReceived); } else if (isAddress) { setPayee(payeeReceived); setReady(true); @@ -87,11 +91,16 @@ const Send = ({ stopSending, value, conversion }) => { setPayeeType('address'); setValidPayee(true); } else { - setPayee(payeeReceived); - setReady(false); - setCheckingPatp(false); - setPayeeType(''); - setValidPayee(false); + const possibleValidPatPMissingSig = '~'.concat(payeeReceived); + if (ob.isValidPatp(possibleValidPatPMissingSig)) { + validPatPCommand(possibleValidPatPMissingSig); + } else { + setPayee(payeeReceived); + setReady(false); + setCheckingPatp(false); + setPayeeType(''); + setValidPayee(false); + } } }; From 764438b248d87f5f30b246067c30356a617bf7d2 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 14:17:52 -0500 Subject: [PATCH 22/64] Show wallet scan progress --- .../src/js/components/lib/balance.js | 25 ++++++++++++++++--- .../src/js/components/lib/walletModal.js | 24 ++++++++++-------- pkg/btc-wallet/src/js/components/root.js | 11 ++++---- pkg/btc-wallet/src/js/hooks/useSettings.js | 12 +++++++++ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/balance.js b/pkg/btc-wallet/src/js/components/lib/balance.js index a963a3dac2..55a88adfac 100644 --- a/pkg/btc-wallet/src/js/components/lib/balance.js +++ b/pkg/btc-wallet/src/js/components/lib/balance.js @@ -16,10 +16,12 @@ const Balance = () => { setPsbt, setFee, setError, + scanProgress, } = useSettings(); const [sending, setSending] = useState(false); const [copiedButton, setCopiedButton] = useState(false); const [copiedString, setCopiedString] = useState(false); + const scanning = scanProgress?.main !== null || scanProgress?.change !== null; const copyAddress = (arg) => { navigator.clipboard.writeText(address); @@ -93,10 +95,25 @@ const Balance = () => { > {value} - {`${sats}${unconfirmedString} sats`} + {scanning ? ( + + + + Balance will be updated shortly: + + + + + {scanProgress.main}/24 addresses scanned + + + + ) : ( + {`${sats}${unconfirmedString} sats`} + )} - - + return ( + <> + {sending ? ( + { + setSending(false); + setPsbt(''); + setFee(0); + setError(''); + }} + /> + ) : ( + + + + Balance + + copyAddress('string')} + > + {copiedString ? 'copied' : addressText} + + + + + + {value} + + {`${sats}${unconfirmedString} sats`} - )} - - ); - } -} + + + + + + )} + + ); +}; + +export default Balance; diff --git a/pkg/btc-wallet/src/js/components/lib/body.js b/pkg/btc-wallet/src/js/components/lib/body.js index eff8c8d8ef..334a622a32 100644 --- a/pkg/btc-wallet/src/js/components/lib/body.js +++ b/pkg/btc-wallet/src/js/components/lib/body.js @@ -1,72 +1,49 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - LoadingSpinner, - Col, -} from '@tlon/indigo-react'; -import { - Switch, - Route, -} from 'react-router-dom'; -import Balance from './balance.js'; +import React from 'react'; +import { Box, LoadingSpinner, Col } from '@tlon/indigo-react'; +import { Switch, Route } from 'react-router-dom'; +import Balance from './balance.js'; import Transactions from './transactions.js'; -import Warning from './warning.js'; -import Header from './header.js'; -import Settings from './settings.js'; +import Warning from './warning.js'; +import Header from './header.js'; +import Settings from './settings.js'; +import { useSettings } from '../../hooks/useSettings.js'; -export default class Body extends Component { - constructor(props) { - super(props); - } +const Body = () => { + const { loaded, showWarning: warning } = useSettings(); + const cardWidth = window.innerWidth <= 475 ? '350px' : '400px'; + return !loaded ? ( + + + + ) : ( + + + +
+ + + + + +
+ {!warning ? null : } + + + + + + ); +}; - render() { - - const cardWidth = window.innerWidth <= 475 ? '350px' : '400px' - - if (!this.props.loaded) { - return ( - - - - ); - } else { - return ( - - - -
- - - - - -
- { (!this.props.warning) ? null : } - - - - - - ); - } - } -} +export default Body; diff --git a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js b/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js index acc147a8e1..3b3a2df34b 100644 --- a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js +++ b/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Box, Icon, @@ -9,231 +9,203 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; - import { Sigil } from './sigil.js'; - import * as bitcoin from 'bitcoinjs-lib'; -import * as kg from 'urbit-key-generation'; import { isValidPatp } from 'urbit-ob'; - -import Sent from './sent.js' -import Error from './error.js' - +import Sent from './sent.js'; +import Error from './error.js'; import { satsToCurrency } from '../../lib/util.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; -export default class BridgeInvoice extends Component { - constructor(props) { - super(props); +const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { + const { error, currencyRates, fee, broadcastSuccess, denomination, psbt } = + useSettings(); + const [txHex, setTxHex] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(''); + const [broadcasting, setBroadcasting] = useState(false); + const invoiceRef = useRef(); - this.state = { - txHex: '', - ready: false, - error: this.props.state.error, - broadcasting: false, - }; - - this.checkTxHex = this.checkTxHex.bind(this); - this.broadCastTx = this.broadCastTx.bind(this); - this.sendBitcoin = this.sendBitcoin.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setInvoiceRef = this.setInvoiceRef.bind(this); - } - - broadCastTx(hex) { - let command = { - 'broadcast-tx': hex + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); } - return this.props.api.btcWalletCommand(command) - } - - componentDidMount() { - window.open('https://bridge.urbit.org/?kind=btc&utx=' + this.props.psbt); - document.addEventListener("click", this.clickDismiss); - } - - componentWillUnmount(){ - document.removeEventListener("click", this.clickDismiss); - } - - setInvoiceRef(n){ - this.invoiceRef = n; - } - - clickDismiss(e){ - if (this.invoiceRef && !(this.invoiceRef.contains(e.target))){ - this.props.stopSending(); - } - } - - componentDidUpdate(prevProps){ - if (this.state.broadcasting) { - if (this.state.error !== '') { - this.setState({broadcasting: false}); - } - } - - if (prevProps.state.error !== this.props.state.error) { - this.setState({error: this.props.state.error}); - } - } - - sendBitcoin(hex) { - try { - bitcoin.Transaction.fromHex(hex) - this.broadCastTx(hex) - this.setState({broadcasting: true}); - } - - catch(e) { - this.setState({error: 'invalid-signed', broadcasting: false}); - } - } - - checkTxHex(e){ - let txHex = e.target.value; - let ready = (txHex.length > 0); - let error = ''; - this.setState({txHex, ready, error}); - } - - render() { - const { stopSending, payee, denomination, satsAmount, psbt, currencyRates, fee } = this.props; - const { error, txHex } = this.state; - - let inputColor = 'black'; - let inputBg = 'white'; - let inputBorder = 'lightGray'; - if (error !== '') { - inputColor = 'red'; - inputBg = 'veryLightRed'; - inputBorder = 'red'; + setLocalError(error); } + }, [error, broadcasting, setBroadcasting]); - const isShip = isValidPatp(payee); + useEffect(() => { + window.open('https://bridge.urbit.org/?kind=btc&utx=' + psbt); + }); - const icon = (isShip) - ? - : ; + const broadCastTx = (hex) => { + let command = { + 'broadcast-tx': hex, + }; + return api.btcWalletCommand(command); + }; - return ( - <> - { this.props.state.broadcastSuccess ? - : + const sendBitcoin = (hex) => { + try { + bitcoin.Transaction.fromHex(hex); + broadCastTx(hex); + setBroadcasting(true); + } catch (e) { + setLocalError('invalid-signed'); + setBroadcasting(false); + } + }; + + const checkTxHex = (e) => { + setTxHex(e.target.value); + setReady(txHex.length > 0); + setLocalError(''); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (localError !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; + } + + const isShip = isValidPatp(payee); + + const icon = isShip ? ( + + ) : ( + + + + ); + + return ( + <> + {broadcastSuccess ? ( + + ) : ( + - - - {satsToCurrency(satsAmount, denomination, currencyRates)} - - - {`${satsAmount} sats`} - - - {`Fee: ${satsToCurrency(fee, denomination, currencyRates)} (${fee} sats)`} - - - You are paying - - - {icon} - {payee} - - - - - Bridge signed transaction + + + {satsToCurrency(satsAmount, denomination, currencyRates)} - - - - Copy the signed transaction from Bridge + + + {`${satsAmount} sats`} + + + {`Fee: ${satsToCurrency( + fee, + denomination, + currencyRates + )} (${fee} sats)`} + + + + You are paying + + + + {icon} + + {payee} - - - { (error !== '') && - - - - } - - + {broadcasting ? : null} + + + )} + + ); +}; + +export default BridgeInvoice; diff --git a/pkg/btc-wallet/src/js/components/lib/currencyPicker.js b/pkg/btc-wallet/src/js/components/lib/currencyPicker.js index 053201bf61..30dc627560 100644 --- a/pkg/btc-wallet/src/js/components/lib/currencyPicker.js +++ b/pkg/btc-wallet/src/js/components/lib/currencyPicker.js @@ -1,50 +1,37 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, - LoadingSpinner, -} from '@tlon/indigo-react'; -import _ from 'lodash'; +import React from 'react'; +import { Icon, Row, Text } from '@tlon/indigo-react'; +import { api } from '../../api'; +import { useSettings } from '../../hooks/useSettings'; -import { satsToCurrency } from '../../lib/util.js' -import { store } from '../../store'; - -export default class CurrencyPicker extends Component { - constructor(props) { - super(props); - this.switchCurrency = this.switchCurrency.bind(this); - } - - switchCurrency(){ +const CurrencyPicker = () => { + const { denomination, currencyRates } = useSettings(); + const switchCurrency = () => { let newCurrency; - if (this.props.denomination === 'BTC') { - if (this.props.currencies['USD']) { - newCurrency = "USD"; + if (denomination === 'BTC') { + if (currencyRates['USD']) { + newCurrency = 'USD'; } - } else if (this.props.denomination === 'USD') { - newCurrency = "BTC"; + } else if (denomination === 'USD') { + newCurrency = 'BTC'; } let setCurrency = { - "put-entry": { + 'put-entry': { value: newCurrency, - "entry-key": "currency", - "bucket-key": "btc-wallet", - } - } - this.props.api.settingsEvent(setCurrency); - } + 'entry-key': 'currency', + 'bucket-key': 'btc-wallet', + }, + }; + api.settingsEvent(setCurrency); + }; + return ( + switchCurrency()}> + + + {denomination} + + + ); +}; - render() { - return ( - - - {this.props.denomination} - - ); - } -} +export default CurrencyPicker; diff --git a/pkg/btc-wallet/src/js/components/lib/error.js b/pkg/btc-wallet/src/js/components/lib/error.js index f7c9ec473a..42d114d182 100644 --- a/pkg/btc-wallet/src/js/components/lib/error.js +++ b/pkg/btc-wallet/src/js/components/lib/error.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Text } from '@tlon/indigo-react'; const errorToString = (error) => { @@ -26,16 +26,12 @@ const errorToString = (error) => { if (error === 'invalid-signed') { return 'Invalid signed bitcoin transaction'; } -} +}; -export default function Error(props) { - const error = errorToString(props.error); +const Error = ({ error, ...rest }) => ( + + {errorToString(error)} + +); - return( - - {error} - - ); -} +export default Error; diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js index ee419dd7ab..00e91c198d 100644 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ b/pkg/btc-wallet/src/js/components/lib/feePicker.js @@ -1,99 +1,99 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { Box, - Icon, - Row, Text, - Button, Col, StatelessRadioButtonField as RadioButton, Label, } from '@tlon/indigo-react'; +const feeLevels = { + low: 'low', + mid: 'mid', + high: 'high', +}; -export default class FeePicker extends Component { - constructor(props) { - super(props); +const FeePicker = ({ feeChoices, feeSelect, feeDismiss }) => { + const [feeSelected, setFeeSelected] = useState(feeLevels.mid); + const [modalElement, setModalElement] = useState(); + const modalRef = useRef(); - this.state = { - selected: 'mid' - } + // const clickDismiss = (e) => { + // console.log(modalElement, e); + // // if (modalRef && !modalRef.contains(e.target)) { + // // feeDismiss(); + // // } + // }; - this.select = this.select.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setModalRef = this.setModalRef.bind(this); - } + const select = (which) => { + setFeeSelected(which); + feeSelect(which); + feeDismiss(); + }; - componentDidMount() { - document.addEventListener("click", this.clickDismiss); - } + // useEffect(() => { + // document.addEventListener('click', (e) => clickDismiss(e)); + // setModalElement(modalRef.current); + // console.log(modalRef.current); + // return () => document.addEventListener('click', clickDismiss); + // }, []); - componentWillUnount() { - document.removeEventListener("click", this.clickDismiss); - } + return ( + feeDismiss()} + position="absolute" + p={4} + border="1px solid green" + zIndex={10} + backgroundColor="white" + borderRadius={3} + > + + Transaction Speed + + + { + select('low'); + }} + > + + - setModalRef(n) { - this.modalRef = n; - } + { + select('mid'); + }} + > + + - clickDismiss(e) { - if (this.modalRef && !(this.modalRef.contains(e.target))){ - this.props.feeDismiss(); - } - } + { + select('high'); + }} + > + + + + + ); +}; - select(which) { - this.setState({selected: which}); - this.props.feeSelect(which); - } - - render() { - return ( - - - Transaction Speed - - - { - this.select('low'); - }} - > - - - - { - this.select('mid'); - }} - > - - - - { - this.select('high'); - }} - > - - - - - - ); - } -} +export default FeePicker; diff --git a/pkg/btc-wallet/src/js/components/lib/header.js b/pkg/btc-wallet/src/js/components/lib/header.js index 9b6225effb..88f4687fff 100644 --- a/pkg/btc-wallet/src/js/components/lib/header.js +++ b/pkg/btc-wallet/src/js/components/lib/header.js @@ -1,78 +1,81 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Icon, Row, Text } from '@tlon/indigo-react'; import { Link } from 'react-router-dom'; +import { useSettings } from '../../hooks/useSettings'; -export default class Header extends Component { - constructor(props) { - super(props); +const Header = ({ settings }) => { + const { provider } = useSettings(); + let icon = settings ? 'X' : 'Adjust'; + let iconColor = settings ? 'black' : 'orange'; + let iconLink = settings ? '/~btc' : '/~btc/settings'; + + let connection = null; + let badge = null; + if (!(provider && provider.connected)) { + connection = ( + + Provider Offline + + ); + + if (!settings) { + badge = ( + + ); + } } - - render() { - let icon = this.props.settings ? "X" : "Adjust"; - let iconColor = this.props.settings ? "black" : "orange"; - let iconLink = this.props.settings ? "/~btc" : "/~btc/settings"; - - let connection = null; - let badge = null; - if (!(this.props.state.provider && this.props.state.provider.connected)) { - connection = - - Provider Offline + return ( + + + + + + + Bitcoin - - if (!this.props.settings) { - badge = - - } - } - - - - return ( - - - + + {connection} + + - + {badge} + - - Bitcoin - - - - {connection} - - - {badge} - - - - + - ); - } -} + + ); +}; + +export default Header; diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/js/components/lib/invoice.js index 6850fb7f21..771a876698 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/js/components/lib/invoice.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; import { Box, Icon, @@ -9,18 +9,15 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; - -import { Sigil } from './sigil.js' - +import { Sigil } from './sigil.js'; import * as bitcoin from 'bitcoinjs-lib'; import * as kg from 'urbit-key-generation'; -import * as bip39 from 'bip39'; - -import Sent from './sent.js' +import Sent from './sent.js'; import { patp2dec, isValidPatq, isValidPatp } from 'urbit-ob'; - import { satsToCurrency } from '../../lib/util.js'; import Error from './error.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; const BITCOIN_MAINNET_INFO = { messagePrefix: '\x18Bitcoin Signed Message:\n', @@ -46,246 +43,234 @@ const BITCOIN_TESTNET_INFO = { wif: 0xef, }; -export default class Invoice extends Component { - constructor(props) { - super(props); +const Invoice = ({ stopSending, payee, satsAmount }) => { + const { + error, + currencyRates, + psbt, + fee, + broadcastSuccess, + network, + denomination, + } = useSettings(); + const [masterTicket, setMasterTicket] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(error); + const [broadcasting, setBroadcasting] = useState(false); + const invoiceRef = useRef(); - this.state = { - masterTicket: '', - ready: false, - error: this.props.state.error, - sent: false, - broadcasting: false, - }; - - this.checkTicket = this.checkTicket.bind(this); - this.broadCastTx = this.broadCastTx.bind(this); - this.sendBitcoin = this.sendBitcoin.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setInvoiceRef = this.setInvoiceRef.bind(this); - } - - componentDidMount(){ - document.addEventListener("click", this.clickDismiss); - } - - componentWillUnMount(){ - document.removeEventListener("click", this.clickDismiss); - } - - setInvoiceRef(n){ - this.invoiceRef = n; - } - - clickDismiss(e){ - if (this.invoiceRef && !(this.invoiceRef.contains(e.target))) { - this.props.stopSending(); + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); } - } + }, [error, broadcasting, setBroadcasting]); - componentDidUpdate(prevProps, prevState) { - if (this.state.broadcasting) { - if (this.state.error !== '') { - this.setState({broadcasting: false}); - } + const clickDismiss = (e) => { + if (invoiceRef && !invoiceRef.contains(e.target)) { + stopSending(); } - } + }; - broadCastTx(psbtHex) { + useEffect(() => { + document.addEventListener('click', clickDismiss); + return () => document.removeEventListener('click', clickDismiss); + }, []); + + const broadCastTx = (psbtHex) => { let command = { - 'broadcast-tx': psbtHex - } - return this.props.api.btcWalletCommand(command) - } + 'broadcast-tx': psbtHex, + }; + return api.btcWalletCommand(command); + }; - sendBitcoin(ticket, psbt) { + const sendBitcoin = (ticket, psbt) => { const newPsbt = bitcoin.Psbt.fromBase64(psbt); - this.setState({broadcasting: true}); - kg.generateWallet({ ticket, ship: parseInt(patp2dec('~' + window.ship)) }) - .then(urbitWallet => { - const { xpub } = this.props.network === 'testnet' - ? urbitWallet.bitcoinTestnet.keys - : urbitWallet.bitcoinMainnet.keys; + setBroadcasting(true); + kg.generateWallet({ + ticket, + ship: parseInt(patp2dec('~' + window.ship)), + }).then((urbitWallet) => { + // const { xpub } = + // network === 'testnet' + // ? urbitWallet.bitcoinTestnet.keys + // : urbitWallet.bitcoinMainnet.keys; - const { xprv: zprv } = urbitWallet.bitcoinMainnet.keys; - const { xprv: vprv } = urbitWallet.bitcoinTestnet.keys; + const { xprv: zprv } = urbitWallet.bitcoinMainnet.keys; + const { xprv: vprv } = urbitWallet.bitcoinTestnet.keys; - const isTestnet = (this.props.network === 'testnet'); - const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/"; + const isTestnet = network === 'testnet'; + const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/"; - const btcWallet = (isTestnet) - ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO) - : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO); + const btcWallet = isTestnet + ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO) + : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO); - try { - const hex = newPsbt.data.inputs - .reduce((psbt, input, idx) => { - // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0 - const path = input.bip32Derivation[0].path - .split(derivationPrefix) - .join(''); - const prv = btcWallet.derivePath(path).privateKey; - return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv)); - }, newPsbt) - .finalizeAllInputs() - .extractTransaction() - .toHex(); + try { + const hex = newPsbt.data.inputs + .reduce((psbt, input, idx) => { + // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0 + const path = input.bip32Derivation[0].path + .split(derivationPrefix) + .join(''); + const prv = btcWallet.derivePath(path).privateKey; + return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv)); + }, newPsbt) + .finalizeAllInputs() + .extractTransaction() + .toHex(); - this.broadCastTx(hex); - } - catch(e) { - this.setState({error: 'invalid-master-ticket', broadcasting: false}); - } - }); + broadCastTx(hex); + } catch (e) { + setLocalError('invalid-master-ticket'); + setBroadcasting(false); + } + }); + }; - } - - - checkTicket(e){ + const checkTicket = (e) => { // TODO: port over bridge ticket validation logic - let masterTicket = e.target.value; - let ready = isValidPatq(masterTicket); - let error = (ready) ? '' : 'invalid-master-ticket'; - this.setState({masterTicket, ready, error}); + setMasterTicket(e.target.value); + setReady(isValidPatq(e.target.value)); + setLocalError(isValidPatq(e.target.value) ? '' : 'invalid-master-ticket'); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (error !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; } - render() { - const broadcastSuccess = this.props.state.broadcastSuccess; - const { stopSending, payee, denomination, satsAmount, psbt, currencyRates, fee } = this.props; - const { sent, error } = this.state; + const isShip = isValidPatp(payee); - let inputColor = 'black'; - let inputBg = 'white'; - let inputBorder = 'lightGray'; + const icon = isShip ? ( + + ) : ( + + + + ); - if (error !== '') { - inputColor = 'red'; - inputBg = 'veryLightRed'; - inputBorder = 'red'; - } - - const isShip = isValidPatp(payee); - - const icon = (isShip) - ? - : ; - - return ( - <> - { broadcastSuccess ? - : + return ( + <> + {broadcastSuccess ? ( + + ) : ( + stopSending()} + > - - - {satsToCurrency(satsAmount, denomination, currencyRates)} - - - {`${satsAmount} sats`} - - - {`Fee: ${satsToCurrency(fee, denomination, currencyRates)} (${fee} sats)`} - - - You are paying - - - {icon} - {payee} - - - - - Ticket + + + {satsToCurrency(satsAmount, denomination, currencyRates)} - value.replace(/[^~-]+/g, '••••••')} - placeholder="••••••-••••••-••••••-••••••" - autoCapitalize="none" - autoCorrect="off" - color={inputColor} - backgroundColor={inputBg} - borderColor={inputBorder} - onChange={this.checkTicket} - /> - {(error !== '') && - - - - } - - + {broadcasting ? : null} + + + )} + + ); +}; + +export default Invoice; diff --git a/pkg/btc-wallet/src/js/components/lib/providerModal.js b/pkg/btc-wallet/src/js/components/lib/providerModal.js index 9b85b449fd..ddb7dc58dd 100644 --- a/pkg/btc-wallet/src/js/components/lib/providerModal.js +++ b/pkg/btc-wallet/src/js/components/lib/providerModal.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Text, @@ -8,159 +8,157 @@ import { Row, LoadingSpinner, } from '@tlon/indigo-react'; - import { isValidPatp } from 'urbit-ob'; +import { api } from '../../api'; +import { useSettings } from '../../hooks/useSettings'; -export default class ProviderModal extends Component { - constructor(props) { - super(props); +const providerStatuses = { + checking: 'checking', + failed: 'failed', + ready: 'ready', + initial: '', +}; - this.state = { - potentialProvider: null, - checkingProvider: false, - providerFailed: false, - ready: false, - provider: null, - connecting: false, - }; +const ProviderModal = () => { + const { providerPerms } = useSettings(); + const [providerStatus, setProviderStatus] = useState( + providerStatuses.initial + ); + const [potentialProvider, setPotentialProvider] = useState(null); + const [provider, setProvider] = useState(null); + const [connecting, setConnecting] = useState(false); - this.checkProvider = this.checkProvider.bind(this); - this.submitProvider = this.submitProvider.bind(this); - } - - checkProvider(e) { + const checkProvider = (e) => { // TODO: loading states - let provider = e.target.value; - let ready = false; - let checkingProvider = false; - let potentialProvider = this.state.potentialProvider; - - if (isValidPatp(provider)) { + setProviderStatus(providerStatuses.initial); + let givenProvider = e.target.value; + if (isValidPatp(givenProvider)) { let command = { - 'check-provider': provider, + 'check-provider': givenProvider, }; - potentialProvider = provider; - checkingProvider = true; - this.props.api.btcWalletCommand(command); + setPotentialProvider(givenProvider); + setProviderStatus(providerStatuses.checking); + api.btcWalletCommand(command); setTimeout(() => { - this.setState({ providerFailed: true, checkingProvider: false }); + setProviderStatus(providerStatuses.failed); }, 5000); } - this.setState({ provider, ready, checkingProvider, potentialProvider }); - } + setProvider(givenProvider); + }; - componentDidUpdate() { - if (!this.state.ready) { - if (this.props.providerPerms[this.state.provider]) { - this.setState({ - ready: true, - checkingProvider: false, - providerFailed: false, - }); + const submitProvider = () => { + if (providerStatus === providerStatuses.ready) { + let command = { + 'set-provider': provider, + }; + api.btcWalletCommand(command); + setConnecting(true); + } + }; + + useEffect(() => { + if (providerStatus !== providerStatuses.ready) { + if (providerPerms.provider === provider && providerPerms.permitted) { + setProviderStatus(providerStatuses.ready); } } - } + }, [providerStatus, providerPerms, provider, setProviderStatus]); - submitProvider() { - if (this.state.ready) { - let command = { - 'set-provider': this.state.provider, - }; - this.props.api.btcWalletCommand(command); - this.setState({ connecting: true }); - } - } - - render() { - let workingNode = null; - let workingColor = null; - let workingBg = null; - if (this.state.ready) { - workingColor = 'green'; - workingBg = 'veryLightGreen'; - workingNode = ( - - - {this.state.provider} is a working provider node - - - ); - } else if (this.state.providerFailed) { - workingColor = 'red'; - workingBg = 'veryLightRed'; - workingNode = ( - - - {this.state.potentialProvider} is not a working provider node - - - ); - } - - return ( - - - - - Step 1 of 2: Set up Bitcoin Provider Node - - - - - In order to perform Bitcoin transaction in Landscape, you'll - need to set a provider node. A provider node is an urbit which - maintains a synced Bitcoin ledger. - - {' '} - Learn More - - - - - - Provider Node - - - - - {this.state.checkingProvider ? : null} - - {workingNode} - - - {this.state.connecting ? : null} - + let workingNode = null; + let workingColor = null; + let workingBg = null; + if (providerStatus === providerStatuses.ready) { + workingColor = 'green'; + workingBg = 'veryLightGreen'; + workingNode = ( + + + {provider} is a working provider node + + + ); + } else if (providerStatus === providerStatuses.failed) { + workingColor = 'red'; + workingBg = 'veryLightRed'; + workingNode = ( + + + {potentialProvider} is not a working provider node + ); } -} + + return ( + + + + + Step 1 of 2: Set up Bitcoin Provider Node + + + + + In order to perform Bitcoin transaction in Landscape, you'll need + to set a provider node. A provider node is an urbit which maintains a + synced Bitcoin ledger. + + {' '} + Learn More + + + + + + Provider Node + + + + checkProvider(e)} + /> + {providerStatus === providerStatuses.checking ? ( + + ) : null} + + {workingNode} + + + {connecting ? : null} + + + ); +}; + +export default ProviderModal; diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index df5eef8f5c..9bb6cbbd8a 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Icon, @@ -8,450 +8,443 @@ import { Button, Col, LoadingSpinner, - StatelessRadioButtonField as RadioButton, } from '@tlon/indigo-react'; - -import Invoice from './invoice.js' -import BridgeInvoice from './bridgeInvoice.js' -import FeePicker from './feePicker.js' -import Error from './error.js' -import Signer from './signer.js' - +import Invoice from './invoice.js'; +import BridgeInvoice from './bridgeInvoice.js'; +import FeePicker from './feePicker.js'; +import Error from './error.js'; +import Signer from './signer.js'; import { validate } from 'bitcoin-address-validation'; - import * as ob from 'urbit-ob'; +import { useSettings } from '../../hooks/useSettings.js'; +import { api } from '../../api'; -export default class Send extends Component { - constructor(props) { - super(props); +const focusFields = { + empty: '', + payee: 'payee', + currency: 'currency', + sats: 'sats', + note: 'note', +}; - this.state = { - signing: false, - denomAmount: '0.00', - satsAmount: '0', - payee: '', - checkingPatp: false, - payeeType: '', - ready: false, - validPayee: false, - focusPayee: true, - focusCurrency: false, - focusSats: false, - focusNote: false, - submitting: false, - feeChoices: { - low: [10, 1], - mid: [10, 1], - high: [10, 1], - }, - feeValue: "mid", - showModal: false, - note: '', - choosingSignMethod: false, - signMethod: 'bridge', - }; +const Send = ({ stopSending, value, conversion }) => { + const { error, setError, network, psbt, denomination, shipWallets } = + useSettings(); + const [signing, setSigning] = useState(false); + const [denomAmount, setDenomAmount] = useState('0.00'); + const [satsAmount, setSatsAmount] = useState('0'); + const [payee, setPayee] = useState(''); + const [checkingPatp, setCheckingPatp] = useState(false); + const [payeeType, setPayeeType] = useState(''); + const [ready, setReady] = useState(false); + const [validPayee, setValidPayee] = useState(false); + const [focusedField, setFocusedField] = useState(focusFields.empty); + const [feeChoices, setFeeChoices] = useState({ + low: [10, 1], + mid: [10, 1], + high: [10, 1], + }); + const [feeValue, setFeeValue] = useState('mid'); + const [showModal, setShowModal] = useState(false); + const [note, setNote] = useState(''); + const [choosingSignMethod, setChoosingSignMethod] = useState(false); + const [signMethod, setSignMethod] = useState('bridge'); - this.initPayment = this.initPayment.bind(this); - this.checkPayee = this.checkPayee.bind(this); - this.feeSelect = this.feeSelect.bind(this); - this.feeDismiss = this.feeDismiss.bind(this); - this.toggleSignMethod = this.toggleSignMethod.bind(this); - this.setSignMethod = this.setSignMethod.bind(this); - } + const feeDismiss = () => { + setShowModal(false); + }; - feeDismiss() { - this.setState({showModal: false}); - } + const feeSelect = (which) => { + setFeeValue(which); + }; - feeSelect(which) { - this.setState({feeValue: which}); - } - - componentDidMount(){ - if (this.props.network === 'bitcoin'){ - let url = "https://bitcoiner.live/api/fees/estimates/latest"; - fetch(url).then(res => res.json()).then(n => { - let estimates = Object.keys(n.estimates); - let mid = Math.floor(estimates.length/2) - let high = estimates.length - 1; - this.setState({ - feeChoices: { - high: [30, n.estimates[30]["sat_per_vbyte"]], - mid: [180, n.estimates[180]["sat_per_vbyte"]], - low: [360, n.estimates[360]["sat_per_vbyte"]], - } - }); - }) - } - } - - setSignMethod(signMethod) { - this.setState({signMethod, choosingSignMethod: false}); - } - - checkPayee(e){ - store.handleEvent({data: {error: ''}}); - - let payee = e.target.value; - let isPatp = ob.isValidPatp(payee); - let isAddress = validate(payee); + const handleSetSignMethod = (signMethod) => { + setSignMethod(signMethod); + setChoosingSignMethod(false); + }; + const checkPayee = (e) => { + console.log('checkPayee', { e }); + setError(''); + let payeeReceived = e.target.value; + let isPatp = ob.isValidPatp(payeeReceived); + let isAddress = validate(payeeReceived); + console.log({ payeeReceived, isPatp, isAddress }); if (isPatp) { - let command = {'check-payee': payee} - this.props.api.btcWalletCommand(command) + console.log('isPatp', isPatp); + let command = { 'check-payee': payeeReceived }; + api.btcWalletCommand(command); setTimeout(() => { - this.setState({checkingPatp: false}); + setCheckingPatp(false); }, 5000); - this.setState({ - checkingPatp: true, - payeeType: 'ship', - payee, - }); + setCheckingPatp(true); + setPayeeType('ship'); + setPayee(payeeReceived); } else if (isAddress) { - this.setState({ - payee, - ready: true, - checkingPatp: false, - payeeType: 'address', - validPayee: true, - }); + setPayee(payeeReceived); + setReady(true); + setCheckingPatp(false); + setPayeeType('address'); + setValidPayee(true); } else { - this.setState({ - payee, - ready: false, - checkingPatp: false, - payeeType: '', - validPayee: false, - }); + setPayee(payeeReceived); + setReady(false); + setCheckingPatp(false); + setPayeeType(''); + setValidPayee(false); } - } + }; - componentDidUpdate(prevProps, prevState) { - if ((prevProps.error !== this.props.error) && - (this.props.error !== '') && (this.props.error !== 'broadcast-fail')) { - this.setState({signing: false}); - } + const toggleSignMethod = () => { + setChoosingSignMethod(!choosingSignMethod); + }; - if (!this.state.ready && this.state.checkingPatp) { - if (this.props.shipWallets[this.state.payee.slice(1)]) { - this.setState({ready: true, checkingPatp: false, validPayee: true}); - } - } - } - - toggleSignMethod(toggle) { - this.setState({choosingSignMethod: !toggle}); - } - - initPayment() { - if (this.state.payeeType === 'ship') { + const initPayment = () => { + if (payeeType === 'ship') { let command = { 'init-payment': { - 'payee': this.state.payee, - 'value': parseInt(this.state.satsAmount), - 'feyb': this.state.feeChoices[this.state.feeValue][1], - 'note': (this.state.note || null), - } - } - this.props.api.btcWalletCommand(command).then(res => this.setState({signing: true})); - } else if (this.state.payeeType === 'address') { + payee, + value: parseInt(satsAmount), + feyb: feeChoices[feeValue][1], + note: note || null, + }, + }; + + api.btcWalletCommand(command).then(() => setSigning(true)); + } else if (payeeType === 'address') { let command = { 'init-payment-external': { - 'address': this.state.payee, - 'value': parseInt(this.state.satsAmount), - 'feyb': 1, - 'note': (this.state.note || null), - } - } - this.props.api.btcWalletCommand(command).then(res => this.setState({signing: true})); + address: payee, + value: parseInt(satsAmount), + feyb: 1, + note: note || null, + }, + }; + api.btcWalletCommand(command).then(() => setSigning(true)); } + }; + + useEffect(() => { + if (network === 'bitcoin') { + let url = 'https://bitcoiner.live/api/fees/estimates/latest'; + fetch(url) + .then((res) => res.json()) + .then((n) => { + // let estimates = Object.keys(n.estimates); + // let mid = Math.floor(estimates.length / 2); + // let high = estimates.length - 1; + setFeeChoices({ + high: [30, n.estimates[30]['sat_per_vbyte']], + mid: [180, n.estimates[180]['sat_per_vbyte']], + low: [360, n.estimates[360]['sat_per_vbyte']], + }); + }); + } + }, []); + + useEffect(() => { + if (!ready && checkingPatp) { + console.log({ ready, checkingPatp, shipWallets, payee }); + if (shipWallets.payee === payee.slice(1) && shipWallets.hasWallet) { + console.log('good'); + setReady(true); + setCheckingPatp(false); + setValidPayee(true); + } + } + }, [ready, checkingPatp, shipWallets]); + + let payeeColor = 'black'; + let payeeBg = 'white'; + let payeeBorder = 'lightGray'; + if (error) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } else if (focusedField === focusFields.payee && validPayee) { + payeeColor = 'green'; + payeeBorder = 'green'; + payeeBg = 'veryLightGreen'; + } else if (!focusedField === focusFields.payee && validPayee) { + payeeColor = 'blue'; + payeeBorder = 'white'; + payeeBg = 'white'; + } else if (focusedField !== focusFields.payee && !validPayee) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } else if ( + focusedField === 'payee' && + !validPayee && + !checkingPatp && + payeeType === 'ship' + ) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; } - render() { - let payeeColor = "black"; - let payeeBg = "white"; - let payeeBorder = "lightGray"; - if (this.props.error) { - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } else if (this.state.focusPayee && this.state.validPayee) { - payeeColor = "green"; - payeeBorder = "green"; - payeeBg = "veryLightGreen"; - } else if (!this.state.focusPayee && this.state.validPayee){ - payeeColor="blue"; - payeeBorder = "white"; - payeeBg = "white"; - } else if (!this.state.focusPayee && !this.state.validPayee) { - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } else if (this.state.focusPayee && - !this.state.validPayee && - !this.state.checkingPatp && - this.state.payeeType === 'ship'){ - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } + const signReady = ready && parseInt(satsAmount) > 0 && !signing; - - const { api, value, conversion, stopSending, denomination, psbt, currencyRates, error, network, fee } = this.props; - const { denomAmount, satsAmount, signing, payee, choosingSignMethod, signMethod } = this.state; - - const signReady = (this.state.ready && (parseInt(this.state.satsAmount) > 0)) && !signing; - - let invoice = null; - if (signMethod === 'masterTicket') { - invoice = - - } else if (signMethod === 'bridge') { - invoice = - - } - - return ( - <> - { (signing && psbt) ? invoice : - - - - Send BTC - {value} - stopSending()} - /> - - - - To - {this.state.checkingPatp ? - : null - } - - {this.setState({focusPayee: true})}} - onBlur={() => {this.setState({focusPayee: false})}} - color={payeeColor} - backgroundColor={payeeBg} - borderColor={payeeBorder} - ml={2} - flexGrow="1" - fontSize='14px' - placeholder='~sampel-palnet or BTC address' - value={payee} - fontFamily="mono" - disabled={signing} - onChange={this.checkPayee} - /> - - {error && - - {/* yes this is a hack */} - - - - } - - Amount - {this.setState({focusCurrency: true})}} - onBlur={() => {this.setState({focusCurrency: false})}} - fontSize='14px' - width='100%' - type='number' - borderColor={this.state.focusCurrency ? "lightGray" : "none"} - disabled={signing} - value={denomAmount} - onChange={e => { - this.setState({ - denomAmount: e.target.value, - satsAmount: Math.round(parseFloat(e.target.value) / conversion * 100000000) - }); - }} - /> - {denomination} - - - {/* yes this is a hack */} - - {this.setState({focusSats: true})}} - onBlur={() => {this.setState({focusSats: false})}} - fontSize='14px' - width='100%' - type='number' - borderColor={this.state.focusSats ? "lightGray" : "none"} - disabled={signing} - value={satsAmount} - onChange={e => { - this.setState({ - denomAmount: parseFloat(e.target.value) * (conversion / 100000000), - satsAmount: e.target.value - }); - }} - /> - sats - - - Fee - - - {this.state.feeChoices[this.state.feeValue][1]} sats/vbyte - - {if (!this.state.showModal) this.setState({showModal: true}); }} - cursor="pointer"/> - - - - {!this.state.showModal ? null : - - } - - - Note - {this.setState({focusNote: true})}} - onBlur={() => {this.setState({focusNote: false})}} - fontSize='14px' - width='100%' - placeholder="What's this for?" - type='text' - borderColor={this.state.focusNote ? "lightGray" : "none"} - disabled={signing} - value={this.state.note} - onChange={e => { - this.setState({ - note: e.target.value, - }); - }} - /> - - - - - { (!(signing && !error)) ? null : - - } - + + {signMethod === 'masterTicket' && ( + + + + We recommend that you sign transactions using Bridge to protect + your master ticket. + + + )} + + )} + + ); +}; + +export default Send; diff --git a/pkg/btc-wallet/src/js/components/lib/sent.js b/pkg/btc-wallet/src/js/components/lib/sent.js index 994b02b97f..be4a8e1093 100644 --- a/pkg/btc-wallet/src/js/components/lib/sent.js +++ b/pkg/btc-wallet/src/js/components/lib/sent.js @@ -1,59 +1,36 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - StatelessTextInput as Input, - Row, - Center, - Text, - Button, - Col, -} from '@tlon/indigo-react'; - +import React from 'react'; +import { Icon, Row, Col, Center, Text } from '@tlon/indigo-react'; import { satsToCurrency } from '../../lib/util.js'; +import { useSettings } from '../../hooks/useSettings'; -export default function Sent(props) { - const { payee, denomination, satsAmount, stopSending, currencyRates } = props; +const Sent = ({ payee, stopSending, satsAmount }) => { + const { denomination, currencyRates } = useSettings(); return ( - - + +
{`You sent BTC to ${payee}`} + style={{ display: 'block', 'overflow-wrap': 'anywhere' }} + color="white" + >{`You sent BTC to ${payee}`}
-
- +
+ {satsToCurrency(satsAmount, denomination, currencyRates)} - - {`${satsAmount} sats`} - + {`${satsAmount} sats`}
); -} +}; + +export default Sent; diff --git a/pkg/btc-wallet/src/js/components/lib/settings.js b/pkg/btc-wallet/src/js/components/lib/settings.js index 445e527504..352cb5500f 100644 --- a/pkg/btc-wallet/src/js/components/lib/settings.js +++ b/pkg/btc-wallet/src/js/components/lib/settings.js @@ -1,121 +1,114 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Row, Text, Button, Col } from '@tlon/indigo-react'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../api'; -export default class Settings extends Component { - constructor(props) { - super(props); - this.changeProvider = this.changeProvider.bind(this); - this.replaceWallet = this.replaceWallet.bind(this); - } +const Settings = () => { + const { wallet, provider } = useSettings(); - changeProvider(){ - this.props.api.btcWalletCommand({'set-provider': null}); - } + const changeProvider = () => { + api.btcWalletCommand({ 'set-provider': null }); + }; - replaceWallet(){ - this.props.api.btcWalletCommand({ - 'delete-wallet': this.props.state.wallet, + const replaceWallet = () => { + api.btcWalletCommand({ + 'delete-wallet': wallet, }); - } + }; - render() { - let connColor = "red"; - let connBackground = "veryLightRed"; - let conn = 'Offline' - let host = ''; - if (this.props.state.provider){ - if (this.props.state.provider.connected) conn = 'Connected'; - if (this.props.state.provider.host) host = this.props.state.provider.host; - if (this.props.state.provider.connected && this.props.state.provider.host) { - connColor = "orange"; - connBackground = "lightOrange"; - } + let connColor = 'red'; + let connBackground = 'veryLightRed'; + let conn = 'Offline'; + let host = ''; + if (provider) { + if (provider.connected) conn = 'Connected'; + if (provider.host) host = provider.host; + if (provider.connected && provider.host) { + connColor = 'orange'; + connBackground = 'lightOrange'; } - - return ( - - - - XPub Derivation - - - - - {this.props.state.wallet} - - - - + + + + BTC Node Provider + + + + + ~{host} + + + {conn} + + + + + + + ); +}; + +export default Settings; diff --git a/pkg/btc-wallet/src/js/components/lib/signer.js b/pkg/btc-wallet/src/js/components/lib/signer.js index 094a6e8dd4..df371f76f5 100644 --- a/pkg/btc-wallet/src/js/components/lib/signer.js +++ b/pkg/btc-wallet/src/js/components/lib/signer.js @@ -1,52 +1,55 @@ -import React, { Component } from 'react'; +import React from 'react'; +import { Box, Button } from '@tlon/indigo-react'; -import { - Box, - Button, -} from '@tlon/indigo-react'; - -export default function Signer(props) { - const { signReady, initPayment, choosingSignMethod, signMethod, setSignMethod } = props; - - return ( - choosingSignMethod ? - +const Signer = ({ + signReady, + initPayment, + choosingSignMethod, + signMethod, + setSignMethod, +}) => { + return choosingSignMethod ? ( + - : + ) : ( + ); +}; + +export default Signer; diff --git a/pkg/btc-wallet/src/js/components/lib/startupModal.js b/pkg/btc-wallet/src/js/components/lib/startupModal.js index 9507452ebc..c4ee50c0a7 100644 --- a/pkg/btc-wallet/src/js/components/lib/startupModal.js +++ b/pkg/btc-wallet/src/js/components/lib/startupModal.js @@ -1,52 +1,44 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Box } from '@tlon/indigo-react'; +import WalletModal from './walletModal.js'; +import ProviderModal from './providerModal.js'; +import { useSettings } from '../../hooks/useSettings.js'; -import WalletModal from './walletModal.js' -import ProviderModal from './providerModal.js' +const StartupModal = () => { + const { wallet, provider } = useSettings(); + let modal = null; - -export default class StartupModal extends Component { - constructor(props) { - super(props); + if (wallet && provider) { + return null; + } else if (!provider) { + modal = ; + } else if (!wallet) { + modal = ; } - - - render() { - let modal = null; - - if (this.props.state.wallet && this.props.state.provider) { - return null; - } else if (!this.props.state.provider){ - modal = - - } else if (!this.props.state.wallet){ - modal = - } - return ( + return ( + - - {modal} - + {modal} - ); - } -} + + ); +}; + +export default StartupModal; diff --git a/pkg/btc-wallet/src/js/components/lib/transaction.js b/pkg/btc-wallet/src/js/components/lib/transaction.js index ba18a32aca..10b9a50951 100644 --- a/pkg/btc-wallet/src/js/components/lib/transaction.js +++ b/pkg/btc-wallet/src/js/components/lib/transaction.js @@ -1,105 +1,96 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, - LoadingSpinner, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Row, Text, Col } from '@tlon/indigo-react'; import _ from 'lodash'; +import TxAction from './tx-action.js'; +import TxCounterparty from './tx-counterparty.js'; +import { satsToCurrency } from '../../lib/util.js'; +import { useSettings } from '../../hooks/useSettings.js'; -import { Sigil } from './sigil.js' -import TxAction from './tx-action.js' -import TxCounterparty from './tx-counterparty.js' -import { satsToCurrency } from '../../lib/util.js' +const Transaction = ({ tx }) => { + const { denomination, currencyRates } = useSettings(); + const pending = !tx.recvd; -export default class Transaction extends Component { - constructor(props) { - super(props); - } + let weSent = _.find(tx.inputs, (input) => { + return input.ship === window.ship; + }); + let weRecv = tx.outputs.every((output) => { + return output.ship === window.ship; + }); - render() { - const pending = (!this.props.tx.recvd); + let action = weRecv ? 'recv' : weSent ? 'sent' : 'recv'; - let weSent = _.find(this.props.tx.inputs, (input) => { - return (input.ship === window.ship); + let counterShip = null; + let counterAddress = null; + let value; + let sign; + + if (action === 'sent') { + let counter = _.find(tx.outputs, (output) => { + return output.ship !== window.ship; }); - let weRecv = this.props.tx.outputs.every((output) => { - return (output.ship === window.ship) - }); - - let action = - (weRecv) ? "recv" : - (weSent) ? "sent" : "recv"; - - let counterShip = null; - let counterAddress = null; - let value; - let sign; - - if (action === "sent") { - let counter = _.find(this.props.tx.outputs, (output) => { - return (output.ship !== window.ship); - }); - counterShip = _.get(counter, 'ship', null); - counterAddress = _.get(counter, 'val.address', null); - value = _.get(counter, 'val.value', null); - sign = '-' - } - else if (action === "recv") { - value = _.reduce(this.props.tx.outputs, (sum, output) => { + counterShip = _.get(counter, 'ship', null); + counterAddress = _.get(counter, 'val.address', null); + value = _.get(counter, 'val.value', null); + sign = '-'; + } else if (action === 'recv') { + value = _.reduce( + tx.outputs, + (sum, output) => { if (output.ship === window.ship) { return sum + output.val.value; } else { return sum; } - }, 0); - - - if (weSent && weRecv) { - counterAddress = _.get(_.find(this.props.tx.inputs, (input) => { - return (input.ship === window.ship); - }), 'val.address', null); - } else { - let counter = _.find(this.props.tx.inputs, (input) => { - return (input.ship !== window.ship); - }); - counterShip = _.get(counter, 'ship', null); - counterAddress = _.get(counter, 'val.address', null); - } - sign = ''; - } - - let currencyValue = sign + satsToCurrency(value, this.props.denom, this.props.rates); - - const failure = Boolean(this.props.tx.failure); - if (failure) action = "fail"; - - const txid = this.props.tx.txid.dat.slice(2).replaceAll('.',''); - - - return ( - - - - - {sign}{value} sats - - - - - - - {currencyValue} - - + }, + 0 ); + + if (weSent && weRecv) { + counterAddress = _.get( + _.find(tx.inputs, (input) => { + return input.ship === window.ship; + }), + 'val.address', + null + ); + } else { + let counter = _.find(tx.inputs, (input) => { + return input.ship !== window.ship; + }); + counterShip = _.get(counter, 'ship', null); + counterAddress = _.get(counter, 'val.address', null); + } + sign = ''; } -} + + let currencyValue = sign + satsToCurrency(value, denomination, currencyRates); + + const failure = Boolean(tx.failure); + if (failure) action = 'fail'; + + const txid = tx.txid.dat.slice(2).replaceAll('.', ''); + + return ( + + + + + {sign} + {value} sats + + + + + + {currencyValue} + + + ); +}; + +export default Transaction; diff --git a/pkg/btc-wallet/src/js/components/lib/transactions.js b/pkg/btc-wallet/src/js/components/lib/transactions.js index a038cd0296..2bd7619956 100644 --- a/pkg/btc-wallet/src/js/components/lib/transactions.js +++ b/pkg/btc-wallet/src/js/components/lib/transactions.js @@ -1,62 +1,43 @@ import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, -} from '@tlon/indigo-react'; - +import { Box, Text, Col } from '@tlon/indigo-react'; import Transaction from './transaction.js'; +import { useSettings } from '../../hooks/useSettings.js'; - -export default class Transactions extends Component { - constructor(props) { - super(props); +const Transactions = () => { + const { history } = useSettings(); + if (!history || history.length <= 0) { + return ( + + + No Transactions Yet + + + ); + } else { + return ( + + {history.map((tx, i) => { + return ; + })} + + ); } +}; - - render() { - if (!this.props.state.history || this.props.state.history.length <= 0) { - return ( - - No Transactions Yet - - ); - } else { - return ( - - { - this.props.state.history.map((tx, i) => { - return( - - ); - }) - } - - ); - } - } -} +export default Transactions; diff --git a/pkg/btc-wallet/src/js/components/lib/tx-action.js b/pkg/btc-wallet/src/js/components/lib/tx-action.js index a8434f5155..12405f4934 100644 --- a/pkg/btc-wallet/src/js/components/lib/tx-action.js +++ b/pkg/btc-wallet/src/js/components/lib/tx-action.js @@ -1,74 +1,72 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Box, Icon, Row, Text, LoadingSpinner } from '@tlon/indigo-react'; +import { useSettings } from '../../hooks/useSettings'; -export default class TxAction extends Component { - constructor(props) { - super(props); - } +const TxAction = ({ action, pending, txid }) => { + const { network } = useSettings(); + const leftIcon = + action === 'sent' + ? 'ArrowSouth' + : action === 'recv' + ? 'ArrowNorth' + : action === 'fail' + ? 'X' + : 'NullIcon'; - render() { - const leftIcon = - this.props.action === 'sent' - ? 'ArrowSouth' - : this.props.action === 'recv' - ? 'ArrowNorth' - : this.props.action === 'fail' - ? 'X' - : 'NullIcon'; + const actionColor = + action === 'sent' + ? 'sentBlue' + : action === 'recv' + ? 'recvGreen' + : action === 'fail' + ? 'gray' + : 'red'; - const actionColor = - this.props.action === 'sent' - ? 'sentBlue' - : this.props.action === 'recv' - ? 'recvGreen' - : this.props.action === 'fail' - ? 'gray' - : 'red'; + const actionText = + action === 'sent' && !pending + ? 'Sent BTC' + : action === 'sent' && pending + ? 'Sending BTC' + : action === 'recv' && !pending + ? 'Received BTC' + : action === 'recv' && pending + ? 'Receiving BTC' + : action === 'fail' + ? 'Failed' + : 'error'; - const actionText = - this.props.action === 'sent' && !this.props.pending - ? 'Sent BTC' - : this.props.action === 'sent' && this.props.pending - ? 'Sending BTC' - : this.props.action === 'recv' && !this.props.pending - ? 'Received BTC' - : this.props.action === 'recv' && this.props.pending - ? 'Receiving BTC' - : this.props.action === 'fail' - ? 'Failed' - : 'error'; + const pendingSpinner = !pending ? null : ( + + ); - const pending = !this.props.pending ? null : ( - - ); + const url = + network === 'testnet' + ? `http://blockstream.info/testnet/tx/${txid}` + : `http://blockstream.info/tx/${txid}`; - const url = - this.props.network === 'testnet' - ? `http://blockstream.info/testnet/tx/${this.props.txid}` - : `http://blockstream.info/tx/${this.props.txid}`; + return ( + + + + + + {actionText} + + + + + {pendingSpinner} + + ); +}; - return ( - - - - - - {actionText} - - - - - {pending} - - ); - } -} +export default TxAction; diff --git a/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js b/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js index 27bf40d573..76cf2fd286 100644 --- a/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js +++ b/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js @@ -1,53 +1,36 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Icon, Row, Text } from '@tlon/indigo-react'; +import { Sigil } from './sigil.js'; -import { Sigil } from './sigil.js' -import TxAction from './tx-action.js' +const TxCounterparty = ({ ship, address }) => { + const icon = ship ? ( + + ) : ( + + + + ); + const addressText = !address + ? '' + : address.slice(0, 6) + '...' + address.slice(-6); + const text = ship ? `~${ship}` : addressText; -export default class TxCounterparty extends Component { - constructor(props) { - super(props); - } + return ( + + {icon} + + {text} + + + ); +}; - - render() { - const icon = (this.props.ship) - ? - : - - - const addressText = (!this.props.address) ? '' : - this.props.address.slice(0, 6) + '...' + - this.props.address.slice(-6); - const text = (this.props.ship) ? - `~${this.props.ship}` : addressText; - - return ( - - {icon} - {text} - - ); - } -} +export default TxCounterparty; diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/js/components/lib/walletModal.js index 49796cd11e..a34a596c03 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/js/components/lib/walletModal.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { useState } from 'react'; import { Box, Text, @@ -6,247 +6,251 @@ import { StatelessTextInput, Icon, Row, - Input, LoadingSpinner, } from '@tlon/indigo-react'; - import { patp2dec, isValidPatq } from 'urbit-ob'; +import * as kg from 'urbit-key-generation'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../api'; -const kg = require('urbit-key-generation'); -const bitcoin = require('bitcoinjs-lib'); -const bs58check = require('bs58check') -import { Buffer } from 'buffer'; +const WalletModal = () => { + const { network } = useSettings(); + const [mode, setMode] = useState('xpub'); + const [masterTicket, setMasterTicket] = useState(''); + const [confirmedMasterTicket, setConfirmedMasterTicket] = useState(''); + const [xpub, setXpub] = useState(''); + const [readyToSubmit, setReadyToSubmit] = useState(false); + const [processingSubmission, setProcessingSubmission] = useState(false); + const [confirmingMasterTicket, setConfirmingMasterTicket] = useState(false); + const [error, setError] = useState(false); -export default class WalletModal extends Component { - constructor(props) { - super(props); - - this.state = { - mode: 'xpub', - masterTicket: '', - confirmedMasterTicket: '', - xpub: '', - readyToSubmit: false, - processingSubmission: false, - confirmingMasterTicket: false, - error: false, - } - this.checkTicket = this.checkTicket.bind(this); - this.checkXPub = this.checkXPub.bind(this); - this.submitMasterTicket = this.submitMasterTicket.bind(this); - this.submitXPub = this.submitXPub.bind(this); - - } - - checkTicket(e){ + const checkTicket = ({ + event: { + target: { value }, + }, + }) => { // TODO: port over bridge ticket validation logic - if (this.state.confirmingMasterTicket) { - let confirmedMasterTicket = e.target.value; - let readyToSubmit = isValidPatq(confirmedMasterTicket); - this.setState({confirmedMasterTicket, readyToSubmit}); + if (confirmingMasterTicket) { + setConfirmedMasterTicket(value); + setReadyToSubmit(isValidPatq(value)); } else { - let masterTicket = e.target.value; - let readyToSubmit = isValidPatq(masterTicket); - this.setState({masterTicket, readyToSubmit}); + setMasterTicket(value); + setReadyToSubmit(isValidPatq(value)); } - } + }; - checkXPub(e){ - let xpub = e.target.value; - let readyToSubmit = (xpub.length > 0); - this.setState({xpub, readyToSubmit}); - } + const checkXPub = ({ + event: { + target: { value: xpubGiven }, + }, + }) => { + setXpub(xpubGiven); + setReadyToSubmit(xpubGiven.length > 0); + }; - submitMasterTicket(ticket){ - this.setState({processingSubmission: true}); - kg.generateWallet({ ticket, ship: parseInt(patp2dec('~' + window.ship)) }) - .then(urbitWallet => { - const { xpub } = this.props.network === 'testnet' - ? urbitWallet.bitcoinTestnet.keys : - urbitWallet.bitcoinMainnet.keys - - this.submitXPub(xpub); - }); - - } - - submitXPub(xpub){ + const submitXPub = (givenXpub) => { const command = { - "add-wallet": { - "xpub": xpub, - "fprint": [4, 0], - "scan-to": null, - "max-gap": 8, - "confs": 1 - } - } + 'add-wallet': { + xpub: givenXpub, + fprint: [4, 0], + 'scan-to': null, + 'max-gap': 8, + confs: 1, + }, + }; api.btcWalletCommand(command); - this.setState({processingSubmission: true}); - } + setProcessingSubmission(true); + }; - render() { - const buttonDisabled = (!this.state.readyToSubmit || this.state.processingSubmission ); - const inputDisabled = this.state.processingSubmission; - const processingSpinner = (!this.state.processingSubmission) ? null : - + const submitMasterTicket = (ticket) => { + setProcessingSubmission(true); + kg.generateWallet({ + ticket, + ship: parseInt(patp2dec('~' + window.ship)), + }).then((urbitWallet) => { + const { xpub: xpubFromWallet } = + network === 'testnet' + ? urbitWallet.bitcoinTestnet.keys + : urbitWallet.bitcoinMainnet.keys; - if (this.state.mode === 'masterTicket'){ - return ( - - - - - Step 2 of 2: Import your extended public key - - - - - - We recommend that you import your wallet using Bridge to protect your master ticket. - - - - {this.state.confirmingMasterTicket && - this.setState({ - confirmingMasterTicket: false, - masterTicket: '', - confirmedMasterTicket: '', - error: false - })}/>} - - {this.state.confirmingMasterTicket ? 'Confirm Master Ticket' : 'Master Ticket'} - - - - value.replace(/[^~-]+/g, '••••••')} - placeholder="••••••-••••••-••••••-••••••" - autoCapitalize="none" - autoCorrect="off" - onChange={this.checkTicket} - /> - {(!inputDisabled) ? null : } - - {this.state.error && - - - Master tickets do not match - - - } - - + + + + ); + } else if (mode === 'xpub') { + return ( + + + + + Step 2 of 2: Import your extended public key + + + + + Visit{' '} + + bridge.urbit.org + {' '} + to obtain your key + - ); - } + + + Extended Public Key (XPub) + + + checkXPub(e)} + /> + + { + if (inputDisabled) return; + setMode('masterTicket'); + setXpub(''); + setMasterTicket(''); + setReadyToSubmit(false); + }} + > + Import using master ticket -> + + + + + ); } -} +}; + +export default WalletModal; diff --git a/pkg/btc-wallet/src/js/components/lib/warning.js b/pkg/btc-wallet/src/js/components/lib/warning.js index 0feaf75ada..d0183b7e50 100644 --- a/pkg/btc-wallet/src/js/components/lib/warning.js +++ b/pkg/btc-wallet/src/js/components/lib/warning.js @@ -1,79 +1,72 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - Button, - Col, - Anchor, -} from '@tlon/indigo-react'; +import React from 'react'; +import { Box, Text, Button, Col, Anchor } from '@tlon/indigo-react'; +import { api } from '../../api'; +import { useSettings } from '../../hooks/useSettings'; -import { store } from '../../store' - - - -export default class Warning extends Component { - constructor(props) { - super(props); - - this.understand = this.understand.bind(this); - } - - understand(){ - store.handleEvent({ data: { bucket: { warning: false}}}); +const Warning = () => { + const { setWarning } = useSettings(); + const understand = () => { + setWarning(false); let removeWarning = { - "put-entry": { + 'put-entry': { value: false, - "entry-key": "warning", - "bucket-key": "btc-wallet", - } - } - this.props.api.settingsEvent(removeWarning); - } + 'entry-key': 'warning', + 'bucket-key': 'btc-wallet', + }, + }; + api.settingsEvent(removeWarning); + }; - render() { - return ( - + + + Warning! + +
+ + Be safe while using this wallet, and be sure to store responsible + amounts of BTC. + + + Always ensure that the checksum of the wallet matches that of the + wallet's repo. + +
+ + + Learn more on urbit.org + + + + +
+ ); +}; + +export default Warning; diff --git a/pkg/btc-wallet/src/js/components/root.js b/pkg/btc-wallet/src/js/components/root.js index 082ffe68b4..e3b6288cf0 100644 --- a/pkg/btc-wallet/src/js/components/root.js +++ b/pkg/btc-wallet/src/js/components/root.js @@ -1,67 +1,39 @@ -import React, { Component } from 'react'; +import React from 'react'; import { BrowserRouter } from 'react-router-dom'; -import { api } from '../api.js'; -import { store } from '../store.js'; import { ThemeProvider } from 'styled-components'; import light from './themes/light'; -// import dark from './themes/dark'; import { Box, Reset } from '@tlon/indigo-react'; import StartupModal from './lib/startupModal.js'; import Body from './lib/body.js'; -import { subscription } from '../subscription.js'; +import { useSettings } from '../hooks/useSettings.js'; -const network = 'bitcoin'; +const Root = () => { + const { loaded, wallet, provider } = useSettings(); + const blur = !loaded ? false : !(wallet && provider); -export class Root extends Component { - constructor(props) { - super(props); - this.ship = window.ship; - this.state = store.state; - store.setStateHandler(this.setState.bind(this)); - } + return ( + + + + {loaded ? : null} + + + + + + ); +}; - componentDidMount() { - this.props.channel.setOnChannelError(() => { - subscription.start(); - }); - subscription.start(); - } - - render() { - const loaded = this.state.loaded; - const warning = this.state.showWarning; - const blur = !loaded ? false : !(this.state.wallet && this.state.provider); - - return ( - - - - {loaded ? ( - - ) : null} - - - - - - ); - } -} +export default Root; diff --git a/pkg/btc-wallet/src/js/lib/util.js b/pkg/btc-wallet/src/js/lib/util.js index 2bc6f7a404..131ec4012c 100644 --- a/pkg/btc-wallet/src/js/lib/util.js +++ b/pkg/btc-wallet/src/js/lib/util.js @@ -1,21 +1,17 @@ -import _ from 'lodash'; -import classnames from 'classnames'; - - export function uuid() { - let str = "0v" - str += Math.ceil(Math.random()*8)+"." + let str = '0v'; + str += Math.ceil(Math.random() * 8) + '.'; for (var i = 0; i < 5; i++) { - let _str = Math.ceil(Math.random()*10000000).toString(32); - _str = ("00000"+_str).substr(-5,5); - str += _str+"."; + let _str = Math.ceil(Math.random() * 10000000).toString(32); + _str = ('00000' + _str).substr(-5, 5); + str += _str + '.'; } - return str.slice(0,-1); + return str.slice(0, -1); } export function isPatTa(str) { - const r = /^[a-z,0-9,\-,\.,_,~]+$/.exec(str) + const r = /^[a-z,0-9,\-,.,_,~]+$/.exec(str); return !!r; } @@ -26,13 +22,15 @@ export function isPatTa(str) { (javascript Date object) */ export function daToDate(st) { - var dub = function(n) { - return parseInt(n) < 10 ? "0" + parseInt(n) : n.toString(); + var dub = function (n) { + return parseInt(n) < 10 ? '0' + parseInt(n) : n.toString(); }; var da = st.split('..'); var bigEnd = da[0].split('.'); var lilEnd = da[1].split('.'); - var ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub(lilEnd[0])}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`; + var ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub( + lilEnd[0] + )}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`; return new Date(ds); } @@ -44,18 +42,18 @@ export function daToDate(st) { */ export function dateToDa(d, mil) { -  var fil = function(n) { -    return n >= 10 ? n : "0" + n; -  }; -  return ( -    `~${d.getUTCFullYear()}.` + -    `${(d.getUTCMonth() + 1)}.` + -    `${fil(d.getUTCDate())}..` + -    `${fil(d.getUTCHours())}.` + -    `${fil(d.getUTCMinutes())}.` + -    `${fil(d.getUTCSeconds())}` + - `${mil ? "..0000" : ""}` -  ); + var fil = function (n) { + return n >= 10 ? n : '0' + n; + }; + return ( + `~${d.getUTCFullYear()}.` + + `${d.getUTCMonth() + 1}.` + + `${fil(d.getUTCDate())}..` + + `${fil(d.getUTCHours())}.` + + `${fil(d.getUTCMinutes())}.` + + `${fil(d.getUTCSeconds())}` + + `${mil ? '..0000' : ''}` + ); } export function deSig(ship) { @@ -64,49 +62,59 @@ export function deSig(ship) { // trim patps to match dojo, chat-cli export function cite(ship) { - let patp = ship, shortened = ""; - if (patp.startsWith("~")) { + let patp = ship, + shortened = ''; + if (patp.startsWith('~')) { patp = patp.substr(1); } // comet if (patp.length === 56) { - shortened = "~" + patp.slice(0, 6) + "_" + patp.slice(50, 56); + shortened = '~' + patp.slice(0, 6) + '_' + patp.slice(50, 56); return shortened; } // moon if (patp.length === 27) { - shortened = "~" + patp.slice(14, 20) + "^" + patp.slice(21, 27); + shortened = '~' + patp.slice(14, 20) + '^' + patp.slice(21, 27); return shortened; } return `~${patp}`; } -export function satsToCurrency(sats, denomination, rates){ +export function satsToCurrency(sats, denomination, rates) { if (!rates) { - throw "nonexistent currency table" + throw 'nonexistent currency table'; } - if (!rates[denomination]){ - denomination = "BTC"; + if (!rates[denomination]) { + denomination = 'BTC'; } let rate = rates[denomination]; - let val = parseFloat(((sats * rate.last) * 0.00000001).toFixed(8)); + let val = rate ? parseFloat((sats * rate.last * 0.00000001).toFixed(8)) : 0; let text; - if (denomination === 'BTC'){ - text = val + ' ' + rate.symbol - } else { - text = rate.symbol + val.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') + if (denomination === 'BTC' && rate) { + text = val + ' ' + rate.symbol; + } else if (rate) { + text = + rate.symbol + val.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); } - return text + return text; } -export function currencyToSats(val, denomination, rates){ +export function currencyToSats(val, denomination, rates) { if (!rates) { - throw "nonexistent currency table" + throw 'nonexistent currency table'; } - if (!rates[denomination]){ - throw 'currency not in table' + if (!rates[denomination]) { + throw 'currency not in table'; } let rate = rates[denomination]; let sats = (parseFloat(val) / rate.last) * 100000000; return sats; } + +export function reduceHistory(history) { + return Object.values(history).sort((hest1, hest2) => { + if (hest1.recvd === null) return -1; + if (hest2.recvd === null) return +1; + return hest2.recvd - hest1.recvd; + }); +} diff --git a/pkg/btc-wallet/src/js/reducers/currency.js b/pkg/btc-wallet/src/js/reducers/currency.js deleted file mode 100644 index 80470bce42..0000000000 --- a/pkg/btc-wallet/src/js/reducers/currency.js +++ /dev/null @@ -1,20 +0,0 @@ -import _ from 'lodash'; - -export class CurrencyReducer { - reduce(json, state) { - if (!json) { - return; - } - if (json.currencyRates) { - for (var c in json.currencyRates) { - state.currencyRates[c] = json.currencyRates[c]; - } - } - - if (json.denomination) { - if (state.currencyRates[json.denomination]) { - state.denomination = json.denomination - } - } - } -} diff --git a/pkg/btc-wallet/src/js/reducers/initial.js b/pkg/btc-wallet/src/js/reducers/initial.js deleted file mode 100644 index 2445eae6b8..0000000000 --- a/pkg/btc-wallet/src/js/reducers/initial.js +++ /dev/null @@ -1,29 +0,0 @@ -import _ from 'lodash'; - -export class InitialReducer { - reduce(json, state) { - let data = _.get(json, 'initial', false); - if (data) { - state.provider = data.provider; - state.wallet = data.wallet; - state.confirmedBalance = _.get(data.balance, 'confirmed', null); - state.unconfirmedBalance = _.get(data.balance, 'unconfirmed', null); - state.btcState = data['btc-state']; - state.history = this.reduceHistory(data.history); - state.address = data.address; - - state.loadedBtc = true; - if (state.loadedSettings) { - state.loaded = true; - } - } - } - - reduceHistory(history) { - return Object.values(history).sort((hest1, hest2) => { - if (hest1.recvd === null) return -1; - if (hest2.recvd === null) return +1; - return (hest2.recvd - hest1.recvd) - }) - } -} diff --git a/pkg/btc-wallet/src/js/reducers/settings.js b/pkg/btc-wallet/src/js/reducers/settings.js deleted file mode 100644 index dce62613bb..0000000000 --- a/pkg/btc-wallet/src/js/reducers/settings.js +++ /dev/null @@ -1,30 +0,0 @@ -import _ from 'lodash'; - -export class SettingsReducer { - reduce(json, state) { - let data = _.get(json, 'bucket', false); - if (data) { - let warning = _.get(json, 'bucket.warning', -1); - if (warning !== -1) { - state.showWarning = warning - } - let currency = _.get(json, 'bucket.currency', -1); - if (currency !== -1) { - state.denomination = currency; - } - - state.loadedSettings = true; - if (state.loadedBtc) { - state.loaded = true; - } - } - let entry = _.get(json, 'settings-event.put-entry.entry-key', false); - if (entry === 'currency') { - let value = _.get(json, 'settings-event.put-entry.value', false); - state.denomination = value; - } else if (entry === 'warning') { - let value = _.get(json, 'settings-event.put-entry.value', false); - state.showWarning = value; - } - } -} diff --git a/pkg/btc-wallet/src/js/reducers/update.js b/pkg/btc-wallet/src/js/reducers/update.js deleted file mode 100644 index b47ced0b5d..0000000000 --- a/pkg/btc-wallet/src/js/reducers/update.js +++ /dev/null @@ -1,116 +0,0 @@ -import _ from 'lodash'; - - -export class UpdateReducer { - reduce(json, state) { - if (!json) { - return; - } - if (json.providerStatus) { - this.reduceProviderStatus(json.providerStatus, state); - } - if (json.checkPayee) { - this.reduceCheckPayee(json.checkPayee, state); - } - if ("change-provider" in json) { - this.reduceChangeProvider(json["change-provider"], state); - } - if (json["change-wallet"]) { - this.changeWallet(json["change-wallet"], state); - } - if (json.hasOwnProperty('psbt')) { - this.reducePsbt(json.psbt, state); - } - if (json["btc-state"]) { - this.reduceBtcState(json["btc-state"], state); - } - if (json["new-tx"]) { - this.reduceNewTx(json["new-tx"], state); - } - if (json["cancel-tx"]) { - this.reduceCancelTx(json["cancel-tx"], state); - } - if (json.address) { - this.reduceAddress(json.address, state); - } - if (json.balance) { - this.reduceBalance(json.balance, state); - } - if (json.hasOwnProperty('error')) { - this.reduceError(json.error, state); - } - if (json.hasOwnProperty('broadcast-success')){ - state.broadcastSuccess = true; - } - if (json.hasOwnProperty('broadcast-fail')){ - state.broadcastSuccess = false; - } - } - - reduceProviderStatus(json, state) { - state.providerPerms[json.provider] = json.permitted; - } - - reduceCheckPayee(json, state) { - state.shipWallets[json.payee] = json.hasWallet; - } - - reduceChangeProvider(json, state) { - state.provider = json; - } - - reduceChangeWallet(json, state) { - state.wallet = json; - } - - reducePsbt(json, state) { - state.psbt = json.pb; - state.fee = json.fee; - } - - reduceBtcState(json, state) { - state.btcState = json; - } - - reduceNewTx(json, state) { - let old = _.findIndex(state.history, (h) => { - return ( h.txid.dat === json.txid.dat && - h.txid.wid === json.txid.wid ); - }); - if (old !== -1) { - delete state.history.splice(old, 1); - } - if (json.recvd === null) { - state.history.unshift(json); - } else { - // we expect history to have null recvd values first, and the rest in - // descending order - let insertionIndex = _.findIndex(state.history, (h) => { - return ((h.recvd < json.recvd) && (h.recvd !== null)); - }); - state.history.splice(insertionIndex, 0, json); - } - } - - reduceCancelTx(json, state) { - let entryIndex = _.findIndex(state.history, (h) => { - return ((json.wid === h.txid.wid) && (json.dat === h.txid.dat)); - }); - if (entryIndex > -1) { - state.history[entryIndex].failure = true; - } - } - - reduceAddress(json, state) { - state.address = json; - } - - reduceBalance(json, state) { - state.unconfirmedBalance = json.unconfirmed; - state.confirmedBalance = json.confirmed; - } - - reduceError(json, state) { - state.error = json; - } -} diff --git a/pkg/btc-wallet/src/js/store.js b/pkg/btc-wallet/src/js/store.js deleted file mode 100644 index 40cd3270fe..0000000000 --- a/pkg/btc-wallet/src/js/store.js +++ /dev/null @@ -1,54 +0,0 @@ -import { InitialReducer } from './reducers/initial'; -import { UpdateReducer } from './reducers/update'; -import { CurrencyReducer } from './reducers/currency'; -import { SettingsReducer } from './reducers/settings'; - -class Store { - constructor() { - this.state = { - loadedBtc: false, - loadedSettings: false, - loaded: false, - providerPerms: {}, - shipWallets: {}, - provider: null, - wallet: null, - confirmedBalance: null, - unconfirmedBalance: null, - btcState: null, - history: [], - psbt: '', - address: null, - currencyRates: { - BTC: { last: 1, symbol: 'BTC' } - }, - denomination: 'BTC', - showWarning: true, - error: '', - broadcastSuccess: false, - }; - - this.initialReducer = new InitialReducer(); - this.updateReducer = new UpdateReducer(); - this.currencyReducer = new CurrencyReducer(); - this.settingsReducer = new SettingsReducer(); - this.setState = () => { }; - } - - setStateHandler(setState) { - this.setState = setState; - } - - handleEvent(data) { - let json = data.data; - this.initialReducer.reduce(json, this.state); - this.updateReducer.reduce(json, this.state); - this.currencyReducer.reduce(json, this.state); - this.settingsReducer.reduce(json, this.state); - - this.setState(this.state); - } -} - -export let store = new Store(); -window.store = store; From e645551f510297a12fe86bc3a0e312ceef681257 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 16:58:05 -0500 Subject: [PATCH 34/64] Fix patp check in send --- pkg/btc-wallet/src/js/components/lib/send.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index 9bb6cbbd8a..c55bc1fd77 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -64,15 +64,12 @@ const Send = ({ stopSending, value, conversion }) => { }; const checkPayee = (e) => { - console.log('checkPayee', { e }); setError(''); let payeeReceived = e.target.value; let isPatp = ob.isValidPatp(payeeReceived); let isAddress = validate(payeeReceived); - console.log({ payeeReceived, isPatp, isAddress }); if (isPatp) { - console.log('isPatp', isPatp); let command = { 'check-payee': payeeReceived }; api.btcWalletCommand(command); setTimeout(() => { @@ -144,16 +141,14 @@ const Send = ({ stopSending, value, conversion }) => { }, []); useEffect(() => { - if (!ready && checkingPatp) { - console.log({ ready, checkingPatp, shipWallets, payee }); + if (!ready && !checkingPatp) { if (shipWallets.payee === payee.slice(1) && shipWallets.hasWallet) { - console.log('good'); setReady(true); setCheckingPatp(false); setValidPayee(true); } } - }, [ready, checkingPatp, shipWallets]); + }, [ready, checkingPatp, shipWallets, payee]); let payeeColor = 'black'; let payeeBg = 'white'; From 278c0d63fa1dfe78ec972733b19353855482726e Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 17:23:10 -0500 Subject: [PATCH 35/64] Fix feepicker --- .../src/js/components/lib/feePicker.js | 45 +++++-------------- pkg/btc-wallet/src/js/components/lib/send.js | 17 ++++--- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js index 00e91c198d..9c7c77a542 100644 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ b/pkg/btc-wallet/src/js/components/lib/feePicker.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useRef } from 'react'; +import React from 'react'; import { Box, Text, @@ -6,38 +6,15 @@ import { StatelessRadioButtonField as RadioButton, Label, } from '@tlon/indigo-react'; +import { feeLevels } from './send'; -const feeLevels = { - low: 'low', - mid: 'mid', - high: 'high', -}; - -const FeePicker = ({ feeChoices, feeSelect, feeDismiss }) => { - const [feeSelected, setFeeSelected] = useState(feeLevels.mid); - const [modalElement, setModalElement] = useState(); - const modalRef = useRef(); - - // const clickDismiss = (e) => { - // console.log(modalElement, e); - // // if (modalRef && !modalRef.contains(e.target)) { - // // feeDismiss(); - // // } - // }; - +const FeePicker = ({ feeChoices, feeValue, setFeeValue, feeDismiss }) => { const select = (which) => { - setFeeSelected(which); - feeSelect(which); + console.log(which); + setFeeValue(feeLevels[which]); feeDismiss(); }; - // useEffect(() => { - // document.addEventListener('click', (e) => clickDismiss(e)); - // setModalElement(modalRef.current); - // console.log(modalRef.current); - // return () => document.addEventListener('click', clickDismiss); - // }, []); - return ( { { - select('low'); + select(feeLevels.low); }} >
@@ -168,7 +168,7 @@ const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { color={inputColor} backgroundColor={inputBg} borderColor={inputBorder} - style={{ 'line-height': '4' }} + style={{ lineHeight: '4' }} onChange={(e) => checkTxHex(e)} /> {localError !== '' && ( diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/js/components/lib/invoice.js index 771a876698..4d9c6563c1 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/js/components/lib/invoice.js @@ -214,7 +214,7 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { mono color="gray" fontSize="14px" - style={{ display: 'block', 'overflow-wrap': 'anywhere' }} + style={{ display: 'block', overflowWrap: 'anywhere' }} > {payee} diff --git a/pkg/btc-wallet/src/js/components/lib/sent.js b/pkg/btc-wallet/src/js/components/lib/sent.js index be4a8e1093..fe570f0fb3 100644 --- a/pkg/btc-wallet/src/js/components/lib/sent.js +++ b/pkg/btc-wallet/src/js/components/lib/sent.js @@ -19,7 +19,7 @@ const Sent = ({ payee, stopSending, satsAmount }) => {
{`You sent BTC to ${payee}`}
From ecc8352656bb3bdcf524180a7e31b67cf1afc18c Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 17:30:33 -0500 Subject: [PATCH 38/64] Remove more console logs --- pkg/btc-wallet/src/js/components/lib/feePicker.js | 1 - pkg/btc-wallet/src/js/hooks/useSettings.js | 1 - 2 files changed, 2 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js index 9c7c77a542..7fc5153a23 100644 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ b/pkg/btc-wallet/src/js/components/lib/feePicker.js @@ -10,7 +10,6 @@ import { feeLevels } from './send'; const FeePicker = ({ feeChoices, feeValue, setFeeValue, feeDismiss }) => { const select = (which) => { - console.log(which); setFeeValue(feeLevels[which]); feeDismiss(); }; diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 81a9f39701..8e35d52345 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -216,7 +216,6 @@ export const SettingsProvider = ({ channel, children }) => { for (let c in checkPayee) { newShipWallets[c] = checkPayee[c]; } - console.log({ newShipWallets }); setShipWallets(newShipWallets); } if (changeWallet) { From 76743129fb49ed6db6b0ec4af4499ac3a8184aa5 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Wed, 30 Jun 2021 17:44:45 -0500 Subject: [PATCH 39/64] Add fee to useSettings hook --- pkg/btc-wallet/src/js/components/lib/send.js | 2 -- pkg/btc-wallet/src/js/hooks/useSettings.js | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index abec450436..f3b050643a 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -125,7 +125,6 @@ const Send = ({ stopSending, value, conversion }) => { }; useEffect(() => { - console.log({ network }); if (network === 'bitcoin') { let url = 'https://bitcoiner.live/api/fees/estimates/latest'; fetch(url) @@ -134,7 +133,6 @@ const Send = ({ stopSending, value, conversion }) => { // let estimates = Object.keys(n.estimates); // let mid = Math.floor(estimates.length / 2); // let high = estimates.length - 1; - console.log('hello'); setFeeChoices({ high: [30, n.estimates[30]['sat_per_vbyte']], mid: [180, n.estimates[180]['sat_per_vbyte']], diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 8e35d52345..02393afcb2 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -27,6 +27,8 @@ export const SettingsContext = createContext({ setBtcState: () => {}, history: [], setHistory: () => {}, + fee: 0, + setFee: () => {}, psbt: '', setPsbt: () => {}, address: null, @@ -58,6 +60,7 @@ export const SettingsProvider = ({ channel, children }) => { const [btcState, setBtcState] = useState(null); const [history, setHistory] = useState([]); const [psbt, setPsbt] = useState(''); + const [fee, setFee] = useState(0); const [address, setAddress] = useState(null); const [currencyRates, setCurrencyRates] = useState({ BTC: { last: 1, symbol: 'BTC' }, @@ -223,6 +226,7 @@ export const SettingsProvider = ({ channel, children }) => { } if (psbtData) { setPsbt(psbtData.pb); + setFee(psbtData.fee); } if (cancelTx) { handleCancelTx(cancelTx); @@ -281,6 +285,8 @@ export const SettingsProvider = ({ channel, children }) => { setHistory, psbt, setPsbt, + fee, + setFee, address, setAddress, currencyRates, From 72fbbb11e52cdcd6d1fad9deb8f40bd2a1b79ab3 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 1 Jul 2021 11:38:18 -0500 Subject: [PATCH 40/64] Fix newTx handler --- pkg/btc-wallet/src/js/hooks/useSettings.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 02393afcb2..55e7038e48 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -134,22 +134,28 @@ export const SettingsProvider = ({ channel, children }) => { } }; - const handleNewTx = ({ txid, recvd }) => { + const handleNewTx = (newTx) => { + const { txid, recvd } = newTx; let old = _.findIndex(history, (h) => { return h.txid.dat === txid.dat && h.txid.wid === txid.wid; }); if (old !== -1) { - delete history.splice(old, 1); + const newHistory = history.filter((o, i) => i !== old); + setHistory(newHistory); } - if (recvd === null) { - history.unshift({ txid, recvd }); - } else { + if (recvd === null && old === -1) { + const newHistory = [...history, newTx]; + setHistory(newHistory); + } else if (recvd !== null && old === -1) { // we expect history to have null recvd values first, and the rest in // descending order let insertionIndex = _.findIndex(history, (h) => { return h.recvd < recvd && h.recvd !== null; }); - history.splice(insertionIndex, 0, { txid, recvd }); + const newHistory = history.map((o, i) => + i === insertionIndex ? newTx : o + ); + setHistory(newHistory); } }; From 3b03f6ba59a8f33bd6ae367caa0c5042a5c7e7a5 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 1 Jul 2021 11:39:12 -0500 Subject: [PATCH 41/64] Cleanup walletModal, transaction, invoice --- .../src/js/components/lib/invoice.js | 27 +++++-------------- .../src/js/components/lib/transactions.js | 2 +- .../src/js/components/lib/walletModal.js | 6 +---- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/js/components/lib/invoice.js index 4d9c6563c1..9b843d617e 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/js/components/lib/invoice.js @@ -1,4 +1,4 @@ -import React, { useRef, useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { Box, Icon, @@ -57,7 +57,6 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { const [ready, setReady] = useState(false); const [localError, setLocalError] = useState(error); const [broadcasting, setBroadcasting] = useState(false); - const invoiceRef = useRef(); useEffect(() => { if (broadcasting && localError !== '') { @@ -65,17 +64,6 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { } }, [error, broadcasting, setBroadcasting]); - const clickDismiss = (e) => { - if (invoiceRef && !invoiceRef.contains(e.target)) { - stopSending(); - } - }; - - useEffect(() => { - document.addEventListener('click', clickDismiss); - return () => document.removeEventListener('click', clickDismiss); - }, []); - const broadCastTx = (psbtHex) => { let command = { 'broadcast-tx': psbtHex, @@ -90,6 +78,7 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { ticket, ship: parseInt(patp2dec('~' + window.ship)), }).then((urbitWallet) => { + // this wasn't being used, not clear why it was pulled out. // const { xpub } = // network === 'testnet' // ? urbitWallet.bitcoinTestnet.keys @@ -127,11 +116,11 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { }); }; - const checkTicket = (e) => { + const checkTicket = ({ target: { value } }) => { // TODO: port over bridge ticket validation logic - setMasterTicket(e.target.value); - setReady(isValidPatq(e.target.value)); - setLocalError(isValidPatq(e.target.value) ? '' : 'invalid-master-ticket'); + setMasterTicket(value); + setReady(isValidPatq(value)); + setLocalError(isValidPatq(value) ? '' : 'invalid-master-ticket'); }; let inputColor = 'black'; @@ -168,13 +157,11 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { ) : ( stopSending()} > { color={inputColor} backgroundColor={inputBg} borderColor={inputBorder} - onChange={() => checkTicket()} + onChange={(e) => checkTicket(e)} /> {error !== '' && ( diff --git a/pkg/btc-wallet/src/js/components/lib/transactions.js b/pkg/btc-wallet/src/js/components/lib/transactions.js index 2bd7619956..c968320bcd 100644 --- a/pkg/btc-wallet/src/js/components/lib/transactions.js +++ b/pkg/btc-wallet/src/js/components/lib/transactions.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { Box, Text, Col } from '@tlon/indigo-react'; import Transaction from './transaction.js'; import { useSettings } from '../../hooks/useSettings.js'; diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/js/components/lib/walletModal.js index a34a596c03..be0bc6ef07 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/js/components/lib/walletModal.js @@ -39,11 +39,7 @@ const WalletModal = () => { } }; - const checkXPub = ({ - event: { - target: { value: xpubGiven }, - }, - }) => { + const checkXPub = ({ target: { value: xpubGiven } }) => { setXpub(xpubGiven); setReadyToSubmit(xpubGiven.length > 0); }; From 696912228e87705cb51f4f55ffd382b05a500327 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 07:21:52 -0500 Subject: [PATCH 42/64] Fix destructured param in checkTicket --- pkg/btc-wallet/src/js/components/lib/walletModal.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/js/components/lib/walletModal.js index be0bc6ef07..f8687441d5 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/js/components/lib/walletModal.js @@ -24,11 +24,7 @@ const WalletModal = () => { const [confirmingMasterTicket, setConfirmingMasterTicket] = useState(false); const [error, setError] = useState(false); - const checkTicket = ({ - event: { - target: { value }, - }, - }) => { + const checkTicket = ({ target: { value } }) => { // TODO: port over bridge ticket validation logic if (confirmingMasterTicket) { setConfirmedMasterTicket(value); From 80de9f9b493c13d6f73563d3437b94bddfa845a3 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 08:27:49 -0500 Subject: [PATCH 43/64] Map dollar sign to USD --- pkg/btc-wallet/src/js/hooks/useSettings.js | 3 ++- pkg/btc-wallet/src/js/lib/util.js | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/js/hooks/useSettings.js index 55e7038e48..c6e4917536 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/js/hooks/useSettings.js @@ -1,7 +1,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; import _ from 'lodash'; import { api } from '../api'; -import { reduceHistory } from '../lib/util'; +import { mapDenominationToSymbol, reduceHistory } from '../lib/util'; export const SettingsContext = createContext({ network: 'bitcoin', @@ -120,6 +120,7 @@ export const SettingsProvider = ({ channel, children }) => { const newCurrencyRates = currencyRates; for (let c in n) { newCurrencyRates[c] = n[c]; + newCurrencyRates[c].symbol = mapDenominationToSymbol(c); } setCurrencyRates(newCurrencyRates); setTimeout(() => initializeCurrencyPoll(), 1000 * 60 * 15); diff --git a/pkg/btc-wallet/src/js/lib/util.js b/pkg/btc-wallet/src/js/lib/util.js index 131ec4012c..2c133559a6 100644 --- a/pkg/btc-wallet/src/js/lib/util.js +++ b/pkg/btc-wallet/src/js/lib/util.js @@ -118,3 +118,12 @@ export function reduceHistory(history) { return hest2.recvd - hest1.recvd; }); } + +export function mapDenominationToSymbol(denomination) { + switch (denomination) { + case 'USD': + return '$'; + default: + return denomination; + } +} From 3ad8e9c49ff8ca5854162da611c4de66aabdc7cb Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 10 Jul 2021 14:17:52 -0500 Subject: [PATCH 44/64] Show wallet scan progress --- .../src/js/components/lib/balance.js | 25 ++++++++++++++++--- .../src/js/components/lib/walletModal.js | 24 ++++++++++-------- pkg/btc-wallet/src/js/components/root.js | 11 ++++---- pkg/btc-wallet/src/js/hooks/useSettings.js | 12 +++++++++ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/balance.js b/pkg/btc-wallet/src/js/components/lib/balance.js index a963a3dac2..55a88adfac 100644 --- a/pkg/btc-wallet/src/js/components/lib/balance.js +++ b/pkg/btc-wallet/src/js/components/lib/balance.js @@ -16,10 +16,12 @@ const Balance = () => { setPsbt, setFee, setError, + scanProgress, } = useSettings(); const [sending, setSending] = useState(false); const [copiedButton, setCopiedButton] = useState(false); const [copiedString, setCopiedString] = useState(false); + const scanning = scanProgress?.main !== null || scanProgress?.change !== null; const copyAddress = (arg) => { navigator.clipboard.writeText(address); @@ -93,10 +95,25 @@ const Balance = () => { > {value} - {`${sats}${unconfirmedString} sats`} + {scanning ? ( + + + + Balance will be updated shortly: + + + + + {scanProgress.main}/24 addresses scanned + + + + ) : ( + {`${sats}${unconfirmedString} sats`} + )} + + + {broadcasting ? : null} + + + )} + + ); +}; + +export default ExternalInvoice; diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js index d49c38e920..f1a6439b05 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/js/components/lib/send.js @@ -19,6 +19,7 @@ import * as ob from 'urbit-ob'; import { useSettings } from '../../hooks/useSettings.js'; import { api } from '../../api'; import { deSig } from '../../lib/util.js'; +import ExternalInvoice from './externalInvoice.js'; const focusFields = { empty: '', @@ -34,6 +35,12 @@ export const feeLevels = { high: 'high', }; +export const signMethods = { + bridge: 'bridge', + masterTicket: 'masterTicket', + external: 'external', +}; + const Send = ({ stopSending, value, conversion }) => { const { error, setError, network, psbt, denomination, shipWallets } = useSettings(); @@ -55,7 +62,7 @@ const Send = ({ stopSending, value, conversion }) => { const [showModal, setShowModal] = useState(false); const [note, setNote] = useState(''); const [choosingSignMethod, setChoosingSignMethod] = useState(false); - const [signMethod, setSignMethod] = useState('bridge'); + const [signMethod, setSignMethod] = useState(signMethods.bridge); const feeDismiss = () => { setShowModal(false); @@ -190,22 +197,40 @@ const Send = ({ stopSending, value, conversion }) => { const signReady = ready && parseInt(satsAmount) > 0 && !signing; let invoice = null; - if (signMethod === 'masterTicket') { - invoice = ( - - ); - } else if (signMethod === 'bridge') { - invoice = ( - - ); + + switch (signMethod) { + case signMethods.masterTicket: { + invoice = ( + + ); + break; + } + case signMethods.bridge: { + invoice = ( + + ); + break; + } + case signMethods.external: { + invoice = ( + + ); + break; + } + default: + break; } return ( @@ -435,7 +460,7 @@ const Send = ({ stopSending, value, conversion }) => { /> - {signMethod === 'masterTicket' && ( + {signMethod === signMethod.masterTicket && ( diff --git a/pkg/btc-wallet/src/js/components/lib/signer.js b/pkg/btc-wallet/src/js/components/lib/signer.js index df371f76f5..098e2da106 100644 --- a/pkg/btc-wallet/src/js/components/lib/signer.js +++ b/pkg/btc-wallet/src/js/components/lib/signer.js @@ -1,5 +1,12 @@ import React from 'react'; import { Box, Button } from '@tlon/indigo-react'; +import { signMethods } from './send'; + +const signMethodLabels = { + bridge: 'Sign with Bridge', + masterTicket: 'Sign with Master Ticket', + external: 'Sign Externally', +}; const Signer = ({ signReady, @@ -10,28 +17,20 @@ const Signer = ({ }) => { return choosingSignMethod ? ( - - + {Object.keys(signMethods).map((method) => ( + + ))} ) : ( ); }; From 237e14399c4ada1b3cc0db8902b642d4675af462 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 31 Jul 2021 11:17:08 -0500 Subject: [PATCH 51/64] Match edouard's designs --- .../src/js/components/lib/externalInvoice.js | 133 +++++++----------- .../src/js/components/lib/feePicker.js | 2 - pkg/btc-wallet/src/js/components/lib/send.js | 67 +++++---- .../src/js/components/lib/signer.js | 43 ++++-- 4 files changed, 125 insertions(+), 120 deletions(-) diff --git a/pkg/btc-wallet/src/js/components/lib/externalInvoice.js b/pkg/btc-wallet/src/js/components/lib/externalInvoice.js index 023dbc0370..866f00a731 100644 --- a/pkg/btc-wallet/src/js/components/lib/externalInvoice.js +++ b/pkg/btc-wallet/src/js/components/lib/externalInvoice.js @@ -23,9 +23,6 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { useSettings(); const [txHex, setTxHex] = useState(''); const [ready, setReady] = useState(false); - const [copiedPsbt, setCopiedPsbt] = useState(false); - const [downloadedButton, setDownloadedButton] = useState(false); - const [copiedButton, setCopiedButton] = useState(false); const [localError, setLocalError] = useState(''); const [broadcasting, setBroadcasting] = useState(false); const invoiceRef = useRef(); @@ -65,16 +62,9 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { const copyPsbt = () => { copyToClipboard(psbt); - setCopiedPsbt(true); - setCopiedButton(true); - setTimeout(() => { - setCopiedPsbt(false); - setCopiedButton(false); - }, 2000); }; const downloadPsbtFile = () => { - setDownloadedButton(true); const blob = new Blob([psbt]); const downloadURL = URL.createObjectURL(blob); const link = document.createElement('a'); @@ -83,9 +73,6 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { document.body.appendChild(link); link.click(); link.parentNode.removeChild(link); - setTimeout(() => { - setDownloadedButton(false); - }, 1000); }; let inputColor = 'black'; @@ -179,80 +166,65 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { - copyPsbt()} - > - {copiedPsbt ? 'copied' : psbt} - - - - - Paste the signed transaction from your external wallet: - - - checkTxHex(e)} - /> - {localError !== '' && ( - - + + + - )} + + + + Signed Tx + + checkTxHex(e)} + /> + {localError !== '' && ( + + + + )} + - - - {!showModal ? null : ( + {!showFeePicker ? null : ( { /> - - + {!(signing && !error) ? null : ( { foreground="orange" /> )} + diff --git a/pkg/btc-wallet/src/js/components/lib/signer.js b/pkg/btc-wallet/src/js/components/lib/signer.js index 098e2da106..06a6dea138 100644 --- a/pkg/btc-wallet/src/js/components/lib/signer.js +++ b/pkg/btc-wallet/src/js/components/lib/signer.js @@ -1,11 +1,11 @@ import React from 'react'; -import { Box, Button } from '@tlon/indigo-react'; +import { Box, Button, Icon, Row } from '@tlon/indigo-react'; import { signMethods } from './send'; const signMethodLabels = { bridge: 'Sign with Bridge', masterTicket: 'Sign with Master Ticket', - external: 'Sign Externally', + external: 'Sign Externally (PSBT)', }; const Signer = ({ @@ -18,18 +18,32 @@ const Signer = ({ return choosingSignMethod ? ( {Object.keys(signMethods).map((method) => ( - + + + {signMethod === signMethods[method] && ( + + )} + ))} ) : ( @@ -38,6 +52,7 @@ const Signer = ({ fontSize={1} fontWeight="bold" borderRadius="24px" + mr={2} height="48px" onClick={initPayment} color={signReady ? 'white' : 'lighterGray'} From 62efce52b8e8f972ce81afe3885f5367e97ae4d8 Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Sat, 31 Jul 2021 16:18:40 -0500 Subject: [PATCH 52/64] Port BTC wallet to Typescript --- pkg/btc-wallet/.eslintignore | 2 + pkg/btc-wallet/config/webpack.dev.js | 66 +++-- pkg/btc-wallet/config/webpack.prod.js | 41 ++-- pkg/btc-wallet/package-lock.json | 228 +++++++++++++++++- pkg/btc-wallet/package.json | 8 +- .../src/{js/components/root.js => App.tsx} | 10 +- .../lib/balance.js => components/Balance.tsx} | 12 +- .../lib/body.js => components/Body.tsx} | 21 +- .../CurrencyPicker.tsx} | 7 +- pkg/btc-wallet/src/components/Error.tsx | 32 +++ .../lib/header.js => components/Header.tsx} | 4 +- .../ProviderModal.tsx} | 26 +- .../Send/BridgeInvoice.tsx} | 35 ++- .../Send/ExternalInvoice.tsx} | 32 ++- .../src/components/Send/FeePicker.tsx | 84 +++++++ .../Send/Invoice.tsx} | 48 ++-- .../lib/send.js => components/Send/Send.tsx} | 152 ++++++------ .../lib/sent.js => components/Send/Sent.tsx} | 10 +- .../signer.js => components/Send/Signer.tsx} | 24 +- .../settings.js => components/Settings.tsx} | 4 +- .../lib/sigil.js => components/Sigil.tsx} | 22 +- .../StartupModal.tsx} | 8 +- .../Transactions/Transaction.tsx} | 29 ++- .../Transactions/Transactions.tsx} | 4 +- .../Transactions/TxAction.tsx} | 8 +- .../Transactions/TxCounterparty.tsx} | 9 +- .../WalletModal.tsx} | 42 +++- .../lib/warning.js => components/Warning.tsx} | 8 +- .../useSettings.js => hooks/useSettings.tsx} | 109 +++++++-- pkg/btc-wallet/src/{index.js => index.tsx} | 14 +- pkg/btc-wallet/src/js/components/lib/error.js | 37 --- .../src/js/components/lib/feePicker.js | 73 ------ .../src/js/components/themes/dark.js | 183 -------------- .../src/js/components/themes/light.js | 184 -------------- pkg/btc-wallet/src/js/subscription.js | 55 ----- pkg/btc-wallet/src/{js/api.js => lib/api.ts} | 45 +++- .../src/{js/lib/util.js => lib/util.ts} | 56 ++--- pkg/btc-wallet/src/themes/dark.js | 183 ++++++++++++++ pkg/btc-wallet/src/themes/light.js | 184 ++++++++++++++ pkg/btc-wallet/src/types.ts | 45 ++++ pkg/btc-wallet/src/urbit-key-generation.d.ts | 1 + pkg/btc-wallet/src/urbit-ob.d.ts | 1 + pkg/btc-wallet/tsconfig.json | 13 + 43 files changed, 1284 insertions(+), 875 deletions(-) create mode 100644 pkg/btc-wallet/.eslintignore rename pkg/btc-wallet/src/{js/components/root.js => App.tsx} (84%) rename pkg/btc-wallet/src/{js/components/lib/balance.js => components/Balance.tsx} (93%) rename pkg/btc-wallet/src/{js/components/lib/body.js => components/Body.tsx} (70%) rename pkg/btc-wallet/src/{js/components/lib/currencyPicker.js => components/CurrencyPicker.tsx} (82%) create mode 100644 pkg/btc-wallet/src/components/Error.tsx rename pkg/btc-wallet/src/{js/components/lib/header.js => components/Header.tsx} (94%) rename pkg/btc-wallet/src/{js/components/lib/providerModal.js => components/ProviderModal.tsx} (91%) rename pkg/btc-wallet/src/{js/components/lib/bridgeInvoice.js => components/Send/BridgeInvoice.tsx} (86%) rename pkg/btc-wallet/src/{js/components/lib/externalInvoice.js => components/Send/ExternalInvoice.tsx} (91%) create mode 100644 pkg/btc-wallet/src/components/Send/FeePicker.tsx rename pkg/btc-wallet/src/{js/components/lib/invoice.js => components/Send/Invoice.tsx} (85%) rename pkg/btc-wallet/src/{js/components/lib/send.js => components/Send/Send.tsx} (81%) rename pkg/btc-wallet/src/{js/components/lib/sent.js => components/Send/Sent.tsx} (82%) rename pkg/btc-wallet/src/{js/components/lib/signer.js => components/Send/Signer.tsx} (72%) rename pkg/btc-wallet/src/{js/components/lib/settings.js => components/Settings.tsx} (96%) rename pkg/btc-wallet/src/{js/components/lib/sigil.js => components/Sigil.tsx} (78%) rename pkg/btc-wallet/src/{js/components/lib/startupModal.js => components/StartupModal.tsx} (81%) rename pkg/btc-wallet/src/{js/components/lib/transaction.js => components/Transactions/Transaction.tsx} (74%) rename pkg/btc-wallet/src/{js/components/lib/transactions.js => components/Transactions/Transactions.tsx} (89%) rename pkg/btc-wallet/src/{js/components/lib/tx-action.js => components/Transactions/TxAction.tsx} (91%) rename pkg/btc-wallet/src/{js/components/lib/tx-counterparty.js => components/Transactions/TxCounterparty.tsx} (83%) rename pkg/btc-wallet/src/{js/components/lib/walletModal.js => components/WalletModal.tsx} (86%) rename pkg/btc-wallet/src/{js/components/lib/warning.js => components/Warning.tsx} (91%) rename pkg/btc-wallet/src/{js/hooks/useSettings.js => hooks/useSettings.tsx} (71%) rename pkg/btc-wallet/src/{index.js => index.tsx} (56%) delete mode 100644 pkg/btc-wallet/src/js/components/lib/error.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/feePicker.js delete mode 100644 pkg/btc-wallet/src/js/components/themes/dark.js delete mode 100644 pkg/btc-wallet/src/js/components/themes/light.js delete mode 100644 pkg/btc-wallet/src/js/subscription.js rename pkg/btc-wallet/src/{js/api.js => lib/api.ts} (51%) rename pkg/btc-wallet/src/{js/lib/util.js => lib/util.ts} (79%) create mode 100644 pkg/btc-wallet/src/themes/dark.js create mode 100644 pkg/btc-wallet/src/themes/light.js create mode 100644 pkg/btc-wallet/src/types.ts create mode 100644 pkg/btc-wallet/src/urbit-key-generation.d.ts create mode 100644 pkg/btc-wallet/src/urbit-ob.d.ts create mode 100644 pkg/btc-wallet/tsconfig.json diff --git a/pkg/btc-wallet/.eslintignore b/pkg/btc-wallet/.eslintignore new file mode 100644 index 0000000000..8dbaf7dc44 --- /dev/null +++ b/pkg/btc-wallet/.eslintignore @@ -0,0 +1,2 @@ +config/webpack.dev.js +config/webpack.prod.js diff --git a/pkg/btc-wallet/config/webpack.dev.js b/pkg/btc-wallet/config/webpack.dev.js index 368de77177..fc0c75792c 100644 --- a/pkg/btc-wallet/config/webpack.dev.js +++ b/pkg/btc-wallet/config/webpack.dev.js @@ -6,9 +6,10 @@ const urbitrc = require('./urbitrc'); const fs = require('fs-extra'); const _ = require('lodash'); -function copy(src,dest) { - return new Promise((res,rej) => - fs.copy(src,dest, err => err ? rej(err) : res())); +function copy(src, dest) { + return new Promise((res, rej) => + fs.copy(src, dest, (err) => (err ? rej(err) : res())) + ); } class UrbitShipPlugin { @@ -35,9 +36,12 @@ let devServer = { historyApiFallback: true, }; -const router = _.mapKeys(urbitrc.FLEET || {}, (value, key) => `${key}.localhost:9000`); +const router = _.mapKeys( + urbitrc.FLEET || {}, + (value, key) => `${key}.localhost:9000` +); -if(urbitrc.URL) { +if (urbitrc.URL) { devServer = { ...devServer, index: '', @@ -45,17 +49,17 @@ if(urbitrc.URL) { '/~btc/js/bundle/index.*.js': { target: 'http://localhost:9000', pathRewrite: (req, path) => { - return '/index.js' - } + return '/index.js'; + }, }, '**': { changeOrigin: true, target: urbitrc.URL, router, // ensure proxy doesn't timeout channels - proxyTimeout: 0 - } - } + proxyTimeout: 0, + }, + }, }; } @@ -63,30 +67,16 @@ module.exports = { node: { fs: 'empty' }, mode: 'development', entry: { - app: './src/index.js' + app: './src/index.tsx', }, module: { rules: [ { test: /\.(j|t)sx?$/, use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', ['@babel/preset-react', { - runtime: 'automatic', - development: 'true', - importSource: '@welldone-software/why-did-you-render', - }]], - plugins: [ - '@babel/transform-runtime', - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-class-properties', - 'react-hot-loader/babel' - ] - } + loader: 'ts-loader', }, - exclude: /node_modules/ + exclude: /node_modules/, }, { test: /\.css$/i, @@ -96,33 +86,31 @@ module.exports = { // Translates CSS into CommonJS 'css-loader', // Compiles Sass to CSS - 'sass-loader' - ] - } - ] + 'sass-loader', + ], + }, + ], }, resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'], }, devtool: 'inline-source-map', devServer: devServer, - plugins: [ - new UrbitShipPlugin(urbitrc) - ], + plugins: [new UrbitShipPlugin(urbitrc)], watch: true, watchOptions: { poll: true, - ignored: '/node_modules/' + ignored: '/node_modules/', }, output: { filename: 'index.js', chunkFilename: 'index.js', path: path.resolve(__dirname, '../dist'), publicPath: '/', - globalObject: 'this' + globalObject: 'this', }, optimization: { minimize: false, - usedExports: true - } + usedExports: true, + }, }; diff --git a/pkg/btc-wallet/config/webpack.prod.js b/pkg/btc-wallet/config/webpack.prod.js index ed7403784e..37017972f2 100644 --- a/pkg/btc-wallet/config/webpack.prod.js +++ b/pkg/btc-wallet/config/webpack.prod.js @@ -6,55 +6,46 @@ module.exports = { node: { fs: 'empty' }, mode: 'production', entry: { - app: './src/index.js' + app: './src/index.tsx', }, module: { rules: [ { - test: /\.jsx?$/, + test: /\.(j|t)sx?$/, use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', '@babel/preset-react'], - plugins: [ - '@babel/transform-runtime', - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-class-properties' - ] - } + loader: 'ts-loader', }, - exclude: /node_modules/ + exclude: /node_modules/, }, { - test: /\.css$/i, + test: /\.css$/i, use: [ // Creates `style` nodes from JS strings 'style-loader', // Translates CSS into CommonJS 'css-loader', // Compiles Sass to CSS - 'sass-loader' - ] - } - ] + 'sass-loader', + ], + }, + ], }, resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'], }, devtool: 'source-map', - plugins: [ - new CleanWebpackPlugin() - ], + plugins: [new CleanWebpackPlugin()], output: { filename: (pathData) => { - return pathData.chunk.name === 'app' ? 'index.[contenthash].js' : '[name].js'; + return pathData.chunk.name === 'app' + ? 'index.[contenthash].js' + : '[name].js'; }, path: path.resolve(__dirname, `../../arvo/app/btc-wallet/js/bundle`), publicPath: '/', }, optimization: { minimize: true, - usedExports: true - } + usedExports: true, + }, }; diff --git a/pkg/btc-wallet/package-lock.json b/pkg/btc-wallet/package-lock.json index bcb054209b..fc22a6ae76 100644 --- a/pkg/btc-wallet/package-lock.json +++ b/pkg/btc-wallet/package-lock.json @@ -1430,6 +1430,22 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", + "integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==", + "dev": true + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", @@ -1441,6 +1457,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" }, + "@types/lodash": { + "version": "4.14.171", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz", + "integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==", + "dev": true + }, "@types/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", @@ -1459,12 +1481,76 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "@types/react": { + "version": "17.0.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz", + "integrity": "sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz", + "integrity": "sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-router": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz", + "integrity": "sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz", + "integrity": "sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "dev": true }, + "@types/styled-components": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.11.tgz", + "integrity": "sha512-u8g3bSw9KUiZY+S++gh+LlURGraqBe3MC5I5dygrNjGDHWWQfsmZZRTJ9K9oHU2CqWtxChWmJkDI/gp+TZPQMw==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "@types/tapable": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz", @@ -1499,6 +1585,12 @@ "source-map": "^0.6.0" } }, + "@types/webpack-env": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.2.tgz", + "integrity": "sha512-vKx7WNQNZDyJveYcHAm9ZxhqSGLYwoyLhrHjLBOkw3a7cT76sTdjgtwyijhk1MaHyRIuSztcVwrUOO/NEu68Dw==", + "dev": true + }, "@types/webpack-sources": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz", @@ -3472,6 +3564,12 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", + "dev": true + }, "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -10240,6 +10338,130 @@ "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-2.1.1.tgz", "integrity": "sha512-74MoNHhwLVuzwaPDcAecFjSkOA9vwWqyOdkeB0Be8Jc/IWSS5SNZKapFllqzkTliqZptkvqX5CZnVeDvfhN8cw==" }, + "ts-loader": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.2.0.tgz", + "integrity": "sha512-ebXBFrNyMSmbWgjnb3WBloUBK+VSx1xckaXsMXxlZRDqce/OPdYBVN5efB0W3V0defq0Gcy4YuzvPGqRgjj85A==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", @@ -10293,9 +10515,9 @@ "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, "typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", "dev": true }, "unbox-primitive": { diff --git a/pkg/btc-wallet/package.json b/pkg/btc-wallet/package.json index 8e4904344c..5481320935 100644 --- a/pkg/btc-wallet/package.json +++ b/pkg/btc-wallet/package.json @@ -20,6 +20,11 @@ "@babel/preset-env": "^7.9.5", "@babel/preset-react": "^7.9.4", "@babel/preset-typescript": "^7.13.0", + "@types/lodash": "^4.14.171", + "@types/react-dom": "^17.0.9", + "@types/react-router-dom": "^5.1.8", + "@types/styled-components": "^5.1.11", + "@types/webpack-env": "^1.16.2", "@welldone-software/why-did-you-render": "^6.1.1", "babel-loader": "^8.1.0", "babel-plugin-root-import": "^6.5.0", @@ -36,7 +41,8 @@ "react-hot-loader": "^4.12.21", "sass": "^1.26.5", "sass-loader": "^8.0.2", - "typescript": "^4.2.3", + "ts-loader": "8.2.0", + "typescript": "^4.3.5", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.10.3" diff --git a/pkg/btc-wallet/src/js/components/root.js b/pkg/btc-wallet/src/App.tsx similarity index 84% rename from pkg/btc-wallet/src/js/components/root.js rename to pkg/btc-wallet/src/App.tsx index 4a6db22ba5..1412f89b4a 100644 --- a/pkg/btc-wallet/src/js/components/root.js +++ b/pkg/btc-wallet/src/App.tsx @@ -3,11 +3,11 @@ import { BrowserRouter } from 'react-router-dom'; import { ThemeProvider } from 'styled-components'; import light from './themes/light'; import { Box, Reset } from '@tlon/indigo-react'; -import StartupModal from './lib/startupModal.js'; -import Body from './lib/body.js'; -import { useSettings } from '../hooks/useSettings.js'; +import StartupModal from './components/StartupModal'; +import { useSettings } from './hooks/useSettings'; +import Body from './components/Body'; -const Root = () => { +const App: React.FC = () => { const { loaded, wallet, provider, scanProgress } = useSettings(); const scanning = scanProgress?.main !== null || scanProgress?.change !== null; const blur = !loaded || scanning ? false : !(wallet && provider); @@ -37,4 +37,4 @@ const Root = () => { ); }; -export default Root; +export default App; diff --git a/pkg/btc-wallet/src/js/components/lib/balance.js b/pkg/btc-wallet/src/components/Balance.tsx similarity index 93% rename from pkg/btc-wallet/src/js/components/lib/balance.js rename to pkg/btc-wallet/src/components/Balance.tsx index d5c0be64a5..d1c568af0e 100644 --- a/pkg/btc-wallet/src/js/components/lib/balance.js +++ b/pkg/btc-wallet/src/components/Balance.tsx @@ -1,10 +1,10 @@ import React, { useState } from 'react'; import { Row, Text, Button, Col } from '@tlon/indigo-react'; -import Send from './send.js'; -import CurrencyPicker from './currencyPicker.js'; -import { copyToClipboard, satsToCurrency } from '../../lib/util.js'; -import { useSettings } from '../../hooks/useSettings.js'; -import { api } from '../../api'; +import Send from './Send/Send'; +import CurrencyPicker from './CurrencyPicker'; +import { copyToClipboard, satsToCurrency } from '../lib/util'; +import { useSettings } from '../hooks/useSettings'; +import { api } from '../lib/api'; const Balance = () => { const { @@ -23,7 +23,7 @@ const Balance = () => { const [copiedString, setCopiedString] = useState(false); const scanning = scanProgress?.main !== null || scanProgress?.change !== null; - const copyAddress = async (arg) => { + const copyAddress = async (arg: 'string' | 'button') => { await copyToClipboard(address); api.btcWalletCommand({ 'gen-new-address': null }); diff --git a/pkg/btc-wallet/src/js/components/lib/body.js b/pkg/btc-wallet/src/components/Body.tsx similarity index 70% rename from pkg/btc-wallet/src/js/components/lib/body.js rename to pkg/btc-wallet/src/components/Body.tsx index 334a622a32..16cda06899 100644 --- a/pkg/btc-wallet/src/js/components/lib/body.js +++ b/pkg/btc-wallet/src/components/Body.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { Box, LoadingSpinner, Col } from '@tlon/indigo-react'; import { Switch, Route } from 'react-router-dom'; -import Balance from './balance.js'; -import Transactions from './transactions.js'; -import Warning from './warning.js'; -import Header from './header.js'; -import Settings from './settings.js'; -import { useSettings } from '../../hooks/useSettings.js'; +import Balance from './Balance'; +import Transactions from './Transactions/Transactions'; +import Warning from './Warning'; +import Header from './Header'; +import Settings from './Settings'; +import { useSettings } from '../hooks/useSettings'; -const Body = () => { +const Body: React.FC = () => { const { loaded, showWarning: warning } = useSettings(); const cardWidth = window.innerWidth <= 475 ? '350px' : '400px'; return !loaded ? ( @@ -19,12 +19,7 @@ const Body = () => { alignItems="center" justifyContent="center" > - + ) : ( diff --git a/pkg/btc-wallet/src/js/components/lib/currencyPicker.js b/pkg/btc-wallet/src/components/CurrencyPicker.tsx similarity index 82% rename from pkg/btc-wallet/src/js/components/lib/currencyPicker.js rename to pkg/btc-wallet/src/components/CurrencyPicker.tsx index 30dc627560..56526847a6 100644 --- a/pkg/btc-wallet/src/js/components/lib/currencyPicker.js +++ b/pkg/btc-wallet/src/components/CurrencyPicker.tsx @@ -1,19 +1,20 @@ import React from 'react'; import { Icon, Row, Text } from '@tlon/indigo-react'; -import { api } from '../../api'; -import { useSettings } from '../../hooks/useSettings'; +import { api } from '../lib/api'; +import { useSettings } from '../hooks/useSettings'; const CurrencyPicker = () => { const { denomination, currencyRates } = useSettings(); const switchCurrency = () => { let newCurrency; if (denomination === 'BTC') { - if (currencyRates['USD']) { + if ((currencyRates as any)['USD']) { newCurrency = 'USD'; } } else if (denomination === 'USD') { newCurrency = 'BTC'; } + console.log({ newCurrency, denomination }); let setCurrency = { 'put-entry': { value: newCurrency, diff --git a/pkg/btc-wallet/src/components/Error.tsx b/pkg/btc-wallet/src/components/Error.tsx new file mode 100644 index 0000000000..cf41266df6 --- /dev/null +++ b/pkg/btc-wallet/src/components/Error.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Text } from '@tlon/indigo-react'; + +enum ErrorTypes { + 'cant-pay-ourselves' = 'Cannot pay ourselves', + 'no-comets' = 'Cannot pay comets', + 'no-dust' = 'Cannot send dust', + 'tx-being-signed' = 'Cannot pay when transaction is being signed', + 'insufficient-balance' = 'Insufficient confirmed balance', + 'broadcast-fail' = 'Transaction broadcast failed', + 'invalid-master-ticker' = 'Invalid master ticket', + 'invalid-signed' = 'Invalid signed bitcoin transaction', +} + +const Error = ({ + error, + fontSize, + ...rest +}: { + error: string; + fontSize?: string; +}) => ( + + { + (ErrorTypes as any)[ + Object.keys(ErrorTypes).filter((et) => et === error)[0] + ] + } + +); + +export default Error; diff --git a/pkg/btc-wallet/src/js/components/lib/header.js b/pkg/btc-wallet/src/components/Header.tsx similarity index 94% rename from pkg/btc-wallet/src/js/components/lib/header.js rename to pkg/btc-wallet/src/components/Header.tsx index 88f4687fff..0c43a09cd9 100644 --- a/pkg/btc-wallet/src/js/components/lib/header.js +++ b/pkg/btc-wallet/src/components/Header.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Box, Icon, Row, Text } from '@tlon/indigo-react'; import { Link } from 'react-router-dom'; -import { useSettings } from '../../hooks/useSettings'; +import { useSettings } from '../hooks/useSettings'; -const Header = ({ settings }) => { +const Header = ({ settings }: { settings: boolean }) => { const { provider } = useSettings(); let icon = settings ? 'X' : 'Adjust'; let iconColor = settings ? 'black' : 'orange'; diff --git a/pkg/btc-wallet/src/js/components/lib/providerModal.js b/pkg/btc-wallet/src/components/ProviderModal.tsx similarity index 91% rename from pkg/btc-wallet/src/js/components/lib/providerModal.js rename to pkg/btc-wallet/src/components/ProviderModal.tsx index ddb7dc58dd..7ce440ce22 100644 --- a/pkg/btc-wallet/src/js/components/lib/providerModal.js +++ b/pkg/btc-wallet/src/components/ProviderModal.tsx @@ -9,15 +9,15 @@ import { LoadingSpinner, } from '@tlon/indigo-react'; import { isValidPatp } from 'urbit-ob'; -import { api } from '../../api'; -import { useSettings } from '../../hooks/useSettings'; +import { api } from '../lib/api'; +import { useSettings } from '../hooks/useSettings'; -const providerStatuses = { - checking: 'checking', - failed: 'failed', - ready: 'ready', - initial: '', -}; +enum providerStatuses { + checking, + failed, + ready, + initial = '', +} const ProviderModal = () => { const { providerPerms } = useSettings(); @@ -28,7 +28,7 @@ const ProviderModal = () => { const [provider, setProvider] = useState(null); const [connecting, setConnecting] = useState(false); - const checkProvider = (e) => { + const checkProvider = (e: React.ChangeEvent) => { // TODO: loading states setProviderStatus(providerStatuses.initial); let givenProvider = e.target.value; @@ -103,7 +103,7 @@ const ProviderModal = () => { to set a provider node. A provider node is an urbit which maintains a synced Bitcoin ledger. { backgroundColor={workingBg} color={workingColor} borderColor={workingColor} - onChange={(e) => checkProvider(e)} + onChange={(e: React.ChangeEvent) => + checkProvider(e) + } /> {providerStatus === providerStatuses.checking ? ( @@ -150,7 +152,7 @@ const ProviderModal = () => { providerStatus === providerStatuses.ready ? 'pointer' : 'default', }} onClick={() => { - submitProvider(provider); + submitProvider(); }} > Set Peer Node diff --git a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js b/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx similarity index 86% rename from pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js rename to pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx index 19b3475276..e7d0d32660 100644 --- a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js +++ b/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx @@ -9,16 +9,22 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; -import { Sigil } from './sigil.js'; +import Sigil from '../Sigil'; import * as bitcoin from 'bitcoinjs-lib'; import { isValidPatp } from 'urbit-ob'; -import Sent from './sent.js'; -import Error from './error.js'; -import { satsToCurrency } from '../../lib/util.js'; -import { useSettings } from '../../hooks/useSettings.js'; -import { api } from '../../api'; +import Sent from './Sent'; +import Error from '../Error'; +import { satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; -const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { +type Props = { + payee: string; + stopSending: () => void; + satsAmount: number; +}; + +const BridgeInvoice: React.FC = ({ payee, stopSending, satsAmount }) => { const { error, currencyRates, fee, broadcastSuccess, denomination, psbt } = useSettings(); const [txHex, setTxHex] = useState(''); @@ -40,14 +46,14 @@ const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { window.open('https://bridge.urbit.org/?kind=btc&utx=' + psbt); }); - const broadCastTx = (hex) => { + const broadCastTx = (hex: string) => { let command = { 'broadcast-tx': hex, }; return api.btcWalletCommand(command); }; - const sendBitcoin = (hex) => { + const sendBitcoin = (hex: string) => { try { bitcoin.Transaction.fromHex(hex); broadCastTx(hex); @@ -58,7 +64,7 @@ const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { } }; - const checkTxHex = (e) => { + const checkTxHex = (e: React.ChangeEvent) => { setTxHex(e.target.value); setReady(txHex.length > 0); setLocalError(''); @@ -169,11 +175,11 @@ const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { backgroundColor={inputBg} borderColor={inputBorder} style={{ lineHeight: '4' }} - onChange={(e) => checkTxHex(e)} + onChange={(e: React.ChangeEvent) => checkTxHex(e)} /> {localError !== '' && ( - + )} @@ -200,7 +206,10 @@ const BridgeInvoice = ({ payee, stopSending, satsAmount }) => { > Send BTC - {broadcasting ? : null} + { + // @ts-ignore + broadcasting ? : null + } )} diff --git a/pkg/btc-wallet/src/js/components/lib/externalInvoice.js b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx similarity index 91% rename from pkg/btc-wallet/src/js/components/lib/externalInvoice.js rename to pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx index 866f00a731..0ac036b1b5 100644 --- a/pkg/btc-wallet/src/js/components/lib/externalInvoice.js +++ b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx @@ -9,16 +9,26 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; -import { Sigil } from './sigil.js'; +import Sigil from '../Sigil'; import * as bitcoin from 'bitcoinjs-lib'; import { isValidPatp } from 'urbit-ob'; import Sent from './sent.js'; -import Error from './error.js'; +import Error from '../Error'; import { copyToClipboard, satsToCurrency } from '../../lib/util.js'; import { useSettings } from '../../hooks/useSettings.js'; -import { api } from '../../api'; +import { api } from '../../lib/api'; -const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { +type Props = { + payee: string; + stopSending: () => void; + satsAmount: number; +}; + +const ExternalInvoice: React.FC = ({ + payee, + stopSending, + satsAmount, +}) => { const { error, currencyRates, fee, broadcastSuccess, denomination, psbt } = useSettings(); const [txHex, setTxHex] = useState(''); @@ -36,14 +46,14 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { } }, [error, broadcasting, setBroadcasting]); - const broadCastTx = (hex) => { + const broadCastTx = (hex: string) => { let command = { 'broadcast-tx': hex, }; return api.btcWalletCommand(command); }; - const sendBitcoin = (hex) => { + const sendBitcoin = (hex: string) => { try { bitcoin.Transaction.fromHex(hex); broadCastTx(hex); @@ -54,7 +64,7 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { } }; - const checkTxHex = (e) => { + const checkTxHex = (e: React.ChangeEvent) => { setTxHex(e.target.value); setReady(txHex.length > 0); setLocalError(''); @@ -211,11 +221,13 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { backgroundColor={inputBg} borderColor={inputBorder} style={{ lineHeight: '4' }} - onChange={(e) => checkTxHex(e)} + onChange={(e: React.ChangeEvent) => + checkTxHex(e) + } /> {localError !== '' && ( - + )} @@ -249,7 +261,7 @@ const ExternalInvoice = ({ payee, stopSending, satsAmount }) => { > Send BTC - {broadcasting ? : null} + {broadcasting ? : null} )} diff --git a/pkg/btc-wallet/src/components/Send/FeePicker.tsx b/pkg/btc-wallet/src/components/Send/FeePicker.tsx new file mode 100644 index 0000000000..1dc48158ce --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/FeePicker.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { + Box, + Text, + Col, + StatelessRadioButtonField as RadioButton, + Label, +} from '@tlon/indigo-react'; +import { FeeChoices, feeLevels } from './Send'; + +type Props = { + feeChoices: FeeChoices; + feeValue: number; + setFeeValue: React.Dispatch; + feeDismiss: () => void; +}; + +const FeePicker: React.FC = ({ + feeChoices, + feeValue, + setFeeValue, + feeDismiss, +}) => ( + + + Transaction Speed + + + { + setFeeValue(feeLevels.low); + feeDismiss(); + }} + > + + + + { + setFeeValue(feeLevels.mid); + feeDismiss(); + }} + > + + + + { + setFeeValue(feeLevels.high); + feeDismiss(); + }} + > + + + + +); + +export default FeePicker; diff --git a/pkg/btc-wallet/src/js/components/lib/invoice.js b/pkg/btc-wallet/src/components/Send/Invoice.tsx similarity index 85% rename from pkg/btc-wallet/src/js/components/lib/invoice.js rename to pkg/btc-wallet/src/components/Send/Invoice.tsx index 9b843d617e..cfccbf8664 100644 --- a/pkg/btc-wallet/src/js/components/lib/invoice.js +++ b/pkg/btc-wallet/src/components/Send/Invoice.tsx @@ -9,15 +9,16 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; -import { Sigil } from './sigil.js'; -import * as bitcoin from 'bitcoinjs-lib'; import * as kg from 'urbit-key-generation'; -import Sent from './sent.js'; import { patp2dec, isValidPatq, isValidPatp } from 'urbit-ob'; -import { satsToCurrency } from '../../lib/util.js'; -import Error from './error.js'; -import { useSettings } from '../../hooks/useSettings.js'; -import { api } from '../../api'; +import * as bitcoin from 'bitcoinjs-lib'; +import Sigil from '../Sigil'; +import Sent from './Sent'; +import { satsToCurrency } from '../../lib/util'; +import Error from '../Error'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; +import { UrbitWallet } from '../../types'; const BITCOIN_MAINNET_INFO = { messagePrefix: '\x18Bitcoin Signed Message:\n', @@ -43,7 +44,13 @@ const BITCOIN_TESTNET_INFO = { wif: 0xef, }; -const Invoice = ({ stopSending, payee, satsAmount }) => { +type Props = { + stopSending: () => void; + payee: string; + satsAmount: number; +}; + +const Invoice: React.FC = ({ stopSending, payee, satsAmount }) => { const { error, currencyRates, @@ -64,20 +71,20 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { } }, [error, broadcasting, setBroadcasting]); - const broadCastTx = (psbtHex) => { + const broadCastTx = (psbtHex: string) => { let command = { 'broadcast-tx': psbtHex, }; return api.btcWalletCommand(command); }; - const sendBitcoin = (ticket, psbt) => { + const sendBitcoin = (ticket: string, psbt: string) => { const newPsbt = bitcoin.Psbt.fromBase64(psbt); setBroadcasting(true); kg.generateWallet({ ticket, - ship: parseInt(patp2dec('~' + window.ship)), - }).then((urbitWallet) => { + ship: parseInt(patp2dec('~' + (window as any).ship)), + }).then((urbitWallet: UrbitWallet) => { // this wasn't being used, not clear why it was pulled out. // const { xpub } = // network === 'testnet' @@ -116,7 +123,9 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { }); }; - const checkTicket = ({ target: { value } }) => { + const checkTicket = ({ + target: { value }, + }: React.ChangeEvent) => { // TODO: port over bridge ticket validation logic setMasterTicket(value); setReady(isValidPatq(value)); @@ -216,19 +225,21 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { fontSize="14px" type="password" name="masterTicket" - obscure={(value) => value.replace(/[^~-]+/g, '••••••')} + obscure={(value: string) => value.replace(/[^~-]+/g, '••••••')} placeholder="••••••-••••••-••••••-••••••" autoCapitalize="none" autoCorrect="off" color={inputColor} backgroundColor={inputBg} borderColor={inputBorder} - onChange={(e) => checkTicket(e)} + onChange={(e: React.ChangeEvent) => + checkTicket(e) + } /> {error !== '' && ( - + )} @@ -252,7 +263,10 @@ const Invoice = ({ stopSending, payee, satsAmount }) => { > Send BTC - {broadcasting ? : null} + { + // @ts-ignore + broadcasting ? : null + } )} diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/components/Send/Send.tsx similarity index 81% rename from pkg/btc-wallet/src/js/components/lib/send.js rename to pkg/btc-wallet/src/components/Send/Send.tsx index d7dea1efb9..1567c5c466 100644 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ b/pkg/btc-wallet/src/components/Send/Send.tsx @@ -9,81 +9,99 @@ import { Col, LoadingSpinner, } from '@tlon/indigo-react'; -import Invoice from './invoice.js'; -import BridgeInvoice from './bridgeInvoice.js'; -import FeePicker from './feePicker.js'; -import Error from './error.js'; -import Signer from './signer.js'; +import Invoice from './Invoice'; +import BridgeInvoice from './BridgeInvoice'; +import ExternalInvoice from './ExternalInvoice'; +import FeePicker from './FeePicker'; +import Error from '../Error'; +import Signer from './Signer'; import { validate } from 'bitcoin-address-validation'; import * as ob from 'urbit-ob'; -import { useSettings } from '../../hooks/useSettings.js'; -import { api } from '../../api'; -import { deSig } from '../../lib/util.js'; -import ExternalInvoice from './externalInvoice.js'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; +import { deSig } from '../../lib/util'; -const focusFields = { - empty: '', - payee: 'payee', - currency: 'currency', - sats: 'sats', - note: 'note', +enum focusFields { + payee, + currency, + sats, + note, + empty = '', +} + +export enum feeLevels { + low, + mid, + high, +} + +export enum signMethods { + bridge, + masterTicket, + external, +} + +enum payeeTypes { + ship, + address, + initial = '', +} + +export type FeeChoices = { + [feeLevels.low]: [number, number]; + [feeLevels.mid]: [number, number]; + [feeLevels.high]: [number, number]; }; -export const feeLevels = { - low: 'low', - mid: 'mid', - high: 'high', +type Props = { + stopSending: () => void; + value: string; + conversion: number; }; -export const signMethods = { - bridge: 'bridge', - masterTicket: 'masterTicket', - external: 'external', -}; - -const Send = ({ stopSending, value, conversion }) => { +const Send: React.FC = ({ stopSending, value, conversion }) => { const { error, setError, network, psbt, denomination, shipWallets } = useSettings(); const [signing, setSigning] = useState(false); - const [denomAmount, setDenomAmount] = useState('0.00'); - const [satsAmount, setSatsAmount] = useState('0'); + const [denomAmount, setDenomAmount] = useState(0.0); + const [satsAmount, setSatsAmount] = useState(0); const [payee, setPayee] = useState(''); const [checkingPatp, setCheckingPatp] = useState(false); - const [payeeType, setPayeeType] = useState(''); + const [payeeType, setPayeeType] = useState(payeeTypes.initial); const [ready, setReady] = useState(false); const [validPayee, setValidPayee] = useState(false); const [focusedField, setFocusedField] = useState(focusFields.empty); - const [feeChoices, setFeeChoices] = useState({ - low: [10, 1], - mid: [10, 1], - high: [10, 1], + const [feeChoices, setFeeChoices] = useState({ + [feeLevels.low]: [10, 1], + [feeLevels.mid]: [10, 1], + [feeLevels.high]: [10, 1], }); const [feeValue, setFeeValue] = useState(feeLevels.mid); const [showFeePicker, setShowFeePicker] = useState(false); const [note, setNote] = useState(''); const [choosingSignMethod, setChoosingSignMethod] = useState(false); - const [signMethod, setSignMethod] = useState(signMethods.bridge); + const [signMethod, setSignMethod] = useState(signMethods.bridge); const feeDismiss = () => { setShowFeePicker(false); }; - const handleSetSignMethod = (signMethod) => { + const handleSetSignMethod = (signMethod: signMethods) => { setSignMethod(signMethod); setChoosingSignMethod(false); }; - const checkPayee = (e) => { + const checkPayee = (e: React.ChangeEvent) => { setError(''); - const validPatPCommand = (validPatP) => { + const validPatPCommand = (validPatP: string) => { let command = { 'check-payee': validPatP }; api.btcWalletCommand(command); setTimeout(() => { setCheckingPatp(false); }, 5000); setCheckingPatp(true); - setPayeeType('ship'); + setPayeeType(payeeTypes.ship); setPayee(validPatP); }; @@ -96,13 +114,13 @@ const Send = ({ stopSending, value, conversion }) => { setPayee(payeeReceived); setReady(true); setCheckingPatp(false); - setPayeeType('address'); + setPayeeType(payeeTypes.address); setValidPayee(true); } else { setPayee(payeeReceived); setReady(false); setCheckingPatp(false); - setPayeeType(''); + setPayeeType(payeeTypes.initial); setValidPayee(false); } }; @@ -112,22 +130,22 @@ const Send = ({ stopSending, value, conversion }) => { }; const initPayment = () => { - if (payeeType === 'ship') { + if (payeeType === payeeTypes.ship) { let command = { 'init-payment': { payee, - value: parseInt(satsAmount), + value: satsAmount, feyb: feeChoices[feeValue][1], note: note || null, }, }; api.btcWalletCommand(command).then(() => setSigning(true)); - } else if (payeeType === 'address') { + } else if (payeeType === payeeTypes.address) { let command = { 'init-payment-external': { address: payee, - value: parseInt(satsAmount), + value: satsAmount, feyb: 1, note: note || null, }, @@ -146,9 +164,9 @@ const Send = ({ stopSending, value, conversion }) => { // let mid = Math.floor(estimates.length / 2); // let high = estimates.length - 1; setFeeChoices({ - high: [30, n.estimates[30]['sat_per_vbyte']], - mid: [180, n.estimates[180]['sat_per_vbyte']], - low: [360, n.estimates[360]['sat_per_vbyte']], + [feeLevels.high]: [30, n.estimates[30]['sat_per_vbyte']], + [feeLevels.mid]: [180, n.estimates[180]['sat_per_vbyte']], + [feeLevels.low]: [360, n.estimates[360]['sat_per_vbyte']], }); }); } @@ -175,7 +193,7 @@ const Send = ({ stopSending, value, conversion }) => { payeeColor = 'green'; payeeBorder = 'green'; payeeBg = 'veryLightGreen'; - } else if (!focusedField === focusFields.payee && validPayee) { + } else if (focusedField !== focusFields.payee && validPayee) { payeeColor = 'blue'; payeeBorder = 'white'; payeeBg = 'white'; @@ -184,17 +202,17 @@ const Send = ({ stopSending, value, conversion }) => { payeeBorder = 'red'; payeeBg = 'veryLightRed'; } else if ( - focusedField === 'payee' && + focusedField === focusFields.payee && !validPayee && !checkingPatp && - payeeType === 'ship' + payeeType === payeeTypes.ship ) { payeeColor = 'red'; payeeBorder = 'red'; payeeBg = 'veryLightRed'; } - const signReady = ready && parseInt(satsAmount) > 0 && !signing; + const signReady = ready && satsAmount > 0 && !signing; let invoice = null; @@ -286,20 +304,16 @@ const Send = ({ stopSending, value, conversion }) => { value={payee} fontFamily="mono" disabled={signing} - onChange={(e) => checkPayee(e)} + onChange={(e: React.ChangeEvent) => + checkPayee(e) + } /> {error && ( {/* yes this is a hack */} - + )} @@ -321,8 +335,8 @@ const Send = ({ stopSending, value, conversion }) => { } disabled={signing} value={denomAmount} - onChange={(e) => { - setDenomAmount(e.target.value); + onChange={(e: React.ChangeEvent) => { + setDenomAmount(parseFloat(e.target.value)); setSatsAmount( Math.round( (parseFloat(e.target.value) / conversion) * 100000000 @@ -352,11 +366,11 @@ const Send = ({ stopSending, value, conversion }) => { } disabled={signing} value={satsAmount} - onChange={(e) => { + onChange={(e: React.ChangeEvent) => { setDenomAmount( parseFloat(e.target.value) * (conversion / 100000000) ); - setSatsAmount(e.target.value); + setSatsAmount(parseInt(e.target.value, 10)); }} /> @@ -431,7 +445,7 @@ const Send = ({ stopSending, value, conversion }) => { } disabled={signing} value={note} - onChange={(e) => { + onChange={(e: React.ChangeEvent) => { setNote(e.target.value); }} /> @@ -444,11 +458,7 @@ const Send = ({ stopSending, value, conversion }) => { justifyContent="flex-end" > {!(signing && !error) ? null : ( - + )} { fontWeight="bold" borderRadius="24px" height="48px" - onClick={() => toggleSignMethod(choosingSignMethod)} + onClick={() => toggleSignMethod()} color={signReady ? 'white' : 'lighterGray'} backgroundColor={ signReady ? 'rgba(33, 157, 255, 0.2)' : 'veryLightGray' @@ -479,7 +489,7 @@ const Send = ({ stopSending, value, conversion }) => { /> - {signMethod === signMethod.masterTicket && ( + {signMethod === signMethods.masterTicket && ( diff --git a/pkg/btc-wallet/src/js/components/lib/sent.js b/pkg/btc-wallet/src/components/Send/Sent.tsx similarity index 82% rename from pkg/btc-wallet/src/js/components/lib/sent.js rename to pkg/btc-wallet/src/components/Send/Sent.tsx index fe570f0fb3..1990d4b285 100644 --- a/pkg/btc-wallet/src/js/components/lib/sent.js +++ b/pkg/btc-wallet/src/components/Send/Sent.tsx @@ -1,9 +1,15 @@ import React from 'react'; import { Icon, Row, Col, Center, Text } from '@tlon/indigo-react'; -import { satsToCurrency } from '../../lib/util.js'; +import { satsToCurrency } from '../../lib/util'; import { useSettings } from '../../hooks/useSettings'; -const Sent = ({ payee, stopSending, satsAmount }) => { +type Props = { + payee: string; + stopSending: () => void; + satsAmount: number; +}; + +const Sent: React.FC = ({ payee, stopSending, satsAmount }) => { const { denomination, currencyRates } = useSettings(); return ( void; + choosingSignMethod: boolean; + signMethod: signMethods; + setSignMethod: (arg: signMethods) => void; +}; + +const Signer: React.FC = ({ signReady, initPayment, choosingSignMethod, @@ -24,13 +32,15 @@ const Signer = ({ backgroundColor="transparent" fontWeight="bold" cursor="pointer" - color={signMethod === signMethods[method] ? 'blue' : 'lightBlue'} + color={ + signMethod === (signMethods as any)[method] ? 'blue' : 'lightBlue' + } height="48px" - onClick={() => setSignMethod(signMethods[method])} + onClick={() => setSignMethod((signMethods as any)[method])} > - {signMethodLabels[method]} + {(signMethodLabels as any)[method]} - {signMethod === signMethods[method] && ( + {signMethod === (signMethods as any)[method] && ( ); }; diff --git a/pkg/btc-wallet/src/js/components/lib/settings.js b/pkg/btc-wallet/src/components/Settings.tsx similarity index 96% rename from pkg/btc-wallet/src/js/components/lib/settings.js rename to pkg/btc-wallet/src/components/Settings.tsx index 352cb5500f..61ceed9278 100644 --- a/pkg/btc-wallet/src/js/components/lib/settings.js +++ b/pkg/btc-wallet/src/components/Settings.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Row, Text, Button, Col } from '@tlon/indigo-react'; -import { useSettings } from '../../hooks/useSettings'; -import { api } from '../../api'; +import { useSettings } from '../hooks/useSettings'; +import { api } from '../lib/api'; const Settings = () => { const { wallet, provider } = useSettings(); diff --git a/pkg/btc-wallet/src/js/components/lib/sigil.js b/pkg/btc-wallet/src/components/Sigil.tsx similarity index 78% rename from pkg/btc-wallet/src/js/components/lib/sigil.js rename to pkg/btc-wallet/src/components/Sigil.tsx index e47eff93f1..5de8569bdf 100644 --- a/pkg/btc-wallet/src/js/components/lib/sigil.js +++ b/pkg/btc-wallet/src/components/Sigil.tsx @@ -2,11 +2,11 @@ import React, { memo } from 'react'; import { sigil, reactRenderer } from '@tlon/sigil-js'; import { Box } from '@tlon/indigo-react'; -export const foregroundFromBackground = (background) => { +export const foregroundFromBackground = (background: string) => { const rgb = { r: parseInt(background.slice(1, 3), 16), g: parseInt(background.slice(3, 5), 16), - b: parseInt(background.slice(5, 7), 16) + b: parseInt(background.slice(5, 7), 16), }; const brightness = (299 * rgb.r + 587 * rgb.g + 114 * rgb.b) / 1000; const whiteBrightness = 255; @@ -14,7 +14,19 @@ export const foregroundFromBackground = (background) => { return whiteBrightness - brightness < 50 ? 'black' : 'white'; }; -export const Sigil = memo( +type Props = { + classes?: string; + color: string; + foreground?: string; + ship: string; + size: number; + svgClass?: string; + icon?: boolean; + padding?: number; + display?: string; +}; + +const Sigil: React.FC = memo( ({ classes = '', color, @@ -24,7 +36,7 @@ export const Sigil = memo( svgClass = '', icon = false, padding = 0, - display = 'inline-block' + display = 'inline-block', }) => { const innerSize = Number(size) - 2 * padding; const paddingPx = `${padding}px`; @@ -55,7 +67,7 @@ export const Sigil = memo( size: innerSize, icon, colors: [color, foregroundColor], - class: svgClass + class: svgClass, })} ); diff --git a/pkg/btc-wallet/src/js/components/lib/startupModal.js b/pkg/btc-wallet/src/components/StartupModal.tsx similarity index 81% rename from pkg/btc-wallet/src/js/components/lib/startupModal.js rename to pkg/btc-wallet/src/components/StartupModal.tsx index c4ee50c0a7..e21f6ce2cc 100644 --- a/pkg/btc-wallet/src/js/components/lib/startupModal.js +++ b/pkg/btc-wallet/src/components/StartupModal.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { Box } from '@tlon/indigo-react'; -import WalletModal from './walletModal.js'; -import ProviderModal from './providerModal.js'; -import { useSettings } from '../../hooks/useSettings.js'; +import WalletModal from './WalletModal'; +import ProviderModal from './ProviderModal'; +import { useSettings } from '../hooks/useSettings'; -const StartupModal = () => { +const StartupModal: React.FC = () => { const { wallet, provider } = useSettings(); let modal = null; diff --git a/pkg/btc-wallet/src/js/components/lib/transaction.js b/pkg/btc-wallet/src/components/Transactions/Transaction.tsx similarity index 74% rename from pkg/btc-wallet/src/js/components/lib/transaction.js rename to pkg/btc-wallet/src/components/Transactions/Transaction.tsx index 10b9a50951..8c6c505d7b 100644 --- a/pkg/btc-wallet/src/js/components/lib/transaction.js +++ b/pkg/btc-wallet/src/components/Transactions/Transaction.tsx @@ -1,23 +1,28 @@ import React from 'react'; import { Box, Row, Text, Col } from '@tlon/indigo-react'; import _ from 'lodash'; -import TxAction from './tx-action.js'; -import TxCounterparty from './tx-counterparty.js'; -import { satsToCurrency } from '../../lib/util.js'; -import { useSettings } from '../../hooks/useSettings.js'; +import TxAction from './TxAction'; +import TxCounterparty from './TxCounterparty'; +import { satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; +import { Transaction as TransactionType } from '../../types'; -const Transaction = ({ tx }) => { +const Transaction = ({ tx }: { tx: TransactionType }) => { const { denomination, currencyRates } = useSettings(); const pending = !tx.recvd; let weSent = _.find(tx.inputs, (input) => { - return input.ship === window.ship; + return input.ship === (window as any).ship; }); let weRecv = tx.outputs.every((output) => { - return output.ship === window.ship; + return output.ship === (window as any).ship; }); - let action = weRecv ? 'recv' : weSent ? 'sent' : 'recv'; + let action: 'sent' | 'recv' | 'fail' = weRecv + ? 'recv' + : weSent + ? 'sent' + : 'recv'; let counterShip = null; let counterAddress = null; @@ -26,7 +31,7 @@ const Transaction = ({ tx }) => { if (action === 'sent') { let counter = _.find(tx.outputs, (output) => { - return output.ship !== window.ship; + return output.ship !== (window as any).ship; }); counterShip = _.get(counter, 'ship', null); counterAddress = _.get(counter, 'val.address', null); @@ -36,7 +41,7 @@ const Transaction = ({ tx }) => { value = _.reduce( tx.outputs, (sum, output) => { - if (output.ship === window.ship) { + if (output.ship === (window as any).ship) { return sum + output.val.value; } else { return sum; @@ -48,14 +53,14 @@ const Transaction = ({ tx }) => { if (weSent && weRecv) { counterAddress = _.get( _.find(tx.inputs, (input) => { - return input.ship === window.ship; + return input.ship === (window as any).ship; }), 'val.address', null ); } else { let counter = _.find(tx.inputs, (input) => { - return input.ship !== window.ship; + return input.ship !== (window as any).ship; }); counterShip = _.get(counter, 'ship', null); counterAddress = _.get(counter, 'val.address', null); diff --git a/pkg/btc-wallet/src/js/components/lib/transactions.js b/pkg/btc-wallet/src/components/Transactions/Transactions.tsx similarity index 89% rename from pkg/btc-wallet/src/js/components/lib/transactions.js rename to pkg/btc-wallet/src/components/Transactions/Transactions.tsx index c968320bcd..0eec82fb96 100644 --- a/pkg/btc-wallet/src/js/components/lib/transactions.js +++ b/pkg/btc-wallet/src/components/Transactions/Transactions.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Box, Text, Col } from '@tlon/indigo-react'; -import Transaction from './transaction.js'; -import { useSettings } from '../../hooks/useSettings.js'; +import Transaction from './Transaction'; +import { useSettings } from '../../hooks/useSettings'; const Transactions = () => { const { history } = useSettings(); diff --git a/pkg/btc-wallet/src/js/components/lib/tx-action.js b/pkg/btc-wallet/src/components/Transactions/TxAction.tsx similarity index 91% rename from pkg/btc-wallet/src/js/components/lib/tx-action.js rename to pkg/btc-wallet/src/components/Transactions/TxAction.tsx index 12405f4934..6ed9e954d4 100644 --- a/pkg/btc-wallet/src/js/components/lib/tx-action.js +++ b/pkg/btc-wallet/src/components/Transactions/TxAction.tsx @@ -2,7 +2,13 @@ import React from 'react'; import { Box, Icon, Row, Text, LoadingSpinner } from '@tlon/indigo-react'; import { useSettings } from '../../hooks/useSettings'; -const TxAction = ({ action, pending, txid }) => { +type Props = { + action: 'sent' | 'recv' | 'fail'; + pending: boolean; + txid: string; +}; + +const TxAction: React.FC = ({ action, pending, txid }) => { const { network } = useSettings(); const leftIcon = action === 'sent' diff --git a/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js b/pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx similarity index 83% rename from pkg/btc-wallet/src/js/components/lib/tx-counterparty.js rename to pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx index 76cf2fd286..8bbdad07dc 100644 --- a/pkg/btc-wallet/src/js/components/lib/tx-counterparty.js +++ b/pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx @@ -1,8 +1,13 @@ import React from 'react'; import { Box, Icon, Row, Text } from '@tlon/indigo-react'; -import { Sigil } from './sigil.js'; +import Sigil from '../Sigil'; -const TxCounterparty = ({ ship, address }) => { +type Props = { + ship: string; + address: string; +}; + +const TxCounterparty: React.FC = ({ ship, address }) => { const icon = ship ? ( ) : ( diff --git a/pkg/btc-wallet/src/js/components/lib/walletModal.js b/pkg/btc-wallet/src/components/WalletModal.tsx similarity index 86% rename from pkg/btc-wallet/src/js/components/lib/walletModal.js rename to pkg/btc-wallet/src/components/WalletModal.tsx index 38105c2482..cbdaf687a7 100644 --- a/pkg/btc-wallet/src/js/components/lib/walletModal.js +++ b/pkg/btc-wallet/src/components/WalletModal.tsx @@ -10,10 +10,11 @@ import { } from '@tlon/indigo-react'; import { patp2dec, isValidPatq } from 'urbit-ob'; import * as kg from 'urbit-key-generation'; -import { useSettings } from '../../hooks/useSettings'; -import { api } from '../../api'; +import { useSettings } from '../hooks/useSettings'; +import { api } from '../lib/api'; +import { UrbitWallet } from '../types'; -const WalletModal = () => { +const WalletModal: React.FC = () => { const { network } = useSettings(); const [mode, setMode] = useState('xpub'); const [masterTicket, setMasterTicket] = useState(''); @@ -24,7 +25,9 @@ const WalletModal = () => { const [confirmingMasterTicket, setConfirmingMasterTicket] = useState(false); const [error, setError] = useState(false); - const checkTicket = ({ target: { value } }) => { + const checkTicket = ({ + target: { value }, + }: React.ChangeEvent) => { // TODO: port over bridge ticket validation logic if (confirmingMasterTicket) { setConfirmedMasterTicket(value); @@ -35,13 +38,24 @@ const WalletModal = () => { } }; - const checkXPub = ({ target: { value: xpubGiven } }) => { + const checkXPub = ({ + target: { value: xpubGiven }, + }: React.ChangeEvent) => { setXpub(xpubGiven); setReadyToSubmit(xpubGiven.length > 0); }; - const submitXPub = (givenXpub) => { - const command = { + const submitXPub = (givenXpub: string) => { + type AddWalletCommand = { + 'add-wallet': { + xpub: string; + fprint: number[]; + 'scan-to': number | null; + 'max-gap': number; + confs: number; + }; + }; + const command: AddWalletCommand = { 'add-wallet': { xpub: givenXpub, fprint: [4, 0], @@ -54,12 +68,12 @@ const WalletModal = () => { setProcessingSubmission(true); }; - const submitMasterTicket = (ticket) => { + const submitMasterTicket = (ticket: string) => { setProcessingSubmission(true); kg.generateWallet({ ticket, - ship: parseInt(patp2dec('~' + window.ship)), - }).then((urbitWallet) => { + ship: parseInt(patp2dec('~' + (window as any).ship)), + }).then((urbitWallet: UrbitWallet) => { const { xpub: xpubFromWallet } = network === 'testnet' ? urbitWallet.bitcoinTestnet.keys @@ -117,11 +131,13 @@ const WalletModal = () => { fontSize="14px" type="password" name="masterTicket" - obscure={(value) => value.replace(/[^~-]+/g, '••••••')} + obscure={(value: string) => value.replace(/[^~-]+/g, '••••••')} placeholder="••••••-••••••-••••••-••••••" autoCapitalize="none" autoCorrect="off" - onChange={(e) => checkTicket(e)} + onChange={(e: React.ChangeEvent) => + checkTicket(e) + } /> {!inputDisabled ? null : } @@ -210,7 +226,7 @@ const WalletModal = () => { name="xpub" autoCapitalize="none" autoCorrect="off" - onChange={(e) => checkXPub(e)} + onChange={(e: React.ChangeEvent) => checkXPub(e)} mr={1} /> {!inputDisabled ? null : } diff --git a/pkg/btc-wallet/src/js/components/lib/warning.js b/pkg/btc-wallet/src/components/Warning.tsx similarity index 91% rename from pkg/btc-wallet/src/js/components/lib/warning.js rename to pkg/btc-wallet/src/components/Warning.tsx index d0183b7e50..0bc5814c49 100644 --- a/pkg/btc-wallet/src/js/components/lib/warning.js +++ b/pkg/btc-wallet/src/components/Warning.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { Box, Text, Button, Col, Anchor } from '@tlon/indigo-react'; -import { api } from '../../api'; -import { useSettings } from '../../hooks/useSettings'; +import { api } from '../lib/api'; +import { useSettings } from '../hooks/useSettings'; const Warning = () => { - const { setWarning } = useSettings(); + const { setShowWarning } = useSettings(); const understand = () => { - setWarning(false); + setShowWarning(false); let removeWarning = { 'put-entry': { value: false, diff --git a/pkg/btc-wallet/src/js/hooks/useSettings.js b/pkg/btc-wallet/src/hooks/useSettings.tsx similarity index 71% rename from pkg/btc-wallet/src/js/hooks/useSettings.js rename to pkg/btc-wallet/src/hooks/useSettings.tsx index fe2838e5f3..a83fa5f760 100644 --- a/pkg/btc-wallet/src/js/hooks/useSettings.js +++ b/pkg/btc-wallet/src/hooks/useSettings.tsx @@ -1,19 +1,76 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; import _ from 'lodash'; -import { api } from '../api'; +import { api } from '../lib/api'; import { mapDenominationToSymbol, reduceHistory } from '../lib/util'; +import { + CurrencyRate, + Denomination, + Network, + Provider, + ProviderPerms, + ScanProgress, + ShipWallets, + Transaction, + TxidType, +} from '../types'; -export const SettingsContext = createContext({ +type SettingsContextType = { + network: Network; + setNetwork: React.Dispatch>; + loadedBtc: boolean; + setLoadedBtc: React.Dispatch>; + loadedSettings: boolean; + setLoadedSettings: React.Dispatch>; + loaded: boolean; + setLoaded: React.Dispatch>; + providerPerms: ProviderPerms; + setProviderPerms: React.Dispatch>; + shipWallets: ShipWallets; + setShipWallets: React.Dispatch>; + provider: Provider; + setProvider: React.Dispatch>; + wallet: string | null; + setWallet: React.Dispatch>; + confirmedBalance: number; + setConfirmedBalance: React.Dispatch>; + unconfirmedBalance: number; + setUnconfirmedBalance: React.Dispatch>; + btcState: any; + setBtcState: React.Dispatch>; + history: Transaction[]; + setHistory: React.Dispatch>; + fee: number; + setFee: React.Dispatch>; + psbt: string; + setPsbt: React.Dispatch>; + address: string | null; + setAddress: React.Dispatch>; + currencyRates: CurrencyRate; + setCurrencyRates: React.Dispatch>; + denomination: Denomination; + setDenomination: React.Dispatch>; + showWarning: boolean; + setShowWarning: React.Dispatch>; + error: string; + setError: React.Dispatch>; + broadcastSuccess: boolean; + setBroadcastSuccess: React.Dispatch>; + scanProgress: ScanProgress; + setScanProgress: React.Dispatch>; +}; + +export const SettingsContext = createContext({ network: 'bitcoin', + setNetwork: () => {}, loadedBtc: false, setLoadedBtc: () => {}, loadedSettings: false, setLoadedSettings: () => {}, loaded: false, setLoaded: () => {}, - providerPerms: {}, + providerPerms: { provider: '', permitted: false }, setProviderPerms: () => {}, - shipWallets: {}, + shipWallets: { payee: '', hasWallet: false }, setShipWallets: () => {}, provider: null, setProvider: () => {}, @@ -36,7 +93,9 @@ export const SettingsContext = createContext({ currencyRates: { BTC: { last: 1, symbol: 'BTC' }, }, + setCurrencyRates: () => {}, denomination: 'BTC', + setDenomination: () => {}, showWarning: true, setShowWarning: () => {}, error: '', @@ -47,14 +106,24 @@ export const SettingsContext = createContext({ setScanProgress: () => {}, }); -export const SettingsProvider = ({ channel, children }) => { - const [network, setNetwork] = useState('bitcoin'); +type Props = { + channel: { setOnChannelError: (arg: () => void) => void }; +}; + +export const SettingsProvider: React.FC = ({ channel, children }) => { + const [network, setNetwork] = useState('bitcoin'); const [channelData, setChannelData] = useState(null); const [loadedBtc, setLoadedBtc] = useState(false); const [loadedSettings, setLoadedSettings] = useState(false); const [loaded, setLoaded] = useState(false); - const [providerPerms, setProviderPerms] = useState({}); - const [shipWallets, setShipWallets] = useState({}); + const [providerPerms, setProviderPerms] = useState({ + provider: '', + permitted: false, + }); + const [shipWallets, setShipWallets] = useState({ + payee: '', + hasWallet: false, + }); const [provider, setProvider] = useState(null); const [wallet, setWallet] = useState(null); const [confirmedBalance, setConfirmedBalance] = useState(0); @@ -67,7 +136,7 @@ export const SettingsProvider = ({ channel, children }) => { const [currencyRates, setCurrencyRates] = useState({ BTC: { last: 1, symbol: 'BTC' }, }); - const [denomination, setDenomination] = useState('BTC'); + const [denomination, setDenomination] = useState('BTC'); const [showWarning, setShowWarning] = useState(false); const [error, setError] = useState(''); const [broadcastSuccess, setBroadcastSuccess] = useState(false); @@ -78,11 +147,11 @@ export const SettingsProvider = ({ channel, children }) => { const { Provider } = SettingsContext; - const success = (event) => { + const success = (event: any) => { console.log({ event }); setChannelData(event); }; - const fail = (error) => console.log({ error }); + const fail = (error: any) => console.log({ error }); const initializeBtcWallet = () => { api.bind('/all', 'PUT', api.ship, 'btc-wallet', success, fail); @@ -123,7 +192,7 @@ export const SettingsProvider = ({ channel, children }) => { fetch('https://blockchain.info/ticker') .then((res) => res.json()) .then((n) => { - const newCurrencyRates = currencyRates; + const newCurrencyRates: any = currencyRates; for (let c in n) { newCurrencyRates[c] = n[c]; newCurrencyRates[c].symbol = mapDenominationToSymbol(c); @@ -141,13 +210,13 @@ export const SettingsProvider = ({ channel, children }) => { } }; - const handleNewTx = (newTx) => { + const handleNewTx = (newTx: Transaction) => { const { txid, recvd } = newTx; - let old = _.findIndex(history, (h) => { + let old = _.findIndex(history, (h: Transaction) => { return h.txid.dat === txid.dat && h.txid.wid === txid.wid; }); if (old !== -1) { - const newHistory = history.filter((o, i) => i !== old); + const newHistory = history.filter((_, i) => i !== old); setHistory(newHistory); } if (recvd === null && old === -1) { @@ -156,7 +225,7 @@ export const SettingsProvider = ({ channel, children }) => { } else if (recvd !== null && old === -1) { // we expect history to have null recvd values first, and the rest in // descending order - let insertionIndex = _.findIndex(history, (h) => { + let insertionIndex = _.findIndex(history, (h: Transaction) => { return h.recvd < recvd && h.recvd !== null; }); const newHistory = history.map((o, i) => @@ -166,8 +235,8 @@ export const SettingsProvider = ({ channel, children }) => { } }; - const handleCancelTx = ({ wid, dat }) => { - let entryIndex = _.findIndex(history, (h) => { + const handleCancelTx = ({ wid, dat }: TxidType) => { + let entryIndex = _.findIndex(history, (h: Transaction) => { return wid === h.txid.wid && dat === h.txid.dat; }); if (entryIndex > -1) { @@ -221,14 +290,14 @@ export const SettingsProvider = ({ channel, children }) => { handleNewTx(newTx); } if (providerStatus) { - let newProviderPerms = providerPerms; + let newProviderPerms: any = providerPerms; for (let c in providerStatus) { newProviderPerms[c] = providerStatus[c]; } setProviderPerms(newProviderPerms); } if (checkPayee) { - let newShipWallets = shipWallets; + let newShipWallets: any = shipWallets; for (let c in checkPayee) { newShipWallets[c] = checkPayee[c]; diff --git a/pkg/btc-wallet/src/index.js b/pkg/btc-wallet/src/index.tsx similarity index 56% rename from pkg/btc-wallet/src/index.js rename to pkg/btc-wallet/src/index.tsx index 09b51a1384..8257091480 100644 --- a/pkg/btc-wallet/src/index.js +++ b/pkg/btc-wallet/src/index.tsx @@ -1,17 +1,15 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import Root from './js/components/root.js'; -import { api } from './js/api.js'; -import { SettingsProvider } from './js/hooks/useSettings'; +import { api } from './lib/api'; +import { SettingsProvider } from './hooks/useSettings'; +import App from './App'; import './css/indigo-static.css'; import './css/fonts.css'; import './css/custom.css'; -// rebuild x3 - -const channel = new window.channel(); -api.setChannel(window.ship, channel); +const channel = new (window as any).channel(); +api.setChannel((window as any).ship, channel); if (module.hot) { module.hot.accept(); @@ -19,7 +17,7 @@ if (module.hot) { ReactDOM.render( - + , document.querySelectorAll('#root')[0] ); diff --git a/pkg/btc-wallet/src/js/components/lib/error.js b/pkg/btc-wallet/src/js/components/lib/error.js deleted file mode 100644 index 42d114d182..0000000000 --- a/pkg/btc-wallet/src/js/components/lib/error.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { Text } from '@tlon/indigo-react'; - -const errorToString = (error) => { - if (error === 'cant-pay-ourselves') { - return 'Cannot pay ourselves'; - } - if (error === 'no-comets') { - return 'Cannot pay comets'; - } - if (error === 'no-dust') { - return 'Cannot send dust'; - } - if (error === 'tx-being-signed') { - return 'Cannot pay when transaction is being signed'; - } - if (error === 'insufficient-balance') { - return 'Insufficient confirmed balance'; - } - if (error === 'broadcast-fail') { - return 'Transaction broadcast failed'; - } - if (error === 'invalid-master-ticket') { - return 'Invalid master ticket'; - } - if (error === 'invalid-signed') { - return 'Invalid signed bitcoin transaction'; - } -}; - -const Error = ({ error, ...rest }) => ( - - {errorToString(error)} - -); - -export default Error; diff --git a/pkg/btc-wallet/src/js/components/lib/feePicker.js b/pkg/btc-wallet/src/js/components/lib/feePicker.js deleted file mode 100644 index febbd5d10c..0000000000 --- a/pkg/btc-wallet/src/js/components/lib/feePicker.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { - Box, - Text, - Col, - StatelessRadioButtonField as RadioButton, - Label, -} from '@tlon/indigo-react'; -import { feeLevels } from './send'; - -const FeePicker = ({ feeChoices, feeValue, setFeeValue, feeDismiss }) => { - const select = (which) => { - setFeeValue(feeLevels[which]); - feeDismiss(); - }; - - return ( - - - Transaction Speed - - - { - select(feeLevels.low); - }} - > - - - - { - select(feeLevels.low); - }} - > - - - - { - select(feeLevels.high); - }} - > - - - - - ); -}; - -export default FeePicker; diff --git a/pkg/btc-wallet/src/js/components/themes/dark.js b/pkg/btc-wallet/src/js/components/themes/dark.js deleted file mode 100644 index a9d048bf71..0000000000 --- a/pkg/btc-wallet/src/js/components/themes/dark.js +++ /dev/null @@ -1,183 +0,0 @@ -import baseStyled from "styled-components"; - -const base = { - white: "rgba(255,255,255,1)", - black: "rgba(0,0,0,1)", - red: "rgba(255,65,54,1)", - yellow: "rgba(255,199,0,1)", - green: "rgba(0,159,101,1)", - blue: "rgba(0,142,255,1)", -}; - -const scales = { - white05: "rgba(255,255,255,0.05)", - white10: "rgba(255,255,255,0.1)", - white20: "rgba(255,255,255,0.2)", - white30: "rgba(255,255,255,0.3)", - white40: "rgba(255,255,255,0.4)", - white50: "rgba(255,255,255,0.5)", - white60: "rgba(255,255,255,0.6)", - white70: "rgba(255,255,255,0.7)", - white80: "rgba(255,255,255,0.8)", - white90: "rgba(255,255,255,0.9)", - white100: "rgba(255,255,255,1)", - black05: "rgba(0,0,0,0.05)", - black10: "rgba(0,0,0,0.1)", - black20: "rgba(0,0,0,0.2)", - black30: "rgba(0,0,0,0.3)", - black40: "rgba(0,0,0,0.4)", - black50: "rgba(0,0,0,0.5)", - black60: "rgba(0,0,0,0.6)", - black70: "rgba(0,0,0,0.7)", - black80: "rgba(0,0,0,0.8)", - black90: "rgba(0,0,0,0.9)", - black100: "rgba(0,0,0,1)", - red05: "rgba(255,65,54,0.05)", - red10: "rgba(255,65,54,0.1)", - red20: "rgba(255,65,54,0.2)", - red30: "rgba(255,65,54,0.3)", - red40: "rgba(255,65,54,0.4)", - red50: "rgba(255,65,54,0.5)", - red60: "rgba(255,65,54,0.6)", - red70: "rgba(255,65,54,0.7)", - red80: "rgba(255,65,54,0.8)", - red90: "rgba(255,65,54,0.9)", - red100: "rgba(255,65,54,1)", - yellow05: "rgba(255,199,0,0.05)", - yellow10: "rgba(255,199,0,0.1)", - yellow20: "rgba(255,199,0,0.2)", - yellow30: "rgba(255,199,0,0.3)", - yellow40: "rgba(255,199,0,0.4)", - yellow50: "rgba(255,199,0,0.5)", - yellow60: "rgba(255,199,0,0.6)", - yellow70: "rgba(255,199,0,0.7)", - yellow80: "rgba(255,199,0,0.8)", - yellow90: "rgba(255,199,0,0.9)", - yellow100: "rgba(255,199,0,1)", - green05: "rgba(0,159,101,0.05)", - green10: "rgba(0,159,101,0.1)", - green20: "rgba(0,159,101,0.2)", - green30: "rgba(0,159,101,0.3)", - green40: "rgba(0,159,101,0.4)", - green50: "rgba(0,159,101,0.5)", - green60: "rgba(0,159,101,0.6)", - green70: "rgba(0,159,101,0.7)", - green80: "rgba(0,159,101,0.8)", - green90: "rgba(0,159,101,0.9)", - green100: "rgba(0,159,101,1)", - blue05: "rgba(0,142,255,0.05)", - blue10: "rgba(0,142,255,0.1)", - blue20: "rgba(0,142,255,0.2)", - blue30: "rgba(0,142,255,0.3)", - blue40: "rgba(0,142,255,0.4)", - blue50: "rgba(0,142,255,0.5)", - blue60: "rgba(0,142,255,0.6)", - blue70: "rgba(0,142,255,0.7)", - blue80: "rgba(0,142,255,0.8)", - blue90: "rgba(0,142,255,0.9)", - blue100: "rgba(0,142,255,1)", -}; - -const util = { - cyan: "#00FFFF", - magenta: "#FF00FF", - yellow: "#FFFF00", - black: "#000000", - gray0: "#333333" -}; - -const theme = { - colors: { - white: util.gray0, - black: base.white, - - gray: scales.white60, - lightGray: scales.white30, - washedGray: scales.white05, - - red: base.red, - lightRed: scales.red30, - washedRed: scales.red05, - - yellow: base.yellow, - lightYellow: scales.yellow30, - washedYellow: scales.yellow10, - - green: base.green, - lightGreen: scales.green30, - washedGreen: scales.green10, - - blue: base.blue, - lightBlue: scales.blue30, - washedBlue: scales.blue10, - - none: "rgba(0,0,0,0)", - - scales: scales, - util: util, - }, - fonts: { - sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`, - mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`, - }, - // font-size - fontSizes: [ - 12, // 0 - 16, // 1 - 24, // 2 - 32, // 3 - 48, // 4 - 64, // 5 - ], - // font-weight - fontWeights: { - thin: 300, - regular: 400, - bold: 600, - }, - // line-height - lineHeights: { - min: 1.2, - short: 1.333333, - regular: 1.5, - tall: 1.666666, - }, - // border, border-top, border-right, border-bottom, border-left - borders: ["none", "1px solid"], - // margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap - space: [ - 0, // 0 - 4, // 1 - 8, // 2 - 16, // 3 - 24, // 4 - 32, // 5 - 48, // 6 - 64, // 7 - 96, // 8 - ], - // border-radius - radii: [ - 0, // 0 - 2, // 1 - 4, // 2 - 8, // 3 - ], - // width, height, min-width, max-width, min-height, max-height - sizes: [ - 0, // 0 - 4, // 1 - 8, // 2 - 16, // 3 - 24, // 4 - 32, // 5 - 48, // 6 - 64, // 7 - 96, // 8 - ], - // z-index - zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - breakpoints: ["550px", "750px", "960px"], -}; -export const styled = baseStyled; -export default theme; diff --git a/pkg/btc-wallet/src/js/components/themes/light.js b/pkg/btc-wallet/src/js/components/themes/light.js deleted file mode 100644 index 5688e07fe7..0000000000 --- a/pkg/btc-wallet/src/js/components/themes/light.js +++ /dev/null @@ -1,184 +0,0 @@ -import baseStyled from "styled-components"; - -const base = { - white: "rgba(255,255,255,1)", - black: "rgba(0,0,0,1)", - red: "rgba(255,65,54,1)", - yellow: "rgba(255,199,0,1)", - green: "rgba(0,159,101,1)", - blue: "rgba(0,142,255,1)", - none: "rgba(0,0,0,0)", -}; - -const scales = { - white10: "rgba(255,255,255,0.1)", - white20: "rgba(255,255,255,0.2)", - white30: "rgba(255,255,255,0.3)", - white40: "rgba(255,255,255,0.4)", - white50: "rgba(255,255,255,0.5)", - white60: "rgba(255,255,255,0.6)", - white70: "rgba(255,255,255,0.7)", - white80: "rgba(255,255,255,0.8)", - white90: "rgba(255,255,255,0.9)", - white100: "rgba(255,255,255,1)", - black04: "rgba(0,0,0,0.04)", - black10: "rgba(0,0,0,0.1)", - black20: "rgba(0,0,0,0.2)", - black30: "rgba(0,0,0,0.3)", - black40: "rgba(0,0,0,0.4)", - black50: "rgba(0,0,0,0.5)", - black60: "rgba(0,0,0,0.6)", - black70: "rgba(0,0,0,0.7)", - black80: "rgba(0,0,0,0.8)", - black90: "rgba(0,0,0,0.9)", - black100: "rgba(0,0,0,1)", - red05: "rgba(255,65,54,0.05)", - red10: "rgba(255,65,54,0.1)", - red20: "rgba(255,65,54,0.2)", - red30: "rgba(255,65,54,0.3)", - red40: "rgba(255,65,54,0.4)", - red50: "rgba(255,65,54,0.5)", - red60: "rgba(255,65,54,0.6)", - red70: "rgba(255,65,54,0.7)", - red80: "rgba(255,65,54,0.8)", - red90: "rgba(255,65,54,0.9)", - red100: "rgba(255,65,54,1)", - yellow10: "rgba(255,199,0,0.1)", - yellow20: "rgba(255,199,0,0.2)", - yellow30: "rgba(255,199,0,0.3)", - yellow40: "rgba(255,199,0,0.4)", - yellow50: "rgba(255,199,0,0.5)", - yellow60: "rgba(255,199,0,0.6)", - yellow70: "rgba(255,199,0,0.7)", - yellow80: "rgba(255,199,0,0.8)", - yellow90: "rgba(255,199,0,0.9)", - yellow100: "rgba(255,199,0,1)", - green05: "rgba(0,159,101,0.05)", - green10: "rgba(0,159,101,0.1)", - green20: "rgba(0,159,101,0.2)", - green30: "rgba(0,159,101,0.3)", - green40: "rgba(0,159,101,0.4)", - green50: "rgba(0,159,101,0.5)", - green60: "rgba(0,159,101,0.6)", - green70: "rgba(0,159,101,0.7)", - green80: "rgba(0,159,101,0.8)", - green90: "rgba(0,159,101,0.9)", - green100: "rgba(0,159,101,1)", - blue10: "rgba(0,142,255,0.1)", - blue20: "rgba(0,142,255,0.2)", - blue30: "rgba(0,142,255,0.3)", - blue40: "rgba(0,142,255,0.4)", - blue50: "rgba(0,142,255,0.5)", - blue60: "rgba(0,142,255,0.6)", - blue70: "rgba(0,142,255,0.7)", - blue80: "rgba(0,142,255,0.8)", - blue90: "rgba(0,142,255,0.9)", - blue100: "rgba(0,142,255,1)", -}; - -const theme = { - colors: { - white: base.white, - black: base.black, - - gray: scales.black60, - lighterGray: scales.black20, - lightGray: scales.black30, - washedGray: scales.black10, - veryLightGray: scales.black04, - - red: base.red, - lightRed: scales.red30, - washedRed: scales.red10, - veryLightRed: scales.red05, - - yellow: base.yellow, - lightYellow: scales.yellow30, - washedYellow: scales.yellow10, - - green: base.green, - lightGreen: scales.green30, - midGreen: scales.green60, - washedGreen: scales.green10, - veryLightGreen: scales.green05, - - blue: base.blue, - lightBlue: scales.blue30, - washedBlue: scales.blue10, - - none: "rgba(0,0,0,0)", - scales: scales, - - orange: "rgba(255, 153, 0, 1)", - midOrange: "rgba(255, 153, 0, 0.2)", - lightOrange: "rgba(255, 153, 0, 0.08)", - - sentBlue: "rgba(33,157,255,1)", - recvGreen: "rgba(0,159,101,1)", - }, - fonts: { - sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`, - mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`, - }, - // font-size - fontSizes: [ - 12, // 0 - 16, // 1 - 24, // 2 - 32, // 3 - 48, // 4 - 64, // 5 - ], - // font-weight - fontWeights: { - thin: 300, - regular: 400, - bold: 600, - }, - // line-height - lineHeights: { - min: 1.2, - short: 1.333333, - regular: 1.5, - tall: 1.666666, - }, - // border, border-top, border-right, border-bottom, border-left - borders: ["none", "1px solid"], - // margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap - space: [ - 0, // 0 - 4, // 1 - 8, // 2 - 16, // 3 - 24, // 4 - 32, // 5 - 48, // 6 - 64, // 7 - 96, // 8 - ], - // border-radius - radii: [ - 0, // 0 - 2, // 1 - 4, // 2 - 8, // 3 - 16, // 4 - ], - // width, height, min-width, max-width, min-height, max-height - sizes: [ - 0, // 0 - 4, // 1 - 8, // 2 - 16, // 3 - 24, // 4 - 32, // 5 - 48, // 6 - 64, // 7 - 96, // 8 - ], - // z-index - zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - breakpoints: ["550px", "750px", "960px"], -}; -export const styled = baseStyled; -export default theme; diff --git a/pkg/btc-wallet/src/js/subscription.js b/pkg/btc-wallet/src/js/subscription.js deleted file mode 100644 index d83f65d92b..0000000000 --- a/pkg/btc-wallet/src/js/subscription.js +++ /dev/null @@ -1,55 +0,0 @@ -import { api } from './api'; -import { store } from './store'; - -export class Subscription { - start() { - if (api.ship) { - this.initializeBtcWallet(); - this.initializeSettings(); - this.initializeCurrencyPoll(); - } else { - console.error("~~~ ERROR: Must set api.authTokens before operation ~~~"); - } - } - - initializeBtcWallet() { - api.bind('/all', 'PUT', api.ship, 'btc-wallet', - this.handleEvent.bind(this), - this.handleError.bind(this)); - } - - initializeSettings() { - let app = 'settings-store'; - let path = '/bucket/btc-wallet'; - - fetch(`/~/scry/${app}${path}.json`).then(res => res.json()) - .then(n => { - this.handleEvent({data: n}); - }); - - api.bind(path, 'PUT', api.ship, app, - this.handleEvent.bind(this), - this.handleError.bind(this)); - } - - initializeCurrencyPoll() { - fetch('https://blockchain.info/ticker') - .then(res => res.json()) - .then(n => { - store.handleEvent({data: {currencyRates: n}}) - setTimeout(this.initializeCurrencyPoll, 1000 * 60 * 15); - }); - } - - handleEvent(diff) { - store.handleEvent(diff); - } - - handleError(err) { - console.error(err); - this.initializeBtcWallet(); - this.initializeSettings(); - } -} - -export let subscription = new Subscription(); diff --git a/pkg/btc-wallet/src/js/api.js b/pkg/btc-wallet/src/lib/api.ts similarity index 51% rename from pkg/btc-wallet/src/js/api.js rename to pkg/btc-wallet/src/lib/api.ts index 6eadba0154..b2b2253722 100644 --- a/pkg/btc-wallet/src/js/api.js +++ b/pkg/btc-wallet/src/lib/api.ts @@ -1,16 +1,44 @@ import _ from 'lodash'; +type Channel = { + poke: ( + ship: string, + appl: string, + mark: string, + data: any, + postDataHandler: (json: any) => void, + errorHandler: (err: string) => void + ) => void; + subscribe: ( + ship: string, + appl: string, + path: string, + errorHandler: (err: string) => void, + eventHandler: (event: any) => void + ) => void; +}; + class UrbitApi { - setChannel(ship, channel) { + ship: string; + channel: Channel; + bindPaths: string[]; + setChannel(ship: string, channel: Channel) { this.ship = ship; this.channel = channel; this.bindPaths = []; } - bind(path, method, ship = this.ship, appl = 'btc-wallet', success, fail) { + bind( + path: string, + method: string, + ship = this.ship, + appl = 'btc-wallet', + success: any, + fail: any + ) { this.bindPaths = _.uniq([...this.bindPaths, path]); - window.subscriptionId = this.channel.subscribe( + (window as any).subscriptionId = this.channel.subscribe( ship, appl, path, @@ -25,22 +53,19 @@ class UrbitApi { path, }, }); - }, - (err) => { - fail(err); } ); } - btcWalletCommand(data) { + btcWalletCommand(data: any) { return this.action('btc-wallet', 'btc-wallet-command', data); } - settingsEvent(data) { + settingsEvent(data: any) { return this.action('settings-store', 'settings-event', data); } - action(appl, mark, data) { + action(appl: string, mark: string, data: string) { return new Promise((resolve, reject) => { this.channel.poke( this.ship, @@ -58,4 +83,4 @@ class UrbitApi { } } export let api = new UrbitApi(); -window.api = api; +(window as any).api = api; diff --git a/pkg/btc-wallet/src/js/lib/util.js b/pkg/btc-wallet/src/lib/util.ts similarity index 79% rename from pkg/btc-wallet/src/js/lib/util.js rename to pkg/btc-wallet/src/lib/util.ts index fa17b1ec80..56967a107d 100644 --- a/pkg/btc-wallet/src/js/lib/util.js +++ b/pkg/btc-wallet/src/lib/util.ts @@ -1,3 +1,5 @@ +import { CurrencyRate, Denomination, Transaction } from '../types'; + export function uuid() { let str = '0v'; str += Math.ceil(Math.random() * 8) + '.'; @@ -10,7 +12,7 @@ export function uuid() { return str.slice(0, -1); } -export function isPatTa(str) { +export function isPatTa(str: string) { const r = /^[a-z,0-9,\-,.,_,~]+$/.exec(str); return !!r; } @@ -21,8 +23,8 @@ export function isPatTa(str) { To: (javascript Date object) */ -export function daToDate(st) { - var dub = function (n) { +export function daToDate(st: string) { + var dub = function (n: string) { return parseInt(n) < 10 ? '0' + parseInt(n) : n.toString(); }; var da = st.split('..'); @@ -41,8 +43,8 @@ export function daToDate(st) { ~2018.7.17..23.15.09..5be5 // urbit @da */ -export function dateToDa(d, mil) { - var fil = function (n) { +export function dateToDa(d: Date, mil: boolean) { + var fil = function (n: number) { return n >= 10 ? n : '0' + n; }; return ( @@ -56,12 +58,12 @@ export function dateToDa(d, mil) { ); } -export function deSig(ship) { +export function deSig(ship: string) { return ship.replace('~', ''); } // trim patps to match dojo, chat-cli -export function cite(ship) { +export function cite(ship: string) { let patp = ship, shortened = ''; if (patp.startsWith('~')) { @@ -80,7 +82,11 @@ export function cite(ship) { return `~${patp}`; } -export function satsToCurrency(sats, denomination, rates) { +export function satsToCurrency( + sats: number, + denomination: Denomination, + rates: CurrencyRate +) { if (!rates) { throw 'nonexistent currency table'; } @@ -99,27 +105,7 @@ export function satsToCurrency(sats, denomination, rates) { return text; } -export function currencyToSats(val, denomination, rates) { - if (!rates) { - throw 'nonexistent currency table'; - } - if (!rates[denomination]) { - throw 'currency not in table'; - } - let rate = rates[denomination]; - let sats = (parseFloat(val) / rate.last) * 100000000; - return sats; -} - -export function reduceHistory(history) { - return Object.values(history).sort((hest1, hest2) => { - if (hest1.recvd === null) return -1; - if (hest2.recvd === null) return +1; - return hest2.recvd - hest1.recvd; - }); -} - -export function mapDenominationToSymbol(denomination) { +export function mapDenominationToSymbol(denomination: string) { switch (denomination) { case 'USD': return '$'; @@ -128,7 +114,15 @@ export function mapDenominationToSymbol(denomination) { } } -export function copyToClipboard(textToCopy) { +export function reduceHistory(history: Transaction[]) { + return Object.values(history).sort((hest1, hest2) => { + if (hest1.recvd === null) return -1; + if (hest2.recvd === null) return +1; + return hest2.recvd - hest1.recvd; + }); +} + +export function copyToClipboard(textToCopy: string) { // navigator clipboard api needs a secure context (https or localhost) if (navigator.clipboard && window.isSecureContext) { return navigator.clipboard.writeText(textToCopy); @@ -141,7 +135,7 @@ export function copyToClipboard(textToCopy) { document.body.appendChild(textArea); textArea.focus(); textArea.select(); - return new Promise((res, rej) => { + return new Promise((res, rej) => { document.execCommand('copy') ? res() : rej(); textArea.remove(); }); diff --git a/pkg/btc-wallet/src/themes/dark.js b/pkg/btc-wallet/src/themes/dark.js new file mode 100644 index 0000000000..5095ef4686 --- /dev/null +++ b/pkg/btc-wallet/src/themes/dark.js @@ -0,0 +1,183 @@ +import baseStyled from 'styled-components'; + +const base = { + white: 'rgba(255,255,255,1)', + black: 'rgba(0,0,0,1)', + red: 'rgba(255,65,54,1)', + yellow: 'rgba(255,199,0,1)', + green: 'rgba(0,159,101,1)', + blue: 'rgba(0,142,255,1)', +}; + +const scales = { + white05: 'rgba(255,255,255,0.05)', + white10: 'rgba(255,255,255,0.1)', + white20: 'rgba(255,255,255,0.2)', + white30: 'rgba(255,255,255,0.3)', + white40: 'rgba(255,255,255,0.4)', + white50: 'rgba(255,255,255,0.5)', + white60: 'rgba(255,255,255,0.6)', + white70: 'rgba(255,255,255,0.7)', + white80: 'rgba(255,255,255,0.8)', + white90: 'rgba(255,255,255,0.9)', + white100: 'rgba(255,255,255,1)', + black05: 'rgba(0,0,0,0.05)', + black10: 'rgba(0,0,0,0.1)', + black20: 'rgba(0,0,0,0.2)', + black30: 'rgba(0,0,0,0.3)', + black40: 'rgba(0,0,0,0.4)', + black50: 'rgba(0,0,0,0.5)', + black60: 'rgba(0,0,0,0.6)', + black70: 'rgba(0,0,0,0.7)', + black80: 'rgba(0,0,0,0.8)', + black90: 'rgba(0,0,0,0.9)', + black100: 'rgba(0,0,0,1)', + red05: 'rgba(255,65,54,0.05)', + red10: 'rgba(255,65,54,0.1)', + red20: 'rgba(255,65,54,0.2)', + red30: 'rgba(255,65,54,0.3)', + red40: 'rgba(255,65,54,0.4)', + red50: 'rgba(255,65,54,0.5)', + red60: 'rgba(255,65,54,0.6)', + red70: 'rgba(255,65,54,0.7)', + red80: 'rgba(255,65,54,0.8)', + red90: 'rgba(255,65,54,0.9)', + red100: 'rgba(255,65,54,1)', + yellow05: 'rgba(255,199,0,0.05)', + yellow10: 'rgba(255,199,0,0.1)', + yellow20: 'rgba(255,199,0,0.2)', + yellow30: 'rgba(255,199,0,0.3)', + yellow40: 'rgba(255,199,0,0.4)', + yellow50: 'rgba(255,199,0,0.5)', + yellow60: 'rgba(255,199,0,0.6)', + yellow70: 'rgba(255,199,0,0.7)', + yellow80: 'rgba(255,199,0,0.8)', + yellow90: 'rgba(255,199,0,0.9)', + yellow100: 'rgba(255,199,0,1)', + green05: 'rgba(0,159,101,0.05)', + green10: 'rgba(0,159,101,0.1)', + green20: 'rgba(0,159,101,0.2)', + green30: 'rgba(0,159,101,0.3)', + green40: 'rgba(0,159,101,0.4)', + green50: 'rgba(0,159,101,0.5)', + green60: 'rgba(0,159,101,0.6)', + green70: 'rgba(0,159,101,0.7)', + green80: 'rgba(0,159,101,0.8)', + green90: 'rgba(0,159,101,0.9)', + green100: 'rgba(0,159,101,1)', + blue05: 'rgba(0,142,255,0.05)', + blue10: 'rgba(0,142,255,0.1)', + blue20: 'rgba(0,142,255,0.2)', + blue30: 'rgba(0,142,255,0.3)', + blue40: 'rgba(0,142,255,0.4)', + blue50: 'rgba(0,142,255,0.5)', + blue60: 'rgba(0,142,255,0.6)', + blue70: 'rgba(0,142,255,0.7)', + blue80: 'rgba(0,142,255,0.8)', + blue90: 'rgba(0,142,255,0.9)', + blue100: 'rgba(0,142,255,1)', +}; + +const util = { + cyan: '#00FFFF', + magenta: '#FF00FF', + yellow: '#FFFF00', + black: '#000000', + gray0: '#333333', +}; + +const theme = { + colors: { + white: util.gray0, + black: base.white, + + gray: scales.white60, + lightGray: scales.white30, + washedGray: scales.white05, + + red: base.red, + lightRed: scales.red30, + washedRed: scales.red05, + + yellow: base.yellow, + lightYellow: scales.yellow30, + washedYellow: scales.yellow10, + + green: base.green, + lightGreen: scales.green30, + washedGreen: scales.green10, + + blue: base.blue, + lightBlue: scales.blue30, + washedBlue: scales.blue10, + + none: 'rgba(0,0,0,0)', + + scales: scales, + util: util, + }, + fonts: { + sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`, + mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`, + }, + // font-size + fontSizes: [ + 12, // 0 + 16, // 1 + 24, // 2 + 32, // 3 + 48, // 4 + 64, // 5 + ], + // font-weight + fontWeights: { + thin: 300, + regular: 400, + bold: 600, + }, + // line-height + lineHeights: { + min: 1.2, + short: 1.333333, + regular: 1.5, + tall: 1.666666, + }, + // border, border-top, border-right, border-bottom, border-left + borders: ['none', '1px solid'], + // margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap + space: [ + 0, // 0 + 4, // 1 + 8, // 2 + 16, // 3 + 24, // 4 + 32, // 5 + 48, // 6 + 64, // 7 + 96, // 8 + ], + // border-radius + radii: [ + 0, // 0 + 2, // 1 + 4, // 2 + 8, // 3 + ], + // width, height, min-width, max-width, min-height, max-height + sizes: [ + 0, // 0 + 4, // 1 + 8, // 2 + 16, // 3 + 24, // 4 + 32, // 5 + 48, // 6 + 64, // 7 + 96, // 8 + ], + // z-index + zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + breakpoints: ['550px', '750px', '960px'], +}; +export const styled = baseStyled; +export default theme; diff --git a/pkg/btc-wallet/src/themes/light.js b/pkg/btc-wallet/src/themes/light.js new file mode 100644 index 0000000000..6dd797c696 --- /dev/null +++ b/pkg/btc-wallet/src/themes/light.js @@ -0,0 +1,184 @@ +import baseStyled from 'styled-components'; + +const base = { + white: 'rgba(255,255,255,1)', + black: 'rgba(0,0,0,1)', + red: 'rgba(255,65,54,1)', + yellow: 'rgba(255,199,0,1)', + green: 'rgba(0,159,101,1)', + blue: 'rgba(0,142,255,1)', + none: 'rgba(0,0,0,0)', +}; + +const scales = { + white10: 'rgba(255,255,255,0.1)', + white20: 'rgba(255,255,255,0.2)', + white30: 'rgba(255,255,255,0.3)', + white40: 'rgba(255,255,255,0.4)', + white50: 'rgba(255,255,255,0.5)', + white60: 'rgba(255,255,255,0.6)', + white70: 'rgba(255,255,255,0.7)', + white80: 'rgba(255,255,255,0.8)', + white90: 'rgba(255,255,255,0.9)', + white100: 'rgba(255,255,255,1)', + black04: 'rgba(0,0,0,0.04)', + black10: 'rgba(0,0,0,0.1)', + black20: 'rgba(0,0,0,0.2)', + black30: 'rgba(0,0,0,0.3)', + black40: 'rgba(0,0,0,0.4)', + black50: 'rgba(0,0,0,0.5)', + black60: 'rgba(0,0,0,0.6)', + black70: 'rgba(0,0,0,0.7)', + black80: 'rgba(0,0,0,0.8)', + black90: 'rgba(0,0,0,0.9)', + black100: 'rgba(0,0,0,1)', + red05: 'rgba(255,65,54,0.05)', + red10: 'rgba(255,65,54,0.1)', + red20: 'rgba(255,65,54,0.2)', + red30: 'rgba(255,65,54,0.3)', + red40: 'rgba(255,65,54,0.4)', + red50: 'rgba(255,65,54,0.5)', + red60: 'rgba(255,65,54,0.6)', + red70: 'rgba(255,65,54,0.7)', + red80: 'rgba(255,65,54,0.8)', + red90: 'rgba(255,65,54,0.9)', + red100: 'rgba(255,65,54,1)', + yellow10: 'rgba(255,199,0,0.1)', + yellow20: 'rgba(255,199,0,0.2)', + yellow30: 'rgba(255,199,0,0.3)', + yellow40: 'rgba(255,199,0,0.4)', + yellow50: 'rgba(255,199,0,0.5)', + yellow60: 'rgba(255,199,0,0.6)', + yellow70: 'rgba(255,199,0,0.7)', + yellow80: 'rgba(255,199,0,0.8)', + yellow90: 'rgba(255,199,0,0.9)', + yellow100: 'rgba(255,199,0,1)', + green05: 'rgba(0,159,101,0.05)', + green10: 'rgba(0,159,101,0.1)', + green20: 'rgba(0,159,101,0.2)', + green30: 'rgba(0,159,101,0.3)', + green40: 'rgba(0,159,101,0.4)', + green50: 'rgba(0,159,101,0.5)', + green60: 'rgba(0,159,101,0.6)', + green70: 'rgba(0,159,101,0.7)', + green80: 'rgba(0,159,101,0.8)', + green90: 'rgba(0,159,101,0.9)', + green100: 'rgba(0,159,101,1)', + blue10: 'rgba(0,142,255,0.1)', + blue20: 'rgba(0,142,255,0.2)', + blue30: 'rgba(0,142,255,0.3)', + blue40: 'rgba(0,142,255,0.4)', + blue50: 'rgba(0,142,255,0.5)', + blue60: 'rgba(0,142,255,0.6)', + blue70: 'rgba(0,142,255,0.7)', + blue80: 'rgba(0,142,255,0.8)', + blue90: 'rgba(0,142,255,0.9)', + blue100: 'rgba(0,142,255,1)', +}; + +const theme = { + colors: { + white: base.white, + black: base.black, + + gray: scales.black60, + lighterGray: scales.black20, + lightGray: scales.black30, + washedGray: scales.black10, + veryLightGray: scales.black04, + + red: base.red, + lightRed: scales.red30, + washedRed: scales.red10, + veryLightRed: scales.red05, + + yellow: base.yellow, + lightYellow: scales.yellow30, + washedYellow: scales.yellow10, + + green: base.green, + lightGreen: scales.green30, + midGreen: scales.green60, + washedGreen: scales.green10, + veryLightGreen: scales.green05, + + blue: base.blue, + lightBlue: scales.blue30, + washedBlue: scales.blue10, + + none: 'rgba(0,0,0,0)', + scales: scales, + + orange: 'rgba(255, 153, 0, 1)', + midOrange: 'rgba(255, 153, 0, 0.2)', + lightOrange: 'rgba(255, 153, 0, 0.08)', + + sentBlue: 'rgba(33,157,255,1)', + recvGreen: 'rgba(0,159,101,1)', + }, + fonts: { + sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`, + mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`, + }, + // font-size + fontSizes: [ + 12, // 0 + 16, // 1 + 24, // 2 + 32, // 3 + 48, // 4 + 64, // 5 + ], + // font-weight + fontWeights: { + thin: 300, + regular: 400, + bold: 600, + }, + // line-height + lineHeights: { + min: 1.2, + short: 1.333333, + regular: 1.5, + tall: 1.666666, + }, + // border, border-top, border-right, border-bottom, border-left + borders: ['none', '1px solid'], + // margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap + space: [ + 0, // 0 + 4, // 1 + 8, // 2 + 16, // 3 + 24, // 4 + 32, // 5 + 48, // 6 + 64, // 7 + 96, // 8 + ], + // border-radius + radii: [ + 0, // 0 + 2, // 1 + 4, // 2 + 8, // 3 + 16, // 4 + ], + // width, height, min-width, max-width, min-height, max-height + sizes: [ + 0, // 0 + 4, // 1 + 8, // 2 + 16, // 3 + 24, // 4 + 32, // 5 + 48, // 6 + 64, // 7 + 96, // 8 + ], + // z-index + zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + breakpoints: ['550px', '750px', '960px'], +}; +export const styled = baseStyled; +export default theme; diff --git a/pkg/btc-wallet/src/types.ts b/pkg/btc-wallet/src/types.ts new file mode 100644 index 0000000000..88a80269eb --- /dev/null +++ b/pkg/btc-wallet/src/types.ts @@ -0,0 +1,45 @@ +export type ProviderPerms = { + provider: string; + permitted: boolean; +}; + +export type ShipWallets = { + payee: string; + hasWallet: boolean; +}; + +export type Transaction = { + txid: TxidType; + recvd: number; + outputs: [{ ship: string; val: { value: number } }]; + inputs: [{ ship: string }]; + failure: string; +}; + +export type TxidType = { + dat: string; + wid: string; +}; + +export type ScanProgress = { + main: null | number; + change: null | number; +}; + +export type Network = 'bitcoin' | 'testnet'; + +export type Denomination = 'BTC' | 'USD'; + +export type UrbitWallet = { + bitcoinTestnet: { keys: { xpub: string; xprv: string } }; + bitcoinMainnet: { keys: { xpub: string; xprv: string } }; +}; + +export type CurrencyRate = { + [Denomination: string]: { last: number; symbol: string }; +}; + +export type Provider = { + host: string; + connected: boolean; +}; diff --git a/pkg/btc-wallet/src/urbit-key-generation.d.ts b/pkg/btc-wallet/src/urbit-key-generation.d.ts new file mode 100644 index 0000000000..f383788ae9 --- /dev/null +++ b/pkg/btc-wallet/src/urbit-key-generation.d.ts @@ -0,0 +1 @@ +declare module 'urbit-key-generation'; diff --git a/pkg/btc-wallet/src/urbit-ob.d.ts b/pkg/btc-wallet/src/urbit-ob.d.ts new file mode 100644 index 0000000000..10e6fb26de --- /dev/null +++ b/pkg/btc-wallet/src/urbit-ob.d.ts @@ -0,0 +1 @@ +declare module 'urbit-ob'; diff --git a/pkg/btc-wallet/tsconfig.json b/pkg/btc-wallet/tsconfig.json new file mode 100644 index 0000000000..5256dd7ee6 --- /dev/null +++ b/pkg/btc-wallet/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "noImplicitAny": true, + "module": "es6", + "target": "es5", + "jsx": "react", + "allowJs": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "lib": ["ESNext", "DOM"] + } +} From 6cd4f6d751f367ce7fcd987db81044e31a39aba3 Mon Sep 17 00:00:00 2001 From: ixv Date: Wed, 18 Aug 2021 17:25:17 -0700 Subject: [PATCH 53/64] aqua: assert pill type --- pkg/arvo/app/aqua.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index 97a4c07107..ac4cdb1a67 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -30,7 +30,7 @@ == +$ state-0 $: %0 - pil=pill + pil=$>(%pill pill) assembled=* tym=@da fleet-snaps=(map term fleet) From 764d0189735f30c2b05ee26d68429159491127f9 Mon Sep 17 00:00:00 2001 From: ixv Date: Wed, 18 Aug 2021 18:07:20 -0700 Subject: [PATCH 54/64] btc-wallet: fix imports --- pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx index 0ac036b1b5..d1e9e6b974 100644 --- a/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx +++ b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx @@ -12,10 +12,10 @@ import { import Sigil from '../Sigil'; import * as bitcoin from 'bitcoinjs-lib'; import { isValidPatp } from 'urbit-ob'; -import Sent from './sent.js'; +import Sent from './Sent.tsx'; import Error from '../Error'; -import { copyToClipboard, satsToCurrency } from '../../lib/util.js'; -import { useSettings } from '../../hooks/useSettings.js'; +import { copyToClipboard, satsToCurrency } from '../../lib/util.ts'; +import { useSettings } from '../../hooks/useSettings.tsx'; import { api } from '../../lib/api'; type Props = { From 6ab7f53fff2775daf409886a89b122c4e7d116f6 Mon Sep 17 00:00:00 2001 From: janeway Date: Wed, 18 Aug 2021 18:12:52 -0700 Subject: [PATCH 55/64] glob: update to 0v4.e52ik.udm4j.6aus5.02b25.vomaj --- bin/solid.pill | 4 ++-- pkg/arvo/app/btc-wallet/index.html | 2 +- pkg/arvo/app/glob.hoon | 4 ++-- pkg/arvo/app/landscape/index.html | 2 +- pkg/btc-wallet/README.md | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/solid.pill b/bin/solid.pill index a41ccb6f9e..b29c433a0d 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6afbe5409c3b416c095bead50b35814646d7037a94eaf4b6bef3ab1a46f184bf -size 13100602 +oid sha256:71e2c4915e6318972c47b01e0ede9f6afa60a5f26fbae8bad5d1b14124a8d284 +size 13219186 diff --git a/pkg/arvo/app/btc-wallet/index.html b/pkg/arvo/app/btc-wallet/index.html index 49ffd4843d..343b6f1bee 100644 --- a/pkg/arvo/app/btc-wallet/index.html +++ b/pkg/arvo/app/btc-wallet/index.html @@ -26,6 +26,6 @@
- + diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon index f2906e0e3a..5d5a8328ec 100644 --- a/pkg/arvo/app/glob.hoon +++ b/pkg/arvo/app/glob.hoon @@ -5,8 +5,8 @@ /- glob, *resource /+ default-agent, verb, dbug |% -++ landscape-hash 0v4.qs4qf.3bam7.736sg.0poq8.c6vhq -++ btc-wallet-hash 0v7.v4dng.o33qi.kc497.5jc02.ke5es +++ landscape-hash 0v3.sdoer.mnnfi.opjrg.npmcj.utr8l +++ btc-wallet-hash 0v4.e52ik.udm4j.6aus5.02b25.vomaj +$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))] +$ state-1 [%1 =globs:glob] +$ all-states diff --git a/pkg/arvo/app/landscape/index.html b/pkg/arvo/app/landscape/index.html index 8aa7e57789..2a59eccbd9 100644 --- a/pkg/arvo/app/landscape/index.html +++ b/pkg/arvo/app/landscape/index.html @@ -24,6 +24,6 @@
- + diff --git a/pkg/btc-wallet/README.md b/pkg/btc-wallet/README.md index bc1e3f3b6f..8ac08a2721 100644 --- a/pkg/btc-wallet/README.md +++ b/pkg/btc-wallet/README.md @@ -5,4 +5,4 @@ dojo: it should return with the following hash: -`0v7.v4dng.o33qi.kc497.5jc02.ke5es` +`0v4.e52ik.udm4j.6aus5.02b25.vomaj` From 0ebd41915a344184ae65a22da2e84856088f479a Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 19 Aug 2021 09:25:03 -0500 Subject: [PATCH 56/64] Fix imports in ExternalInvoice --- pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx index 0ac036b1b5..af50ed6d2e 100644 --- a/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx +++ b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx @@ -12,10 +12,10 @@ import { import Sigil from '../Sigil'; import * as bitcoin from 'bitcoinjs-lib'; import { isValidPatp } from 'urbit-ob'; -import Sent from './sent.js'; +import Sent from './Sent'; import Error from '../Error'; -import { copyToClipboard, satsToCurrency } from '../../lib/util.js'; -import { useSettings } from '../../hooks/useSettings.js'; +import { copyToClipboard, satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; import { api } from '../../lib/api'; type Props = { From 2ae99433c437982dfd3ef602306fc561ea50510d Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 19 Aug 2021 09:25:26 -0500 Subject: [PATCH 57/64] Fix enum, was breaking signer button --- pkg/btc-wallet/src/components/Send/Send.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/btc-wallet/src/components/Send/Send.tsx b/pkg/btc-wallet/src/components/Send/Send.tsx index 1567c5c466..ddb06f3f60 100644 --- a/pkg/btc-wallet/src/components/Send/Send.tsx +++ b/pkg/btc-wallet/src/components/Send/Send.tsx @@ -36,9 +36,9 @@ export enum feeLevels { } export enum signMethods { - bridge, - masterTicket, - external, + bridge = 'bridge', + masterTicket = 'masterTicket', + external = 'external', } enum payeeTypes { From 82766647c6abcfe721d281f2a66a0f69dbfe71cc Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 19 Aug 2021 09:31:50 -0500 Subject: [PATCH 58/64] Fix scanning text issues --- pkg/btc-wallet/src/components/Balance.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/btc-wallet/src/components/Balance.tsx b/pkg/btc-wallet/src/components/Balance.tsx index d1c568af0e..2fcab6afd8 100644 --- a/pkg/btc-wallet/src/components/Balance.tsx +++ b/pkg/btc-wallet/src/components/Balance.tsx @@ -104,10 +104,14 @@ const Balance = () => { - {scanProgress.main} main wallet addresses scanned - {scanProgress.change} change wallet addresses scanned + {scanProgress.main === null ? 0 : scanProgress.main} main + wallet addresses scanned + + {scanProgress.change === null ? 0 : scanProgress.change}{' '} + change wallet addresses scanned + ) : ( Date: Thu, 19 Aug 2021 09:33:37 -0500 Subject: [PATCH 59/64] Fix issue with change provider button not triggering modal --- pkg/btc-wallet/src/components/Settings.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/btc-wallet/src/components/Settings.tsx b/pkg/btc-wallet/src/components/Settings.tsx index 61ceed9278..3a376f506d 100644 --- a/pkg/btc-wallet/src/components/Settings.tsx +++ b/pkg/btc-wallet/src/components/Settings.tsx @@ -8,6 +8,7 @@ const Settings = () => { const changeProvider = () => { api.btcWalletCommand({ 'set-provider': null }); + window.location.reload(); }; const replaceWallet = () => { From b4eae08f88f167cfc89cd8bab6f1f794848017bf Mon Sep 17 00:00:00 2001 From: finned-palmer Date: Thu, 19 Aug 2021 17:12:56 -0500 Subject: [PATCH 60/64] Add exit buttons to invoices --- pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx | 3 +++ pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx | 3 +++ pkg/btc-wallet/src/components/Send/Invoice.tsx | 3 +++ 3 files changed, 9 insertions(+) diff --git a/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx b/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx index e7d0d32660..1753d79beb 100644 --- a/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx +++ b/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx @@ -111,6 +111,9 @@ const BridgeInvoice: React.FC = ({ payee, stopSending, satsAmount }) => { mb={5} p={5} > + + stopSending()} /> + = ({ mb={5} p={5} > + + stopSending()} /> + = ({ stopSending, payee, satsAmount }) => { mb={5} p={5} > + + stopSending()} /> + Date: Thu, 19 Aug 2021 17:13:23 -0500 Subject: [PATCH 61/64] Fix feepicker issues --- .../src/components/Send/FeePicker.tsx | 134 ++++++++++-------- pkg/btc-wallet/src/components/Send/Send.tsx | 11 +- 2 files changed, 83 insertions(+), 62 deletions(-) diff --git a/pkg/btc-wallet/src/components/Send/FeePicker.tsx b/pkg/btc-wallet/src/components/Send/FeePicker.tsx index 1dc48158ce..b84ef01b7c 100644 --- a/pkg/btc-wallet/src/components/Send/FeePicker.tsx +++ b/pkg/btc-wallet/src/components/Send/FeePicker.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import { Box, Text, @@ -20,65 +20,81 @@ const FeePicker: React.FC = ({ feeValue, setFeeValue, feeDismiss, -}) => ( - - - Transaction Speed - - - { - setFeeValue(feeLevels.low); - feeDismiss(); - }} - > - - +}) => { + const modalRef = useRef(null); + const clickDismiss = (e: any) => { + if (modalRef && !modalRef.current.contains(e.target)) { + feeDismiss(); + } + }; + useEffect(() => { + document.addEventListener('click', clickDismiss); + return () => { + document.removeEventListener('click', clickDismiss); + }; + }, []); - { - setFeeValue(feeLevels.mid); - feeDismiss(); - }} - > - - + return ( + + + Transaction Speed + + + { + setFeeValue(feeLevels.low); + feeDismiss(); + }} + > + + - { - setFeeValue(feeLevels.high); - feeDismiss(); - }} - > - - - - -); + { + setFeeValue(feeLevels.mid); + feeDismiss(); + }} + > + + + + { + setFeeValue(feeLevels.high); + feeDismiss(); + }} + > + + + + + ); +}; export default FeePicker; diff --git a/pkg/btc-wallet/src/components/Send/Send.tsx b/pkg/btc-wallet/src/components/Send/Send.tsx index ddb06f3f60..22c5ab3c3d 100644 --- a/pkg/btc-wallet/src/components/Send/Send.tsx +++ b/pkg/btc-wallet/src/components/Send/Send.tsx @@ -397,14 +397,19 @@ const Send: React.FC = ({ stopSending, value, conversion }) => { width="24px" border="none" backgroundColor="rgba(33, 157, 255)" + onClick={() => { + setShowFeePicker((prev) => { + if (prev) { + return false; + } + return true; + }); + }} > { - if (!showFeePicker) setShowFeePicker(true); - }} cursor="pointer" /> From fa894b9690deae9e2334ccec5492ba90cb0b38f9 Mon Sep 17 00:00:00 2001 From: janeway Date: Thu, 19 Aug 2021 15:25:15 -0700 Subject: [PATCH 62/64] glob: update to 0v758lj.uf0s5.0nh3m.gunn6.942gj --- bin/solid.pill | 4 ++-- pkg/arvo/app/btc-wallet/index.html | 2 +- pkg/arvo/app/glob.hoon | 2 +- pkg/btc-wallet/README.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/solid.pill b/bin/solid.pill index b29c433a0d..1a27caa02f 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71e2c4915e6318972c47b01e0ede9f6afa60a5f26fbae8bad5d1b14124a8d284 -size 13219186 +oid sha256:12ba08bb71205669907a99e722e1339b3777c2c189f49764b8bbfbeabc38f3d6 +size 16596163 diff --git a/pkg/arvo/app/btc-wallet/index.html b/pkg/arvo/app/btc-wallet/index.html index 343b6f1bee..fea35ace88 100644 --- a/pkg/arvo/app/btc-wallet/index.html +++ b/pkg/arvo/app/btc-wallet/index.html @@ -26,6 +26,6 @@
- + diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon index 5d5a8328ec..d01f0afb8a 100644 --- a/pkg/arvo/app/glob.hoon +++ b/pkg/arvo/app/glob.hoon @@ -6,7 +6,7 @@ /+ default-agent, verb, dbug |% ++ landscape-hash 0v3.sdoer.mnnfi.opjrg.npmcj.utr8l -++ btc-wallet-hash 0v4.e52ik.udm4j.6aus5.02b25.vomaj +++ btc-wallet-hash 0v758lj.uf0s5.0nh3m.gunn6.942gj +$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))] +$ state-1 [%1 =globs:glob] +$ all-states diff --git a/pkg/btc-wallet/README.md b/pkg/btc-wallet/README.md index 8ac08a2721..60933f149e 100644 --- a/pkg/btc-wallet/README.md +++ b/pkg/btc-wallet/README.md @@ -5,4 +5,4 @@ dojo: it should return with the following hash: -`0v4.e52ik.udm4j.6aus5.02b25.vomaj` +`0v758lj.uf0s5.0nh3m.gunn6.942gj` From 02d2185eb36e390449ba8d0a01891b9f54c1f519 Mon Sep 17 00:00:00 2001 From: Edward Amsden Date: Mon, 27 Sep 2021 15:10:19 -0400 Subject: [PATCH 63/64] move hoons to base-dev --- pkg/{arvo => base-dev}/lib/ph/io.hoon | 0 pkg/{arvo => base-dev}/lib/ph/util.hoon | 0 pkg/{arvo => base-dev}/lib/pill.hoon | 0 pkg/{arvo => base-dev}/sur/aquarium.hoon | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename pkg/{arvo => base-dev}/lib/ph/io.hoon (100%) rename pkg/{arvo => base-dev}/lib/ph/util.hoon (100%) rename pkg/{arvo => base-dev}/lib/pill.hoon (100%) rename pkg/{arvo => base-dev}/sur/aquarium.hoon (100%) diff --git a/pkg/arvo/lib/ph/io.hoon b/pkg/base-dev/lib/ph/io.hoon similarity index 100% rename from pkg/arvo/lib/ph/io.hoon rename to pkg/base-dev/lib/ph/io.hoon diff --git a/pkg/arvo/lib/ph/util.hoon b/pkg/base-dev/lib/ph/util.hoon similarity index 100% rename from pkg/arvo/lib/ph/util.hoon rename to pkg/base-dev/lib/ph/util.hoon diff --git a/pkg/arvo/lib/pill.hoon b/pkg/base-dev/lib/pill.hoon similarity index 100% rename from pkg/arvo/lib/pill.hoon rename to pkg/base-dev/lib/pill.hoon diff --git a/pkg/arvo/sur/aquarium.hoon b/pkg/base-dev/sur/aquarium.hoon similarity index 100% rename from pkg/arvo/sur/aquarium.hoon rename to pkg/base-dev/sur/aquarium.hoon From 5272346cb81bc0a6bf5846d46fe9dcc2b2ccaf9c Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Mon, 27 Sep 2021 17:28:30 -0500 Subject: [PATCH 64/64] btc-wallet: manual merge from master to resolve conflicts --- pkg/btc-wallet/.eslintignore | 2 + pkg/btc-wallet/README.md | 2 +- pkg/btc-wallet/config/webpack.dev.js | 83 ++- pkg/btc-wallet/config/webpack.prod.js | 41 +- pkg/btc-wallet/package-lock.json | 228 +++++++- pkg/btc-wallet/package.json | 8 +- pkg/btc-wallet/src/App.tsx | 40 ++ pkg/btc-wallet/src/components/Balance.tsx | 162 ++++++ pkg/btc-wallet/src/components/Body.tsx | 44 ++ .../src/components/CurrencyPicker.tsx | 39 ++ pkg/btc-wallet/src/components/Error.tsx | 32 ++ pkg/btc-wallet/src/components/Header.tsx | 81 +++ .../src/components/ProviderModal.tsx | 166 ++++++ .../src/components/Send/BridgeInvoice.tsx | 223 ++++++++ .../src/components/Send/ExternalInvoice.tsx | 275 ++++++++++ .../src/components/Send/FeePicker.tsx | 100 ++++ .../src/components/Send/Invoice.tsx | 280 ++++++++++ pkg/btc-wallet/src/components/Send/Send.tsx | 512 ++++++++++++++++++ pkg/btc-wallet/src/components/Send/Sent.tsx | 42 ++ pkg/btc-wallet/src/components/Send/Signer.tsx | 79 +++ pkg/btc-wallet/src/components/Settings.tsx | 115 ++++ .../lib/sigil.js => components/Sigil.tsx} | 22 +- .../src/components/StartupModal.tsx | 44 ++ .../components/Transactions/Transaction.tsx | 101 ++++ .../components/Transactions/Transactions.tsx | 43 ++ .../src/components/Transactions/TxAction.tsx | 78 +++ .../Transactions/TxCounterparty.tsx | 41 ++ pkg/btc-wallet/src/components/WalletModal.tsx | 268 +++++++++ pkg/btc-wallet/src/components/Warning.tsx | 73 +++ pkg/btc-wallet/src/hooks/useSettings.tsx | 397 ++++++++++++++ pkg/btc-wallet/src/index.js | 23 - pkg/btc-wallet/src/index.tsx | 24 + .../src/js/components/lib/balance.js | 170 ------ pkg/btc-wallet/src/js/components/lib/body.js | 72 --- .../src/js/components/lib/bridgeInvoice.js | 239 -------- .../src/js/components/lib/currencyPicker.js | 51 -- pkg/btc-wallet/src/js/components/lib/error.js | 41 -- .../src/js/components/lib/feePicker.js | 99 ---- .../src/js/components/lib/header.js | 78 --- .../src/js/components/lib/invoice.js | 291 ---------- .../src/js/components/lib/providerModal.js | 166 ------ pkg/btc-wallet/src/js/components/lib/send.js | 457 ---------------- pkg/btc-wallet/src/js/components/lib/sent.js | 59 -- .../src/js/components/lib/settings.js | 121 ----- .../src/js/components/lib/signer.js | 52 -- .../src/js/components/lib/startupModal.js | 52 -- .../src/js/components/lib/transaction.js | 105 ---- .../src/js/components/lib/transactions.js | 62 --- .../src/js/components/lib/tx-action.js | 74 --- .../src/js/components/lib/tx-counterparty.js | 53 -- .../src/js/components/lib/walletModal.js | 252 --------- .../src/js/components/lib/warning.js | 80 --- pkg/btc-wallet/src/js/components/root.js | 67 --- .../src/js/components/themes/dark.js | 183 ------- .../src/js/components/themes/light.js | 184 ------- pkg/btc-wallet/src/js/lib/util.js | 112 ---- pkg/btc-wallet/src/js/reducers/currency.js | 20 - pkg/btc-wallet/src/js/reducers/initial.js | 29 - pkg/btc-wallet/src/js/reducers/settings.js | 30 - pkg/btc-wallet/src/js/reducers/update.js | 116 ---- pkg/btc-wallet/src/js/store.js | 54 -- pkg/btc-wallet/src/js/subscription.js | 55 -- pkg/btc-wallet/src/{js/api.js => lib/api.ts} | 45 +- pkg/btc-wallet/src/{js => lib}/channel.js | 138 ++--- pkg/btc-wallet/src/lib/util.ts | 143 +++++ pkg/btc-wallet/src/themes/dark.js | 183 +++++++ pkg/btc-wallet/src/themes/light.js | 184 +++++++ pkg/btc-wallet/src/types.ts | 45 ++ pkg/btc-wallet/src/urbit-key-generation.d.ts | 1 + pkg/btc-wallet/src/urbit-ob.d.ts | 1 + pkg/btc-wallet/src/window.ts | 8 + pkg/btc-wallet/tsconfig.json | 13 + 72 files changed, 4248 insertions(+), 3605 deletions(-) create mode 100644 pkg/btc-wallet/.eslintignore create mode 100644 pkg/btc-wallet/src/App.tsx create mode 100644 pkg/btc-wallet/src/components/Balance.tsx create mode 100644 pkg/btc-wallet/src/components/Body.tsx create mode 100644 pkg/btc-wallet/src/components/CurrencyPicker.tsx create mode 100644 pkg/btc-wallet/src/components/Error.tsx create mode 100644 pkg/btc-wallet/src/components/Header.tsx create mode 100644 pkg/btc-wallet/src/components/ProviderModal.tsx create mode 100644 pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx create mode 100644 pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx create mode 100644 pkg/btc-wallet/src/components/Send/FeePicker.tsx create mode 100644 pkg/btc-wallet/src/components/Send/Invoice.tsx create mode 100644 pkg/btc-wallet/src/components/Send/Send.tsx create mode 100644 pkg/btc-wallet/src/components/Send/Sent.tsx create mode 100644 pkg/btc-wallet/src/components/Send/Signer.tsx create mode 100644 pkg/btc-wallet/src/components/Settings.tsx rename pkg/btc-wallet/src/{js/components/lib/sigil.js => components/Sigil.tsx} (78%) create mode 100644 pkg/btc-wallet/src/components/StartupModal.tsx create mode 100644 pkg/btc-wallet/src/components/Transactions/Transaction.tsx create mode 100644 pkg/btc-wallet/src/components/Transactions/Transactions.tsx create mode 100644 pkg/btc-wallet/src/components/Transactions/TxAction.tsx create mode 100644 pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx create mode 100644 pkg/btc-wallet/src/components/WalletModal.tsx create mode 100644 pkg/btc-wallet/src/components/Warning.tsx create mode 100644 pkg/btc-wallet/src/hooks/useSettings.tsx delete mode 100644 pkg/btc-wallet/src/index.js create mode 100644 pkg/btc-wallet/src/index.tsx delete mode 100644 pkg/btc-wallet/src/js/components/lib/balance.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/body.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/currencyPicker.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/error.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/feePicker.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/header.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/invoice.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/providerModal.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/send.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/sent.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/settings.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/signer.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/startupModal.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/transaction.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/transactions.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/tx-action.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/tx-counterparty.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/walletModal.js delete mode 100644 pkg/btc-wallet/src/js/components/lib/warning.js delete mode 100644 pkg/btc-wallet/src/js/components/root.js delete mode 100644 pkg/btc-wallet/src/js/components/themes/dark.js delete mode 100644 pkg/btc-wallet/src/js/components/themes/light.js delete mode 100644 pkg/btc-wallet/src/js/lib/util.js delete mode 100644 pkg/btc-wallet/src/js/reducers/currency.js delete mode 100644 pkg/btc-wallet/src/js/reducers/initial.js delete mode 100644 pkg/btc-wallet/src/js/reducers/settings.js delete mode 100644 pkg/btc-wallet/src/js/reducers/update.js delete mode 100644 pkg/btc-wallet/src/js/store.js delete mode 100644 pkg/btc-wallet/src/js/subscription.js rename pkg/btc-wallet/src/{js/api.js => lib/api.ts} (51%) rename pkg/btc-wallet/src/{js => lib}/channel.js (71%) create mode 100644 pkg/btc-wallet/src/lib/util.ts create mode 100644 pkg/btc-wallet/src/themes/dark.js create mode 100644 pkg/btc-wallet/src/themes/light.js create mode 100644 pkg/btc-wallet/src/types.ts create mode 100644 pkg/btc-wallet/src/urbit-key-generation.d.ts create mode 100644 pkg/btc-wallet/src/urbit-ob.d.ts create mode 100644 pkg/btc-wallet/src/window.ts create mode 100644 pkg/btc-wallet/tsconfig.json diff --git a/pkg/btc-wallet/.eslintignore b/pkg/btc-wallet/.eslintignore new file mode 100644 index 0000000000..2e63ff8db4 --- /dev/null +++ b/pkg/btc-wallet/.eslintignore @@ -0,0 +1,2 @@ +config/webpack.dev.js +config/webpack.prod.js \ No newline at end of file diff --git a/pkg/btc-wallet/README.md b/pkg/btc-wallet/README.md index bc1e3f3b6f..60933f149e 100644 --- a/pkg/btc-wallet/README.md +++ b/pkg/btc-wallet/README.md @@ -5,4 +5,4 @@ dojo: it should return with the following hash: -`0v7.v4dng.o33qi.kc497.5jc02.ke5es` +`0v758lj.uf0s5.0nh3m.gunn6.942gj` diff --git a/pkg/btc-wallet/config/webpack.dev.js b/pkg/btc-wallet/config/webpack.dev.js index 532f849740..8b31012044 100644 --- a/pkg/btc-wallet/config/webpack.dev.js +++ b/pkg/btc-wallet/config/webpack.dev.js @@ -6,9 +6,10 @@ const urbitrc = require('./urbitrc'); const fs = require('fs-extra'); const _ = require('lodash'); -function copy(src,dest) { - return new Promise((res,rej) => - fs.copy(src,dest, err => err ? rej(err) : res())); +function copy(src, dest) { + return new Promise((res, rej) => + fs.copy(src, dest, (err) => (err ? rej(err) : res())) + ); } class UrbitShipPlugin { @@ -36,24 +37,29 @@ let devServer = { publicPath: '/apps/bitcoin/', }; -const router = _.mapKeys(urbitrc.FLEET || {}, (value, key) => `${key}.localhost:9000`); +const router = _.mapKeys( + urbitrc.FLEET || {}, + (value, key) => `${key}.localhost:9000` +); -if(urbitrc.URL) { +if (urbitrc.URL) { devServer = { ...devServer, index: 'index.html', - proxy: [{ - target: 'http://localhost:9000', - changeOrigin: true, - target: urbitrc.URL, - router, - context: path => { - if(path === '/apps/bitcoin/desk.js') { - return true; - } - return !path.startsWith('/apps/bitcoin') - } - }] + proxy: [ + { + target: 'http://localhost:9000', + changeOrigin: true, + target: urbitrc.URL, + router, + context: (path) => { + if (path === '/apps/bitcoin/desk.js') { + return true; + } + return !path.startsWith('/apps/bitcoin'); + }, + }, + ], }; } @@ -61,30 +67,16 @@ module.exports = { node: { fs: 'empty' }, mode: 'development', entry: { - app: './src/index.js' + app: './src/index.tsx', }, module: { rules: [ { test: /\.(j|t)sx?$/, use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', ['@babel/preset-react', { - runtime: 'automatic', - development: 'true', - importSource: '@welldone-software/why-did-you-render', - }]], - plugins: [ - '@babel/transform-runtime', - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-class-properties', - 'react-hot-loader/babel' - ] - } + loader: 'ts-loader', }, - exclude: /node_modules/ + exclude: /node_modules/, }, { test: /\.css$/i, @@ -94,13 +86,13 @@ module.exports = { // Translates CSS into CommonJS 'css-loader', // Compiles Sass to CSS - 'sass-loader' - ] - } - ] + 'sass-loader', + ], + }, + ], }, resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'], }, devtool: 'inline-source-map', devServer: devServer, @@ -108,24 +100,23 @@ module.exports = { new UrbitShipPlugin(urbitrc), new HtmlWebpackPlugin({ title: 'Bitcoin Wallet', - template: './public/index.html' - }) - + template: './public/index.html', + }), ], watch: true, watchOptions: { poll: true, - ignored: '/node_modules/' + ignored: '/node_modules/', }, output: { filename: 'index.js', chunkFilename: 'index.js', path: path.resolve(__dirname, '../dist'), publicPath: '/apps/bitcoin/', - globalObject: 'this' + globalObject: 'this', }, optimization: { minimize: false, - usedExports: true - } + usedExports: true, + }, }; diff --git a/pkg/btc-wallet/config/webpack.prod.js b/pkg/btc-wallet/config/webpack.prod.js index fbdbae0590..930182af0f 100644 --- a/pkg/btc-wallet/config/webpack.prod.js +++ b/pkg/btc-wallet/config/webpack.prod.js @@ -7,59 +7,52 @@ module.exports = { node: { fs: 'empty' }, mode: 'production', entry: { - app: './src/index.js' + app: './src/index.tsx', }, module: { rules: [ { - test: /\.jsx?$/, + test: /\.(j|t)sx?$/, use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', '@babel/preset-react'], - plugins: [ - '@babel/transform-runtime', - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-class-properties' - ] - } + loader: 'ts-loader', }, - exclude: /node_modules/ + exclude: /node_modules/, }, { - test: /\.css$/i, + test: /\.css$/i, use: [ // Creates `style` nodes from JS strings 'style-loader', // Translates CSS into CommonJS 'css-loader', // Compiles Sass to CSS - 'sass-loader' - ] - } - ] + 'sass-loader', + ], + }, + ], }, resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'], }, devtool: 'source-map', plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Bitcoin Wallet', - template: './public/index.html' - }) + template: './public/index.html', + }), ], output: { filename: (pathData) => { - return pathData.chunk.name === 'app' ? 'index.[contenthash].js' : '[name].js'; + return pathData.chunk.name === 'app' + ? 'index.[contenthash].js' + : '[name].js'; }, path: path.resolve(__dirname, '../dist'), publicPath: '/apps/bitcoin/', }, optimization: { minimize: true, - usedExports: true - } + usedExports: true, + }, }; diff --git a/pkg/btc-wallet/package-lock.json b/pkg/btc-wallet/package-lock.json index 458e93169b..6ade588292 100644 --- a/pkg/btc-wallet/package-lock.json +++ b/pkg/btc-wallet/package-lock.json @@ -1430,6 +1430,22 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz", + "integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==", + "dev": true + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", @@ -1441,6 +1457,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" }, + "@types/lodash": { + "version": "4.14.171", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz", + "integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==", + "dev": true + }, "@types/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", @@ -1459,12 +1481,76 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "@types/react": { + "version": "17.0.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz", + "integrity": "sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz", + "integrity": "sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-router": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz", + "integrity": "sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz", + "integrity": "sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "dev": true }, + "@types/styled-components": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.11.tgz", + "integrity": "sha512-u8g3bSw9KUiZY+S++gh+LlURGraqBe3MC5I5dygrNjGDHWWQfsmZZRTJ9K9oHU2CqWtxChWmJkDI/gp+TZPQMw==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "@types/tapable": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz", @@ -1499,6 +1585,12 @@ "source-map": "^0.6.0" } }, + "@types/webpack-env": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.2.tgz", + "integrity": "sha512-vKx7WNQNZDyJveYcHAm9ZxhqSGLYwoyLhrHjLBOkw3a7cT76sTdjgtwyijhk1MaHyRIuSztcVwrUOO/NEu68Dw==", + "dev": true + }, "@types/webpack-sources": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz", @@ -3472,6 +3564,12 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", + "dev": true + }, "cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -10240,6 +10338,130 @@ "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-2.1.1.tgz", "integrity": "sha512-74MoNHhwLVuzwaPDcAecFjSkOA9vwWqyOdkeB0Be8Jc/IWSS5SNZKapFllqzkTliqZptkvqX5CZnVeDvfhN8cw==" }, + "ts-loader": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.2.0.tgz", + "integrity": "sha512-ebXBFrNyMSmbWgjnb3WBloUBK+VSx1xckaXsMXxlZRDqce/OPdYBVN5efB0W3V0defq0Gcy4YuzvPGqRgjj85A==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", @@ -10293,9 +10515,9 @@ "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, "typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", "dev": true }, "unbox-primitive": { diff --git a/pkg/btc-wallet/package.json b/pkg/btc-wallet/package.json index 8e4904344c..5481320935 100644 --- a/pkg/btc-wallet/package.json +++ b/pkg/btc-wallet/package.json @@ -20,6 +20,11 @@ "@babel/preset-env": "^7.9.5", "@babel/preset-react": "^7.9.4", "@babel/preset-typescript": "^7.13.0", + "@types/lodash": "^4.14.171", + "@types/react-dom": "^17.0.9", + "@types/react-router-dom": "^5.1.8", + "@types/styled-components": "^5.1.11", + "@types/webpack-env": "^1.16.2", "@welldone-software/why-did-you-render": "^6.1.1", "babel-loader": "^8.1.0", "babel-plugin-root-import": "^6.5.0", @@ -36,7 +41,8 @@ "react-hot-loader": "^4.12.21", "sass": "^1.26.5", "sass-loader": "^8.0.2", - "typescript": "^4.2.3", + "ts-loader": "8.2.0", + "typescript": "^4.3.5", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.10.3" diff --git a/pkg/btc-wallet/src/App.tsx b/pkg/btc-wallet/src/App.tsx new file mode 100644 index 0000000000..a47ad3a9d2 --- /dev/null +++ b/pkg/btc-wallet/src/App.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { BrowserRouter } from 'react-router-dom'; +import { ThemeProvider } from 'styled-components'; +import light from './themes/light'; +import { Box, Reset } from '@tlon/indigo-react'; +import StartupModal from './components/StartupModal'; +import { useSettings } from './hooks/useSettings'; +import Body from './components/Body'; + +const App: React.FC = () => { + const { loaded, wallet, provider, scanProgress } = useSettings(); + const scanning = scanProgress?.main !== null || scanProgress?.change !== null; + const blur = !loaded || scanning ? false : !(wallet && provider); + + return ( + + + + {loaded && !scanning ? : null} + + + + + + ); +}; + +export default App; diff --git a/pkg/btc-wallet/src/components/Balance.tsx b/pkg/btc-wallet/src/components/Balance.tsx new file mode 100644 index 0000000000..2fcab6afd8 --- /dev/null +++ b/pkg/btc-wallet/src/components/Balance.tsx @@ -0,0 +1,162 @@ +import React, { useState } from 'react'; +import { Row, Text, Button, Col } from '@tlon/indigo-react'; +import Send from './Send/Send'; +import CurrencyPicker from './CurrencyPicker'; +import { copyToClipboard, satsToCurrency } from '../lib/util'; +import { useSettings } from '../hooks/useSettings'; +import { api } from '../lib/api'; + +const Balance = () => { + const { + address, + confirmedBalance: sats, + unconfirmedBalance: unconfirmedSats, + denomination, + currencyRates, + setPsbt, + setFee, + setError, + scanProgress, + } = useSettings(); + const [sending, setSending] = useState(false); + const [copiedButton, setCopiedButton] = useState(false); + const [copiedString, setCopiedString] = useState(false); + const scanning = scanProgress?.main !== null || scanProgress?.change !== null; + + const copyAddress = async (arg: 'string' | 'button') => { + await copyToClipboard(address); + api.btcWalletCommand({ 'gen-new-address': null }); + + if (arg === 'button') { + setCopiedButton(true); + setTimeout(() => { + setCopiedButton(false); + }, 2000); + } else if (arg === 'string') { + setCopiedString(true); + setTimeout(() => { + setCopiedString(false); + }, 2000); + } + }; + + const unconfirmedString = unconfirmedSats ? ` (${unconfirmedSats}) ` : ''; + + const value = satsToCurrency(sats, denomination, currencyRates); + const sendDisabled = sats === 0; + const addressText = + address === null ? '' : address.slice(0, 6) + '...' + address.slice(-6); + + const conversion = currencyRates[denomination]?.last; + + return ( + <> + {sending ? ( + { + setSending(false); + setPsbt(''); + setFee(0); + setError(''); + }} + /> + ) : ( + + + + Balance + + copyAddress('string')} + > + {copiedString ? 'copied' : addressText} + + + + + + {value} + + {scanning ? ( + + + + Balance will be updated shortly: + + + + + {scanProgress.main === null ? 0 : scanProgress.main} main + wallet addresses scanned + + + + {scanProgress.change === null ? 0 : scanProgress.change}{' '} + change wallet addresses scanned + + + ) : ( + {`${sats}${unconfirmedString} sats`} + )} + + + + + + + )} + + ); +}; + +export default Balance; diff --git a/pkg/btc-wallet/src/components/Body.tsx b/pkg/btc-wallet/src/components/Body.tsx new file mode 100644 index 0000000000..57e3be8c64 --- /dev/null +++ b/pkg/btc-wallet/src/components/Body.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Box, LoadingSpinner, Col } from '@tlon/indigo-react'; +import { Switch, Route } from 'react-router-dom'; +import Balance from './Balance'; +import Transactions from './Transactions/Transactions'; +import Warning from './Warning'; +import Header from './Header'; +import Settings from './Settings'; +import { useSettings } from '../hooks/useSettings'; + +const Body: React.FC = () => { + const { loaded, showWarning: warning } = useSettings(); + const cardWidth = window.innerWidth <= 475 ? '350px' : '400px'; + return !loaded ? ( + + + + ) : ( + + + +
+ + + + + +
+ {!warning ? null : } + + + + + + ); +}; + +export default Body; diff --git a/pkg/btc-wallet/src/components/CurrencyPicker.tsx b/pkg/btc-wallet/src/components/CurrencyPicker.tsx new file mode 100644 index 0000000000..7e3498d26b --- /dev/null +++ b/pkg/btc-wallet/src/components/CurrencyPicker.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Icon, Row, Text } from '@tlon/indigo-react'; +import { api } from '../lib/api'; +import { useSettings } from '../hooks/useSettings'; + +const CurrencyPicker = () => { + const { denomination, currencyRates } = useSettings(); + const switchCurrency = () => { + let newCurrency; + if (denomination === 'BTC') { + if ((currencyRates as any)['USD']) { + newCurrency = 'USD'; + } + } else if (denomination === 'USD') { + newCurrency = 'BTC'; + } + console.log({ newCurrency, denomination }); + let setCurrency = { + 'put-entry': { + desk: window.desk, + value: newCurrency, + 'entry-key': 'currency', + 'bucket-key': 'btc-wallet', + }, + }; + api.settingsEvent(setCurrency); + }; + + return ( + switchCurrency()}> + + + {denomination} + + + ); +}; + +export default CurrencyPicker; diff --git a/pkg/btc-wallet/src/components/Error.tsx b/pkg/btc-wallet/src/components/Error.tsx new file mode 100644 index 0000000000..cf41266df6 --- /dev/null +++ b/pkg/btc-wallet/src/components/Error.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Text } from '@tlon/indigo-react'; + +enum ErrorTypes { + 'cant-pay-ourselves' = 'Cannot pay ourselves', + 'no-comets' = 'Cannot pay comets', + 'no-dust' = 'Cannot send dust', + 'tx-being-signed' = 'Cannot pay when transaction is being signed', + 'insufficient-balance' = 'Insufficient confirmed balance', + 'broadcast-fail' = 'Transaction broadcast failed', + 'invalid-master-ticker' = 'Invalid master ticket', + 'invalid-signed' = 'Invalid signed bitcoin transaction', +} + +const Error = ({ + error, + fontSize, + ...rest +}: { + error: string; + fontSize?: string; +}) => ( + + { + (ErrorTypes as any)[ + Object.keys(ErrorTypes).filter((et) => et === error)[0] + ] + } + +); + +export default Error; diff --git a/pkg/btc-wallet/src/components/Header.tsx b/pkg/btc-wallet/src/components/Header.tsx new file mode 100644 index 0000000000..4e1422b887 --- /dev/null +++ b/pkg/btc-wallet/src/components/Header.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Box, Icon, Row, Text } from '@tlon/indigo-react'; +import { Link } from 'react-router-dom'; +import { useSettings } from '../hooks/useSettings'; + +const Header = ({ settings }: { settings: boolean }) => { + const { provider } = useSettings(); + let icon = settings ? 'X' : 'Adjust'; + let iconColor = settings ? 'black' : 'orange'; + let iconLink = settings ? '/' : '/settings'; + + let connection = null; + let badge = null; + if (!(provider && provider.connected)) { + connection = ( + + Provider Offline + + ); + + if (!settings) { + badge = ( + + ); + } + } + + return ( + + + + + + + Bitcoin + + + + {connection} + + + {badge} + + + + + + ); +}; + +export default Header; diff --git a/pkg/btc-wallet/src/components/ProviderModal.tsx b/pkg/btc-wallet/src/components/ProviderModal.tsx new file mode 100644 index 0000000000..7ce440ce22 --- /dev/null +++ b/pkg/btc-wallet/src/components/ProviderModal.tsx @@ -0,0 +1,166 @@ +import React, { useEffect, useState } from 'react'; +import { + Box, + Text, + Button, + StatelessTextInput, + Icon, + Row, + LoadingSpinner, +} from '@tlon/indigo-react'; +import { isValidPatp } from 'urbit-ob'; +import { api } from '../lib/api'; +import { useSettings } from '../hooks/useSettings'; + +enum providerStatuses { + checking, + failed, + ready, + initial = '', +} + +const ProviderModal = () => { + const { providerPerms } = useSettings(); + const [providerStatus, setProviderStatus] = useState( + providerStatuses.initial + ); + const [potentialProvider, setPotentialProvider] = useState(null); + const [provider, setProvider] = useState(null); + const [connecting, setConnecting] = useState(false); + + const checkProvider = (e: React.ChangeEvent) => { + // TODO: loading states + setProviderStatus(providerStatuses.initial); + let givenProvider = e.target.value; + if (isValidPatp(givenProvider)) { + let command = { + 'check-provider': givenProvider, + }; + setPotentialProvider(givenProvider); + setProviderStatus(providerStatuses.checking); + api.btcWalletCommand(command); + setTimeout(() => { + setProviderStatus(providerStatuses.failed); + }, 5000); + } + setProvider(givenProvider); + }; + + const submitProvider = () => { + if (providerStatus === providerStatuses.ready) { + let command = { + 'set-provider': provider, + }; + api.btcWalletCommand(command); + setConnecting(true); + } + }; + + useEffect(() => { + if (providerStatus !== providerStatuses.ready) { + if (providerPerms.provider === provider && providerPerms.permitted) { + setProviderStatus(providerStatuses.ready); + } + } + }, [providerStatus, providerPerms, provider, setProviderStatus]); + + let workingNode = null; + let workingColor = null; + let workingBg = null; + if (providerStatus === providerStatuses.ready) { + workingColor = 'green'; + workingBg = 'veryLightGreen'; + workingNode = ( + + + {provider} is a working provider node + + + ); + } else if (providerStatus === providerStatuses.failed) { + workingColor = 'red'; + workingBg = 'veryLightRed'; + workingNode = ( + + + {potentialProvider} is not a working provider node + + + ); + } + + return ( + + + + + Step 1 of 2: Set up Bitcoin Provider Node + + + + + In order to perform Bitcoin transaction in Landscape, you'll need + to set a provider node. A provider node is an urbit which maintains a + synced Bitcoin ledger. + + {' '} + Learn More + + + + + + Provider Node + + + + ) => + checkProvider(e) + } + /> + {providerStatus === providerStatuses.checking ? ( + + ) : null} + + {workingNode} + + + {connecting ? : null} + + + ); +}; + +export default ProviderModal; diff --git a/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx b/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx new file mode 100644 index 0000000000..1753d79beb --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/BridgeInvoice.tsx @@ -0,0 +1,223 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { + Box, + Icon, + StatelessTextInput as Input, + Row, + Text, + Button, + Col, + LoadingSpinner, +} from '@tlon/indigo-react'; +import Sigil from '../Sigil'; +import * as bitcoin from 'bitcoinjs-lib'; +import { isValidPatp } from 'urbit-ob'; +import Sent from './Sent'; +import Error from '../Error'; +import { satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; + +type Props = { + payee: string; + stopSending: () => void; + satsAmount: number; +}; + +const BridgeInvoice: React.FC = ({ payee, stopSending, satsAmount }) => { + const { error, currencyRates, fee, broadcastSuccess, denomination, psbt } = + useSettings(); + const [txHex, setTxHex] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(''); + const [broadcasting, setBroadcasting] = useState(false); + const invoiceRef = useRef(); + + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); + } + if (error !== '') { + setLocalError(error); + } + }, [error, broadcasting, setBroadcasting]); + + useEffect(() => { + window.open('https://bridge.urbit.org/?kind=btc&utx=' + psbt); + }); + + const broadCastTx = (hex: string) => { + let command = { + 'broadcast-tx': hex, + }; + return api.btcWalletCommand(command); + }; + + const sendBitcoin = (hex: string) => { + try { + bitcoin.Transaction.fromHex(hex); + broadCastTx(hex); + setBroadcasting(true); + } catch (e) { + setLocalError('invalid-signed'); + setBroadcasting(false); + } + }; + + const checkTxHex = (e: React.ChangeEvent) => { + setTxHex(e.target.value); + setReady(txHex.length > 0); + setLocalError(''); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (localError !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; + } + + const isShip = isValidPatp(payee); + + const icon = isShip ? ( + + ) : ( + + + + ); + + return ( + <> + {broadcastSuccess ? ( + + ) : ( + + + stopSending()} /> + + + + + {satsToCurrency(satsAmount, denomination, currencyRates)} + + + + {`${satsAmount} sats`} + + + {`Fee: ${satsToCurrency( + fee, + denomination, + currencyRates + )} (${fee} sats)`} + + + + You are paying + + + + {icon} + + {payee} + + + + + + Bridge signed transaction + + + + + Copy the signed transaction from Bridge + + + ) => checkTxHex(e)} + /> + {localError !== '' && ( + + + + )} + + + { + // @ts-ignore + broadcasting ? : null + } + + + )} + + ); +}; + +export default BridgeInvoice; diff --git a/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx new file mode 100644 index 0000000000..864425c59a --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/ExternalInvoice.tsx @@ -0,0 +1,275 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { + Box, + Icon, + StatelessTextInput as Input, + Row, + Text, + Button, + Col, + LoadingSpinner, +} from '@tlon/indigo-react'; +import Sigil from '../Sigil'; +import * as bitcoin from 'bitcoinjs-lib'; +import { isValidPatp } from 'urbit-ob'; +import Sent from './Sent'; +import Error from '../Error'; +import { copyToClipboard, satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; + +type Props = { + payee: string; + stopSending: () => void; + satsAmount: number; +}; + +const ExternalInvoice: React.FC = ({ + payee, + stopSending, + satsAmount, +}) => { + const { error, currencyRates, fee, broadcastSuccess, denomination, psbt } = + useSettings(); + const [txHex, setTxHex] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(''); + const [broadcasting, setBroadcasting] = useState(false); + const invoiceRef = useRef(); + + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); + } + if (error !== '') { + setLocalError(error); + } + }, [error, broadcasting, setBroadcasting]); + + const broadCastTx = (hex: string) => { + let command = { + 'broadcast-tx': hex, + }; + return api.btcWalletCommand(command); + }; + + const sendBitcoin = (hex: string) => { + try { + bitcoin.Transaction.fromHex(hex); + broadCastTx(hex); + setBroadcasting(true); + } catch (e) { + setLocalError('invalid-signed'); + setBroadcasting(false); + } + }; + + const checkTxHex = (e: React.ChangeEvent) => { + setTxHex(e.target.value); + setReady(txHex.length > 0); + setLocalError(''); + }; + + const copyPsbt = () => { + copyToClipboard(psbt); + }; + + const downloadPsbtFile = () => { + const blob = new Blob([psbt]); + const downloadURL = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = downloadURL; + link.setAttribute('download', 'urbit.psbt'); + document.body.appendChild(link); + link.click(); + link.parentNode.removeChild(link); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (localError !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; + } + + const isShip = isValidPatp(payee); + + const icon = isShip ? ( + + ) : ( + + + + ); + + return ( + <> + {broadcastSuccess ? ( + + ) : ( + + + stopSending()} /> + + + + + {satsToCurrency(satsAmount, denomination, currencyRates)} + + + + {`${satsAmount} sats`} + + + {`Fee: ${satsToCurrency( + fee, + denomination, + currencyRates + )} (${fee} sats)`} + + + + You are paying + + + + {icon} + + {payee} + + + + + + Partially-signed Bitcoin Transaction (PSBT) + + + + + + + + + + + Signed Tx + + ) => + checkTxHex(e) + } + /> + {localError !== '' && ( + + + + )} + + + + {broadcasting ? : null} + + + )} + + ); +}; + +export default ExternalInvoice; diff --git a/pkg/btc-wallet/src/components/Send/FeePicker.tsx b/pkg/btc-wallet/src/components/Send/FeePicker.tsx new file mode 100644 index 0000000000..b84ef01b7c --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/FeePicker.tsx @@ -0,0 +1,100 @@ +import React, { useEffect, useRef } from 'react'; +import { + Box, + Text, + Col, + StatelessRadioButtonField as RadioButton, + Label, +} from '@tlon/indigo-react'; +import { FeeChoices, feeLevels } from './Send'; + +type Props = { + feeChoices: FeeChoices; + feeValue: number; + setFeeValue: React.Dispatch; + feeDismiss: () => void; +}; + +const FeePicker: React.FC = ({ + feeChoices, + feeValue, + setFeeValue, + feeDismiss, +}) => { + const modalRef = useRef(null); + const clickDismiss = (e: any) => { + if (modalRef && !modalRef.current.contains(e.target)) { + feeDismiss(); + } + }; + useEffect(() => { + document.addEventListener('click', clickDismiss); + return () => { + document.removeEventListener('click', clickDismiss); + }; + }, []); + + return ( + + + Transaction Speed + + + { + setFeeValue(feeLevels.low); + feeDismiss(); + }} + > + + + + { + setFeeValue(feeLevels.mid); + feeDismiss(); + }} + > + + + + { + setFeeValue(feeLevels.high); + feeDismiss(); + }} + > + + + + + ); +}; + +export default FeePicker; diff --git a/pkg/btc-wallet/src/components/Send/Invoice.tsx b/pkg/btc-wallet/src/components/Send/Invoice.tsx new file mode 100644 index 0000000000..e26ca82c81 --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/Invoice.tsx @@ -0,0 +1,280 @@ +import React, { useState, useEffect } from 'react'; +import { + Box, + Icon, + StatelessTextInput as Input, + Row, + Text, + Button, + Col, + LoadingSpinner, +} from '@tlon/indigo-react'; +import * as kg from 'urbit-key-generation'; +import { patp2dec, isValidPatq, isValidPatp } from 'urbit-ob'; +import * as bitcoin from 'bitcoinjs-lib'; +import Sigil from '../Sigil'; +import Sent from './Sent'; +import { satsToCurrency } from '../../lib/util'; +import Error from '../Error'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; +import { UrbitWallet } from '../../types'; + +const BITCOIN_MAINNET_INFO = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bc', + bip32: { + public: 0x04b24746, + private: 0x04b2430c, + }, + pubKeyHash: 0x00, + scriptHash: 0x05, + wif: 0x80, +}; + +const BITCOIN_TESTNET_INFO = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'tb', + bip32: { + public: 0x045f1cf6, + private: 0x045f18bc, + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef, +}; + +type Props = { + stopSending: () => void; + payee: string; + satsAmount: number; +}; + +const Invoice: React.FC = ({ stopSending, payee, satsAmount }) => { + const { + error, + currencyRates, + psbt, + fee, + broadcastSuccess, + network, + denomination, + } = useSettings(); + const [masterTicket, setMasterTicket] = useState(''); + const [ready, setReady] = useState(false); + const [localError, setLocalError] = useState(error); + const [broadcasting, setBroadcasting] = useState(false); + + useEffect(() => { + if (broadcasting && localError !== '') { + setBroadcasting(false); + } + }, [error, broadcasting, setBroadcasting]); + + const broadCastTx = (psbtHex: string) => { + let command = { + 'broadcast-tx': psbtHex, + }; + return api.btcWalletCommand(command); + }; + + const sendBitcoin = (ticket: string, psbt: string) => { + const newPsbt = bitcoin.Psbt.fromBase64(psbt); + setBroadcasting(true); + kg.generateWallet({ + ticket, + ship: parseInt(patp2dec('~' + window.ship)), + }).then((urbitWallet: UrbitWallet) => { + // this wasn't being used, not clear why it was pulled out. + // const { xpub } = + // network === 'testnet' + // ? urbitWallet.bitcoinTestnet.keys + // : urbitWallet.bitcoinMainnet.keys; + + const { xprv: zprv } = urbitWallet.bitcoinMainnet.keys; + const { xprv: vprv } = urbitWallet.bitcoinTestnet.keys; + + const isTestnet = network === 'testnet'; + const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/"; + + const btcWallet = isTestnet + ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO) + : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO); + + try { + const hex = newPsbt.data.inputs + .reduce((psbt, input, idx) => { + // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0 + const path = input.bip32Derivation[0].path + .split(derivationPrefix) + .join(''); + const prv = btcWallet.derivePath(path).privateKey; + return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv)); + }, newPsbt) + .finalizeAllInputs() + .extractTransaction() + .toHex(); + + broadCastTx(hex); + } catch (e) { + setLocalError('invalid-master-ticket'); + setBroadcasting(false); + } + }); + }; + + const checkTicket = ({ + target: { value }, + }: React.ChangeEvent) => { + // TODO: port over bridge ticket validation logic + setMasterTicket(value); + setReady(isValidPatq(value)); + setLocalError(isValidPatq(value) ? '' : 'invalid-master-ticket'); + }; + + let inputColor = 'black'; + let inputBg = 'white'; + let inputBorder = 'lightGray'; + + if (error !== '') { + inputColor = 'red'; + inputBg = 'veryLightRed'; + inputBorder = 'red'; + } + + const isShip = isValidPatp(payee); + + const icon = isShip ? ( + + ) : ( + + + + ); + + return ( + <> + {broadcastSuccess ? ( + + ) : ( + + + stopSending()} /> + + + + + {satsToCurrency(satsAmount, denomination, currencyRates)} + + + + {`${satsAmount} sats`} + + + {`Fee: ${satsToCurrency( + fee, + denomination, + currencyRates + )} (${fee} sats)`} + + + + You are paying + + + + {icon} + + {payee} + + + + + + Ticket + + value.replace(/[^~-]+/g, '••••••')} + placeholder="••••••-••••••-••••••-••••••" + autoCapitalize="none" + autoCorrect="off" + color={inputColor} + backgroundColor={inputBg} + borderColor={inputBorder} + onChange={(e: React.ChangeEvent) => + checkTicket(e) + } + /> + + {error !== '' && ( + + + + )} + + + { + // @ts-ignore + broadcasting ? : null + } + + + )} + + ); +}; + +export default Invoice; diff --git a/pkg/btc-wallet/src/components/Send/Send.tsx b/pkg/btc-wallet/src/components/Send/Send.tsx new file mode 100644 index 0000000000..22c5ab3c3d --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/Send.tsx @@ -0,0 +1,512 @@ +import React, { useEffect, useState } from 'react'; +import { + Box, + Icon, + StatelessTextInput as Input, + Row, + Text, + Button, + Col, + LoadingSpinner, +} from '@tlon/indigo-react'; +import Invoice from './Invoice'; +import BridgeInvoice from './BridgeInvoice'; +import ExternalInvoice from './ExternalInvoice'; +import FeePicker from './FeePicker'; +import Error from '../Error'; +import Signer from './Signer'; +import { validate } from 'bitcoin-address-validation'; +import * as ob from 'urbit-ob'; +import { useSettings } from '../../hooks/useSettings'; +import { api } from '../../lib/api'; +import { deSig } from '../../lib/util'; + +enum focusFields { + payee, + currency, + sats, + note, + empty = '', +} + +export enum feeLevels { + low, + mid, + high, +} + +export enum signMethods { + bridge = 'bridge', + masterTicket = 'masterTicket', + external = 'external', +} + +enum payeeTypes { + ship, + address, + initial = '', +} + +export type FeeChoices = { + [feeLevels.low]: [number, number]; + [feeLevels.mid]: [number, number]; + [feeLevels.high]: [number, number]; +}; + +type Props = { + stopSending: () => void; + value: string; + conversion: number; +}; + +const Send: React.FC = ({ stopSending, value, conversion }) => { + const { error, setError, network, psbt, denomination, shipWallets } = + useSettings(); + const [signing, setSigning] = useState(false); + const [denomAmount, setDenomAmount] = useState(0.0); + const [satsAmount, setSatsAmount] = useState(0); + const [payee, setPayee] = useState(''); + const [checkingPatp, setCheckingPatp] = useState(false); + const [payeeType, setPayeeType] = useState(payeeTypes.initial); + const [ready, setReady] = useState(false); + const [validPayee, setValidPayee] = useState(false); + const [focusedField, setFocusedField] = useState(focusFields.empty); + const [feeChoices, setFeeChoices] = useState({ + [feeLevels.low]: [10, 1], + [feeLevels.mid]: [10, 1], + [feeLevels.high]: [10, 1], + }); + const [feeValue, setFeeValue] = useState(feeLevels.mid); + const [showFeePicker, setShowFeePicker] = useState(false); + const [note, setNote] = useState(''); + const [choosingSignMethod, setChoosingSignMethod] = useState(false); + const [signMethod, setSignMethod] = useState(signMethods.bridge); + + const feeDismiss = () => { + setShowFeePicker(false); + }; + + const handleSetSignMethod = (signMethod: signMethods) => { + setSignMethod(signMethod); + setChoosingSignMethod(false); + }; + + const checkPayee = (e: React.ChangeEvent) => { + setError(''); + + const validPatPCommand = (validPatP: string) => { + let command = { 'check-payee': validPatP }; + api.btcWalletCommand(command); + setTimeout(() => { + setCheckingPatp(false); + }, 5000); + setCheckingPatp(true); + setPayeeType(payeeTypes.ship); + setPayee(validPatP); + }; + + let payeeReceived = e.target.value; + let isPatp = ob.isValidPatp(`~${deSig(payeeReceived)}`); + let isAddress = validate(payeeReceived); + if (isPatp) { + validPatPCommand(`~${deSig(payeeReceived)}`); + } else if (isAddress) { + setPayee(payeeReceived); + setReady(true); + setCheckingPatp(false); + setPayeeType(payeeTypes.address); + setValidPayee(true); + } else { + setPayee(payeeReceived); + setReady(false); + setCheckingPatp(false); + setPayeeType(payeeTypes.initial); + setValidPayee(false); + } + }; + + const toggleSignMethod = () => { + setChoosingSignMethod(!choosingSignMethod); + }; + + const initPayment = () => { + if (payeeType === payeeTypes.ship) { + let command = { + 'init-payment': { + payee, + value: satsAmount, + feyb: feeChoices[feeValue][1], + note: note || null, + }, + }; + + api.btcWalletCommand(command).then(() => setSigning(true)); + } else if (payeeType === payeeTypes.address) { + let command = { + 'init-payment-external': { + address: payee, + value: satsAmount, + feyb: 1, + note: note || null, + }, + }; + api.btcWalletCommand(command).then(() => setSigning(true)); + } + }; + + useEffect(() => { + if (network === 'bitcoin') { + let url = 'https://bitcoiner.live/api/fees/estimates/latest'; + fetch(url) + .then((res) => res.json()) + .then((n) => { + // let estimates = Object.keys(n.estimates); + // let mid = Math.floor(estimates.length / 2); + // let high = estimates.length - 1; + setFeeChoices({ + [feeLevels.high]: [30, n.estimates[30]['sat_per_vbyte']], + [feeLevels.mid]: [180, n.estimates[180]['sat_per_vbyte']], + [feeLevels.low]: [360, n.estimates[360]['sat_per_vbyte']], + }); + }); + } + }, []); + + useEffect(() => { + if (!ready && !checkingPatp) { + if (shipWallets.payee === payee.slice(1) && shipWallets.hasWallet) { + setReady(true); + setCheckingPatp(false); + setValidPayee(true); + } + } + }, [ready, checkingPatp, shipWallets, payee]); + + let payeeColor = 'black'; + let payeeBg = 'white'; + let payeeBorder = 'lightGray'; + if (error) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } else if (focusedField === focusFields.payee && validPayee) { + payeeColor = 'green'; + payeeBorder = 'green'; + payeeBg = 'veryLightGreen'; + } else if (focusedField !== focusFields.payee && validPayee) { + payeeColor = 'blue'; + payeeBorder = 'white'; + payeeBg = 'white'; + } else if (focusedField !== focusFields.payee && !validPayee) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } else if ( + focusedField === focusFields.payee && + !validPayee && + !checkingPatp && + payeeType === payeeTypes.ship + ) { + payeeColor = 'red'; + payeeBorder = 'red'; + payeeBg = 'veryLightRed'; + } + + const signReady = ready && satsAmount > 0 && !signing; + + let invoice = null; + + switch (signMethod) { + case signMethods.masterTicket: { + invoice = ( + + ); + break; + } + case signMethods.bridge: { + invoice = ( + + ); + break; + } + case signMethods.external: { + invoice = ( + + ); + break; + } + default: + break; + } + + return ( + <> + {signing && psbt ? ( + invoice + ) : ( + + + + + Send BTC + + + {value} + + stopSending()} /> + + + + + To + + {checkingPatp ? ( + + ) : null} + + { + setFocusedField(focusFields.payee); + }} + onBlur={() => { + setFocusedField(focusFields.empty); + }} + color={payeeColor} + backgroundColor={payeeBg} + borderColor={payeeBorder} + ml={2} + flexGrow="1" + fontSize="14px" + placeholder="~sampel-palnet or BTC address" + value={payee} + fontFamily="mono" + disabled={signing} + onChange={(e: React.ChangeEvent) => + checkPayee(e) + } + /> + + {error && ( + + {/* yes this is a hack */} + + + + )} + + + Amount + + { + setFocusedField(focusFields.currency); + }} + onBlur={() => { + setFocusedField(focusFields.empty); + }} + fontSize="14px" + width="100%" + type="number" + borderColor={ + focusedField === focusFields.currency ? 'lightGray' : 'none' + } + disabled={signing} + value={denomAmount} + onChange={(e: React.ChangeEvent) => { + setDenomAmount(parseFloat(e.target.value)); + setSatsAmount( + Math.round( + (parseFloat(e.target.value) / conversion) * 100000000 + ) + ); + }} + /> + + {denomination} + + + + {/* yes this is a hack */} + + { + setFocusedField(focusFields.sats); + }} + onBlur={() => { + setFocusedField(focusFields.empty); + }} + fontSize="14px" + width="100%" + type="number" + borderColor={ + focusedField === focusFields.sats ? 'lightGray' : 'none' + } + disabled={signing} + value={satsAmount} + onChange={(e: React.ChangeEvent) => { + setDenomAmount( + parseFloat(e.target.value) * (conversion / 100000000) + ); + setSatsAmount(parseInt(e.target.value, 10)); + }} + /> + + sats + + + + + Fee + + + + {feeChoices[feeValue][1]} sats/vbyte + + + + + + {!showFeePicker ? null : ( + + )} + + + + Note + + { + setFocusedField(focusFields.note); + }} + onBlur={() => { + setFocusedField(focusFields.empty); + }} + fontSize="14px" + width="100%" + placeholder="What's this for?" + type="text" + borderColor={ + focusedField === focusFields.note ? 'lightGray' : 'none' + } + disabled={signing} + value={note} + onChange={(e: React.ChangeEvent) => { + setNote(e.target.value); + }} + /> + + + + {!(signing && !error) ? null : ( + + )} + + + + {signMethod === signMethods.masterTicket && ( + + + + We recommend that you sign transactions using Bridge to protect + your master ticket. + + + )} + + )} + + ); +}; + +export default Send; diff --git a/pkg/btc-wallet/src/components/Send/Sent.tsx b/pkg/btc-wallet/src/components/Send/Sent.tsx new file mode 100644 index 0000000000..1990d4b285 --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/Sent.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Icon, Row, Col, Center, Text } from '@tlon/indigo-react'; +import { satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; + +type Props = { + payee: string; + stopSending: () => void; + satsAmount: number; +}; + +const Sent: React.FC = ({ payee, stopSending, satsAmount }) => { + const { denomination, currencyRates } = useSettings(); + return ( + + + + +
+ {`You sent BTC to ${payee}`} +
+
+ + {satsToCurrency(satsAmount, denomination, currencyRates)} + + {`${satsAmount} sats`} +
+ + ); +}; + +export default Sent; diff --git a/pkg/btc-wallet/src/components/Send/Signer.tsx b/pkg/btc-wallet/src/components/Send/Signer.tsx new file mode 100644 index 0000000000..f783011727 --- /dev/null +++ b/pkg/btc-wallet/src/components/Send/Signer.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { Box, Button, Icon, Row } from '@tlon/indigo-react'; +import { signMethods } from './Send'; + +const signMethodLabels = { + bridge: 'Sign with Bridge', + masterTicket: 'Sign with Master Ticket', + external: 'Sign Externally (PSBT)', +}; + +type Props = { + signReady: boolean; + initPayment: () => void; + choosingSignMethod: boolean; + signMethod: signMethods; + setSignMethod: (arg: signMethods) => void; +}; + +const Signer: React.FC = ({ + signReady, + initPayment, + choosingSignMethod, + signMethod, + setSignMethod, +}) => { + return choosingSignMethod ? ( + + {Object.keys(signMethods).map((method) => ( + + + {signMethod === (signMethods as any)[method] && ( + + )} + + ))} + + ) : ( + + ); +}; + +export default Signer; diff --git a/pkg/btc-wallet/src/components/Settings.tsx b/pkg/btc-wallet/src/components/Settings.tsx new file mode 100644 index 0000000000..3a376f506d --- /dev/null +++ b/pkg/btc-wallet/src/components/Settings.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { Row, Text, Button, Col } from '@tlon/indigo-react'; +import { useSettings } from '../hooks/useSettings'; +import { api } from '../lib/api'; + +const Settings = () => { + const { wallet, provider } = useSettings(); + + const changeProvider = () => { + api.btcWalletCommand({ 'set-provider': null }); + window.location.reload(); + }; + + const replaceWallet = () => { + api.btcWalletCommand({ + 'delete-wallet': wallet, + }); + }; + + let connColor = 'red'; + let connBackground = 'veryLightRed'; + let conn = 'Offline'; + let host = ''; + if (provider) { + if (provider.connected) conn = 'Connected'; + if (provider.host) host = provider.host; + if (provider.connected && provider.host) { + connColor = 'orange'; + connBackground = 'lightOrange'; + } + } + + return ( + + + + XPub Derivation + + + + + {wallet} + + + + + + + + BTC Node Provider + + + + + ~{host} + + + {conn} + + + + + + + ); +}; + +export default Settings; diff --git a/pkg/btc-wallet/src/js/components/lib/sigil.js b/pkg/btc-wallet/src/components/Sigil.tsx similarity index 78% rename from pkg/btc-wallet/src/js/components/lib/sigil.js rename to pkg/btc-wallet/src/components/Sigil.tsx index e47eff93f1..5de8569bdf 100644 --- a/pkg/btc-wallet/src/js/components/lib/sigil.js +++ b/pkg/btc-wallet/src/components/Sigil.tsx @@ -2,11 +2,11 @@ import React, { memo } from 'react'; import { sigil, reactRenderer } from '@tlon/sigil-js'; import { Box } from '@tlon/indigo-react'; -export const foregroundFromBackground = (background) => { +export const foregroundFromBackground = (background: string) => { const rgb = { r: parseInt(background.slice(1, 3), 16), g: parseInt(background.slice(3, 5), 16), - b: parseInt(background.slice(5, 7), 16) + b: parseInt(background.slice(5, 7), 16), }; const brightness = (299 * rgb.r + 587 * rgb.g + 114 * rgb.b) / 1000; const whiteBrightness = 255; @@ -14,7 +14,19 @@ export const foregroundFromBackground = (background) => { return whiteBrightness - brightness < 50 ? 'black' : 'white'; }; -export const Sigil = memo( +type Props = { + classes?: string; + color: string; + foreground?: string; + ship: string; + size: number; + svgClass?: string; + icon?: boolean; + padding?: number; + display?: string; +}; + +const Sigil: React.FC = memo( ({ classes = '', color, @@ -24,7 +36,7 @@ export const Sigil = memo( svgClass = '', icon = false, padding = 0, - display = 'inline-block' + display = 'inline-block', }) => { const innerSize = Number(size) - 2 * padding; const paddingPx = `${padding}px`; @@ -55,7 +67,7 @@ export const Sigil = memo( size: innerSize, icon, colors: [color, foregroundColor], - class: svgClass + class: svgClass, })} ); diff --git a/pkg/btc-wallet/src/components/StartupModal.tsx b/pkg/btc-wallet/src/components/StartupModal.tsx new file mode 100644 index 0000000000..e21f6ce2cc --- /dev/null +++ b/pkg/btc-wallet/src/components/StartupModal.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Box } from '@tlon/indigo-react'; +import WalletModal from './WalletModal'; +import ProviderModal from './ProviderModal'; +import { useSettings } from '../hooks/useSettings'; + +const StartupModal: React.FC = () => { + const { wallet, provider } = useSettings(); + let modal = null; + + if (wallet && provider) { + return null; + } else if (!provider) { + modal = ; + } else if (!wallet) { + modal = ; + } + return ( + + + {modal} + + + ); +}; + +export default StartupModal; diff --git a/pkg/btc-wallet/src/components/Transactions/Transaction.tsx b/pkg/btc-wallet/src/components/Transactions/Transaction.tsx new file mode 100644 index 0000000000..fd0df48ca4 --- /dev/null +++ b/pkg/btc-wallet/src/components/Transactions/Transaction.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { Box, Row, Text, Col } from '@tlon/indigo-react'; +import _ from 'lodash'; +import TxAction from './TxAction'; +import TxCounterparty from './TxCounterparty'; +import { satsToCurrency } from '../../lib/util'; +import { useSettings } from '../../hooks/useSettings'; +import { Transaction as TransactionType } from '../../types'; + +const Transaction = ({ tx }: { tx: TransactionType }) => { + const { denomination, currencyRates } = useSettings(); + const pending = !tx.recvd; + + let weSent = _.find(tx.inputs, (input) => { + return input.ship === window.ship; + }); + let weRecv = tx.outputs.every((output) => { + return output.ship === window.ship; + }); + + let action: 'sent' | 'recv' | 'fail' = weRecv + ? 'recv' + : weSent + ? 'sent' + : 'recv'; + + let counterShip = null; + let counterAddress = null; + let value; + let sign; + + if (action === 'sent') { + let counter = _.find(tx.outputs, (output) => { + return output.ship !== window.ship; + }); + counterShip = _.get(counter, 'ship', null); + counterAddress = _.get(counter, 'val.address', null); + value = _.get(counter, 'val.value', null); + sign = '-'; + } else if (action === 'recv') { + value = _.reduce( + tx.outputs, + (sum, output) => { + if (output.ship === window.ship) { + return sum + output.val.value; + } else { + return sum; + } + }, + 0 + ); + + if (weSent && weRecv) { + counterAddress = _.get( + _.find(tx.inputs, (input) => { + return input.ship === window.ship; + }), + 'val.address', + null + ); + } else { + let counter = _.find(tx.inputs, (input) => { + return input.ship !== window.ship; + }); + counterShip = _.get(counter, 'ship', null); + counterAddress = _.get(counter, 'val.address', null); + } + sign = ''; + } + + let currencyValue = sign + satsToCurrency(value, denomination, currencyRates); + + const failure = Boolean(tx.failure); + if (failure) action = 'fail'; + + const txid = tx.txid.dat.slice(2).replaceAll('.', ''); + + return ( + + + + + {sign} + {value} sats + + + + + + {currencyValue} + + + ); +}; + +export default Transaction; diff --git a/pkg/btc-wallet/src/components/Transactions/Transactions.tsx b/pkg/btc-wallet/src/components/Transactions/Transactions.tsx new file mode 100644 index 0000000000..0eec82fb96 --- /dev/null +++ b/pkg/btc-wallet/src/components/Transactions/Transactions.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Box, Text, Col } from '@tlon/indigo-react'; +import Transaction from './Transaction'; +import { useSettings } from '../../hooks/useSettings'; + +const Transactions = () => { + const { history } = useSettings(); + if (!history || history.length <= 0) { + return ( + + + No Transactions Yet + + + ); + } else { + return ( + + {history.map((tx, i) => { + return ; + })} + + ); + } +}; + +export default Transactions; diff --git a/pkg/btc-wallet/src/components/Transactions/TxAction.tsx b/pkg/btc-wallet/src/components/Transactions/TxAction.tsx new file mode 100644 index 0000000000..6ed9e954d4 --- /dev/null +++ b/pkg/btc-wallet/src/components/Transactions/TxAction.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { Box, Icon, Row, Text, LoadingSpinner } from '@tlon/indigo-react'; +import { useSettings } from '../../hooks/useSettings'; + +type Props = { + action: 'sent' | 'recv' | 'fail'; + pending: boolean; + txid: string; +}; + +const TxAction: React.FC = ({ action, pending, txid }) => { + const { network } = useSettings(); + const leftIcon = + action === 'sent' + ? 'ArrowSouth' + : action === 'recv' + ? 'ArrowNorth' + : action === 'fail' + ? 'X' + : 'NullIcon'; + + const actionColor = + action === 'sent' + ? 'sentBlue' + : action === 'recv' + ? 'recvGreen' + : action === 'fail' + ? 'gray' + : 'red'; + + const actionText = + action === 'sent' && !pending + ? 'Sent BTC' + : action === 'sent' && pending + ? 'Sending BTC' + : action === 'recv' && !pending + ? 'Received BTC' + : action === 'recv' && pending + ? 'Receiving BTC' + : action === 'fail' + ? 'Failed' + : 'error'; + + const pendingSpinner = !pending ? null : ( + + ); + + const url = + network === 'testnet' + ? `http://blockstream.info/testnet/tx/${txid}` + : `http://blockstream.info/tx/${txid}`; + + return ( + + + + + + {actionText} + + + + + {pendingSpinner} + + ); +}; + +export default TxAction; diff --git a/pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx b/pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx new file mode 100644 index 0000000000..8bbdad07dc --- /dev/null +++ b/pkg/btc-wallet/src/components/Transactions/TxCounterparty.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Box, Icon, Row, Text } from '@tlon/indigo-react'; +import Sigil from '../Sigil'; + +type Props = { + ship: string; + address: string; +}; + +const TxCounterparty: React.FC = ({ ship, address }) => { + const icon = ship ? ( + + ) : ( + + + + ); + const addressText = !address + ? '' + : address.slice(0, 6) + '...' + address.slice(-6); + const text = ship ? `~${ship}` : addressText; + + return ( + + {icon} + + {text} + + + ); +}; + +export default TxCounterparty; diff --git a/pkg/btc-wallet/src/components/WalletModal.tsx b/pkg/btc-wallet/src/components/WalletModal.tsx new file mode 100644 index 0000000000..e46c865d7e --- /dev/null +++ b/pkg/btc-wallet/src/components/WalletModal.tsx @@ -0,0 +1,268 @@ +import React, { useState } from 'react'; +import { + Box, + Text, + Button, + StatelessTextInput, + Icon, + Row, + LoadingSpinner, +} from '@tlon/indigo-react'; +import { patp2dec, isValidPatq } from 'urbit-ob'; +import * as kg from 'urbit-key-generation'; +import { useSettings } from '../hooks/useSettings'; +import { api } from '../lib/api'; +import { UrbitWallet } from '../types'; + +const WalletModal: React.FC = () => { + const { network } = useSettings(); + const [mode, setMode] = useState('xpub'); + const [masterTicket, setMasterTicket] = useState(''); + const [confirmedMasterTicket, setConfirmedMasterTicket] = useState(''); + const [xpub, setXpub] = useState(''); + const [readyToSubmit, setReadyToSubmit] = useState(false); + const [processingSubmission, setProcessingSubmission] = useState(false); + const [confirmingMasterTicket, setConfirmingMasterTicket] = useState(false); + const [error, setError] = useState(false); + + const checkTicket = ({ + target: { value }, + }: React.ChangeEvent) => { + // TODO: port over bridge ticket validation logic + if (confirmingMasterTicket) { + setConfirmedMasterTicket(value); + setReadyToSubmit(isValidPatq(value)); + } else { + setMasterTicket(value); + setReadyToSubmit(isValidPatq(value)); + } + }; + + const checkXPub = ({ + target: { value: xpubGiven }, + }: React.ChangeEvent) => { + setXpub(xpubGiven); + setReadyToSubmit(xpubGiven.length > 0); + }; + + const submitXPub = (givenXpub: string) => { + type AddWalletCommand = { + 'add-wallet': { + xpub: string; + fprint: number[]; + 'scan-to': number | null; + 'max-gap': number; + confs: number; + }; + }; + const command: AddWalletCommand = { + 'add-wallet': { + xpub: givenXpub, + fprint: [4, 0], + 'scan-to': null, + 'max-gap': 8, + confs: 1, + }, + }; + api.btcWalletCommand(command); + setProcessingSubmission(true); + }; + + const submitMasterTicket = (ticket: string) => { + setProcessingSubmission(true); + kg.generateWallet({ + ticket, + ship: parseInt(patp2dec('~' + window.ship)), + }).then((urbitWallet: UrbitWallet) => { + const { xpub: xpubFromWallet } = + network === 'testnet' + ? urbitWallet.bitcoinTestnet.keys + : urbitWallet.bitcoinMainnet.keys; + + submitXPub(xpubFromWallet); + }); + }; + + const buttonDisabled = !readyToSubmit || processingSubmission; + const inputDisabled = processingSubmission; + // const processingSpinner = !processingSubmission ? null : ; + + if (mode === 'masterTicket') { + return ( + + + + + Step 2 of 2: Import your extended public key + + + + + + We recommend that you import your wallet using Bridge to protect + your master ticket. + + + + {confirmingMasterTicket && ( + { + setConfirmingMasterTicket(false); + setMasterTicket(''); + setConfirmedMasterTicket(''); + setError(false); + }} + /> + )} + + {confirmingMasterTicket ? 'Confirm Master Ticket' : 'Master Ticket'} + + + + value.replace(/[^~-]+/g, '••••••')} + placeholder="••••••-••••••-••••••-••••••" + autoCapitalize="none" + autoCorrect="off" + onChange={(e: React.ChangeEvent) => + checkTicket(e) + } + /> + {!inputDisabled ? null : } + + {error && ( + + + Master tickets do not match + + + )} + + + + + + ); + } else if (mode === 'xpub') { + return ( + + + + + Step 2 of 2: Import your extended public key + + + + + Visit{' '} + + bridge.urbit.org + {' '} + to obtain your key + + + + + Extended Public Key (XPub) + + + + ) => checkXPub(e)} + mr={1} + /> + {!inputDisabled ? null : } + + + { + if (inputDisabled) return; + setMode('masterTicket'); + setXpub(''); + setMasterTicket(''); + setReadyToSubmit(false); + }} + > + Import using master ticket -> + + + + + ); + } +}; + +export default WalletModal; diff --git a/pkg/btc-wallet/src/components/Warning.tsx b/pkg/btc-wallet/src/components/Warning.tsx new file mode 100644 index 0000000000..466f144142 --- /dev/null +++ b/pkg/btc-wallet/src/components/Warning.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { Box, Text, Button, Col, Anchor } from '@tlon/indigo-react'; +import { api } from '../lib/api'; +import { useSettings } from '../hooks/useSettings'; + +const Warning = () => { + const { setShowWarning } = useSettings(); + const understand = () => { + setShowWarning(false); + let removeWarning = { + 'put-entry': { + value: false, + desk: window.desk, + 'entry-key': 'warning', + 'bucket-key': 'btc-wallet', + }, + }; + api.settingsEvent(removeWarning); + }; + + return ( + + + + Warning! + +
+ + Be safe while using this wallet, and be sure to store responsible + amounts of BTC. + + + Always ensure that the checksum of the wallet matches that of the + wallet's repo. + +
+ + + Learn more on urbit.org + + + + +
+ ); +}; + +export default Warning; diff --git a/pkg/btc-wallet/src/hooks/useSettings.tsx b/pkg/btc-wallet/src/hooks/useSettings.tsx new file mode 100644 index 0000000000..8460c3998c --- /dev/null +++ b/pkg/btc-wallet/src/hooks/useSettings.tsx @@ -0,0 +1,397 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import _ from 'lodash'; +import { api } from '../lib/api'; +import { mapDenominationToSymbol, reduceHistory } from '../lib/util'; +import { + CurrencyRate, + Denomination, + Network, + Provider, + ProviderPerms, + ScanProgress, + ShipWallets, + Transaction, + TxidType, +} from '../types'; + +type SettingsContextType = { + network: Network; + setNetwork: React.Dispatch>; + loadedBtc: boolean; + setLoadedBtc: React.Dispatch>; + loadedSettings: boolean; + setLoadedSettings: React.Dispatch>; + loaded: boolean; + setLoaded: React.Dispatch>; + providerPerms: ProviderPerms; + setProviderPerms: React.Dispatch>; + shipWallets: ShipWallets; + setShipWallets: React.Dispatch>; + provider: Provider; + setProvider: React.Dispatch>; + wallet: string | null; + setWallet: React.Dispatch>; + confirmedBalance: number; + setConfirmedBalance: React.Dispatch>; + unconfirmedBalance: number; + setUnconfirmedBalance: React.Dispatch>; + btcState: any; + setBtcState: React.Dispatch>; + history: Transaction[]; + setHistory: React.Dispatch>; + fee: number; + setFee: React.Dispatch>; + psbt: string; + setPsbt: React.Dispatch>; + address: string | null; + setAddress: React.Dispatch>; + currencyRates: CurrencyRate; + setCurrencyRates: React.Dispatch>; + denomination: Denomination; + setDenomination: React.Dispatch>; + showWarning: boolean; + setShowWarning: React.Dispatch>; + error: string; + setError: React.Dispatch>; + broadcastSuccess: boolean; + setBroadcastSuccess: React.Dispatch>; + scanProgress: ScanProgress; + setScanProgress: React.Dispatch>; +}; + +export const SettingsContext = createContext({ + network: 'bitcoin', + setNetwork: () => {}, + loadedBtc: false, + setLoadedBtc: () => {}, + loadedSettings: true, + setLoadedSettings: () => {}, + loaded: false, + setLoaded: () => {}, + providerPerms: { provider: '', permitted: false }, + setProviderPerms: () => {}, + shipWallets: { payee: '', hasWallet: false }, + setShipWallets: () => {}, + provider: null, + setProvider: () => {}, + wallet: null, + setWallet: () => {}, + confirmedBalance: 0, + setConfirmedBalance: () => {}, + unconfirmedBalance: 0, + setUnconfirmedBalance: () => {}, + btcState: null, + setBtcState: () => {}, + history: [], + setHistory: () => {}, + fee: 0, + setFee: () => {}, + psbt: '', + setPsbt: () => {}, + address: null, + setAddress: () => {}, + currencyRates: { + BTC: { last: 1, symbol: 'BTC' }, + }, + setCurrencyRates: () => {}, + denomination: 'BTC', + setDenomination: () => {}, + showWarning: false, + setShowWarning: () => {}, + error: '', + setError: () => {}, + broadcastSuccess: false, + setBroadcastSuccess: () => {}, + scanProgress: { main: null, change: null }, + setScanProgress: () => {}, +}); + +type Props = { + channel: { setOnChannelError: (arg: () => void) => void }; +}; + +export const SettingsProvider: React.FC = ({ channel, children }) => { + const [network, setNetwork] = useState('bitcoin'); + const [channelData, setChannelData] = useState(null); + const [loadedBtc, setLoadedBtc] = useState(false); + const [loadedSettings, setLoadedSettings] = useState(false); + const [loaded, setLoaded] = useState(false); + const [providerPerms, setProviderPerms] = useState({ + provider: '', + permitted: false, + }); + const [shipWallets, setShipWallets] = useState({ + payee: '', + hasWallet: false, + }); + const [provider, setProvider] = useState(null); + const [wallet, setWallet] = useState(null); + const [confirmedBalance, setConfirmedBalance] = useState(0); + const [unconfirmedBalance, setUnconfirmedBalance] = useState(0); + const [btcState, setBtcState] = useState(null); + const [history, setHistory] = useState([]); + const [psbt, setPsbt] = useState(''); + const [fee, setFee] = useState(0); + const [address, setAddress] = useState(null); + const [currencyRates, setCurrencyRates] = useState({ + BTC: { last: 1, symbol: 'BTC' }, + }); + const [denomination, setDenomination] = useState('BTC'); + const [showWarning, setShowWarning] = useState(false); + const [error, setError] = useState(''); + const [broadcastSuccess, setBroadcastSuccess] = useState(false); + const [scanProgress, setScanProgress] = useState({ + main: null, + change: null, + }); + + const { Provider } = SettingsContext; + + const success = (event: any) => { + console.log({ event }); + setChannelData(event); + }; + const fail = (error: any) => console.log({ error }); + + const initializeBtcWallet = () => { + api.bind('/all', 'PUT', api.ship, 'btc-wallet', success, fail); + }; + + const initializeSettings = () => { + let app = 'settings-store'; + let path = `/bucket/${window.desk}btc-wallet`; + + fetch(`/~/scry/${app}${path}.json`) + .then((res) => res.json()) + .then((n) => { + let data = _.get(n, 'initial', false); + let bucketData = _.get(n, 'bucket', false); + if (data) { + setChannelData(n); + } + if (bucketData) { + let bucketWarning = _.get(n, 'bucket.warning', -1); + if (bucketWarning !== -1) { + setShowWarning(bucketWarning); + } + let bucketCurrency = _.get(n, 'bucket.currency', -1); + if (bucketCurrency !== -1) { + setDenomination(bucketCurrency); + } + setLoadedSettings(true); + if (loadedBtc) { + setLoaded(true); + } + } + }); + + api.bind(path, 'PUT', api.ship, app, success, fail); + }; + + const initializeCurrencyPoll = () => { + fetch('https://blockchain.info/ticker') + .then((res) => res.json()) + .then((n) => { + const newCurrencyRates: any = currencyRates; + for (let c in n) { + newCurrencyRates[c] = n[c]; + newCurrencyRates[c].symbol = mapDenominationToSymbol(c); + } + setCurrencyRates(newCurrencyRates); + setTimeout(() => initializeCurrencyPoll(), 1000 * 60 * 15); + }); + }; + + const start = () => { + if (api.ship) { + initializeBtcWallet(); + initializeSettings(); + initializeCurrencyPoll(); + } + }; + + const handleNewTx = (newTx: Transaction) => { + const { txid, recvd } = newTx; + let old = _.findIndex(history, (h: Transaction) => { + return h.txid.dat === txid.dat && h.txid.wid === txid.wid; + }); + if (old !== -1) { + const newHistory = history.filter((_, i) => i !== old); + setHistory(newHistory); + } + if (recvd === null && old === -1) { + const newHistory = [...history, newTx]; + setHistory(newHistory); + } else if (recvd !== null && old === -1) { + // we expect history to have null recvd values first, and the rest in + // descending order + let insertionIndex = _.findIndex(history, (h: Transaction) => { + return h.recvd < recvd && h.recvd !== null; + }); + const newHistory = history.map((o, i) => + i === insertionIndex ? newTx : o + ); + setHistory(newHistory); + } + }; + + const handleCancelTx = ({ wid, dat }: TxidType) => { + let entryIndex = _.findIndex(history, (h: Transaction) => { + return wid === h.txid.wid && dat === h.txid.dat; + }); + if (entryIndex > -1) { + history[entryIndex].failure = true; + } + }; + + useEffect(() => { + const initialData = channelData?.data?.initial; + const putEntryData = channelData?.data?.['settings-event']?.['put-entry']; + const btcStateData = channelData?.data?.['btc-state']; + const changeProvider = channelData?.data?.['change-provider']; + const newTx = channelData?.data?.['new-tx']; + const providerStatus = channelData?.data?.providerStatus; + const checkPayee = channelData?.data?.checkPayee; + const changeWallet = channelData?.data?.changeWallet; + const psbtData = channelData?.data.psbt; + const cancelTx = channelData?.data['cancel-tx']; + const addressData = channelData?.data?.address; + const balanceData = channelData?.data?.balance; + const errorData = channelData?.data?.error; + const broadcastSuccessData = channelData?.data?.['broadcast-success']; + const broadcastFailData = channelData?.data?.['broadcast-fail']; + const scanProgressData = channelData?.data?.['scan-progress']; + if (initialData) { + setProvider(initialData.provider); + setWallet(initialData.wallet); + setConfirmedBalance(_.get(initialData.balance, 'confirmed', null)); + setUnconfirmedBalance(_.get(initialData.balance, 'unconfirmed', null)); + setBtcState(initialData['btc-state']); + setHistory(reduceHistory(initialData.history)); + setAddress(initialData.address); + setLoadedBtc(true); + if (loadedSettings) { + setLoaded(true); + } + } + if (putEntryData && putEntryData?.['entry-key'] === 'currency') { + setDenomination(putEntryData.value); + } + if (putEntryData && putEntryData?.['entry-key'] === 'warning') { + setShowWarning(putEntryData.value); + } + if (btcStateData) { + setBtcState(btcStateData); + } + if (changeProvider) { + setProvider(changeProvider); + } + if (newTx) { + handleNewTx(newTx); + } + if (providerStatus) { + let newProviderPerms: any = providerPerms; + for (let c in providerStatus) { + newProviderPerms[c] = providerStatus[c]; + } + setProviderPerms(newProviderPerms); + } + if (checkPayee) { + let newShipWallets: any = shipWallets; + + for (let c in checkPayee) { + newShipWallets[c] = checkPayee[c]; + } + setShipWallets(newShipWallets); + } + if (changeWallet) { + setWallet(changeWallet); + } + if (psbtData) { + setPsbt(psbtData.pb); + setFee(psbtData.fee); + } + if (cancelTx) { + handleCancelTx(cancelTx); + } + if (addressData) { + setAddress(addressData); + } + if (balanceData) { + setUnconfirmedBalance(balanceData.unconfirmed); + setConfirmedBalance(balanceData.confirmed); + } + if (errorData) { + setError(errorData); + } + if (broadcastSuccessData) { + setBroadcastSuccess(true); + } + if (broadcastFailData) { + setBroadcastSuccess(false); + } + if (scanProgressData) { + setScanProgress(scanProgressData); + } + }, [channelData]); + + useEffect(() => { + channel.setOnChannelError(() => { + start(); + }); + start(); + }, []); + + return ( + + {children} + + ); +}; + +export const useSettings = () => useContext(SettingsContext); diff --git a/pkg/btc-wallet/src/index.js b/pkg/btc-wallet/src/index.js deleted file mode 100644 index 5aa431a923..0000000000 --- a/pkg/btc-wallet/src/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Root } from './js/components/root.js'; -import { api } from './js/api.js'; -import Channel from './js/channel'; - -import './css/indigo-static.css'; -import './css/fonts.css'; -import './css/custom.css'; - -// rebuild x3 - -const channel = new Channel(); -api.setChannel(window.ship, channel); - - -if (module.hot) { - module.hot.accept() -} - -ReactDOM.render(( - -), document.querySelectorAll("#root")[0]); diff --git a/pkg/btc-wallet/src/index.tsx b/pkg/btc-wallet/src/index.tsx new file mode 100644 index 0000000000..848e9910de --- /dev/null +++ b/pkg/btc-wallet/src/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { api } from './lib/api'; +import Channel from './lib/channel'; +import { SettingsProvider } from './hooks/useSettings'; +import App from './App'; + +import './css/indigo-static.css'; +import './css/fonts.css'; +import './css/custom.css'; + +const channel = new Channel(); +api.setChannel(window.ship, channel); + +if (module.hot) { + module.hot.accept(); +} + +ReactDOM.render( + + + , + document.querySelectorAll('#root')[0] +); diff --git a/pkg/btc-wallet/src/js/components/lib/balance.js b/pkg/btc-wallet/src/js/components/lib/balance.js deleted file mode 100644 index 7d2e8eb300..0000000000 --- a/pkg/btc-wallet/src/js/components/lib/balance.js +++ /dev/null @@ -1,170 +0,0 @@ -import React, { Component } from 'react'; -import { Row, Text, Button, Col } from '@tlon/indigo-react'; -import Send from './send.js'; -import CurrencyPicker from './currencyPicker.js'; -import { satsToCurrency } from '../../lib/util.js'; -import { store } from '../../store.js'; - -export default class Balance extends Component { - constructor(props) { - super(props); - - this.state = { - sending: false, - copiedButton: false, - copiedString: false, - }; - - this.copyAddress = this.copyAddress.bind(this); - } - - copyAddress(arg) { - let address = this.props.state.address; - navigator.clipboard.writeText(address); - this.props.api.btcWalletCommand({ 'gen-new-address': null }); - - if (arg === 'button') { - this.setState({ copiedButton: true }); - setTimeout(() => { - this.setState({ copiedButton: false }); - }, 2000); - } else if (arg === 'string') { - this.setState({ copiedString: true }); - setTimeout(() => { - this.setState({ copiedString: false }); - }, 2000); - } - } - - render() { - const sats = this.props.state.confirmedBalance || 0; - const unconfirmedSats = this.props.state.unconfirmedBalance; - - const unconfirmedString = unconfirmedSats ? ` (${unconfirmedSats}) ` : ''; - - const denomination = this.props.state.denomination; - const value = satsToCurrency( - sats, - denomination, - this.props.state.currencyRates - ); - const sendDisabled = sats === 0; - const addressText = - this.props.state.address === null - ? '' - : this.props.state.address.slice(0, 6) + - '...' + - this.props.state.address.slice(-6); - - const conversion = this.props.state.currencyRates[denomination].last; - - return ( - <> - {this.state.sending ? ( - { - this.setState({ sending: false }); - store.handleEvent({ - data: { psbt: '', fee: 0, error: '', 'broadcast-fail': null }, - }); - }} - /> - ) : ( - - - - Balance - - { - this.copyAddress('string'); - }} - > - {this.state.copiedString ? 'copied' : addressText} - - - - - - {value} - - {`${sats}${unconfirmedString} sats`} - - - - - - - )} - - ); - } -} diff --git a/pkg/btc-wallet/src/js/components/lib/body.js b/pkg/btc-wallet/src/js/components/lib/body.js deleted file mode 100644 index 0fe1d798d6..0000000000 --- a/pkg/btc-wallet/src/js/components/lib/body.js +++ /dev/null @@ -1,72 +0,0 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - Row, - Text, - LoadingSpinner, - Col, -} from '@tlon/indigo-react'; -import { - Switch, - Route, -} from 'react-router-dom'; -import Balance from './balance.js'; -import Transactions from './transactions.js'; -import Warning from './warning.js'; -import Header from './header.js'; -import Settings from './settings.js'; - -export default class Body extends Component { - constructor(props) { - super(props); - } - - render() { - - const cardWidth = window.innerWidth <= 475 ? '350px' : '400px' - - if (!this.props.loaded) { - return ( - - - - ); - } else { - return ( - - - -
- - - - - -
- { (!this.props.warning) ? null : } - - - - - - ); - } - } -} diff --git a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js b/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js deleted file mode 100644 index acc147a8e1..0000000000 --- a/pkg/btc-wallet/src/js/components/lib/bridgeInvoice.js +++ /dev/null @@ -1,239 +0,0 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - StatelessTextInput as Input, - Row, - Text, - Button, - Col, - LoadingSpinner, -} from '@tlon/indigo-react'; - -import { Sigil } from './sigil.js'; - -import * as bitcoin from 'bitcoinjs-lib'; -import * as kg from 'urbit-key-generation'; -import { isValidPatp } from 'urbit-ob'; - -import Sent from './sent.js' -import Error from './error.js' - -import { satsToCurrency } from '../../lib/util.js'; - -export default class BridgeInvoice extends Component { - constructor(props) { - super(props); - - this.state = { - txHex: '', - ready: false, - error: this.props.state.error, - broadcasting: false, - }; - - this.checkTxHex = this.checkTxHex.bind(this); - this.broadCastTx = this.broadCastTx.bind(this); - this.sendBitcoin = this.sendBitcoin.bind(this); - this.clickDismiss = this.clickDismiss.bind(this); - this.setInvoiceRef = this.setInvoiceRef.bind(this); - } - - broadCastTx(hex) { - let command = { - 'broadcast-tx': hex - } - return this.props.api.btcWalletCommand(command) - } - - componentDidMount() { - window.open('https://bridge.urbit.org/?kind=btc&utx=' + this.props.psbt); - document.addEventListener("click", this.clickDismiss); - } - - componentWillUnmount(){ - document.removeEventListener("click", this.clickDismiss); - } - - setInvoiceRef(n){ - this.invoiceRef = n; - } - - clickDismiss(e){ - if (this.invoiceRef && !(this.invoiceRef.contains(e.target))){ - this.props.stopSending(); - } - } - - componentDidUpdate(prevProps){ - if (this.state.broadcasting) { - if (this.state.error !== '') { - this.setState({broadcasting: false}); - } - } - - if (prevProps.state.error !== this.props.state.error) { - this.setState({error: this.props.state.error}); - } - } - - sendBitcoin(hex) { - try { - bitcoin.Transaction.fromHex(hex) - this.broadCastTx(hex) - this.setState({broadcasting: true}); - } - - catch(e) { - this.setState({error: 'invalid-signed', broadcasting: false}); - } - } - - checkTxHex(e){ - let txHex = e.target.value; - let ready = (txHex.length > 0); - let error = ''; - this.setState({txHex, ready, error}); - } - - render() { - const { stopSending, payee, denomination, satsAmount, psbt, currencyRates, fee } = this.props; - const { error, txHex } = this.state; - - let inputColor = 'black'; - let inputBg = 'white'; - let inputBorder = 'lightGray'; - - if (error !== '') { - inputColor = 'red'; - inputBg = 'veryLightRed'; - inputBorder = 'red'; - } - - const isShip = isValidPatp(payee); - - const icon = (isShip) - ? - : ; - - return ( - <> - { this.props.state.broadcastSuccess ? - : - - - - {satsToCurrency(satsAmount, denomination, currencyRates)} - - - {`${satsAmount} sats`} - - - {`Fee: ${satsToCurrency(fee, denomination, currencyRates)} (${fee} sats)`} - - - You are paying - - - {icon} - {payee} - - - - - Bridge signed transaction - - - - - Copy the signed transaction from Bridge - - - - { (error !== '') && - - - - } - - - {this.state.connecting ? : null} - - - ); - } -} diff --git a/pkg/btc-wallet/src/js/components/lib/send.js b/pkg/btc-wallet/src/js/components/lib/send.js deleted file mode 100644 index df5eef8f5c..0000000000 --- a/pkg/btc-wallet/src/js/components/lib/send.js +++ /dev/null @@ -1,457 +0,0 @@ -import React, { Component } from 'react'; -import { - Box, - Icon, - StatelessTextInput as Input, - Row, - Text, - Button, - Col, - LoadingSpinner, - StatelessRadioButtonField as RadioButton, -} from '@tlon/indigo-react'; - -import Invoice from './invoice.js' -import BridgeInvoice from './bridgeInvoice.js' -import FeePicker from './feePicker.js' -import Error from './error.js' -import Signer from './signer.js' - -import { validate } from 'bitcoin-address-validation'; - -import * as ob from 'urbit-ob'; - -export default class Send extends Component { - constructor(props) { - super(props); - - this.state = { - signing: false, - denomAmount: '0.00', - satsAmount: '0', - payee: '', - checkingPatp: false, - payeeType: '', - ready: false, - validPayee: false, - focusPayee: true, - focusCurrency: false, - focusSats: false, - focusNote: false, - submitting: false, - feeChoices: { - low: [10, 1], - mid: [10, 1], - high: [10, 1], - }, - feeValue: "mid", - showModal: false, - note: '', - choosingSignMethod: false, - signMethod: 'bridge', - }; - - this.initPayment = this.initPayment.bind(this); - this.checkPayee = this.checkPayee.bind(this); - this.feeSelect = this.feeSelect.bind(this); - this.feeDismiss = this.feeDismiss.bind(this); - this.toggleSignMethod = this.toggleSignMethod.bind(this); - this.setSignMethod = this.setSignMethod.bind(this); - } - - feeDismiss() { - this.setState({showModal: false}); - } - - feeSelect(which) { - this.setState({feeValue: which}); - } - - componentDidMount(){ - if (this.props.network === 'bitcoin'){ - let url = "https://bitcoiner.live/api/fees/estimates/latest"; - fetch(url).then(res => res.json()).then(n => { - let estimates = Object.keys(n.estimates); - let mid = Math.floor(estimates.length/2) - let high = estimates.length - 1; - this.setState({ - feeChoices: { - high: [30, n.estimates[30]["sat_per_vbyte"]], - mid: [180, n.estimates[180]["sat_per_vbyte"]], - low: [360, n.estimates[360]["sat_per_vbyte"]], - } - }); - }) - } - } - - setSignMethod(signMethod) { - this.setState({signMethod, choosingSignMethod: false}); - } - - checkPayee(e){ - store.handleEvent({data: {error: ''}}); - - let payee = e.target.value; - let isPatp = ob.isValidPatp(payee); - let isAddress = validate(payee); - - - if (isPatp) { - let command = {'check-payee': payee} - this.props.api.btcWalletCommand(command) - setTimeout(() => { - this.setState({checkingPatp: false}); - }, 5000); - this.setState({ - checkingPatp: true, - payeeType: 'ship', - payee, - }); - } else if (isAddress) { - this.setState({ - payee, - ready: true, - checkingPatp: false, - payeeType: 'address', - validPayee: true, - }); - } else { - this.setState({ - payee, - ready: false, - checkingPatp: false, - payeeType: '', - validPayee: false, - }); - } - } - - componentDidUpdate(prevProps, prevState) { - if ((prevProps.error !== this.props.error) && - (this.props.error !== '') && (this.props.error !== 'broadcast-fail')) { - this.setState({signing: false}); - } - - if (!this.state.ready && this.state.checkingPatp) { - if (this.props.shipWallets[this.state.payee.slice(1)]) { - this.setState({ready: true, checkingPatp: false, validPayee: true}); - } - } - } - - toggleSignMethod(toggle) { - this.setState({choosingSignMethod: !toggle}); - } - - initPayment() { - if (this.state.payeeType === 'ship') { - let command = { - 'init-payment': { - 'payee': this.state.payee, - 'value': parseInt(this.state.satsAmount), - 'feyb': this.state.feeChoices[this.state.feeValue][1], - 'note': (this.state.note || null), - } - } - this.props.api.btcWalletCommand(command).then(res => this.setState({signing: true})); - } else if (this.state.payeeType === 'address') { - let command = { - 'init-payment-external': { - 'address': this.state.payee, - 'value': parseInt(this.state.satsAmount), - 'feyb': 1, - 'note': (this.state.note || null), - } - } - this.props.api.btcWalletCommand(command).then(res => this.setState({signing: true})); - } - } - - render() { - let payeeColor = "black"; - let payeeBg = "white"; - let payeeBorder = "lightGray"; - if (this.props.error) { - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } else if (this.state.focusPayee && this.state.validPayee) { - payeeColor = "green"; - payeeBorder = "green"; - payeeBg = "veryLightGreen"; - } else if (!this.state.focusPayee && this.state.validPayee){ - payeeColor="blue"; - payeeBorder = "white"; - payeeBg = "white"; - } else if (!this.state.focusPayee && !this.state.validPayee) { - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } else if (this.state.focusPayee && - !this.state.validPayee && - !this.state.checkingPatp && - this.state.payeeType === 'ship'){ - payeeColor="red"; - payeeBorder = "red"; - payeeBg="veryLightRed"; - } - - - const { api, value, conversion, stopSending, denomination, psbt, currencyRates, error, network, fee } = this.props; - const { denomAmount, satsAmount, signing, payee, choosingSignMethod, signMethod } = this.state; - - const signReady = (this.state.ready && (parseInt(this.state.satsAmount) > 0)) && !signing; - - let invoice = null; - if (signMethod === 'masterTicket') { - invoice = - - } else if (signMethod === 'bridge') { - invoice = - - } - - return ( - <> - { (signing && psbt) ? invoice : - - - - Send BTC - {value} - stopSending()} - /> - - - - To - {this.state.checkingPatp ? - : null - } - - {this.setState({focusPayee: true})}} - onBlur={() => {this.setState({focusPayee: false})}} - color={payeeColor} - backgroundColor={payeeBg} - borderColor={payeeBorder} - ml={2} - flexGrow="1" - fontSize='14px' - placeholder='~sampel-palnet or BTC address' - value={payee} - fontFamily="mono" - disabled={signing} - onChange={this.checkPayee} - /> - - {error && - - {/* yes this is a hack */} - - - - } - - Amount - {this.setState({focusCurrency: true})}} - onBlur={() => {this.setState({focusCurrency: false})}} - fontSize='14px' - width='100%' - type='number' - borderColor={this.state.focusCurrency ? "lightGray" : "none"} - disabled={signing} - value={denomAmount} - onChange={e => { - this.setState({ - denomAmount: e.target.value, - satsAmount: Math.round(parseFloat(e.target.value) / conversion * 100000000) - }); - }} - /> - {denomination} - - - {/* yes this is a hack */} - - {this.setState({focusSats: true})}} - onBlur={() => {this.setState({focusSats: false})}} - fontSize='14px' - width='100%' - type='number' - borderColor={this.state.focusSats ? "lightGray" : "none"} - disabled={signing} - value={satsAmount} - onChange={e => { - this.setState({ - denomAmount: parseFloat(e.target.value) * (conversion / 100000000), - satsAmount: e.target.value - }); - }} - /> - sats - - - Fee - - - {this.state.feeChoices[this.state.feeValue][1]} sats/vbyte - - {if (!this.state.showModal) this.setState({showModal: true}); }} - cursor="pointer"/> - - - - {!this.state.showModal ? null : - - } - - - Note - {this.setState({focusNote: true})}} - onBlur={() => {this.setState({focusNote: false})}} - fontSize='14px' - width='100%' - placeholder="What's this for?" - type='text' - borderColor={this.state.focusNote ? "lightGray" : "none"} - disabled={signing} - value={this.state.note} - onChange={e => { - this.setState({ - note: e.target.value, - }); - }} - /> - - - - - { (!(signing && !error)) ? null : - - } -