Code Monkey home page Code Monkey logo

use-wallet's Introduction

👛 useWallet()

useWallet() allows dapp users to connect to the provider of their choice in a way that is as straightforward as possible. It provides a common data structure for any connected account, no matter what provider has been chosen by the user. It aims to provide some features that are often reimplemented by dapp developers: connecting to a wallet, keeping track of transactions, and more (submit a issue or PR!).

Features

  • All-in-one solution to connect to Ethereum providers.
  • Completely library agnostic (use Web3.js, ethers.js, …).
  • Provides the current balance.
  • Keeps track of the recent transactions (coming soon).

Opinionated?

Oh yes:

  • React only.
  • Ethereum only (for now).
  • Supports one network at a time.
  • Embeds as many providers as possible.
  • Every prop and parameter is optional.

What useWallet() is not

  • An Ethereum wallet implementation.
  • A low-level, fully configurable connection system for Ethereum providers (see web3-react if you are after that).
  • An general library to interact with Ethereum (see ethers.js, Web3.js, etc.).

Used by

Usage

Add it to your project:

yarn add use-wallet

Use it in your React app:

// App.js

import React from 'react'
import { useWallet, UseWalletProvider } from 'use-wallet'

function App() {
  const wallet = useWallet()
  const blockNumber = wallet.getBlockNumber()

  return (
    <>
      <h1>Wallet</h1>
      {wallet.status === 'connected' ? (
        <div>
          <div>Account: {wallet.account}</div>
          <div>Balance: {wallet.balance}</div>
          <button onClick={() => wallet.reset()}>disconnect</button>
        </div>
      ) : (
        <div>
          Connect:
          <button onClick={() => wallet.connect()}>MetaMask</button>
          <button onClick={() => wallet.connect('frame')}>Frame</button>
          <button onClick={() => wallet.connect('portis')}>Portis</button>
        </div>
      )}
    </>
  )
}

// Wrap everything in <UseWalletProvider />
export default () => (
  <UseWalletProvider
    chainId={1}
    connectors={{
      // This is how connectors get configured
      portis: { dAppId: 'my-dapp-id-123-xyz' },
    }}
  >
    <App />
  </UseWalletProvider>
)

API

<UseWalletProvider />

This is the provider component. It should be placed above any component using useWallet(). Apart from children, it accepts two other props:

chainId

The Chain ID supported by the connection. Defaults to 1.

connectors

Configuration for the different connectors. If you use a connector that requires a configuration but do not provide one, an error will be thrown.

  • injected: no configuration needed.
  • frame: no configuration needed.
  • fortmatic: { apiKey }
  • portis: { dAppId }
  • provided: { provider }
  • authereum: no configuration needed.
  • squarelink: { clientId, options }
  • walletconnect: { rpc: { 1: 'https://rpc-url', 3: 'https://rpc-url' } }
  • walletlink: { url, appName, appLogoUrl }

See the web3-react documentation for more details.

autoConnect

Automatically connect to wallet on page load. Defaults to false.

useWallet()

This is the hook to be used throughout the app.

It takes an optional object as a single param, containing the following:

  • pollBalanceInterval: the interval used to poll the wallet balance. Defaults to 2000.
  • pollBlockNumberInterval: the interval used to poll the block number. Defaults to 5000.

