Coder Social home page Coder Social logo

renproject / ren-js Goto Github PK

View Code? Open in Web Editor NEW
135.0 22.0 48.0 18.71 MB

RenJS - the official JavaScript library for interacting with RenVM

Home Page: https://renproject.github.io/ren-client-docs/

License: MIT License

TypeScript 98.87% JavaScript 1.13%

ren-js's Introduction

🛠️ ren-js

Version

The official Javascript SDK for interacting with RenVM.

yarn add @renproject/ren @renproject/chains

Docs

See Ren Client Docs

RenJS v3

RenJS v3 is currently available as an alpha release:

yarn add @renproject/ren @renproject/chains

RenJS v3 Docs (WIP)

Changelog

See the Releases page.

Package list


Developer docs - click to expand

Developing locally

# Clone repository
git clone [email protected]:renproject/ren-js.git && cd ren-js

# Install dependencies
yarn

# Build every package
yarn run build

Linking

If you want to use your local version of RenJS in another repository, run

# In the ren-js repository
yarn run link:all

You can now link it to any other local repository by running:

# In other local repositories
yarn link @renproject/ren @renproject/chains @renproject/utils @renproject/provider

Running tests

You'll need to:

  1. Generate a mnemonic and send ETH (goerli for testnet) (path: m/44'/60'/0'/0/).
    • let w = require("ethers").Wallet.createRandom(); console.debug(w.address, w.mnemonic.phrase);
  2. Generate a private key and send testnet crypto funds.
    • require("send-crypto").newPrivateKey();
  3. Optionally generate an Infura API key.

Create a .env file which contains the following exported variables:

export MNEMONIC="your mnemonic here"
export TESTNET_PRIVATE_KEY="your bitcoin private key"

# Optional
export INFURA_KEY="your infura key"

To run the tests:

yarn run test

ren-js's People

Contributors

0x31 avatar jazg avatar loongy avatar negaex avatar ryanswrt avatar terrafirmatrekker avatar vinceau avatar xnyl 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

ren-js's Issues

Improve testing for individual packages

RenJS's tests should be split up among the relevant packages.

  • Tests are split up into relevant packages
  • Tests pass for all packages
  • Test coverage is calculated for all packages cumulatively

How to restore a transaction with BTC TXID?

For the usecase of depositing (or minting), there is a time window (to wait for 6 confirmations) between a successful ren transaction submission and a valid ren response which holds signature and etc that are necessary for subsequent Ethereum submission.

Suppose that, for some reason, our application lost track of the process in this time window, which means the random nonce used to produce gatewayAddress and ren txHash is lost, and the only thing that our user can provide us with is his/her BTC TXID and corresponding amount.

In this senario, what can be done to restore the transaction process? I did a quick look at 2.0.0-alpha and the new processDeposit method, but it requires same nonce to be provided, which is lost.

A possible workaround is to tag every mint with user's Ethereum wallet address, and if above said case occurs, we can use queryTxs rpc method to query all transactions tagged with user's Ethereum wallet address, and filter them with BTC TXID and amount (as ren tx holds corresponding UTXO).

However, this approach is quite time consuming: queryTxs does not support status filtering yet and it is sorted by creation time in ascending order, and pageSize parameter also has a upper bound, so we need to do an exhaustive search to find the lost transaction.

Handle multiple deposits to gateway address in GatewayJS

Description

GatewayJS can't currently handle multiple transfers to a gateway address, and it does not warn the user of this either.

Design

Possible solutions include:

  1. The Gateway widget lets the user submit multiple signatures to Ethereum
  2. The Gateway widget forks into multiple widgets to handle the deposits
  3. The user is warned that additional deposits can't be recovered

Multiple solutions could be provided, allowing the 3rd party developers to decide how multiple deposits should be handled.

Sats utill sometimes returns float

RenJS.utils.value(amount, "btc").sats().toNumber() will sometimes return a float, which if using to pass uint256 amounts to web3, will cause errors.

Allow developers to configure/override RenJS dependencies

Standard interfaces should be designed and documented to allow developers to override some of the following:

  • The lightnode URL - supported but not documented
  • The Ethereum connection - supported by providing a Web3 provider
  • The price feeds used to choose shards
  • The APIs used to look for transactions to the gateway addresses

Bridge not working when Brave Shields are UP

Attempting to do a transaction on RenBridge does not work if the Brave browser Shields are UP.

Disabling the Brave Shields makes the problem go away.

Sorry if this is not the correct repository, I couldn't one specifically for RenBridge.

Query gateway pubkeys when building gateway addresses

Currently, RenJS cannot handle gateway pubkeys that change over time. During SubZero, this is not an issue, because there is only one gateway pubkey and it does not change. However, for future compatibility, RenJS should not rely on this behaviour.

Instead, RenJS should call the ren_queryShards RPC to get back a list of all the shards, and all the gateways within each shard. From this, RenJS can calculate which shard is the least utilised, and use the gateway pubkey from that shard to construct a gateway address.

Design

The frontend must go through the following steps when building gateways (it can optimise slightly by doing it on load, and make the assumption the page will be refreshed at least once per epoch):

  • Call the ren_queryShards RPC.
  • Filter to only keep shards that are online.
  • Find the shard with the lowest total value locked (sum all the locked amounts from all gateways in a shard, after converting to a consistent currencies using the CoinMarketCap API).
  • Get the gateway pubkey from the gateway with the right asset within the shard with the lowest total value locked.
  • Use this gateway pubkey to build the gateway address.

Depends on

https://github.com/renproject/darknode/issues/232

Improve examples, add more tutorials, and review existing tutorials

All examples could be improved by:

  1. Providing a clonable repo of the example
  2. Providing a CodePen (or similar) with the code of the example
  3. Providing a link to the deployed version of the example (whether its contracts or a UI)

An example on how to integrate to import and deploy the darknode-sol contracts in a truffle project should should be written.

RenJS + React + Truffle boilerplate

Truffle boxes are a popular way to provide a boilerplate for projects that tie in one or more of a JavaScript library, a user interface and Ethereum smart contracts.

The boilerplate should provide the following:

  1. Migration scripts that set-up local versions of the Gateway Registry, Gateways and ren tokens.
  2. An example smart contract that provides a function for minting and burning the locally deployed ren tokens.
  3. A React page that uses RenJS to talk to the smart contract.
  4. Truffle tests that interact with the smart contract without RenJS
  5. JavaScript tests that interact with the smart contract with RenJS.

See https://truffle-box.github.io/

Reliable GatewayJS storage

Currently, GatewayJS uses browser local storage for storing transfers that are in progress.

Local storage is not reliable, as it 1) doesn't work in a private browser window, 2) can be disabled by users, 3) gets reset occasionally by users and 4) gets reset after a week on mobile safari.

