diff --git a/.prettierignore b/.prettierignore index 75d4e18..b807fed 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,3 @@ src/chains.js +dist +example diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b68a4d0..28dd66c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to ethers-svelte -Thank you for your interest in improving ethers-svelte! We welcome contributions of all kinds: from discussions and documentation to bug fixes and feature enhancements. +Thank you for your interest in improving ethers-svelte! We welcome contributions of all kinds: from discussions and documentation to bug fixes and feature enhancements. Please review this document to help streamline the process and save everyone's precious time. diff --git a/README.md b/README.md index 4003998..95344f7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # ethers-svelte Use the [ethers.js library](https://docs.ethers.io/) as a @@ -35,16 +34,24 @@ or the selected account change. You can import them directly in any Svelte or JavaScript files : ```js -import { connected, provider, chainId, chainData, signer, signerAddress, contracts } from 'ethers-svelte' +import { + connected, + provider, + chainId, + chainData, + signer, + signerAddress, + contracts, +} from "ethers-svelte"; ``` - * connected: store value is true if a connection has been set up. - * provider: store value is an Ethers.js Provider instance when connected. - * chainId: store value is the current chainId when connected (always BigInt) - * chainData: store value is the current blokchain CAIP-2 data (when connected), see below. - * signer: store value is an Ethers.js Signer instance when connected. - * signerAddress: store value is a shortcut to get `$signer.getAddress()` when connected. - * contract: store value is an Object for all ethers.Contract instances you need. +- connected: store value is true if a connection has been set up. +- provider: store value is an Ethers.js Provider instance when connected. +- chainId: store value is the current chainId when connected (always BigInt) +- chainData: store value is the current blokchain CAIP-2 data (when connected), see below. +- signer: store value is an Ethers.js Signer instance when connected. +- signerAddress: store value is a shortcut to get `$signer.getAddress()` when connected. +- contract: store value is an Object for all ethers.Contract instances you need. For these stores to be useful in your Svelte application, a connection to an EVM blockchain first need to established . The abstract helper @@ -52,7 +59,7 @@ to an EVM blockchain first need to established . The abstract helper automatically instanciate all stores. ```js -import { defaultEvmStores } from 'ethers-svelte' +import { defaultEvmStores } from "ethers-svelte"; ``` ### Connection with the browser provider (eg wallets like Metamask) @@ -63,7 +70,7 @@ injected in the browser `window` context, simply call `setProvider` on the library abstract helper with no argument: ```js -defaultEvmStores.setProvider() +defaultEvmStores.setProvider(); ``` Please note that `setProvider` can only to be called with no argument @@ -72,27 +79,24 @@ 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.setProvider() - } - ) +onMount(() => { + // add a test to return in SSR context + defaultEvmStores.setProvider(); +}); ``` `ethers-svelte` will automatically update the stores when the network or accounts change and remove listeners at disconnection. - ### Connection with non injected EIP-1193 providers To connect to non injected EIP-1193 providers like : - * buidler.dev - * ethers.js - * eth-provider - * WalletConnect - * Web3Modal +- buidler.dev +- ethers.js +- eth-provider +- WalletConnect +- Web3Modal Call `setProvider` on the library abstract helper with the JavaScript provider instance object of the library. For example with Web3Modal : @@ -106,7 +110,6 @@ defaultEvmStores.setProvider(provider) `ethers-svelte` will automatically update the stores when the network or accounts change and remove listeners at disconnection. - ### Connection with other Ethers.js providers (ws, http, ipc, ...) You can instanciate many types of providers using Ethers.js, see the @@ -116,9 +119,9 @@ simply pass them as argument to `defaultEvmStores.setProvider()` to inititate th ```js defaultEvmStores.setProvider(new ethers.providers.InfuraProvider()) -// or +// or defaultEvmStores.setProvider(new ethers.providers.EtherscanProvider()) -// or +// or defaultEvmStores.setProvider(new ethers.providers.AlchemyProvider()) // etc... ``` @@ -139,19 +142,15 @@ If you don't need a signer, you might also call `setProvider` with the argument `addressOrIndex` as a `null` value and bypass any attempt to detect an account. - ### Using the stores After a connection has been established, you may import the stores anywhere in your application. Most of the time, you should use the `$` prefix Svelte notation to access the stores values. - ```html {#if !$connected} @@ -168,9 +167,9 @@ prefix Svelte notation to access the stores values. ### Using the Ethers.js Providers and Signers API Likewise use the `$` prefix Svelte notation to access Provider or Signer -read-only abstractions and use the whole Ethers.js API. (beware, in the Ethers +read-only abstractions and use the whole Ethers.js API. (beware, in the Ethers library documentation, Provider or Signer instances are always noted as `provider` - and `signer`, without `$`, but in the context of `ethers-svelte`, this naming +and `signer`, without `$`, but in the context of `ethers-svelte`, this naming is used by the Svelte stores themselves encapsulating Provider or Signer instances). ```js @@ -181,14 +180,13 @@ is used by the Svelte stores themselves encapsulating Provider or Signer instanc const { name, chainId } = await $provider.getNetwork() const balance = await $signer.getBalance() - + $signer.sendTransaction({, , }); ``` For providers that don't support `getSigner`, the value `$signer` will be `null`. - ### Using the contracts store for reactive contract calls To enjoy the same reactivity as using `$provider` and `$signer` but @@ -196,16 +194,14 @@ with a contract instance, you first need to declare its address and interface. To differenciate each `ethers.Contract` instance, you also need to define a logical name. That's the function `attachContract`: - ```html ``` @@ -221,24 +217,20 @@ using the `$` notation and the logical name : ```html +{#await $contracts.myContract.totalSupply()} - {#await $contracts.myContract.totalSupply()} - - waiting... - - {:then value} +waiting... - result of contract call totalSupply on my contract : { value } +{:then value} - {/await} +result of contract call totalSupply on my contract : { value } +{/await} ``` By default, `ethers-svelte` build contract instances using the signer if @@ -246,7 +238,10 @@ available and if not the provider. You may want to force using the current provi by passing `false` as fourth argument. ```html - defaultEvmStores.attachContract('myContract',
, , ) +defaultEvmStores.attachContract('myContract', +
+ , , ) +
``` ### Reading stores outside of Svelte files @@ -257,13 +252,11 @@ values in pure javascript library without subscribing to the store, you can use a special getter on the library abstract helper: ```js -// this is not a Svelte file but a standard JavaScript file -import { defaultEvmStores } from 'ethers-svelte' +// this is not a Svelte file but a standard JavaScript file +import { defaultEvmStores } from "ethers-svelte"; if (defaultEvmStores.$selectedAccount) { - // do something if store selectedAccount is non null - } ``` @@ -273,7 +266,7 @@ Simply call the function `disconnect` directly on the on the library abstract helper: ```js -defaultEvmStores.disconnect() +defaultEvmStores.disconnect(); ``` ## Human readable chain CAIP-2 information @@ -291,7 +284,7 @@ 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 +Below is the CAIP-2 formatted information when the default store is connected with the Ethereum Mainnet : ```json @@ -315,11 +308,13 @@ connected with the Ethereum Mainnet : "networkId": 1, "slip44": 60, "ens": { "registry": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" }, - "explorers": [{ - "name": "etherscan", - "url": "https://etherscan.io", - "standard": "EIP3091" - }] + "explorers": [ + { + "name": "etherscan", + "url": "https://etherscan.io", + "standard": "EIP3091" + } + ] } ``` @@ -328,9 +323,9 @@ You might want to access all chains CAIP-2 data directly without using the the list of all CAIP-2 data available. ```js -import { allChainsData } from 'ethers-svelte' +import { allChainsData } from "ethers-svelte"; -console.log( allChainsData ) +console.log(allChainsData); ``` Another solution is to use the helper function `getChainDataByChainId` @@ -338,9 +333,9 @@ that takes the chainId as argument and returns the CAIP-2 data or an empty object if not found. ```js -import { getChainDataByChainId } from 'ethers-svelte' +import { getChainDataByChainId } from "ethers-svelte"; -console.log( getChainDataByChainId(5) ) +console.log(getChainDataByChainId(5)); ``` ## Ethers Svelte components @@ -362,14 +357,11 @@ See also the `components` route in the example directory. ``` - ## FAQ - -### *how to auto-connect on page load?* +### _how to auto-connect on page load?_ It is out of scope of this package to implement this function but it generally depends on the type of provider you are using and a way to store connection information between page loads (for example by using localStorage). - diff --git a/package.json b/package.json index b10a238..44509cf 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ }, "scripts": { "update-chains": "node ./scripts/update-chains.mjs", - "lint": "prettier-standard --lint 'src/**/*.js'", + "format": "prettier --plugin-search-dir . --write .", "build": "rollup -c" }, "dependencies": { @@ -39,17 +39,15 @@ }, "devDependencies": { "ethers": "^6.0.5", + "prettier-config-standard": "^5.0.0", "lint-staged": "^13.1.2", - "prettier-standard": "^16.4.1", "release-it": "^15.6.0", "rollup": "^3.17.2", "rollup-plugin-dts": "^5.2.0", "svelte": "^3.55.1" }, "lint-staged": { - "*": [ - "prettier-standard --lint" - ] + "*.{js,json,md}": "prettier --write" }, "release-it": { "github": { diff --git a/rollup.config.mjs b/rollup.config.mjs index 9cdea93..1ecd95a 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -5,12 +5,12 @@ export default [ input: "./src/stores.js", output: [ { file: "dist/index.mjs", format: "es" }, - { file: "dist/index.js", format: "umd", name: "ethers-stores" } - ] + { file: "dist/index.js", format: "umd", name: "ethers-stores" }, + ], }, { input: "./src/ethers-svelte.d.ts", output: [{ file: "dist/ethers-svelte.d.ts", format: "es" }], plugins: [dts()], }, -] +]; diff --git a/scripts/update-chains.mjs b/scripts/update-chains.mjs index 9caefd4..83eb1d2 100644 --- a/scripts/update-chains.mjs +++ b/scripts/update-chains.mjs @@ -1,24 +1,24 @@ -import util from 'util' -import fetch from 'node-fetch' -import { writeFile } from 'fs/promises' +import util from "util"; +import fetch from "node-fetch"; +import { writeFile } from "fs/promises"; const run = async () => { + const res = await fetch("https://chainid.network/chains.json"); - const res = await fetch('https://chainid.network/chains.json') + const chains = await res.json(); - const chains = await res.json() - - if (!chains.map( c => c.name ).includes('Ethereum Mainnet')) { - throw new Error('something wrong... check!') + if (!chains.map((c) => c.name).includes("Ethereum Mainnet")) { + throw new Error("something wrong... check!"); } - await writeFile('./src/chains.js', ` + await writeFile( + "./src/chains.js", + ` const chains = ${util.inspect(chains, { depth: null, maxArrayLength: null })} export default chains ` -) - -} + ); +}; -run() +run(); diff --git a/src/components/index.js b/src/components/index.js index 1e87876..b56369e 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,5 +1,5 @@ -import Balance from './Balance.svelte' -import Identicon from './Identicon.svelte' -import Jazzicon from './Jazzicon.svelte' +import Balance from "./Balance.svelte"; +import Identicon from "./Identicon.svelte"; +import Jazzicon from "./Jazzicon.svelte"; -export { Balance, Identicon, Jazzicon } +export { Balance, Identicon, Jazzicon }; diff --git a/src/ethers-svelte.d.ts b/src/ethers-svelte.d.ts index 94bfb45..aebd920 100644 --- a/src/ethers-svelte.d.ts +++ b/src/ethers-svelte.d.ts @@ -8,85 +8,85 @@ import type { Contract } from "@ethersproject/contracts"; * @see https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md */ export interface ChainData { + name: string; + chain: string; + network: string; + rpc: string[]; + faucets: string[]; + nativeCurrency: { name: string; - chain: string; - network: string; - rpc: string[]; - faucets: string[]; - nativeCurrency: { - name: string; - symbol: string; - decimals: number; - }; - infoURL: string; - shortName: string; - chainId: number; - networkId: number; + symbol: string; + decimals: number; + }; + infoURL: string; + shortName: string; + chainId: number; + networkId: number; + icon: string; + explorers: { + name: string; + url: string; icon: string; - explorers: { - name: string; - url: string; - icon: string; - standard: string; - }[]; + standard: string; + }[]; } export interface DefaultEVMStore { - /** - * Forces a disconnect (and event subscriptions from a provider) - */ - disconnect: () => Promise; - /** - * Enables a connection with the current window provider. - * Note that your code need to be in browser context when setProvider is running. - * So you may want to use onMount when using Sapper or Sveltekit. - * @param provider An url string or a valid provider object (as returned by web3Modal or WalletConnect for example) - * @param index Select another account than the default when possible. - */ - setProvider: (provider?: Provider, index?: string) => Promise; - /** - * To enjoy the same reactivity as using `$provider` and `$signer` but with a contract instance, you first need to declare its address and interface. - * To differentiate each `ethers.Contract` instance, you also need to define a logical name. - * - * This function only needs to be called once and can be called before connection since `ethers.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. - * - * @param name The logical name of the contract - * @param address The address of the contract - * @param abi The ABI of the contract - * @param fromSigner By default, svelte-ethers-store build contract instances using the signer if available and if not the provider. You may want to force using the current provider by passing `false`. - */ - attachContract: ( - name: string, - address: string, - abi: string, - fromSigner?: boolean - ) => Promise; - /** - * A stored Ethers.js Provider instance when connected. - */ - provider: Readable; - /** - * A stored Ethers.js Signer instance when connected. - */ - signer: Readable; - /** - * Current selected account address if connected, `null` otherwise. - */ - selectedAccount: Readable; - /** - * `true` if connection to the provider was successful. - */ - connected: Readable; - /** - * The current chainId (if connected). - */ - chainId: Readable; - /** - * Store value is a shortcut to get `$signer.getAddress()` when connected. - */ - signerAddress: Readable; + /** + * Forces a disconnect (and event subscriptions from a provider) + */ + disconnect: () => Promise; + /** + * Enables a connection with the current window provider. + * Note that your code need to be in browser context when setProvider is running. + * So you may want to use onMount when using Sapper or Sveltekit. + * @param provider An url string or a valid provider object (as returned by web3Modal or WalletConnect for example) + * @param index Select another account than the default when possible. + */ + setProvider: (provider?: Provider, index?: string) => Promise; + /** + * To enjoy the same reactivity as using `$provider` and `$signer` but with a contract instance, you first need to declare its address and interface. + * To differentiate each `ethers.Contract` instance, you also need to define a logical name. + * + * This function only needs to be called once and can be called before connection since `ethers.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. + * + * @param name The logical name of the contract + * @param address The address of the contract + * @param abi The ABI of the contract + * @param fromSigner By default, svelte-ethers-store build contract instances using the signer if available and if not the provider. You may want to force using the current provider by passing `false`. + */ + attachContract: ( + name: string, + address: string, + abi: string, + fromSigner?: boolean + ) => Promise; + /** + * A stored Ethers.js Provider instance when connected. + */ + provider: Readable; + /** + * A stored Ethers.js Signer instance when connected. + */ + signer: Readable; + /** + * Current selected account address if connected, `null` otherwise. + */ + selectedAccount: Readable; + /** + * `true` if connection to the provider was successful. + */ + connected: Readable; + /** + * The current chainId (if connected). + */ + chainId: Readable; + /** + * Store value is a shortcut to get `$signer.getAddress()` when connected. + */ + signerAddress: Readable; } /** diff --git a/src/stores.js b/src/stores.js index 79ac977..55d2c12 100644 --- a/src/stores.js +++ b/src/stores.js @@ -1,160 +1,160 @@ +import { ethers } from "ethers"; +import { proxied } from "svelte-proxied-store"; +import { derived } from "svelte/store"; -import { ethers } from 'ethers' -import { proxied } from 'svelte-proxied-store' -import { derived } from 'svelte/store' - -import chains from './chains.js' +import chains from "./chains.js"; /* eslint no-undef: "warn" */ const getGlobalObject = () => { - if (typeof globalThis !== 'undefined') { - return globalThis + if (typeof globalThis !== "undefined") { + return globalThis; } - if (typeof self !== 'undefined') { - return self + if (typeof self !== "undefined") { + return self; } - if (typeof window !== 'undefined') { - return window + if (typeof window !== "undefined") { + return window; } - if (typeof global !== 'undefined') { - return global + if (typeof global !== "undefined") { + return global; } - throw new Error('[ethers-svelte] cannot find the global object') -} + throw new Error("[ethers-svelte] cannot find the global object"); +}; const getWindowEthereum = () => { try { - if (getGlobalObject().ethereum) return getGlobalObject().ethereum + if (getGlobalObject().ethereum) return getGlobalObject().ethereum; } catch (err) { - console.error('[ethers-svelte] no globalThis.ethereum object') + console.error("[ethers-svelte] no globalThis.ethereum object"); } -} +}; export const createStore = () => { - const { emit, get, subscribe, assign, deleteAll } = proxied() + const { emit, get, subscribe, assign, deleteAll } = proxied(); // BrowserProvider const switch1193Provider = async ({ chainId, signerAddress, // not used - addressOrIndex = 0 + addressOrIndex = 0, }) => { - if (!get('provider')) { + if (!get("provider")) { //console.log('lost connection') - init() - emit() - return + init(); + emit(); + return; } if (!chainId) { - chainId = (await get('provider').getNetwork()).chainId + chainId = (await get("provider").getNetwork()).chainId; } // BrowserProvider doesn't take account argument - const signer = await (get('provider') instanceof ethers.BrowserProvider ? get('provider').getSigner() : get('provider').getSigner(addressOrIndex)) + const signer = await (get("provider") instanceof ethers.BrowserProvider + ? get("provider").getSigner() + : get("provider").getSigner(addressOrIndex)); try { - signerAddress = await signer.getAddress() + signerAddress = await signer.getAddress(); } catch (e) { - console.warn('[ethers-svelte] '+e) + console.warn("[ethers-svelte] " + e); } assign({ connected: true, chainId, signer, - signerAddress - }) - emit() - } + signerAddress, + }); + emit(); + }; - const accountsChangedHandler = accounts => + const accountsChangedHandler = (accounts) => switch1193Provider({ addressOrIndex: - Array.isArray(accounts) && accounts.length ? accounts[0] : 0 - }) - const chainChangedHandler = (eipProvider, addressOrIndex) => chainId => set1193Provider(eipProvider, addressOrIndex, BigInt(chainId)) + Array.isArray(accounts) && accounts.length ? accounts[0] : 0, + }); + const chainChangedHandler = (eipProvider, addressOrIndex) => (chainId) => + set1193Provider(eipProvider, addressOrIndex, BigInt(chainId)); // TODO better error support ? - const disconnectHandler = error => switch1193Provider({ error }) + const disconnectHandler = (error) => switch1193Provider({ error }); const init = () => { - if (get('eipProvider') && get('eipProvider').removeListener) { - get('eipProvider').removeListener( - 'accountsChanged', + if (get("eipProvider") && get("eipProvider").removeListener) { + get("eipProvider").removeListener( + "accountsChanged", accountsChangedHandler - ) - get('eipProvider').removeListener('chainChanged', chainChangedHandler) - get('eipProvider').removeListener('disconnect', disconnectHandler) + ); + get("eipProvider").removeListener("chainChanged", chainChangedHandler); + get("eipProvider").removeListener("disconnect", disconnectHandler); } - deleteAll() + deleteAll(); assign({ connected: false, - evmProviderType: '' - }) - } + evmProviderType: "", + }); + }; const set1193Provider = async (eipProvider, addressOrIndex, chainId) => { - init() - let accounts + init(); + let accounts; try { - accounts = await eipProvider.request({ method: 'eth_requestAccounts' }) + accounts = await eipProvider.request({ method: "eth_requestAccounts" }); } catch (e) { - console.warn('[ethers-svelte] non compliant 1193 provider') + console.warn("[ethers-svelte] non compliant 1193 provider"); // some provider may store accounts directly like walletconnect - accounts = eipProvider.accounts + accounts = eipProvider.accounts; } if (addressOrIndex == null && Array.isArray(accounts) && accounts.length) { - addressOrIndex = accounts[0] + addressOrIndex = accounts[0]; } - const provider = new ethers.BrowserProvider(eipProvider) + const provider = new ethers.BrowserProvider(eipProvider); assign({ provider, eipProvider, - evmProviderType: 'EIP1193' - }) + evmProviderType: "EIP1193", + }); if (eipProvider.on) { // TODO handle disconnect/connect events - eipProvider.on('accountsChanged', accountsChangedHandler) - eipProvider.on('chainChanged', chainChangedHandler(eipProvider, addressOrIndex)) - eipProvider.on('disconnect', disconnectHandler) + eipProvider.on("accountsChanged", accountsChangedHandler); + eipProvider.on( + "chainChanged", + chainChangedHandler(eipProvider, addressOrIndex) + ); + eipProvider.on("disconnect", disconnectHandler); } - return switch1193Provider({ addressOrIndex, chainId }) - } + return switch1193Provider({ addressOrIndex, chainId }); + }; const setProvider = async (provider, addressOrIndex = 0) => { if (!provider) { if (!getWindowEthereum()) throw new Error( - '[ethers-svelte] Please authorize browser extension (Metamask or similar)' - ) - getWindowEthereum().autoRefreshOnNetworkChange = false - return set1193Provider(getWindowEthereum()) + "[ethers-svelte] Please authorize browser extension (Metamask or similar)" + ); + getWindowEthereum().autoRefreshOnNetworkChange = false; + return set1193Provider(getWindowEthereum()); } - if (typeof provider === 'object' && provider.request) - return set1193Provider(provider, addressOrIndex) - init() + if (typeof provider === "object" && provider.request) + return set1193Provider(provider, addressOrIndex); + init(); if ( - typeof provider !== 'object' || - (!( - Object.getPrototypeOf(provider) instanceof ethers.AbstractProvider - ) && - !( - Object.getPrototypeOf(provider) instanceof - ethers.UrlJsonRpcProvider - )) + typeof provider !== "object" || + (!(Object.getPrototypeOf(provider) instanceof ethers.AbstractProvider) && + !(Object.getPrototypeOf(provider) instanceof ethers.UrlJsonRpcProvider)) ) { - provider = new ethers.JsonRpcProvider(provider) + provider = new ethers.JsonRpcProvider(provider); } - const { chainId } = await provider.getNetwork() - let signer, signerAddress + const { chainId } = await provider.getNetwork(); + let signer, signerAddress; if (addressOrIndex !== null) { try { - signer = await provider.getSigner(addressOrIndex) + signer = await provider.getSigner(addressOrIndex); // XXX some providers do not support getSigner // if (typeof provider.listAccounts === 'function') { // signer = await provider.getSigner(addressOrIndex) // } else { // signer = await provider.getSigner() // } - signerAddress = await signer.getAddress() + signerAddress = await signer.getAddress(); } catch (e) { - console.warn('[ethers-svelte] '+e) + console.warn("[ethers-svelte] " + e); } } assign({ @@ -163,155 +163,158 @@ export const createStore = () => { provider, connected: true, chainId: BigInt(chainId), - evmProviderType: provider.constructor.name - }) - emit() - } + evmProviderType: provider.constructor.name, + }); + emit(); + }; const disconnect = async () => { - init() - emit() - } + init(); + emit(); + }; return { setProvider, disconnect, subscribe, - get - } -} + get, + }; +}; export const createContractStore = () => { - const { emit, get, subscribe, assign, deleteAll } = proxied() + const { emit, get, subscribe, assign, deleteAll } = proxied(); const attachContract = async (name, address, abi, fromSigner = true) => { assign({ - [name]: [address, abi, fromSigner] - }) - emit() - } + [name]: [address, abi, fromSigner], + }); + emit(); + }; return { attachContract, subscribe, - get - } -} + get, + }; +}; -const allStores = {} +const allStores = {}; -const noData = { rpc: [], explorers: [{}], faucets: [], nativeCurrency: {} } +const noData = { rpc: [], explorers: [{}], faucets: [], nativeCurrency: {} }; -const getData = id => { +const getData = (id) => { for (const data of chains) { - if (BigInt(data.chainId) === id) return data + if (BigInt(data.chainId) === id) return data; } - return noData -} + return noData; +}; const subStoreNames = [ - 'connected', - 'provider', - 'chainId', - 'chainData', - 'signer', - 'signerAddress', - 'evmProviderType', - 'contracts' -] - -export const makeEvmStores = name => { - const evmStore = (allStores[name] = createStore()) - const registry = createContractStore() - const target = {} + "connected", + "provider", + "chainId", + "chainData", + "signer", + "signerAddress", + "evmProviderType", + "contracts", +]; + +export const makeEvmStores = (name) => { + const evmStore = (allStores[name] = createStore()); + const registry = createContractStore(); + const target = {}; allStores[name].connected = derived( evmStore, - $evmStore => $evmStore.connected - ) + ($evmStore) => $evmStore.connected + ); - allStores[name].provider = derived(evmStore, $evmStore => $evmStore.provider) - allStores[name].chainId = derived(evmStore, $evmStore => $evmStore.chainId) - allStores[name].chainData = derived(evmStore, $evmStore => + allStores[name].provider = derived( + evmStore, + ($evmStore) => $evmStore.provider + ); + allStores[name].chainId = derived(evmStore, ($evmStore) => $evmStore.chainId); + allStores[name].chainData = derived(evmStore, ($evmStore) => $evmStore.chainId ? getData(BigInt($evmStore.chainId)) : {} - ) + ); - allStores[name].signer = derived(evmStore, $evmStore => $evmStore.signer) + allStores[name].signer = derived(evmStore, ($evmStore) => $evmStore.signer); allStores[name].signerAddress = derived( evmStore, - $evmStore => $evmStore.signerAddress - ) + ($evmStore) => $evmStore.signerAddress + ); allStores[name].evmProviderType = derived( evmStore, - $evmStore => $evmStore.evmProviderType - ) + ($evmStore) => $evmStore.evmProviderType + ); allStores[name].contracts = derived( - [ evmStore, registry ], - ([ $evmStore, $registry ]) => { - if (!$evmStore.connected) return target + [evmStore, registry], + ([$evmStore, $registry]) => { + if (!$evmStore.connected) return target; for (let key of Object.keys($registry)) { target[key] = new ethers.Contract( $registry[key][0], $registry[key][1], - !$registry[key][2] || !$evmStore.signer ? $evmStore.provider : $evmStore.signer - ) + !$registry[key][2] || !$evmStore.signer + ? $evmStore.provider + : $evmStore.signer + ); } - return target + return target; } - ) + ); // force one subscribtion on $contracts so it's defined via proxy - allStores[name].contracts.subscribe(()=>{}) + allStores[name].contracts.subscribe(() => {}); return new Proxy(allStores[name], { get: function (internal, property) { - if (property === '$contracts') return target + if (property === "$contracts") return target; if (/^\$/.test(property)) { // TODO forbid deconstruction ! - property = property.slice(1) + property = property.slice(1); if (subStoreNames.includes(property)) - return allStores[name].get(property) - throw new Error(`[ethers-svelte] no store named ${property}`) + return allStores[name].get(property); + throw new Error(`[ethers-svelte] no store named ${property}`); } - if (property === 'attachContract') return registry.attachContract + if (property === "attachContract") return registry.attachContract; if ( [ - 'setBrowserProvider', - 'setProvider', - 'disconnect', - ...subStoreNames + "setBrowserProvider", + "setProvider", + "disconnect", + ...subStoreNames, ].includes(property) ) - return Reflect.get(internal, property) - throw new Error(`[ethers-svelte] no store named ${property}`) - } - }) -} - + return Reflect.get(internal, property); + throw new Error(`[ethers-svelte] no store named ${property}`); + }, + }); +}; -export const getChainStore = name => { +export const getChainStore = (name) => { if (!allStores[name]) - throw new Error(`[ethers-svelte] chain store ${name} does not exist`) - return allStores[name] -} - -export { chains as allChainsData } - -export const getChainDataByChainId = id => (chains.filter(o => o.chainId === id) || [{}])[0] + throw new Error(`[ethers-svelte] chain store ${name} does not exist`); + return allStores[name]; +}; -export const defaultEvmStores = makeEvmStores('default') +export { chains as allChainsData }; -export const connected = allStores.default.connected -export const chainId = allStores.default.chainId -export const chainData = allStores.default.chainData +export const getChainDataByChainId = (id) => + (chains.filter((o) => o.chainId === id) || [{}])[0]; -export const provider = allStores.default.provider -export const signer = allStores.default.signer -export const signerAddress = allStores.default.signerAddress +export const defaultEvmStores = makeEvmStores("default"); -export const evmProviderType = allStores.default.evmProviderType -export const contracts = allStores.default.contracts +export const connected = allStores.default.connected; +export const chainId = allStores.default.chainId; +export const chainData = allStores.default.chainData; +export const provider = allStores.default.provider; +export const signer = allStores.default.signer; +export const signerAddress = allStores.default.signerAddress; +export const evmProviderType = allStores.default.evmProviderType; +export const contracts = allStores.default.contracts;