It returns an object representing the connected account (“wallet”), containing:

  • account: the address of the account, or null when disconnected.
  • balance: the balance of the account, in wei.
  • chainId: The specified Chain ID of the network you're connected to.
  • connect(connectorId): call this function with a connector ID to “connect” to a provider (see above for the connectors provided by default).
  • connector: The "key" of the wallet you're connected to (e.g "metamask", "portis").
  • connectors: the full list of connectors.
  • error: contains an error object with the corresponding name and message if status is set to error.
  • ethereum: the connected Ethereum provider.
  • getBlockNumber(): this function returns the current block number. This is a function because the block number updates often, which could trigger as many extra renders. Making an explicit call for the block number allows users of useWallet() to avoid extra renders when the block number is not needed.
  • isConnected(): this function returns whether the wallet is connected.
  • networkName: a human-readable name corresponding to the Chain ID.
  • reset(): call this function to “disconnect” from the current provider. This will also clean the latest error value stored in use-wallet's state.
  • status: contains the current status of the wallet connection. The possible values are:
    • "disconnected": no wallet connected (default state).
    • "connecting": trying to connect to the wallet.
    • "connected": connected to the wallet (i.e. the account is available).
    • "error": a connection error happened.
  • type: whether or not the account is a contract. Can be null when you're disconnected, or either "contract" or "normal".

Examples

To run the examples, switch to the respective directories. Then, simply run yarn install to install, and yarn start to run the examples on localhost:1234.

Special thanks

useWallet() is a built on web3-react and its connectors, which are doing all the hard work internally.

use-wallet's People

Contributors

0xgabi avatar andrekat avatar balachandrap avatar biga816 avatar bpierre avatar cbrzn avatar david-hung-thunder avatar dependabot[bot] avatar deri-protocol avatar eggswap avatar evalir avatar fabricevladimir avatar githubdoramon avatar jameslefrere avatar jyap808 avatar kellvembarbosa avatar lilesper avatar masquel avatar mathewmeconry avatar panukettu avatar sacgrover avatar saranshisatgit avatar sepehr2github avatar sohkai avatar venables avatar victaphu avatar wissenistnacht avatar wong2 avatar zxqx 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  avatar  avatar  avatar  avatar

use-wallet's Issues

Add auto connect mechanism

So I am able to connect the wallet successfully, but if I happen to refresh the page, the wallet will disconnect and I need to reconnect it again.

Wouldn't it make sense for the wallet to only disconnect when using the wallet.reset() function?

Release 0.8.1

Provided type is missing in the last release. It has been added with the #61 PR.
Please release a new version.

Snowpack / webpack 5 compatibility

This issue will keep track of the updates needed in the dependencies we use, in order to achieve full compatibility with Snowpack and webpack 5.

