Code Monkey home page Code Monkey logo

kotlin-walletconnect-lib's Introduction

kotlin-walletconnect-lib

library to use WalletConnect with Kotlin or Java

Add this library to your project

Add the @jitpack repository to your gradle file:

repositories {
	...
	maven { url 'https://jitpack.io' }
}

Add the dependency:

dependencies {
	implementation 'com.github.WalletConnect:kotlin-walletconnect-lib:version'
}

kotlin-walletconnect-lib's People

Contributors

ibrahimbhabay avatar ligi avatar pedrouid avatar rmeissner avatar sgc-code avatar talhaali00 avatar teresajiar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotlin-walletconnect-lib's Issues

Is ext.deps in buiild.gradle really needed?

I'm wondering what this block is needed for? I deleted it and the project builds anyways, and as an Android Dev I also don't know this, thankful about clarifications.

ext.deps = [
                android: [
                        'runtime'     : 'com.google.android:android:4.1.1.4',
                        'gradlePlugin': "com.android.tools.build:gradle:${versions.androidPlugin}",
                ]
        ]

Sign method does work

The sign method, for which there is any code in the sample or test doesn't work:

data class SignMessage(val id: Long, val address: String, val message: String) : MethodCall(id)

The strange think is, it works when you pass the address as message and the message string as address, and I'm not kidding :D tested it 100 times and couldn't figure it out yet.

Is the WalletConnect supporting the "Binance Smart Chain (BEP20)" wallets?

Hi,
I was checking Kotlin WalletConnect App and it is working fine for the Etherium ERC20.
But I need to know is this possible to use this App for the Binance Smart Chain (BEP20) transactions?
If Yes, can you please share the methods?

I checked the code and it shows the methods of eth_SendTransaction & eth_sign methods. But could not find anything related to Binance Smart Chain (BEP20).

Trust Wallet:
When I tried to use the WalletConnect Kotlin Sample App with Trust Wallet, it give me the "Wallet not supported" message.

MetaMask:
When I tried to use the WalletConnect Kotlin Sample App with MetaMask, it gives me no response. MetaMask opens but nothing happens over there.
In the case of Etherium, when I press the "Connect" button, it opens the MetaMask app and gives the prompt to connect the MetaMask Wallet to WalletConnect App, it works successfully and I get the "Connected" status on my WalletConnect App upon tapping the connect in MetaMask. Now when I tap over the "Send Sample Transaction" on the WalletConnect App, a new prompt appears in the MetaMask App with the requesting Transaction App to authorize. It works fine in the case of ETH, but not in the case of BEP20.

Didn't find class "org.bouncycastle.crypto.paddings.PKCS7Padding"

The following error occurs when I compile the JAR and run it on the simulator after another project is introduced

java.lang.NoClassDefFoundError: Failed resolution of: Lorg/bouncycastle/crypto/paddings/PKCS7Padding;
    at org.walletconnect.impls.MoshiPayloadAdapter.prepare(MoshiPayloadAdapter.kt:63)

the CustomRequest that method is wc_exchangeKey should filtered

the CustomRequest that method is wc_exchangeKey should filtered

