Skip to content

Commit

Permalink
better setProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
clbrge committed Jan 2, 2022
1 parent 84978c5 commit daa3cf6
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 66 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ No software is bug-free. So, if you got an issue, follow these steps:
- Clear title (shorter is better).
- Describe the issue in clear language.
- Share error logs, screenshots and etc.
- To speed up the issue fixing process, send us a sample repo with the issue you faced:
- To speed up the issue fixing process, send us a sample repo with the issue you faced.

## Pull Requests (PRs)

We welcome all contributions. There are many ways you can help us. This is few of those ways:
We welcome all contributions.
85 changes: 67 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Svelte or JavaScript files :
import { connected, web3, selectedAccount, chainId, chainData } from 'svelte-web3'
```

* connected: store value is true a connection has been set up.
* connected: store value is true if a connection has been set up.
* web3: store value is a Web3.js instance when connected.
* selectedAccount: store value is the current selected account (when connected).
* chainId: store value is the current chainId when connected.
Expand All @@ -68,37 +68,82 @@ former naming still works but will be removed in later versions of

### Connection with the browser provider (eg wallets like Metamask)

To enable a connection with the current window provider, simply call
`setBrowserProvider` on the library abstract helper:
To enable a connection with the current [EIP-1193
provider](https://eips.ethereum.org/EIPS/eip-1193#appendix-i-consumer-facing-api-documentation)
injected in the browser `window` context, simply call `setProvider` on
the library abstract helper with no argument:

```js
defaultEvmStores.setBrowserProvider()
defaultEvmStores.setProvider()
```

Please note that `setBrowserProvider` can only to be executed in a browser
context. So you may want to use `onMount` when using Sapper or
SvelteKit. Similarly, you cannot use `setBrowserProvider` in SSR
context.
Please note that `setProvider` can only to be called with no argument
in a browser context. So you may want to use `onMount` when using
Sapper or SvelteKit. Similarly, you cannot use `setProvider` with no
argument in SSR context.

```js
onMount(
() => {
// add a test to return in SSR context
defaultEvmStores.setBrowserProvider()
defaultEvmStores.setProvider()
}
)
```

### Connection with other providers (ws, http, Web3Modal, Walletconnect, etc)
`svelte-web3` will automatically update the stores when the network or
accounts change and remove listeners at disconnection.

To enable connection using an URL string or a valid provider object
(for example as returned by web3Modal or WalletConnect):
:exclamation: previous version of `svelte-web3` were using a special
method `setBrowserProvider`. The former naming still works but will be
removed in later versions. Please update your code!


### Connection with non injected EIP-1193 providers

To connect to non injected EIP-1193 providers like :

* buidler.dev
* ethers.js
* eth-provider
* WalletConnect
* Web3Modal

Call `setProvider` on the library abstract helper with the js provider
instance object of the library. For example with Web3Modal :

```js
defaultEvmStores.setProvider(<ws/https or http provider url or provider Object>)
const web3Modal = new Web3Modal(<your config>)
const provider = await web3Modal.connect()
defaultEvmStores.setProvider(provider)
```

Please check `examples/svelte-app-template-web3/src/Web3Modal.svelte` in github.
`svelte-web3` will automatically update the stores when the network or
accounts change and remove listeners at disconnection.

Please check
`examples/svelte-app-template-web3/src/Web3Modal.svelte`(https://github.com/clbrge/svelte-web3/tree/master/examples/svelte-app-template-web3/src/Web3Modal.svelte).
in github for a complete example.

### Connection with other Web3 providers (ws, http, ipc, ...)

Any provider supported by Web3.js can also be used with `setProvider`.
A WS or HTTP RPC string URL or any providers returned by `new
Web3.providers`, for example :

```js
defaultEvmStores.setProvider('https://rinkeby.infura.io/v3/your-api-key')
// or
defaultEvmStores.setProvider('https://eth-mainnet.alchemyapi.io/v2/your-api-key')
// or
defaultEvmStores.setProvider('http://localhost:8545')
// or
defaultEvmStores.setProvider(new Web3.providers.WebsocketProvider('ws://localhost:8546'))
// or
var net = require('net')
defaultEvmStores.setProvider(new Web3.providers.IpcProvider('/Users/myuser/Library/Ethereum/geth.ipc', net))
// etc...
```

### Using the stores

Expand Down Expand Up @@ -178,8 +223,12 @@ defaultEvmStores.disconnect()

The information returned by the `chainData` store depends (like all
other web3 stores) on which chain the current provider is
connected. If the store has not yet been connected (with `setProvider`
or `setBrowserProvider`), the store value will be `undefined`.
connected. If the store has not yet been connected (with
`setProvider`), the store value will be `undefined`.

This object is extremely useful to build components that reactively
update all variables elements that depends on the current active chain
or account.

Below is the CAIP-2 formatted information when the default store is
connected with the Ethereum Mainnet :
Expand Down Expand Up @@ -235,8 +284,8 @@ parameters as a ̀new web3.eth.Contract` call:
makeContractStore(jsonInterface[, address][, options])
```

This store is conveniently and automatically updated after connection
and when the account or chain change.
This store is also conveniently and automatically updated after
connection and when the account or chain change.


## Web3 Svelte component [ experimental ]
Expand Down
7 changes: 6 additions & 1 deletion examples/svelte-app-template-web3/src/Web3Modal.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
import { defaultEvmStores, web3, selectedAccount, connected, chainId, chainData } from 'svelte-web3'
import { defaultEvmStores, web3, selectedAccount, walletType, connected, chainId, chainData } from 'svelte-web3'
import { Balance } from 'svelte-web3/components'
const Web3Modal = window.Web3Modal.default
Expand Down Expand Up @@ -55,6 +55,11 @@
Selected account: {$selectedAccount || 'not defined'}
</p>

<p>
Wallet type: {$walletType || 'not defined'}
</p>


<p>Selected account balance = <Balance address={checkAccount} /> {$chainData.nativeCurrency?.symbol}</p>

{/if}
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
},
"dependencies": {
"svelte": "^3.0.0",
"svelte-proxied-store": "^1.1.0"
"svelte-proxied-store": "^1.1.2"
},
"devDependencies": {
"eslint-plugin-svelte3": "^3.2.1",
Expand Down
90 changes: 53 additions & 37 deletions src/stores.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export const createStore = () => {
const init = () => {
loadWeb3()
if (!Web3.version) throw new Error('[svelte-web3] Cannot find Web3')
if (getWindowEthereum()) getWindowEthereum().autoRefreshOnNetworkChange = false
if (get('eipProvider') && get('eipProvider').removeListener) {
get('eipProvider').removeListener('accountsChanged', () => switch1193Provider())
get('eipProvider').removeListener('chainChanged', () => switch1193Provider())
}
deleteAll()
assign({
connected: false,
Expand All @@ -47,57 +50,69 @@ export const createStore = () => {
})
}

const setProvider = async (evmProvider, callback) => {
init()
const web3 = new Web3(evmProvider)
const chainId = await web3.eth.getChainId()
// no account with ganache use try catch ?
const accounts = /127/.test(evmProvider) ? [] : await web3.eth.getAccounts()
if (callback) {
web3._provider.removeListener('accountsChanged', () => setProvider(evmProvider, true))
web3._provider.removeListener('chainChanged', () => setProvider(evmProvider, true))
} else {
if (web3._provider && web3._provider.on) {
web3._provider.on('accountsChanged', () => setProvider(evmProvider, true))
web3._provider.on('chainChanged', () => setProvider(evmProvider, true))
}
const switch1193Provider = async accounts => {
const chainId = await get('web3').eth.getChainId()
if (!accounts) {
accounts = await get('eipProvider').request({ method: 'eth_requestAccounts' })
}
assign({
web3,
selectedAccount: accounts.length ? accounts[0] : null,
connected: true,
selectedAccount: accounts.length ? accounts[0] : null,
chainId,
evmProvider: web3._provider,
evmProviderType: typeof evmProvider === 'string' ? 'RPC' : 'Web3',
accounts
})
emit()
}

const setBrowserProvider = async () => {
const set1193Provider = async eipProvider => {
init()
const accounts = await eipProvider.request({ method: 'eth_requestAccounts' })
const web3 = new Web3(eipProvider)
assign({
web3,
eipProvider,
evmProviderType: 'EIP1193',
accounts
})
if (eipProvider.on) {
// TODO handle disconnect/connect events
eipProvider.on('accountsChanged', () => switch1193Provider())
eipProvider.on('chainChanged', () => switch1193Provider())
}
return switch1193Provider(accounts)
}

const setProvider = async evmProvider => {
if (!evmProvider) {
if (!getWindowEthereum()) throw new Error('[svelte-web3] Please authorize browser extension (Metamask or similar)')
getWindowEthereum().autoRefreshOnNetworkChange = false
return set1193Provider(getWindowEthereum())
}
if (typeof evmProvider === 'object' && evmProvider.request) return set1193Provider(evmProvider)
init()
if (!getWindowEthereum()) throw new Error('[svelte-web3] Please authorize browser extension (Metamask or similar)')
const accounts = await getWindowEthereum().request({ method: 'eth_requestAccounts' })
getWindowEthereum().on('accountsChanged', setBrowserProvider)
getWindowEthereum().on('chainChanged', setBrowserProvider)
const web3 = new Web3(getWindowEthereum())
const web3 = new Web3(evmProvider)
const chainId = await web3.eth.getChainId()
let accounts = []
try {
// not all provider support accounts
accounts = await web3.eth.getAccounts()
} catch (e) {
console.warn(e)
}
assign({
web3,
selectedAccount: accounts.length ? accounts[0] : null,
connected: true,
chainId,
evmProvider: getWindowEthereum(),
evmProviderType: 'Browser',
evmProviderType: 'Web3',
accounts
})
emit()
}

const disconnect = async (evmProvider) => {
if(evmProvider && evmProvider.disconnect) {
await evmProvider.disconnect()
}
const setBrowserProvider = () => setProvider()

const disconnect = async () => {
init()
emit()
}
Expand Down Expand Up @@ -150,11 +165,11 @@ export const makeEvmStores = name => {

allStores[name].evmProviderType = derived(evmStore, $evmStore => $evmStore.evmProviderType)
allStores[name].walletType = derived(evmStore, $evmStore => {
if (!$evmStore.provider) return null
if (typeof $evmStore.provider === 'string') return $evmStore.provider
if ($evmStore.provider.isMetaMask) return 'MetaMask (or compatible)'
if ($evmStore.provider.isNiftyWallet) return 'Nifty'
if ($evmStore.provider.isTrust) return 'Trust'
if (!$evmStore.eipProvider) return null
if (typeof $evmStore.eipProvider === 'string') return $evmStore.eipProvider
if ($evmStore.eipProvider.isMetaMask) return 'MetaMask (or compatible)'
if ($evmStore.eipProvider.isNiftyWallet) return 'Nifty'
if ($evmStore.eipProvider.isTrust) return 'Trust'
return 'Unknown'
})

Expand All @@ -166,7 +181,8 @@ export const makeEvmStores = name => {
if (subStoreNames.includes(property)) return allStores[name].get(property)
throw new Error(`[svelte-web3] no value for store named ${property}`)
}
if (['subscribe', 'get', 'setBrowserProvider', 'setProvider', 'evmProviderType', 'chainData', 'walletType', 'close', 'disconnect', ...subStoreNames].includes(property))
if (['subscribe', 'get', 'setBrowserProvider', 'setProvider', 'evmProviderType',
'chainData', 'walletType', 'close', 'disconnect', ...subStoreNames].includes(property))
return Reflect.get(internal, property)
throw new Error(`[svelte-web3] no store named ${property}`)
}
Expand Down

0 comments on commit daa3cf6

Please sign in to comment.