The two main issues are the way modules are exported, and the fact that they import native Node modules, requiring polyfills (which both Snowpack and webpack 5 don’t provide by default).

  • web3-react: replace Node imports (pull request).
  • @web3-react/frame-connector:
    • ethereum-provider: replace Node imports (pull request).
    • eth-provider: replace Node imports (pull request).
    • eth-provider: use updated ethereum-provider (commit).
    • @web3-react/frame-connector: use updated eth-provider.
  • @web3/walletconnect-connector:
    • @walletconnect/web3-provider: replace web3-provider-engine](WalletConnect/walletconnect-monorepo#310).
    • @walletconnect/http-connection: replace Node imports (pull request).
    • @walletconnect/web3-provider: use updated @walletconnect/http-connection.
    • @web3/walletconnect-connector: use updated @walletconnect/web3-provider.
  • @web3-react/portis-connector:
    • @portis/web3: replace web3-provider-engine (issue).
    • @web3-react/portis-connector: use updated @portis/web3.
  • @web3-react/walletlink-connector:
    • walletlink: replace Node imports (issue).
    • @web3-react/walletlink-connector: use updated walletlink.
  • @web3-react/torus-connector:
    • @toruslabs/torus-embed: remove use of eval().
    • @web3-react/torus-connector: use updated @toruslabs/torus-embed.

Add transaction queue

User story

As a user of use-wallet, I would like the library to manage pending and sent transactions from the signer.

This will also make it very easy to implement functionality like the ActivityPanel.

I cloned goosedefi.com and if i connect using walletconnect to goosedefi, it works but if i try to call contract method, it fails.

I cloned goosedefi.com and if i connect using walletconnect to goosedefi, it works but if i try to call contract method, it fails.
I checked the code and I think useWallet() hooks returns undefined even after connected.

@binance-chain/bsc-use-wallet ^0.8.1

<bsc.UseWalletProvider
    chainId={chainId}
    connectors={{
      walletconnect: { "https://bsc-dataseed.binance.org" },
      bsc,
    }}
></bsc.UseWalletProvider>

Originally posted by @hawkofsky in #84 (comment)

after update verison 0.11.0

Type '{ children: Element; chainId: number; connectors: { walletconnect: { chainId: number[]; rpcUrl: any; }; fortmatic: { chainId: number[]; apiKey: any; }; portis: { chainId: number[]; dAppId: any; }; walletlink: { ...; }; }; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<UseWalletProviderProps, "children" | "connectors" | "pollBalanceInterval" | "pollBlockNumberInterval"> & Pick<...> & Pick<...>, "children"> & Partial<...> & Partial<...>'.
Property 'chainId' does not exist on type 'IntrinsicAttributes & Pick<Pick<UseWalletProviderProps, "children" | "connectors" | "pollBalanceInterval" | "pollBlockNumberInterval"> & Pick<...> & Pick<...>, "children"> & Partial<...> & Partial<...>'. TS2322

222 |             <AccountButtonProvider>
223 |                 <UseWalletProvider

224 | chainId={chainId}
| ^

Unabled to catch connector rejections

Description

When running the reference code found in the Readme, when attempting to connect using the connectorId: walletconnect, if a user closes the modal, a breaking error is displayed Unhandled Rejection (RejectedActivationError):

Screenshot_20200720_142428

I have wrapped the wallet.activate in a try catch as well as a lower level class based componentDidCatch component but I am unable to intercept this error to prevent a crash

Problem using walletconnect connector

I use the use-wallet component on my react dapp. I have two connectors configured, a default one (injected) for metamask and another one for walletconnect. Any action or transaction that uses Metamask (injected) works perfectly, but whenever I want to use walletconnect I get errors after any simple web3 request.

"Uncaught TypeError: this.send is not a function....."
Every web3 request that works using Metamask fails on walletconnect.
I am using infura as the rpc for walletconnect and working on the Rinkeby testnet for my tests.
Could it be an specific version of my dependencies? I am using the latest available version of use-wallet through npm.

Thanks in advance.

Mobile browser support?

Hey there, maybe I'm missing something but I've noticed when I use this on a mobile phone browser, it does not launch the metamask app. Has anyone been able to get this to work?

Thanks!

WalletConnect not taking options

Even though I've set the right WalletConnectConnector options, it is still using "mainnet.infura.io" - is there anything I am missing? I've also tried implementing WalletConnect using a custom connector (which works without using this library).

My setup:

const walletConnectors = {
    injected: {
        supportedChainIds: [environment.CHAIN_ID],
        chainId: [environment.CHAIN_ID]
    },
    walletconnect: {
        chainId: 56,//environment.CHAIN_ID,
        rpcUrl: "https://bsc-dataseed.binance.org/", //environment.CHAIN_URL"",
        pollingInterval: 15000
    },
    walletconnectbsc: () => ({
        web3ReactConnector() {
            return new WalletConnectConnector({
                rpc: {
                    [environment.CHAIN_ID!]: environment.CHAIN_URL!
                },
                qrcode: true,
                pollingInterval: 15000,
            })
        },
        handleActivationError(err: any) {
            if(err instanceof UserRejectedRequestError) {
                return new ConnectionRejectedError()
            }

            return new Error(err)
        }

    })
}

connect('walletconnect') shows the connect dialog but returns a 401 of mainnet.infura.io
connect('walletconnectbsc') shows the connect dialog but returns a 401 of mainnet.infura.io

I encountered a problem during use

When my dapp is not connected to the wallet, the program will not get the contract data. I think the structure is because the Ethereum chainId cannot be obtained without connecting the wallet, or is it something I don’t know? Is there a way to use use-wallet to obtain contract data without connecting to the wallet?

Typescript error

Switching JS to TS causes this error:

const connectors = {
  walletconnect: { rpcUrl: process.env.REACT_APP_RPC_URL_3 },
  walletlink: { url: process.env.REACT_APP_RPC_URL_3, appName: 'Hello' },
}

Type '{ walletconnect: { rpcUrl: string | undefined; }; walletlink: { url: string | undefined; appName: string; }; }' is not assignable to type 'Partial<{ authereum: {}; fortmatic: { apiKey: string; }; frame: {}; injected: {}; portis: { dAppId: string; }; squarelink: { clientId: string; options: object; }; provided: {}; torus: { chainId?: number | undefined; initOptions: object; constructorOptions: object; }; walletconnect: { ...; }; walletlink: { ...; }; }>'.
  The types of 'walletconnect.rpcUrl' are incompatible between these types.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.  TS2322

This library uses an outdated version of the elliptic package marked as High Vulnerability.

This library uses an outdated version of the elliptic package marked as High Vulnerability.

Overview
The Elliptic package before version 6.5.3 for Node.js allows ECDSA signature malleability via variations in encoding, leading '\0' bytes, or integer overflows. This could conceivably have a security-relevant impact if an application relied on a single canonical signature.

Remediation
Upgrade to version 6.5.3 or later.

Hardware wallet (Ledger returns error when connecting)

There seems to be an issue when connecting ledger wallet.
Whenever I click connect, address is fetched, but balance returns -1.

Console returns error:

Uncaught (in promise)
Error: Web3ProviderEngine does not support synchronous requests

Suppressed stale connector activation

When I have connected to any connector, I call connect(id) again and an error will be reported Warning: Suppressed stale connector activation,At this point, I need to call connect(id) again to connect to the wallet normally,Is there any API to disconnect directly?
image

Usage with Typescript

Using use-wallet with create-react-app / typescript. Getting the following..

Property 'children' is missing in type '{ chainId: number; connectors: {}; }' but required in type 'UseWalletProviderProps'

Are there any steps necessary to get use-wallet working with TS?

can't use use-wallet library on heroku or netlify

Heroku - v2 build log

108 | attribute((deprecated(message))) declarator
| ^~~~~~~~~~
In file included from ../src/HID.cc:34:
../src/HID.cc: At global scope:
/app/.cache/node-gyp/14.17.5/include/node/node.h:758:43: warning: cast between incompatible function types from ‘void ()(v8::Localv8::Object)’ to ‘node::addon_register_func’ {aka ‘void ()(v8::Localv8::Object, v8::Localv8::Value, void*)’} [-Wcast-function-type]
758 | (node::addon_register_func) (regfunc), \\n | ^
/app/.cache/node-gyp/14.17.5/include/node/node.h:792:3: note: in expansion of macro ‘NODE_MODULE_X’
792 | NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
| ^~~~~~~~~~~~~
../src/HID.cc:646:3: note: in expansion of macro ‘NODE_MODULE’
646 | NODE_MODULE(HID, init);
| ^~~~~~~~~~~
SOLINK_MODULE(target) Release/obj.target/HID-hidraw.node
/usr/bin/ld: cannot find -lusb-1.0
collect2: error: ld returned 1 exit status
make: *** [HID-hidraw.target.mk:152: Release/obj.target/HID-hidraw.node] Error 1
gyp ERR! build error
gyp ERR! stack Error: make failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/tmp/build_f2ea8826/.heroku/node/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23)
gyp ERR! stack at ChildProcess.emit (events.js:400:28)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:277:12)
gyp ERR! System Linux 4.4.0-1094-aws
gypmake: Leaving directory '/tmp/build_f2ea8826/node_modules/node-hid/build'
ERR! command "/tmp/build_f2ea8826/.heroku/node/bin/node" "/tmp/build_f2ea8826/.heroku/node/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /tmp/build_f2ea8826/node_modules/node-hid
gyp ERR! node -v v14.17.5
gyp ERR! node-gyp -v v5.1.0
gyp ERR! not ok"