detali:
Custom(id=1557730797842490, method=wc_exchangeKey, params=[{peerId=60dfeecf-e905-41d9-a002-2222895a1265, peerMeta={description=, url=https://testnet.binance.org, icons=[https://testnet.binance.org/favicon.png, https://testnet.binance.org/favicon.png], name=Binance - | Dex Trading | Decentralized Exchange | Binance.org}, nextKey=ec768644e1c09eae44e5ca31f893dd485e3c210a9c1625d3ec738f96b946d819}])

sell and buy problem

Why does the website always send a same message to the phone when the transaction is initiated?

Balance transfer will not.

Why?

Parsing issue with EncryptedPayload on release

Hi. I'm working on Flutter plug-in for iOS/Android. The problem is when I run release version the iv value in EncryptedPayload is null. I solved that by adding @JsonClass(generateAdapter = true) to model and kapt "com.squareup.moshi:moshi-kotlin-codegen:1.8.0" and injecting clone of MoshiPayloadAdapter. Possibly I'm doing something wrong but I spent a lot of time to investigate why I didn't get session_request in the app and it's possibly duplication of #14

session_request doesn't get triggered

When using either Trust Wallet or WallETH on Android to connect to the Example Dapp (example.walletconnect.org). After scanning the QR Code, the wallet continues loading indefinitely.

On the Bridge server, the logs show that the Wallet receives the wc_sessionRequest payload and it also subscribes to it's own clientId. However no peerMeta is displayed for session_request event.

My guess is that either the payload fails to decrypt or the session_request event doesn't get triggered. Most likely the latter

WCSessionStore APIs are not async friendly.

Hi from OpenSea! We have found an issue when enabling Android's StrictMode. It fails because FileWCSessionStore is doing file I/O on the main thread.

The WCSessionStore's methods are all synchronous, but a store (unless it is purely in-memory) is by nature asynchronous. In general, this means implementations would have to block the current thread while doing either file or network I/O. store() and remove() methods could maybe be implemented as fire-and-forget methods, but then we lose access to Kotlin's built-in structured concurrency.

With the provided FileWCSessionStore implementation, this means we are blocking the main thread while doing file I/O whenever these methods are called. FileWCSessionStore is also doing file I/O on the main thread in its' init block.

The proposed solution would be to make these methods suspend functions. Then, consumers can use their own coroutine scope to run these methods. This does mean that WCSession would need to take in a coroutine scope to launch these new suspend functions from.

Compilation failure in v0.9.7

I can't compile the latest version (v0.9.7). It might be because my project needs the jetifier.

   > Failed to transform bcprov-jdk15on-1.66.jar (org.bouncycastle:bcprov-jdk15on:1.66) to match attributes {artifactType=android-classes-jar, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.
      > Execution failed for JetifyTransform: /Users/sgc-code/.gradle/caches/modules-2/files-2.1/org.bouncycastle/bcprov-jdk15on/1.66/ed564ade61defca27e26fb1378a70b22831fc5c1/bcprov-jdk15on-1.66.jar.
         > Failed to transform '/Users/sgc-code/.gradle/caches/modules-2/files-2.1/org.bouncycastle/bcprov-jdk15on/1.66/ed564ade61defca27e26fb1378a70b22831fc5c1/bcprov-jdk15on-1.66.jar' using Jetifier. Reason: IllegalArgumentException, message: Unsupported class file major version 59. (Run with --stacktrace for more details.)
           Suggestions:
            - Check out existing issues at https://issuetracker.google.com/issues?q=componentid:460323&s=modified_time:desc, it's possible that this issue has already been filed there.
            - If this issue has not been filed, please report it at https://issuetracker.google.com/issues/new?component=460323 (run with --stacktrace and provide a stack trace if possible).

This is related to the latest update to org.bouncycastle:bcprov-jdk15on:1.66. As a workaround i'm using:

    implementation "com.github.WalletConnect:kotlin-walletconnect-lib:0.9.7", {
        exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
    }
    implementation 'org.bouncycastle:bcprov-jdk15on:1.65.01'

I tested with a few other versions of the bouncy castle library and here are the results:

    implementation 'org.bouncycastle:bcprov-jdk15on:1.64' // ok
    implementation 'org.bouncycastle:bcprov-jdk15on:1.65' // ok
    implementation 'org.bouncycastle:bcprov-jdk15on:1.65.01' // ok
    implementation 'org.bouncycastle:bcprov-jdk15on:1.66' // error
    implementation 'org.bouncycastle:bcprov-jdk15on:1.67' // error

Maybe we can revert to the last working version

Wallet connect library is working in debug mode but not in release mode

Wallet connect library is working in debug mode but when we create release build it stops working and we start getting error on every wallet like trust, metamask etc.

  • Below is the final URI :
    wc:339941ce-54d4-48ba-9b11-7cbf76d746bf@1?bridge=https%3A%2F%2Fbridge.walletconnect.org%2F&key=753547c93732ec3c7c4536518120e2bbc4a64570da9df3607405eec5dbb505f6

Error Image Link : https://ibb.co/4dc0xxj [Trust wallet screen ] same error on every wallet

Please help us, as we already spend day on this error.

approveRequest has question ?

when i invoke session.approveRequest(mCalledId,json)
mCalledId=1557716792569829
json{"signature":"a6003141600a3327190de4d6c0c25392e148902ddd8a79398bdaccba977febbb4abed804dd9b6cd72ceb1a4754baa239fa295cbdbcbf017133a8cbe0723fae67","publicKey":"0434f5d2f2c90b1bcdb7875775b702fe0c440993dbfe88f9101657d4b2081f01e181272df52eefdee77eed40762b3d013baea3d34281f0b772115316351e376cc6"}

but after invoked,the broswer just exit my bnb account and jump to Homepage!
Can you give me some advice?

Expose personal_sign, eth_signTransaction event

current only eth_sign and eth_sendTransaction are handled

private fun ByteArray.toMethodCall(): Session.MethodCall =
        String(this).let { json ->
            mapAdapter.fromJson(json)?.let {
                try {
                    val method = it["method"]
                    when (method) {
                        "wc_sessionRequest" -> it.toSessionRequest()
                        "wc_sessionUpdate" -> it.toSessionUpdate()
                        "eth_sendTransaction" -> it.toSendTransaction()
                        "eth_sign" -> it.toSignMessage()
                        null -> it.toResponse()
                        else -> it.toCustom()
                    }
                } catch (e: Exception) {
                    throw Session.MethodCallException.InvalidRequest(it.getId(), "$json (${e.message ?: "Unknown error"})")
                }
            } ?: throw IllegalArgumentException("Invalid json")
        }

Properly include moshi with generated adapters

To avoid issues as in #14 (partial fix with #15) we should use proper objects with moshi and generate adapters. These adapters will skip unknown values and known values are properly type checked.

ToDos

  • Add moshi code generation
  • Create data class for web socket message to bridge
  • Check current parsing logic and potentially create more data classes
  • Create a json parser interface and abstract away moshi in all classes to allow replacement by other json parsers

The chainId is Null

When the library try to parse a data from the server, chainId comes double but it tries to parse long, so we get null.

How can I open metamask and trust wallet app directly?

I see in the example using "wc:" schema to open wallet app and I have to choose which app I want to open in a dialog. But I want to open the app directly so how can I do that? There are any schemas to open Metmask or Trust wallet directly?

I appreciate your help.

Executing contract methods

Successfully connected Metamask with this library. Now, How do I establish web3 connection to my contract and call the contract's method. Can you please provide me some examples?

How can I get a wallet's public id?

Hi, I am a beginner dapp developer and I want to get user's wallet's public id using walletconnect. So far I am able to navigate my users to the app they chose (metamask, trustwallet etc) but I can't send a request to connect their wallets like Zerion did: https://i.stack.imgur.com/G5Thz.jpg How can I achieve this? Any help would be great, thank you.

Send Transaction issue

Hi,
No example is provide to send any transaction on any contract method.

  • We are done with wallet connect.
  • Could anyone provide us any code or example to execute a transaction on contract of a chain through wallet, We are stuck and not able to proceed further on it.

Thanks in advance!!

How can I sign message

I can connect to metamask, and get the wallet address.
Now, I want to sign message by the address.

I use

Session.preformMethodCall(
     Session.MethodCall.SignMessage(
          System.currentTimeMillis(),
          address,
          message
     ),
     callback
)

But the callback didn't be invoked.

I paste my log below here:

D/#####: Message: {"topic":"79bd6a79-5ca8-4156-a74f-b91987e1e43f","type":"pub","payload":"{\"data\":\"8147a49da277be922fe8d2ccf35d83e6826ed9e9e0eafd3f9a8a2442bc34c04acb18c40e0180e075b6a9ec6d148017d2dc3f12ced7e0b98fd28c02b87d156fd48fe32ca795895442f5559780f283e19da8b4bc56ec7d9b48db88bd40b601a2efe6321b5c90455f53d644d18e646ff2b2e8d6f3c4a1fa966636cf9170242f7d890d94bd7674156ebf12d9308d2d9f4385178450a38393dd3ccd3bf275a92e097adc844bd41f39e289bbd8e83d019063b595ff1c8309514163d52b3af90b5086ad13e3d26a13c733a56bdb0c8546926855ae71fc146335f079082698dfa29c5acd402e31b427e7b803e9fb0006c732ca363411c8f3a725edd1d956eec968ba08666ed295a24ebcbcd575d0b88c4ca4ae9e33228309ab2e008ef3cc6ceaf8ff71e2e417bddce3f909d2041b7144c4f93d83ea51ef8738daf794ee1168a80b554860\",\"hmac\":\"9f9bada49e44fa078525c818dbf7ac0c6effc249a3de94fcda12037744813df0\",\"iv\":\"10d665ce1699bba96a8ebe573828901e\"}"}

D/#####: Message: {"topic":"79bd6a79-5ca8-4156-a74f-b91987e1e43f","type":"ack","payload":"","silent":true}

W/System.err: java.lang.IllegalStateException: Unknown type

Disconnecting from wallets

Hi everyone, I am able to connect wallets to my native android app. Thanks to you, it works like a charm. But now, I have a different problem. How do I disconnect wallets from my app? I want to close session and port that oppened for my connection. Any help would be great, thanks a lot!

KHex version clashes with Kethereum lib

If the latest KEthereum and the walletconnect kotlin lib are used at the same time there will be errors due to incompatible versions (KEthereum uses KHex 1.0.0-rc6 while this lib uses KHex 0.6)

Solution: update KHex lib here

session callback change

follow up to #13 that introduced transportStatus and made the inconsistent callback even more inconsistent:

    interface Callback {
        fun transportStatus(status: Transport.Status)

        fun handleMethodCall(call: MethodCall)

        fun sessionApproved()

        fun sessionClosed()
    }

I suggest either add handleXX everywhere or remove it for handleMethodCall
might go even further here (and reason this is an issue and not a PR) we could reduce to 2 methods:
handleStaus and handleMethodCall and the session open closed events then come via the status

Add documentation & example

Context

This repository needs more context on the README file to be used by Android developers. As a reference let's use the swift-walletconnect-lib repository as an example of what is required to complete this bounty.

Goal

Include more information on the README file and additionally include an example app that can be used for testing.

Requirements

  • Example app (see swift-walletconnect-lib for example)
  • Demo video
  • List requirements
  • List Features (including TODO)
  • Describe Installation process
  • Describe Usage (including API reference)

Notes

If there are any other information that is commonly included in Kotlin libraries for Android developers, please also include these in the README.

How to set chaindId from calling client?

Should it be possible to set the chainId when constructing WCSession? I saw that the code works with chainIds is some parts, but if I understand it correctly this is only set AFTER the wallet connection was approved, you cannot predefine it before, right? I need to set the chainID, but don't know what part of the code to change to allow this.

Questions about the Android version demo

Is there a demo that can be run on Android? The current version seems to have a wrong directory structure. It can run after adjustment, but it crashes when you click connect.

java.net.ConnectException (Not triggering the request in wallet)

This is output of debug on running the app

[java.net.ConnectException: Failed to connect to bridge.walletconnect.org/2606:4700:20::ac43:45b8:7737, java.net.ConnectException: Failed to connect to bridge.walletconnect.org/2606:4700:20::681a:32c:7737, java.net.ConnectException: Failed to connect to bridge.walletconnect.org/2606:4700:20::681a:22c:7737, java.net.SocketTimeoutException: failed to connect to bridge.walletconnect.org/104.26.2.44 (port 7737) from /192.168.1.8 (port 44900) after 10000ms, java.net.SocketTimeoutException: failed to connect to bridge.walletconnect.org/172.67.69.184 (port 7737) from /192.168.1.8 (port 40876) after 10000ms]

ExampleAplication.kt
`import androidx.multidex.MultiDexApplication
import com.squareup.moshi.Moshi
import com.peepout.io.peepout.server.BridgeServer
import com.squareup.moshi.JsonClass
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import okhttp3.OkHttpClient
import okhttp3.CipherSuite
import okhttp3.TlsVersion
import okhttp3.ConnectionSpec
import android.util.Log
import org.komputing.khex.extensions.toNoPrefixHexString
import org.walletconnect.Session
import org.walletconnect.impls.*
import org.walletconnect.nullOnThrow
import java.io.File
import java.net.InetSocketAddress
import java.net.Proxy
import java.util.*

class ExampleApplication:MultiDexApplication() {

@JsonClass(generateAdapter = true)
override fun onCreate() {
    super.onCreate()
    initMoshi()
    initClient()
    initBridge()
    initSessionStorage()
}

private fun initClient() {


    client = OkHttpClient.Builder().build()
}

private fun initMoshi() {
    moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
}


private fun initBridge() {
    bridge = BridgeServer(moshi)
    bridge.start()
}

private fun initSessionStorage() {
    storage = FileWCSessionStore(File(cacheDir, "session_store.json").apply { createNewFile() }, moshi)
}

companion object {
    private lateinit var client: OkHttpClient
    private lateinit var moshi: Moshi
    private lateinit var bridge: BridgeServer
    private lateinit var storage: WCSessionStore
    lateinit var config: Session.Config
    lateinit var session: Session

    fun resetSession() {
        nullOnThrow { session }?.clearCallbacks()
        val key = ByteArray(32).also { Random().nextBytes(it) }.toNoPrefixHexString()
        Log.d("Kashif",BridgeServer.PORT.toString())
        config = Session.Config(UUID.randomUUID().toString(), "https://bridge.walletconnect.org", key)
        session = WCSession(config, MoshiPayloadAdapter(moshi), storage,
            OkHttpTransport.Builder(client, moshi),
            Session.PeerMeta(name = "Peepout"))
        session.offer() }
}

}BridgeServer:import android.util.Log
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import org.java_websocket.WebSocket
import org.java_websocket.handshake.ClientHandshake
import org.java_websocket.server.WebSocketServer
import java.lang.Exception
import java.lang.ref.WeakReference
import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.ConcurrentHashMap

class BridgeServer(moshi: Moshi) : WebSocketServer(InetSocketAddress(PORT)) {
@JsonClass(generateAdapter = true)

private val adapter = moshi.adapter<Map<String, String>>(

    Types.newParameterizedType(
        Map::class.java,
        String::class.java,
        String::class.java
    )
)

private val pubs: MutableMap<String, MutableList<WeakReference<WebSocket>>> = ConcurrentHashMap()
private val pubsLock = Any()
private val pubsCache: MutableMap<String, String?> = ConcurrentHashMap()

override fun onOpen(conn: WebSocket?, handshake: ClientHandshake?) {
    Log.d("Kashif",PORT.toString())
    Log.d("#####", "onOpen: ${conn?.remoteSocketAddress?.address?.hostAddress}")
}

override fun onClose(conn: WebSocket?, code: Int, reason: String?, remote: Boolean) {
    Log.d("#####", "onClose: ${conn?.remoteSocketAddress?.address?.hostAddress}")
    conn?.let { cleanUpSocket(it) }
}

override fun onMessage(conn: WebSocket?, message: String?) {


    Log.d("#####", "Message: $message")
    var check = message
    check =check?.replace("true","\"true\"")

    try {
        conn ?: error("Unknown socket")
        check?.also {
            val msg = adapter.fromJson(it) ?: error("Invalid message")//error
            val type: String = msg["type"] ?: error("Type not found")
            val topic: String = msg["topic"] ?: error("Topic not found")

            when (type) {
                "pub" -> {

                    var sendMessage = false
                    pubs[topic]?.forEach { r ->
                        r.get()?.apply {
                            send(check)
                            sendMessage = true
                        }
                    }
                    if (!sendMessage) {
                        Log.d("#####", "Cache message: $check")
                        pubsCache[topic] = check
                    }
                }
                "sub" -> {

                    pubs.getOrPut(topic, { mutableListOf() }).add(WeakReference(conn))
                    pubsCache[topic]?.let { cached ->
                        Log.d("#####", "Send cached: $cached")
                        conn.send(cached)
                    }

                }
                else -> error("Unknown type")
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

override fun onStart() {
    Log.d("#####", "Server started")
    connectionLostTimeout = 1000000000
}

override fun onError(conn: WebSocket?, ex: Exception?) {
    Log.d("#####", "onError")
    ex?.printStackTrace()
    conn?.let { cleanUpSocket(it) }
}

private fun cleanUpSocket(conn: WebSocket) {
    synchronized(pubsLock) {
        pubs.forEach {
            it.value.removeAll { r -> r.get().let { v -> v == null || v == conn } }
        }
    }
}

companion object {
    val PORT = 5000 + Random().nextInt(60000)

}

}`

How do I sign with personal_sign

val from = ExampleApplication.session.approvedAccounts()?.first() ?: return
Log.i(TAG, "---click-->$from")
ExampleApplication.session.performMethodCall(
Session.MethodCall.Custom(
System.currentTimeMillis(),
"personal_sign",
arrayListOf(
"0xdeadbeaf", from)
)
) { response ->
Log.d(TAG, "------>{response.result}--->{response.error}")
}

I use the Custom method, but I get no response, I can't find what the problem is

Sample App is not working

Hello,

I am testing Sample application.

When I click on Connect button, it opens the Metamask application, but nothing happens.

It Dont show the dialog to connect with metamask.

Do v1-lib support the ping message!

I use OkhttpClient to create the webSocket with 1s interval ping message.But , I just get the "Missing or invalid socket data" back.
Will the v1 could supoort the ping message? I need this heart stragy to keep the websocket alive.

How to sign ether transaction?

Hi, I receive this:
SendTransaction(id=1643806348107256, from=0x47a1d3cf6908263372d737655d6084c377040f1f, to=0xd4a57a3bd3657d0d46b4c5bac12b3f156b9b886b, nonce=null, gasPrice=null, gasLimit=null, value=0x3a43b68e51000, data=0xe99a3f80...
I sign transaction, and get hash -> 0xd887b7bbdaabe5fdc21625fd41c44ec014323549518dd6aac96b65d636e33299.
But what next? I am trying to do session?.performMethodCall(sign), but nothing happens

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.