Code Monkey home page Code Monkey logo

waffle's Introduction

CI Discord

Ethereum Waffle

The most advanced framework for testing smart contracts.

Sweeter, simpler and faster.

Links

Philosophy

  • Simpler: Minimalistic, few dependencies.
  • Sweeter: Nice syntax, easy to extend.
  • Faster: Strong focus on the speed of test execution.

Features:

  • Sweet set of chai matchers, e.g.:
    • expect(...).to.be.revertedWith('Error message')
    • expect(...).to.emit(contract, 'EventName').withArgs(...))
  • Importing contracts from npm modules working out of the box, e.g.:
    • import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
  • Fixtures that help write fast and maintainable test suites, e.g.:
    • const {token} = await loadFixture(standardTokenWithBalance);
  • Customizable compilation options with native solc, dockerized solc and any version of solc-js loaded remotely at compiled time
  • Mocking smart contracts, e.g.:
    • const mockToken = await deployMockContract(wallet, IERC20.abi);
  • Support for promise-based configuration, e.g.:
    • use native solc binary for fast compilation in CI environment
    • use solc-js based on contract versions detected (async)
  • Support for TypeScript
  • Type-safe contract deployment and interactions with TypeChain
  • Documentation

Documentation

Documentation is available here.

Installation:

To get started install ethereum-waffle with yarn:

yarn add --dev ethereum-waffle

Or if you prefer using npm:

npm install --save-dev ethereum-waffle

Step by step guide

Add external dependency:

To add an external library install it using npm:

npm install @openzeppelin/contracts -D

or with yarn:

yarn add @openzeppelin/contracts -D

Note

Find this example in examples/basic and use it.

Example contract

Below is an example contract written in Solidity. Place it in contracts/BasicToken.sol file of your project:

pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// Example class - a mock class using delivering from ERC20
contract BasicToken is ERC20 {
    constructor(uint256 initialBalance) ERC20("Basic", "BSC") public {
        _mint(msg.sender, initialBalance);
    }
}

Example test

Below is an example test written for the contract above compiled with Waffle. Place it under test/BasicToken.test.ts file in your project directory:

import {expect, use} from 'chai';
import {Contract} from 'ethers';
import {deployContract, MockProvider, solidity} from 'ethereum-waffle';
import BasicToken from '../build/BasicToken.json';

use(solidity);

describe('BasicToken', () => {
  const [wallet, walletTo] = new MockProvider().getWallets();
  let token: Contract;

  beforeEach(async () => {
    token = await deployContract(wallet, BasicToken, [1000]);
  });

  it('Assigns initial balance', async () => {
    expect(await token.balanceOf(wallet.address)).to.equal(1000);
  });

  it('Transfer adds amount to destination account', async () => {
    await token.transfer(walletTo.address, 7);
    expect(await token.balanceOf(walletTo.address)).to.equal(7);
  });

  it('Transfer emits event', async () => {
    await expect(token.transfer(walletTo.address, 7))
      .to.emit(token, 'Transfer')
      .withArgs(wallet.address, walletTo.address, 7);
  });

  it('Can not transfer above the amount', async () => {
    await expect(token.transfer(walletTo.address, 1007)).to.be.reverted;
  });

  it('Can not transfer from empty account', async () => {
    const tokenFromOtherWallet = token.connect(walletTo);
    await expect(tokenFromOtherWallet.transfer(wallet.address, 1))
      .to.be.reverted;
  });

  it('Calls totalSupply on BasicToken contract', async () => {
    await token.totalSupply();
    expect('totalSupply').to.be.calledOnContract(token);
  });

  it('Calls balanceOf with sender address on BasicToken contract', async () => {
    await token.balanceOf(wallet.address);
    expect('balanceOf').to.be.calledOnContractWith(token, [wallet.address]);
  });
});

Note: You will also need to install the following dependencies to run the example above:

yarn add mocha -D
yarn add chai -D

Or with npm:

npm i chai -D
npm i mocha -D

Compiling

To compile your smart contracts run:

npx waffle

To compile using a custom configuration file run:

npx waffle config.json

Example configuration file looks like this (all fields optional):

{
  "sourceDirectory": "./custom_contracts",
  "outputDirectory": "./custom_build",
  "nodeModulesDirectory": "./custom_node_modules"
}

To enable generation of typechain artifacts:

{
  "typechainEnabled": true
}

Flattener

