Code Monkey home page Code Monkey logo

nami's Introduction

Nami

Nami is a browser based wallet extension to interact with the Cardano blockchain. It's an open-source project maintained by IOG.

Testnet

Download and extract the zip attached to the latest Release. Then go to chrome://extensions, click Load unpacked at the top left and select the build folder.

Injected API

Since Nami is a browser extension, it can inject content inside the web context, which means you can connect the wallet to any website. The exposed API follows CIP-0030. The returned types are in cbor/bytes format. A helpful library for serializing and de-serializing these low-level data structures is the serialization-lib. To verify a signature returned from cardano.dataSign(address, payload) the message-signing library helps.

Basic Usage

  • Detect the Cardano provider (window.cardano) and detect Nami (window.cardano.nami)
  • Request the api from window.cardano.nami.enable()
  • Detect which Cardano network the user is connected to (ID 1 = Mainnet, ID 0 = Testnet)
  • Get the user's Cardano account

Methods

The full list of methods can be found in CIP-0030. For the wallet namespace Nami uses nami.

Note: Nami follows the ongoing PR for the dataSign endpoint. (Very similar to the previous dataSign endpoint from Nami).

Nami also uses a few custom endpoints, which are available under api.experimental:

api.experimental.getCollateral()
cardano.getCollateral() : [TransactionUnspentOutput]
api.experimental.on(eventName, callback)

Register events coming from Nami. Available events are:

accountChange: ((addresses : [BaseAddress]) => void)
networkChange: ((network : number) => void)
api.experimental.off(eventName, callback)

Deregister the events (works also with anonymous functions).


Injected API (Deprecated)

Since Nami is a browser extension, it can inject content inside the web context, which means you can connect the wallet to any website. The exposed API follows for most parts this proposed CIP. The returned types are in cbor/bytes format. A helpful library for serializing and de-serializing these low-level data structures is the serialization-lib. To verify a signature returned from cardano.dataSign(address, payload) the message-signing library helps.

Basic Usage

  • Detect the Cardano provider (window.cardano)
  • Detect which Cardano network the user is connected to (ID 1 = Mainnet, ID 0 = Testnet)
  • Get the user's Cardano account

Methods

All methods will return their values as Promise. For simplicity and easier understanding the API is explained without the Promises.

cardano.enable()

Will ask the user to give access to requested website. If access is given, this function will return true, otherwise throws an error. If the user calls this function again with already having permission to the requested website, it will simply return true.

cardano.isEnabled()
cardano.isEnabled() : boolean

Returns true if wallet has access to requested website, false otherwise.

cardano.getBalance()
cardano.getBalance() : Value

Value is a hex encoded cbor string.

cardano.getUtxos(amount, paginate)
cardano.getUtxos(amount?: Value, paginate?: {page: number, limit: number}) : [TransactionUnspentOutput]

TransactionUnspentOutput is a hex encoded bytes string.

amount and paginate are optional parameters. They are meant to filter the overall utxo set of a user's wallet.

cardano.getCollateral()
cardano.getCollateral() : [TransactionUnspentOutput]
cardano.getUsedAddresses()
cardano.getUsedAddresses() : [BaseAddress]

BaseAddress is a hex encoded bytes string.

Note Nami doesn't utilize the concept of multipe addresses per wallet. This function will return an array of length 1 and will always return the same single address. Just to follow the standards of the proposed CIP, it will return the address in an array.

cardano.getUnusedAddresses()
cardano.getUnusedAddresses() : [BaseAddress]

Note This endpoint will return an empty array []. Same reason as above, simply to follow the standards.

cardano.getChangeAddress()
cardano.getChangeAddress() : BaseAddress

Will return the same address as the one in cardano.getUsedAddresses().

cardano.getRewardAddress()
cardano.getRewardAddress() : [RewardAddress]

RewardAddress is a hex encoded bytes string.

Note This function will return an array of length 1 and will always return the same single address.

cardano.getNetworkId()
cardano.getNetworkId() : number

Returns 0 if on testnet, otherwise 1 if on mainnet.

