Giter Site home page Giter Site logo

round-rb's Introduction

round-rb: A Ruby client for the Gem API

The round client is designed to interact with Gem's API to make building blockchain apps drop dead simple. All the complexity of the bitcoin protocol and crypto has been abstracted away so you can focus on building your product. Here are a few of the many great things the API and clients provide:

  • Multi-signature wallets with Gem as a cosigner
  • Webhook notifications automatically subscribed for you
  • Integrated 2FA solution with arbitrary endpoints to build into your app
  • Simplified balance inqueries
  • Easy address management
  • Hardware Security Modules for co-signing key
  • Rules engine for transactions
  • SDKs for many popular languages

Support information

Installing round-rb:

Prerequisites:

  • Ruby 2.1.5

Getting Started Tutorial

Table of Contents

Introduction

This tutorial will have you run through setting up your application and creating your own wallet as a user of your application. By the end of the tutorial, you will have created your User, wallet, account, an address as well as fund it and then make a payment using the bitcoin testnet network.

This tutorial assumes that you have completed the developer signup and that you have successfully installed the client

1. Run the Client

In this step you will learn how to instantiate the API client for the given networks.

  1. start your favorite interactive shell and import the round library

    $ irb
    > require 'round'
  2. Create the client object.

client = Round.client ```

[top]

2. Configure your application and API Token

In this step your application and you will retrieve the API Token for the application and set your applications redirect url. The url is used to push the user back to your app after they complete an out of band challenge.

  1. In the console copy your api_token by clicking on show

  2. Go back to your shell session and set a variable for api_token

    api_token = 'q234t09ergoasgr-9_qt4098qjergjia-asdf2490'

[top]

3. Create your User and Wallet

In this step you will create your own personal Gem user and wallet authorized on your application. This is an end-user account, which will have a 2-of-3 multisig bitcoin wallet.

  1. Authenticate your client

    client.authenticate_identify(api_token: api_token)
  2. Create your user and wallet:

    #  Store the device token for future authentication
    device_token = client.users.create(
                      first_name: 'YOUR FIRST NAME',
                      last_name: 'YOUR LAST NAME',
                      email: 'YOUR EMAIL ADDRESS',
                      passphrase: 'aReallyStrongPassphrase',
                      device_name: 'SOME DEVICE NAME',
                      redirect_uri: 'http://something.com/user-device-approved')
                    )
  3. Your application should store the device_token permanently as this will be required to authenticate from your app as this user.

  4. You (acting as a user) will receive an email from Gem asking you to confirm your account and finish setup. Please follow the instructions. At the end of the User sign up flow, you'll be redirected to the redirect_uri provided in users.create (if you provided one).

[top]

4. Authenticate your User

In this step you will learn how to authenticate to the Gem API on a User's device to get a fully functional User object with which to perform wallet actions.

  1. Call the authenticate_device method from the client object

    full_user = client.authenticate_device(
                api_token: api_token,
                device_token: device_token,
                email: email
              )

[top]

5. Access the wallet and Default Account

Wallets and Accounts

  1. Get the wallet and then default account

    my_account = full_user.wallet.accounts['default']

[top]

6. Generate an Address and Add Funds

In this section you'll learn how to create an address to fund with testnet coins aka funny money.

  1. Create an address

    address = my_account.addresses.create
    puts address.string

puts address.path ```

  1. Copy the address string and go to a faucet to fund it:
    1. TP's TestNet Faucet
    2. Mojocoin Testnet3 Faucet

Payments have to be confirmed by the network and on Testnet that can be slow. To monitor for confirmations: input the address into the following url https://live.blockcypher.com/btc-testnet/address/<YOUR ADDRESS>. The current standard number of confirmations for a transaction to be considered safe is 6.

You will be able to make a payment on a single confirmation. While you wait for that to happen, feel free to read more details about: Wallets and Accounts

[top]

7. Make a Payment

In this section you’ll learn how to create a payment a multi-signature payment in an HD wallet. Once your address gets one more more confirmations we’ll be able to send a payment out of the wallet. To make a payment, you'll unlock a wallet, generate a list of payees and then call the pay method.

  1. Unlock the wallet:

    my_account.wallet.unlock(<YOUR PASSWORD>)
  2. Make a payment

    transaction = my_account.pay([{address: 'mxzdT4ShBudVtZbMqPMh9NVM3CS56Fp11s', amount: 25000}], 1, 'http://some-redirect-uri.com/')

