Code Monkey home page Code Monkey logo

usedapp's Introduction

useDapp

CI Npm package version Discord

Ethereum ๐Ÿค React

Framework for rapid Dapp development.
Simple. Robust. Extendable. Testable.

About

Introduces great features:

  • ๐Ÿ—๏ธ React hooks - Uses React hooks as your primary building ingredient
  • ๐Ÿš… Auto refresh - Refreshes on a new block, wallet change or network change
  • ๐Ÿ›’ Multicall - Combines multiple blockchain calls into a single multicall

Combines the best practices:

  • ๐Ÿ”ง Modern stack - Employs ethers.js, multicall & waffle
  • ๐Ÿ“š Extendability - Extends easily with custom hooks
  • ๐Ÿ’ก Testability - Simple integration tests for UI and blockchain

Example

import { Mainnet } from '@usedapp/core/modal/chain/ethereum'
import { useEthers, useEtherBalance } from '@usedapp/core'

const config = {
  readOnlyChainId: Mainnet.chainId,
  readOnlyUrls: {
    [Mainnet.chainId]: 'https://mainnet.infura.io/v3/62687d1a985d4508b2b7a24827551934',
  },
}

ReactDOM.render(
  <DAppProvider config={config}>
    <App />
  </DAppProvider>,
  document.getElementById('root')
)

const STAKING_CONTRACT = '0x00000000219ab540356cBB839Cbe05303d7705Fa'

export function App() {
  const { activateBrowserWallet, deactivate, account } = useEthers()
  const userBalance = useEtherBalance(account)
  const stakingBalance = useEtherBalance(STAKING_CONTRACT)

  return (
    <div>
      {!account && <button onClick={activateBrowserWallet}> Connect </button>}
      {account && <button onClick={deactivate}> Disconnect </button>}
      {stakingBalance && <p>ETH2 staking balance: {formatEther(stakingBalance)} ETH </p>}
      {account && <p>Account: {account}</p>}
      {userBalance && <p>Ether balance: {formatEther(userBalance)} ETH </p>}
    </div>
  )
}

See application here.

Documentation

For detailed feature walkthrough checkout documentation.

Contributing

Contributions are always welcome, no matter how large or small. Before contributing, please read the code of conduct and contribution policy.

Before you issue pull request:

  • Make sure all tests pass.
  • Make sure linter passes.
  • Make sure you have test coverage for any new features.

To install dependencies type:

npm i -g pnpm
pnpm install

To build project:

yarn build

To run tests type:

yarn test

To run linter type:

yarn lint

License

useDapp is released under the MIT License.

usedapp's People

Contributors

afa7789 avatar clumsyvlad avatar devanoneth avatar dimlbc avatar dmaretskyi avatar doubleotheven avatar gasolin avatar github-actions[bot] avatar hubertnastaly avatar itsshadowl avatar jakvbs avatar justynabroniszewska avatar kacperkurek avatar marcin-trust avatar marekkirejczyk avatar miziet avatar mj426382 avatar nezouse avatar pawelpolak2 avatar quagliero avatar rzadp avatar sz-piotr avatar szymx95 avatar truefi-bot avatar truefibot avatar tt-marek avatar tylerether avatar vanruch avatar wlmmfx avatar yivlad avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

usedapp's Issues

Custom connectors

Amazing tools you guys are creating here!

Obviously, using InjectedProvider is not always the unique option dApps need. Supporting other connectors such as WalletConnect is a must-have.

Could be great to add support of common connectors available on web3-react with easy setup from the useDApp configuration object, like in web3modal.

Error after update to 0.3.0

After update app throws this error
Module not found: Can't resolve '@ethersproject/abi' in '/Users/kacper/Projects/earnbase/node_modules/@usedapp/core/dist/src/constants/abi'

Start/Build Webpack Errors on Ubuntu