cardano.signData(address, payload)
cardano.signData(address: BaseAddress|RewardAddress, payload: string) : CoseSign1

payload is a hex encoded utf8 string. CoseSign1 is a hex encoded bytes string.

If address is the BaseAddress the signature is returned with the Payment Credential, otherwise if the address is the RewardAddress the signature is returned with the Stake Credential.

The returned CoseSign1 object contains the payload, signature and the following protected headers:

  • key_id => PublicKey,
  • address => BaseAddress | RewardAddress
  • algorithm_id => EdDSA(0) (the algorithm used for Cardano addresses).

Read more about message signing in CIP-0008.

cardano.signTx(tx, partialSign)
cardano.signTx(tx: Transaction, partialSign?: boolean) : TransactionWitnessSet

Transaction is a hex encoded cbor string. TransactionWitnessSet is a hex encoded cbor string.

partialSign is by default false and optional. The wallet needs to provide all required signatures. If it can't an error is thrown, otherwise the TransactionWitnessSet is returned.

If partialSign is true, the wallet doesn't need to provide all required signatures.

cardano.submitTx(tx)
cardano.submitTx(tx : Transaction) : hash32

Returns the transaction hash, if transaction was submitted successfully, otherwise throws an error.

Events

cardano.onAccountChange(addresses)
cardano.onAccountChange((addresses : [BaseAddress]) => void)

Note To follow the standards of multiple addresses the callback will return an array, although Nami will just return an array with a single address, which is the same as the one in cardano.getUsedAddresses().

cardano.onNetworkChange(network)
cardano.onNetworkChange((network : number) => void)

Develop

The project_id for API requests can be created under blockfrost.io.

Recommended: Follow this approach in order to keep the keys seperate from the repository.

# Update secrets file with your own keys
cp secrets.testing.js secrets.development.js

The quick solution is to go under ./src/config/provider.js and replace secrets.PROJECT_ID_MAINNET, secrets.PROJECT_ID_TESTNET, secrets.PROJECT_ID_PREVIEW and secrets.PROJECT_ID_PREPROD with the project ids from blockfrost.

Requirements
  • Node.js 20
Start development server
# Update secrets file with your own keys
cp secrets.testing.js secrets.development.js
npm start
Create production build
# Update secrets file with your own keys
cp secrets.testing.js secrets.production.js
npm run build
Run tests
npm test

Additional

Wasm packages commit hash: 9986d0532334c465bbe4aa00234968edb43575d6

Website

Visit namiwallet.io

nami's People

Contributors

alessandrokonrad avatar danielfarrelly avatar deadpxlz avatar dominikguzei avatar edridudi avatar hashguardian avatar hodlonaut avatar kevinbalkoski avatar ljagiela avatar lucas-barros avatar matheussanton avatar mkazlauskas avatar neo00411 avatar nicholasmaselli avatar pascallapointe avatar renanvalentin avatar rhyslbw avatar sadasant avatar sublayerio avatar thaddeusdiamond avatar trenchatop avatar vanessapc 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

nami's Issues

Improvement Proposal: State management

Abstract

Centralize (yes, sometimes centralization is good) state in the Nami wallet. Move the (only the necessary) state away from the component level to the global application level. This implies writing selectors (select state) & reducers (mutating state) that manage state. This state can then be stored in a persistent data layer within the chrome extension itself and retrieved whenever the application re-opens. This can be applied incrementally.

Motivation

Whenever you open the chrome extension and perform an action the UI changes. Let's say you click "send" and the application takes you to the screen where you can enter an address. These are changes to the "state" of the application. Once the user clicks on any part of the screen that's not the extension, the extension loses focus and closes. Once the extension then is opened again, the extension has not preserved the changed "state". The goal is to preserve this state and give the user the experience that they can continue their actions in the extension after (accidentally) closing it.

Specification

Add dependency

Remember the page the user has opened

Changes to send page

Store values in global state:

  • address — The receiving address for the tx.
  • amount — The amount of ada being send for the tx.
  • assets — The list of assets that are send alongside the tx.

Possible future changes once this has been implemented

  • Store history / assets tab state for wallet page.