puts transaction.mfa_uri # redirect your user to this URI to complete payment! ```

The pay call takes a list of payee objects. A payee is a hash of {address: ADDRESS, amount: amount} where address is the bitcoin address and amount is the number of satoshis. utxo_confirmations default to 6 and represents the number of confirmations an unspent output needs to have in order to be selected for the transaction.
The last argument is the redirect uri for Gem to send the user back to your application after the user submits their MFA challenge.

CONGRATS - now build something cool.

[top]

round-rb's People

Contributors

automatthew avatar bezreyhan avatar dllaurence avatar jvergeldedios avatar ksylvest avatar kyledcline avatar semaj avatar thedoctor avatar

Stargazers

 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  avatar  avatar

round-rb's Issues

Method to derive an address from a script

This probably exists in bitcoin-ruby already, but it needs to be in the Output wrapper, too. This came up in the Blockr class when creating Transactions from raw payloads. Outputs are not getting the address variable set.

Build failing on Patchboard::API reference

When attempting to deploy to production (Heroku), the assets:precompile task is failing on line 117 of patchboard/api.rb. I am working on troubleshooting, but if this is a known issue or some recent changes affect this, please let me know.

----> Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       rake aborted!
       NoMethodError: undefined method `[]' for nil:NilClass
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/patchboard-0.5.1/lib/patchboard/api.rb:117:in `generate_url'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/patchboard-0.5.1/lib/patchboard/endpoints.rb:22:in `block (2 levels) in initialize'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/cache/round-rb-00c689f00262/lib/round/client.rb:67:in `application'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/cache/round-rb-00c689f00262/lib/round/client.rb:27:in `authenticate_application'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/config/initializers/round.rb:4:in `<top (required)>'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:241:in `load'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:241:in `block in load'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:232:in `load_dependency'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:241:in `load'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/engine.rb:648:in `block in load_config_initializer'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/notifications.rb:161:in `instrument'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/engine.rb:647:in `load_config_initializer'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/engine.rb:612:in `block (2 levels) in <class:Engine>'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/engine.rb:611:in `each'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/engine.rb:611:in `block in <class:Engine>'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/initializable.rb:30:in `instance_exec'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/initializable.rb:30:in `run'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/initializable.rb:55:in `block in run_initializers'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/initializable.rb:44:in `each'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/initializable.rb:44:in `tsort_each_child'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/initializable.rb:54:in `run_initializers'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/application.rb:300:in `initialize!'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/railtie.rb:194:in `public_send'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/railtie.rb:194:in `method_missing'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/config/environment.rb:5:in `<top (required)>'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:247:in `require'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:247:in `block in require'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:232:in `load_dependency'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.7/lib/active_support/dependencies.rb:247:in `require'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/application.rb:276:in `require_environment!'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/railties-4.1.7/lib/rails/application.rb:389:in `block in run_tasks_blocks'
       /tmp/build_a9865187baa4eef87a2da6dc5c3687ac/vendor/bundle/ruby/2.1.0/gems/sprockets-rails-2.2.0/lib/sprockets/rails/task.rb:64:in `block (2 levels) in define'
       Tasks: TOP => environment

[FR] Ability to request a fee for a hypothetical transaction

In order to estimate a transaction's total, it would be useful to be able to submit a hypothetical payment and have returned Gem's calculated fee. This hypothetical response can have any expectation of an expiration, such as 5 or 10 seconds.

Code example

# attempt mock payment
@mock_payment = account.mock_pay payees
# OR
@mock_payment = account.pay payees, mock: true

  => { "type" => "outgoing",
       "key" => nil,
       "data" => {
         "value" => 100000,
         "fee" => 11337,
         ...
       }
     }

# what kind of fee should I tell the user to expect?
@mock_payment[:data][:fee]
  => 11337

`check_string': Public key was 32 bytes (Expected 13809239872994541600) (RbNaCl::LengthError)

Ruby version : 2.2.0
Rails version : 4.2.4
Gemfile i added : gem 'round'
then i did bundle install
up to now everything working fine. when i start rails server (bundle exec rails s) i am getting error like this