To flat your smart contracts run:

npx waffle flatten

In configuration file you can add optional field with path to flatten files:

{
  "flattenOutputDirectory": "./custom_flatten"
}

Running tests

To run the tests run the following command:

npx mocha

Adding an npm script

For convenience, you can add the following to your package.json:

{
  ...,
  "scripts": {
    "test": "waffle && mocha"
  }
}

Now you can build and test your contracts with one command:

npm test

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 and linters pass. Make sure you have test coverage for any new features.

Running tests

Note: To make end-to-end test pass, you need to:

  • have Docker installed, up and running
  • have Ethereum stable docker image pulled, if not run docker pull ethereum/solc:stable
  • have native solidity 0.5.* installed

To run tests type:

yarn test

To run linter type:

yarn lint

Building documentation

Install Sphinx to build documentation:

cd docs
make html

Before building documentation for the first time you may have to install required python packages:

pip3 install -r docs/requirements.txt

Roadmap

See #155

License

Waffle is released under the MIT License.

waffle's People

Contributors

alcuadrado avatar alxiong avatar asselstine avatar cehali avatar dependabot[bot] avatar duckception avatar github-actions[bot] avatar jakvbs avatar jozwiaczek avatar justynabroniszewska avatar krzkaczor avatar malyfko avatar marekkirejczyk avatar miksujak avatar mj426382 avatar nezouse avatar p-sad avatar paulrberg avatar pawelpolak2 avatar rzadp avatar snario avatar sz-piotr avatar truefi-bot avatar truefibot avatar vanruch avatar vladstarostenko avatar wachulski avatar yaram avatar yivlad avatar zgorizzo69 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

waffle's Issues

Events: Support indexed array attributes in tests

The current to.emit().withArgs() matcher does not support indexed (variable length) arrays. Indexed attributes must be fixed size (32 bytes), so solidity stores a hash of the array content. This is not checked for and the comparison in a test fails.

Example contract:

pragma solidity ^0.5.4;

contract Test {
  event Event (bytes a, bytes indexed b);

  function emitEvent() public {
    bytes memory test = abi.encode(0x01);
    emit Event(test, test);
  }
}

Example test case:

import chai from 'chai';
import { createMockProvider, deployContract, getWallets, solidity } from 'ethereum-waffle';

import Test from '../build/Test';

chai.use(solidity);
const { expect } = chai;

describe('Test', () => {
  const provider = createMockProvider();
  const [wallet] = getWallets(provider);

  const packed = '0x0000000000000000000000000000000000000000000000000000000000000001';

  it('Should emit', async () => {
    const t = await deployContract(wallet, Test, []);

    await expect(t.emitEvent())
      .to.emit(t, 'Event')
      .withArgs(packed, packed);
  });
});

The actualArgs.values in matchers.ts line 113 contains the following:

     '0': '0x0000000000000000000000000000000000000000000000000000000000000001',
     '1':
      _Indexed {
        hash: '0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6' },

Which is not unexpected, but the comparison fails for the indexed argument. When passing an object in the test (i.e. { hash: '0xb10...' } instead of the 0x0..01 string), the comparison fails as well.

It would be great to have a simple way to test indexed attributes.

Make waffle work with solc >0.5.1

Updating solc produces an error during tests:

Uncaught error outside test suite:
     Uncaught Error: the string "abort(\"Random reason\"). Build with -s ASSERTIONS=1 for more info." was thrown, throw an Error :)

import takes a long time

We are trying to use a non-waffle test framework (jest) and just importing the matchers from ethereum-waffle. It turns out that the import of ethereum-waffle takes about 20 seconds to run.

import { solidity } from "ethereum-waffle";

Is this expected?

New matcher: expect to change balance

Example usage:

expect(() => wallet.transfer('0xab..cd.', 200)).to.changeBalance('0xb', 200)

chained:

expect(() => wallet.transfer('0xab...cd', 200))
  .to.changeBalance('0xab...cd', -200)  
  .to.changeBalance('0xab...cd', 200);

token:

expect(() => token.transfer('0xb', 200))
  .to.changeTokenBalance(someToken, '0xb', 200);

Linking does not work on beta 2

Seems like the way that links are referenced has changed to look like this:

build/AppRegistry.json:

  610     "assembly": "    /* \"contracts/AppRegistry.sol\":358:525  contract AppRegistry
  611     "bytecode": {
  612       "linkReferences": {
  613         "contracts/libs/LibStaticCall.sol": {
  614           "LibStaticCall": [
  615             {
  616               "length": 20,
  617               "start": 5221
  618             },
  619             {
  620               "length": 20,
  621               "start": 6257
  622             },
  623             {
  624               "length": 20,
  625               "start": 6733
  626             },
  627             {
  628               "length": 20,
  629               "start": 7119
  630             }
  631           ]
  632         }
  633       },
  634       "object": "608060405234801561001057600080fd5b50614089806100206 ...
  635   

So this logic doesn't work anymore I don't think:

https://github.com/EthWorks/Waffle/blob/111bccd2740d06d4a74ef1e005853b18a927f1e3/lib/waffle.js#L41-L44

Haven't fully tested yet.

Hard to tell what's in each new version

Could you use GitHub releases or perhaps keep a single branch which only gets updated on version bumps with all the included changesets? Every time there is an update it just takes a little while to review each new PR

Pass additional compiler options

I need an option in the config to pass additional compiler settings. Particularly I'd like to set evmVersion to compile for constantinople.

Import from Node Modules does not work on Windows

Hello all !

I just saw this package and I wanted to try it out for writing and testing some contracts.

Because of work reglementations I am forced to develop on a Windows 10 OS. Everything went well until I tried to import the ERC721 from the Open Zepplin Package.

import "openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";

When doing so I got this error :

Error: Invalid URI "c:/Users/tchataignerprestatai/WebstormProjects/artikodin-contracts/node_modules/openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol"

After looking from where it might come from I found that this test is going on in the package
if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) (from request package, request.js line 260)
and its where the code is returning.

Any idea of how to solve that ?

Use with typechain

I am trying to use Waffle with Typechain. I get the following error when I try to cast a Waffle deployed contract as a generated type:

My relevant test code:

import { deployContract, getWallets, solidity } from "ethereum-waffle";

describe("Sparkle contract", function() {
  beforeEach(async () => {
    trojanToken = await deployContract(wallet, TrojanTokenArtifact); // type error here
    await trojanToken.functions.setTrojanPool(trojanPool.address);
  })
  ...
})

Type error

Type 'Contract' is not assignable to type 'TrojanToken'.
  Types of property 'interface' are incompatible.
    Type 'Interface' is not assignable to type 'TrojanTokenInterface'.
      Types of property 'functions' are incompatible.
        Type '{ [name: string]: _FunctionDescription; }' is missing the following properties from type '{ approve: TypedFunctionDescription<{ encode([spender, value]: [string, BigNumberish]): string; }>; transferFrom: TypedFunctionDescription<{ encode([from, to, value]: [string, string, BigNumberish]): string; }>; ... 5 more ...; sellSparkle: TypedFunctionDescription<...>; }': approve, transferFrom, increaseAllowance, decreaseAllowance, and 4 more

If I ts-ignore the line, everything works properly

Export LinkableContract type

Currently LinkableContract type is not exported, yet the link() function requires the type of the first parameter to be of that type.

`to.be.reverted` does not work

I followed the exact syntax in the docs and receive this output.

AssertionError: Expected transaction to be reverted
Expected :not reverted
Actual   :reverted

It seems backwards. I can confirm that the tx was reverted. Other expect properties like to.emit seems to work. There is no difference in how I call them. I'm using a Web3Provider connected to an external ganache-cli client.

Solc version

SyntaxError: Source file requires different compiler version (current compiler is 0.4.18+commit.9cf6e910.Emscripten.clang - note that nightly builds are considered to be strictly less than the released version while the current Solidity version is 0.4.24

npx waffle fails

Hey there, thanks for working on this, it looks great!

I'm attempting to try it out, but get errors when running npx waffle to compile. I installed waffle and ethers using NPM, and got the following error:

Cannot find module 'ganache-core'

After running npm install ganache-core, I now get the following error:

Waffle.compile is not a function

Any pointers?

Add dynamic Smart Contracts mocking

Motivation:

Common pattern in Smart Contract testing is to create mock smart contracts for testing.
Examples can be found here:

Outside of the blockchain space it is common to use dynamic mocking instead. This reduces amount of code and allow to avoid context switching when writing and reading tests.

An example:

import { createMock } from '@ethereum-waffle/mock';

// setup mock

const tokenMock = await createMock(wallet, [
  'function balanceOf(address holder) view returns (uint)',
]);
const someContract = someContractFactory.deploy(tokenMock.address);

// test against it

it('reverts if no balance', async () => {
  await tokenMock.balanceOf.returns('0'); 
  await expect(someContract.check()).to.be.revertedWith('Not enough balance');
});

it('returns true if positive balance', async () => {
  await tokenMock.balanceOf.returns('1000000')); 
  expect(await someContract.check()).to.equal(true);
});

