From 0108d26c18e1ad476661a8b4a7fd9ecb6c2b0979 Mon Sep 17 00:00:00 2001 From: Paul Freund Date: Wed, 3 Mar 2021 15:11:03 +0200 Subject: [PATCH] dapp auth scaffolding --- dApp/user/bsconfig.json | 10 +- dApp/user/package.json | 3 +- dApp/user/src/Apollo.bs.js | 54 +- dApp/user/src/Apollo.res | 39 +- dApp/user/src/App.bs.js | 27 +- dApp/user/src/App.res | 24 +- dApp/user/src/Dapp.bs.js | 6 +- dApp/user/src/Dapp.res | 15 +- .../components/Auth/AuthenticateButton.bs.js | 39 ++ .../components/Auth/AuthenticateButton.res | 23 + dApp/user/src/components/Auth/SignUp.bs.js | 554 ++++++++++++++++++ dApp/user/src/components/Auth/SignUp.res | 89 +++ dApp/user/src/components/Form.bs.js | 27 + dApp/user/src/components/Form.res | 13 + dApp/user/src/lib/Auth/AuthProvider.bs.js | 76 +++ dApp/user/src/lib/Auth/AuthProvider.res | 41 ++ dApp/user/src/lib/Ethers/Ethers.bs.js | 26 +- dApp/user/src/lib/Ethers/Ethers.res | 52 +- dApp/user/src/lib/Js.Promise/JsPromise.bs.js | 2 +- dApp/user/src/lib/Old/RootProvider.bs.js | 12 +- dApp/user/src/lib/Old/RootProvider.res | 6 +- dApp/user/yarn.lock | 20 +- 22 files changed, 1106 insertions(+), 52 deletions(-) create mode 100644 dApp/user/src/components/Auth/AuthenticateButton.bs.js create mode 100644 dApp/user/src/components/Auth/AuthenticateButton.res create mode 100644 dApp/user/src/components/Auth/SignUp.bs.js create mode 100644 dApp/user/src/components/Auth/SignUp.res create mode 100644 dApp/user/src/components/Form.bs.js create mode 100644 dApp/user/src/components/Form.res create mode 100644 dApp/user/src/lib/Auth/AuthProvider.bs.js create mode 100644 dApp/user/src/lib/Auth/AuthProvider.res diff --git a/dApp/user/bsconfig.json b/dApp/user/bsconfig.json index f6ad408..29551a0 100644 --- a/dApp/user/bsconfig.json +++ b/dApp/user/bsconfig.json @@ -3,10 +3,7 @@ "reason": { "react-jsx": 3 }, - "bsc-flags": [ - "-open Belt", - "-bs-super-errors" - ], + "bsc-flags": ["-open Belt", "-bs-super-errors"], "sources": [ { "dir": "src", @@ -36,10 +33,9 @@ "bs-moment", "bs-css", "bs-css-emotion", + "re-formality", "rescript-apollo-client" ], - "ppx-flags": [ - "@reasonml-community/graphql-ppx/ppx" - ], + "ppx-flags": ["re-formality/ppx", "@reasonml-community/graphql-ppx/ppx"], "refmt": 3 } diff --git a/dApp/user/package.json b/dApp/user/package.json index d1c0114..3e4719e 100644 --- a/dApp/user/package.json +++ b/dApp/user/package.json @@ -28,6 +28,7 @@ "moment": "^2.29.1", "pouchdb-adapter-memory": "^7.2.2", "raiden-ts": "^0.15.0", + "re-formality": "4.0.0-beta.7", "react": "^17.0.0", "react-dom": "^17.0.0", "subscriptions-transport-ws": "^0.9.18" @@ -39,7 +40,7 @@ "@babel/preset-typescript": "^7.12.7", "@reasonml-community/graphql-ppx": "^1.0.2", "@truffle/hdwallet-provider": "^1.2.1", - "bs-platform": "^8.4.2", + "bs-platform": "^9.0.1", "customize-cra": "^1.0.0", "get-graphql-schema": "^2.1.2", "react-app-rewired": "^2.1.8", diff --git a/dApp/user/src/Apollo.bs.js b/dApp/user/src/Apollo.bs.js index 2bcda81..0dabb4c 100644 --- a/dApp/user/src/Apollo.bs.js +++ b/dApp/user/src/Apollo.bs.js @@ -2,6 +2,7 @@ import * as Caml_option from "bs-platform/lib/es6/caml_option.js"; import * as ApolloClient from "rescript-apollo-client/src/ApolloClient.bs.js"; +import * as Ethers$FlowsUserApp from "./lib/Ethers/Ethers.bs.js"; import * as ApolloClient__Link_Ws from "rescript-apollo-client/src/@apollo/client/link/ws/ApolloClient__Link_Ws.bs.js"; import * as ApolloClient__Utilities from "rescript-apollo-client/src/@apollo/client/utilities/ApolloClient__Utilities.bs.js"; import * as ApolloClient__ApolloClient from "rescript-apollo-client/src/@apollo/client/ApolloClient__ApolloClient.bs.js"; @@ -16,10 +17,37 @@ var headers = { "x-hasura-admin-secret": "testing" }; +function getAuthHeaders(user) { + if (user === undefined) { + return ; + } + var u = Caml_option.valFromOption(user); + var getUserSignature = function (__x) { + return Caml_option.null_to_opt(__x.getItem(Ethers$FlowsUserApp.Utils.ethAdrToLowerStr(u))); + }; + var uS = getUserSignature(localStorage); + if (uS !== undefined) { + return { + "eth-address": Ethers$FlowsUserApp.Utils.ethAdrToStr(u), + "eth-signature": uS + }; + } + +} + var httpLink = ApolloClient__Link_Http_HttpLink.make((function (param) { return "http://localhost:8080/v1/graphql"; }), undefined, undefined, Caml_option.some(headers), undefined, undefined, undefined, undefined); +function makeHttpLink(user) { + var headers = getAuthHeaders(user); + return ApolloClient__Link_Http_HttpLink.make((function (param) { + return "http://localhost:8080/v1/graphql"; + }), undefined, undefined, Caml_option.some(headers !== undefined ? headers : (function (prim) { + return {}; + })), undefined, undefined, undefined, undefined); +} + var wsLink = ApolloClient__Link_Ws.WebSocketLink.make("ws://localhost:8080/v1/graphql", ApolloClient__SubscriptionsTransportWs.ClientOptions.make({ TAG: /* ConnectionParams */0, _0: { @@ -27,24 +55,30 @@ var wsLink = ApolloClient__Link_Ws.WebSocketLink.make("ws://localhost:8080/v1/gr } }, undefined, true, undefined, undefined, undefined, undefined, undefined), undefined, undefined); -var terminatingLink = ReasonMLCommunity__ApolloClient.Link.split((function (param) { - var definition = ApolloClient__Utilities.getOperationDefinition(param.query); - if (definition !== undefined && definition.kind === "OperationDefinition") { - return definition.operation === "subscription"; - } else { - return false; - } - }), wsLink, httpLink); +function terminatingLink(user) { + return ReasonMLCommunity__ApolloClient.Link.split((function (param) { + var definition = ApolloClient__Utilities.getOperationDefinition(param.query); + if (definition !== undefined && definition.kind === "OperationDefinition") { + return definition.operation === "subscription"; + } else { + return false; + } + }), wsLink, makeHttpLink(user)); +} -var client = ApolloClient.make(undefined, undefined, undefined, Caml_option.some(terminatingLink), ApolloClient__Cache_InMemory_InMemoryCache.make(undefined, undefined, undefined, undefined, undefined, undefined), undefined, undefined, true, undefined, ApolloClient__ApolloClient.DefaultOptions.make(ApolloClient__ApolloClient.DefaultMutateOptions.make(undefined, undefined, true, /* All */2, undefined, undefined), ApolloClient__ApolloClient.DefaultQueryOptions.make(/* NetworkOnly */2, /* All */2, undefined, undefined), ApolloClient__ApolloClient.DefaultWatchQueryOptions.make(/* NetworkOnly */3, /* All */2, undefined, undefined), undefined), undefined, undefined, undefined, undefined, undefined, undefined, undefined); +function makeClient(user) { + return ApolloClient.make(undefined, undefined, undefined, Caml_option.some(terminatingLink(user)), ApolloClient__Cache_InMemory_InMemoryCache.make(undefined, undefined, undefined, undefined, undefined, undefined), undefined, undefined, true, undefined, ApolloClient__ApolloClient.DefaultOptions.make(ApolloClient__ApolloClient.DefaultMutateOptions.make(undefined, undefined, true, /* All */2, undefined, undefined), ApolloClient__ApolloClient.DefaultQueryOptions.make(/* NetworkOnly */2, /* All */2, undefined, undefined), ApolloClient__ApolloClient.DefaultWatchQueryOptions.make(/* NetworkOnly */3, /* All */2, undefined, undefined), undefined), undefined, undefined, undefined, undefined, undefined, undefined, undefined); +} export { graphqlEndpoint , headers , + getAuthHeaders , httpLink , + makeHttpLink , wsLink , terminatingLink , - client , + makeClient , } /* httpLink Not a pure module */ diff --git a/dApp/user/src/Apollo.res b/dApp/user/src/Apollo.res index 0d286d8..afe0b96 100644 --- a/dApp/user/src/Apollo.res +++ b/dApp/user/src/Apollo.res @@ -2,12 +2,43 @@ let graphqlEndpoint = "localhost:8080/v1/graphql" let headers = {"x-hasura-admin-secret": "testing"} + +type clientHeaders = { + @as("eth-address") + ethAddress: string, + @as("eth-signature") + ethSignature: string, +} + +let getAuthHeaders = (~user) => { + open Ethers.Utils + switch(user){ + | None => None + | Some(u) => { + let getUserSignature = Dom.Storage2.getItem(_, u->ethAdrToLowerStr) + switch(getUserSignature(Dom.Storage2.localStorage)){ + | None => None + | Some(uS) => Some({ethAddress: u->ethAdrToStr, ethSignature: uS}) + } + } + } +} + let httpLink = ApolloClient.Link.HttpLink.make( ~uri=_ => "http://" ++ graphqlEndpoint, ~headers=Obj.magic(headers), (), ) +let makeHttpLink = (~user) => ApolloClient.Link.HttpLink.make( + ~uri= _ => "http://" ++ graphqlEndpoint, + ~headers={ + switch(getAuthHeaders(~user)){ + | Some(headers) => headers->Obj.magic + | None => Js.Obj.empty->Obj.magic + } +}, ()) + let wsLink = { open ApolloClient.Link.WebSocketLink make( @@ -21,15 +52,15 @@ let wsLink = { ) } -let terminatingLink = ApolloClient.Link.split(~test=({query}) => { +let terminatingLink = (~user) => ApolloClient.Link.split(~test=({query}) => { let definition = ApolloClient.Utilities.getOperationDefinition(query) switch definition { | Some({kind, operation}) => kind === "OperationDefinition" && operation === "subscription" | None => false } -}, ~whenTrue=wsLink, ~whenFalse=httpLink) +}, ~whenTrue=wsLink, ~whenFalse=makeHttpLink(~user)) -let client = { +let makeClient = (~user) => { open ApolloClient make( ~cache=Cache.InMemoryCache.make(), @@ -40,7 +71,7 @@ let client = { ~watchQuery=DefaultWatchQueryOptions.make(~fetchPolicy=NetworkOnly, ~errorPolicy=All, ()), (), ), - ~link=terminatingLink, + ~link=terminatingLink(~user), (), ) } diff --git a/dApp/user/src/App.bs.js b/dApp/user/src/App.bs.js index 7722be9..f97b34d 100644 --- a/dApp/user/src/App.bs.js +++ b/dApp/user/src/App.bs.js @@ -6,6 +6,7 @@ import * as Dapp$FlowsUserApp from "./Dapp.bs.js"; import * as Login$FlowsUserApp from "./Login/Login.bs.js"; import * as Apollo$FlowsUserApp from "./Apollo.bs.js"; import * as Router$FlowsUserApp from "./Router.bs.js"; +import * as AuthProvider$FlowsUserApp from "./lib/Auth/AuthProvider.bs.js"; import * as RootProvider$FlowsUserApp from "./lib/Old/RootProvider.bs.js"; function App$OnlyLoggedIn(Props) { @@ -59,11 +60,28 @@ var Router = { make: App$Router }; -function App(Props) { +function App$GraphQl(Props) { + var children = Props.children; + var user = RootProvider$FlowsUserApp.useCurrentUser(undefined); + var client = React.useMemo((function () { + return Apollo$FlowsUserApp.makeClient(user); + }), [user]); return React.createElement(Client.ApolloProvider, { - client: Apollo$FlowsUserApp.client, - children: React.createElement(RootProvider$FlowsUserApp.make, { - children: React.createElement(App$Router, {}) + client: client, + children: children + }); +} + +var GraphQl = { + make: App$GraphQl +}; + +function App(Props) { + return React.createElement(RootProvider$FlowsUserApp.make, { + children: React.createElement(App$GraphQl, { + children: React.createElement(AuthProvider$FlowsUserApp.make, { + children: React.createElement(App$Router, {}) + }) }) }); } @@ -75,6 +93,7 @@ export { Main , NotFound , Router , + GraphQl , make , } diff --git a/dApp/user/src/App.res b/dApp/user/src/App.res index 2fb0ea8..175c3b2 100644 --- a/dApp/user/src/App.res +++ b/dApp/user/src/App.res @@ -34,8 +34,26 @@ module Router = { } } } + +module GraphQl = { + @react.component + let make = (~children) => { + let user = RootProvider.useCurrentUser() + let client = React.useMemo1(() => + Apollo.makeClient( + ~user + ) + , [user]) + + children + } +} @react.component let make = () => - - - + + + + + + + \ No newline at end of file diff --git a/dApp/user/src/Dapp.bs.js b/dApp/user/src/Dapp.bs.js index e1d933c..b78cd94 100644 --- a/dApp/user/src/Dapp.bs.js +++ b/dApp/user/src/Dapp.bs.js @@ -3,11 +3,15 @@ import * as React from "react"; import * as RaidenTs from "raiden-ts"; import * as Caml_option from "bs-platform/lib/es6/caml_option.js"; +import * as SignUp$FlowsUserApp from "./components/Auth/SignUp.bs.js"; +import * as AuthProvider$FlowsUserApp from "./lib/Auth/AuthProvider.bs.js"; import * as RootProvider$FlowsUserApp from "./lib/Old/RootProvider.bs.js"; +import * as AuthenticateButton$FlowsUserApp from "./components/Auth/AuthenticateButton.bs.js"; function Dapp(Props) { var optWeb3Provider = RootProvider$FlowsUserApp.useWeb3(undefined); var optSigner = RootProvider$FlowsUserApp.useSigner(undefined); + var match = AuthProvider$FlowsUserApp.useAuthStatus(undefined); React.useEffect((function () { if (optWeb3Provider !== undefined && optSigner !== undefined) { RaidenTs.Raiden.create(Caml_option.valFromOption(optWeb3Provider), 0); @@ -17,7 +21,7 @@ function Dapp(Props) { optWeb3Provider, optSigner ]); - return React.createElement("div", undefined, React.createElement("p", undefined, "something")); + return React.createElement("div", undefined, match.isAuthorized ? React.createElement(SignUp$FlowsUserApp.make, {}) : React.createElement(AuthenticateButton$FlowsUserApp.make, {}), React.createElement("p", undefined, "something")); } var make = Dapp; diff --git a/dApp/user/src/Dapp.res b/dApp/user/src/Dapp.res index 3f89585..f62a154 100644 --- a/dApp/user/src/Dapp.res +++ b/dApp/user/src/Dapp.res @@ -3,6 +3,8 @@ let make = () => { let optWeb3Provider = RootProvider.useWeb3() let optSigner = RootProvider.useSigner() + let { isAuthorized } = AuthProvider.useAuthStatus() + React.useEffect2(() => { switch (optWeb3Provider, optSigner) { | (Some(web3Provider), Some(_signer)) => { @@ -12,5 +14,16 @@ let make = () => { } None }, (optWeb3Provider, optSigner)) -

{"something"->React.string}

+
+ { + if(!isAuthorized){ + + }else{ + + } + } +

+ {"something"->React.string} +

+
} diff --git a/dApp/user/src/components/Auth/AuthenticateButton.bs.js b/dApp/user/src/components/Auth/AuthenticateButton.bs.js new file mode 100644 index 0000000..03982a6 --- /dev/null +++ b/dApp/user/src/components/Auth/AuthenticateButton.bs.js @@ -0,0 +1,39 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Curry from "bs-platform/lib/es6/curry.js"; +import * as React from "react"; +import * as Ethers$FlowsUserApp from "../../lib/Ethers/Ethers.bs.js"; +import * as AuthProvider$FlowsUserApp from "../../lib/Auth/AuthProvider.bs.js"; +import * as RootProvider$FlowsUserApp from "../../lib/Old/RootProvider.bs.js"; + +function setSignInData(ethAddress, ethSignature) { + localStorage.setItem(ethAddress, ethSignature); + +} + +function AuthenticateButton(Props) { + var signer = RootProvider$FlowsUserApp.useSignerExn(undefined); + var userAddress = RootProvider$FlowsUserApp.useCurrentUserExn(undefined); + var match = AuthProvider$FlowsUserApp.useAuthStatus(undefined); + var setIsAuthorized = match.setIsAuthorized; + return React.createElement("button", { + onClick: (function (param) { + signer.signMessage("flows.finance-signin-string:" + Ethers$FlowsUserApp.Utils.ethAdrToStr(userAddress)).then(function (result) { + setSignInData(Ethers$FlowsUserApp.Utils.ethAdrToLowerStr(userAddress), String(result)); + return Curry._1(setIsAuthorized, (function (param) { + return true; + })); + }); + + }) + }, "Authenticate"); +} + +var make = AuthenticateButton; + +export { + setSignInData , + make , + +} +/* react Not a pure module */ diff --git a/dApp/user/src/components/Auth/AuthenticateButton.res b/dApp/user/src/components/Auth/AuthenticateButton.res new file mode 100644 index 0000000..73b6fa0 --- /dev/null +++ b/dApp/user/src/components/Auth/AuthenticateButton.res @@ -0,0 +1,23 @@ +open Ethers.Utils + +let setSignInData = (~ethAddress: string, ~ethSignature: string) => Dom.Storage2.localStorage->Dom.Storage2.setItem(ethAddress, ethSignature) + +@react.component +let make = () => { + let signer = RootProvider.useSignerExn() + let userAddress = RootProvider.useCurrentUserExn() + let { setIsAuthorized } = AuthProvider.useAuthStatus() + +} \ No newline at end of file diff --git a/dApp/user/src/components/Auth/SignUp.bs.js b/dApp/user/src/components/Auth/SignUp.bs.js new file mode 100644 index 0000000..7af68ce --- /dev/null +++ b/dApp/user/src/components/Auth/SignUp.bs.js @@ -0,0 +1,554 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Curry from "bs-platform/lib/es6/curry.js"; +import * as React from "react"; +import * as Formality from "re-formality/src/Formality.bs.js"; +import * as Belt_Option from "bs-platform/lib/es6/belt_Option.js"; +import * as Caml_option from "bs-platform/lib/es6/caml_option.js"; +import * as Form$FlowsUserApp from "../Form.bs.js"; +import * as Ethers$FlowsUserApp from "../../lib/Ethers/Ethers.bs.js"; +import * as Formality__ReactUpdate from "re-formality/src/Formality__ReactUpdate.bs.js"; + +var validators_name = { + strategy: /* OnFirstBlur */0, + validate: (function (param) { + var name = param.name; + if (name === "") { + return { + TAG: /* Error */1, + _0: "Name is required" + }; + } else { + return { + TAG: /* Ok */0, + _0: name + }; + } + }) +}; + +var validators_address = { + strategy: /* OnFirstBlur */0, + validate: (function (param) { + var address = param.address; + if (address === "") { + return { + TAG: /* Error */1, + _0: "Address is required" + }; + } else { + return Belt_Option.mapWithDefault(Ethers$FlowsUserApp.Utils.getAddress(address), { + TAG: /* Error */1, + _0: "Couldn't parse address" + }, (function (address) { + return { + TAG: /* Ok */0, + _0: address + }; + })); + } + }) +}; + +var validators = { + name: validators_name, + address: validators_address +}; + +function initialFieldsStatuses(_input) { + return { + name: /* Pristine */0, + address: /* Pristine */0 + }; +} + +function initialState(input) { + return { + input: input, + fieldsStatuses: { + name: /* Pristine */0, + address: /* Pristine */0 + }, + collectionsStatuses: undefined, + formStatus: /* Editing */0, + submissionStatus: /* NeverSubmitted */0 + }; +} + +function validateForm(input, validators, fieldsStatuses) { + var match = fieldsStatuses.name; + var match_0 = match ? match._0 : Curry._1(validators.name.validate, input); + var match$1 = fieldsStatuses.address; + var match_0$1 = match$1 ? match$1._0 : Curry._1(validators.address.validate, input); + var nameResult = match_0; + var nameResult$1; + if (nameResult.TAG === /* Ok */0) { + var addressResult = match_0$1; + if (addressResult.TAG === /* Ok */0) { + return { + TAG: /* Valid */0, + output: { + address: addressResult._0, + name: nameResult._0 + }, + fieldsStatuses: { + name: /* Dirty */{ + _0: nameResult, + _1: /* Shown */0 + }, + address: /* Dirty */{ + _0: addressResult, + _1: /* Shown */0 + } + }, + collectionsStatuses: undefined + }; + } + nameResult$1 = nameResult; + } else { + nameResult$1 = nameResult; + } + return { + TAG: /* Invalid */1, + fieldsStatuses: { + name: /* Dirty */{ + _0: nameResult$1, + _1: /* Shown */0 + }, + address: /* Dirty */{ + _0: match_0$1, + _1: /* Shown */0 + } + }, + collectionsStatuses: undefined + }; +} + +function useForm(initialInput, onSubmit) { + var memoizedInitialState = React.useMemo((function () { + return initialState(initialInput); + }), [initialInput]); + var match = Formality__ReactUpdate.useReducer(memoizedInitialState, (function (state, action) { + if (typeof action === "number") { + switch (action) { + case /* BlurNameField */0 : + var result = Formality.validateFieldOnBlurWithValidator(state.input, state.fieldsStatuses.name, validators_name, (function (status) { + var init = state.fieldsStatuses; + return { + name: status, + address: init.address + }; + })); + if (result !== undefined) { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: result, + collectionsStatuses: state.collectionsStatuses, + formStatus: state.formStatus, + submissionStatus: state.submissionStatus + } + }; + } else { + return /* NoUpdate */0; + } + case /* BlurAddressField */1 : + var result$1 = Formality.validateFieldOnBlurWithValidator(state.input, state.fieldsStatuses.address, validators_address, (function (status) { + var init = state.fieldsStatuses; + return { + name: init.name, + address: status + }; + })); + if (result$1 !== undefined) { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: result$1, + collectionsStatuses: state.collectionsStatuses, + formStatus: state.formStatus, + submissionStatus: state.submissionStatus + } + }; + } else { + return /* NoUpdate */0; + } + case /* Submit */2 : + var match = state.formStatus; + if (typeof match !== "number" && match.TAG === /* Submitting */0) { + return /* NoUpdate */0; + } + var match$1 = validateForm(state.input, validators, state.fieldsStatuses); + if (match$1.TAG !== /* Valid */0) { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: match$1.fieldsStatuses, + collectionsStatuses: match$1.collectionsStatuses, + formStatus: /* Editing */0, + submissionStatus: /* AttemptedToSubmit */1 + } + }; + } + var output = match$1.output; + var error = state.formStatus; + var tmp; + tmp = typeof error === "number" || error.TAG !== /* SubmissionFailed */1 ? undefined : Caml_option.some(error._0); + return { + TAG: /* UpdateWithSideEffects */1, + _0: { + input: state.input, + fieldsStatuses: match$1.fieldsStatuses, + collectionsStatuses: match$1.collectionsStatuses, + formStatus: { + TAG: /* Submitting */0, + _0: tmp + }, + submissionStatus: /* AttemptedToSubmit */1 + }, + _1: (function (param) { + var dispatch = param.dispatch; + return Curry._2(onSubmit, output, { + notifyOnSuccess: (function (input) { + return Curry._1(dispatch, { + TAG: /* SetSubmittedStatus */2, + _0: input + }); + }), + notifyOnFailure: (function (error) { + return Curry._1(dispatch, { + TAG: /* SetSubmissionFailedStatus */3, + _0: error + }); + }), + reset: (function (param) { + return Curry._1(dispatch, /* Reset */5); + }), + dismissSubmissionResult: (function (param) { + return Curry._1(dispatch, /* DismissSubmissionResult */4); + }) + }); + }) + }; + break; + case /* DismissSubmissionError */3 : + var match$2 = state.formStatus; + if (typeof match$2 === "number" || match$2.TAG !== /* SubmissionFailed */1) { + return /* NoUpdate */0; + } else { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: state.fieldsStatuses, + collectionsStatuses: state.collectionsStatuses, + formStatus: /* Editing */0, + submissionStatus: state.submissionStatus + } + }; + } + case /* DismissSubmissionResult */4 : + var match$3 = state.formStatus; + if (typeof match$3 === "number") { + if (match$3 === /* Editing */0) { + return /* NoUpdate */0; + } + + } else if (match$3.TAG === /* Submitting */0) { + return /* NoUpdate */0; + } + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: state.fieldsStatuses, + collectionsStatuses: state.collectionsStatuses, + formStatus: /* Editing */0, + submissionStatus: state.submissionStatus + } + }; + case /* Reset */5 : + return { + TAG: /* Update */0, + _0: initialState(initialInput) + }; + + } + } else { + switch (action.TAG | 0) { + case /* UpdateNameField */0 : + var nextInput = Curry._1(action._0, state.input); + return { + TAG: /* Update */0, + _0: { + input: nextInput, + fieldsStatuses: Formality.validateFieldOnChangeWithValidator(nextInput, state.fieldsStatuses.name, state.submissionStatus, validators_name, (function (status) { + var init = state.fieldsStatuses; + return { + name: status, + address: init.address + }; + })), + collectionsStatuses: state.collectionsStatuses, + formStatus: state.formStatus, + submissionStatus: state.submissionStatus + } + }; + case /* UpdateAddressField */1 : + var nextInput$1 = Curry._1(action._0, state.input); + return { + TAG: /* Update */0, + _0: { + input: nextInput$1, + fieldsStatuses: Formality.validateFieldOnChangeWithValidator(nextInput$1, state.fieldsStatuses.address, state.submissionStatus, validators_address, (function (status) { + var init = state.fieldsStatuses; + return { + name: init.name, + address: status + }; + })), + collectionsStatuses: state.collectionsStatuses, + formStatus: state.formStatus, + submissionStatus: state.submissionStatus + } + }; + case /* SetSubmittedStatus */2 : + var input = action._0; + if (input !== undefined) { + return { + TAG: /* Update */0, + _0: { + input: input, + fieldsStatuses: { + name: /* Pristine */0, + address: /* Pristine */0 + }, + collectionsStatuses: state.collectionsStatuses, + formStatus: /* Submitted */1, + submissionStatus: state.submissionStatus + } + }; + } else { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: { + name: /* Pristine */0, + address: /* Pristine */0 + }, + collectionsStatuses: state.collectionsStatuses, + formStatus: /* Submitted */1, + submissionStatus: state.submissionStatus + } + }; + } + case /* SetSubmissionFailedStatus */3 : + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: state.fieldsStatuses, + collectionsStatuses: state.collectionsStatuses, + formStatus: { + TAG: /* SubmissionFailed */1, + _0: action._0 + }, + submissionStatus: state.submissionStatus + } + }; + case /* MapSubmissionError */4 : + var map = action._0; + var error$1 = state.formStatus; + if (typeof error$1 === "number") { + return /* NoUpdate */0; + } + if (error$1.TAG !== /* Submitting */0) { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: state.fieldsStatuses, + collectionsStatuses: state.collectionsStatuses, + formStatus: { + TAG: /* SubmissionFailed */1, + _0: Curry._1(map, error$1._0) + }, + submissionStatus: state.submissionStatus + } + }; + } + var error$2 = error$1._0; + if (error$2 !== undefined) { + return { + TAG: /* Update */0, + _0: { + input: state.input, + fieldsStatuses: state.fieldsStatuses, + collectionsStatuses: state.collectionsStatuses, + formStatus: { + TAG: /* Submitting */0, + _0: Caml_option.some(Curry._1(map, Caml_option.valFromOption(error$2))) + }, + submissionStatus: state.submissionStatus + } + }; + } else { + return /* NoUpdate */0; + } + + } + } + })); + var dispatch = match[1]; + var state = match[0]; + var match$1 = state.formStatus; + var tmp; + tmp = typeof match$1 === "number" || match$1.TAG !== /* Submitting */0 ? false : true; + return { + updateName: (function (nextInputFn, nextValue) { + return Curry._1(dispatch, { + TAG: /* UpdateNameField */0, + _0: (function (__x) { + return Curry._2(nextInputFn, __x, nextValue); + }) + }); + }), + updateAddress: (function (nextInputFn, nextValue) { + return Curry._1(dispatch, { + TAG: /* UpdateAddressField */1, + _0: (function (__x) { + return Curry._2(nextInputFn, __x, nextValue); + }) + }); + }), + blurName: (function (param) { + return Curry._1(dispatch, /* BlurNameField */0); + }), + blurAddress: (function (param) { + return Curry._1(dispatch, /* BlurAddressField */1); + }), + nameResult: Formality.exposeFieldResult(state.fieldsStatuses.name), + addressResult: Formality.exposeFieldResult(state.fieldsStatuses.address), + input: state.input, + status: state.formStatus, + dirty: (function (param) { + var match = state.fieldsStatuses; + if (match.name || match.address) { + return true; + } else { + return false; + } + }), + valid: (function (param) { + var match = validateForm(state.input, validators, state.fieldsStatuses); + if (match.TAG === /* Valid */0) { + return true; + } else { + return false; + } + }), + submitting: tmp, + submit: (function (param) { + return Curry._1(dispatch, /* Submit */2); + }), + dismissSubmissionError: (function (param) { + return Curry._1(dispatch, /* DismissSubmissionError */3); + }), + dismissSubmissionResult: (function (param) { + return Curry._1(dispatch, /* DismissSubmissionResult */4); + }), + mapSubmissionError: (function (map) { + return Curry._1(dispatch, { + TAG: /* MapSubmissionError */4, + _0: map + }); + }), + reset: (function (param) { + return Curry._1(dispatch, /* Reset */5); + }) + }; +} + +var SignUpForm = { + validators: validators, + initialFieldsStatuses: initialFieldsStatuses, + initialCollectionsStatuses: undefined, + initialState: initialState, + validateForm: validateForm, + useForm: useForm +}; + +var initialInput = { + address: "", + name: "" +}; + +function SignUp(Props) { + var form = useForm(initialInput, (function (param, param$1) { + console.log("NEEDS TO BE IMPLEMENTED"); + + })); + var match = form.addressResult; + var tmp; + tmp = match !== undefined && match.TAG !== /* Ok */0 ? React.createElement("div", undefined, match._0) : null; + var match$1 = form.nameResult; + var tmp$1; + tmp$1 = match$1 !== undefined && match$1.TAG !== /* Ok */0 ? React.createElement("div", undefined, match$1._0) : null; + return React.createElement(Form$FlowsUserApp.make, { + className: "", + onSubmit: (function (param) { + return Curry._1(form.submit, undefined); + }), + children: null + }, React.createElement("h2", undefined, "Sign Up"), React.createElement("label", { + htmlFor: "address" + }, "Raiden Address: "), React.createElement("input", { + id: "address", + disabled: form.submitting, + type: "text", + value: form.input.address, + onBlur: (function (param) { + return Curry._1(form.blurAddress, undefined); + }), + onChange: (function ($$event) { + return Curry._2(form.updateAddress, (function (input, value) { + return { + address: value, + name: input.name + }; + }), $$event.target.value); + }) + }), tmp, React.createElement("br", undefined), React.createElement("label", { + htmlFor: "name" + }, "Name: "), React.createElement("input", { + id: "name", + disabled: form.submitting, + type: "text", + value: form.input.name, + onBlur: (function (param) { + return Curry._1(form.blurName, undefined); + }), + onChange: (function ($$event) { + return Curry._2(form.updateName, (function (input, value) { + return { + address: input.address, + name: value + }; + }), $$event.target.value); + }) + }), tmp$1, React.createElement("br", undefined), React.createElement("button", undefined, "Sign Up")); +} + +var make = SignUp; + +export { + SignUpForm , + initialInput , + make , + +} +/* react Not a pure module */ diff --git a/dApp/user/src/components/Auth/SignUp.res b/dApp/user/src/components/Auth/SignUp.res new file mode 100644 index 0000000..54cbfe5 --- /dev/null +++ b/dApp/user/src/components/Auth/SignUp.res @@ -0,0 +1,89 @@ +module SignUpForm = %form( + type input = {address: string, name: string} + type output = {address: Ethers.ethAddress, name: string} + + let validators = { + address: { + strategy: OnFirstBlur, + validate: ({address}) => { + switch address { + | "" => Error("Address is required") + | address => + Ethers.Utils.getAddress(address)->Option.mapWithDefault( + Error("Couldn't parse address"), + address => address->Ok, + ) + } + }, + }, + name: { + strategy: OnFirstBlur, + validate: ({name}) => if(name == ""){ + Error("Name is required") + }else{ + name->Ok + } + } + } +) + +let initialInput: SignUpForm.input = { + address: "", + name: "" +} + +@react.component +let make = () => { + let form = SignUpForm.useForm(~initialInput, ~onSubmit=(_, _)=>{ + Js.log("NEEDS TO BE IMPLEMENTED") + }) +
form.submit()} + > +