Related issues

  • UX improvements suggestions #21

Content script cardano object may not be there when the page loads

The content script uses run_at: document_start which does not guarantee that the extension cardano object is present when the page loads:

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts#run_at

https://developer.chrome.com/docs/extensions/mv2/content_scripts/#run_time

As an example, when loading the following simple page, cardano object is undefined in the logs, but there once manually typing cardano in the javascript console after.

<html>
  <head>
    <script>console.log("head", cardano)</script>
  </head>
  <body>    
    Testing Nami Injection
    <script>console.log("body", cardano)</script>
  </body>
</html>

nami_injection

As there is no setting that can guarantee that, metamask for example sends a ethereum#initialized event once the wallet provider is fully loaded:

https://github.com/MetaMask/providers/blob/a5b8ebec8538469849ded71a9385761f6e6efb71/src/initializeInpageProvider.ts#L78

Maybe something similar should be done if there is no better solution.

Until then, testing cardano presence with a timer and timeout seems to be the most reliable way.

Nami minting takes forever to mint one NFT. Same Policy ID for two different NFT?

So I was just minting some NFT gifs, but sometimes it takes forever.

Also. I minted two completely unique NFTs but it grouped some token together like one policy ID had 8 tokens, one single policy ID had 2 token, and so on. Is this normal?
I named them similar like "NameofProject # 02 - Name" as they are supposed to be part of a project.

Is that the reason?

Image URLs that are split into arrays in metadata do not render in Nami wallet

I'm having issues with assets not rendering their images correctly which I believe is related to them being an array. The CIP for 721 metadata, it says image can be a URI or an array:

https://github.com/cardano-foundation/CIPs/blob/8b1f2f0900d81d6233e9805442c2b42aa1779d2d/CIP-NFTMetadataStandard.md

"image": <uri | array>,

My IPFS URLs are > 64 chars, so I split them into an array like this:

         image: [
            "ipfs://bafkreighf2nnlrg25yyf7u47gfgexj5jywzuyj6wlvp7uh2ofz2c2jqi",
            "g4"
         ],

However, they fail to render in nami wallet. If I inspect the page, I see that the URL being requested is like:

https://ipfs.blockfrost.dev/ipfs/bafkreighf2nnlrg25yyf7u47gfgexj5jywzuyj6wlvp7uh2ofz2c2jqi,g4