API changes

Issues with the current API

  1. In a normal scenario, app authors have to try / catch the activate() and store the error in the state so that users can be presented a connection error message. This is verbose and takes us farther from the goal of keeping the boilerplate code as minimal as possible useWallet().
  2. Passing both the connector identifier and the state, using activating, activated and connected, requires consumers of the library to test several variables to know the current state and the connector identifier.
  3. Using a mix of the term “activated” and the term “connected” is confusing. “activated” is coming from EIP 1102, which both defines and deprecates it. Most Ethereum providers use the term “connect” for this (see image below).

Proposed changes

The goal of these changes is generally to:

  • Make it possible to use the library by only importing the default export, useWallet().
  • Remove the confusion between “connected” and “activated” by only using the term “connect”.
  • Keep the boilerplate to a minimum for simple cases.

connect() and disconnect()

connect(id) and disconnect() would replace activate(id) and deactivate(). This is to remove any reference to the term “activation” and increase consistency.

function App() {
  const { account, connect, disconnect } = useWallet()
  return (
    <button onClick={account ? disconnect : connect}>
      {account ? 'Connect' : 'Disconnect'}
    </button>
  )
}

status

status would be a new exported value that would contain the current status of the wallet connection. It would have take one of these values at any given time:

  • "disconnected": no wallet connected (default state).
  • "connecting": trying to connect to the wallet.
  • "connected": connected to the wallet (i.e. the account is available).
  • "error": a connection error happened.

