├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── README.md ├── package.json └── src ├── avatar.js ├── constants.js ├── index.js └── openSeaAPI.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "targets": { 5 | "node": "current" 6 | } 7 | }], 8 | "es2015", 9 | "es2017" 10 | ], 11 | "plugins": ["transform-runtime", "transform-decorators-legacy"] 12 | } 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser" : "babel-eslint", 3 | "extends" : [ 4 | "standard", 5 | "standard-react", 6 | "plugin:promise/recommended" 7 | ], 8 | "plugins": [ 9 | "babel", 10 | "promise", 11 | "react" 12 | ], 13 | "env": { 14 | "browser" : true, 15 | "node" : true, 16 | "mocha" : true, 17 | "jest" : true 18 | }, 19 | "globals" : { 20 | "artifacts": true, 21 | "web3": true, 22 | "assert": true, 23 | "contract": true 24 | }, 25 | "rules": { 26 | 27 | // Strict mode 28 | "strict": [2, "global"], 29 | 30 | // Code style 31 | "indent": [2, 2], 32 | "quotes": [2, "double"], 33 | "no-unused-vars": [1, {}], 34 | "no-use-before-define": 0, 35 | "semi": ["error", "always"], 36 | "eqeqeq": [2, "smart"], 37 | "dot-notation": [2, {"allowKeywords": true, "allowPattern": ""}], 38 | "no-redeclare": [2, {"builtinGlobals": true}], 39 | "no-trailing-spaces": [2, { "skipBlankLines": true }], 40 | "eol-last": 1, 41 | "comma-spacing": [2, {"before": false, "after": true}], 42 | "camelcase": [2, {"properties": "always"}], 43 | "no-mixed-spaces-and-tabs": [2, "smart-tabs"], 44 | "no-dupe-args": 2, 45 | "no-dupe-keys": 2, 46 | "no-debugger": 0, 47 | "no-undef": 2, 48 | "jsx-quotes": [2, "prefer-double"], 49 | "object-curly-spacing": [2, "always"], 50 | "max-len": [2, 120, 2], 51 | "generator-star-spacing": ["error", "before"], 52 | "space-before-function-paren": ["error", "never"], 53 | "react/jsx-no-bind": 0, 54 | "react/jsx-uses-vars": 1, 55 | "react/prop-types": [0, {}] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log 5 | yarn-error.log 6 | 7 | # OSX 8 | .DS_Store 9 | 10 | .env 11 | node_modules 12 | yarn.lock 13 | 14 | lib 15 | dist 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example 2 | webpack.config.js 3 | yarn.lock 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethmoji-js 2 | 3 | Let's developers interact with the Ethmoji game, starting with fetching your users' Ethmoji avatar. 4 | 5 | ##### Installation & Setup: 6 | 7 | ``` 8 | yarn add ethmoji-js 9 | ``` 10 | 11 | ##### Using ES6 style imports (recommended): 12 | 13 | ```javascript 14 | import EthmojiAPI from "ethmoji-js"; 15 | ... 16 | 17 | const ethmojiAPI = new EthmojiAPI(web3.currentProvider); 18 | await ethmojiAPI.init(); 19 | await avatar = ethmojiAPI.getAvatar(ownerAddress); 20 | => Avatar { 21 | name: Ethmoji name 22 | tokenId: Ethmoji tokenId in the smart contract 23 | imageUrl: Ethmoji image url for displaying 24 | ownerAddress: Ethmoji ownerAddress 25 | ownerUsername: Ethmojis ownerUsername on OpenSea 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ethmoji-js", 3 | "version": "0.0.26", 4 | "description": "Add an Ethmoji avatar for users of your dapp", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "npm run build:clean && npm run build:lib", 8 | "build:clean": "rm -rf lib", 9 | "build:lib": "babel src --out-dir lib", 10 | "prepare": "NODE_ENV=production npm run build", 11 | "lint": "eslint --fix src/" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/ProjectOpenSea/ethmoji-js.git" 16 | }, 17 | "keywords": ["ethereum", "ethmoji", "smart-contracts"], 18 | "author": "support@opensea.io", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/ProjectOpenSea/ethmoji-js/issues" 22 | }, 23 | "homepage": "https://github.com/ProjectOpenSea/ethmoji-js#readme", 24 | "dependencies": { 25 | "babel-runtime": "^6.26.0", 26 | "ethmoji-contracts": "^1.0.4", 27 | "humps": "^2.0.1", 28 | "jquery-param": "^1.0.0", 29 | "truffle-contract": "^3.0.4", 30 | "web3": "^0.20.4" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.26.0", 34 | "babel-core": "^6.26.0", 35 | "babel-eslint": "^8.2.2", 36 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 37 | "babel-plugin-transform-runtime": "^6.23.0", 38 | "babel-preset-env": "^1.6.1", 39 | "babel-preset-es2015": "^6.24.1", 40 | "babel-preset-es2017": "^6.24.1", 41 | "babel-preset-react": "^6.24.1", 42 | "babel-preset-stage-2": "^6.24.1", 43 | "babel-preset-stage-3": "^6.24.1", 44 | "eslint": "^4.18.2", 45 | "eslint-config-standard": "^11.0.0", 46 | "eslint-config-standard-react": "^6.0.0", 47 | "eslint-plugin-babel": "^4.1.2", 48 | "eslint-plugin-import": "^2.9.0", 49 | "eslint-plugin-node": "^6.0.1", 50 | "eslint-plugin-promise": "^3.6.0", 51 | "eslint-plugin-react": "^7.7.0", 52 | "eslint-plugin-standard": "^3.0.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/avatar.js: -------------------------------------------------------------------------------- 1 | export default class Avatar { 2 | constructor(attrs = {}) { 3 | this.setAttrs(attrs); 4 | } 5 | 6 | setAttrs(attrs) { 7 | const auction = attrs.auctions[0]; 8 | const ownerSource = auction !== undefined ? auction.seller : attrs.owner; 9 | 10 | this.name = attrs.name || `Ethmoji ${attrs.token_id}`; 11 | this.tokenId = attrs.token_id; 12 | this.imageUrl = attrs.image_url; 13 | this.ownerAddress = (ownerSource || {}).address; 14 | this.ownerUsername = ((ownerSource || {}).user || {}).username; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | const CONTRACT_ADDRESSES = { 2 | live: "0xa6d954d08877f8ce1224f6bfb83484c7d3abf8e9", 3 | rinkeby: "0x40eb59a8cc2d865baf536dd9d0ec3108934afced" 4 | }; 5 | 6 | const OPENSEA_URLS = { 7 | live: "https://opensea.io", 8 | rinkeby: "https://rinkeby.opensea.io" 9 | }; 10 | 11 | const API_URLS = { 12 | live: "https://opensea-api.herokuapp.com", 13 | rinkeby: "https://etherbay-api-1.herokuapp.com" 14 | }; 15 | 16 | export default class Constants { 17 | constructor(network) { 18 | this.network = network; 19 | } 20 | 21 | get baseUrl() { 22 | return OPENSEA_URLS[this.network]; 23 | } 24 | 25 | get apiUrl() { 26 | return API_URLS[this.network]; 27 | } 28 | 29 | get contractAddress() { 30 | return CONTRACT_ADDRESSES[this.network]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Web3 from "web3"; 2 | import contract from "truffle-contract"; 3 | import { Ethmoji } from "ethmoji-contracts"; 4 | 5 | import OpenSeaAPI from "./openSeaAPI"; 6 | import Avatar from "./avatar"; 7 | import Constants from "./constants"; 8 | 9 | const NETWORKS_BY_ID = { 10 | "1": "live", 11 | "2": "morden", 12 | "3": "ropsten", 13 | "4": "rinkeby" 14 | }; 15 | 16 | export default class EthmojiAPI { 17 | constructor(web3Provider) { 18 | if (web3Provider === undefined) { 19 | throw new Error("Web3 provider is undefined"); 20 | } 21 | 22 | this.web3Provider = web3Provider; 23 | this.web3 = new Web3(web3Provider); 24 | } 25 | 26 | async init() { 27 | this.network = await this.getNetwork(); 28 | if (this.network !== "live" && this.network !== "rinkeby") { 29 | throw new Error("Please connect to the Mainnet or Rinkeby"); 30 | } 31 | 32 | this.constants = new Constants(this.network); 33 | this.openSeaAPI = new OpenSeaAPI(this.constants.apiUrl); 34 | 35 | const EthmojiContract = contract(Ethmoji); 36 | EthmojiContract.setProvider(this.web3Provider); 37 | try { 38 | this.contractInstance = await EthmojiContract.at( 39 | this.constants.contractAddress 40 | ); 41 | } catch (error) { 42 | throw new Error(error); 43 | } 44 | } 45 | 46 | async getAvatar(ownerAddress) { 47 | let tokenId; 48 | try { 49 | tokenId = await this.contractInstance.getAvatar(ownerAddress); 50 | } catch (error) { 51 | throw new Error(error); 52 | } 53 | if (tokenId.toString() === "0") return undefined; 54 | try { 55 | const asset = await this.openSeaAPI.get( 56 | `/asset/${this.constants.contractAddress}/${tokenId.toString()}/` 57 | ); 58 | return new Avatar(asset); 59 | } catch (error) { 60 | throw new Error(error); 61 | } 62 | } 63 | 64 | getNetwork() { 65 | return new Promise((resolve, reject) => { 66 | this.web3.version.getNetwork((error, result) => { 67 | if (error) reject(error); 68 | else resolve(NETWORKS_BY_ID[result]); 69 | }); 70 | }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/openSeaAPI.js: -------------------------------------------------------------------------------- 1 | import param from "jquery-param"; 2 | import { decamelizeKeys } from "humps"; 3 | 4 | export default class OpenSeaAPI { 5 | constructor(apiUrl) { 6 | this.apiUrl = apiUrl; 7 | } 8 | 9 | fetch(path) { 10 | return fetch(this.apiUrl.toString() + path.toString(), { 11 | method: "GET", 12 | headers: { 13 | Accept: "application/json", 14 | "Content-Type": "application/json" 15 | } 16 | }); 17 | } 18 | 19 | async get(path, data = {}) { 20 | path += "?" + param(decamelizeKeys(data)); 21 | 22 | let response = await this.fetch(path); 23 | response = this.checkStatus(response); 24 | return response.json(); 25 | } 26 | 27 | checkStatus(response) { 28 | if (!response.ok && response.status === 401) { 29 | throw new Error("Unauthorized"); 30 | } 31 | return response; 32 | } 33 | } 34 | --------------------------------------------------------------------------------