{"Sign Up"->React.string}

+ + form.blurAddress()} + onChange={ + event => form.updateAddress((input, value) => { + ...input, + address: value, + }, (event->ReactEvent.Form.target)["value"]) + } + /> + { + {switch form.addressResult { + | Some(Error(message)) =>
{message->React.string}
+ | _ => React.null + }} + } +
+ + form.blurName()} + onChange={ + event => form.updateName((input, value) => { + ...input, + name: value, + }, (event->ReactEvent.Form.target)["value"]) + } + /> + { + {switch form.nameResult { + | Some(Error(message)) =>
{message->React.string}
+ | _ => React.null + }} + } +
+ +
+} \ No newline at end of file diff --git a/dApp/user/src/components/Form.bs.js b/dApp/user/src/components/Form.bs.js new file mode 100644 index 0000000..3afceb6 --- /dev/null +++ b/dApp/user/src/components/Form.bs.js @@ -0,0 +1,27 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Curry from "bs-platform/lib/es6/curry.js"; +import * as React from "react"; + +function Form(Props) { + var className = Props.className; + var onSubmit = Props.onSubmit; + var children = Props.children; + return React.createElement("form", { + className: className, + onSubmit: (function ($$event) { + if (!$$event.defaultPrevented) { + $$event.preventDefault(); + } + return Curry._1(onSubmit, undefined); + }) + }, children); +} + +var make = Form; + +export { + make , + +} +/* react Not a pure module */ diff --git a/dApp/user/src/components/Form.res b/dApp/user/src/components/Form.res new file mode 100644 index 0000000..10c003e --- /dev/null +++ b/dApp/user/src/components/Form.res @@ -0,0 +1,13 @@ +@react.component +let make = (~className, ~onSubmit, ~children) => { +
{ + if !(event->ReactEvent.Form.defaultPrevented) { + event->ReactEvent.Form.preventDefault + } + onSubmit() + }}> + children +
+} diff --git a/dApp/user/src/lib/Auth/AuthProvider.bs.js b/dApp/user/src/lib/Auth/AuthProvider.bs.js new file mode 100644 index 0000000..418327e --- /dev/null +++ b/dApp/user/src/lib/Auth/AuthProvider.bs.js @@ -0,0 +1,76 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Curry from "bs-platform/lib/es6/curry.js"; +import * as React from "react"; +import * as Belt_Option from "bs-platform/lib/es6/belt_Option.js"; +import * as Apollo$FlowsUserApp from "../../Apollo.bs.js"; +import * as RootProvider$FlowsUserApp from "../Old/RootProvider.bs.js"; + +var context = React.createContext({ + isAuthorized: false, + setIsAuthorized: (function (param) { + + }) + }); + +var provider = context.Provider; + +function AuthProvider$AuthContext$Provider(Props) { + var value = Props.value; + var children = Props.children; + return React.createElement(provider, { + value: value, + children: children + }); +} + +var Provider = { + provider: provider, + make: AuthProvider$AuthContext$Provider +}; + +var AuthContext = { + context: context, + Provider: Provider +}; + +function getUserAuthStatus(user) { + return Belt_Option.isSome(Apollo$FlowsUserApp.getAuthHeaders(user)); +} + +function AuthProvider(Props) { + var children = Props.children; + var user = RootProvider$FlowsUserApp.useCurrentUser(undefined); + var match = React.useState(function () { + return false; + }); + var setIsAuthorized = match[1]; + React.useEffect((function () { + Curry._1(setIsAuthorized, (function (param) { + return Belt_Option.isSome(Apollo$FlowsUserApp.getAuthHeaders(user)); + })); + + }), [user]); + return React.createElement(AuthProvider$AuthContext$Provider, { + value: { + isAuthorized: match[0], + setIsAuthorized: setIsAuthorized + }, + children: children + }); +} + +function useAuthStatus(param) { + return React.useContext(context); +} + +var make = AuthProvider; + +export { + AuthContext , + getUserAuthStatus , + make , + useAuthStatus , + +} +/* context Not a pure module */ diff --git a/dApp/user/src/lib/Auth/AuthProvider.res b/dApp/user/src/lib/Auth/AuthProvider.res new file mode 100644 index 0000000..72f84df --- /dev/null +++ b/dApp/user/src/lib/Auth/AuthProvider.res @@ -0,0 +1,41 @@ +type authContextType = { + isAuthorized: bool, + setIsAuthorized: ((bool => bool) => unit) +} + +module AuthContext = { + let context = React.createContext({isAuthorized: false, setIsAuthorized: (_)=> () }) + + module Provider = { + let provider = React.Context.provider(context) + + @react.component + let make = (~value, ~children) => { + React.createElement(provider, {"value": value, "children": children}) + } + } +} + +let getUserAuthStatus = (user) => Apollo.getAuthHeaders(~user)->Option.isSome + +@react.component +let make = (~children) => { + let user = RootProvider.useCurrentUser() + + let (isAuthorized, setIsAuthorized) = React.useState(_ => false) + + React.useEffect1(() => { + let _ = setIsAuthorized(_ => user->getUserAuthStatus) + None + }, [user]) + + + + {children} + +} + +let useAuthStatus = () => React.useContext(AuthContext.context) \ No newline at end of file diff --git a/dApp/user/src/lib/Ethers/Ethers.bs.js b/dApp/user/src/lib/Ethers/Ethers.bs.js index 38c1a36..5a51d61 100644 --- a/dApp/user/src/lib/Ethers/Ethers.bs.js +++ b/dApp/user/src/lib/Ethers/Ethers.bs.js @@ -3,6 +3,8 @@ import * as Curry from "bs-platform/lib/es6/curry.js"; import * as Js_exn from "bs-platform/lib/es6/js_exn.js"; import * as Ethers from "ethers"; +import * as Belt_Float from "bs-platform/lib/es6/belt_Float.js"; +import * as Belt_Option from "bs-platform/lib/es6/belt_Option.js"; import * as Caml_option from "bs-platform/lib/es6/caml_option.js"; import * as Caml_js_exceptions from "bs-platform/lib/es6/caml_js_exceptions.js"; @@ -51,26 +53,42 @@ function parseEther(amount) { return parseUnits(amount, "ether"); } +function parseEtherUnsafe(amount) { + return Ethers.utils.parseUnits(amount, "ether"); +} + function getAddress(addressString) { return unsafeToOption(function (param) { return Ethers.utils.getAddress(addressString); }); } -function toString(prim) { +function formatEther(__x) { + return Ethers.utils.formatUnits(__x, "ether"); +} + +function formatEtherToPrecision(number, digits) { + var digitMultiplier = Math.pow(10.0, digits); + return String(Math.floor(Belt_Option.getExn(Belt_Float.fromString(Ethers.utils.formatUnits(number, "ether"))) * digitMultiplier) / digitMultiplier); +} + +function ethAdrToStr(prim) { return prim; } -function toLowerString(address) { +function ethAdrToLowerStr(address) { return address.toLowerCase(); } var Utils = { parseUnits: parseUnits, parseEther: parseEther, + parseEtherUnsafe: parseEtherUnsafe, getAddress: getAddress, - toString: toString, - toLowerString: toLowerString + formatEther: formatEther, + formatEtherToPrecision: formatEtherToPrecision, + ethAdrToStr: ethAdrToStr, + ethAdrToLowerStr: ethAdrToLowerStr }; export { diff --git a/dApp/user/src/lib/Ethers/Ethers.res b/dApp/user/src/lib/Ethers/Ethers.res index b5b75a2..17223b4 100644 --- a/dApp/user/src/lib/Ethers/Ethers.res +++ b/dApp/user/src/lib/Ethers/Ethers.res @@ -48,27 +48,36 @@ type ethersBigNumber module BigNumber = { type t = ethersBigNumber + @module("ethers") @scope("BigNumber") + external fromUnsafe: string => t = "from" + @module("ethers") @scope("BigNumber") + external fromInt: int => t = "from" + @send external add: (t, t) => t = "add" @send external sub: (t, t) => t = "sub" @send external mul: (t, t) => t = "mul" @send external div: (t, t) => t = "div" + @send external mod: (t, t) => t = "mod" + @send external pow: (t, t) => t = "pow" + @send external abs: t => t = "abs" + @send external gt: (t, t) => bool = "gt" + @send external gte: (t, t) => bool = "gte" @send external lt: (t, t) => bool = "lt" - @dead("+eq") @send external eq: (t, t) => bool = "eq" - @send external cmp: (t, t) => int = "cmp" - @dead("+sqr") @send external sqr: t => t = "sqr" + @send external lte: (t, t) => bool = "lte" + @send external eq: (t, t) => bool = "eq" + @send external toString: t => string = "toString" - @send external toStringRad: (t, int) => string = "toString" @send external toNumber: t => int = "toNumber" @send external toNumberFloat: t => float = "toNumber" } +type providerType + @send -external waitForTransaction: (Web3.rawProvider, string) => JsPromise.t = - "waitForTransaction" +external waitForTransaction: (providerType, string) => JsPromise.t = "waitForTransaction" -type providerType type walletType = {address: string, provider: providerType} module Wallet = { @@ -76,13 +85,17 @@ module Wallet = { @new @module("ethers") external makePrivKeyWallet: (string, providerType) => t = "Wallet" + + type rawSignature + @send + external signMessage: (t, string) => JsPromise.t = "signMessage" } module Providers = { type t = providerType @new @module("ethers") @scope("providers") - external makeProvider: string => Web3.rawProvider = "JsonRpcProvider" + external makeProvider: string => t = "JsonRpcProvider" @send external getBalance: (t, ethAddress) => JsPromise.t> = "getBalance" @send @@ -138,12 +151,31 @@ module Utils = { let parseUnits = (~amount, ~unit) => Misc.unsafeToOption(() => parseUnitsUnsafe(. amount, unit)) let parseEther = (~amount) => parseUnits(~amount, ~unit=#ether) + let parseEtherUnsafe = (~amount) => parseUnitsUnsafe(. amount, #ether) @module("ethers") @scope("utils") external getAddressUnsafe: string => ethAddress = "getAddress" let getAddress: string => option = addressString => Misc.unsafeToOption(() => getAddressUnsafe(addressString)) - let toString: ethAddress => string = Obj.magic - let toLowerString: ethAddress => string = address => address->toString->Js.String.toLowerCase + @module("ethers") @scope("utils") + external formatUnits: (. BigNumber.t, ethUnit) => string = "formatUnits" + + let formatEther = formatUnits(. _, #ether) + + let formatEtherToPrecision = (number, digits) => { + let digitMultiplier = Js.Math.pow_float(~base=10.0, ~exp=digits->Float.fromInt) + number + ->formatEther + ->Float.fromString + ->Option.getExn + ->(x => x *. digitMultiplier) + ->Js.Math.floor_float + ->(x => x /. digitMultiplier) + ->Float.toString + } + + let ethAdrToStr: ethAddress => string = Obj.magic + let ethAdrToLowerStr: ethAddress => string = address => + address->ethAdrToStr->Js.String.toLowerCase } diff --git a/dApp/user/src/lib/Js.Promise/JsPromise.bs.js b/dApp/user/src/lib/Js.Promise/JsPromise.bs.js index 6b7e3b4..a628adb 100644 --- a/dApp/user/src/lib/Js.Promise/JsPromise.bs.js +++ b/dApp/user/src/lib/Js.Promise/JsPromise.bs.js @@ -3,7 +3,7 @@ import * as Curry from "bs-platform/lib/es6/curry.js"; import * as Caml_exceptions from "bs-platform/lib/es6/caml_exceptions.js"; -var JsError = Caml_exceptions.create("JsPromise-FlowsUserApp.JsError"); +var JsError = /* @__PURE__ */Caml_exceptions.create("JsPromise-FlowsUserApp.JsError"); function $$catch(promise, callback) { return promise.catch(function (err) { diff --git a/dApp/user/src/lib/Old/RootProvider.bs.js b/dApp/user/src/lib/Old/RootProvider.bs.js index 21f362e..455e611 100644 --- a/dApp/user/src/lib/Old/RootProvider.bs.js +++ b/dApp/user/src/lib/Old/RootProvider.bs.js @@ -171,10 +171,14 @@ function useCurrentUser(param) { } +function useCurrentUserExn(param) { + return Belt_Option.getExn(useCurrentUser(undefined)); +} + function useIsAddressCurrentUser(address) { var currentUser = useCurrentUser(undefined); if (currentUser !== undefined) { - return Ethers$FlowsUserApp.Utils.toLowerString(address) === Ethers$FlowsUserApp.Utils.toLowerString(Caml_option.valFromOption(currentUser)); + return Ethers$FlowsUserApp.Utils.ethAdrToLowerStr(address) === Ethers$FlowsUserApp.Utils.ethAdrToLowerStr(Caml_option.valFromOption(currentUser)); } else { return false; } @@ -228,6 +232,10 @@ function useSigner(param) { } +function useSignerExn(param) { + return Belt_Option.getExn(useSigner(undefined)); +} + function useActivateConnector(param) { var context = Core.useWeb3React(); var match = React.useState(function () { @@ -277,6 +285,7 @@ export { RootContext , RootWithWeb3 , useCurrentUser , + useCurrentUserExn , useIsAddressCurrentUser , useEthBalance , useNetworkId , @@ -284,6 +293,7 @@ export { useDeactivateWeb3 , useWeb3 , useSigner , + useSignerExn , useActivateConnector , make$1 as make, diff --git a/dApp/user/src/lib/Old/RootProvider.res b/dApp/user/src/lib/Old/RootProvider.res index b7ffdc3..f0d569b 100644 --- a/dApp/user/src/lib/Old/RootProvider.res +++ b/dApp/user/src/lib/Old/RootProvider.res @@ -136,11 +136,13 @@ let useCurrentUser: unit => option = () => { } } +let useCurrentUserExn = () => useCurrentUser() -> Option.getExn + let useIsAddressCurrentUser: Ethers.ethAddress => bool = address => { let currentUser = useCurrentUser() switch currentUser { | Some(currentUserAddress) => - address->Ethers.Utils.toLowerString == currentUserAddress->Ethers.Utils.toLowerString + address->Ethers.Utils.ethAdrToLowerStr == currentUserAddress->Ethers.Utils.ethAdrToLowerStr | None => false } } @@ -185,6 +187,8 @@ let useSigner: unit => option = () => { } } +let useSignerExn = () => useSigner() -> Option.getExn + type connection = | Standby | Connected diff --git a/dApp/user/yarn.lock b/dApp/user/yarn.lock index 9182877..dd20cdb 100644 --- a/dApp/user/yarn.lock +++ b/dApp/user/yarn.lock @@ -4523,10 +4523,10 @@ bs-moment@^0.6.0: resolved "https://registry.yarnpkg.com/bs-moment/-/bs-moment-0.6.0.tgz#b49c5580598ef277696494684fd274ea1f8b1019" integrity sha512-AYtL5j6hXHwA+SZut4uhvlhDrabpqwjuXIa/0qfVpEjR45HGBruSZFxUcXwjtOGy9eoRc1FKjRM0n+X21oJb+w== -bs-platform@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-8.4.2.tgz#778dabd1dfb3bc95e0086c58dabae74e4ebdee8a" - integrity sha512-9q7S4/LLV/a68CweN382NJdCCr/lOSsJR3oQYnmPK98ChfO/AdiA3lYQkQTp6T+U0I5Z5RypUAUprNstwDtMDQ== +bs-platform@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-9.0.1.tgz#069dae007aadb400963cec25c8349ea1f3f6cae0" + integrity sha512-RxUrwxVBCx9AiiyFIthZwfPn+tAn9noRHCb9xJK4Uw2deGxIytqyoWDepMbH35v7EpxX0OhfXL5RrjHTaXersA== bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" @@ -12762,6 +12762,18 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +re-debouncer@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/re-debouncer/-/re-debouncer-2.1.0.tgz#2eaf7994ba41590f4f80d4a363ae8905156b7dc1" + integrity sha512-Iy9BecWF9X4V6zXfL3ROZpKGkCnpzYdjG/j6NIBxQn7gEwO2DzED5BsbfRUiPeqr7C0tAD0HPHF6tYIcQFEnyA== + +re-formality@4.0.0-beta.7: + version "4.0.0-beta.7" + resolved "https://registry.yarnpkg.com/re-formality/-/re-formality-4.0.0-beta.7.tgz#c29787034bcc3874251d7d214208951d270e2e47" + integrity sha512-vlfjdlua/idT+vjNb25KxF7LDzrSrycaoYNGmyABB6MVLEjVCIKeck3PXlSnd/d3CD2iBvircz1QEsUf0tjTug== + dependencies: + re-debouncer "2.1.0" + react-app-polyfill@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz#a0bea50f078b8a082970a9d853dc34b6dcc6a3cf"