These are strings and not symbols so that users don’t have to import many symbols.

These values would get deprecated: activated, activating and connected.

Having a status connected guarantees that account is not null.

function App() {
  const { account, connect, status } = useWallet()

  if (status === 'connecting') {
    return <p>Connecting…</p>
  }

  if (status === 'connected') {
    return <p>Connected as {account}</p>
  }

  // status === 'disconnected' or status === 'error'
  return <button onClick={() => connect('injected')}>Connect</button>
}

error and reset()

error is a new value that contains the error object that comes with an "error" status.

There are different types of errors exported by module:

  • UnsupportedChainError
  • UnsupportedConnectorError
  • RejectedActivationError
  • ConnectorConfigError

error would be null when the status is not error, otherwise it would get populated by the corresponding error instance.

Users would have the possibility to import the error constructors and use instanceof, or to use the name property.

The message property can also be used to display the current error.

Calling reset() will make status go back to disconnected, and error back to null. Calling connect() would also clear the error state before initiating the new connection.

function App() {
  const { account, connect, error, reset, status } = useWallet()

  if (status === 'connecting') {
    return <p>Connecting…</p>
  }

  if (status === 'connected') {
    return <p>Connected as {account}</p>
  }

  if (status === 'error') {
    return (
      <div>
        <p>Error: {error.message}.</p>
        <button onClick={() => reset()}>OK</button>
      </div>
    )
  }

  // status === 'disconnected'
  return <button onClick={() => connect('injected')}>Connect</button>
}

@Evalir @sohkai Let me know what you guys think!

Injected provider: attempt to detect which injected provider

It would be nice if we had more details on the injected provider, as this was the most popular way to build web3 providers in the past and is more or less the only option on mobile.

There is really no easy way to obtain which injected provider is being used, but we may be able to try a few things (either here or in web3-react):

  • Special flags injected by the provider (e.g. Metamask)
  • Use eth_clientVersion (not sure about others, but frame supports it)
  • User agents (mostly for mobile web3 browsers)

Read-only connector?

Following @ajsantander suggestion, it might be interesting to provide a connector for non connected accounts, so that consumers could still benefit from the useWallet() utilities, even without using a wallet 😄

It could be named something like account or address, but we should make it clear that it is not a normal connector (that connects to a wallet), and calling connect() wouldn’t do anything when using it.