Related feature: #229

The code will be based on Doppelganger, but will come with a range of novel features and improvements. The result should also include better documentation and tutorial.

Peer dependency

Ethersjs should be a peer dependency to allow the user to select its version.

Web3 beta 38 causes the package to stop working

Caused by:

web3/web3.js#2221

Recommended solution for Waffle:

Blacklist web3 beta38 version or just wait for web3 to fix the issue. It is not Waffle's fault.

Recommended solution for users:

Use yarn and add this to package.json

"resolutions": {
  "web3": "1.0.0-beta.37"
}

Feature request: loadConfig as a promise

Pain point:
I'm trying to assign different waffle config in a waffle.js on the fly based on the build environment, counterfactual/monorepo#1211
Yet it would be an async operation, thus just by running waffle waffle.js would only gives me default settings.

What it look like in my waffle.js:

module.exports = new Promise((resolve, reject) => {
  getConfig((result) => {
    resolve(result);
  });
});

Documentation feedback

More info on functions:
e.g. deploy a contract:
token = await deployContract(wallet, BasicTokenMock, [wallet.address, 1000]);
What each argument is doing?

More step-by-step intro:

  • link to install npm/yarn
  • create folder

Adding ABIEncoderV2 pragma to source code returns exit 1 and does not build contracts

I tried compiling on the latest version (2 beta 0) the following:

pragma solidity 0.5;

contract Test {

  enum ActionTypes { INCREMENT, DECREMENT}

  struct Action {
    ActionTypes actionType;
    uint256 byHowMuch;
  }

  struct AppState {
    address player1;
    address player2;
    uint256 count;
    uint256 turnNum;
  }
}

and it worked however when adding

pragma experimental ABIEncoderV2;

No build files are created.

This warning is emitted, which is expected:

contracts/Test.sol:3:1: Warning: Experimental features are turned on. Do not use experimental features on live deployments.
pragma experimental ABIEncoderV2;
^-------------------------------^

Contents of build folder

What should be inside the build folder after npx waffle?

In mine is only a myContract.json file. Because of that the import in the test script importing './build/myContract' fails.

What do I do wrong?

Library functions blow up

Hi all,

I am trying to build a library with functions (i.e. using "self") to use in a main contract. An example is here:
https://pastebin.com/haz3jMrr

The issue is that, when I write the main contract and uncomment either two lines of code :

`function testme(PaddyInfo.PaddyAppointment memory _appointment) public payable returns (bool) {

    // It breaks when we uncomment the below functions
    // bytes32 appointmentid = _appointment.computeAppointmentID();
    // bytes32 sigHash = _appointment.computeEncodedAppointmentHash();

    return true;
}`

Then when I try to compile everything ('npx waffle'), the compiler just blows up.
Screen Shot 2019-10-19 at 06 41 19

It looks like it is not compiling correctly and I'm not totally sure what to do. Is this a problem with waffle? It should be easy for someone else to quickly test the above (i.e. uncomment the code) and just see if waffle builds it.

mocha to npx mocha

Hi,

In the README.md, it should read "npx mocha" and not mocha.

I was going through it today, and "mocha" alone doesnt work.

expect().to.emit.withArgs fails when 2 contract trigger the same event

I have a case where a transaction triggers the same event on 2 contracts with different arg:

โ†’ contractA emits SuccessCall(1)
โ†’ contractB emits SuccessCall(0)

If I try

expect(...).to.emit(contractA, "SuccessCall").withArgs(1).emit(contractB, "SuccessCall").withArgs(0)

I get an error (from arguments for the call). It looks like the filtering on contract address in not done properly ... I'm also wondering what would happen is a single contract was to emit the same event multiple times with different args.

Follow semantic versioning for releases

Given a version number MAJOR.MINOR.PATCH, increment the:

MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backwards-compatible manner, and
PATCH version when you make backwards-compatible bug fixes.

https://semver.org/