The current options being considered are:

  1. 3Box - decentralized but affects the user experience by requiring signatures before reading and writing data
  2. Lightnodes - stored in a central server, but may be self-hostable in the future
  3. Require developers to implement their own storage

If (1) or (2) are chosen, developers should still have the option of passing in a set of functions that implement a storage API to override the default storage. (3) is what RenJS requires, but may not be the best option for GatewayJS, whose aim is to to allow developers to get started quickly, while still remaining customizable.

Compatible with Darknode v0.1.0

Dependencies

https://github.com/renproject/darknode/milestone/1

Overview

  • args have been broken up into in, out and autogen
  • b20 type is now called ext_ethCompatAddress
  • hash field is now called sighash
  • number values are now transmitted as strings
  • Values are not included in gateway generation
  • Transaction hashes are now based on the the gateway plus the btc/bch/zec transaction hash and vOut.
In: Formals{
    {"phash", TypeB32},                 // The hash of the payload data
    {"token", ExtTypeEthCompatAddress}, // The ERC20 contract address on Etherum for ZZEC
    {"to", ExtTypeEthCompatAddress},    // The address on the Ethereum blockchain to which ZZEC will be transferred
    {"n", TypeB32},                     // The nonce is used to randomise the gateway
    {"utxo", ExtTypeBtcCompatUTXO},     // The UTXO sent to the gateway
},
Autogen: Formals{
    {"ghash", TypeB32},   // The hash returned by `keccak256(abi.encode(phash, token, to, n))`
    {"nhash", TypeB32},   // The hash returned by `keccak256(abi.encode(n, txhash))`
    {"sighash", TypeB32}, // The hash returned by `keccak256(abi.encode(phash, amount, token, to, nhash))`
    {"gas", TypeU64},     // The blockchain gas that has been spent to move funds through the gateway
},
Out: Formals{
    {"r", TypeB}, // The R component of a signature over the input hash
    {"s", TypeB}, // The S component of a signature over the input hash (lower range)
    {"v", TypeB}, // The V component of a signature over the input hash
},

ren_submitTx

