Skip to content

Commit

Permalink
add attachContract/ store functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
clbrge committed Aug 28, 2022
1 parent bf3e9a2 commit 5698e58
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 49 deletions.
94 changes: 78 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import { defaultEvmStores } from 'svelte-web3'

:exclamation: `defaultEvmStores` was named before `defaultChainStore`. The
former naming has been removed in later versions of
`svelte-web3` package. Please update your code!
`svelte-web3` package.

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

Expand Down Expand Up @@ -196,6 +196,66 @@ not it's value).
const { name, chainId } = await $web3.eth.getChainId()

const balance = await $web3.eth.getBalance('0x0000000000000000000000000000000000000000') : ''

```

### Using the contracts store for reactive contract calls

To enjoy the same reactivity as using `$web3` but with a `web3.eth.Contract`
contract instance, you first need to declare its address and interface. To
differentiate each `eth.Contract` instance, you also need to define a logical
name. That's the function `attachContract`:


```html
<script>
import { defaultEvmStores as evm } from 'svelte-web3'
// ...
evm.attachContract('myContract',<address>, <abi>)
</script>
```

`attachContract` only needs to be called once and can be called before
connection since `web3.eth.Contract` instances will only be created when
a connection becomes available. You may want to reattach new contract
definition or abi for example when you the current network change. For
the old definition will be overwritten and instance updated in the
`contracts` store, simply use the same logical name.

After a contract as be declared, you can use its instance anywhere
using the `$` notation and the logical name :

```html
<script>
import { contracts } from 'svelte-web3'
// ...
</script>


