Giter Site home page Giter Site logo

randlabs / myalgo-connect Goto Github PK

View Code? Open in Web Editor NEW
60.0 15.0 27.0 498 KB

Browser package to bring access to MyAlgo Wallet accounts to dApps

Home Page: https://connect.myalgo.com/

License: Apache License 2.0

JavaScript 77.66% CSS 4.30% HTML 18.04%

myalgo-connect's Introduction

MyAlgo Connect

npm version Website shields.io Discord Twitter

myalgo-logo

Overview

MyAlgo Connect is a Javascript library developed by Rand Labs to securely sign transactions with My Algo

Installation

The package can be installed via npm:

npm i @randlabs/myalgo-connect

or imported in the HTML

<script src="./myalgo.min.js"></script>

Find the browser minified version in our github releases

API Usage

Quick start

import MyAlgoConnect from '@randlabs/myalgo-connect';
const myAlgoWallet = new MyAlgoConnect();

Connect to My Algo

/*Warning: Browser will block pop-up if user doesn't trigger myAlgoWallet.connect() with a button interation */
async function connectToMyAlgo() {
  try {
    const accounts = await myAlgoWallet.connect();
    const addresses = accounts.map(account => account.address);
    
  } catch (err) {
    console.error(err);
  }
}
<button onclick="connectToMyAlgo()">Connect!</button>

Sign transaction

import algosdk from 'algosdk';
const algodClient = new algosdk.Algodv2('', 'https://node.algoexplorerapi.io/', 443);

/*Warning: Browser will block pop-up if user doesn't trigger myAlgoWallet.connect() with a button interation */
async function signTransaction (from, to, amount, suggestedParams) {
  try {
    const txn = algosdk.makePaymentTxnWithSuggestedParams({ suggestedParams, from, to, amount });
    const signedTxn = await myAlgoWallet.signTransaction(txn.toByte());  
    const response = await algodClient.sendRawTransaction(signedTxn.blob).do();
    console.log(response)
  } catch(err) {
    console.error(err); 
  }
};

Documentation

Documentation for this package is available here: https://connect.myalgo.com/. An example of an integration with MyAlgo Connect: https://github.com/randlabs/myalgo-connect-test

Copyright and License

See LICENSE file.

myalgo-connect's People

Contributors

alex99y avatar ignacio-87 avatar itslesther avatar m-picco avatar nullun 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

Watchers

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

myalgo-connect's Issues

Operation cancelled

Hi All,

We have a user who is experiencing this error, Operation cancelled which seems to stem from signTransaction.

Unfortunately we can't reproduce and are having a hard time getting much from the user, but I thought I'd reach out to see if anyone is seeing something similar, or has an troubleshooting tips.

Basically all we are doing is asking our users to connect their wallet (popup 1), then sign a 0 amount transaction (popup 2). Each with a user action to avoid popup warnings.

We have at least 10 people regularly and successfully doing this (multiple times a day) on our web app.

This user however, seems to always get the error Operation cancelled and we have tracked the message back to the call to signTransaction.

Again, not much to go off, we are asking the client to try a different browser, new account, etc... but any tips would be welcome.

Cheers.

sign 2 batches of tx groups in a roll

Hi there,

I have a large size array of txns that I would like to sign and I've divided them into smaller arrays with a length of 16 (due to the tx group size limitation). It's something like this:
image

and what I wanna do next is to sign this array of tx group in sequence:

let signedTxg = new Array(txgg.length); // txgg here is the chunked array above
for (let i = 0; i < txgg.length; i++) {
  signedTxg[i] = await walletContext.myAlgoConnect.signTransaction(
    txgg[i].map((txn) => txn.toByte())
  );
  console.log(txgg[i]);
}
console.log(signedTxg);

The expected behavior is when I close the first popup, the second popup can show up.

However, the first popup did work and I did get the signed txgroup while the second one doesn't show up and I got an error message like this: PopupOpenError: Can not open popup window - blocked