The comma near the end looks suspicious. It looks like this might have been fixed by b194398 but that shipped in 1.1.3 and I'm using 1.1.5. I wonder if perhaps there's another place that needs this - perhaps line 47 here? (I'm not sure of the difference between `result.metadata and result.on_chain_metadata):

https://github.com/Berry-Pool/nami-wallet/blob/b19439815d217247a001bb17884bfe0a90ade288/src/ui/app/components/asset.jsx#L41-L48

Primary Wallet wont sync since version 2.0 update

I'm have several sub wallets within nami. The primary wallet though is problematic. I sent a few transactions after it updated to version 2. Whats happening now is this:

  1. Attempt to view primary wallet
  2. Wallet displays its contents as they were prior to the last transaction
  3. 10 seconds later the wallet attempts to refresh; and remains with the loading spinner

I've tried to let it sit for an hour or more with no responsiveness.

Because I'm unable to change between wallets while its doing this, the wallet is stuck. My work around is to go in to settings, change to testnet, then move to a different subwallet, and then flip back to mainnet.

Adding a seed verification feature near the reset button.

User should be able and strongly recommended to verify their seed before user the reset feature.

A feature allowing the seed verification like in Daedalus shall be implemented.

There is other way to verify a seed, like trying to import it with an other wallet, Nami on an other computer, Nami in an other browser.
But making it easy would protect Nami user from loosing their owning.
Nami with the wallet growing fast, I think we should put as much tool as possible to avoid any loss.

Is it safe to store wallet credentials in chrome storage?

As per the code, https://github.com/Berry-Pool/nami-wallet/blob/9cb160bbb740434ff6c4968140c5852546a2bdac/src/api/extension/index.js#L37

for every new wallet created, the accessors are stored in the chrome storage.
As high-lighted by the Google Chrome dev portal : https://developer.chrome.com/docs/extensions/reference/storage/, its unsafe to store any confidential information in chrome storage. Additionally, the information stored isn't encrypted by default. Besides, browser wallets are the easiest ways to be hacked into, plus storing the wallet information in browser may lead to security flaws.

Require login after a certain amount of time has passed

It'd be great if the user had to login after a certain amount of time has passed. The motivation behind this proposal is solely security based. If people don't wan't to unlock their wallet - after lets say 5 mins - it'd be great if this feature is optional and can be changed, depending on the users preferences. All major wallets have this feature build-in and I'd like to see it in the Nami Wallet as well.

Testnet UI

A UI detail may be: Testnet version rightly reports tADA as a balance but still displays USD/EUR equivalent value. It probably should not - Hoping to avoid mistakes while switching between different wallets/machine/browsers.

Anyway, congrats on this nice piece of software !

submitTx() : Wallet could not send the tx.

cardano.submitTx(tx : Transaction) : hash32 return this error:

{code: 2, info: "Wallet could not send the tx."}

I suppose tx input for submitTx() is the output of signTx()?

Anyway, I also used the tx input of signTx(tx: Transaction), for the input for submitTx(tx : Transaction). Still getting the same error message Wallet could not send the tx..

cardano.enable(): return false instead of throwing error

... If access is given, this function will return true, otherwise throws an error.

Any reason why throw an error instead of returning false? This error seems like try catch is unable to catch it, thus unable to prompt user what to do.

Get assets using injected api

I can't find a way to retrieve all nfts of the wallet using the injected api, is there a way, and if, how?

Thanks

UX improvements suggestions

Hi there!

So far I really like my experience with NAMI Wallet. I have observed 2 things that could make a better user experience. Here they are:

  1. When you click outside the extension to copy an address or an ADA amount to transfer, the extension closes and you have to start over. Keeping the extension open permanently would greatly improve this.

  2. Once we enter our password to spend, we have to manually click on "ok" to proceed with the sending. Allowing us to use "enter" to confirm the transaction would be great to make the experience even smoother.

Thanks a lot and let me know if I'm in the wrong place to leave this kind of feedback.

Sam

Cardano.signTX error

Hi!
thanks a lot for this tool,
I am trying to sign some transactions but I keep getting this error "Inputs do not conform to this spec or are otherwise invalid."

My api call is:

let transaction= {tx:"83a30081825820745084b076a17275916921ad4cadbda2ef071587fe0b2cda80e14df5a2848a83000182825839011c471b31ea0b04c652bd8f76b239aea5f57139bdc5a2b28ab1e69175fd3a6bfce30d7744ac55e9cf9146d8a2a04ec7fb2ce2ee69862606531a000c917b825839010d50033b5cfc6177d1bf3fdfc958fc0112b4ec3ba12960df29cebfe39500d456024a38c1658ff57ba22f2f91612aad725ad26a1d2daf00f31a001e8480021a0002b0c59ffff6"};
console.log(transaction)
const signedTx=await cardano.signTx(transaction);

Can we ever expect a Firefox extension?

Can we get a firefox extension for Nami? That would be so good.

With the recent API updates, google is forcing extensions to use the newer v3 API that effectively makes adblockers unusable. This is a big issue as they can force a lot of different things in the future. That's just my thinking, maybe. But it makes me want a bit anxious.

Obviously a lot of people use Firefox and it would be really nice to have Nami on Firefox.

Thanks :)

Making storage resilient to structure change

Issue

Making storage data structure change breaks the wallet, forcing users to reinstall the extension to fix the issue.

Current work-around

The wallet is currently using if/else condition check to adopt the proper behavior. This will quickly go out of hands, thus the current issue to find a better way.

Some possible options

Adding a version field that test last storage version against current software version, or possibly a storageVersion field, then:

options:

  • flush all storage except the user master key, tracked account and user settings choices. Since all data are fetch from an API, the wallet can rebuild the new data structure from scratch. Downside: the cache is void causing long loading time after every update.
  • creating a storage migration mechanism. Upon detecting a version change, the wallet will seek for migration script to run. The migration script ensure the proper migration of one data structure to an other without any data loss. The migration number is then tracked in a migrationVersion to avoid running it again. Downside: The implementation of the mechanism and writing bug free migration is tedious.

AssetsViewer: UI suggestion

Hi! Thanks for creating Nami :D

What do you think about displaying assets in a more beautiful way like Solana's Phantom wallet:

image

2 assets per line (bigger), no texts shown unless there are interactions, scroll down to keep seeing.

I almost opened a pull request changing the assets grid to 2 columns, but then I saw you're using Slider so it wouldn't work that well.
Why are you using Slider instead of a y overflow though? Doesn't it require more clicks for the user?

Cannot send my ADA away

Hi, I’m not able to send my ADA away from the wallet. The balance is ok, but when I press the send button and the recipient, I cannot select the amount of my ADA…

Cannot send Test ADA

Hi,

I did a few test transactions to the test addresses generated using serialisation-lib working on top of 'Tango-Crypto library. The transactions are all valid in the explorer. However, the ADA are still 0 in the target account.

txns :

  1. https://explorer.cardano-testnet.iohkdev.io/en/transaction?id=fe0898f70ecbad8c6fd38c3be0c1060586f0a4f126d1eb60d97406c1fadfdccc

  2. https://explorer.cardano-testnet.iohkdev.io/en/transaction?id=0f305f87c2bd1280f4d3c2af1b6d87870458679ea4de35089f884b4dce0accc4

Add “send all” support

Make it more convenient for people to empty their wallet in a single transaction without having to figure out the exact decimals given the tx fee.

revamp api

Hi

some constructive feedback:

if you want MASS adaption of you wallet, then get rid of the cardano types.

Make the api as simple as possible: when sending a value send it as 12345.6789 ADA and not as a Value object.

When you send an address, send it as a bech32 address. Accept values in that format and transform your self.

Heck, you could have a raw api (with cardano types) and a default API with simple types.

One of the things that makes metamask and web3 so wonderful, is how easy it is to work with it. If you give this simplicity to web developers, you will be the standard wallet. Try to design APIs with your end user in mind, not the system you are interfacing with.

You can get in touch with me on discord if you want to discuss Anixai#4159.

PS: for the rest I am sending you a lot of LOVE for creating nami wallet. Thanks for all your efforts! If you want help / contributers, let me know.

Jeroen

Typo in getBalance() method

  1. Enable wallet via cardano.enable()
  2. Access the wallet balance via cardano.getBalance()

Result: Accessing the getBalance() method returns an error due to typo in method name (see attachment)

Screen Shot 2021-07-12 at 4 34 23 PM

Creating transaction for signTx

Is there a way to create a transaction that can be passed into cardano.signTx without using CLI? I'm trying to build a site where users can simply click a button to buy an NFT and would like to take advantage of Nami's injected API to do this. What's the best way to create such a transaction from JS?

Get the Wallet address

So my issue is simple:
I achieve to get the address through the API with cardano.getUsedAddresses().
But I need to convert the return to a correct form. I didn't achieve to anything and the documentation of cardano serialization is awful.
If someone have a solution or a tips it's welcome!

FR: Simple Transaction Endpoint in API

Create an API endpoint that will allow a website (such as a token drop or merchant interaction) to simply send parameters for a transaction (i.e. send me 15 Ada to this address) rather than requiring merchants to build out the entire transactional logic on behalf of the user's wallet.

The current low-level implementation seems rife with the possibility for errors on behalf of the merchant/vendor and could also lead to scams and abuse. The wallet already has native token selection and transaction compilation logic embedded so much better for a vendor to simply request funds and provide a payment address and leave the logic to the wallet.

In considering the current CNFT space, perhaps this could also specify one or more tokens that should be provided (i.e. 500 spacecoins, 1 Spacebudz, etc) as part of some gamification, escrow, etc.

Injected API