args are now accesssible from the in field and the b20 type (base64) has been changed to ext_ethCompatAddress (hexadecimal).

{
	"tx": {
		"to": "ZEC0Zec2Eth",
		"in": [{
			"name": "phash",
			"type": "b32",
			"value": "G/sJ856l0Dre1HYiKN4V7UFBJgaIcCahHG8nXuQEzVI="
		}, {
			"name": "token",
			"type": "ext_ethCompatAddress",
			"value": "71b6a19fc832bd9c739489ecbea67ab41261026f"
		}, {
			"name": "to",
			"type": "ext_ethCompatAddress",
			"value": "254db636af5a759b00cd809e605d513a63704724"
		}, {
			"name": "n",
			"type": "b32",
			"value": "y6EaObWm2BY3T+OJYMGiNwcGKdfqZX2W1d1rqFmirAM="
		}, {
			"name": "utxo",
			"type": "ext_btcCompatUTXO",
			"value": {
				"txHash": "8YhnMnqUgZ9oC+2Ao6sY4YlPzhHMO0pnb7YlXTg7ij8=",
				"vOut": "0"
			}
		}]
	}
}

Is there a way to sign transactions from smart contracts?

Hi there,

We are developing a staking pool for the Ren protocol. At the moment the app allows users to deposit/withdraw tokens, register and deregister a darknode on the ren protocol. This is the repo and here a humble video showing the main functionality

The problem is, given that the darknode is registered via a smart contract and not a regular wallet, we are unable to claim fees due to the fact that we need to sign a transaction and smart contracts don't have a notion of private keys. I was wondering if there is any implementation of the EIP-1271 on the ren protocol / sdk or any other approach you would suggest to tackle the problem.

Thanks and keep with the great work :)

FIL Transaction failure

Transaction failed, I don't know what the reason, its trading hash is: YtTCBCPloLXf7sJGunwhj9vUc6ok3KSj5EdtQlEtvU0;