I'm using nextjs and the code is triggered by a button onclick.

I tried to setTimeout after the signTransaction function returns but it didn't work.

I got the myAlgoConnect instance from the contextAPI and I'm wondering if this is an issue?
Should I use a new instance everytime I signTx?

Thanks in advance.

Ho do I listen to account events?

How do I set up events for accounts disconnected/connected/error etc?

For example when the page reloads, how do I check if the account was already connected to my app? Do I need to run, connect, every time?
Or what if the user disconnected the app from his wallet?

I'm referring to similar events that there are on MetaMask and other web3 wallets.

"Error: Not supported at MyAlgoConnect.signTransaction" when signing app create transaction

I keep getting Error: Not supported at MyAlgoConnect.signTransaction when signing an app create transaction, and it doesn't seem to matter which TEAL version I'm using.

Example code below:

let createApp = async () => {
  let accounts = await myAlgoWallet.connect();
  let params = await algodClient.getTransactionParams().do();

  let transaction = {
    from: accounts[0].address,
    type: 'appl',
    appOnComplete: 0,
    appApprovalProgram: "AiABASI=",
    appClearProgram: "AiABASI=",
    appLocalInts: 0,
    appLocalByteSlices: 0,
    appGlobalInts: 1,
    appGlobalByteSlices: 1,
    ...params
  };

  let signedTransaction = await myAlgoWallet.signTransaction(transaction);
}

Am I doing something wrong or is application create transactions really not supported?

Thanks!

myAlgoWallet.connect() is only returning 1 address even if you have multiple addresses present in your MyAlgoWallet.

Tried playing around with the API and noticed that an array of addressess is intended to be returned by the myAlgoConnect.connect() method. I have imported a 2nd address over but the method is only returning the first address at the moment. I'm not sure if this is intended behaviour and would appreciate some clarity as to how would I access the second address stored in the wallet.

I can't sign rekeyed account transactions

Hi and thanks for this awesome lib! I ran into a small issue.

Preconditions

I have two accounts:

  • Main is a My Algo account
  • Secondary is a fresh account that is immediately rekeyed to the My Algo Account

Issue

After the rekey the secondary account must be signed by the primary account's key but myAlgoWallet.signTransaction(tx) fails.

Algosdk demo code

import algosdk from "algosdk";

const main = algosdk.mnemonicToSecretKey("mnemonic");
const secondary = algosdk.generateAccount();

export default async function makeRekeyedAccount() {
  const params = await algodclient.getTransactionParams().do();

  const fundTx = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
    suggestedParams: params,
    from: main.addr,
    to: secondary.addr,
    amount: 200000,
  });

  const rekeyTx = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
    suggestedParams: params,
    from: secondary.addr,
    to: secondary.addr,
    amount: 0,
    rekeyTo: main.addr,
  });

  const txs = [fundTx, rekeyTx];
  algosdk.assignGroupID(txs);

  const fundSigned = algosdk.signTransaction(fundTx, main.sk);
  const rekeySigned = algosdk.signTransaction(rekeyTx, secondary.sk);

  await client.sendRawTransaction([fundSigned.blob, rekeySigned.blob]).do();
}

export async function testTransaction() {
  const params = await algodclient.getTransactionParams().do();

  const testTx = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
    suggestedParams: params,
    // send a transaction from the secondary account which is already rekeyed to the main account
    from: secondary.addr,
    to: main.addr,
    amount: 0,
  });

  // the transaction has to be signed by the main account
  // this would fail if the main account would be a My Algo account and I used
  // `await myAlgoWallet.signTransaction(testTx)` here
  const testSigned = algosdk.signTransaction(testTx, main.sk);

  await client.sendRawTransaction(testSigned.blob).do();
}

There error is originating from here:

throw new Error(res.message);

image
image

Probable solution

I guess you validate the transactions' from address and maybe forgot to check account["auth-addr"] against it.

Have to disconnect site to add new address