I am also wondering if we should pass the account using the connector configuration, or if we should add the possibility to pass parameters to connect(name), and pass it after the connector name?

@Evalir @sohkai What do you think?

Edit: actually the connect() could work the same way, except it would always succeed and happen instantly.

"Error: unknown account" with WalletConnect

Provider:

<UseWalletProvider
  chainId={chainId}
  connectors={{
    walletconnect: { rpcUrl: 'https://ethnode.steaker.capital/' },
  }}
>

After scanning the WalletConnect QRCode shown on my PC with my iPhone, authorizing with my iPhone imToken app, every calls to contract methods lead to this error:

index.js:1 Error: unknown account
    at t.default.<anonymous> (index.js:198)
    at Generator.next (<anonymous>)
    at i (tslib.es6.js:71)

any idea?

Error using trustwallet .

Getting the below error when using trustwallet as an injected provider.

Uncaught (in promise) Error: Trust does not support calling undefined synchronously without a callback. Please provide a callback parameter to call undefined asynchronously.
at TrustWeb3Provider.value (account:657)
at tr (vendorsmain.chunk.js:163526)
at vendors
main.chunk.js:163949
at request (vendorsmain.chunk.js:163950)
at o (vendors
main.chunk.js:163534)
at vendorsmain.chunk.js:163543
at vendors
main.chunk.js:163954
at invokePassiveEffectCreate (vendorsmain.chunk.js:117187)
at HTMLUnknownElement.callCallback (vendors
main.chunk.js:97803)
at Object.invokeGuardedCallbackDev (vendorsmain.chunk.js:97852)
at invokeGuardedCallback (vendors
main.chunk.js:97912)
at flushPassiveEffectsImpl (vendorsmain.chunk.js:117269)
at unstable_runWithPriority (vendors
main.chunk.js:138484)
at runWithPriority$1 (vendorsmain.chunk.js:105209)
at flushPassiveEffects (vendors
main.chunk.js:117146)
at performSyncWorkOnRoot (vendorsmain.chunk.js:115986)
at vendors
main.chunk.js:105263
at unstable_runWithPriority (vendorsmain.chunk.js:138484)
at runWithPriority$1 (vendors
main.chunk.js:105209)
at flushSyncCallbackQueueImpl (vendorsmain.chunk.js:105258)
at flushSyncCallbackQueue (vendors
main.chunk.js:105246)
at scheduleUpdateOnFiber (vendorsmain.chunk.js:115610)
at dispatchAction (vendors
main.chunk.js:109979)
at Object.connect (vendors~main.chunk.js:164152)
at async getSummary (main.91fa86827f088634653a.hot-update.js:455)

MetaMask no longer injects web3.

Since short I'm getting the following error making me unable to use use-wallet

MetaMask no longer injects web3. For details, see: https://docs.metamask.io/guide/provider-migration.html#replacing-window-web3


get | @ | inpage.js:1
-- | -- | --
  | (anonymous) | @ | browser.js:7
  | ../../node_modules/use-wallet/dist/index-ae10dbe8.js | @ | index.js:381
  | __webpack_require__ | @ | bootstrap:856
  | fn | @ | bootstrap:150
  | ../../node_modules/use-wallet/dist/index.js | @ | index.js:381
  | __webpack_require__ | @ | bootstrap:856
  | fn | @ | bootstrap:150
  | (anonymous) | @ | index.css:7

Optionally import wallets to minimize final bundle size

Issue:
Big final bundle size even if you want to use only one connection method.
Bellow i attached the image where you can see that dependencies such as Torus alone gets 23.2% even if it's not being used actively.

Solution:
I didn't go deeply into the repo but making the wallets you would like to use optional by letting UseWalletProvider know and installing/importing the wallet dependencies separately could be a solution.

Let me know your thoughts and thanks for maintaining the library

Kazam_screenshot_00008

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.