Home
Softono
p

pubkey

Professional software vendor delivering innovative solutions on the Softono platform. Specialized in both open-source and proprietary software development.

Total Products
2

Software by pubkey

rxdb
Open Source

rxdb

<p align="center"> <a href="https://rxdb.info/"> <img src="./docs-src/static/files/logo/logo_text.svg" width="380px" alt="JavaScript Database" /> </a> <br /> <h3 align="center">A fast, <a href="https://rxdb.info/articles/local-first-future.html">local-first</a>, reactive Database for JavaScript Applications</h3> </p> <br /> <p align="center"> <a href="https://github.com/pubkey/rxdb/releases"><img src="https://img.shields.io/github/v/release/pubkey/rxdb?color=%23ff00a0&include_prereleases&label=version&sort=semver&style=flat-square"></a> &nbsp; <a href="https://rxdb.info/tutorials/typescript.html"><img src="https://img.shields.io/npm/types/rxdb?style=flat-square"></a> &nbsp; <a href="https://github.com/pubkey/rxdb/blob/master/LICENSE.txt"><img src="https://img.shields.io/github/license/pubkey/rxdb?style=flat-square"></a> &nbsp; <a href="https://github.com/pubkey/rxdb/stargazers"><img src="https://img.shields.io/github/stars/pubkey/rxdb?color=f6f8fa&style=flat-square"></a> &nbsp; <a href="https://www.npmjs.com/package/rxdb"><img src="https://img.shields.io/npm/dm/rxdb?color=c63a3b&style=flat-square"></a> </p> <p align="center"> <a href="https://rxdb.info/chat"><img src="https://img.shields.io/discord/969553741705539624?label=discord&style=flat-square&color=5a66f6"></a> &nbsp; <a href="https://twitter.com/intent/follow?screen_name=rxdbjs"><img src="https://img.shields.io/badge/twitter-%40rxdbjs-1DA1F2?style=flat-square"></a> &nbsp; <a href="https://www.linkedin.com/company/rxdb"><img src="https://img.shields.io/badge/linkedin-%40rxdb-0e76a8?style=flat-square"></a> &nbsp; <a href="https://rxdb.info/newsletter"><img src="https://img.shields.io/badge/newsletter-subscribe-e05b29?style=flat-square"></a> </p> <br /> <h2> <img height="16" width="16" src="./docs-src/static/files/logo/logo.svg">&nbsp;&nbsp;What is RxDB? </h2> <p align="justify"> RxDB (short for <b>R</b>eactive <b>D</b>ata<b>b</b>ase) is a <a href="https://rxdb.info/articles/local-first-future.html">local-first</a>, NoSQL-database for JavaScript Applications. Reactive means that you can not only query the current state, but <b>subscribe</b> to all state changes like the result of a query or even a single field of a document. This is great for UI-based <b>realtime</b> applications in a way that makes it easy to develop and also has great performance benefits. </p> Use the [quickstart](https://rxdb.info/quickstart.html), read the [documentation](https://rxdb.info/install.html) or explore the [example projects](https://github.com/pubkey/rxdb/tree/master/examples). <p align="center"> <a href="https://www.youtube.com/watch?v=tj7AaDDHv2g" target="_blank"><img src="./docs-src/static/files/readme_video.png" alt="RxDB on Youtube" /></a> </p> <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/people.svg">&nbsp;&nbsp;Used by <b style="color: #e6008d;">many</b> </h2> RxDB is a proven technology used by [thousands of developers](https://github.com/pubkey/rxdb/network/dependents) worldwide. With its flexibility, RxDB is used in a diverse range of apps and services. <a href="https://rxdb.info/#reviews" target="_blank"> <img src="./docs-src/static/files/used-by-many.png" /> </a> <br /> (<a href="https://forms.gle/7taae27VqLXqd4sF6">add yours</a>) <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/multiplayer.svg">&nbsp;&nbsp;Multiplayer realtime applications <img height="36" src="./docs-src/static/files/icons/with-gradient/text/made-easy.svg"> </h2> ![realtime.gif](docs-src/static/files/animations/realtime.gif) <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/replication.svg">&nbsp;&nbsp;Replicate with your <b style="color: #e6008d;">existing infrastructure</b> </h2> RxDB provides an easy to implement, <b>battle-tested</b> <a href="https://rxdb.info/replication.html">Sync Engine</a> for <a href="https://rxdb.info/articles/realtime-database.html">realtime</a> replication with your existing infrastructure.<br /> You do not have to use a specific cloud or backend database. The protocol works by implementing three simple HTTP endpoints. There are also production-ready plugins to easily replicate with <a href="https://rxdb.info/replication-graphql.html">GraphQL</a>, <a href="https://rxdb.info/replication-couchdb.html">CouchDB</a>, <a href="https://rxdb.info/replication-websocket.html">Websocket</a>, <a href="https://rxdb.info/replication-webrtc.html">WebRTC (P2P)</a>, <a href="https://rxdb.info/replication-supabase.html">Supabase</a>, <a href="https://rxdb.info/replication-firestore.html">Firestore</a>, <a href="https://rxdb.info/replication-nats.html">NATS</a> or <a href="https://rxdb.info/replication-google-drive.html">Google Drive</a>. <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/storage-layer.svg">&nbsp;&nbsp;<b style="color: #8D2089;">Flexible</b> storage layer </h2> RxDB is based on a [storage interface](https://rxdb.info/rx-storage.html) that enables you to swap out the underlying storage engine. This increases **code reuse** because the same database code can be used in different JavaScript environments by just switching out the storage settings. You can use RxDB on top of [LocalStorage](https://rxdb.info/rx-storage-localstorage.html), [IndexedDB](https://rxdb.info/rx-storage-indexeddb.html), [OPFS](https://rxdb.info/rx-storage-opfs.html), [LokiJS](https://rxdb.info/rx-storage-lokijs.html), [Dexie.js](https://rxdb.info/rx-storage-dexie.html), [in-memory](https://rxdb.info/rx-storage-memory.html), [SQLite](https://rxdb.info/rx-storage-sqlite.html), in a [WebWorker](https://rxdb.info/rx-storage-worker.html) thread and even on top of [FoundationDB](https://rxdb.info/rx-storage-foundationdb.html) and [DenoKV](https://rxdb.info/rx-storage-denokv.html). No matter what kind of runtime you have, as long as it runs JavaScript, it can run RxDB: <h4> <img height="13" src="https://cdnjs.cloudflare.com/ajax/libs/browser-logos/39.2.2/chrome/chrome_24x24.png" /> <img height="13" src="https://cdnjs.cloudflare.com/ajax/libs/browser-logos/39.2.2/firefox/firefox_24x24.png" /> <img height="13" src="https://cdnjs.cloudflare.com/ajax/libs/browser-logos/39.2.2/safari/safari_24x24.png" /> <img height="13" src="https://cdnjs.cloudflare.com/ajax/libs/browser-logos/39.2.2/edge/edge_24x24.png" /> <a href="./examples/angular">Browsers</a> <img height="13" src="docs-src/static/files/icons/nodejs.svg" /> <a href="./examples/node">Node.js</a> <img height="13" src="docs-src/static/files/icons/react.svg" /> <a href="https://rxdb.info/react-native-database.html">React Native</a> <img height="13" src="docs-src/static/files/icons/capacitor.svg" /> <a href="https://rxdb.info/capacitor-database.html">Capacitor</a> <img height="13" src="docs-src/static/files/icons/nativescript.svg" /> <a href="https://github.com/herefishyfish/rxdb-nativescript">NativeScript</a> <img height="13" src="docs-src/static/files/icons/flutter.svg" /> <a href="./examples/flutter">Flutter</a> or as an <img height="13" src="docs-src/static/files/icons/electron.svg" /> <a href="https://rxdb.info/electron-database.html">Electron Database</a> </h4> <h2> All the features that you need </h2> Since its beginning in 2018, RxDB has gained a huge set of features and plugins which makes it a flexible full solution regardless of which type of application you are building. Every feature that you need now or might need in the future is already there. <table> <tr> <td> <a href="https://rxdb.info/logger.html">Logging</a><br> <a href="https://rxdb.info/rx-attachment.html">Attachments</a><br> <a href="https://rxdb.info/orm.html">ORM</a><br> <a href="https://rxdb.info/transactions-conflicts-revisions.html">Conflict Handling</a><br> <a href="https://rxdb.info/middleware.html">Middleware</a><br> <a href="https://rxdb.info/reactivity.html">Signals</a> </td> <td> <a href="https://rxdb.info/rx-state.html">State</a><br> <a href="https://rxdb.info/backup.html">Backup</a><br> <a href="https://rxdb.info/replication.html">Replication</a><br> <a href="https://rxdb.info/rx-server.html">Server</a><br> <a href="https://rxdb.info/rx-storage.html">Storages</a><br> <a href="https://rxdb.info/rx-local-document.html">Local Documents</a> </td> <td> <a href="https://rxdb.info/schema-validation.html">Schema Validation</a><br> <a href="https://rxdb.info/key-compression.html">Compression</a><br> <a href="https://rxdb.info/migration-schema.html">Migration</a><br> <a href="https://rxdb.info/encryption.html">Encryption</a><br> <a href="https://rxdb.info/crdt.html">CRDT</a><br> <a href="https://rxdb.info/population.html">Population</a> </td> </tr> </table> <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/rocket.svg">&nbsp;&nbsp;Quick start </h2> #### Install ```sh npm install rxdb rxjs --save ``` #### Store data ```javascript import { createRxDatabase } from 'rxdb/plugins/core'; /** * For browsers, we use the localstorage based storage. * In other JavaScript runtimes, we can use different storages: * @link https://rxdb.info/rx-storage.html */ import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage'; // create a database const db = await createRxDatabase({ name: 'heroesdb', // the name of the database storage: getRxStorageLocalstorage() }); // add collections with a schema await db.addCollections({ heroes: { schema: { version: 0, primaryKey: 'name', type: 'object', properties: { name: { type: 'string', maxLength: 100 }, healthpoints: { type: 'number' } }, required: ['name', 'healthpoints'] } } }); // insert a document await db.heroes.insert({ name: 'Bob', healthpoints: 100 }); ``` #### Query data once ```javascript const aliveHeroes = await db.heroes.find({ selector: { healthpoints: { $gt: 0 } } }).exec(); // the exec() returns the result once ``` #### Observe a Query ```javascript await db.heroes.find({ selector: { healthpoints: { $gt: 0 } } }) .$ // the $ returns an observable that emits each time the result set of the query changes .subscribe(aliveHeroes => console.dir(aliveHeroes)); ``` <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/rocket.svg">&nbsp;&nbsp;Get started </h2> Get started now by [reading the docs](https://rxdb.info/quickstart.html) or exploring the [example-projects](./examples). <h2> <img height="16" width="16" src="./docs-src/static/files/icons/with-gradient/contribute.svg">&nbsp;&nbsp;Support and Contribute </h2> - **Leave a Star ☝️** - [Check out how you can contribute to this project](https://rxdb.info/contribution.html). - [Read this when you have found a bug](./orga/bug-checklist.md) - [Buy access to the premium plugins](https://rxdb.info/premium/) - [Join us at discord to get help](https://rxdb.info/chat) - [Follow us at LinkedIn](https://www.linkedin.com/company/rxdb/) <!-- There are many websites that just exact-copy the readme of github projects. This generates free backlinks so we add some links here to pages that we need to rank better. --> #### More content [Angular Database](https://rxdb.info/articles/angular-database.html), [Frontend Database](https://rxdb.info/articles/frontend-database.html), [localStorage](https://rxdb.info/articles/localstorage.html), [React Database](https://rxdb.info/articles/react-database.html), [Browser Database](https://rxdb.info/articles/browser-database.html), [React Native Database](https://rxdb.info/react-native-database.html), [PWA Database](https://rxdb.info/articles/progressive-web-app-database.html), [In-memory NoSQL database](https://rxdb.info/articles/in-memory-nosql-database.html), [JSON database](https://rxdb.info/articles/json-database.html), [Angular IndexedDB](https://rxdb.info/articles/angular-indexeddb.html), [React IndexedDB](https://rxdb.info/articles/react-indexeddb.html), [Optimistic UI](https://rxdb.info/articles/optimistic-ui.html), [local database](https://rxdb.info/articles/local-database.html), [React Native Encryption](https://rxdb.info/articles/react-native-encryption.html), [Vue Database](https://rxdb.info/articles/vue-database.html), [jQuery Database](https://rxdb.info/articles/jquery-database.html), [Vue IndexedDB](https://rxdb.info/articles/vue-indexeddb.html), [Firestore Alternative](https://rxdb.info/articles/firestore-alternative.html), [Firebase Realtime Database Alternative](https://rxdb.info/articles/firebase-realtime-database-alternative.html), [Ionic Storage](https://rxdb.info/articles/ionic-storage.html) ---------------- View [llms.txt](https://rxdb.info/llms.txt)

JavaScript Libraries & Components NoSQL Databases
23.2K Github Stars
eth-crypto
Open Source

eth-crypto

<p align="center"> <a href="https://twitter.com/pubkeypubkey"> <img src="https://img.shields.io/twitter/follow/pubkeypubkey.svg?style=social&logo=twitter" alt="follow on Twitter"> </a> </p> # eth-crypto Cryptographic javascript-functions for ethereum and tutorials on how to use them together with web3js and solidity. ## Tutorials - **[Creating keys and use them for ethereum transactions](./tutorials/creating-transactions.md)** In this tutorial we will create an ethereum-identity and use it to send transactions to the blockchain. - **[Sign and validate data with solidity](./tutorials/signed-data.md)** In this tutorial we will sign data in javascript and validate the signature inside of a smart-contract. - **[Sending encrypted and signed data to other identities](./tutorials/encrypted-message.md)** In this tutorial we will use the ethereum-identities and asymmetric cryptography to send an encrypted and signed message from Alice to Bob. ## Sponsored by <p align="center"> <a href="https://rxdb.info/?utm_source=github&utm_medium=repo&utm_campaign=github-eth-crypto-readme"> <img src="https://rxdb.info/files/logo/logo_text.svg" alt="JavaScript Database" width="300" /> <br /> <br /> <span>The <b>JavaScript Database</b></span> </a> </p> ## Using eth-crypto ### Install ```bash npm install eth-crypto --save ``` ```javascript // es6 import EthCrypto from 'eth-crypto'; // node const EthCrypto = require('eth-crypto'); ``` ## API - [createIdentity()](#createidentity) - [publicKeyByPrivateKey()](#publickeybyprivatekey) - [publicKey.toAddress()](#publickeytoaddress) - [publicKey.compress()](#publickeycompress) - [publicKey.decompress()](#publickeydecompress) - [sign()](#sign) - [recover()](#recover) - [recoverPublicKey()](#recoverpublickey) - [encryptWithPublicKey()](#encryptwithpublickey) - [decryptWithPrivateKey()](#decryptwithprivatekey) - [cipher.stringify()](#cipherstringify) - [cipher.parse()](#cipherparse) - [signTransaction()](#signtransaction) - [txDataByCompiled()](#txdatabycompiled) - [calculateContractAddress()](#calculatecontractaddress) - [hex compress/decompress](#hex-compressdecompress) ### createIdentity() Creates a new ethereum-identity with privateKey, publicKey and address as hex-string. ```javascript const identity = EthCrypto.createIdentity(); /* > { address: '0x3f243FdacE01Cfd9719f7359c94BA11361f32471', privateKey: '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07', publicKey: 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...' } */ ``` You can also create an identity by providing your own entropy-buffer. Use this with caution, a bad entropy can result in an unsecure private key. ```javascript const entropy = Buffer.from('f2dacf...', 'utf-8'); // must contain at least 128 chars const identity = EthCrypto.createIdentity(entropy); /* > { address: '0x59c8d4d645B0a3b230DE368d815ebDE372d37Ea8', privateKey: '0x18cea40e44624867ddfd775b2898cdb2da29b4be92ee072b9eb02d43b6f2473a', publicKey: '991ce4643653ef452327ee3d1a56af19c84599d340ffd427e784...' } */ ``` ### publicKeyByPrivateKey() Derives the publicKey from a privateKey and returns it as hex-string. ```javascript const publicKey = EthCrypto.publicKeyByPrivateKey( '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07' ); // > 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...' ``` ### publicKey.toAddress() Derives the ethereum-address from the publicKey. ```javascript const address = EthCrypto.publicKey.toAddress( 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...' ); // > '0x3f243FdacE01Cfd9719f7359c94BA11361f32471' ``` ### publicKey.compress() Compresses an uncompressed publicKey. ```javascript const address = EthCrypto.publicKey.compress( '04a34d6aef3eb42335fb3cacb59...' ); // > '03a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b' // compressed keys start with '02' or '03' ``` ### publicKey.decompress() Decompresses a compressed publicKey. ```javascript const address = EthCrypto.publicKey.decompress( '03a34d6aef3eb42335fb3c...' ); // > 'a34d6aef3eb42335fb3cacb5947' // non-compressed keys start with '04' or no prefix ``` ### sign() Signs the hash with the privateKey. Returns the signature as hex-string. ```javascript const message = 'foobar'; const messageHash = EthCrypto.hash.keccak256(message); const signature = EthCrypto.sign( '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07', // privateKey messageHash // hash of message ); // > '0xc04b809d8f33c46ff80c44ba58e866ff0d5..' ``` ### recover() Recovers the signers address from the signature. ```javascript const signer = EthCrypto.recover( '0xc04b809d8f33c46ff80c44ba58e866ff0d5..', EthCrypto.hash.keccak256('foobar') // signed message hash ); // > '0x3f243FdacE01Cfd9719f7359c94BA11361f32471' ``` ### recoverPublicKey() Recovers the signers `publicKey` from the signature. ```javascript const signer = EthCrypto.recoverPublicKey( '0xc04b809d8f33c46ff80c44ba58e866ff0d5..', // signature EthCrypto.hash.keccak256('foobar') // message hash ); // > 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece..' ``` ### encryptWithPublicKey() Encrypts the message with the publicKey so that only the corresponding privateKey can decrypt it. Returns (async) the encrypted data as object with hex-strings. ```javascript const encrypted = await EthCrypto.encryptWithPublicKey( 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...', // publicKey 'foobar' // message ); /* > { iv: '02aeac54cb45283b427bd1a5028552c1', ephemPublicKey: '044acf39ed83c304f19f41ea66615d7a6c0068d5fc48ee181f2fb1091...', ciphertext: '5fbbcc1a44ee19f7499dbc39cfc4ce96', mac: '96490b293763f49a371d3a2040a2d2cb57f246ee88958009fe3c7ef2a38264a1' } */ ``` ### decryptWithPrivateKey() Decrypts the encrypted data with the privateKey. Returns (async) the message as string. ```javascript const message = await EthCrypto.decryptWithPrivateKey( '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07', // privateKey { iv: '02aeac54cb45283b427bd1a5028552c1', ephemPublicKey: '044acf39ed83c304f19f41ea66615d7a6c0068d5fc48ee181f2fb1091...', ciphertext: '5fbbcc1a44ee19f7499dbc39cfc4ce96', mac: '96490b293763f49a371d3a2040a2d2cb57f246ee88958009fe3c7ef2a38264a1' } // encrypted-data ); // 'foobar' ``` ### cipher.stringify() Transforms the object with the encrypted data into a smaller string-representation. ```javascript const str = EthCrypto.cipher.stringify({ iv: '02aeac54cb45283b427bd1a5028552c1', ephemPublicKey: '044acf39ed83c304f19f41ea66615d7a6c0068d5fc48ee181f2fb1091...', ciphertext: '5fbbcc1a44ee19f7499dbc39cfc4ce96', mac: '96490b293763f49a371d3a2040a2d2cb57f246ee88958009fe3c7ef2a38264a1' }); // > '59ab06532fc965b0107977f43e69e5a4038db32099dab281c8f5aece2852...' ``` ### cipher.parse() Parses the string-representation back into the encrypted object. ```javascript const str = EthCrypto.cipher.parse('59ab06532fc965b0107977f43e69e5a4038db32099dab281c8f5aece2852...'); /* > { iv: '02aeac54cb45283b427bd1a5028552c1', ephemPublicKey: '044acf39ed83c304f19f41ea66615d7a6c0068d5fc48ee181f2fb1091...', ciphertext: '5fbbcc1a44ee19f7499dbc39cfc4ce96', mac: '96490b293763f49a371d3a2040a2d2cb57f246ee88958009fe3c7ef2a38264a1' } */ ``` ### signTransaction() Signs a raw transaction with the privateKey. Returns a `Promise` that resolves a serialized tx-string which can be submitted to the node. ```javascript const identity = EthCrypto.createIdentity(); const rawTx = { from: identity.address, to: '0x86Fa049857E0209aa7D9e616F7eb3b3B78ECfdb0', value: new BN('1000000000000000000'), gasPrice: 5000000000, nonce: 0, gasLimit: 21000 }; const signedTx = await EthCrypto.signTransaction( rawTx, identity.privateKey ); console.log(signedTx); // > '071d3a2040a2d2cb...' // you can now send the tx to the node const receipt = await web3.eth.sendSignedTransaction(signedTx); ``` ### txDataByCompiled() Creates the data-string which must be submitted with an transaction to create a contract-instance. ```javascript const SolidityCli = require('solidity-cli'); // create compiled solidity-code const compiled = await SolidityCli.compileCode( 'contract ExampleContract {...' )[':ExampleContract']; const createCode = EthCrypto.txDataByCompiled( compiled.interface, // abi compiled.bytecode, // bytecode [identity.address] // constructor-arguments ); // now you can submit this to the blockchain const serializedTx = await EthCrypto.signTransaction( { from: identity.address, nonce: 0, gasLimit: 5000000, gasPrice: 5000000000, data: createCode }, identity.privateKey ); const receipt = await web3.eth.sendSignedTransaction(serializedTx); ``` ### calculateContractAddress() Calculates the address for the contract from the senders address and the nonce, without deploying it to the blockchain. ```javascript // pre-calculate address const calculatedAddress = EthCrypto.calculateContractAddress( account.address, // address of the sender 3 // nonce with which the contract will be deployed ); const rawTx = { from: account.address, gasPrice: parseInt(gasPrice), nonce: 3, data: compiled.code }; const receipt = await state.web3.eth.sendTransaction(rawTx); console.log(receipt.contractAddress === calculatedAddress); // > true ``` ### hex compress/decompress "Compress" or "decompress" a hex-string to make it smaller. You can either compress to utf16 which reduces the size to about 1/4, or to base64 which reduces the size to about 4/5. This is not a real compression, it just make your string smaller when you have to store it in utf-16 anyways. ```javascript const hexString = '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07'; // 66 chars const utf16 = EthCrypto.hex.compress(hexString); // compress to utf16 // > 'ၻ炞䆷襞ⶬ輦ꂩቊ쮷蛰ﴚ艡Řᨇ' // 16 chars const base64 = EthCrypto.hex.compress(hexString, true); // compress to base64 // > 'EHvpRnCeQbeJXuqfLaz5mKCpEkrLt4bw/RqCYQFYGgc=' // 44 chars EthCrypto.hex.decompress(utf16); // decompress from utf16 // > '0x107be946709e41b7895eea9f2d...' EthCrypto.hex.decompress(base64, true); // decompress from base64 // > '0x107be946709e41b7895eea9f2d...' ```

Developer Tools Security Crypto & Blockchain
916 Github Stars