When you re-connect the addresses to a website that has already been connected in the past (and therefore is in myalgo connected sites in settings), there is no option / check for new wallets. You have to disconnect the site to add the new wallet address.

To reproduce:

  1. call new MyAlgo().connect()
  2. create new wallet on myalgo
  3. call new MyAlgo().connect()

Removing Multisig Account

When removing a multisig account signer from MyAlgo Connect on dApp, the multisig account becomes unavailable to be used (since you need both the multisig signer account and the multisig account). However removing multisig signer account does not remove multisig account.

Steps to reproduce:

  1. Add both signer and msig accounts when connecting your wallet
  2. Disconnet
  3. Reconnect and click "Manage your account"
  4. Untick signer account (msig account will say "Disabled)
  5. Connect

Unable to recover access to an Algorand account by entering my BIP39 mnemonic (+ optional passphrase) in MyAlgo

Unable to recover access to an Algorand account by entering my BIP39 mnemonic (+ optional passphrase) in MyAlgo web wallet.

There seems to be no way to enter a 12, 18 or 24 word (+ optional passphrase) BIP39 mnemonic in MyAlgo to recover access to an Algorand account.

Same issue with the "Official Algorand phone wallet", by the way.

Does MyAlgo use a proprietary mnemonic format (not compatible with BIP39)?

Any plan to support the BIP39 standard recovery mnemonic format (12, 18 or 24 word + optional passphrase), which now-a-days are supported by almost all crypto wallets for seed recovery?

[EDITED]

Apparently Algorand wallets use a proprietary recovery phrase that directly encode the keys: https://discord.com/channels/491256308461207573/631209441240416256/812841568620642354

Algorand does not use at all BIP39
The mnemonic directly encodes the key.

Algorand wallets should add support for recovery from standard BIP39 seeds (from BIP39 mnemonic and optional passphrase), and then derive the ALGO keys using the standard ALGO derivation path, m/44'/283'/x'/0/0 for account #x.

Augment MyAlgoAccount.connect() to return the account nickname

Currently, the MyAlgoAccount.connect() API only returns the account Algorand addresses, which are not user-friendly. The account nickname that was assigned in the MyAlgo wallet should also be provided. The nickname would make it easier for the user to confirm and identify the Algorand address from within the app.

I propose to augment Accounts with a nickname field.

export type Nickname = string;

export interface Accounts {
	address: Address;
        nickname: Nickname
}

ReferenceError: Buffer is not defined

I followed the steps for the My Algo Connect setup, and most of it was working (such as the popup to connect), but when it came to sign a transaction I was getting the error ReferenceError: Buffer is not defined at MyAlgoConnect.signTransaction (webpack://algodex/./node_modules/@randlabs/myalgo-connect/lib/main.js?:329:35).

I managed to fix this with a hack on line 11 in @randlabs/myalgo-connect/lib/main.js:

window.Buffer = window.Buffer || require('buffer').Buffer;

This looks like some dependency problem where NPM for MyAlgoConnect doesn't install the node module for Buffer as a sub-dependency of it. However, I'm not an NPM expert, so not sure why this happened.

Last character in note of txn cut off

When signing a payment txn with a note attached the last character is being cut off. The string is shown correctly in the signing window.

This happens when using new Uint8Array(Buffer.from(NOTE, "base64")). It seems to work without the second argument.

getBalance

I've searched through the docs and don't see any information regarding - getting the balance of a wallet address and the ability to logout without having to go to https://wallet.myalgo.com/
Do you not offer these features which seem to be standard in other wallets on other chains?

Nano-S HW wallet issue

When using a Nano-S hardware wallet for signing, MyAlgo connect works correctly only the first time: asks for password in the popup window, then displays the txn details on the Nano-S and waits for Accept/Reject. But during the next call, it asks for the password, then the popup disappears, and the Nano-S is not asked for signing. Test was done on Testnet, using Firefox and Chrome browsers.

Critical bug in Myalgo web wallet due to caching of ALGO public-key/address in Algorand ledger app

Just making sure that you get aware of the critical bug reported here:

https://www.reddit.com/r/ledgerwallet/comments/mb9brr/psa_do_not_use_algo_with_the_ledger_via_the_algo/

You should never cache the ALGO account address in the Algorand ledger app, because the ledger seed is not always the same:

A user can use a BIP39 passphrase and this will generate different seeds (with the active/unlocked seed being derived from the currently used passphrase - or empty passphrase if none is used).

The bug is critical and can cause permanent loss of ALGO coins and tokens.

Note: this bug also affects https://algorandwallet.com/ , but I cannot find their Github to report it.

Availability of CDN package

Hi,
is there a CDN package available so that we can import this package using script tag instead of using NPM.

TypeError: Cannot read properties of undefined (reading 'WINDOWS_NOT_LOADED')

myalgo.min.js v1.4.1
Unbuntu v22.04.1 LTS
AWS EC2
Apache 2.4.52
Let's Encrypt CertBot SSL

When trying to connect, https://wallet.myalgo.com/bridge/connect pops up and continues to spin for about 10 seconds, then closes without login prompt. The error returned:

TypeError: Cannot read properties of undefined (reading 'WINDOWS_NOT_LOADED')

Works fine without SSL. I've also tried AWS ACM SSL certificate with same error. Are these SSL incompatible with MyAlgo?

Is it possible to pass groups with transactions not to be signed?

WalletConnect allows to pass group transactions that are not to be signed by the current user (i.e. only for informative purpose). See e.g tutorial:

const txnsToSign = txns.map(txn => {
  const encodedTxn = Buffer.from(algosdk.encodeUnsignedTransaction(txn.txn)).toString("base64");

  return {
    txn: encodedTxn,
    message: 'Description of transaction being signed',
    // Note: if the transaction does not need to be signed (because it's part of an atomic group
    // that will be signed by another party), specify an empty singers array like so:
    // signers: [],
  };
});

This way it's possible for the user to review all the transactions in the group when signing their part.

Is this possible with My Algo?

window is not defined when running on nextjs application

Hello!

Trying to integrate your lib into nextjs application.
But i am getting 'window is not defined' error, which makes sense given SSR.

Any chance you can fix it somehow to support SSR or guide how to initialize the lib in nextjs app ?
Error is thrown here

ReferenceError: window is not defined at Messenger._installListener (***/node_modules/@randlabs/communication-bridge/lib/messenger.js:144:3)

Asset opt-in label?

When sending an asset opt-in, the transaction is labelled like a normal asset transfer:

{"amount":0,"assetIndex":42,"fee":1000,"firstRound":4,"flatFee":true,"from":"MKRBTLNZRS3UZZDS5OWPLP7YPHUDNKXFUFN5PNCJ3P2XRG74HNOGY6XOYQ","genesisHash":"HSY4rC3pz7fMnrI38j5OJJIm4R/uks2m/VTpuw8a158=","genesisID":"tn50e-v1","lastRound":1004,"to":"MKRBTLNZRS3UZZDS5OWPLP7YPHUDNKXFUFN5PNCJ3P2XRG74HNOGY6XOYQ","type":"axfer"}

Screen Shot 2021-09-24 at 09 04 21

I imagine that this will effectively opt-in the user (have not tested it yet, my flow is a bit complicated and I'm not submitting immediately). It would be better in any case to display explicitly that it's an opt-in.

Maybe I'm doing something wrong?

Note: when I don't send amount, there's an error, so I assume that the 0 is expected.

Feature proposal: Create and Verify Zero Value Transactions

The core idea is that we want to build UX systems where we can verify ownership of an account (and by inference ASAs) without bothering the user with constant popups and without making the user pay for the privilege.

There is a forum discussion thread which is part of the way there, it achieves the verification step https://forum.algorand.org/t/custody-verification-in-algorand/1422/9 and we are starting to see this implemented in the wild such as this site https://blackbook.originalsonly.io/

My feature proposal is for two additional SDK function calls to make this process much simpler:

  1. createZeroValueTxn(noteId):txn which creates a signed transaction with the noteId (off-chain) and can easily be sent in a JSON payload (byte array?)
  2. verifyZeroValueTxn(txn, noteId):bool which takes the txn and returns a boolean that verifies the a zero value signed txn includes the noteId field (new context, e.g. could be on another client, or node server).

Importantly is it possible to achieve this without a popup, perhaps after an initial unlock, in order to create smooth UX experiences?

Probably there would also be a need to add an opt-in flag to allow this (maybe per account).

Pls let me know if this makes sense and if it has merit I'm happy to help with a PR.

Also, if this is not the right place for a feature suggestion discussion pls let me know where (discord?)

Additional references:

Is it possible to sign multisig transactions?

As the title says. I found no option to sign a multisig transaction on myAlgo.

For reference, in AlgoSigner, during the signing process, there is a possibility to pass an optional parameter msig, which specifies that a txn will be a multisig one and provides all needed data for it (required adresses, threshold & version). I didn't find anything like that here. signTransaction function only accepts transaction or an array of transactions as first parameter and signOptions as a second one but those options are just limited to overrideSigner, which I believe, has nothing to do with multisig transactions.

So, is there any way to do it? If not, it would be nice to implement this feature

Can not sign a normal transaction with getting error cross-origin

Currently I am working on a project called Sigma wallet. I have a input where user would paste the base64 format transaction and sign the multisig transaction.
Now I am heading on making multisig transaction work with myalgo. But I have test if I paste a normal transaction and sign that transaction using myalgo wallet. It appear that window would spinning till the end of the universe with errors:
Uncaught (in promise) DOMException: Blocked a frame with origin "http://localhost:8080" from accessing a cross-origin frame. at isRef (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:1329:20) at Object.get (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:622:9) at Proxy.signTransaction (webpack-internal:///./node_modules/@randlabs/myalgo-connect/lib/main.js:378:29) at async Proxy.signTransaction (webpack-internal:///./node_modules/@algo-builder/web/build/lib/myalgowallet-mode.js:69:12) at async Proxy.sign (webpack-internal:///./node_modules/@vue/cli-plugin-typescript/node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js??clonedRuleSet-41.use[1]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/pages/MsigUI.vue?vue&type=script&lang=ts:91:27)
Link to my source code: https://github.com/scale-it/algobuilder-msig/blob/815ea0a93504e0d00fff38bc40f67915a4b4eb46/src/pages/MsigUI.vue#L169

image

Is there any way that I can fix this?

Edited: I can access the the window myalgo wallet connect like below
image

image

I have try to enable vue.config.js like this but it still did not work
image

Window blanks on icon hover

When signing a transaction, when my mouse hovers over the i information icon in the top right, the window goes completely white.

Unable to override signer when original sender address is not in wallet

It seems that if I want to sign a transaction using the overrideSigner option of the signTransaction method, I need to have the original address that is present in the from/sender field as an account in MyAlgoWallet.

Here is an example to reproduce the error. ACC1 is an account not present in MyAlgoWallet, while ACC2 is an account that is present.

const ACC1 = "..."
const ACC2 = "..."

const node = 'https://node.testnet.algoexplorerapi.io';

const myAlgoConnect = new MyAlgoConnect({ disableLedgerNano: false,  });
myAlgoConnect.connect();

const algodClient = new algosdk.Algodv2('', node, '');

const params = await algodClient.getTransactionParams().do();
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
  suggestedParams: { ...params },
  from: ACC1,
  to: ACC1,
  amount: 10,
});

const signedTxn = await myAlgoConnect.signTransaction(txn.toByte(), { overrideSigner  : ACC2 });
Error: Account does not exist in the storage
    at MyAlgoConnect.signTransaction (main.js:356:1)

If instead both ACC1 and ACC2 are added onto the wallet, then everything works fine.

Incorrect optional keys are silently ignored?

I submitted a transaction with an incorrectly cased genesis id key (genesisId instead of genesisID). My Algo accepted the transaction while silently ignoring the field. The returned signed transaction didn't have a genesis id.

Is this behavior intentional? Does it happen with all the optional fields? From a security standpoint it seems risky.

Would rejecting the transaction when the payload is malformed be an option?

Tested version: 1.0.1

Unable to scroll down to 'Manage your accounts' on mobile

Using iPhone SE 2020 - when opening the myAlgo connect popup/new tab for mobile, as per title, unable to scroll to 'Manage your accounts'. I can hold the scroll and see it is at the bottom of the screen, but on release it is hidden below the window and inaccessible.

How to use rekeyed account?

I'm having problems figuring out how to sign with a rekeyed account.

Given 2 addresses: rekeyed: rekeyed address, auth, the address that was rekeyed to.

I authorized both addresses to connect to my site.

On the tx, I set rekeyed as sender. And now I'm expecting to be able to sign with auth. But My Algo always tries to sign with rekeyed and I don't see a way to change it?

I found this #38 where apparently a bug was fixed, not sure if related.

Error: Invalid txn type

For some reason I keep on getting this error when trying to call an application. I cannot seem to diagnose the issue.

Relevant code is:

const buyer: string = addresses[0];

let params: SuggestedParams = await client.getTransactionParams().do();
params.fee = 1000;
params.flatFee = true;

const buy: Base64 = "buy";
const appArgs: Base64[] = [buy];
let callAppTxn: CallApplTxn = {
    ...params,
    type: "appl",
    from: buyer,
    appIndex: appId,
    appOnComplete: 0,
    appArgs: appArgs
}

let rawSignedCallAppTxn = await myAlgoWallet.signTransaction(callAppTxn) as TxSig;

The error occurs when signing the transaction. The pop up window appears (blank) for a split second and then I get the error Unhandled Rejection (Error): Invalid txn type from node_modules/@randlabs/myalgo-connect/lib/main.js:311.

Thanks in advance!

Report a display bug in myalgo web front-end

Hello, I encountered a display bug in front-end of myalgo wallet. As seems there is not a public respository about myalgo webpages, I will describe this bug here.

The situation is:

  1. I created a wallet in myalgo, and sent some $algo (let n1 denote its number) to this wallet address $a1;

  2. Then, I tried to call py-algorand-sdk https://github.com/algorand/py-algorand-sdk to transfer all n1 $algo in $a1 to another wallet $a2 with the code:
    params = algodclient.suggested_params()
    txn = transaction.PaymentTxn(from_addr, params, to_addr, amount, asset_id, close_assets_to=to_addr)
    algodclient.send_transaction(txn.sign(from_key))
    where from_addr=$a1 , to_addr=$a2, and close_assets_to is set for emptying wallet $a1;

  3. However, the dispaly balance of $a1 would remain n1 after several hours of emptying, not synchronized with the algorand network, in the myalgo webpage;

  4. As I repeated similar calling processes for several times, the situation remained the same. Here are some screenshots:
    wallet1274
    wallet1275
    wallet1276

  5. BTW, when I chose to left some $algo in myalgo wallet, the display is correct:
    wallet1

So I guess there is some condition missing considered in balance display.

Signing arbitrary data like HTTP Requests/JSON data

I want to try to sign my HTTP requests/JSON data (destined for my own backend server) using the key of the Algo Connect User. Any chance that I could use signTransaction or signLogicSig in the browser with sort of arbitrary data? Then I would use verifyBytes on my server. I assume that My Algo Connect does not actually get access to the signing key?

Sort of related.. I notice you use the algodClient in your examples, with a connection to a server. But it seems like I would need to expose my API key on the frontend if I do this.. or am I missing something?

Export SignedTx

The signTransaction method returns Promise<SignedTx | SignedTx[]> however the interface SignedTx is not exported.

This means that I need to check if blob is a member of the returned value when it would be easier to just tell the compiler the type.

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.