-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial E2E Working Setup #1
Conversation
1. added start:examples-ts script to launch example app 2. added postinstall to get examples/ts-frontend setup
|
||
Open app in `localhost:1234` | ||
|
||
For wallet connect, please use `test.walletconnect.org` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I was also having Safe issue, think the bridge URI was down when testing. Walletconnect integration is buggy at best. Not sure how to proceed on that front but will test more
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should extract the wallet connect part from this PR.
We can create one PR that just adds Wallet Connect integration, then merge it when it's fully working?
If we have some specific case it's not working, like the ones I shared, we can make it easily reproducible and share with Wallet Connect team.
"react-dom": "^16.8.6", | ||
"react-hot-loader": "^4.12.10", | ||
"styled-components": "^4.3.2", | ||
"web3": "^2.0.0-alpha.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the doc of web3, they recommend not using 2.0 for production.
https://web3js.readthedocs.io/en/v2.0.0-alpha.1/getting-started.html
Should we use 1.0 instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Velenir thoughts here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my experience 2.0.0 is more stable than 1.0. Certainly has more fixes included. Remember they were both renamed from beta only a month ago. 1.0 is more stable in the sense that truffle and co. are using it, that's all
On a semi-related note, I think we may be better off not dealing with web3 at all, only provider. Leave web3 (or etherjs or whatever) instantiation to anyone using the library. Everything from accountsChange to WC session management can be done with provider only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, right now is a copy of 1.0 but if they write that is not mainnet ready it's because they expect breaking changes.
I think the app we have to do is super basic, it should be ok to work with "not the last version". If there's a big reason to jump for the unstable, we could review it, otherwise I think is not worth it at this point. We can revisit this in a few months.
src/api/utils.ts
Outdated
@@ -0,0 +1,20 @@ | |||
export const windowLoaded = new Promise((accept, reject) => { | |||
if (typeof window === 'undefined') { | |||
return accept() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the promise resolved if there's no window object?
Shouldn't be this handled as an exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better as to not block the rest of the logic that isn't entirely window
dependent - though tbh I haven't tested this logic with a non window
exposing device
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me thinks, leave waiting on window to the library users. If they need it, like ens only ever set up react on window.onload
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if the method is "windowLoaded" and there's no window, it seems or to return a failed promise.
Also, note that you already fail if it doesn't allow you to subscribe the listener:
You don't block the user. The one using the library decides how to handle the exception. You shouldn't decide for the user of the util function what to do if there's something wrong, like "no window"
} catch (error) { | ||
if (error.readyState === 4 && (error.status === 400 || error.status === 200)) { | ||
// the endpoint is active | ||
console.debug('API/WEB3.ts catch block => Success') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean?
Will be provider = new Web3(url)
always initialized in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for readOnly
mode, yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get:
1. What is a null provider (it's asked already in other comment)provider = new Web3(null)
-
The logic: if the fetch fails, what is this branch doing. It seems complicated:
-
This logic. Are we always using metamask if it's installed or do we allow the user to choose. For example, if we want to use wallet connect, it will prompt in the metamask extension.
Also, do we really need to hardcode the development ganache as a fallback?
} else { | ||
console.debug('API/WEB3.ts => No web3 instance injected. Falling back to null provider.') | ||
readOnly = true | ||
provider = new Web3(null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a null provider?
Also, what is "readOnly"? If there's no connection with the node you can't read, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
App loads, no blocking, no thrown error, user can see the app, better UX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, I'm not sure about this. This getProvider
returns a promise: Promise<Web3>
This means, that if something is wrong, we can handle the case.
One solution is to do something like:
resolve(null)
signaling, there's no Web3 object cause we didn't connect the wallet yet. The app can react and handle that case. So you can still have a good UX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to do a full review before my trip, but I didn't manage. Anyways, I think it's enough review, we can review first the open conversations, since there are many already :D
It think it's a super good start. I understand that this PR got big and next iterations should be smaller in changes and scope, I think it's ok this one time exception, but it made the review much more complicated and I think for both sides, since we'll have a lot of things to discuss here.
Many of the discussions opened aim to open discussions about project structure, code style and so on, because I think it's good to do this exercise now so we are all in sync
|
||
/* Provider Specific */ | ||
const setProvider = web3.setProvider.bind(web3) | ||
const resetProvider = (): void => setProvider(web3.currentProvider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need resetProvider
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we don't use it, we should remove.
If it's a bug of a provider, we can review that specific case
1. ethereum utils imported from Web3 2. misc utils 3. exported from index
1. global file with constants 2. types file (global) 3. removed typings.d.ts + old utils file
1. allow synthetic imports into examples ts 2. removed unused lines
1. moved types into global 2. clarified names of fns 3. removed async catching 4. fixed tests to naming changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All positive changes since last review!
I added several replies and comments.
One of the most important things to get this merged, is to separate wallet connect from this PR until it's stable.
const setter = async (): Promise<void> => { | ||
const accountsProm = web3Api.getAccounts() | ||
const networkNameProm = web3Api.getNetworkName() | ||
const clientVersionProm = web3Api.web3.currentProvider.send('web3_clientVersion', []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there was already a web3 method for this.
Anyways. How do you handle the null provider
thing we are discussing in the other thread.
As I wrote in the other thead. If the wallet is not connected. I think it's better to have a null web3Api
and handle that case.
If the wallet re-connects, this component function will be called again with a non-null value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No universal method for that. Won't work with some wallets, and this method will
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But one of my comments is, I saw in other parts of the PR that web3Api.web3.currentProvider
can be null
in some cases.
This will crash if it's the case, right?
Anyways, I also think is nor right to do the new Web3(null)
, we should handle the case differently too (there's other conversation for this topic)
Also, I would argue that the web3Api.web3.currentProvider.send(...)
is a bit coupled, and since you already have an interface for interacting web3Api
it would be simpler and more readable to do sth like web3Api.sendRpc(...)
|
||
export const DisplayWeb3: React.FC<DisplayWeb3Props> = ({ web3Api }) => { | ||
const [account, setAccount] = useState<string>() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
supernit: I would get rid of this line breaks
|
||
useEffect(() => { | ||
const setter = async (): Promise<void> => { | ||
const accountsProm = web3Api.getAccounts() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clientVersionProm, | ||
]) | ||
|
||
ReactDOM.unstable_batchedUpdates(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is unstable_batchedUpdates
?
I'm not super familiar, made me curious the word "unstable"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's what they use internally in event handlers to avoid multiple rerenders.
React-redux also uses it, so even if it's called unstable everyone does it
const [web3Api, setWeb3FrontendAPI] = useState<Web3Api>() | ||
|
||
return ( | ||
<> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is not needed
<Web3Connect.Button | ||
providerOptions={{ | ||
walletconnect: { | ||
bridge: 'https://safe-walletconnect.dev.gnosisdev.com/', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this hardcoded?
What is this bridge?
GOERLI: 'GOERLI', | ||
} | ||
|
||
export const NETWORK_ID_TO_NAME = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is not enough a single object for the networks?
return getWeb3Api(options) | ||
} catch (error) { | ||
console.error('launchDappUI Error!', error) | ||
throw new Error(error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we rethrow. Shouldn't we handle this error?
getTransaction: (txHash: string) => Promise<Transaction> | ||
getTransactionReceipt: (txHash: string) => Promise<TransactionReceipt> | ||
// Utils | ||
isReadOnly: () => boolean |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this. I would think this only make sense if we allow users to "sign in" by entering an address, so we show a read only version.
I think what you were using this for is to display a limited UI when there's an error connecting the wallet. But I think we can take other approaches (there's other open discussions about it, so no need to add it here too )
@@ -0,0 +1,3 @@ | |||
import { isAddress, toBN, isBN, isBigNumber, fromAscii, toHex, toWei, fromWei } from 'web3-utils' | |||
|
|||
export { isAddress, toBN, isBN, isBigNumber, fromAscii, toHex, toWei, fromWei } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good one! I think it was the right move to move these utils from the API to here.
@W3stside I see it has the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lest resolve the open conversations before this gets merged.
superseded but the waterfall 💧 |
This PR is large. But can be visualised and broken down into parts:
This provides an E2E testable app. Lots of the commits are just setup related. The main files are explained below. All following PRs will be focused.
Please follow the
README
to test - but should run simply vianpm i && npm run start:example-ts
src/api/web3.ts
sets upWeb3Api
singleton able to be passed arounda. Tested inside
./tests/api/index.test.ts
src/components/Web3ConnectButton.tsx
pullsweb3connect
button and uses the initialisedWeb3Api
src/components/DisplayWeb3.tsx
takes the instantiatedWeb3Api
and displays:a. Account
b. ETH Balance
c. Network Name
d. Client (Provider) Version
./examples/ts-frontend
is used to test the components/setup fns in points1-3