Currently PATCH is updated when new features are added (instead of MINOR).

deployContract returns null when using Infura nodes

My code works fine with my local dev, until I started to deploy contracts to Rinkeby with Infura nodes. And got errors like TypeError: Cannot read property 'contractAddress' of null.

I did a bit research, and found out that someone mentioned that it's because the light clients don't return the receipt for getTransactionReceipt call.

So, I'm wondering if the team knows that or we just have to run a full node to deploy contracts?

Normalize source paths for linking

When using waffle to build the contracts on windows, the hashed link references include backslashes. When linking later, the second argument to link also needs to use slashes, or the linking fails (hashes mismatch).

I'd suggest to normalize the slashes to forward slashes (unix) both when calling solc and when linking.

Better api

Using ethereumjs-vm

it('waffle test', () => {
  const chain = waffle.createTestChain()

  const wallet = chain.createWallet()
  const provider = chain.getProvider()

  chain.setTime(123123)

  chain.setBalance(wallet.address, utils.parseEther('1'))
})

Not working on Node v10

After running waffle in node v10.8.0 I get the following error

$ waffle
internal/modules/cjs/loader.js:718
  return process.dlopen(module, path.toNamespacedPath(filename));
info                 ^

 Visit Error: The module '/Users/ivan/WebstormProjects/node_modules/scrypt/build/Release/scrypt.node'
https://yarnpkg.com/en/docs/cli/runwas compiled against a different Node.js version using
 for documentation about this command.
NODE_MODULE_VERSION 59. This version of Node.js requires
NODE_MODULE_VERSION 64. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
    at Object.Module._extensions..node (internal/modules/cjs/loader.js:718:18)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (/Users/ivan/WebstormProjects/node_modules/scrypt/index.js:3:20)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
error Command failed with exit code 1.

Probably should rebuild the package on newer node version

Expect to emit event should take an already awaited transaction

Expect to emit event should take a transaction that has already been awaited.

Failing code:

const tx = await contract.someFunction()

await expect(tx).to.emit(contract, 'SomeEvent').withArgs(some, args)

Although this works:

const tx = await contract.someFunction()

await expect(Promise.resolve(tx)).to.emit(contract, 'SomeEvent').withArgs(some, args)

Scrypt.node makes still problems

When starting mocha I get:

module.js:681
  return process.dlopen(module, path._makeLong(filename));
                 ^
Error: The module '/home/jochen/WebstormProjects/blockchain/node_modules/ganache- 
core/node_modules/scrypt/build/Release/scrypt.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 64. This version of Node.js requires
NODE_MODULE_VERSION 57. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).

I am using node V10.14.2.

The only way to get rid of that problem was to rebuild scrypt with node 8.4.0.

Native compiler is unable to handle disallowed file paths in import statements

I think there is a flag you can pass to solc called --allow-paths that fixes an error I'm facing:

contracts/TicTacToeApp.sol:4:1: ParserError: Source "/Users/liam/src/code/monorepo/node_modules/@counterfactual/contracts/contracts/libs/Transfer.sol" not found: File outside of allowed directories.
import "@counterfactual/contracts/contracts/libs/Transfer.sol";
^-------------------------------------------------------------^

The string gets correctly mapped to place where the file is located, but solc rejects it.

Google results from the error: https://www.google.com/search?q=File+outside+of+allowed+directories.&rlz=1C5CHFA_enCA715CA715&oq=File+outside+of+allowed+directories.&aqs=chrome..69i57j69i64.438j0j1&sourceid=chrome&ie=UTF-8

Missing types for ganache-core

Using ethworks-waffle requires manual install of ganache-core types.

node_modules/ethereum-waffle/dist/waffle.d.ts:1:29 - error TS7016: Could not find a declaration file for module 'ganache-core'. '/home/travis/build/EthWorks/Doppelganger/node_modules/ethereum-waffle/node_modules/ganache-core/index.js' implicitly has an 'any' type.
  Try `npm install @types/ganache-core` if it exists or add a new declaration (.d.ts) file containing `declare module 'ganache-core';`
1 import { GanacheOpts } from 'ganache-core';

Swap arguments for fixture

Currently the signature for fixture is async function fixture (provider, wallets). Since the provider argument is very rarely used compared to wallets I propose a breaking change for the next version of waffle that swaps the arguments. The new signature would be:

async function fixture (wallets: Wallet[], provider: providers.Provider)

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.