@usedapp/example $ tsc --noEmit && rimraf build && webpack --mode production --progress | fatal: Needed a single revision | [webpack-cli] Failed to load '/home/user/Downloads/useDApp-master/packages/example/webpack.config.js' config | [webpack-cli] { Error: Command failed: git rev-parse --short HEAD | fatal: Needed a single revision
^ I am getting this error trying to use yarn &&, then yarn build on the main directory
[webpack-cli] Failed to load '/home/user/Downloads/useDApp-master/packages/example/webpack.config.js' config [webpack-cli] { Error: Command failed: git rev-parse --short HEAD fatal: Needed a single revision
^ this error cding into the example project and doing npm start

I am using ubuntu 20.04 , npm version 6.14.4

I spent a few hours troubleshooting NPM versions and webpack versions for a while

๐Ÿ† 0xHack Bounty: ๐Ÿ’ฒ1000 DAI - Best tutorial or guide for useDApp

0xHack Bounty: ๐Ÿ’ฒ1000 DAI - Best tutorial or guide

Prize Bounty

Amount: 1000 DAI

Challenge Description

Ethworks is sponsoring 0xHack with 2nd best tutorial or guide prize of $2000 DAI!
Writing tutorial or guide for useDApp is easy and should pose no difficulty to anyone ready to accept the challenge!

A guide is a should be added to useDApp documentation.
A tutorial should be added to tutorial section of Ethereum website.

Submission Requirements

The main requirement is that the tutorial or guide teaches has to work with useDApp.

Judging Criteria

  • Easy to understand
  • Nicely structured (i.e. clear steps)
  • Good visual appearance (i.e. readable code snippets, optionally screenshots)

Submission Deadline:

May 31st, 11:00 CEST

Winner Announcement Date

Judging will be at the end of the Hackathon on June 2nd.

Better Error Handling for Calls to Contracts with invalid ABI Interface

Some of the useDapp hooks tend to abruptly fail if everything isn't perfect. As it stands I may have to pass in a contract address that may or may not have the correct ABI.

For example when using useContractCall hook the application often throws "Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement." if the contract doesn't have the correct ABI.

Imagine a dynamic list of contract addresses where some may be ERC20s and others not. Those list of addresses are passed into a component that attempts to call symbol and if the contract doesn't have a symbol function then the errors start.

Example: const [symbolCall] = useERC20ContractCall(address, "symbol");

image

Which seems to be coming from node_modules/@usedapp/core/dist/src/providers/chainState/provider.js (34:40)

image

What's the best way to prevent errors like this when using useDapp hooks?

Wallet connectors

Is it possible to extend useEthers to use other wallet connectors such as walletconnect?

Address helpers

Address helpers: addressEqual, addressCompare, shortenAddress, shortenIfAddress

Implementation draft:

function addressEqual(address0: string, address1: string) {
  return utils.getAddress(address0) === utils.getAddress(address1)
}
function addressCompare(address0: string, address1: string) {
  const isGreater = BigNumber.from(address0).gte(BigNumber.from(address1))
  return isGreater ? 1 : -1
}
export function shortenAddress(address: string) {
  return address.substring(0, 6) + '...' + address.substring(address.length - 4)
}

Error after upgrading to 0.3 with nextjs

My app is server side rendered and was functioning perfect on 0.2, but since 0.3 I receive this error on startup:

Server Error
ReferenceError: window is not defined
This error happened while generating the page. Any console logs will be displayed in the terminal window.

Call Stack
getItem
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/@usedapp/core/dist/src/hooks/useLocalStorage.js (6:16)
<unknown>
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/@usedapp/core/dist/src/hooks/useLocalStorage.js (29:52)
useReducer
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/react-dom/cjs/react-dom-server.node.development.js (1537:57)
Object.useState
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/react-dom/cjs/react-dom-server.node.development.js (1475:10)
Object.useState
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/react/cjs/react.development.js (1508:21)
Object.useLocalStorage
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/@usedapp/core/dist/src/hooks/useLocalStorage.js (29:22)
NotificationsProvider
file:/Users/dylankilkenny/dev/github/pxlgen/node_modules/@usedapp/core/dist/src/providers/notifications/provider.js (12:22)

Advanced config: Multichain support, useContracts and hooks overrides

Summary

This is proposal for introducing multichain support in useDApp. This will allow to connect to several chains in read-only mode and one in write mode.

In the future multiple write chains might be available, without backward compatibility breaking.

TODO

  • Define precise configuration types (Config, what about non-network items)
  • Figure out ChainId
  • Update hooks (useChainCall, useContractFunction, defaults, etc)

Configuration

Example configuration with mulitchain:

const config = {
  networks: {
    [chainId: ChainId.Mainnet]: {
      url: 'https://mainnet.infura.io/v3/93626a985d4508b2b7a24827551487d1'
     },
    [chainId: ChainId.Kovan]: {
      url: 'https://kovan.infura.io/v3/93626a985d4508b2b7a24827551487d1'
    },
    [chainId: 777]: {
      url: 'magicChain.url.com',
      contracts: {
        multicall: '0x123...456'
     }
  },
  defaultNetwork: ChainId.Mainnet,
  notifications: {
    checkInterval: 15000,
    expirationPeriod: 5000
  }
}

New types:

export type Config {
   networks: {
      [chainId: number]: {
           name:string,
           url:string,
           pollingInterval?: number,
           contracts: {
                multicall: string,
                uniswapFactory: string
           }
      }
   },
   notifications: {
       checkInterval: number
       expirationPeriod: number
   },
   defaultNetwork: number
}

Flexible chainId

export enum KnownChainId {  
  Mainnet = 1,  
  Ropsten = 3,  
  ...
}
type ChainId = KnownChainId | number

It's also possible to leave chainId as is and in config accept number. Enum type can be supplied to number parameter.

Legacy config support

To introduce backward compatibility we can rename current config type to LegacyConfig and introduce new type Config.

type BackwardsCompatibleConfig = LegacyConfig | Config

New hooks

NetworkConnectorProvider

Provider that will hold a NetworkConnector object.
Object will be updated when networks in config change.

Draft:

export const NetworkConnectorContext = createContext<NetworkConnector>(new NetworkConnector({urls:[]}))

export function useNetworkConnector() {
  return useContext(NetworkConnectorContext)
}

export function NetworkConnectorProvider({ children }: {children:ReactNode}) {
  const {networks} = useConfig()
  const networkConnector = useMemo(() => (
    new NetworkConnector({urls: networks})
  ),[networks])

  return <NetworkConnectorContext.Provider value={{ networkConnector }} children={children} />
}

LibrariesProvider

Web3ReactProvider for read only libraries.
See docs

Draft:

export function LibrariesProvider({ children, pollingInterval }: EthersProviderProps) {
  function getLibrary(provider: any): Web3Provider {
    const library = new Web3Provider(provider, 'any')
    library.pollingInterval = pollingInterval || DEFAULT_POLLING_INTERVAL
    return library
  }
  const Provider = createWeb3ReactRoot('libraries')
  return <Provider getLibrary={getLibrary}>{children}</Provider>
}

useLibraries

Similar to use ethers will use NetworkConnector to change connected ID. Also will contain a hasBlockChanged to tell whether the block changed on currently connected network from the last time this function was called.

const {provider, changeNetworkID, hasBlockChanged } = useLibraries()

Draft:

export function useEthers() {
  const result = useWeb3React<Web3Provider>('libraries')
  const networkConnector = useNetworkConnector()
  const [blockNumbers, setBlockNumbers] = useState<{[chainId: number]:number }>({})

  const changeNetworkID = useCallback(
    async (chainId: ChainId | number) => {
      networkConnector.changeChainId(chainId)
      await result.activate(networkConnector)
    },
    []
  )
  const hasBlockChanged = useCallback(async () => {
    const chainId = result.chainId
    const blockNumber = blockNumbers[chainId]
    const newBlockNumber = await result.library?.getBlockNumber()
    if(blockNumber != newBlockNumber){
      setBlockNumbers(...blockNumbers, [chainId]:newBlockNumber)
      return true
    }
    return false
  },[])
  return { ...result, changeNetworkID, hasBlockChanged }
}

useContracts

const {uniswapFactory} = useContracts(chainId?)

New models

CallOptions

export type CallOptions = {  
  chainId?: ChainId
}

Changes to old models

TransactionOptions

export type TransactionOptions {
  signer?: Signer
  transactionName?: string
  chainId?: ChainId | number
}

Changes to old hooks

useConfig

Add support for both legacy and new config

Extract multicallAdressess and supportedChains from new Config

   const {config, multicallAdressess, supportedChains} = useConfig()

useUpdateConfig

    const {updateConfig, addNetwork, removeNetwork, updateNetwork } = useUpdateConfig()

ChainStateProvider

ChainStateProvider will have to hold calls and results that are separated between each chainId, also will hold special list of calls that follows the chainId of connected wallet.

Draft:

export type Calls = {
    walletCalls: ChainCall[]
    [chainId: ChainId | number]: ChainCall[]
}

export interface State {
  walletState: 
    | {
      blockNumber: number
      state?: ChainState
      error?: unknown
    }
  | undefined
  ,
  [chainId: number]:
    | {
        blockNumber: number
        state?: ChainState
        error?: unknown
      }
    | undefined
}

export function ChainStateProvider({ children, multicallAddresses }: Props) {
    ...
    const {library,changeNetworkID, hasBlockChanged} = useLibraries
    cost {networks} = useConfig
    useEffect(() => {
        const update = setInterval(async () => {
                 networks.forEach((network) => {
                        await changeNetworkID(network.chainId)
                        
                        if (await hasBlockChanged()){
                               const blockNumber = await library.blockNumber
                               multicall(library, network.contract.multicall,blockNumber, calls[network.chainId])
                               .then((state) => dispatchState({ type: 'FETCH_SUCCESS', blockNumber, network.chainId, state }))
                               .catch((error) => {
                                                   console.error(error)
                                                   dispatchState({ type: 'FETCH_ERROR', blockNumber, network.chainId, error })
                                }
                ) }
        }  ,libraryPollingInterval)

        return () => update.cancel()
    }
   ,[networks])

  const provided = { value:state, multicallAddress, addCalls, removeCalls }

  return <ChainStateContext.Provider value={provided} children={children} />
}

useChainCalls

export function useChainCalls(calls: (ChainCall | Falsy)[], chainId?: ChainId | number) 

read only hooks

Add CallOptions parameters to functions that read state from blockchain

export function useContractCalls(calls: (ContractCall | Falsy)[], options?:CallOptions): (any[] | undefined)[] 

export function useContractCall(call: ContractCall | Falsy,  options?:CallOptions): any[] | undefined

export function useEtherBalance(address: string | Falsy, options?:CallOptions): BigNumber | undefined

export function useTokenAllowance(
  tokenAddress: string | Falsy,
  ownerAddress: string | Falsy,
  spenderAddress: string | Falsy,
  options?: CallOptions
): BigNumber | undefined

export function useTokenBalance(
   tokenAddress: string | Falsy, 
   address: string | Falsy, 
   options?: CallOptions
): BigNumber | undefined

usePromiseTransaction

usePromiseTransaction will set exception state when options.chainId doesn't match wallet chainId

Proposed task list

  • Add new backwards compatible config
  • Add necessary providers (new providers shouldn't change DApp behaviour)
  • Refactor ChainCallProvider calls state and return state (make calls array differentiate between calls to different chainIDs)
  • Make ChainCallProvider call other chains
  • Refactor useContractCall to be able to set chainID
  • Refactor transaction sending sets exception state
  • General refactor

๐Ÿ† 0xHack Bounty: ๐Ÿ’ฒ1000 DAI - 2nd BEST DApp using useDApp

0xHack Bounty: 1000 DAI 2nd BEST DApp using useDApp

Prize Bounty

Amount: 1000 DAI

Challenge Description

Ethworks is sponsoring 0xHack with 2nd best DApp using useDApp prize of $2000 DAI!
Building an application using useDApp is very easy and should pose no difficulty to anyone ready to accept the challenge!

Submission Requirements

The main requirement is that the application has useDApp in dependencies and is using it's features.

Judging Criteria

  • Originality
  • Use of useDApp features
  • Great usability
  • Fun factor

Submission Deadline:

May 31st, 11:00 CEST

Winner Announcement Date

Judging will be at the end of the Hackathon on June 2nd.

Roadmap

Roadmap is a subject to change.

0.2 Connecting and reading from blockchain (published)

0.3 Sending transaction and notifications (published)

  • Address helpers #72
  • Sending transactions #68
  • Pending transactions useTransactions, example here
  • Notification useNotifications
  • shortenTransactionHash & shortenIfTransactionHash

0.4 Misc

  • Persisting the connection #145
  • Connecting to Ganche/HardHat (automatically deploy multicall?)
  • useSendTransaction()
  • useEtherPrice()

0.5 Various

  • Flexible chainId as part of config #47
  • decodeTransaction() 151
  • useEvents (research)
  • Introduce @usedapp/tokens
  • useToken() - returns Currency (immutable)
  • useTokenList()
  • useTokenPrice() (returns currency/price)

0.6 Configuration

  • setConfig(key, value)

0.10 (compatibility breaking changes)

Introduce useCalls

  • Return error on invalid calls (with Multicall V2 )
  • Typechain integration (experimental)

0.11

  • Multichain support
  • Hook overrides
  • useContracts()

Future

  • Extension - multichain and error handling support
  • Override refresh frequency
  • Augment token lists for testnets with various tokens

@usedapp/components

A simple library of components build on top of @usedapp/core, example components:

  • Account bar (connection status, address, count of pending transactions)
  • Network (show current network - Mainnet, Kovan, Goerli, Unsupported network)
  • Transactions (list of transactions made by user)
  • Notifications (bubble with notifications: wallet connected, transaction started, failed)
  • In progress dialog (generic in progress dialog for transaction with custom icon)
  • Send button (states: none, mining, success, error, validation? )
  • Connect Wallet Dialog (aka Web3Modal)

decodeTransactionType

A function that allows to decode semantic transaction type from Transaction type.

function decodeTransactionType(transaction: Transaction) : TransactionType {
  ...
}

type TransactionType = {
  type: 'TokenApprove', 
  token: address
  from: address 
  to: address
  amount: BigNumber
} | {
  type: 'TokenTransfer',
  token: address
  from: address 
  to: address
  amount: BigNumber
} | ...

Considerations:

  • how do we make it extendable ('StakeETH', 'Created MakerDAO CDP`)
  • how do we extend it with token names (e.g. (TokenTransfer, DAI))
  • how do we combine it into readable messages (e.g. '10 DAI transfered from 0x1234...2345 to CDP contract'

getting an error with useEthers

I tried to include useDapp in a basic next app and when I click on the activateBrowserWallet button, I get the following error: Error: Invariant failed: No <Web3ReactProvider ... /> found.. I did include my infuria in env. Any idea what I'm doing wrong?

import Link from "next/link";
import Layout from "../components/Layout";
import {
  ChainId,
  DAppProvider,
  useEthers,
} from "@usedapp/core";
const config = {
  readOnlyChainId: ChainId.Mainnet,
  readOnlyUrls: {
    [ChainId.Mainnet]: process.env.NEXT_PUBLIC_PROVIDER_KEY,
  },
};

const IndexPage = () => {
  const { activateBrowserWallet, account } = useEthers();

  return (
    <Layout title="Home | Next.js + TypeScript Example">
      <h1>Hello Next.js ๐Ÿ‘‹</h1>
      <p>
        <Link href="/about">
          <a>About</a>
        </Link>
      </p>
      <DAppProvider config={config}>
        <p>{JSON.stringify(config)}</p>
        <button onClick={() => activateBrowserWallet()}>Connect</button>
        {account && <p>Account: {account}</p>}
      </DAppProvider>
    </Layout>
  );
};

export default IndexPage;

๐Ÿ† 0xHack Bounty: 5x ๐Ÿ’ฒ200 DAI - Application using useDApp

0xHack Bounty: 200 USDC DApp using useDApp

Prize Bounty

Amount: 200 DAI

Challenge Description

Ethworks is sponsoring 0xHack with five equal bounty prizes of $200 Dai each!
Building an application using useDApp is very easy and should pose no difficulty to anyone ready to accept the challenge!

Submission Requirements

The main requirement is that the application has useDApp in dependencies and is using it's features.

Judging Criteria

  • Good use of technology
  • Good usability
  • Additional criteria: Originality of concept and fun factor

Submission Deadline:

May 31st, 11:00 CEST

Winner Announcement Date

Judging will be at the end of the Hackathon on June 2nd.

NotificationsProvider not exported.

'NotificationsProvider' is not exported from '@usedapp/core'
Im using my own Web3ReactProvider and I want to reuse the DAppProviderWithConfig but with my own implementation of the web3provider, changing EthersProvider for my own provider. Its there a way to do that?

๐Ÿ† 0xHack Bounty: ๐Ÿ’ฒ2000 DAI - 2nd BEST NEW useDApp feature

0xHack Bounty: 2000 DAI - BEST New useDApp feature

Prize Bounty

Amount: ๐Ÿ’ฒ2000 DAI

Challenge Description

Ethworks is sponsoring 0xHack with 2nd best new useDApp feature prize of $2000 DAI!
Building an new feature using useDApp is quite easy and should pose no difficulty to anyone ready to accept the challenge!

For inspiration check out useDApp roadmap and issues.

Submission Requirements

The main requirement is to build a new feature for useDApp as a pull request to main repository along with example use.

Judging Criteria

  • Usefulness of the feature for Ethereum developer community
  • Technical corectness
  • Idiomatic approach

Submission Deadline:

May 31st, 11:00 CEST

Winner Announcement Date

Judging will be at the end of the Hackathon on June 2nd.

๐Ÿ† 0xHack Bounty: ๐Ÿ’ฒ2000 DAI - BEST DApp using useDApp

0xHack Bounty: 2000 DAI BEST DApp using useDApp

Prize Bounty

Amount: 2000 DAI

Challenge Description

Ethworks is sponsoring 0xHack with best DApp using useDApp prize of $2000 DAI!
Building an application using useDApp is very easy and should pose no difficulty to anyone ready to accept the challenge!

Submission Requirements

The main requirement is that the application has useDApp in dependencies and is using it's features.

Judging Criteria

  • Originality
  • Use of useDApp features
  • Great usability
  • Fun factor

Submission Deadline:

May 31st, 11:00 CEST

Winner Announcement Date

Judging will be at the end of the Hackathon on June 2nd.

Notification Types suggestion

Currently all notification types are exported in a union type Notification. Since the properties in each type are not consistent the following code is needed to access non universal properties:

const notif = notifications[0];
if ("submittedAt" in notif && "transaction" in notif && "receipt" in notif) {
  // access all properties
}

Currently only type is accessible without a type guard. Without such type gaurd we get Property 'submittedAt' does not exist on type 'WalletConnected'

I think it would be cleaner to have type removed from the notification types and instead export the interfaces for each notification. Then we can use:

if(notif instanceof TransactionFailed){
    // access all properties
}

I can possibly create a PR, let me know your thoughts

Project logo

Is there a project logo? If there is no project logo, would you accept one created by contributors?

Troubles connecting to local Ganache

Hello,

first of all, thank you for the amazing work!

I am using a truffle Project to deploy my migrations to a local ganache instance.
Isn't it possible to connect useDapp to my ganache local network running on localhost:7545?

Regards

probably missing @ethersproject/contracts export

Hi, I'm not sure, but, in order to use useContractFunction hook we need to create Contract instance. That type comes from @ethersproject/contracts package.

The question is, if it should be (re)exported from useContractFunction.ts, or maybe I'm misiing something?

Add Caching/Limits to Contract Calls

With useContractCall and useContractCalls it's mentioned the data is updated every block.

This approach can get expensive when paying for a node service (Infura/Alchemy)

Any discussions for adding caching or limiting fetches?

Personally, I've been using react-query to wrap my contract call and had a lot of success.

Would be great to add at a minimum the ability to limit refetches and/or also the ability cache requests and only refetch if certain parameters in the state have been met i.e. a certain function has been called/broadcast/confirmed or another state change in the application.

useMulticall interface proposal

declare function  useContractData (): {
    results: Record<string, string|undefined>,
    addRequests: (req: string[]) => void,
    removeRequests: (req: string[]) => void
}


function useMulticall (data: [string /* address */, string /* data */][]) {
    const contractData = useContractData()

    const requests = data.map(([address, data]) => address.concat(data))
    const results = requests.map(key => contractData.results[key])

    useEffect(() => {
        contractData.addRequests(requests)
        return () => contractData.removeRequests(requests)
    }, [requests])

    return results
}

function useMulticallWithDecode (data: [string, string, (data: string) => any][]) {
    /* ... */
}

function useBalance (contract: string, owner: string){
    useMulticallWithDecode([[contract, encode('balanceOf', owner), BigNumer.from]])
}

Transaction speed-up and cancel

Synopsis

It is a common pattern to allow user to speed-up or cancel transactions.
To do so we can introduce new features in useContractFunction:

  • slow - boolean that indicates if transaction is slow. Timeout configurable in framework config.
  • speedUp - will create identical transaction as current with higher gas. Gas bump configurable in config and options.
  • cancel - creates transaction with 0 value with the same nonce.

Todo

When No-op and when throws? No-op if called in state Success or Pending. Throws in state New. Others?

Example usage

const { state, send, slow, speedUp, cancel } = useContractFunction(contract, 'deposit')

slow && <button onClick={speedUp}>Speed up</button>
button onClick={cancel}>Cancel</button>

Extended transaction state

export type TransactionStatus =
... |
{
  status: 'Canceling'
  originalTransaction: TransactionResponse
  cancelTransaction: TransactionResponse
},
{
  status: 'Canceled'
  originalTransaction: TransactionResponse
  cancelTransaction: TransactionResponse
  receipt: TransactionReceipt
},
{
  status: 'SpeedingUp'
  originalTransaction: TransactionResponse
  speedUpTransaction: TransactionResponse
}

Transaction that was unsuccessful in canceling should be in Success or Failed state.
Transaction that was successful in canceling should be in Canceled state.

Pending, Success and Failed states should include additional field otherTransactions, that was created with the same nonce. In case of Pending transaction field should include the latest transaction created by user. In case of Success and Failed it should include mined transaction (which not always will be latest), corresponding to receipt.

Transactions and notifications

Transactions should include information if transaction is slow, as well as ability to cancel/speed-up transaction.
Notifications should notify on cancel or speed-up attempt as well as of successful cancelation.

Implementation idea

[TODO]
Introduce transaction override:

const { state, send, override } = useContractFunction(contract, 'deposit')

Handle block number - ethers block number vs fetched block number

There is a difference between a block number coming from useBlockNumber, and the block number of last successfully fetched number.

The difference surfaces here:

https://github.com/EthWorks/dapp-boilerplate/blob/8cccf7082d98c5d314d6d4ff050faa0ce759fb43/packages/ui/src/components/page/BlockNumber.tsx#L11-L12

It should be clear what a block number means and which one to use.
Perhaps only the fetched block number should be exposed to the end user of useDapp? i.e. add block number to useBlockMeta and do not expose useBlockNumber?

Hook for sending transaction useContractFunction

Starting discussion on hook for sending transaction to blockchain

function someComponent() {
  const [status, send, speedup] = useContractFunction(abi, functionName)
  ...
  send(arg1, arg2, ... , argn, transactionOptions)
  if (status.state == 'Mining') {
    return (<p> Mining: <a href="{getExplorerTransactionLink(status.transaction.hash)}"> shortenTransactionHash(status.transaction.hash)</a>)
    if (status.slow) {
       //Speed-up button
    }
  } else {
     // do sth with status.receipt
  }
  
type TransactionStatus = {
  state: 'None' 
} | {
 state: 'Mining',
 chainId: ChainID,
 transaction: Transaction
 slow: boolean 
} | {
 state: 'Success' 
 chainId: ChainID,
 transaction: Transaction
 receipt: TransactionReceipt
} | {
 state: 'Fail' 
 chainId: ChainID,
 transaction: Transaction
 receipt?: TransactionReceipt
 errorMessage: string
} | {
 state: 'Exception' 
 chainId: ChainID,
 transaction: Transaction
 errorMessage: string
}
const isMined(status: TransactionStatus) => 
  status.state == 'Fail' || status.state == 'Success'
const isError(status: TransactionStatus) => 
  status.state == 'Fail' || status.state == 'Exception'
interface TransactionOptions = TransactionOverrides | {
  transactionSlowTimeout: number
  chainId: number
}

Implementation draft (no speed-up yet):

function useContractFunction(contract, functionName) {
  const [state, setState] = useState<TransactionStatus>()
  const send = (args) => {
    const transaction = contract[functionName](args)
    setStatus({'Mining', transaction})
    const receipt = transaction.wait()
    setStatus({status: getStatus(receipt), receipt})
  return {send, state}
}

Persisting the connection

On Reload of the page the user always has to reconnect manually.
Is it possible to persist the connection, so that the user if he didn't explicitly remove the dapp from the connected sites
it auto connects?

greets

Error thrown on NextJS

Hello, I was trying useDapp with NextJS earlier today and as soon as I pasted all the basic example code I got this error:

Screen Shot 2021-04-04 at 12 55 08 AM

Server Error
ReferenceError: window is not defined

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Call Stack
getItem
file:///Users/fede/Documents/learning/usedapp+nextjs/usedapp-test-web/node_modules/@usedapp/core/dist/src/hooks/useLocalStorage.js (6:16)
<unknown>
file:///Users/fede/Documents/learning/usedapp+nextjs/usedapp-test-web/node_modules/@usedapp/core/dist/src/hooks/useLocalStorage.js (29:52)
useReducer
file:///Users/fede/Documents/learning/usedapp+nextjs/usedapp-test-web/node_modules/react-dom/cjs/react-dom-server.node.development.js (1537:57)

This article talks about this issue with NextJS https://medium.com/frontend-digest/why-is-window-not-defined-in-nextjs-44daf7b4604e , but there are many more you can google.

Is there a way to get a fix for this so useDapp becomes NextJS compatible or it does not make sense at all for you guys? Can I help?

Thanks for your time! Have a great day!

Persisting connection on page refresh

Hey Guys! It's me again, I need a quick help on this:

I want the metamask login to persist even when the page refreshes. Unfortunately with useDapp the user needs to click "connect" every time there is a change of page...

The UX of having persistent login is very important, so far I have only seen it implemented here: https://github.com/mirshko/next-web3-boilerplate
Whenever I refresh the page I am still connected with metamask and react renders a "hello 0x1A2B3..." instead of "Connect"

Is this something that can be achieved with useDapp too? Any suggestions on this topic?

Hope I'm not too annoying, I'm just looking to make the perfect login experience for my dApps. Thanks for your time :)

Work on a comparable useMemo alternative?

Hi,
Been using this library(big time-saver) in production and been noticing some issues(either from my side or the library) i can't pinpoint.

My thoughts :

useMemo isn't the best solution for comparing objects returned from contract calls and BigNumber object as it uses Object.is() which is known to not make deep-comparison..

Solution Proposed :
If for it, we can change the whole useMemo call to a better alternative i found on the web
https://github.com/kotarella1110/use-custom-compare#usecustomcomparememo which implements a compare(prev, next) => boolean callback to deep compare the returned values to avoid unnecessary re-renders on user's components

I can in my spare time quickly work on this and we run tests to be sure all is when, before fully merging

๐Ÿ† 0xHack Bounty: ๐Ÿ’ฒ3000 DAI - BEST NEW useDApp feature

0xHack Bounty: ๐Ÿ’ฒ3000 DAI - BEST New useDApp feature

Prize Bounty

Amount: ๐Ÿ’ฒ2000 DAI

Challenge Description

Ethworks is sponsoring 0xHack with BEST NEW useDApp feature prize of $3000 DAI!
Building an new feature using useDApp is quite easy and should pose no difficulty to anyone ready to accept the challenge!

For inspiration check out useDApp roadmap and issues.

Submission Requirements

The main requirement is to build a new feature for useDApp as a pull request to main repository along with example use.

Judging Criteria

  • Usefulness of the feature for Ethereum developer community
  • Technical corectness
  • Idiomatic approach

Submission Deadline:

May 31st, 11:00 CEST

Winner Announcement Date

Judging will be at the end of the Hackathon on June 2nd.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.