So kind of a noob question here. If I want to write a website that only reads a user's Cardano address from Nami wallet, can I use window.cardano with just the browser extension installed, or do I need to import the API scripts from this repo into my website to do that?

I'm thinking along the lines of using window.ethereum with Metamask... I tried calling window.cardano but it returns undefined. I must be missing something?

Fire Site Removal Event

Right now, if you connect the wallet, then the browser gets a response. However, if a user goes to Whitelisted Sites and removes a site, there is no callback that a website can use to perform an action (i.e. remove cached wallet data).

Suggestion: add an event for onSiteRemoval or something similar to allow browsers to react to this.

Adding support for paper wallets

I would like to request support be added so one could use a paper wallet to transfer an asset into a Nami wallet. Ideally, a mnemonic word group would be used show assets in that wallet, those assets could then be transferred out of the paper wallet and into the Nami wallet.

`cardano.getCollateral()` returning empty array, 'Collateral not set' in wallet

I'm trying to submit an Alonzo-era transaction with some collateral. I was able to do it in my server, but when requesting a signature from the user I get the error: Collateral not set. This piece of code (src/ui/app/pages/signTx.jsx line 331) is causing it:

const checkCollateral = (tx, account) => {
    const collateralInputs = tx.body().collateral();
    if (!collateralInputs) return;
    if (!account.collateral) { // Here it's not detecting the collateral
      setIsLoading((l) => ({ ...l, error: 'Collateral not set' }));
      return;
    }
    for (let i = 0; i < collateralInputs.len(); i++) {
      const collateral = collateralInputs.get(i);
      if (
        !(
          Buffer.from(collateral.transaction_id().to_bytes()).toString('hex') ==
            account.collateral.txHash &&
          collateral.index() == account.collateral.txId
        )
      ) {
        setIsLoading((l) => ({ ...l, error: 'Invalid collateral used' }));
        break;
      }
    }
  };

HOW TO DECODE USEDADDRESS FROM NAMIWALLET?

Can you tell me how to get this address from the wallet
image

I have this address:
image

When I connect the wallet to the site, I get this:
image

How do I decode a hex encoded bytes string?
I will be very grateful!!!!!!!!!!!

Inputs do not conform to this spec or are otherwise invalid

I'm getting this error when trying to sign the simplest transaction:

cardano-cli transaction build-raw \
    --tx-in 88d9225ff5d46b0efcd56dda20f2ea93b0a5fa232e7f70ab7d626746075a20d1#0 \
    --tx-out addr_test1qzymym7qc47kx65sjhntdl2prq85rj5v8mzv5pvyffnntz4el5zqnamef3yt2kprt866p5f38w6um6642lvzrj80d0xstnnmxq+10 \
    --tx-out addr_test1qzjlc05tyyw264wy7m4u7np5yqdwglks0xhu6765cl4qex9r9kvav4hmznru9px9n7cpa2hmmv4593eegve3t834xppqwskp4t+999823269 \
    --invalid-hereafter 369400 \
    --fee 176721 \
    --out-file tx.raw

ready2

It's an alonzo-era transaction

signTx's TransactionWitnessSet produce TextEnvelope decode error in cardano-cli

@alessandrokonrad . For cardano.signTx(), the output TransactionWitnessSet, does not tally the input required in cardano-cli, producing this error:

Command failed: transaction sign-witness  Error: buyer_witness_set_from_nami_signTx.witness: TextEnvelope decode error: DecoderErrorDeserialiseFailure "Shelley Witness" (DeserialiseFailure 0 "expected list len")

Seems like, the cborHex Nami produced after signTx() has extra 2 characters.

Balance displaying wrong amount

Balance has be reporting showing wrong amount a few time by different user.

Example:

Balance showing 1.5 ADA

History (DESC order):

+1.5 ADA

  • 50 ADA

So here the transaction of 50ADA isn't reflected in the balance.
The last transaction is 1.5ADA and it is the only amount showing.
The balance hasn't updated fully on the last transaction, so the code need to be reviewed first to pin point the exact situation that could cause this to happen.

The user reported being able to transfer back the full balance to another wallet.

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.