/home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:77: warning: already initialized constant RbNaCl::Box /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:77: warning: previous definition of Box was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:78: warning: already initialized constant RbNaCl::PrivateKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:78: warning: previous definition of PrivateKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:79: warning: already initialized constant RbNaCl::PublicKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:79: warning: previous definition of PublicKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:80: warning: already initialized constant RbNaCl::SecretBox /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:80: warning: previous definition of SecretBox was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:81: warning: already initialized constant RbNaCl::SigningKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:81: warning: previous definition of SigningKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:82: warning: already initialized constant RbNaCl::VerifyKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:82: warning: previous definition of VerifyKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:83: warning: already initialized constant RbNaCl::GroupElement /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:83: warning: previous definition of GroupElement was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:84: warning: already initialized constant RbNaCl::OneTimeAuth /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:84: warning: previous definition of OneTimeAuth was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-libsodium-1.0.3/lib/rbnacl/libsodium.rb:7: warning: already initialized constant RBNACL_LIBSODIUM_GEM_LIB_PATH /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-libsodium-1.0.3/lib/rbnacl/libsodium.rb:7: warning: previous definition of RBNACL_LIBSODIUM_GEM_LIB_PATH was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:77: warning: already initialized constant RbNaCl::Box /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:77: warning: previous definition of Box was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:78: warning: already initialized constant RbNaCl::PrivateKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:78: warning: previous definition of PrivateKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:79: warning: already initialized constant RbNaCl::PublicKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:79: warning: previous definition of PublicKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:80: warning: already initialized constant RbNaCl::SecretBox /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:80: warning: previous definition of SecretBox was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:81: warning: already initialized constant RbNaCl::SigningKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:81: warning: previous definition of SigningKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:82: warning: already initialized constant RbNaCl::VerifyKey /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:82: warning: previous definition of VerifyKey was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:83: warning: already initialized constant RbNaCl::GroupElement /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:83: warning: previous definition of GroupElement was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:84: warning: already initialized constant RbNaCl::OneTimeAuth /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:84: warning: previous definition of OneTimeAuth was here /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl/util.rb:93:incheck_string': Public key was 32 bytes (Expected 13809239872994541600) (RbNaCl::LengthError)
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl/boxes/curve25519xsalsa20poly1305/public_key.rb:28:in initialize' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl/self_test.rb:16:innew'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl/self_test.rb:16:in box_test' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl/self_test.rb:133:in<top (required)>'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:in require' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:inblock in require'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:240:in load_dependency' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:inrequire'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-3.1.2/lib/rbnacl.rb:91:in <top (required)>' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:inrequire'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:in block in require' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:240:inload_dependency'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:274:in require' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/rbnacl-libsodium-1.0.3/lib/rbnacl/libsodium.rb:11:in<top (required)>'
from /home/moorem/.rvm/gems/ruby-2.2.0@global/gems/bundler-1.8.3/lib/bundler/runtime.rb:85:in require' from /home/moorem/.rvm/gems/ruby-2.2.0@global/gems/bundler-1.8.3/lib/bundler/runtime.rb:85:inrescue in block in require'
from /home/moorem/.rvm/gems/ruby-2.2.0@global/gems/bundler-1.8.3/lib/bundler/runtime.rb:68:in block in require' from /home/moorem/.rvm/gems/ruby-2.2.0@global/gems/bundler-1.8.3/lib/bundler/runtime.rb:61:ineach'
from /home/moorem/.rvm/gems/ruby-2.2.0@global/gems/bundler-1.8.3/lib/bundler/runtime.rb:61:in require' from /home/moorem/.rvm/gems/ruby-2.2.0@global/gems/bundler-1.8.3/lib/bundler.rb:134:inrequire'
from /home/moorem/Documents/koin/config/application.rb:7:in <top (required)>' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:78:inrequire'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:78:in block in server' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:75:intap'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:75:in server' from /home/moorem/.rvm/gems/ruby-2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:39:inrun_command!'
from /home/moorem/.rvm/gems/ruby-2.2.0/gems/railties-4.2.4/lib/rails/commands.rb:17:in <top (required)>' from bin/rails:4:inrequire'
from bin/rails:4:in <main>'

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.