other state data:
{"logger":{"level":0},"selector":"FIL/toEthereum","config":{"logger":{"level":0},"networkDelay":15000},"renNetwork":{"name":"mainnet-v0.3","lightnode":"https://lightnode-new-mainnet.herokuapp.com/","isTestnet":false},"pHash":{"type":"Buffer","data":[142,254,40,115,211,135,90,202,17,62,125,163,224,23,130,28,63,59,208,186,179,222,255,6,231,200,18,238,160,196,173,131]},"gHash":{"type":"Buffer","data":[59,239,159,117,33,60,69,0,234,137,24,232,130,85,219,224,99,252,193,34,214,156,79,146,254,35,220,81,185,227,53,167]},"gPubKey":{"type":"Buffer","data":[3,137,39,69,120,0,147,28,125,68,220,148,203,0,33,180,232,129,249,147,92,18,52,220,146,244,86,227,46,192,25,213,215]},"targetConfirmations":200,"nHash":{"type":"Buffer","data":[247,103,160,200,134,71,231,219,193,108,185,172,187,34,189,129,195,195,179,192,171,106,20,54,186,126,8,28,219,218,138,108]},"nonce":{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,10,248,236,171,22]},"output":{"txid":{"type":"Buffer","data":[1,113,160,228,2,32,177,43,224,206,14,131,246,48,249,94,127,7,251,70,93,171,245,105,17,103,165,83,174,230,28,67,119,212,96,223,71,175]},"txindex":"72"},"amount":"100000000000000000","payload":{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,48,107,184,8,28,125,211,86,234,149,23,149,206,64,114,230,228,191,220,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,70,73,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"to":"Ae65b0f676313Fd715F29D07538d1dc8557f2b1A","fn":"mint","fnABI":[{"constant":false,"inputs":[{"type":"string","name":"_symbol"},{"type":"address","name":"_address"},{"name":"_amount","type":"uint256"},{"name":"_nHash","type":"bytes32"},{"name":"_sig","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable","type":"function","name":"mint"}],"tags":[],"txHash":"YtTCBCPloLXf7sJGunwhj9vUc6ok3KSj5EdtQlEtvU0","renTxSubmitted":false,"queryTxResult":{"hash":"YtTCBCPloLXf7sJGunwhj9vUc6ok3KSj5EdtQlEtvU0","txStatus":"done","to":"FIL/toEthereum","in":{"txid":{"type":"Buffer","data":[1,113,160,228,2,32,177,43,224,206,14,131,246,48,249,94,127,7,251,70,93,171,245,105,17,103,165,83,174,230,28,67,119,212,96,223,71,175]},"txindex":"72","amount":"100000000000000000","payload":{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,48,107,184,8,28,125,211,86,234,149,23,149,206,64,114,230,228,191,220,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,70,73,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"phash":{"type":"Buffer","data":[142,254,40,115,211,135,90,202,17,62,125,163,224,23,130,28,63,59,208,186,179,222,255,6,231,200,18,238,160,196,173,131]},"to":{"type":"Buffer","data":[65,101,54,53,98,48,102,54,55,54,51,49,51,70,100,55,49,53,70,50,57,68,48,55,53,51,56,100,49,100,99,56,53,53,55,102,50,98,49,65]},"nonce":{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,10,248,236,171,22]},"nhash":{"type":"Buffer","data":[247,103,160,200,134,71,231,219,193,108,185,172,187,34,189,129,195,195,179,192,171,106,20,54,186,126,8,28,219,218,138,108]},"gpubkey":{"type":"Buffer","data":[3,137,39,69,120,0,147,28,125,68,220,148,203,0,33,180,232,129,249,147,92,18,52,220,146,244,86,227,46,192,25,213,215]},"ghash":{"type":"Buffer","data":[59,239,159,117,33,60,69,0,234,137,24,232,130,85,219,224,99,252,193,34,214,156,79,146,254,35,220,81,185,227,53,167]}},"out":{"revert":{"type":"Buffer","data":[105,110,115,117,102,102,105,99,105,101,110,116,32,97,109,111,117,110,116,58,32,101,120,112,101,99,116,101,100,32,49,44,32,103,111,116,32,48]}}},"token":"0xD5147bc8e386d91Cc5DBE72099DAC6C9b99276F5"}

Docs and installation review

  • Tutorials reviewed
  • Typedocs reviewed, all snippets up to date
  • @renproject/ren and @renproject/gateway installed and tested with various node and browser versions

GatewayJS shows white screen

This may be caused by a cached index.html trying to load old JS bundles, or may be caused by incompatible GatewayJS and gateway.renproject.io versions.

Error: error code: 1016

why recv Error: error code: 1016 when call await renVM.queryBlockState(asset, 5) . provider is https://rpc-testnet.renproject.io.
code:

import { providers, Wallet } from "ethers";

import { Bitcoin, Ethereum } from "@renproject/chains";
import { RenJS } from "@renproject/ren";

// Test account - do not send real funds.
const mnemonic =
    "black magic humor turtle symptom liar salmon rally hurt concert tower run";
const network = "testnet";

const main = async () => {
    // Initialize Bitcoin and Ethereum.
    const bitcoin = new Bitcoin({ network });
    const ethereum = new Ethereum({
        network,
        provider: new providers.JsonRpcProvider(
            Ethereum.configMap[network].config.rpcUrls[0]
        ),
        signer: Wallet.fromMnemonic(mnemonic),
    });

    // Create RenJS instance. NOTE - chains must now be linked to RenJS using
    // `withChains`.
    const renJS = new RenJS(network).withChains(bitcoin, ethereum);
//    console.log('renJS', renJS)

    // Create gateway - mints and burns are both initialized with `gateway`.
    // Gateway parameters are serializable.
    const gateway = await renJS.gateway({
        asset: bitcoin.assets.BTC, // "BTC"
        from: bitcoin.GatewayAddress(),
        to: ethereum.Account(),
    });

    // `gateway.fees` exposes values and helpers for calculating fees.
    console.log(gateway.fees);

    console.log(`Deposit ${gateway.params.asset} to ${gateway.gatewayAddress}`);

    // NOTE: Event has been renamed from "deposit" to "transaction".
    gateway.on("transaction", (tx) => {
        (async () => {
            // GatewayTransaction parameters are serializable. To re-create
            // the transaction, call `renJS.gatewayTransaction`.
            console.log(tx.params);

            // Wait for remaining confirmations for input transaction.
            await tx.in.wait();

            // RenVM transaction also follows the submit/wait pattern.
            await tx.renVM.submit().on("progress", console.log);
            await tx.renVM.wait();

            // `submit` accepts a `txConfig` parameter for overriding
            // transaction config.
            await tx.out.submit({
                txConfig: {
                    gasLimit: 1000000,
                },
            });
            await tx.out.wait();

            // All transactions return a `ChainTransaction` object in the
            // progress, with a `txid` field (base64) and a `txidFormatted`
            // field (chain-dependent).
            const outTx = tx.out.progress.transaction;
            console.log("Done:", outTx.txidFormatted);

            // All chain classes expose a common set of helper functions (see
            // `Chain` class.)
            console.log(tx.toChain.transactionExplorerLink(outTx));
        })().catch(console.error);
    });
};

main().catch((error) => {
    console.error(error);
    process.exit(1);
});

Also, is there a Discord channel to discuss encountered issues?

@renproject/gateway imports old darknode-sol contracts

I tried doing the tutorial on the website but I kept getting this error message.

./node_modules/@renproject/contracts/dist/src/networks/devnet.js Cannot find file: 'RenBCH.json' does not match the corresponding name on disk: './node_modules/darknode-sol/build/devnet/renBCH.json'.

I tried cloning your repo for the tutorial but faced the same error. Tried doing a clean install with create-react-app, npm install @renproject/gateway and just importing the package in a file would throw the same error.

It seems like the library depends on an old version of https://github.com/renproject/darknode-sol/ and 3 contracts are downcased when they should be uppercased.

  • renZEC.json
  • renBCH.json
  • renBTC.json

Uppercasing them fixed the issue for me.

Validate GatewayJS and RenJS inputs

GatewayJS and RenJS should validate the inputs being passed to them in order to provide immediate feedback to developers, especially when breaking changes are made to the interface.

Parameters are typed using TypeScript, but this is a compile-time check only. However, there may be tools for validating values at runtime based on the TypeScript types. Otherwise, there are popular packages for value type validation including ow and is.

Metamask RPC error while burning $testBTC on Kovan

Error - The $testBTC mint is working fine but for some reason, the burn function mentioned in the RenJS docs is giving a
MetaMask - RPC Error: The execution failed due to an exception. {code: -32016, message: 'The execution failed due to an exception.', data: 'Reverted'}. error.

Steps to reproduce - After connecting to wallet, simply calling the renJS.burnAndRelease gives the error. Here's the code for it.

  const burn = async()=>{
    const {ethereum} = window;
    try{
      if(ethereum){
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const signer = provider.getSigner();
        const address = await signer.getAddress();
        const ethereumProvider = { provider, signer };

        const value = 0.001* 1e18;

        const renJS = new RenJS("testnet",{
          useV2TransactionFormat: true,
        });
        const burnAndRelease = await renJS.burnAndRelease({
          asset: "BTC",
          to: Bitcoin().Address("0xtb1q8kfmt02l5q5fgpuuhc2prygm8jwsava9kd620l"),
          from: Ethereum(ethereumProvider).Account({ value }),
        });

        let confirmations = 0;
        await burnAndRelease
          .burn()
          .on("confirmation", (confs) => {
            confirmations = confs;
          })
          .on("transactionHash", (txHash) => console.log(`txHash: ${String(txHash)}`));

          await burnAndRelease
          .release()
          .on("status", (status) =>
            status === "confirming"
              ? console.log(`${status} (${confirmations}/15)`)
              : console.log(status)
          )
          .on("txHash", console.log);

          console.log(`Withdrew ${value} BTC to ${"tb1q8kfmt02l5q5fgpuuhc2prygm8jwsava9kd620l"}.`);
      }
    }catch(e){
      console.log(e);
    }
  }

I've tried to omit the useV2TransactionFormat format as well, but it throws the same error.

GatewayJS mobile support

GatewayJS seems to be incompatible with some mobile wallets at the moment. May be caused by being inside an iFrame.

For compatible wallets, the UI should be more responsive.

View past GatewayJS transfers at gateway.renproject.io

GatewayJS stores past and current transfers in its storage (see #44), but they aren't accessible to the user.

If gateway.renproject.io is visited directly, rather than through an iFrame, it currently shows an empty page with a link to the GatewayJS repository. Instead, it could be used for one of the following:

  1. Showing past and current transfers
  2. Show additional settings and configurations
  3. Show a status page for GatewayJS
  4. Show statistics about GatewayJS

Handle users changing account in Web3 wallet

If the user has changed their account mid-transfer, GatewayJS currently throws an error when it tries to submit to Ethereum.

Two solutions include:

  1. Sign the Ethereum transaction with whatever account they have unlocked at the time.
  2. Warn the user to change their address back, or restart if necessary - not user friendly but is more predictable for the integrator, which may have calculated the transaction parameters based on the previously logged-in account.

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.