{#await $contracts.myContract.methods.totalSupply().call()}

<span>waiting...</span>

{:then value}

<span>result of contract call totalSupply on my contract : { value } </span>

{/await}

```

By default, `svelte-web3` build contract instances using the current connection
options. You may want to overwrite options by passing them as fourth argument.

```js
defaultEvmStores.attachContract('myContract', <address>, <abi>, { from: <account>, ... })
```

### Reading stores outside of Svelte files
Expand Down Expand Up @@ -292,19 +352,6 @@ import { getChainDataByChainId } from 'svelte-web3'
console.log( getChainDataByChainId(5) )
```

## Create contract stores

The function `makeContractStore` allows you to create a Svelte derived
store of a `web3.eth.Contract` object instance. It takes the same
parameters as a ̀new web3.eth.Contract` call:

```js
makeContractStore(jsonInterface[, address][, options])
```

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


## Web3 Svelte component [ experimental ]

Expand All @@ -324,6 +371,19 @@ discussions in our [Discord](https://discord.gg/7yXuwDwaHF).

```

## Create contract stores (deprecated)

The function `makeContractStore` allowed you to create a Svelte derived
store of a `web3.eth.Contract` object instance. It takes the same
parameters as a ̀new web3.eth.Contract` call:

```js
makeContractStore(jsonInterface[, address][, options])
```

This function will be removed in the next major release of `svelte-web3`.
Please update your code to use the new $contracts store.

## Simultaneous multi chain usage

You can also using the library to create several stores, each
Expand Down Expand Up @@ -394,16 +454,18 @@ Please check [examples/sveltekit-app-template-web3 on github](https://github.com

### Svelte basic example (based on rollup template)

:exclamation: This is a legacy example and will be removed in future version of `svelte-web3`.

Please check [examples/svelte-app-template-web3 on github](https://github.com/clbrge/svelte-web3/tree/master/examples/svelte-app-template-web3).

This is a legacy example and will be removed in future version of `svelte-web3`.

Contains demos to use the default store and multi stores.

### Sapper basic example (based on webpack template)

:exclamation: This is a legacy example and will be removed in future version of `svelte-web3`.

Please check [examples/sapper-app-template-web3 on github](https://github.com/clbrge/svelte-web3/tree/master/examples/sapper-app-template-web3).
This is a legacy example and will be removed in future version of `svelte-web3`.


### tradingstrategy.ai presented at EthLisbon 2021
Expand Down
33 changes: 26 additions & 7 deletions examples/svelte-vite-template-web3/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@
import ethereumLogo from '../public/ethereum.svg'
import Counter from './lib/Counter.svelte'
import Providers from './Providers.svelte'
$: route = window.location.pathname
import { defaultEvmStores as evm, connected, web3, evmProviderType, selectedAccount, chainId, chainData } from 'svelte-web3'
import Providers from './Providers.svelte'
import Contracts from './Contracts.svelte'
import Menu from './Menu.svelte'
// super basic router
let route = window.location.pathname || '/'
function click(e) {
var x = e.target.closest('a'), y = x && x.getAttribute('href');
if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.button || e.defaultPrevented) return;
if (!y || x.target || x.host !== location.host || y[0] == '#') return;
e.preventDefault()
history.pushState(y, '', y)
route = y
}
addEventListener('click', click)
</script>

Expand All @@ -18,9 +29,9 @@

<Providers />

{:else if /setprovidex/.test(route)}
{:else if /contracts/.test(route)}

<Providers />
<Contracts />

{:else}

Expand Down Expand Up @@ -49,12 +60,20 @@
Before using the svelte-web3 stores, you need to connect to a provider: <a href="/setprovider">here are a few examples</a>...
</p>

<p>
Use the contracts store for easy access to any web3 smart-contract instance: <a href="/contracts">erc20 example</a>...
</p>

<p class="read-the-docs">
Click on the Vite, Svelte and web3.js/Ethereum logos to learn more
</p>

{/if}

{#if !/^\/$/.test(route)}
<Menu />
{/if}

</main>

<style>
Expand Down
10 changes: 0 additions & 10 deletions examples/svelte-vite-template-web3/src/Providers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,8 @@

</div>

<hr />

<a class="menu" href="/"> Home </a>


<style>
.content {
font-size: 90%;
min-height: calc(100vh - 10em);
}
select {
margin-top: 1em;
padding: 0.5em;
Expand Down
16 changes: 16 additions & 0 deletions examples/svelte-vite-template-web3/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,19 @@ button:focus-visible {
background-color: #f9f9f9;
}
}

/* svelte-web3|ethers */

.content {
font-size: 90%;
min-height: calc(100vh - 10em);
}

.menu {
display: flex;

}

.menu a {
width: 30%;
}
67 changes: 51 additions & 16 deletions src/stores.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export const createStore = () => {
const { emit, get, subscribe, assign, deleteAll } = proxied()

const switch1193Provider = async ({
accounts,
chainId,
accounts,
addressOrIndex = 0
}) => {
// console.log('switch1193Provider', { accounts, chainId }, get('web3'), get('eipProvider'))
Expand Down Expand Up @@ -170,7 +170,23 @@ export const createStore = () => {
setBrowserProvider,
setProvider,
disconnect,
close: disconnect,
subscribe,
get
}
}

export const createContractStore = () => {
const { emit, get, subscribe, assign, deleteAll } = proxied()

const attachContract = async (name, address, abi, options) => {
assign({
[name]: [address, abi, options]
})
emit()
}

return {
attachContract,
subscribe,
get
}
Expand All @@ -193,6 +209,8 @@ const subStoreNames = ['web3', 'selectedAccount', 'connected', 'chainId']

export const makeEvmStores = name => {
const evmStore = (allStores[name] = createStore())
const registry = createContractStore()
const target = {}

allStores[name].web3 = derived(evmStore, $evmStore => {
if (!$evmStore.web3) return { utils: Web3.utils, version: Web3.version }
Expand Down Expand Up @@ -231,15 +249,31 @@ export const makeEvmStores = name => {
return 'Unknown'
})

allStores[name].contracts = derived(
[ evmStore, registry ],
([ $evmStore, $registry ]) => {
if (!$evmStore.connected) return target
for (let key of Object.keys($registry)) {
target[key] = new $evmStore.web3.eth.Contract($registry[key][1], $registry[key][0], $registry[key][2])
}
return target
}
)

// force one subscribtion on $contracts so it's defined via proxy
allStores[name].contracts.subscribe(()=>{})

return new Proxy(allStores[name], {
get: function (internal, property) {
if (property === '$contracts') return target
if (/^\$/.test(property)) {
// TODO forbid deconstruction !
property = property.slice(1)
if (subStoreNames.includes(property))
return allStores[name].get(property)
throw new Error(`[svelte-web3] no value for store named ${property}`)
}
if (property === 'attachContract') return registry.attachContract
if (
[
'subscribe',
Expand All @@ -266,32 +300,33 @@ export const getChainStore = name => {
return allStores[name]
}

export const makeContractStore = (abi, address, defaults = {}) =>
derived([web3, connected], ([$web3, $connected]) => {
if ($connected && $web3.eth) {
return new $web3.eth.Contract(abi, address, defaults)
}
return null
})

loadWeb3()

export { chains as allChainsData }

export const getChainDataByChainId = id => (allChainsData.filter(o => o.chainId === id) || [{}])[0]
export const getChainDataByChainId = id => (chains.filter(o => o.chainId === id) || [{}])[0]

export const defaultEvmStores = makeEvmStores('default')

export const connected = allStores.default.connected
export const chainId = allStores.default.chainId
export const evmProviderType = allStores.default.evmProviderType
export const chainData = allStores.default.chainData

export const selectedAccount = allStores.default.selectedAccount
export const web3 = allStores.default.web3
export const chainData = allStores.default.chainData

export const evmProviderType = allStores.default.evmProviderType
export const contracts = allStores.default.contracts

// TODO spin off dectector
export const walletType = allStores.default.walletType

// legacy naming

export const defaultChainStore = defaultEvmStores
// TODO legacy makeContractStore to be removed
export const makeContractStore = (abi, address, defaults = {}) =>
console.warn('makeContractStore is deprecated. Please use teh new $contracts store')
derived([web3, connected], ([$web3, $connected]) => {
if ($connected && $web3.eth) {
return new $web3.eth.Contract(abi, address, defaults)
}
return null
})

0 comments on commit 5698e58

Please sign in to comment.