├── .husky ├── .gitignore └── pre-commit ├── abis ├── Math.json ├── EmptyEtherCollateral.json ├── EmptyEtherWrapper.json ├── EscrowChecker.json ├── SafeDecimalMath.json ├── SignedSafeDecimalMath.json ├── EmptyFuturesMarketManager.json ├── SynthUtil.json ├── ReadProxy.json ├── SynthetixBridgeEscrow.json ├── Proxy.json ├── DappMaintenance.json ├── LegacyTokenState.json ├── BinaryOptionMarketFactory.json ├── TokenState.json ├── OwnerRelayOnEthereum.json ├── NativeEtherWrapper.json ├── AddressResolver.json ├── WrapperFactory.json ├── OneNetAggregatorDebtRatio.json ├── OneNetAggregatorIssuedSynths.json └── SynthRedeemer.json ├── .prettierrc.js ├── subgraphs ├── balances.js ├── delegation.graphql ├── latest-rates.js ├── main.js ├── balances.graphql ├── liquidations.graphql ├── delegation.js ├── periodic-updates.js ├── latest-rates.graphql ├── liquidations.js ├── fragments │ └── balances.js ├── periodic-updates.graphql ├── loans.graphql ├── shorts.js ├── exchanger.js ├── wrapper.graphql ├── exchanger.graphql ├── issuance.graphql ├── loans.js ├── utils │ └── network.js ├── wrapper.js ├── shorts.graphql ├── issuance.js └── exchanges.graphql ├── tsconfig.json ├── LICENSE ├── .gitignore ├── package.json ├── scripts └── helpers │ ├── prepare-abis.js │ └── create-contracts.js ├── .eslintrc.js ├── .github └── workflows │ ├── build.yml │ └── updateDependency.yml └── src ├── delegation.ts ├── liquidations.ts ├── lib └── helpers.ts ├── fragments └── balances.ts ├── wrapper.ts └── periodic-updates.ts /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /abis/Math.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /subgraphs/balances.js: -------------------------------------------------------------------------------- 1 | const balances = require('./fragments/balances'); 2 | 3 | const { createSubgraphManifest } = require('./utils/network'); 4 | 5 | module.exports = createSubgraphManifest('balances', balances.dataSources, []); 6 | -------------------------------------------------------------------------------- /subgraphs/delegation.graphql: -------------------------------------------------------------------------------- 1 | type DelegatedWallet @entity { 2 | " authoriser-delegate " 3 | id: ID! 4 | authoriser: Bytes! 5 | delegate: Bytes! 6 | canMint: Boolean 7 | canBurn: Boolean 8 | canClaim: Boolean 9 | canExchange: Boolean 10 | } 11 | -------------------------------------------------------------------------------- /subgraphs/latest-rates.js: -------------------------------------------------------------------------------- 1 | const latestRates = require('./fragments/latest-rates'); 2 | 3 | const { createSubgraphManifest } = require('./utils/network'); 4 | 5 | module.exports = createSubgraphManifest('latest-rates', latestRates.dataSources, latestRates.templates); 6 | -------------------------------------------------------------------------------- /abis/EmptyEtherCollateral.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "totalIssuedSynths", 6 | "outputs": [ 7 | { 8 | "internalType": "uint256", 9 | "name": "", 10 | "type": "uint256" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "pure", 15 | "type": "function" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "strict": true, 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "module": "esnext", 10 | "outDir": "./build", 11 | "target": "es6", 12 | "skipLibCheck": true, 13 | "isolatedModules": true, 14 | "baseUrl": "src", 15 | "lib": ["ESNext"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /abis/EmptyEtherWrapper.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "payable": false, 5 | "stateMutability": "nonpayable", 6 | "type": "constructor" 7 | }, 8 | { 9 | "constant": false, 10 | "inputs": [], 11 | "name": "distributeFees", 12 | "outputs": [], 13 | "payable": false, 14 | "stateMutability": "nonpayable", 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "totalIssuedSynths", 21 | "outputs": [ 22 | { 23 | "internalType": "uint256", 24 | "name": "", 25 | "type": "uint256" 26 | } 27 | ], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /subgraphs/main.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { createSubgraphManifest } = require('./utils/network'); 4 | 5 | const includedSubgraphs = fs.readdirSync(path.join(__dirname, '../subgraphs')).reduce((acc, val) => { 6 | if (val.endsWith('.js') && val !== 'main.js') { 7 | acc.push(val.slice(0, -3)); 8 | } 9 | return acc; 10 | }, []); 11 | 12 | const dataSources = {}; 13 | const templates = {}; 14 | 15 | for (const included of includedSubgraphs) { 16 | const def = require(`./${included}`); 17 | 18 | for (const ds of def.dataSources) { 19 | dataSources[ds.name] = ds; 20 | } 21 | 22 | if (def.templates) { 23 | for (const tl of def.templates) { 24 | templates[tl.name] = tl; 25 | } 26 | } 27 | } 28 | 29 | module.exports = createSubgraphManifest('main', dataSources, templates); 30 | -------------------------------------------------------------------------------- /subgraphs/balances.graphql: -------------------------------------------------------------------------------- 1 | type SynthByCurrencyKey @entity { 2 | " currency key " 3 | id: ID! 4 | proxyAddress: Bytes! 5 | } 6 | 7 | type Synth @entity { 8 | " lowercase address of the proxy contract for the synth " 9 | id: ID! 10 | name: String! 11 | symbol: String! 12 | 13 | totalSupply: BigDecimal! 14 | } 15 | 16 | type SynthBalance @entity { 17 | " timestamp + account + synth address " 18 | id: ID! 19 | amount: BigDecimal! 20 | address: Bytes! 21 | account: String! # using a string here because its ID compatible 22 | timestamp: BigInt! 23 | synth: Synth 24 | } 25 | 26 | " we dont query these entities but only use it to store aggregate data we need during syncing " 27 | type LatestSynthBalance @entity { 28 | " account + synth address " 29 | id: ID! 30 | amount: BigDecimal! 31 | address: Bytes! 32 | account: String! 33 | timestamp: BigInt! 34 | synth: Synth 35 | } 36 | -------------------------------------------------------------------------------- /abis/EscrowChecker.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [ 5 | { 6 | "name": "account", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "checkAccountSchedule", 11 | "outputs": [ 12 | { 13 | "name": "", 14 | "type": "uint256[16]" 15 | } 16 | ], 17 | "payable": false, 18 | "stateMutability": "view", 19 | "type": "function", 20 | "signature": "0x449d0eb1" 21 | }, 22 | { 23 | "constant": true, 24 | "inputs": [], 25 | "name": "synthetix_escrow", 26 | "outputs": [ 27 | { 28 | "name": "", 29 | "type": "address" 30 | } 31 | ], 32 | "payable": false, 33 | "stateMutability": "view", 34 | "type": "function", 35 | "signature": "0x9514c232" 36 | }, 37 | { 38 | "inputs": [ 39 | { 40 | "name": "_esc", 41 | "type": "address" 42 | } 43 | ], 44 | "payable": false, 45 | "stateMutability": "nonpayable", 46 | "type": "constructor" 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Synthetix 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 | -------------------------------------------------------------------------------- /.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 | 63 | build 64 | .vscode 65 | 66 | generated 67 | .DS_Store 68 | test-deploy.sh 69 | -------------------------------------------------------------------------------- /subgraphs/liquidations.graphql: -------------------------------------------------------------------------------- 1 | type AccountFlaggedForLiquidation @entity { 2 | " the deadline plus the staker address " 3 | id: ID! 4 | 5 | " the address of the staker " 6 | account: Bytes! 7 | 8 | " liqudation deadline " 9 | deadline: BigInt! 10 | 11 | " current collateral ratio " 12 | collateralRatio: BigInt! 13 | 14 | " snx that is liquidatable " 15 | liquidatableNonEscrowSNX: BigDecimal! 16 | 17 | " total collateral held by the staker including escrow amount " 18 | collateral: BigDecimal! 19 | } 20 | 21 | type AccountRemovedFromLiquidation @entity { 22 | " the time at which the staker fixed their c-ratio plus the staker address " 23 | id: ID! 24 | 25 | " the address of the staker " 26 | account: Bytes! 27 | 28 | " the time at which the staker fixed their c-ratio " 29 | time: BigInt! 30 | } 31 | 32 | type AccountLiquidated @entity { 33 | id: ID! 34 | 35 | "the liquidated address" 36 | account: Bytes! 37 | 38 | "the amount of SNX redeemed by the liquidator" 39 | snxRedeemed: BigDecimal! 40 | 41 | "the amount of sUSD liquidated" 42 | amountLiquidated: BigDecimal! 43 | 44 | "the address liquidating the account" 45 | liquidator: Bytes! 46 | 47 | "the time at which the liquidation occurred" 48 | time: BigInt! 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "synthetix-subgraph", 3 | "license": "MIT", 4 | "version": "1.2.1", 5 | "author": "Synthetix", 6 | "main": "index.js", 7 | "browser": "index.min.js", 8 | "bin": { 9 | "snxData": "bin.js" 10 | }, 11 | "scripts": { 12 | "lint": "eslint src/ scripts/ subgraphs/", 13 | "prepare": "husky install", 14 | "deploy": "node ./scripts/deploy.js" 15 | }, 16 | "lint-staged": { 17 | "**/*": "prettier --write --ignore-unknown" 18 | }, 19 | "dependencies": { 20 | "@graphprotocol/graph-cli": "0.25.1", 21 | "@graphprotocol/graph-ts": "0.24.1", 22 | "@graphql-tools/merge": "^8.2.1", 23 | "chalk": "4.1.2", 24 | "commander": "8.2.0", 25 | "eslint-config-prettier": "^8.3.0", 26 | "mustache": "4.2.0", 27 | "synthetix": "^2.73.1" 28 | }, 29 | "devDependencies": { 30 | "@typescript-eslint/eslint-plugin": "4.32.0", 31 | "@typescript-eslint/parser": "4.32.0", 32 | "eslint": "7.32.0", 33 | "eslint-import-resolver-typescript": "2.5.0", 34 | "eslint-plugin-import": "2.24.2", 35 | "eslint-plugin-prettier": "4.0.0", 36 | "husky": "7.0.2", 37 | "lint-staged": "11.1.2", 38 | "minify": "7.2.1", 39 | "node-fetch": "^2.6.7", 40 | "prettier": "2.4.1", 41 | "typescript": "4.4.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /subgraphs/delegation.js: -------------------------------------------------------------------------------- 1 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 2 | 3 | const manifest = []; 4 | 5 | getContractDeployments('DelegateApprovals').forEach((a, i) => { 6 | manifest.push({ 7 | kind: 'ethereum/contract', 8 | name: `delegation_DelegateApprovals_${i}`, 9 | network: getCurrentNetwork(), 10 | source: { 11 | address: a.address, 12 | startBlock: a.startBlock, 13 | abi: 'DelegateApprovals', 14 | }, 15 | mapping: { 16 | kind: 'ethereum/events', 17 | apiVersion: '0.0.5', 18 | language: 'wasm/assemblyscript', 19 | file: '../src/delegation.ts', 20 | entities: ['DelegatedWallet'], 21 | abis: [ 22 | { 23 | name: 'DelegateApprovals', 24 | file: '../abis/DelegateApprovals.json', 25 | }, 26 | ], 27 | eventHandlers: [ 28 | { 29 | event: 'Approval(indexed address,address,bytes32)', 30 | handler: 'handleDelegateApproval', 31 | }, 32 | { 33 | event: 'WithdrawApproval(indexed address,address,bytes32)', 34 | handler: 'handleDelegateWithdrawApproval', 35 | }, 36 | ], 37 | }, 38 | }); 39 | }); 40 | 41 | module.exports = createSubgraphManifest('delegation', manifest, []); 42 | -------------------------------------------------------------------------------- /scripts/helpers/prepare-abis.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | 'use strict'; 3 | 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const { gray, yellow } = require('chalk'); 7 | const program = require('commander'); 8 | const snx = require('synthetix'); 9 | 10 | program.action(async () => { 11 | const abiPath = path.join(__dirname, '../../abis'); 12 | const sources = snx.getSource({ network: 'kovan' }); 13 | 14 | const doesEntryHaveMultidimensionalArrays = ({ type }) => /\[[0-9]*\]\[[0-9]*\]/.test(type); 15 | 16 | Object.entries(sources) 17 | .map(([source, { abi }]) => { 18 | const { name } = 19 | abi.find( 20 | ({ inputs = [], outputs = [] }) => 21 | inputs.find(doesEntryHaveMultidimensionalArrays) || outputs.find(doesEntryHaveMultidimensionalArrays), 22 | ) || {}; 23 | if (name) { 24 | console.log(yellow(`Note: Found multidimensional array in ABI and stripping it: ${source}.${name}`)); 25 | abi = abi.filter((entry) => entry.name !== name); 26 | } 27 | 28 | return [source, { abi }]; 29 | }) 30 | .forEach(([source, { abi }]) => { 31 | const targetFile = path.join(abiPath, `${source}.json`); 32 | console.log(gray('Writing ABI:', `${source}.json`)); 33 | fs.writeFileSync(targetFile, JSON.stringify(abi, null, 2) + '\n'); 34 | }); 35 | }); 36 | 37 | program.parse(process.argv); 38 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es6: true, 4 | node: true, 5 | }, 6 | 7 | plugins: ['import'], 8 | extends: [ 9 | 'eslint:recommended', 10 | 'plugin:import/errors', 11 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 12 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 13 | ], 14 | 15 | parserOptions: { 16 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 17 | sourceType: 'module', // Allows for the use of imports 18 | }, 19 | 20 | settings: { 21 | 'import/parsers': { 22 | '@typescript-eslint/parser': ['.ts', '.tsx'], 23 | }, 24 | 'import/resolver': { 25 | // use /tsconfig.json 26 | typescript: { 27 | alwaysTryTypes: true, // always try to resolve types under `@types` directory even it doesn't contain any source code, like `@types/unist` 28 | }, 29 | }, 30 | }, 31 | 32 | rules: { 33 | quotes: ['error', 'single'], 34 | 'import/no-unresolved': 2, 35 | 'no-undef': 0, 36 | 'prefer-const': 0, 37 | semi: ['error', 'always'], 38 | 'no-console': 0, 39 | '@typescript-eslint/explicit-member-accessibility': 0, 40 | '@typescript-eslint/camelcase': 0, 41 | '@typescript-eslint/class-name-casing': 0, 42 | '@typescript-eslint/no-var-requires': 0, 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /subgraphs/periodic-updates.js: -------------------------------------------------------------------------------- 1 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 2 | 3 | const manifest = []; 4 | 5 | getContractDeployments('ProxyERC20').forEach((a, i) => { 6 | manifest.push({ 7 | kind: 'ethereum/contract', 8 | name: `periodicUpdates_ProxyERC20_${i}`, 9 | network: getCurrentNetwork(), 10 | source: { 11 | address: a.address, 12 | startBlock: getCurrentNetwork() === 'mainnet' ? Math.max(13000000, a.startBlock) : a.startBlock, 13 | abi: 'Proxy', 14 | }, 15 | mapping: { 16 | kind: 'ethereum/events', 17 | apiVersion: '0.0.5', 18 | language: 'wasm/assemblyscript', 19 | file: '../src/periodic-updates.ts', 20 | entities: ['DebtState', 'SystemSetting'], 21 | abis: [ 22 | { 23 | name: 'Proxy', 24 | file: '../abis/Proxy.json', 25 | }, 26 | { 27 | name: 'AddressResolver', 28 | file: '../abis/AddressResolver.json', 29 | }, 30 | { 31 | name: 'SynthetixDebtShare', 32 | file: '../abis/SynthetixDebtShare.json', 33 | }, 34 | { 35 | name: 'Synthetix', 36 | file: '../abis/SynthetixGlobalDebt.json', 37 | }, 38 | { 39 | name: 'SystemSettings', 40 | file: '../abis/SystemSettings.json', 41 | }, 42 | ], 43 | blockHandlers: [{ handler: 'handleBlock' }], 44 | }, 45 | }); 46 | }); 47 | 48 | module.exports = createSubgraphManifest('periodic-updates', manifest, []); 49 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: 9 | branches: 10 | - main 11 | - master 12 | 13 | jobs: 14 | build: 15 | name: Build subgraphs 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-node@v2 20 | with: 21 | node-version: '16' 22 | registry-url: 'https://registry.npmjs.org' 23 | - name: Set npm cache directory 24 | run: npm config set cache .npm-cache --global 25 | continue-on-error: true 26 | 27 | - name: Cache node modules 28 | uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # pin@v2 29 | with: 30 | path: | 31 | .npm-cache 32 | node_modules 33 | key: ${{ runner.os }}-alpine-node-${{ hashFiles('**/package-lock.json') }} 34 | restore-keys: | 35 | ${{ runner.os }}-alpine-node- 36 | continue-on-error: true 37 | # issue with github actions, see https://github.com/actions/setup-node/issues/214#issuecomment-810829250 38 | - name: Reconfigure git to use HTTP authentication 39 | run: > 40 | git config --global url."https://github.com/".insteadOf 41 | ssh://git@github.com/ 42 | - run: npm ci 43 | - name: build 44 | run: | 45 | node scripts/deploy.js --build-only -s main -n mainnet 46 | # lint happens after build because it depends on some of the codegen 47 | - run: npm run lint 48 | -------------------------------------------------------------------------------- /scripts/helpers/create-contracts.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const fs = require('fs'); 3 | 4 | const _ = require('lodash'); 5 | 6 | try { 7 | fs.mkdirSync(__dirname + '/../../generated/'); 8 | /* eslint-disable no-empty */ 9 | } catch {} 10 | 11 | const genTs = []; 12 | 13 | genTs.push(` 14 | import { BigInt, Address } from "@graphprotocol/graph-ts"; 15 | 16 | interface ContractInfo { address: string }; 17 | 18 | export function getContractDeployment(contractName: string, network: string, block: BigInt): Address | null { 19 | `); 20 | 21 | for (const network of ['mainnet', 'mainnet-ovm', 'kovan', 'kovan-ovm']) { 22 | const versions = require(`synthetix/publish/deployed/${network}/versions.json`); 23 | 24 | let networkName; 25 | switch (network) { 26 | case 'mainnet': 27 | case 'kovan': 28 | networkName = network; 29 | break; 30 | case 'mainnet-ovm': 31 | networkName = 'optimism'; 32 | break; 33 | case 'kovan-ovm': 34 | networkName = 'optimism-kovan'; 35 | break; 36 | } 37 | 38 | genTs.push(`if (network == '${networkName}') {`); 39 | 40 | for (const vers of _.reverse(_.values(versions))) { 41 | for (const c in vers.contracts) { 42 | genTs.push( 43 | `if (contractName == '${c}') return changetype
(Address.fromHexString('${ 44 | vers.contracts[c].address || '0x0' 45 | }'));`, 46 | ); 47 | } 48 | } 49 | 50 | genTs.push('}'); 51 | } 52 | 53 | genTs.push(` 54 | return null; 55 | } 56 | `); 57 | 58 | fs.writeFileSync(__dirname + '/../../generated/addresses.ts', genTs.join('\n')); 59 | -------------------------------------------------------------------------------- /.github/workflows/updateDependency.yml: -------------------------------------------------------------------------------- 1 | name: Publish Subgraphs 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | synthetix_version: 7 | description: 'Synthetix Repo upstream version (empty for `latest`)' 8 | required: true 9 | subgraph_version: 10 | description: 'Subgraph Repo new version (empty for `patch`)' 11 | default: patch 12 | 13 | jobs: 14 | update_version: 15 | name: Update synthetix in contracts-interface 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-node@v2 20 | with: 21 | node-version: '14' 22 | registry-url: 'https://registry.npmjs.org' 23 | - name: resolve version 24 | run: | 25 | export theversion="patch" 26 | if [ -n "${{ github.event.inputs.monorepo_version }}" ]; then export theversion="${{ github.event.inputs.monorepo_version }}"; fi 27 | echo "Resolved version $theversion" 28 | echo "new_version=$theversion" >> $GITHUB_ENV 29 | - name: set synthetix version 30 | run: npm install --save synthetix@$new_version 31 | - name: update ABIs 32 | run: node scripts/helpers/prepare-abis.js 33 | - run: npm version ${{ github.event.inputs.subgraph_version }} 34 | - name: push new branch & pr 35 | run: | 36 | export VERSION=v$(jq .version < package.json) 37 | gco -b $VERSION 38 | git push origin $VERSION 39 | gh pr create --title "$VERSION" --body "This is an automated release. Fix any errors that occur with the build, as it will be using updated ABIs. Once merged, official subgraphs should be deployed." 40 | -------------------------------------------------------------------------------- /abis/SafeDecimalMath.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "decimals", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "uint8" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function", 15 | "signature": "0x313ce567" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "PRECISE_UNIT", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "uint256" 25 | } 26 | ], 27 | "payable": false, 28 | "stateMutability": "view", 29 | "type": "function", 30 | "signature": "0x864029e7" 31 | }, 32 | { 33 | "constant": true, 34 | "inputs": [], 35 | "name": "unit", 36 | "outputs": [ 37 | { 38 | "name": "", 39 | "type": "uint256" 40 | } 41 | ], 42 | "payable": false, 43 | "stateMutability": "pure", 44 | "type": "function", 45 | "signature": "0x907af6c0" 46 | }, 47 | { 48 | "constant": true, 49 | "inputs": [], 50 | "name": "UNIT", 51 | "outputs": [ 52 | { 53 | "name": "", 54 | "type": "uint256" 55 | } 56 | ], 57 | "payable": false, 58 | "stateMutability": "view", 59 | "type": "function", 60 | "signature": "0x9d8e2177" 61 | }, 62 | { 63 | "constant": true, 64 | "inputs": [], 65 | "name": "preciseUnit", 66 | "outputs": [ 67 | { 68 | "name": "", 69 | "type": "uint256" 70 | } 71 | ], 72 | "payable": false, 73 | "stateMutability": "pure", 74 | "type": "function", 75 | "signature": "0xd5e5e6e6" 76 | }, 77 | { 78 | "constant": true, 79 | "inputs": [], 80 | "name": "highPrecisionDecimals", 81 | "outputs": [ 82 | { 83 | "name": "", 84 | "type": "uint8" 85 | } 86 | ], 87 | "payable": false, 88 | "stateMutability": "view", 89 | "type": "function", 90 | "signature": "0xdef4419d" 91 | } 92 | ] 93 | -------------------------------------------------------------------------------- /abis/SignedSafeDecimalMath.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "PRECISE_UNIT", 6 | "outputs": [ 7 | { 8 | "internalType": "int256", 9 | "name": "", 10 | "type": "int256" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "view", 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "UNIT", 21 | "outputs": [ 22 | { 23 | "internalType": "int256", 24 | "name": "", 25 | "type": "int256" 26 | } 27 | ], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | }, 32 | { 33 | "constant": true, 34 | "inputs": [], 35 | "name": "decimals", 36 | "outputs": [ 37 | { 38 | "internalType": "uint8", 39 | "name": "", 40 | "type": "uint8" 41 | } 42 | ], 43 | "payable": false, 44 | "stateMutability": "view", 45 | "type": "function" 46 | }, 47 | { 48 | "constant": true, 49 | "inputs": [], 50 | "name": "highPrecisionDecimals", 51 | "outputs": [ 52 | { 53 | "internalType": "uint8", 54 | "name": "", 55 | "type": "uint8" 56 | } 57 | ], 58 | "payable": false, 59 | "stateMutability": "view", 60 | "type": "function" 61 | }, 62 | { 63 | "constant": true, 64 | "inputs": [], 65 | "name": "preciseUnit", 66 | "outputs": [ 67 | { 68 | "internalType": "int256", 69 | "name": "", 70 | "type": "int256" 71 | } 72 | ], 73 | "payable": false, 74 | "stateMutability": "pure", 75 | "type": "function" 76 | }, 77 | { 78 | "constant": true, 79 | "inputs": [], 80 | "name": "unit", 81 | "outputs": [ 82 | { 83 | "internalType": "int256", 84 | "name": "", 85 | "type": "int256" 86 | } 87 | ], 88 | "payable": false, 89 | "stateMutability": "pure", 90 | "type": "function" 91 | } 92 | ] 93 | -------------------------------------------------------------------------------- /subgraphs/latest-rates.graphql: -------------------------------------------------------------------------------- 1 | type Candle @entity { 2 | " synth-period-periodId (periodId is timestamp / period) " 3 | id: ID! 4 | " Ticker for synth (e.g. 'sUSD') or 'SNX'" 5 | synth: String! 6 | open: BigDecimal! 7 | high: BigDecimal! 8 | low: BigDecimal! 9 | close: BigDecimal! 10 | average: BigDecimal! 11 | timestamp: BigInt! 12 | " Duration this candle captures in seconds. Year, quarter, month, week, day, hour, and 15 minutes available. " 13 | period: BigInt! 14 | " Number of RateUpdates aggregated into this candle, mostly useful for the indexer to calculate averages " 15 | aggregatedPrices: BigInt! 16 | } 17 | 18 | type InversePricingInfo @entity { 19 | " Name of inverse synth. E.g. iETH " 20 | id: ID! 21 | 22 | " whether or not this inverse synth has been frozen " 23 | frozen: Boolean! 24 | 25 | " configured upper limit " 26 | upperLimit: BigDecimal! 27 | 28 | " configured lower limit " 29 | lowerLimit: BigDecimal! 30 | 31 | " matching price point with long synth " 32 | entryPoint: BigDecimal! 33 | } 34 | 35 | type LatestRate @entity { 36 | " Name of synth. E.g. sUSD " 37 | id: ID! 38 | 39 | " Synth USD rate " 40 | rate: BigDecimal! 41 | 42 | " Address of the aggregator which produces current result " 43 | aggregator: Bytes! 44 | } 45 | 46 | " Latest Rates over time " 47 | type RateUpdate @entity { 48 | " - " 49 | id: ID! 50 | 51 | " currencyKey for which this this rate update applies " 52 | currencyKey: Bytes! 53 | 54 | " currencyKey expressed as a string " 55 | synth: String! 56 | 57 | " the rate recorded at this timestamp " 58 | rate: BigDecimal! 59 | 60 | " the block which this rate was recorded " 61 | block: BigInt! 62 | 63 | " timestamp of the block in which the rate was recorded " 64 | timestamp: BigInt! 65 | } 66 | 67 | type FeeRate @entity { 68 | " string representing the setting name " 69 | id: ID! 70 | 71 | setting: String! 72 | 73 | " name of the synth this record applies to, if any " 74 | synth: String 75 | 76 | " value of the setting " 77 | rate: BigDecimal! 78 | } 79 | 80 | " DEPRECATED: See the Candles entity" 81 | type DailyCandle @entity { 82 | " DEPRECATED: See the Candles entity " 83 | id: ID! 84 | synth: String! 85 | open: BigDecimal! 86 | high: BigDecimal! 87 | low: BigDecimal! 88 | close: BigDecimal! 89 | timestamp: BigInt! 90 | } 91 | -------------------------------------------------------------------------------- /src/delegation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Approval as DelegateApprovalEvent, 3 | WithdrawApproval as DelegateWithdrawApprovalEvent, 4 | } from '../generated/subgraphs/delegation/delegation_DelegateApprovals_0/DelegateApprovals'; 5 | 6 | import { DelegatedWallet } from '../generated/subgraphs/delegation/schema'; 7 | 8 | import { Address, Bytes, log } from '@graphprotocol/graph-ts'; 9 | 10 | function setDelegateApproval(authoriser: Address, delegate: Address, action: Bytes, isApproval: boolean): void { 11 | let id = authoriser.toHex() + '-' + delegate.toHex(); 12 | let delegatedWalletEntity = DelegatedWallet.load(id); 13 | let actionRight = isApproval ? true : false; 14 | if (delegatedWalletEntity == null) { 15 | if (!isApproval) { 16 | return; 17 | } 18 | delegatedWalletEntity = new DelegatedWallet(id); 19 | delegatedWalletEntity.authoriser = authoriser; 20 | delegatedWalletEntity.delegate = delegate; 21 | } 22 | let actionAsString = action.toString(); 23 | if (actionAsString == 'ApproveAll') { 24 | delegatedWalletEntity.canMint = actionRight; 25 | delegatedWalletEntity.canBurn = actionRight; 26 | delegatedWalletEntity.canClaim = actionRight; 27 | delegatedWalletEntity.canExchange = actionRight; 28 | } else if (actionAsString == 'IssueForAddress') { 29 | delegatedWalletEntity.canMint = actionRight; 30 | } else if (actionAsString == 'BurnForAddress') { 31 | delegatedWalletEntity.canBurn = actionRight; 32 | } else if (actionAsString == 'ClaimForAddress') { 33 | delegatedWalletEntity.canClaim = actionRight; 34 | } else if (actionAsString == 'ExchangeForAddress') { 35 | delegatedWalletEntity.canExchange = actionRight; 36 | } else { 37 | log.error('Unknown action "' + actionAsString + '" no entry will be saved.', []); 38 | return; 39 | } 40 | 41 | delegatedWalletEntity.save(); 42 | } 43 | 44 | export function handleDelegateApproval(event: DelegateApprovalEvent): void { 45 | let authoriser = event.params.authoriser; 46 | let delegate = event.params.delegate; 47 | let action = event.params.action; 48 | setDelegateApproval(authoriser, delegate, action, true); 49 | } 50 | 51 | export function handleDelegateWithdrawApproval(event: DelegateWithdrawApprovalEvent): void { 52 | let authoriser = event.params.authoriser; 53 | let delegate = event.params.delegate; 54 | let action = event.params.action; 55 | setDelegateApproval(authoriser, delegate, action, false); 56 | } 57 | -------------------------------------------------------------------------------- /subgraphs/liquidations.js: -------------------------------------------------------------------------------- 1 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 2 | 3 | const manifest = []; 4 | 5 | getContractDeployments('ProxyERC20').forEach((a, i) => { 6 | manifest.push({ 7 | kind: 'ethereum/contract', 8 | name: `liquidations_Synthetix_${i}`, 9 | network: getCurrentNetwork(), 10 | source: { 11 | address: a.address, 12 | startBlock: a.startBlock, 13 | abi: 'Synthetix', 14 | }, 15 | mapping: { 16 | kind: 'ethereum/events', 17 | apiVersion: '0.0.5', 18 | language: 'wasm/assemblyscript', 19 | file: '../src/liquidations.ts', 20 | entities: ['AccountLiquidated'], 21 | abis: [ 22 | { 23 | name: 'Synthetix', 24 | file: '../abis/Synthetix.json', 25 | }, 26 | ], 27 | eventHandlers: [ 28 | { 29 | event: 'AccountLiquidated(indexed address,uint256,uint256,address)', 30 | handler: 'handleAccountLiquidated', 31 | }, 32 | ], 33 | }, 34 | }); 35 | }); 36 | 37 | getContractDeployments('Liquidations').forEach((a, i) => { 38 | manifest.push({ 39 | kind: 'ethereum/contract', 40 | name: `liquidations_Liquidations_${i}`, 41 | network: getCurrentNetwork(), 42 | source: { 43 | address: a.address, 44 | startBlock: a.startBlock, 45 | abi: 'Liquidations', 46 | }, 47 | mapping: { 48 | kind: 'ethereum/events', 49 | apiVersion: '0.0.5', 50 | language: 'wasm/assemblyscript', 51 | file: '../src/liquidations.ts', 52 | entities: ['AccountFlaggedForLiquidation', 'AccountRemovedFromLiquidation'], 53 | abis: [ 54 | { 55 | name: 'Liquidations', 56 | file: '../abis/Liquidations.json', 57 | }, 58 | { 59 | name: 'AddressResolver', 60 | file: '../abis/AddressResolver.json', 61 | }, 62 | { 63 | name: 'Synthetix32', 64 | file: '../abis/Synthetix_bytes32.json', 65 | }, 66 | ], 67 | eventHandlers: [ 68 | { 69 | event: 'AccountFlaggedForLiquidation(indexed address,uint256)', 70 | handler: 'handleAccountFlaggedForLiquidation', 71 | }, 72 | { 73 | event: 'AccountRemovedFromLiquidation(indexed address,uint256)', 74 | handler: 'handleAccountRemovedFromLiquidation', 75 | }, 76 | ], 77 | }, 78 | }); 79 | }); 80 | 81 | module.exports = createSubgraphManifest('liquidations', manifest, []); 82 | -------------------------------------------------------------------------------- /subgraphs/fragments/balances.js: -------------------------------------------------------------------------------- 1 | const { getContractDeployments, getCurrentNetwork, getReleaseInfo } = require('../utils/network'); 2 | 3 | const synthsManifests = []; 4 | 5 | // TODO: added for possible future case of needing to handle these events with templated data source 6 | /*getContractDeployments('Issuer').forEach((a, i) => { 7 | synthsManifests.push({ 8 | kind: 'ethereum/contract', 9 | name: `balances_Issuer_${i}`, 10 | network: getCurrentNetwork(), 11 | source: { 12 | address: a.address, 13 | startBlock: a.startBlock, 14 | abi: 'Issuer', 15 | }, 16 | mapping: { 17 | kind: 'ethereum/events', 18 | apiVersion: '0.0.5', 19 | language: 'wasm/assemblyscript', 20 | file: '../src/fragments/balances.ts', 21 | entities: ['Synth'], 22 | abis: [ 23 | { 24 | name: 'Issuer', 25 | file: '../abis/Issuer.json', 26 | }, 27 | { 28 | name: 'Synth', 29 | file: '../abis/Synth.json', 30 | }, 31 | ], 32 | eventHandlers: [ 33 | { 34 | event: 'SynthAdded(bytes32,address)', 35 | handler: 'handleAddSynth', 36 | }, 37 | { 38 | event: 'SynthRemoved(bytes32,address)', 39 | handler: 'handleRemoveSynth', 40 | }, 41 | ], 42 | }, 43 | }); 44 | });*/ 45 | 46 | const synths = getReleaseInfo('synths'); 47 | 48 | for (const { name } of synths) { 49 | getContractDeployments('Proxy' + (name == 'sUSD' ? 'ERC20' + name : name)).forEach((a, i) => { 50 | synthsManifests.push({ 51 | kind: 'ethereum/contract', 52 | // for some reason sUSD has different contract name 53 | name: `balances_Synth${name}_${i}`, 54 | network: getCurrentNetwork(), 55 | source: { 56 | address: a.address, 57 | startBlock: a.startBlock, 58 | abi: 'Synth', 59 | }, 60 | mapping: { 61 | kind: 'ethereum/events', 62 | apiVersion: '0.0.5', 63 | language: 'wasm/assemblyscript', 64 | file: '../src/fragments/balances.ts', 65 | entities: ['Synth', 'LatestSynthBalance', 'AggregateSynthBalance'], 66 | abis: [ 67 | { 68 | name: 'Synth', 69 | file: '../abis/Synth.json', 70 | }, 71 | ], 72 | eventHandlers: [ 73 | { 74 | event: 'Transfer(indexed address,indexed address,uint256)', 75 | handler: 'handleTransferSynth', 76 | }, 77 | ], 78 | }, 79 | }); 80 | }); 81 | } 82 | 83 | module.exports = { 84 | dataSources: synthsManifests, 85 | }; 86 | -------------------------------------------------------------------------------- /abis/EmptyFuturesMarketManager.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "allMarkets", 6 | "outputs": [ 7 | { 8 | "internalType": "address[]", 9 | "name": "", 10 | "type": "address[]" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "view", 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [ 20 | { 21 | "internalType": "bytes32", 22 | "name": "marketKey", 23 | "type": "bytes32" 24 | } 25 | ], 26 | "name": "marketForKey", 27 | "outputs": [ 28 | { 29 | "internalType": "address", 30 | "name": "", 31 | "type": "address" 32 | } 33 | ], 34 | "payable": false, 35 | "stateMutability": "view", 36 | "type": "function" 37 | }, 38 | { 39 | "constant": true, 40 | "inputs": [ 41 | { 42 | "internalType": "uint256", 43 | "name": "index", 44 | "type": "uint256" 45 | }, 46 | { 47 | "internalType": "uint256", 48 | "name": "pageSize", 49 | "type": "uint256" 50 | } 51 | ], 52 | "name": "markets", 53 | "outputs": [ 54 | { 55 | "internalType": "address[]", 56 | "name": "", 57 | "type": "address[]" 58 | } 59 | ], 60 | "payable": false, 61 | "stateMutability": "view", 62 | "type": "function" 63 | }, 64 | { 65 | "constant": true, 66 | "inputs": [ 67 | { 68 | "internalType": "bytes32[]", 69 | "name": "marketKeys", 70 | "type": "bytes32[]" 71 | } 72 | ], 73 | "name": "marketsForKeys", 74 | "outputs": [ 75 | { 76 | "internalType": "address[]", 77 | "name": "", 78 | "type": "address[]" 79 | } 80 | ], 81 | "payable": false, 82 | "stateMutability": "view", 83 | "type": "function" 84 | }, 85 | { 86 | "constant": true, 87 | "inputs": [], 88 | "name": "numMarkets", 89 | "outputs": [ 90 | { 91 | "internalType": "uint256", 92 | "name": "", 93 | "type": "uint256" 94 | } 95 | ], 96 | "payable": false, 97 | "stateMutability": "view", 98 | "type": "function" 99 | }, 100 | { 101 | "constant": true, 102 | "inputs": [], 103 | "name": "totalDebt", 104 | "outputs": [ 105 | { 106 | "internalType": "uint256", 107 | "name": "debt", 108 | "type": "uint256" 109 | }, 110 | { 111 | "internalType": "bool", 112 | "name": "isInvalid", 113 | "type": "bool" 114 | } 115 | ], 116 | "payable": false, 117 | "stateMutability": "view", 118 | "type": "function" 119 | } 120 | ] 121 | -------------------------------------------------------------------------------- /src/liquidations.ts: -------------------------------------------------------------------------------- 1 | import { Synthetix32 } from '../generated/subgraphs/liquidations/liquidations_Liquidations_0/Synthetix32'; 2 | 3 | import { AddressResolver } from '../generated/subgraphs/liquidations/liquidations_Liquidations_0/AddressResolver'; 4 | 5 | import { 6 | AccountFlaggedForLiquidation as AccountFlaggedForLiquidationEvent, 7 | AccountRemovedFromLiquidation as AccountRemovedFromLiquidationEvent, 8 | Liquidations, 9 | } from '../generated/subgraphs/liquidations/liquidations_Liquidations_0/Liquidations'; 10 | 11 | import { AccountLiquidated as AccountLiquidatedEvent } from '../generated/subgraphs/liquidations/liquidations_Synthetix_0/Synthetix'; 12 | 13 | import { 14 | AccountFlaggedForLiquidation, 15 | AccountRemovedFromLiquidation, 16 | AccountLiquidated, 17 | } from '../generated/subgraphs/liquidations/schema'; 18 | 19 | import { strToBytes, toDecimal } from './lib/helpers'; 20 | 21 | export function handleAccountFlaggedForLiquidation(event: AccountFlaggedForLiquidationEvent): void { 22 | let liquidationsContract = Liquidations.bind(event.address); 23 | let resolver = AddressResolver.bind(liquidationsContract.resolver()); 24 | let synthetix = Synthetix32.bind(resolver.getAddress(strToBytes('Synthetix', 32))); 25 | let accountFlaggedForLiquidation = new AccountFlaggedForLiquidation( 26 | event.params.deadline.toString() + '-' + event.params.account.toHex(), 27 | ); 28 | accountFlaggedForLiquidation.account = event.params.account; 29 | accountFlaggedForLiquidation.deadline = event.params.deadline; 30 | accountFlaggedForLiquidation.collateralRatio = synthetix.collateralisationRatio(event.params.account); 31 | accountFlaggedForLiquidation.collateral = toDecimal(synthetix.collateral(event.params.account)); 32 | accountFlaggedForLiquidation.liquidatableNonEscrowSNX = toDecimal(synthetix.balanceOf(event.params.account)); 33 | accountFlaggedForLiquidation.save(); 34 | } 35 | 36 | export function handleAccountRemovedFromLiquidation(event: AccountRemovedFromLiquidationEvent): void { 37 | let accountRemovedFromLiquidation = new AccountRemovedFromLiquidation( 38 | event.params.time.toString() + '-' + event.params.account.toHex(), 39 | ); 40 | accountRemovedFromLiquidation.account = event.params.account; 41 | accountRemovedFromLiquidation.time = event.params.time; 42 | accountRemovedFromLiquidation.save(); 43 | } 44 | 45 | export function handleAccountLiquidated(event: AccountLiquidatedEvent): void { 46 | let entity = new AccountLiquidated(event.transaction.hash.toHex() + '-' + event.logIndex.toString()); 47 | 48 | entity.account = event.params.account; 49 | entity.snxRedeemed = toDecimal(event.params.snxRedeemed); 50 | entity.amountLiquidated = toDecimal(event.params.amountLiquidated); 51 | entity.liquidator = event.params.liquidator; 52 | entity.time = event.block.timestamp; 53 | 54 | entity.save(); 55 | } 56 | -------------------------------------------------------------------------------- /src/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import { BigDecimal, BigInt, Bytes, ByteArray, log, Address, dataSource } from '@graphprotocol/graph-ts'; 2 | 3 | import { LatestRate, FeeRate } from '../../generated/subgraphs/latest-rates/schema'; 4 | import { initFeed, initFeeRate } from '../fragments/latest-rates'; 5 | import { getContractDeployment } from '../../generated/addresses'; 6 | 7 | export let ZERO = BigInt.fromI32(0); 8 | export let ONE = BigInt.fromI32(1); 9 | 10 | export let ZERO_ADDRESS = changetype
(Address.fromHexString('0x0000000000000000000000000000000000000000')); 11 | export let FEE_ADDRESS = changetype
(Address.fromHexString('0xfeefeefeefeefeefeefeefeefeefeefeefeefeef')); 12 | 13 | export let FIFTEEN_MINUTE_SECONDS = BigInt.fromI32(900); 14 | export let DAY_SECONDS = BigInt.fromI32(86400); 15 | export let YEAR_SECONDS = BigInt.fromI32(31556736); 16 | 17 | export let CANDLE_PERIODS: BigInt[] = [ 18 | YEAR_SECONDS, 19 | YEAR_SECONDS.div(BigInt.fromI32(4)), 20 | YEAR_SECONDS.div(BigInt.fromI32(12)), 21 | DAY_SECONDS.times(BigInt.fromI32(7)), 22 | DAY_SECONDS, 23 | FIFTEEN_MINUTE_SECONDS.times(BigInt.fromI32(4)), 24 | FIFTEEN_MINUTE_SECONDS, 25 | ]; 26 | 27 | export function toDecimal(value: BigInt, decimals: u32 = 18): BigDecimal { 28 | let precision = BigInt.fromI32(10) 29 | .pow(decimals) 30 | .toBigDecimal(); 31 | 32 | return value.divDecimal(precision); 33 | } 34 | 35 | export function strToBytes(str: string, length: i32 = 32): Bytes { 36 | return Bytes.fromByteArray(Bytes.fromUTF8(str)); 37 | } 38 | 39 | export let sUSD32 = strToBytes('sUSD', 32); 40 | export let sUSD4 = strToBytes('sUSD', 4); 41 | 42 | export function getTimeID(timestamp: BigInt, num: BigInt): BigInt { 43 | let remainder = timestamp.mod(num); 44 | return timestamp.minus(remainder); 45 | } 46 | 47 | export function getUSDAmountFromAssetAmount(amount: BigInt, rate: BigDecimal): BigDecimal { 48 | let decimalAmount = toDecimal(amount); 49 | return decimalAmount.times(rate); 50 | } 51 | 52 | export function getLatestRate(synth: string, txHash: string): BigDecimal | null { 53 | let latestRate = LatestRate.load(synth); 54 | if (latestRate == null) { 55 | log.warning('latest rate missing for synth: {}, in tx hash: {}', [synth, txHash]); 56 | 57 | // load feed for the first time, and use contract call to get rate 58 | return initFeed(synth); 59 | } 60 | return latestRate.rate; 61 | } 62 | 63 | export function getExchangeFee(type: string, synth: string): BigDecimal { 64 | let rate = FeeRate.load(type + '-' + synth); 65 | if (rate == null) { 66 | log.warning('atomic exchange rate missing for synth: {}', [synth]); 67 | 68 | // load feed for the first time, and use contract call to get rate 69 | return initFeeRate(type, synth); 70 | } 71 | return rate.rate; 72 | } 73 | 74 | export function isEscrow(holder: string, network: string): boolean { 75 | return ( 76 | getContractDeployment('SynthetixEscrow', dataSource.network(), BigInt.fromI32(1000000000))!.toHexString() == 77 | holder || 78 | getContractDeployment('RewardEscrow', dataSource.network(), BigInt.fromI32(1000000000))!.toHexString() == holder 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /subgraphs/periodic-updates.graphql: -------------------------------------------------------------------------------- 1 | type DebtState @entity { 2 | " Global historical debt entry index " 3 | id: ID! 4 | 5 | period: BigInt! 6 | 7 | " time at which these values are recorded " 8 | timestamp: BigInt! 9 | 10 | " representation of total amount of debt issued over time. increases or decreases proportionally whenever synths are minted/burned " 11 | debtEntry: BigDecimal! 12 | 13 | " current value of all issued synths which this debt pool is responsible for. fluctuates based on the synth breakdown of the system * exchange rates " 14 | totalIssuedSynths: BigDecimal! 15 | 16 | " totalIssuedSynths / debtEntry - useful for tracking debt over time " 17 | debtRatio: BigDecimal! 18 | } 19 | 20 | type SystemSetting @entity { 21 | id: ID! 22 | 23 | " time at which these values are recorded " 24 | timestamp: BigInt! 25 | 26 | " SIP-37 Fee Reclamation: The number of seconds after an exchange is executed that must be waited before settlement. " 27 | waitingPeriodSecs: BigInt! 28 | 29 | " SIP-65 Decentralized Circuit Breaker: The factor amount expressed in decimal format E.g. 3e18 = factor 3, meaning movement up to 3x and above or down to 1/3x and below " 30 | priceDeviationThresholdFactor: BigDecimal! 31 | 32 | " The raio of collateral Expressed in 18 decimals. So 800% cratio is 100/800 = 0.125 (0.125e18) " 33 | issuanceRatio: BigDecimal! 34 | 35 | " How long a fee period lasts at a minimum. It is required for anyone to roll over the periods, so they are not guaranteed to roll over at exactly this duration, but the contract enforces that they cannot roll over any quicker than this duration. " 36 | feePeriodDuration: BigInt! 37 | 38 | " Users are unable to claim fees if their collateralisation ratio drifts out of target threshold " 39 | targetThreshold: BigDecimal! 40 | 41 | " SIP-15 Liquidations: liquidation time delay after address flagged (seconds) " 42 | liquidationDelay: BigInt! 43 | 44 | " SIP-15 Liquidations: issuance ratio when account can be flagged for liquidation (with 18 decimals), e.g 0.5 issuance ratio when flag means 1/0.5 = 200% cratio " 45 | liquidationRatio: BigDecimal! 46 | 47 | " SIP-15 Liquidations: penalty taken away from target of liquidation (with 18 decimals). E.g. 10% is 0.1e18 " 48 | liquidationPenalty: BigDecimal! 49 | 50 | " How long will the ExchangeRates contract assume the rate of any asset is correct " 51 | rateStalePeriod: BigInt! 52 | 53 | minimumStakeTime: BigInt! 54 | 55 | debtSnapshotStaleTime: BigInt! 56 | 57 | aggregatorWarningFlags: String! 58 | 59 | " SIP 112: ETH Wrappr: The maximum amount of ETH held by the EtherWrapper. " 60 | etherWrapperMaxETH: BigDecimal! 61 | 62 | " SIP 112: ETH Wrappr: The fee for depositing ETH into the EtherWrapper. " 63 | etherWrapperMintFeeRate: BigDecimal! 64 | 65 | " SIP 112: ETH Wrappr: The fee for burning sETH and releasing ETH from the EtherWrapper. " 66 | etherWrapperBurnFeeRate: BigDecimal! 67 | 68 | " SIP-120 Atomic exchanges: max allowed volume per block for atomic exchanges " 69 | atomicMaxVolumePerBlock: BigInt! 70 | 71 | " SIP-120 Atomic exchanges: time window (in seconds) for TWAP prices when considered for atomic exchanges " 72 | atomicTwapWindow: BigInt! 73 | } 74 | -------------------------------------------------------------------------------- /subgraphs/loans.graphql: -------------------------------------------------------------------------------- 1 | type Loan @entity { 2 | " the loan id " 3 | id: ID! 4 | 5 | " the transaction hash of the loan " 6 | txHash: String! 7 | 8 | " the account receiving the loan " 9 | account: Bytes! 10 | 11 | " the currency of loan amount " 12 | currency: String! 13 | 14 | " the currency of the collateralAmount " 15 | collateralMinted: String! 16 | 17 | " the amount of the loan " 18 | amount: BigDecimal! 19 | 20 | " the amount of collateral " 21 | collateralAmount: BigDecimal! 22 | 23 | " is the loan still open? " 24 | isOpen: Boolean! 25 | 26 | " the timestamp the loan was created " 27 | createdAt: BigInt! 28 | 29 | " the timestamp the loan was closed " 30 | closedAt: BigInt 31 | 32 | " whether the loan has any partial liquidations " 33 | hasPartialLiquidations: Boolean! 34 | } 35 | 36 | type LoanLiquidated @entity { 37 | " the event tx hash plus event log index " 38 | id: ID! 39 | 40 | " the loan id " 41 | loanId: BigInt! 42 | 43 | " the account that created the loan " 44 | account: Bytes! 45 | 46 | " the account that liquidated the loan " 47 | liquidator: Bytes! 48 | 49 | " the timestamp the loan was liquidated " 50 | timestamp: BigInt! 51 | } 52 | 53 | type LoanPartiallyLiquidated @entity { 54 | " the event tx hash plus event log index " 55 | id: ID! 56 | 57 | " the loan id " 58 | loanId: BigInt! 59 | 60 | " the account that created the loan " 61 | account: Bytes! 62 | 63 | " the account that partially liquidated the loan " 64 | liquidator: Bytes! 65 | 66 | " the amount partially liquidated " 67 | liquidatedAmount: BigDecimal! 68 | 69 | " the amount partially liquidated plus the liquidation fee " 70 | liquidatedCollateral: BigDecimal! 71 | 72 | " the timestamp the loan was partially liquidated " 73 | timestamp: BigInt! 74 | } 75 | 76 | type CollateralDeposited @entity { 77 | " the event tx hash plus event log index " 78 | id: ID! 79 | 80 | " the amount of collateral deposited " 81 | collateralAmount: BigDecimal! 82 | 83 | " the total amount of collateral after the deposit is included " 84 | collateralAfter: BigDecimal! 85 | 86 | " the account that created the loan " 87 | account: Bytes! 88 | 89 | " the loan id " 90 | loanId: BigInt! 91 | 92 | " the timestamp collateral was deposited " 93 | timestamp: BigInt! 94 | } 95 | 96 | type CollateralWithdrawn @entity { 97 | " the event tx hash plus event log index " 98 | id: ID! 99 | 100 | " the amount of collateral withdrawn " 101 | amountWithdrawn: BigDecimal! 102 | 103 | " the total amount of collateral after the withdrawal is accounted for " 104 | collateralAfter: BigDecimal! 105 | 106 | " the account that created the loan " 107 | account: Bytes! 108 | 109 | " the loan id " 110 | loanId: BigInt! 111 | 112 | " the timestamp collateral was withdrawn " 113 | timestamp: BigInt! 114 | } 115 | 116 | type LoanRepaid @entity { 117 | " the event tx hash plus event log index " 118 | id: ID! 119 | 120 | " the amount of the loan that was repaid " # what is the measurement 121 | repaidAmount: BigDecimal! 122 | 123 | " the total amount of the loan after the repaid amount is accounted for " 124 | newLoanAmount: BigDecimal! 125 | 126 | " the account that created the loan " 127 | account: Bytes! 128 | 129 | " the loan id " 130 | loanId: BigInt! 131 | 132 | " the timestamp the loan was partially or fully repaid " 133 | timestamp: BigInt! 134 | } 135 | -------------------------------------------------------------------------------- /subgraphs/shorts.js: -------------------------------------------------------------------------------- 1 | const { clone } = require('lodash'); 2 | 3 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 4 | 5 | const latestRates = require('./fragments/latest-rates'); 6 | 7 | const manifest = clone(latestRates.dataSources); 8 | 9 | /** 10 | * NOTE we need to update this file when we start using 11 | * different types of collateral for shorts 12 | */ 13 | 14 | getContractDeployments('CollateralShort').forEach((a, i) => { 15 | manifest.push({ 16 | kind: 'ethereum/contract', 17 | name: `shorts_CollateralShort_${i}`, 18 | network: getCurrentNetwork(), 19 | source: { 20 | address: a.address, 21 | startBlock: a.startBlock, 22 | abi: 'CollateralShort', 23 | }, 24 | mapping: { 25 | kind: 'ethereum/events', 26 | apiVersion: '0.0.5', 27 | language: 'wasm/assemblyscript', 28 | file: '../src/shorts.ts', 29 | entities: ['Short', 'ShortLiquidation', 'ShortCollateralChange', 'ShortLoanChange'], 30 | abis: [ 31 | { 32 | name: 'AddressResolver', 33 | file: '../abis/AddressResolver.json', 34 | }, 35 | { 36 | name: 'ExchangeRates', 37 | file: '../abis/ExchangeRates.json', 38 | }, 39 | { 40 | name: 'AggregatorProxy', 41 | file: '../abis/AggregatorProxy.json', 42 | }, 43 | { 44 | name: 'CollateralShort', 45 | file: '../abis/CollateralShortOVM.json', 46 | }, 47 | ], 48 | eventHandlers: [ 49 | { 50 | event: 'LoanCreated(indexed address,uint256,uint256,uint256,bytes32,uint256)', 51 | handler: 'handleShortLoanCreatedsUSD', 52 | }, 53 | { 54 | event: 'LoanClosed(indexed address,uint256)', 55 | handler: 'handleShortLoanClosedsUSD', 56 | }, 57 | { 58 | event: 'CollateralDeposited(indexed address,uint256,uint256,uint256)', 59 | handler: 'handleShortCollateralDepositedsUSD', 60 | }, 61 | { 62 | event: 'CollateralWithdrawn(indexed address,uint256,uint256,uint256)', 63 | handler: 'handleShortCollateralWithdrawnsUSD', 64 | }, 65 | { 66 | event: 'LoanRepaymentMade(indexed address,indexed address,uint256,uint256,uint256)', 67 | handler: 'handleShortLoanRepaymentMadesUSD', 68 | }, 69 | { 70 | event: 'LoanDrawnDown(indexed address,uint256,uint256)', 71 | handler: 'handleShortLoanDrawnDownsUSD', 72 | }, 73 | { 74 | event: 'LoanPartiallyLiquidated(indexed address,uint256,address,uint256,uint256)', 75 | handler: 'handleLoanPartiallyLiquidatedsUSD', 76 | }, 77 | { 78 | event: 'LoanClosedByLiquidation(indexed address,uint256,indexed address,uint256,uint256)', 79 | handler: 'handleLoanClosedByLiquidationsUSD', 80 | }, 81 | { 82 | event: 'MinCollateralUpdated(uint256)', 83 | handler: 'handleMinCollateralUpdatedsUSD', 84 | }, 85 | { 86 | event: 'IssueFeeRateUpdated(uint256)', 87 | handler: 'handleIssueFeeRateUpdatedsUSD', 88 | }, 89 | { 90 | event: 'CanOpenLoansUpdated(bool)', 91 | handler: 'handleCanOpenLoansUpdatedsUSD', 92 | }, 93 | { 94 | event: 'LoanClosedByRepayment(indexed address,uint256,uint256,uint256)', 95 | handler: 'handleLoanClosedByRepayment', 96 | }, 97 | ], 98 | }, 99 | }); 100 | }); 101 | 102 | module.exports = createSubgraphManifest('shorts', manifest, latestRates.templates); 103 | -------------------------------------------------------------------------------- /abis/SynthUtil.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "resolver", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor", 13 | "signature": "constructor" 14 | }, 15 | { 16 | "constant": true, 17 | "inputs": [], 18 | "name": "addressResolverProxy", 19 | "outputs": [ 20 | { 21 | "internalType": "contract IAddressResolver", 22 | "name": "", 23 | "type": "address" 24 | } 25 | ], 26 | "payable": false, 27 | "stateMutability": "view", 28 | "type": "function", 29 | "signature": "0xd18ab376" 30 | }, 31 | { 32 | "constant": true, 33 | "inputs": [], 34 | "name": "frozenSynths", 35 | "outputs": [ 36 | { 37 | "internalType": "bytes32[]", 38 | "name": "", 39 | "type": "bytes32[]" 40 | } 41 | ], 42 | "payable": false, 43 | "stateMutability": "view", 44 | "type": "function", 45 | "signature": "0xeade6d2d" 46 | }, 47 | { 48 | "constant": true, 49 | "inputs": [ 50 | { 51 | "internalType": "address", 52 | "name": "account", 53 | "type": "address" 54 | } 55 | ], 56 | "name": "synthsBalances", 57 | "outputs": [ 58 | { 59 | "internalType": "bytes32[]", 60 | "name": "", 61 | "type": "bytes32[]" 62 | }, 63 | { 64 | "internalType": "uint256[]", 65 | "name": "", 66 | "type": "uint256[]" 67 | }, 68 | { 69 | "internalType": "uint256[]", 70 | "name": "", 71 | "type": "uint256[]" 72 | } 73 | ], 74 | "payable": false, 75 | "stateMutability": "view", 76 | "type": "function", 77 | "signature": "0xa827bf48" 78 | }, 79 | { 80 | "constant": true, 81 | "inputs": [], 82 | "name": "synthsRates", 83 | "outputs": [ 84 | { 85 | "internalType": "bytes32[]", 86 | "name": "", 87 | "type": "bytes32[]" 88 | }, 89 | { 90 | "internalType": "uint256[]", 91 | "name": "", 92 | "type": "uint256[]" 93 | } 94 | ], 95 | "payable": false, 96 | "stateMutability": "view", 97 | "type": "function", 98 | "signature": "0x27fe55a6" 99 | }, 100 | { 101 | "constant": true, 102 | "inputs": [], 103 | "name": "synthsTotalSupplies", 104 | "outputs": [ 105 | { 106 | "internalType": "bytes32[]", 107 | "name": "", 108 | "type": "bytes32[]" 109 | }, 110 | { 111 | "internalType": "uint256[]", 112 | "name": "", 113 | "type": "uint256[]" 114 | }, 115 | { 116 | "internalType": "uint256[]", 117 | "name": "", 118 | "type": "uint256[]" 119 | } 120 | ], 121 | "payable": false, 122 | "stateMutability": "view", 123 | "type": "function", 124 | "signature": "0x492dbcdd" 125 | }, 126 | { 127 | "constant": true, 128 | "inputs": [ 129 | { 130 | "internalType": "address", 131 | "name": "account", 132 | "type": "address" 133 | }, 134 | { 135 | "internalType": "bytes32", 136 | "name": "currencyKey", 137 | "type": "bytes32" 138 | } 139 | ], 140 | "name": "totalSynthsInKey", 141 | "outputs": [ 142 | { 143 | "internalType": "uint256", 144 | "name": "total", 145 | "type": "uint256" 146 | } 147 | ], 148 | "payable": false, 149 | "stateMutability": "view", 150 | "type": "function", 151 | "signature": "0x0120be33" 152 | } 153 | ] 154 | -------------------------------------------------------------------------------- /abis/ReadProxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor", 13 | "signature": "constructor" 14 | }, 15 | { 16 | "anonymous": false, 17 | "inputs": [ 18 | { 19 | "indexed": false, 20 | "internalType": "address", 21 | "name": "oldOwner", 22 | "type": "address" 23 | }, 24 | { 25 | "indexed": false, 26 | "internalType": "address", 27 | "name": "newOwner", 28 | "type": "address" 29 | } 30 | ], 31 | "name": "OwnerChanged", 32 | "type": "event", 33 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 34 | }, 35 | { 36 | "anonymous": false, 37 | "inputs": [ 38 | { 39 | "indexed": false, 40 | "internalType": "address", 41 | "name": "newOwner", 42 | "type": "address" 43 | } 44 | ], 45 | "name": "OwnerNominated", 46 | "type": "event", 47 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 48 | }, 49 | { 50 | "anonymous": false, 51 | "inputs": [ 52 | { 53 | "indexed": false, 54 | "internalType": "address", 55 | "name": "newTarget", 56 | "type": "address" 57 | } 58 | ], 59 | "name": "TargetUpdated", 60 | "type": "event", 61 | "signature": "0x814250a3b8c79fcbe2ead2c131c952a278491c8f4322a79fe84b5040a810373e" 62 | }, 63 | { 64 | "payable": false, 65 | "stateMutability": "nonpayable", 66 | "type": "fallback" 67 | }, 68 | { 69 | "constant": false, 70 | "inputs": [], 71 | "name": "acceptOwnership", 72 | "outputs": [], 73 | "payable": false, 74 | "stateMutability": "nonpayable", 75 | "type": "function", 76 | "signature": "0x79ba5097" 77 | }, 78 | { 79 | "constant": false, 80 | "inputs": [ 81 | { 82 | "internalType": "address", 83 | "name": "_owner", 84 | "type": "address" 85 | } 86 | ], 87 | "name": "nominateNewOwner", 88 | "outputs": [], 89 | "payable": false, 90 | "stateMutability": "nonpayable", 91 | "type": "function", 92 | "signature": "0x1627540c" 93 | }, 94 | { 95 | "constant": true, 96 | "inputs": [], 97 | "name": "nominatedOwner", 98 | "outputs": [ 99 | { 100 | "internalType": "address", 101 | "name": "", 102 | "type": "address" 103 | } 104 | ], 105 | "payable": false, 106 | "stateMutability": "view", 107 | "type": "function", 108 | "signature": "0x53a47bb7" 109 | }, 110 | { 111 | "constant": true, 112 | "inputs": [], 113 | "name": "owner", 114 | "outputs": [ 115 | { 116 | "internalType": "address", 117 | "name": "", 118 | "type": "address" 119 | } 120 | ], 121 | "payable": false, 122 | "stateMutability": "view", 123 | "type": "function", 124 | "signature": "0x8da5cb5b" 125 | }, 126 | { 127 | "constant": false, 128 | "inputs": [ 129 | { 130 | "internalType": "address", 131 | "name": "_target", 132 | "type": "address" 133 | } 134 | ], 135 | "name": "setTarget", 136 | "outputs": [], 137 | "payable": false, 138 | "stateMutability": "nonpayable", 139 | "type": "function", 140 | "signature": "0x776d1a01" 141 | }, 142 | { 143 | "constant": true, 144 | "inputs": [], 145 | "name": "target", 146 | "outputs": [ 147 | { 148 | "internalType": "address", 149 | "name": "", 150 | "type": "address" 151 | } 152 | ], 153 | "payable": false, 154 | "stateMutability": "view", 155 | "type": "function", 156 | "signature": "0xd4b83992" 157 | } 158 | ] 159 | -------------------------------------------------------------------------------- /subgraphs/exchanger.js: -------------------------------------------------------------------------------- 1 | const { clone } = require('lodash'); 2 | 3 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 4 | 5 | const latestRatesManifests = require('./fragments/latest-rates'); 6 | 7 | const manifest = clone(latestRatesManifests.dataSources); 8 | 9 | // add exchanger events 10 | getContractDeployments('Exchanger').forEach((a, i) => { 11 | manifest.push({ 12 | kind: 'ethereum/contract', 13 | name: `exchanger_Exchanger_${i}`, 14 | network: getCurrentNetwork(), 15 | source: { 16 | address: a.address, 17 | startBlock: a.startBlock, 18 | abi: 'Exchanger', 19 | }, 20 | mapping: { 21 | kind: 'ethereum/events', 22 | apiVersion: '0.0.5', 23 | language: 'wasm/assemblyscript', 24 | file: '../src/exchanger.ts', 25 | entities: ['ExchangeEntrySettled', 'ExchangeEntryAppended', 'TemporaryExchangePartnerTracker'], 26 | abis: [ 27 | { 28 | name: 'Exchanger', 29 | file: '../abis/Exchanger.json', 30 | }, 31 | ], 32 | eventHandlers: [ 33 | { 34 | event: 35 | 'ExchangeEntrySettled(indexed address,bytes32,uint256,bytes32,uint256,uint256,uint256,uint256,uint256)', 36 | handler: 'handleExchangeEntrySettled', 37 | }, 38 | { 39 | event: 'ExchangeEntryAppended(indexed address,bytes32,uint256,bytes32,uint256,uint256,uint256,uint256)', 40 | handler: 'handleExchangeEntryAppended', 41 | }, 42 | ], 43 | }, 44 | }); 45 | }); 46 | 47 | // add synthetix (only the new contracts) 48 | getContractDeployments('ProxyERC20').forEach((a, i) => { 49 | if (i === 0 && (getCurrentNetwork() !== 'optimism' || getCurrentNetwork() !== 'optimism-kovan')) { 50 | manifest.push({ 51 | kind: 'ethereum/contract', 52 | name: 'exchanger_SynthetixOldTracking', 53 | network: getCurrentNetwork(), 54 | source: { 55 | address: a.address, 56 | startBlock: a.startBlock, 57 | abi: 'SynthetixOldTracking', 58 | }, 59 | mapping: { 60 | kind: 'ethereum/events', 61 | apiVersion: '0.0.5', 62 | language: 'wasm/assemblyscript', 63 | file: '../src/exchanger.ts', 64 | entities: ['DailyExchangePartner', 'ExchangePartner', 'TemporaryExchangePartnerTracker'], 65 | abis: [ 66 | { 67 | name: 'SynthetixOldTracking', 68 | file: '../abis/Synthetix_oldTracking.json', 69 | }, 70 | ], 71 | eventHandlers: [ 72 | { 73 | event: 'ExchangeTracking(indexed bytes32,bytes32,uint256)', 74 | handler: 'handleExchangeTrackingV1', 75 | }, 76 | ], 77 | }, 78 | }); 79 | } 80 | manifest.push({ 81 | kind: 'ethereum/contract', 82 | name: `exchanger_Synthetix_${i}`, 83 | network: getCurrentNetwork(), 84 | source: { 85 | address: a.address, 86 | startBlock: 87 | i === 0 && getCurrentNetwork() === 'mainnet' 88 | ? Math.max(parseInt(process.env.SNX_START_BLOCK || '0'), 12733161) 89 | : a.startBlock, 90 | abi: 'Synthetix', 91 | }, 92 | mapping: { 93 | kind: 'ethereum/events', 94 | apiVersion: '0.0.5', 95 | language: 'wasm/assemblyscript', 96 | file: '../src/exchanger.ts', 97 | entities: ['DailyExchangePartner', 'ExchangePartner', 'TemporaryExchangePartnerTracker'], 98 | abis: [ 99 | { 100 | name: 'Synthetix', 101 | file: '../abis/Synthetix.json', 102 | }, 103 | ], 104 | eventHandlers: [ 105 | { 106 | event: 'ExchangeTracking(indexed bytes32,bytes32,uint256,uint256)', 107 | handler: 'handleExchangeTrackingV2', 108 | }, 109 | ], 110 | }, 111 | }); 112 | }); 113 | 114 | module.exports = createSubgraphManifest('exchanger', manifest, latestRatesManifests.templates); 115 | -------------------------------------------------------------------------------- /abis/SynthetixBridgeEscrow.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor", 13 | "signature": "constructor" 14 | }, 15 | { 16 | "anonymous": false, 17 | "inputs": [ 18 | { 19 | "indexed": false, 20 | "internalType": "address", 21 | "name": "_token", 22 | "type": "address" 23 | }, 24 | { 25 | "indexed": true, 26 | "internalType": "address", 27 | "name": "spender", 28 | "type": "address" 29 | }, 30 | { 31 | "indexed": false, 32 | "internalType": "uint256", 33 | "name": "value", 34 | "type": "uint256" 35 | } 36 | ], 37 | "name": "BridgeApproval", 38 | "type": "event", 39 | "signature": "0x303c1899feacd9139708637ad42646489d4e1be45f252b25b53561e934d74712" 40 | }, 41 | { 42 | "anonymous": false, 43 | "inputs": [ 44 | { 45 | "indexed": false, 46 | "internalType": "address", 47 | "name": "oldOwner", 48 | "type": "address" 49 | }, 50 | { 51 | "indexed": false, 52 | "internalType": "address", 53 | "name": "newOwner", 54 | "type": "address" 55 | } 56 | ], 57 | "name": "OwnerChanged", 58 | "type": "event", 59 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 60 | }, 61 | { 62 | "anonymous": false, 63 | "inputs": [ 64 | { 65 | "indexed": false, 66 | "internalType": "address", 67 | "name": "newOwner", 68 | "type": "address" 69 | } 70 | ], 71 | "name": "OwnerNominated", 72 | "type": "event", 73 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 74 | }, 75 | { 76 | "constant": false, 77 | "inputs": [], 78 | "name": "acceptOwnership", 79 | "outputs": [], 80 | "payable": false, 81 | "stateMutability": "nonpayable", 82 | "type": "function", 83 | "signature": "0x79ba5097" 84 | }, 85 | { 86 | "constant": false, 87 | "inputs": [ 88 | { 89 | "internalType": "address", 90 | "name": "_token", 91 | "type": "address" 92 | }, 93 | { 94 | "internalType": "address", 95 | "name": "_bridge", 96 | "type": "address" 97 | }, 98 | { 99 | "internalType": "uint256", 100 | "name": "_amount", 101 | "type": "uint256" 102 | } 103 | ], 104 | "name": "approveBridge", 105 | "outputs": [], 106 | "payable": false, 107 | "stateMutability": "nonpayable", 108 | "type": "function", 109 | "signature": "0x38e499cd" 110 | }, 111 | { 112 | "constant": false, 113 | "inputs": [ 114 | { 115 | "internalType": "address", 116 | "name": "_owner", 117 | "type": "address" 118 | } 119 | ], 120 | "name": "nominateNewOwner", 121 | "outputs": [], 122 | "payable": false, 123 | "stateMutability": "nonpayable", 124 | "type": "function", 125 | "signature": "0x1627540c" 126 | }, 127 | { 128 | "constant": true, 129 | "inputs": [], 130 | "name": "nominatedOwner", 131 | "outputs": [ 132 | { 133 | "internalType": "address", 134 | "name": "", 135 | "type": "address" 136 | } 137 | ], 138 | "payable": false, 139 | "stateMutability": "view", 140 | "type": "function", 141 | "signature": "0x53a47bb7" 142 | }, 143 | { 144 | "constant": true, 145 | "inputs": [], 146 | "name": "owner", 147 | "outputs": [ 148 | { 149 | "internalType": "address", 150 | "name": "", 151 | "type": "address" 152 | } 153 | ], 154 | "payable": false, 155 | "stateMutability": "view", 156 | "type": "function", 157 | "signature": "0x8da5cb5b" 158 | } 159 | ] 160 | -------------------------------------------------------------------------------- /subgraphs/wrapper.graphql: -------------------------------------------------------------------------------- 1 | type Wrapper @entity { 2 | " wrapper address " 3 | id: ID! 4 | 5 | " address of wrapped token, empty if ETH" 6 | tokenAddress: String! 7 | 8 | " the current amount of synths minted by this wrapper" 9 | amount: BigDecimal! 10 | 11 | " the current amount of synths minted by this wrapper in USD" 12 | amountInUSD: BigDecimal! 13 | 14 | " the maximum amount of synths that can be minted by this wrapper" 15 | maxAmount: BigDecimal! 16 | 17 | " the currency key of this wrapper " 18 | currencyKey: String! 19 | 20 | " the total amount of fees generated by this wrapper " 21 | totalFees: BigDecimal! 22 | 23 | " the total amount of fees generated by this wrapper in USD" 24 | totalFeesInUSD: BigDecimal! 25 | } 26 | 27 | type WrapperMint @entity { 28 | " the transaction hash with a log index appended " 29 | id: ID! 30 | 31 | " address of the user minting " 32 | account: String! 33 | 34 | " amount of synth minted " 35 | principal: BigDecimal! 36 | 37 | " amount of fees collected " 38 | fee: BigDecimal! 39 | 40 | " total amount added to the wrapper " 41 | amountIn: BigDecimal! 42 | 43 | " the timestamp of the block that includes this event " 44 | timestamp: BigInt! 45 | 46 | " the address of the wrapper that minted this synth " 47 | wrapperAddress: String! 48 | } 49 | 50 | type WrapperBurn @entity { 51 | " the transaction hash with a log index appended " 52 | id: ID! 53 | 54 | " address of the user burning " 55 | account: String! 56 | 57 | " amount of synth burned " 58 | principal: BigDecimal! 59 | 60 | " amount of fees collected " 61 | fee: BigDecimal! 62 | 63 | " total amount removed from the wrapper " 64 | amountOut: BigDecimal! 65 | 66 | " the timestamp of the block that includes this event " 67 | timestamp: BigInt! 68 | 69 | " the address of the wrapper that burned this synth " 70 | wrapperAddress: String! 71 | } 72 | 73 | " we dont query this entity but only use it to store aggregate data we need during syncing " 74 | type InversePricingInfo @entity { 75 | " Name of inverse synth. E.g. iETH " 76 | id: ID! 77 | 78 | " whether or not this inverse synth has been frozen " 79 | frozen: Boolean! 80 | 81 | " configured upper limit " 82 | upperLimit: BigDecimal! 83 | 84 | " configured lower limit " 85 | lowerLimit: BigDecimal! 86 | 87 | " matching price point with long synth " 88 | entryPoint: BigDecimal! 89 | } 90 | 91 | type LatestRate @entity { 92 | " Name of synth. E.g. sUSD " 93 | id: ID! 94 | 95 | " Synth USD rate " 96 | rate: BigDecimal! 97 | 98 | " Address of the aggregator which produces current result " 99 | aggregator: Bytes! 100 | } 101 | 102 | " Latest Rates over time " 103 | type RateUpdate @entity { 104 | " - " 105 | id: ID! 106 | " currencyKey for which this this rate update applies " 107 | currencyKey: Bytes! 108 | " currencyKey expressed as a string " 109 | synth: String! 110 | " the rate recorded at this timestamp " 111 | rate: BigDecimal! 112 | " the block which this rate was recorded " 113 | block: BigInt! 114 | " timestamp of the block in which the rate was recorded " 115 | timestamp: BigInt! 116 | } 117 | 118 | type Candle @entity { 119 | " synth-period-periodId (periodId is timestamp / period) " 120 | id: ID! 121 | " Ticker for synth (e.g. 'sUSD') or 'SNX'" 122 | synth: String! 123 | open: BigDecimal! 124 | high: BigDecimal! 125 | low: BigDecimal! 126 | close: BigDecimal! 127 | average: BigDecimal! 128 | timestamp: BigInt! 129 | " Duration this candle captures in seconds. Year, quarter, month, week, day, hour, and 15 minutes available. " 130 | period: BigInt! 131 | " Number of RateUpdates aggregated into this candle, mostly useful for the indexer to calculate averages " 132 | aggregatedPrices: BigInt! 133 | } 134 | 135 | " DEPRECATED: See the Candles entity" 136 | type DailyCandle @entity { 137 | " DEPRECATED: See the Candles entity " 138 | id: ID! 139 | synth: String! 140 | open: BigDecimal! 141 | high: BigDecimal! 142 | low: BigDecimal! 143 | close: BigDecimal! 144 | timestamp: BigInt! 145 | } 146 | -------------------------------------------------------------------------------- /subgraphs/exchanger.graphql: -------------------------------------------------------------------------------- 1 | type ExchangeEntrySettled @entity { 2 | " transaction hash and log index " 3 | id: ID! 4 | 5 | " synth exchanged from " 6 | from: Bytes! 7 | 8 | " synth exchanged to " 9 | src: Bytes! 10 | 11 | " number of units of synth from exchanged " 12 | amount: BigDecimal! 13 | 14 | " address which receives the settlement " 15 | dest: Bytes! 16 | 17 | " amount reclaimed of dest due to underpayment " 18 | reclaim: BigDecimal! 19 | 20 | " amount returned of dest due to overpayment " 21 | rebate: BigDecimal! 22 | 23 | " aggregator price round for src synth " 24 | srcRoundIdAtPeriodEnd: BigInt! 25 | 26 | " aggregator price round for dest synth " 27 | destRoundIdAtPeriodEnd: BigInt! 28 | 29 | " time when the original exchange occured " 30 | exchangeTimestamp: BigInt! 31 | } 32 | 33 | type ExchangeEntryAppended @entity { 34 | " transaction hash and log index " 35 | id: ID! # the transaction hash plus event log 36 | " ethereum address which funded the exchange " 37 | account: Bytes! 38 | 39 | " synth exchanged from " 40 | src: Bytes! 41 | 42 | " number of units of synth from exchanged " 43 | amount: BigDecimal! 44 | 45 | " synth exchanged to " 46 | dest: Bytes! 47 | 48 | " number of units of synth to received " 49 | amountReceived: BigDecimal! 50 | 51 | " fee paid in sUSD to the synthetix fee pool " 52 | exchangeFeeRate: BigDecimal! 53 | 54 | " aggregator price round for src synth " 55 | roundIdForSrc: BigInt! 56 | 57 | " aggregator price round for dest synth " 58 | roundIdForDest: BigInt! 59 | } 60 | 61 | type TemporaryExchangePartnerTracker @entity { 62 | " Transaction hash of the Exchange event " 63 | id: ID! 64 | 65 | " Total transaction volume in USD across all ExchangeEntryAppended events in a single tx hash " 66 | usdVolume: BigDecimal 67 | 68 | " Total fees from this transaction hash " 69 | usdFees: BigDecimal 70 | 71 | " String format of the tracking code for a given partner " 72 | partner: String 73 | } 74 | 75 | type DailyExchangePartner @entity { 76 | " Day timestamp + tracking code of the partner " 77 | id: ID! 78 | 79 | " Total transaction volume in USD for the partner on this day " 80 | usdVolume: BigDecimal! 81 | 82 | " Total fees generated by the volume partner for this day " 83 | usdFees: BigDecimal! 84 | 85 | " Total number of trades from the volume partner for this day " 86 | trades: BigInt! 87 | 88 | " Tracking code of the partner " 89 | partner: String! 90 | 91 | " unix timestamp at the beginning of the day " 92 | timestamp: BigInt! 93 | } 94 | 95 | type ExchangePartner @entity { 96 | " Tracking code of the partner " 97 | id: ID! 98 | 99 | " Total transaction volume in USD for the partner " 100 | usdVolume: BigDecimal! 101 | 102 | " Total fees generated by the volume partner " 103 | usdFees: BigDecimal! 104 | 105 | " Total number of trades from the volume partner " 106 | trades: BigInt! 107 | } 108 | 109 | type InversePricingInfo @entity { 110 | " Name of inverse synth. E.g. iETH " 111 | id: ID! 112 | 113 | " whether or not this inverse synth has been frozen " 114 | frozen: Boolean! 115 | 116 | " configured upper limit " 117 | upperLimit: BigDecimal! 118 | 119 | " configured lower limit " 120 | lowerLimit: BigDecimal! 121 | 122 | " matching price point with long synth " 123 | entryPoint: BigDecimal! 124 | } 125 | 126 | type LatestRate @entity { 127 | " Name of synth. E.g. sUSD " 128 | id: ID! 129 | 130 | " Synth USD rate " 131 | rate: BigDecimal! 132 | 133 | " Address of the aggregator which produces current result " 134 | aggregator: Bytes! 135 | } 136 | 137 | " Latest Rates over time " 138 | type RateUpdate @entity { 139 | " - " 140 | id: ID! 141 | " currencyKey for which this this rate update applies " 142 | currencyKey: Bytes! 143 | " currencyKey expressed as a string " 144 | synth: String! 145 | " the rate recorded at this timestamp " 146 | rate: BigDecimal! 147 | " the block which this rate was recorded " 148 | block: BigInt! 149 | " timestamp of the block in which the rate was recorded " 150 | timestamp: BigInt! 151 | } 152 | 153 | " DEPRECATED: See the Candles entity" 154 | type DailyCandle @entity { 155 | " DEPRECATED: See the Candles entity " 156 | id: ID! 157 | synth: String! 158 | open: BigDecimal! 159 | high: BigDecimal! 160 | low: BigDecimal! 161 | close: BigDecimal! 162 | timestamp: BigInt! 163 | } 164 | -------------------------------------------------------------------------------- /src/fragments/balances.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigDecimal, BigInt, log } from '@graphprotocol/graph-ts'; 2 | import { 3 | Synth as SynthContract, 4 | Transfer as SynthTransferEvent, 5 | } from '../../generated/subgraphs/balances/balances_SynthsUSD_0/Synth'; 6 | 7 | import { Synth, SynthBalance, LatestSynthBalance, SynthByCurrencyKey } from '../../generated/subgraphs/balances/schema'; 8 | import { toDecimal, ZERO, ZERO_ADDRESS } from '../lib/helpers'; 9 | 10 | export function registerSynth(synthAddress: Address): Synth | null { 11 | // the address associated with the issuer may not be the proxy 12 | let synthBackContract = SynthContract.bind(synthAddress); 13 | let proxyQuery = synthBackContract.try_proxy(); 14 | let nameQuery = synthBackContract.try_name(); 15 | let symbolQuery = synthBackContract.try_symbol(); 16 | let totalSupplyQuery = synthBackContract.try_totalSupply(); 17 | 18 | if (symbolQuery.reverted) { 19 | log.warning('tried to save invalid synth {}', [synthAddress.toHex()]); 20 | return null; 21 | } 22 | 23 | if (!proxyQuery.reverted) { 24 | synthAddress = proxyQuery.value; 25 | } 26 | 27 | let newSynth = new Synth(synthAddress.toHex()); 28 | newSynth.name = nameQuery.reverted ? symbolQuery.value : nameQuery.value; 29 | newSynth.symbol = symbolQuery.value; 30 | newSynth.totalSupply = toDecimal(totalSupplyQuery.value); 31 | newSynth.save(); 32 | 33 | // symbol is same as currencyKey 34 | let newSynthByCurrencyKey = new SynthByCurrencyKey(symbolQuery.value); 35 | newSynthByCurrencyKey.proxyAddress = synthAddress; 36 | newSynthByCurrencyKey.save(); 37 | 38 | // legacy sUSD contract uses wrong name 39 | if (symbolQuery.value == 'nUSD') { 40 | let newSynthByCurrencyKey = new SynthByCurrencyKey('sUSD'); 41 | newSynthByCurrencyKey.proxyAddress = synthAddress; 42 | newSynthByCurrencyKey.save(); 43 | } 44 | 45 | return newSynth; 46 | } 47 | 48 | function trackSynthHolder(synthAddress: Address, account: Address, timestamp: BigInt, value: BigDecimal): void { 49 | let synth = Synth.load(synthAddress.toHex()); 50 | 51 | if (synth == null) { 52 | registerSynth(synthAddress); 53 | } 54 | 55 | let totalBalance = toDecimal(ZERO); 56 | let latestBalanceID = account.toHex() + '-' + synthAddress.toHex(); 57 | let oldSynthBalance = LatestSynthBalance.load(latestBalanceID); 58 | 59 | if (oldSynthBalance == null || oldSynthBalance.timestamp.equals(timestamp)) { 60 | totalBalance = toDecimal(SynthContract.bind(synthAddress).balanceOf(account)); 61 | } else { 62 | totalBalance = oldSynthBalance.amount.plus(value); 63 | } 64 | 65 | let newLatestBalance = new LatestSynthBalance(latestBalanceID); 66 | newLatestBalance.address = account; 67 | newLatestBalance.account = account.toHex(); 68 | newLatestBalance.timestamp = timestamp; 69 | newLatestBalance.synth = synthAddress.toHex(); 70 | newLatestBalance.amount = totalBalance; 71 | newLatestBalance.save(); 72 | 73 | let newBalanceID = timestamp.toString() + '-' + account.toHex() + '-' + synthAddress.toHex(); 74 | let newBalance = new SynthBalance(newBalanceID); 75 | newBalance.address = account; 76 | newBalance.account = account.toHex(); 77 | newBalance.timestamp = timestamp; 78 | newBalance.synth = synthAddress.toHex(); 79 | newBalance.amount = totalBalance; 80 | newBalance.save(); 81 | } 82 | 83 | function trackMintOrBurn(synthAddress: Address, value: BigDecimal): void { 84 | let synth = Synth.load(synthAddress.toHex()); 85 | 86 | if (synth == null) { 87 | synth = registerSynth(synthAddress); 88 | } 89 | 90 | if (synth != null) { 91 | let newSupply = synth.totalSupply.plus(value); 92 | 93 | if (newSupply.lt(toDecimal(ZERO))) { 94 | log.warning('totalSupply needs correction, is negative: %s', [synth.symbol]); 95 | let synthBackContract = SynthContract.bind(synthAddress); 96 | synth.totalSupply = toDecimal(synthBackContract.totalSupply()); 97 | } else { 98 | synth.totalSupply = newSupply; 99 | } 100 | 101 | synth.save(); 102 | } 103 | } 104 | 105 | export function handleTransferSynth(event: SynthTransferEvent): void { 106 | if (event.params.from.toHex() != ZERO_ADDRESS.toHex() && event.params.from != event.address) { 107 | trackSynthHolder(event.address, event.params.from, event.block.timestamp, toDecimal(event.params.value).neg()); 108 | } else { 109 | trackMintOrBurn(event.address, toDecimal(event.params.value)); 110 | } 111 | 112 | if (event.params.to.toHex() != ZERO_ADDRESS.toHex() && event.params.to != event.address) { 113 | trackSynthHolder(event.address, event.params.to, event.block.timestamp, toDecimal(event.params.value)); 114 | } else { 115 | trackMintOrBurn(event.address, toDecimal(event.params.value).neg()); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /abis/Proxy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": false, 4 | "inputs": [ 5 | { 6 | "name": "_owner", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "nominateNewOwner", 11 | "outputs": [], 12 | "payable": false, 13 | "stateMutability": "nonpayable", 14 | "type": "function", 15 | "signature": "0x1627540c" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "nominatedOwner", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "address" 25 | } 26 | ], 27 | "payable": false, 28 | "stateMutability": "view", 29 | "type": "function", 30 | "signature": "0x53a47bb7" 31 | }, 32 | { 33 | "constant": false, 34 | "inputs": [ 35 | { 36 | "internalType": "contract Proxyable", 37 | "name": "_target", 38 | "type": "address" 39 | } 40 | ], 41 | "name": "setTarget", 42 | "outputs": [], 43 | "payable": false, 44 | "stateMutability": "nonpayable", 45 | "type": "function", 46 | "signature": "0x776d1a01" 47 | }, 48 | { 49 | "constant": false, 50 | "inputs": [], 51 | "name": "acceptOwnership", 52 | "outputs": [], 53 | "payable": false, 54 | "stateMutability": "nonpayable", 55 | "type": "function", 56 | "signature": "0x79ba5097" 57 | }, 58 | { 59 | "constant": true, 60 | "inputs": [], 61 | "name": "owner", 62 | "outputs": [ 63 | { 64 | "name": "", 65 | "type": "address" 66 | } 67 | ], 68 | "payable": false, 69 | "stateMutability": "view", 70 | "type": "function", 71 | "signature": "0x8da5cb5b" 72 | }, 73 | { 74 | "constant": false, 75 | "inputs": [ 76 | { 77 | "name": "callData", 78 | "type": "bytes" 79 | }, 80 | { 81 | "name": "numTopics", 82 | "type": "uint256" 83 | }, 84 | { 85 | "name": "topic1", 86 | "type": "bytes32" 87 | }, 88 | { 89 | "name": "topic2", 90 | "type": "bytes32" 91 | }, 92 | { 93 | "name": "topic3", 94 | "type": "bytes32" 95 | }, 96 | { 97 | "name": "topic4", 98 | "type": "bytes32" 99 | } 100 | ], 101 | "name": "_emit", 102 | "outputs": [], 103 | "payable": false, 104 | "stateMutability": "nonpayable", 105 | "type": "function", 106 | "signature": "0x907dff97" 107 | }, 108 | { 109 | "constant": true, 110 | "inputs": [], 111 | "name": "useDELEGATECALL", 112 | "outputs": [ 113 | { 114 | "name": "", 115 | "type": "bool" 116 | } 117 | ], 118 | "payable": false, 119 | "stateMutability": "view", 120 | "type": "function", 121 | "signature": "0x95578ebd" 122 | }, 123 | { 124 | "constant": false, 125 | "inputs": [ 126 | { 127 | "name": "value", 128 | "type": "bool" 129 | } 130 | ], 131 | "name": "setUseDELEGATECALL", 132 | "outputs": [], 133 | "payable": false, 134 | "stateMutability": "nonpayable", 135 | "type": "function", 136 | "signature": "0xbefff6af" 137 | }, 138 | { 139 | "constant": true, 140 | "inputs": [], 141 | "name": "target", 142 | "outputs": [ 143 | { 144 | "internalType": "contract Proxyable", 145 | "name": "", 146 | "type": "address" 147 | } 148 | ], 149 | "payable": false, 150 | "stateMutability": "view", 151 | "type": "function", 152 | "signature": "0xd4b83992" 153 | }, 154 | { 155 | "inputs": [ 156 | { 157 | "name": "_owner", 158 | "type": "address" 159 | } 160 | ], 161 | "payable": false, 162 | "stateMutability": "nonpayable", 163 | "type": "constructor" 164 | }, 165 | { 166 | "payable": true, 167 | "stateMutability": "payable", 168 | "type": "fallback" 169 | }, 170 | { 171 | "anonymous": false, 172 | "inputs": [ 173 | { 174 | "indexed": false, 175 | "internalType": "contract Proxyable", 176 | "name": "newTarget", 177 | "type": "address" 178 | } 179 | ], 180 | "name": "TargetUpdated", 181 | "type": "event", 182 | "signature": "0x814250a3b8c79fcbe2ead2c131c952a278491c8f4322a79fe84b5040a810373e" 183 | }, 184 | { 185 | "anonymous": false, 186 | "inputs": [ 187 | { 188 | "indexed": false, 189 | "name": "newOwner", 190 | "type": "address" 191 | } 192 | ], 193 | "name": "OwnerNominated", 194 | "type": "event", 195 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 196 | }, 197 | { 198 | "anonymous": false, 199 | "inputs": [ 200 | { 201 | "indexed": false, 202 | "name": "oldOwner", 203 | "type": "address" 204 | }, 205 | { 206 | "indexed": false, 207 | "name": "newOwner", 208 | "type": "address" 209 | } 210 | ], 211 | "name": "OwnerChanged", 212 | "type": "event", 213 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 214 | } 215 | ] 216 | -------------------------------------------------------------------------------- /abis/DappMaintenance.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor", 13 | "signature": "constructor" 14 | }, 15 | { 16 | "anonymous": false, 17 | "inputs": [ 18 | { 19 | "indexed": false, 20 | "internalType": "address", 21 | "name": "oldOwner", 22 | "type": "address" 23 | }, 24 | { 25 | "indexed": false, 26 | "internalType": "address", 27 | "name": "newOwner", 28 | "type": "address" 29 | } 30 | ], 31 | "name": "OwnerChanged", 32 | "type": "event", 33 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 34 | }, 35 | { 36 | "anonymous": false, 37 | "inputs": [ 38 | { 39 | "indexed": false, 40 | "internalType": "address", 41 | "name": "newOwner", 42 | "type": "address" 43 | } 44 | ], 45 | "name": "OwnerNominated", 46 | "type": "event", 47 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 48 | }, 49 | { 50 | "anonymous": false, 51 | "inputs": [ 52 | { 53 | "indexed": false, 54 | "internalType": "bool", 55 | "name": "isPaused", 56 | "type": "bool" 57 | } 58 | ], 59 | "name": "SXMaintenance", 60 | "type": "event", 61 | "signature": "0x5c1a8bee0278c3d0a78882d64b2152ae4cacfea1789f447025658aead92331c6" 62 | }, 63 | { 64 | "anonymous": false, 65 | "inputs": [ 66 | { 67 | "indexed": false, 68 | "internalType": "bool", 69 | "name": "isPaused", 70 | "type": "bool" 71 | } 72 | ], 73 | "name": "StakingMaintenance", 74 | "type": "event", 75 | "signature": "0x628bebe481126673e44b33fd8b7525b2e3a2e356838e838fb2934a82c79aea32" 76 | }, 77 | { 78 | "constant": false, 79 | "inputs": [], 80 | "name": "acceptOwnership", 81 | "outputs": [], 82 | "payable": false, 83 | "stateMutability": "nonpayable", 84 | "type": "function", 85 | "signature": "0x79ba5097" 86 | }, 87 | { 88 | "constant": true, 89 | "inputs": [], 90 | "name": "isPausedSX", 91 | "outputs": [ 92 | { 93 | "internalType": "bool", 94 | "name": "", 95 | "type": "bool" 96 | } 97 | ], 98 | "payable": false, 99 | "stateMutability": "view", 100 | "type": "function", 101 | "signature": "0x93c22125" 102 | }, 103 | { 104 | "constant": true, 105 | "inputs": [], 106 | "name": "isPausedStaking", 107 | "outputs": [ 108 | { 109 | "internalType": "bool", 110 | "name": "", 111 | "type": "bool" 112 | } 113 | ], 114 | "payable": false, 115 | "stateMutability": "view", 116 | "type": "function", 117 | "signature": "0xc65a0ea2" 118 | }, 119 | { 120 | "constant": false, 121 | "inputs": [ 122 | { 123 | "internalType": "address", 124 | "name": "_owner", 125 | "type": "address" 126 | } 127 | ], 128 | "name": "nominateNewOwner", 129 | "outputs": [], 130 | "payable": false, 131 | "stateMutability": "nonpayable", 132 | "type": "function", 133 | "signature": "0x1627540c" 134 | }, 135 | { 136 | "constant": true, 137 | "inputs": [], 138 | "name": "nominatedOwner", 139 | "outputs": [ 140 | { 141 | "internalType": "address", 142 | "name": "", 143 | "type": "address" 144 | } 145 | ], 146 | "payable": false, 147 | "stateMutability": "view", 148 | "type": "function", 149 | "signature": "0x53a47bb7" 150 | }, 151 | { 152 | "constant": true, 153 | "inputs": [], 154 | "name": "owner", 155 | "outputs": [ 156 | { 157 | "internalType": "address", 158 | "name": "", 159 | "type": "address" 160 | } 161 | ], 162 | "payable": false, 163 | "stateMutability": "view", 164 | "type": "function", 165 | "signature": "0x8da5cb5b" 166 | }, 167 | { 168 | "constant": false, 169 | "inputs": [ 170 | { 171 | "internalType": "bool", 172 | "name": "isPaused", 173 | "type": "bool" 174 | } 175 | ], 176 | "name": "setMaintenanceModeAll", 177 | "outputs": [], 178 | "payable": false, 179 | "stateMutability": "nonpayable", 180 | "type": "function", 181 | "signature": "0x1d008652" 182 | }, 183 | { 184 | "constant": false, 185 | "inputs": [ 186 | { 187 | "internalType": "bool", 188 | "name": "isPaused", 189 | "type": "bool" 190 | } 191 | ], 192 | "name": "setMaintenanceModeSX", 193 | "outputs": [], 194 | "payable": false, 195 | "stateMutability": "nonpayable", 196 | "type": "function", 197 | "signature": "0xee02f27c" 198 | }, 199 | { 200 | "constant": false, 201 | "inputs": [ 202 | { 203 | "internalType": "bool", 204 | "name": "isPaused", 205 | "type": "bool" 206 | } 207 | ], 208 | "name": "setMaintenanceModeStaking", 209 | "outputs": [], 210 | "payable": false, 211 | "stateMutability": "nonpayable", 212 | "type": "function", 213 | "signature": "0xb33a5a00" 214 | } 215 | ] 216 | -------------------------------------------------------------------------------- /subgraphs/issuance.graphql: -------------------------------------------------------------------------------- 1 | " Synthentix is an aggregation entity " 2 | type Synthetix @entity { 3 | id: ID! 4 | 5 | " number of stakers currently staking " 6 | issuers: BigInt! 7 | 8 | " number of addresses which hold SNX " 9 | snxHolders: BigInt! 10 | } 11 | 12 | " An individual Issuer " 13 | type Issuer @entity { 14 | id: ID! 15 | } 16 | 17 | " An individual SNX holder (always overridden with their latest information) " 18 | type SNXHolder @entity { 19 | " address of holder " 20 | id: ID! 21 | 22 | " last block where an event happened " 23 | block: BigInt! 24 | 25 | " last time where an event happened " 26 | timestamp: BigInt! 27 | 28 | " current SNX balance of the holder " 29 | balanceOf: BigDecimal 30 | 31 | # the following will be null before multicurrency (v2) release 32 | " SNX which is being used for collateral as of last event and cannot be spent " 33 | collateral: BigDecimal 34 | 35 | " SNX which can be spent as of last event " 36 | transferable: BigDecimal 37 | 38 | initialDebtOwnership: BigInt 39 | 40 | debtEntryAtIndex: BigInt 41 | 42 | " number of claims performed " 43 | claims: BigInt 44 | 45 | " number of mints performed " 46 | mints: BigInt 47 | } 48 | 49 | " A historical debt tracker " 50 | type DebtSnapshot @entity { 51 | id: ID! 52 | 53 | " last block where an event happened " 54 | block: BigInt! 55 | 56 | " last time when an event happened " 57 | timestamp: BigInt! 58 | 59 | " address for which these statistics are applicable " 60 | account: Bytes! # address 61 | balanceOf: BigDecimal # early on it was possible this wouldn't have a value (pre v2) 62 | # the following will be null before multicurrency (v2) release 63 | 64 | " SNX which is being used for collateral as of last event and cannot be spent " 65 | collateral: BigDecimal 66 | 67 | " sUSD of debt as of last event " 68 | debtBalanceOf: BigDecimal 69 | 70 | " sUSD debt portion a user had at last index " 71 | initialDebtOwnership: BigDecimal 72 | 73 | " debt entry when `initialDebtOwnership` applies" 74 | debtEntryAtIndex: BigInt 75 | } 76 | 77 | type DailyIssued @entity { 78 | " unix timestamp at beginning of day of the measurement " 79 | id: ID! 80 | 81 | " amount issued " 82 | value: BigDecimal! 83 | 84 | " amount of debt as of the last event for this day " 85 | totalDebt: BigDecimal! 86 | } 87 | 88 | type DailyBurned @entity { 89 | " unix timestamp at beginning of day of the measurement " 90 | id: ID! 91 | 92 | " amount burned " 93 | value: BigDecimal! 94 | 95 | " amount of debt as of the last event for this day " 96 | totalDebt: BigDecimal! 97 | } 98 | 99 | " Tracks this event from various Synth.sol instances " 100 | type Issued @entity { 101 | id: ID! 102 | account: Bytes! # address 103 | value: BigDecimal! 104 | source: String! 105 | timestamp: BigInt! 106 | gasPrice: BigInt! 107 | block: BigInt! 108 | } 109 | 110 | " Burned tracks this event from various Synth.sol instances " 111 | type Burned @entity { 112 | id: ID! 113 | account: Bytes! # address 114 | value: BigDecimal! # uint256 115 | source: String! 116 | timestamp: BigInt! 117 | gasPrice: BigInt! 118 | block: BigInt! 119 | } 120 | 121 | type RewardEscrowHolder @entity { 122 | " address which holds a rewardescrow " 123 | id: ID! 124 | 125 | " amount of tokens remaining to be claimed from the escrow " 126 | balanceOf: BigDecimal! 127 | 128 | " amount of SNX claimed from the escrow " 129 | vestedBalanceOf: BigDecimal! 130 | } 131 | 132 | " Tracks this event from Synthetix.sol " 133 | type FeesClaimed @entity { 134 | id: ID! 135 | account: Bytes! # address 136 | value: BigDecimal! # uint256 137 | rewards: BigDecimal! # uint256 138 | block: BigInt! 139 | timestamp: BigInt! 140 | } 141 | 142 | type FeePeriod @entity { 143 | id: ID! 144 | startTime: BigInt! 145 | feesToDistribute: BigDecimal! 146 | feesClaimed: BigDecimal! 147 | rewardsToDistribute: BigDecimal! 148 | rewardsClaimed: BigDecimal! 149 | } 150 | 151 | type TotalActiveStaker @entity { 152 | " single value " 153 | id: ID! 154 | 155 | " number of stakers seen " 156 | count: BigInt! 157 | } 158 | 159 | type TotalDailyActiveStaker @entity { 160 | " unix timestamp at beginning of day relevant to this statistic " 161 | id: ID! 162 | 163 | " unix timestamp as a BigInt (so it can be filtered) " 164 | timestamp: BigInt! 165 | 166 | " number of stakers seen on this day " 167 | count: BigInt! 168 | } 169 | 170 | type ActiveStaker @entity { 171 | id: ID! 172 | } 173 | type SynthByCurrencyKey @entity { 174 | " currency key " 175 | id: ID! 176 | proxyAddress: Bytes! 177 | } 178 | 179 | type Synth @entity { 180 | " lowercase address of the proxy contract for the synth " 181 | id: ID! 182 | name: String! 183 | symbol: String! 184 | 185 | totalSupply: BigDecimal! 186 | } 187 | 188 | type SynthBalance @entity { 189 | " timestamp + account + synth address " 190 | id: ID! 191 | amount: BigDecimal! 192 | address: Bytes! 193 | account: String! # using a string here because its ID compatible 194 | timestamp: BigInt! 195 | synth: Synth 196 | } 197 | 198 | " we dont query these entities but only use it to store aggregate data we need during syncing " 199 | type LatestSynthBalance @entity { 200 | " account + synth address " 201 | id: ID! 202 | amount: BigDecimal! 203 | address: Bytes! 204 | account: String! 205 | timestamp: BigInt! 206 | synth: Synth 207 | } 208 | -------------------------------------------------------------------------------- /abis/LegacyTokenState.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": false, 4 | "inputs": [ 5 | { 6 | "name": "_associatedContract", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "setAssociatedContract", 11 | "outputs": [], 12 | "payable": false, 13 | "stateMutability": "nonpayable", 14 | "type": "function", 15 | "signature": "0x52f445ca" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "nominatedOwner", 21 | "outputs": [ 22 | { 23 | "name": "", 24 | "type": "address" 25 | } 26 | ], 27 | "payable": false, 28 | "stateMutability": "view", 29 | "type": "function", 30 | "signature": "0x53a47bb7" 31 | }, 32 | { 33 | "constant": false, 34 | "inputs": [ 35 | { 36 | "name": "_owner", 37 | "type": "address" 38 | } 39 | ], 40 | "name": "nominateOwner", 41 | "outputs": [], 42 | "payable": false, 43 | "stateMutability": "nonpayable", 44 | "type": "function", 45 | "signature": "0x5b94db27" 46 | }, 47 | { 48 | "constant": true, 49 | "inputs": [ 50 | { 51 | "name": "", 52 | "type": "address" 53 | } 54 | ], 55 | "name": "balanceOf", 56 | "outputs": [ 57 | { 58 | "name": "", 59 | "type": "uint256" 60 | } 61 | ], 62 | "payable": false, 63 | "stateMutability": "view", 64 | "type": "function", 65 | "signature": "0x70a08231" 66 | }, 67 | { 68 | "constant": false, 69 | "inputs": [], 70 | "name": "acceptOwnership", 71 | "outputs": [], 72 | "payable": false, 73 | "stateMutability": "nonpayable", 74 | "type": "function", 75 | "signature": "0x79ba5097" 76 | }, 77 | { 78 | "constant": true, 79 | "inputs": [], 80 | "name": "owner", 81 | "outputs": [ 82 | { 83 | "name": "", 84 | "type": "address" 85 | } 86 | ], 87 | "payable": false, 88 | "stateMutability": "view", 89 | "type": "function", 90 | "signature": "0x8da5cb5b" 91 | }, 92 | { 93 | "constant": true, 94 | "inputs": [], 95 | "name": "associatedContract", 96 | "outputs": [ 97 | { 98 | "name": "", 99 | "type": "address" 100 | } 101 | ], 102 | "payable": false, 103 | "stateMutability": "view", 104 | "type": "function", 105 | "signature": "0xaefc4ccb" 106 | }, 107 | { 108 | "constant": false, 109 | "inputs": [ 110 | { 111 | "name": "account", 112 | "type": "address" 113 | }, 114 | { 115 | "name": "value", 116 | "type": "uint256" 117 | } 118 | ], 119 | "name": "setBalanceOf", 120 | "outputs": [], 121 | "payable": false, 122 | "stateMutability": "nonpayable", 123 | "type": "function", 124 | "signature": "0xb46310f6" 125 | }, 126 | { 127 | "constant": false, 128 | "inputs": [ 129 | { 130 | "name": "tokenOwner", 131 | "type": "address" 132 | }, 133 | { 134 | "name": "spender", 135 | "type": "address" 136 | }, 137 | { 138 | "name": "value", 139 | "type": "uint256" 140 | } 141 | ], 142 | "name": "setAllowance", 143 | "outputs": [], 144 | "payable": false, 145 | "stateMutability": "nonpayable", 146 | "type": "function", 147 | "signature": "0xda46098c" 148 | }, 149 | { 150 | "constant": true, 151 | "inputs": [ 152 | { 153 | "name": "", 154 | "type": "address" 155 | }, 156 | { 157 | "name": "", 158 | "type": "address" 159 | } 160 | ], 161 | "name": "allowance", 162 | "outputs": [ 163 | { 164 | "name": "", 165 | "type": "uint256" 166 | } 167 | ], 168 | "payable": false, 169 | "stateMutability": "view", 170 | "type": "function", 171 | "signature": "0xdd62ed3e" 172 | }, 173 | { 174 | "inputs": [ 175 | { 176 | "name": "_owner", 177 | "type": "address" 178 | }, 179 | { 180 | "name": "_associatedContract", 181 | "type": "address" 182 | } 183 | ], 184 | "payable": false, 185 | "stateMutability": "nonpayable", 186 | "type": "constructor" 187 | }, 188 | { 189 | "anonymous": false, 190 | "inputs": [ 191 | { 192 | "indexed": false, 193 | "name": "_associatedContract", 194 | "type": "address" 195 | } 196 | ], 197 | "name": "AssociatedContractUpdated", 198 | "type": "event", 199 | "signature": "0x73f20cff579e8a4086fa607db83867595f1b6a798e718c0bfa0b94a404128e03" 200 | }, 201 | { 202 | "anonymous": false, 203 | "inputs": [ 204 | { 205 | "indexed": false, 206 | "name": "newOwner", 207 | "type": "address" 208 | } 209 | ], 210 | "name": "OwnerNominated", 211 | "type": "event", 212 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 213 | }, 214 | { 215 | "anonymous": false, 216 | "inputs": [ 217 | { 218 | "indexed": false, 219 | "name": "oldOwner", 220 | "type": "address" 221 | }, 222 | { 223 | "indexed": false, 224 | "name": "newOwner", 225 | "type": "address" 226 | } 227 | ], 228 | "name": "OwnerChanged", 229 | "type": "event", 230 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 231 | } 232 | ] 233 | -------------------------------------------------------------------------------- /subgraphs/loans.js: -------------------------------------------------------------------------------- 1 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 2 | 3 | const manifest = []; 4 | 5 | /** 6 | * NOTE look at why the EtherCollateral doesn't have Partial Liquidation entity 7 | * or collateral change entities like EtherCollateralsUSD 8 | */ 9 | 10 | getContractDeployments('EtherCollateral').forEach((a, i) => { 11 | manifest.push({ 12 | kind: 'ethereum/contract', 13 | name: `loans_EtherCollateral_${i}`, 14 | network: getCurrentNetwork(), 15 | source: { 16 | address: a.address, 17 | startBlock: a.startBlock, 18 | abi: 'EtherCollateral', 19 | }, 20 | mapping: { 21 | kind: 'ethereum/events', 22 | apiVersion: '0.0.5', 23 | language: 'wasm/assemblyscript', 24 | file: '../src/loans.ts', 25 | entities: ['Loan', 'LoanCreated', 'LoanClosed', 'LoanLiquidated'], 26 | abis: [ 27 | { 28 | name: 'EtherCollateral', 29 | file: '../abis/EtherCollateral.json', 30 | }, 31 | ], 32 | eventHandlers: [ 33 | { 34 | event: 'LoanLiquidated(indexed address,uint256,address)', 35 | handler: 'handleLoanLiquidatedLegacy', 36 | }, 37 | { 38 | event: 'LoanCreated(indexed address,uint256,uint256)', 39 | handler: 'handleLoanCreatedEtherLegacy', 40 | }, 41 | { 42 | event: 'LoanClosed(indexed address,uint256,uint256)', 43 | handler: 'handleLoanClosedEtherLegacy', 44 | }, 45 | ], 46 | }, 47 | }); 48 | }); 49 | 50 | getContractDeployments('EtherCollateralsUSD').forEach((a, i) => { 51 | manifest.push({ 52 | kind: 'ethereum/contract', 53 | name: `loans_EtherCollateralsUSD_${i}`, 54 | network: getCurrentNetwork(), 55 | source: { 56 | address: a.address, 57 | startBlock: a.startBlock, 58 | abi: 'EtherCollateralsUSD', 59 | }, 60 | mapping: { 61 | kind: 'ethereum/events', 62 | apiVersion: '0.0.5', 63 | language: 'wasm/assemblyscript', 64 | file: '../src/loans.ts', 65 | entities: [ 66 | 'Loan', 67 | 'LoanCreated', 68 | 'LoanClosed', 69 | 'LoanLiquidated', 70 | 'LoanPartiallyLiquidated', 71 | 'CollateralDeposited', 72 | 'CollateralWithdrawn', 73 | 'LoanRepaid', 74 | ], 75 | abis: [ 76 | { 77 | name: 'EtherCollateralsUSD', 78 | file: '../abis/EtherCollateralsUSD.json', 79 | }, 80 | ], 81 | eventHandlers: [ 82 | { 83 | event: 'LoanLiquidated(indexed address,uint256,address)', 84 | handler: 'handleLoanLiquidatedLegacy', 85 | }, 86 | { 87 | event: 'LoanCreated(indexed address,uint256,uint256)', 88 | handler: 'handleLoanCreatedsUSDLegacy', 89 | }, 90 | { 91 | event: 'LoanClosed(indexed address,uint256,uint256)', 92 | handler: 'handleLoanClosedsUSDLegacy', 93 | }, 94 | { 95 | event: 'LoanPartiallyLiquidated(indexed address,uint256,address,uint256,uint256)', 96 | handler: 'handleLoanPartiallyLiquidatedLegacy', 97 | }, 98 | { 99 | event: 'CollateralDeposited(indexed address,uint256,uint256,uint256)', 100 | handler: 'handleCollateralDepositedLegacy', 101 | }, 102 | { 103 | event: 'CollateralWithdrawn(indexed address,uint256,uint256,uint256)', 104 | handler: 'handleCollateralWithdrawnLegacy', 105 | }, 106 | { 107 | event: 'LoanRepaid(indexed address,uint256,uint256,uint256)', 108 | handler: 'handleLoanRepaidLegacy', 109 | }, 110 | ], 111 | }, 112 | }); 113 | }); 114 | 115 | getContractDeployments('CollateralEth').forEach((a, i) => { 116 | manifest.push({ 117 | kind: 'ethereum/contract', 118 | name: `loans_CollateralEth_${i}`, 119 | network: getCurrentNetwork(), 120 | source: { 121 | address: a.address, 122 | startBlock: a.startBlock, 123 | abi: 'CollateralEth', 124 | }, 125 | mapping: { 126 | kind: 'ethereum/events', 127 | apiVersion: '0.0.5', 128 | language: 'wasm/assemblyscript', 129 | file: '../src/loans.ts', 130 | entities: [ 131 | 'Loan', 132 | 'LoanCreated', 133 | 'LoanClosed', 134 | 'LoanLiquidated', 135 | 'LoanPartiallyLiquidated', 136 | 'CollateralDeposited', 137 | 'CollateralWithdrawn', 138 | 'LoanRepaid', 139 | ], 140 | abis: [ 141 | { 142 | name: 'CollateralEth', 143 | file: '../abis/CollateralEth.json', 144 | }, 145 | ], 146 | eventHandlers: [ 147 | { 148 | event: 'LoanCreated(indexed address,uint256,uint256,uint256,bytes32,uint256)', 149 | handler: 'handleLoanCreatedEther', 150 | }, 151 | { 152 | event: 'LoanClosed(indexed address,uint256)', 153 | handler: 'handleLoanClosedEther', 154 | }, 155 | { 156 | event: 'LoanClosedByLiquidation(indexed address,uint256,indexed address,uint256,uint256)', 157 | handler: 'handleLoanClosedByLiquidation', 158 | }, 159 | { 160 | event: 'LoanPartiallyLiquidated(indexed address,uint256,address,uint256,uint256)', 161 | handler: 'handleLoanPartiallyLiquidated', 162 | }, 163 | { 164 | event: 'LoanRepaymentMade(indexed address,indexed address,uint256,uint256,uint256)', 165 | handler: 'handleLoanRepaymentMade', 166 | }, 167 | { 168 | event: 'CollateralDeposited(indexed address,uint256,uint256,uint256)', 169 | handler: 'handleCollateralDeposited', 170 | }, 171 | { 172 | event: 'CollateralWithdrawn(indexed address,uint256,uint256,uint256)', 173 | handler: 'handleCollateralWithdrawn', 174 | }, 175 | { 176 | event: 'LoanDrawnDown(indexed address,uint256,uint256)', 177 | handler: 'handleLoanDrawnDown', 178 | }, 179 | ], 180 | }, 181 | }); 182 | }); 183 | 184 | module.exports = createSubgraphManifest('loans', manifest, []); 185 | -------------------------------------------------------------------------------- /subgraphs/utils/network.js: -------------------------------------------------------------------------------- 1 | const { values, reverse, sortedIndexBy } = require('lodash'); 2 | 3 | const package = require('../../package.json'); 4 | 5 | const BLOCK_SAFETY_OFFSET = 8640; 6 | 7 | function getCurrentNetwork() { 8 | return process.env['SNX_NETWORK'] || 'mainnet'; 9 | } 10 | 11 | function getCurrentSubgraph() { 12 | return process.env['SUBGRAPH']; 13 | } 14 | 15 | function getReleaseInfo(file, network = undefined) { 16 | const net = network || getCurrentNetwork(); 17 | 18 | let info = null; 19 | if (net === 'mainnet' || net === 'kovan') { 20 | return require('synthetix/publish/deployed/' + net + '/' + file); 21 | } else if (net === 'optimism') { 22 | return require('synthetix/publish/deployed/mainnet-ovm/' + file); 23 | } else if (net === 'optimism-kovan') { 24 | return require('synthetix/publish/deployed/kovan-ovm/' + file); 25 | } 26 | 27 | return info; 28 | } 29 | 30 | function estimateBlock(date) { 31 | const blockInfo = values(getReleaseInfo('versions')) 32 | .filter((v) => v.block && v.date) 33 | .map((v) => [v.block, v.date]); 34 | 35 | // find the release immediately after the specified time 36 | const idx = sortedIndexBy(blockInfo, [0, date], (v) => v[1]); 37 | 38 | const numDate = new Date(date).getTime(); 39 | 40 | if (idx == blockInfo.length) { 41 | if (blockInfo.length < 3) { 42 | return null; 43 | } 44 | 45 | // determine some semblance of block rate 46 | const rate = 47 | (blockInfo[blockInfo.length - 1][0] - blockInfo[blockInfo.length - 3][0]) / 48 | (new Date(blockInfo[blockInfo.length - 1][1]).getTime() - new Date(blockInfo[blockInfo.length - 3][1]).getTime()); 49 | 50 | return Math.floor( 51 | blockInfo[blockInfo.length - 1][0] + rate * (numDate - new Date(blockInfo[blockInfo.length - 1][1]).getTime()), 52 | ); 53 | } 54 | 55 | if (blockInfo[idx][1] === date) { 56 | return blockInfo[idx][0]; 57 | } 58 | 59 | if (idx == 0) { 60 | return null; 61 | } 62 | 63 | const beforeDate = new Date(blockInfo[idx - 1][1]).getTime(); 64 | const afterDate = new Date(blockInfo[idx][1]).getTime(); 65 | 66 | return Math.floor( 67 | blockInfo[idx - 1][0] + 68 | ((blockInfo[idx][0] - blockInfo[idx - 1][0]) * (numDate - beforeDate)) / (afterDate - beforeDate), 69 | ); 70 | } 71 | 72 | function getReleaseBlocks() { 73 | const versionInfo = getReleaseInfo('versions'); 74 | 75 | const versionNameMap = {}; 76 | 77 | for (const n in versionInfo) { 78 | const info = versionInfo[n]; 79 | versionNameMap[info.release || info.tag] = info.block || estimateBlock(info.date); 80 | } 81 | 82 | return versionNameMap; 83 | } 84 | 85 | const versions = getReleaseBlocks(); 86 | 87 | function getContractDeployments(contractName, startBlock = 0, endBlock = Number.MAX_VALUE, network = undefined) { 88 | startBlock = Math.max(Math.max(startBlock, process.env.GRAFT_BLOCK || 0), process.env['SNX_START_BLOCK'] || 0); 89 | 90 | const versionInfo = getReleaseInfo('versions', network); 91 | 92 | const addressInfo = []; 93 | 94 | let lastStartBlock = null; 95 | 96 | // search for contract deployments 97 | for (const info of reverse(values(versionInfo))) { 98 | const contractInfo = info.contracts[contractName]; 99 | if (contractInfo) { 100 | if ((network || getCurrentNetwork()).match('optimism') != null) { 101 | addressInfo.push({ 102 | address: contractInfo.address, 103 | // with the regenesis, assume all contracts are basically deployed on the first block (doesn't hurt anything if they were deployed later) 104 | startBlock: startBlock, 105 | endBlock: null, 106 | }); 107 | } else { 108 | let contractStartBlock = Math.max(info.block || estimateBlock(info.date), BLOCK_SAFETY_OFFSET); 109 | 110 | // Relevant information is missing from the kovan versions.json file, so we hardcode a minimum here 111 | if (network == 'kovan' || getCurrentNetwork() == 'kovan') { 112 | contractStartBlock = Math.max(contractStartBlock, 10412700); 113 | } 114 | 115 | if (contractStartBlock >= endBlock) break; 116 | 117 | if (contractStartBlock < startBlock) { 118 | addressInfo.push({ address: contractInfo.address, startBlock, endBlock: lastStartBlock }); 119 | break; 120 | } else { 121 | const cushionStartBlock = 122 | contractStartBlock - BLOCK_SAFETY_OFFSET * 2 > 0 123 | ? contractStartBlock - BLOCK_SAFETY_OFFSET * 2 124 | : contractStartBlock - BLOCK_SAFETY_OFFSET; 125 | 126 | addressInfo.push({ 127 | address: contractInfo.address, 128 | startBlock: cushionStartBlock, 129 | endBlock: lastStartBlock, 130 | }); 131 | 132 | lastStartBlock = contractStartBlock; 133 | } 134 | } 135 | } 136 | } 137 | 138 | return reverse(addressInfo); 139 | } 140 | 141 | function createSubgraphManifest(name, dataSources, templates) { 142 | const dataSourcesArray = Object.values(dataSources); 143 | const templatesArray = Object.values(templates); 144 | 145 | dataSourcesArray.reverse(); 146 | templatesArray.reverse(); 147 | 148 | const manifest = { 149 | specVersion: '0.0.4', 150 | features: ['grafting'], 151 | description: name ? 'Synthetix Subgraph' : 'Synthetix Subgraph ' + name, 152 | repository: 'https://github.com/Synthetixio/synthetix-subgraph', 153 | schema: { 154 | file: `./${name}.graphql`, 155 | }, 156 | dataSources: dataSourcesArray, 157 | templates: templatesArray, 158 | }; 159 | 160 | if (process.env.GRAFT_BASE) { 161 | manifest.graft = { 162 | base: process.env.GRAFT_BASE, 163 | block: parseInt(process.env.GRAFT_BLOCK), 164 | }; 165 | } 166 | 167 | if (process.env.DEBUG_MANIFEST) { 168 | console.log('generated manifest:', JSON.stringify(manifest, null, 2)); 169 | } 170 | 171 | return manifest; 172 | } 173 | 174 | const NETWORKS = ['mainnet', 'kovan', 'optimism-kovan', 'optimism']; 175 | 176 | module.exports = { 177 | getCurrentNetwork, 178 | getReleaseInfo, 179 | estimateBlock, 180 | versions, 181 | getContractDeployments, 182 | NETWORKS, 183 | getCurrentSubgraph, 184 | createSubgraphManifest, 185 | }; 186 | -------------------------------------------------------------------------------- /abis/BinaryOptionMarketFactory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_resolver", 12 | "type": "address" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "constructor", 18 | "signature": "constructor" 19 | }, 20 | { 21 | "anonymous": false, 22 | "inputs": [ 23 | { 24 | "indexed": false, 25 | "internalType": "bytes32", 26 | "name": "name", 27 | "type": "bytes32" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "address", 32 | "name": "destination", 33 | "type": "address" 34 | } 35 | ], 36 | "name": "CacheUpdated", 37 | "type": "event", 38 | "signature": "0x88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68" 39 | }, 40 | { 41 | "anonymous": false, 42 | "inputs": [ 43 | { 44 | "indexed": false, 45 | "internalType": "address", 46 | "name": "oldOwner", 47 | "type": "address" 48 | }, 49 | { 50 | "indexed": false, 51 | "internalType": "address", 52 | "name": "newOwner", 53 | "type": "address" 54 | } 55 | ], 56 | "name": "OwnerChanged", 57 | "type": "event", 58 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 59 | }, 60 | { 61 | "anonymous": false, 62 | "inputs": [ 63 | { 64 | "indexed": false, 65 | "internalType": "address", 66 | "name": "newOwner", 67 | "type": "address" 68 | } 69 | ], 70 | "name": "OwnerNominated", 71 | "type": "event", 72 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 73 | }, 74 | { 75 | "constant": false, 76 | "inputs": [], 77 | "name": "acceptOwnership", 78 | "outputs": [], 79 | "payable": false, 80 | "stateMutability": "nonpayable", 81 | "type": "function", 82 | "signature": "0x79ba5097" 83 | }, 84 | { 85 | "constant": false, 86 | "inputs": [ 87 | { 88 | "internalType": "address", 89 | "name": "creator", 90 | "type": "address" 91 | }, 92 | { 93 | "internalType": "uint256[2]", 94 | "name": "creatorLimits", 95 | "type": "uint256[2]" 96 | }, 97 | { 98 | "internalType": "bytes32", 99 | "name": "oracleKey", 100 | "type": "bytes32" 101 | }, 102 | { 103 | "internalType": "uint256", 104 | "name": "strikePrice", 105 | "type": "uint256" 106 | }, 107 | { 108 | "internalType": "bool", 109 | "name": "refundsEnabled", 110 | "type": "bool" 111 | }, 112 | { 113 | "internalType": "uint256[3]", 114 | "name": "times", 115 | "type": "uint256[3]" 116 | }, 117 | { 118 | "internalType": "uint256[2]", 119 | "name": "bids", 120 | "type": "uint256[2]" 121 | }, 122 | { 123 | "internalType": "uint256[3]", 124 | "name": "fees", 125 | "type": "uint256[3]" 126 | } 127 | ], 128 | "name": "createMarket", 129 | "outputs": [ 130 | { 131 | "internalType": "contract BinaryOptionMarket", 132 | "name": "", 133 | "type": "address" 134 | } 135 | ], 136 | "payable": false, 137 | "stateMutability": "nonpayable", 138 | "type": "function", 139 | "signature": "0x130efa50" 140 | }, 141 | { 142 | "constant": true, 143 | "inputs": [], 144 | "name": "isResolverCached", 145 | "outputs": [ 146 | { 147 | "internalType": "bool", 148 | "name": "", 149 | "type": "bool" 150 | } 151 | ], 152 | "payable": false, 153 | "stateMutability": "view", 154 | "type": "function", 155 | "signature": "0x2af64bd3" 156 | }, 157 | { 158 | "constant": false, 159 | "inputs": [ 160 | { 161 | "internalType": "address", 162 | "name": "_owner", 163 | "type": "address" 164 | } 165 | ], 166 | "name": "nominateNewOwner", 167 | "outputs": [], 168 | "payable": false, 169 | "stateMutability": "nonpayable", 170 | "type": "function", 171 | "signature": "0x1627540c" 172 | }, 173 | { 174 | "constant": true, 175 | "inputs": [], 176 | "name": "nominatedOwner", 177 | "outputs": [ 178 | { 179 | "internalType": "address", 180 | "name": "", 181 | "type": "address" 182 | } 183 | ], 184 | "payable": false, 185 | "stateMutability": "view", 186 | "type": "function", 187 | "signature": "0x53a47bb7" 188 | }, 189 | { 190 | "constant": true, 191 | "inputs": [], 192 | "name": "owner", 193 | "outputs": [ 194 | { 195 | "internalType": "address", 196 | "name": "", 197 | "type": "address" 198 | } 199 | ], 200 | "payable": false, 201 | "stateMutability": "view", 202 | "type": "function", 203 | "signature": "0x8da5cb5b" 204 | }, 205 | { 206 | "constant": false, 207 | "inputs": [], 208 | "name": "rebuildCache", 209 | "outputs": [], 210 | "payable": false, 211 | "stateMutability": "nonpayable", 212 | "type": "function", 213 | "signature": "0x74185360" 214 | }, 215 | { 216 | "constant": true, 217 | "inputs": [], 218 | "name": "resolver", 219 | "outputs": [ 220 | { 221 | "internalType": "contract AddressResolver", 222 | "name": "", 223 | "type": "address" 224 | } 225 | ], 226 | "payable": false, 227 | "stateMutability": "view", 228 | "type": "function", 229 | "signature": "0x04f3bcec" 230 | }, 231 | { 232 | "constant": true, 233 | "inputs": [], 234 | "name": "resolverAddressesRequired", 235 | "outputs": [ 236 | { 237 | "internalType": "bytes32[]", 238 | "name": "addresses", 239 | "type": "bytes32[]" 240 | } 241 | ], 242 | "payable": false, 243 | "stateMutability": "view", 244 | "type": "function", 245 | "signature": "0x899ffef4" 246 | } 247 | ] 248 | -------------------------------------------------------------------------------- /abis/TokenState.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_associatedContract", 12 | "type": "address" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "constructor", 18 | "signature": "constructor" 19 | }, 20 | { 21 | "anonymous": false, 22 | "inputs": [ 23 | { 24 | "indexed": false, 25 | "internalType": "address", 26 | "name": "associatedContract", 27 | "type": "address" 28 | } 29 | ], 30 | "name": "AssociatedContractUpdated", 31 | "type": "event", 32 | "signature": "0x73f20cff579e8a4086fa607db83867595f1b6a798e718c0bfa0b94a404128e03" 33 | }, 34 | { 35 | "anonymous": false, 36 | "inputs": [ 37 | { 38 | "indexed": false, 39 | "internalType": "address", 40 | "name": "oldOwner", 41 | "type": "address" 42 | }, 43 | { 44 | "indexed": false, 45 | "internalType": "address", 46 | "name": "newOwner", 47 | "type": "address" 48 | } 49 | ], 50 | "name": "OwnerChanged", 51 | "type": "event", 52 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 53 | }, 54 | { 55 | "anonymous": false, 56 | "inputs": [ 57 | { 58 | "indexed": false, 59 | "internalType": "address", 60 | "name": "newOwner", 61 | "type": "address" 62 | } 63 | ], 64 | "name": "OwnerNominated", 65 | "type": "event", 66 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 67 | }, 68 | { 69 | "constant": false, 70 | "inputs": [], 71 | "name": "acceptOwnership", 72 | "outputs": [], 73 | "payable": false, 74 | "stateMutability": "nonpayable", 75 | "type": "function", 76 | "signature": "0x79ba5097" 77 | }, 78 | { 79 | "constant": true, 80 | "inputs": [ 81 | { 82 | "internalType": "address", 83 | "name": "", 84 | "type": "address" 85 | }, 86 | { 87 | "internalType": "address", 88 | "name": "", 89 | "type": "address" 90 | } 91 | ], 92 | "name": "allowance", 93 | "outputs": [ 94 | { 95 | "internalType": "uint256", 96 | "name": "", 97 | "type": "uint256" 98 | } 99 | ], 100 | "payable": false, 101 | "stateMutability": "view", 102 | "type": "function", 103 | "signature": "0xdd62ed3e" 104 | }, 105 | { 106 | "constant": true, 107 | "inputs": [], 108 | "name": "associatedContract", 109 | "outputs": [ 110 | { 111 | "internalType": "address", 112 | "name": "", 113 | "type": "address" 114 | } 115 | ], 116 | "payable": false, 117 | "stateMutability": "view", 118 | "type": "function", 119 | "signature": "0xaefc4ccb" 120 | }, 121 | { 122 | "constant": true, 123 | "inputs": [ 124 | { 125 | "internalType": "address", 126 | "name": "", 127 | "type": "address" 128 | } 129 | ], 130 | "name": "balanceOf", 131 | "outputs": [ 132 | { 133 | "internalType": "uint256", 134 | "name": "", 135 | "type": "uint256" 136 | } 137 | ], 138 | "payable": false, 139 | "stateMutability": "view", 140 | "type": "function", 141 | "signature": "0x70a08231" 142 | }, 143 | { 144 | "constant": false, 145 | "inputs": [ 146 | { 147 | "internalType": "address", 148 | "name": "_owner", 149 | "type": "address" 150 | } 151 | ], 152 | "name": "nominateNewOwner", 153 | "outputs": [], 154 | "payable": false, 155 | "stateMutability": "nonpayable", 156 | "type": "function", 157 | "signature": "0x1627540c" 158 | }, 159 | { 160 | "constant": true, 161 | "inputs": [], 162 | "name": "nominatedOwner", 163 | "outputs": [ 164 | { 165 | "internalType": "address", 166 | "name": "", 167 | "type": "address" 168 | } 169 | ], 170 | "payable": false, 171 | "stateMutability": "view", 172 | "type": "function", 173 | "signature": "0x53a47bb7" 174 | }, 175 | { 176 | "constant": true, 177 | "inputs": [], 178 | "name": "owner", 179 | "outputs": [ 180 | { 181 | "internalType": "address", 182 | "name": "", 183 | "type": "address" 184 | } 185 | ], 186 | "payable": false, 187 | "stateMutability": "view", 188 | "type": "function", 189 | "signature": "0x8da5cb5b" 190 | }, 191 | { 192 | "constant": false, 193 | "inputs": [ 194 | { 195 | "internalType": "address", 196 | "name": "tokenOwner", 197 | "type": "address" 198 | }, 199 | { 200 | "internalType": "address", 201 | "name": "spender", 202 | "type": "address" 203 | }, 204 | { 205 | "internalType": "uint256", 206 | "name": "value", 207 | "type": "uint256" 208 | } 209 | ], 210 | "name": "setAllowance", 211 | "outputs": [], 212 | "payable": false, 213 | "stateMutability": "nonpayable", 214 | "type": "function", 215 | "signature": "0xda46098c" 216 | }, 217 | { 218 | "constant": false, 219 | "inputs": [ 220 | { 221 | "internalType": "address", 222 | "name": "_associatedContract", 223 | "type": "address" 224 | } 225 | ], 226 | "name": "setAssociatedContract", 227 | "outputs": [], 228 | "payable": false, 229 | "stateMutability": "nonpayable", 230 | "type": "function", 231 | "signature": "0x52f445ca" 232 | }, 233 | { 234 | "constant": false, 235 | "inputs": [ 236 | { 237 | "internalType": "address", 238 | "name": "account", 239 | "type": "address" 240 | }, 241 | { 242 | "internalType": "uint256", 243 | "name": "value", 244 | "type": "uint256" 245 | } 246 | ], 247 | "name": "setBalanceOf", 248 | "outputs": [], 249 | "payable": false, 250 | "stateMutability": "nonpayable", 251 | "type": "function", 252 | "signature": "0xb46310f6" 253 | } 254 | ] 255 | -------------------------------------------------------------------------------- /subgraphs/wrapper.js: -------------------------------------------------------------------------------- 1 | const { clone } = require('lodash'); 2 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 3 | const latestRates = require('./fragments/latest-rates'); 4 | let manifest = clone(latestRates.dataSources); 5 | 6 | getContractDeployments('WrapperFactory').forEach((a, i) => { 7 | manifest.push({ 8 | kind: 'ethereum/contract', 9 | name: `WrapperFactory_${i}`, 10 | network: getCurrentNetwork(), 11 | source: { 12 | address: a.address, 13 | startBlock: a.startBlock, 14 | abi: 'WrapperFactory', 15 | }, 16 | mapping: { 17 | kind: 'ethereum/events', 18 | apiVersion: '0.0.5', 19 | language: 'wasm/assemblyscript', 20 | file: '../src/wrapper.ts', 21 | entities: [], 22 | abis: [ 23 | { 24 | name: 'WrapperFactory', 25 | file: '../abis/WrapperFactory.json', 26 | }, 27 | ], 28 | eventHandlers: [ 29 | { 30 | event: 'WrapperCreated(indexed address,indexed bytes32,address)', 31 | handler: 'handleWrapperCreated', 32 | }, 33 | ], 34 | }, 35 | }); 36 | }); 37 | 38 | let wrapperTemplate = { 39 | kind: 'ethereum/contract', 40 | name: 'WrapperTemplate', 41 | network: getCurrentNetwork(), 42 | source: { 43 | abi: 'Wrapper', 44 | }, 45 | mapping: { 46 | kind: 'ethereum/events', 47 | apiVersion: '0.0.5', 48 | language: 'wasm/assemblyscript', 49 | file: '../src/wrapper.ts', 50 | entities: ['Wrapper', 'WrapperMint', 'WrapperBurn'], 51 | abis: [ 52 | { 53 | name: 'Wrapper', 54 | file: '../abis/Wrapper.json', 55 | }, 56 | { 57 | name: 'AddressResolver', 58 | file: '../abis/AddressResolver.json', 59 | }, 60 | { 61 | name: 'ExchangeRates', 62 | file: '../abis/ExchangeRates.json', 63 | }, 64 | { 65 | name: 'AggregatorProxy', 66 | file: '../abis/AggregatorProxy.json', 67 | }, 68 | ], 69 | eventHandlers: [ 70 | { 71 | event: 'Minted(indexed address,uint256,uint256,uint256)', 72 | handler: 'handleMinted', 73 | }, 74 | { 75 | event: 'Burned(indexed address,uint256,uint256,uint256)', 76 | handler: 'handleBurned', 77 | }, 78 | ], 79 | }, 80 | }; 81 | 82 | getContractDeployments('EtherWrapper').forEach((a, i) => { 83 | manifest.push({ 84 | kind: 'ethereum/contract', 85 | name: `EtherWrapper_${i}`, 86 | network: getCurrentNetwork(), 87 | source: { 88 | address: a.address, 89 | startBlock: a.startBlock, 90 | abi: 'EtherWrapper', 91 | }, 92 | mapping: { 93 | kind: 'ethereum/events', 94 | apiVersion: '0.0.5', 95 | language: 'wasm/assemblyscript', 96 | file: '../src/wrapper.ts', 97 | entities: ['Wrapper'], 98 | abis: [ 99 | { 100 | name: 'EtherWrapper', 101 | file: '../abis/EtherWrapper.json', 102 | }, 103 | { 104 | name: 'AddressResolver', 105 | file: '../abis/AddressResolver.json', 106 | }, 107 | { 108 | name: 'ExchangeRates', 109 | file: '../abis/ExchangeRates.json', 110 | }, 111 | { 112 | name: 'AggregatorProxy', 113 | file: '../abis/AggregatorProxy.json', 114 | }, 115 | ], 116 | eventHandlers: [ 117 | { 118 | event: 'Minted(indexed address,uint256,uint256,uint256)', 119 | handler: 'handleMinted', 120 | }, 121 | { 122 | event: 'Burned(indexed address,uint256,uint256,uint256)', 123 | handler: 'handleBurned', 124 | }, 125 | ], 126 | }, 127 | }); 128 | }); 129 | 130 | getContractDeployments('SystemSettings').forEach((a, i) => { 131 | manifest.push({ 132 | kind: 'ethereum/contract', 133 | name: `SystemSettings_${i}`, 134 | network: getCurrentNetwork(), 135 | source: { 136 | address: a.address, 137 | startBlock: a.startBlock, 138 | abi: 'SystemSettings', 139 | }, 140 | mapping: { 141 | kind: 'ethereum/events', 142 | apiVersion: '0.0.5', 143 | language: 'wasm/assemblyscript', 144 | file: '../src/wrapper.ts', 145 | entities: [], 146 | abis: [ 147 | { 148 | name: 'SystemSettings', 149 | file: '../abis/SystemSettings.json', 150 | }, 151 | { 152 | name: 'AddressResolver', 153 | file: '../abis/AddressResolver.json', 154 | }, 155 | ], 156 | eventHandlers: [ 157 | { 158 | event: 'WrapperMaxTokenAmountUpdated(address,uint256)', 159 | handler: 'handleWrapperMaxTokenAmountUpdated', 160 | }, 161 | { 162 | event: 'EtherWrapperMaxETHUpdated(uint256)', 163 | handler: 'handleEtherWrapperMaxETHUpdated', 164 | }, 165 | ], 166 | }, 167 | }); 168 | }); 169 | 170 | // We manually add the ETH, DAI, and LUSD Wrappers when indexing Optimism 171 | // because the WrapperFactory events that would generate them 172 | // were lost in the regenesis on 11/11/21. 173 | if (getCurrentNetwork() == 'optimism') { 174 | let daiWrapper = clone(wrapperTemplate); 175 | daiWrapper.name = 'daiWrapper'; 176 | let daiSource = clone(daiWrapper.source); 177 | daiSource.address = '0xad32aA4Bff8b61B4aE07E3BA437CF81100AF0cD7'; 178 | daiWrapper.source = daiSource; 179 | daiWrapper.source.startBlock = parseInt(process.env.SNX_START_BLOCK) || 0; 180 | manifest.push(daiWrapper); 181 | 182 | let ethWrapper = clone(wrapperTemplate); 183 | ethWrapper.name = 'ethWrapper'; 184 | let ethSource = clone(ethWrapper.source); 185 | ethSource.address = '0x6202A3B0bE1D222971E93AaB084c6E584C29DB70'; 186 | ethWrapper.source = ethSource; 187 | ethWrapper.source.startBlock = parseInt(process.env.SNX_START_BLOCK) || 0; 188 | manifest.push(ethWrapper); 189 | 190 | let lusdWrapper = clone(wrapperTemplate); 191 | lusdWrapper.name = 'lusdWrapper'; 192 | let lusdSource = clone(lusdWrapper.source); 193 | lusdSource.address = '0x8a91e92fdd86e734781c38db52a390e1b99fba7c'; 194 | lusdWrapper.source = lusdSource; 195 | lusdWrapper.source.startBlock = parseInt(process.env.SNX_START_BLOCK) || 0; 196 | manifest.push(lusdWrapper); 197 | } 198 | 199 | module.exports = createSubgraphManifest('wrapper', manifest, latestRates.templates.concat([wrapperTemplate])); 200 | -------------------------------------------------------------------------------- /abis/OwnerRelayOnEthereum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_resolver", 12 | "type": "address" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "constructor" 18 | }, 19 | { 20 | "anonymous": false, 21 | "inputs": [ 22 | { 23 | "indexed": false, 24 | "internalType": "bytes32", 25 | "name": "name", 26 | "type": "bytes32" 27 | }, 28 | { 29 | "indexed": false, 30 | "internalType": "address", 31 | "name": "destination", 32 | "type": "address" 33 | } 34 | ], 35 | "name": "CacheUpdated", 36 | "type": "event" 37 | }, 38 | { 39 | "anonymous": false, 40 | "inputs": [ 41 | { 42 | "indexed": false, 43 | "internalType": "address", 44 | "name": "oldOwner", 45 | "type": "address" 46 | }, 47 | { 48 | "indexed": false, 49 | "internalType": "address", 50 | "name": "newOwner", 51 | "type": "address" 52 | } 53 | ], 54 | "name": "OwnerChanged", 55 | "type": "event" 56 | }, 57 | { 58 | "anonymous": false, 59 | "inputs": [ 60 | { 61 | "indexed": false, 62 | "internalType": "address", 63 | "name": "newOwner", 64 | "type": "address" 65 | } 66 | ], 67 | "name": "OwnerNominated", 68 | "type": "event" 69 | }, 70 | { 71 | "anonymous": false, 72 | "inputs": [ 73 | { 74 | "indexed": false, 75 | "internalType": "address[]", 76 | "name": "targets", 77 | "type": "address[]" 78 | }, 79 | { 80 | "indexed": false, 81 | "internalType": "bytes[]", 82 | "name": "payloads", 83 | "type": "bytes[]" 84 | } 85 | ], 86 | "name": "RelayBatchInitiated", 87 | "type": "event" 88 | }, 89 | { 90 | "anonymous": false, 91 | "inputs": [ 92 | { 93 | "indexed": false, 94 | "internalType": "address", 95 | "name": "target", 96 | "type": "address" 97 | }, 98 | { 99 | "indexed": false, 100 | "internalType": "bytes", 101 | "name": "payload", 102 | "type": "bytes" 103 | } 104 | ], 105 | "name": "RelayInitiated", 106 | "type": "event" 107 | }, 108 | { 109 | "constant": false, 110 | "inputs": [], 111 | "name": "acceptOwnership", 112 | "outputs": [], 113 | "payable": false, 114 | "stateMutability": "nonpayable", 115 | "type": "function" 116 | }, 117 | { 118 | "constant": false, 119 | "inputs": [ 120 | { 121 | "internalType": "address", 122 | "name": "target", 123 | "type": "address" 124 | }, 125 | { 126 | "internalType": "bytes", 127 | "name": "payload", 128 | "type": "bytes" 129 | }, 130 | { 131 | "internalType": "uint32", 132 | "name": "crossDomainGasLimit", 133 | "type": "uint32" 134 | } 135 | ], 136 | "name": "initiateRelay", 137 | "outputs": [], 138 | "payable": false, 139 | "stateMutability": "nonpayable", 140 | "type": "function" 141 | }, 142 | { 143 | "constant": false, 144 | "inputs": [ 145 | { 146 | "internalType": "address[]", 147 | "name": "targets", 148 | "type": "address[]" 149 | }, 150 | { 151 | "internalType": "bytes[]", 152 | "name": "payloads", 153 | "type": "bytes[]" 154 | }, 155 | { 156 | "internalType": "uint32", 157 | "name": "crossDomainGasLimit", 158 | "type": "uint32" 159 | } 160 | ], 161 | "name": "initiateRelayBatch", 162 | "outputs": [], 163 | "payable": false, 164 | "stateMutability": "nonpayable", 165 | "type": "function" 166 | }, 167 | { 168 | "constant": true, 169 | "inputs": [], 170 | "name": "isResolverCached", 171 | "outputs": [ 172 | { 173 | "internalType": "bool", 174 | "name": "", 175 | "type": "bool" 176 | } 177 | ], 178 | "payable": false, 179 | "stateMutability": "view", 180 | "type": "function" 181 | }, 182 | { 183 | "constant": false, 184 | "inputs": [ 185 | { 186 | "internalType": "address", 187 | "name": "_owner", 188 | "type": "address" 189 | } 190 | ], 191 | "name": "nominateNewOwner", 192 | "outputs": [], 193 | "payable": false, 194 | "stateMutability": "nonpayable", 195 | "type": "function" 196 | }, 197 | { 198 | "constant": true, 199 | "inputs": [], 200 | "name": "nominatedOwner", 201 | "outputs": [ 202 | { 203 | "internalType": "address", 204 | "name": "", 205 | "type": "address" 206 | } 207 | ], 208 | "payable": false, 209 | "stateMutability": "view", 210 | "type": "function" 211 | }, 212 | { 213 | "constant": true, 214 | "inputs": [], 215 | "name": "owner", 216 | "outputs": [ 217 | { 218 | "internalType": "address", 219 | "name": "", 220 | "type": "address" 221 | } 222 | ], 223 | "payable": false, 224 | "stateMutability": "view", 225 | "type": "function" 226 | }, 227 | { 228 | "constant": false, 229 | "inputs": [], 230 | "name": "rebuildCache", 231 | "outputs": [], 232 | "payable": false, 233 | "stateMutability": "nonpayable", 234 | "type": "function" 235 | }, 236 | { 237 | "constant": true, 238 | "inputs": [], 239 | "name": "resolver", 240 | "outputs": [ 241 | { 242 | "internalType": "contract AddressResolver", 243 | "name": "", 244 | "type": "address" 245 | } 246 | ], 247 | "payable": false, 248 | "stateMutability": "view", 249 | "type": "function" 250 | }, 251 | { 252 | "constant": true, 253 | "inputs": [], 254 | "name": "resolverAddressesRequired", 255 | "outputs": [ 256 | { 257 | "internalType": "bytes32[]", 258 | "name": "addresses", 259 | "type": "bytes32[]" 260 | } 261 | ], 262 | "payable": false, 263 | "stateMutability": "view", 264 | "type": "function" 265 | } 266 | ] 267 | -------------------------------------------------------------------------------- /abis/NativeEtherWrapper.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_resolver", 12 | "type": "address" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "constructor", 18 | "signature": "constructor" 19 | }, 20 | { 21 | "anonymous": false, 22 | "inputs": [ 23 | { 24 | "indexed": true, 25 | "internalType": "address", 26 | "name": "account", 27 | "type": "address" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "uint256", 32 | "name": "amount", 33 | "type": "uint256" 34 | } 35 | ], 36 | "name": "Burned", 37 | "type": "event", 38 | "signature": "0x696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df7" 39 | }, 40 | { 41 | "anonymous": false, 42 | "inputs": [ 43 | { 44 | "indexed": false, 45 | "internalType": "bytes32", 46 | "name": "name", 47 | "type": "bytes32" 48 | }, 49 | { 50 | "indexed": false, 51 | "internalType": "address", 52 | "name": "destination", 53 | "type": "address" 54 | } 55 | ], 56 | "name": "CacheUpdated", 57 | "type": "event", 58 | "signature": "0x88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68" 59 | }, 60 | { 61 | "anonymous": false, 62 | "inputs": [ 63 | { 64 | "indexed": true, 65 | "internalType": "address", 66 | "name": "account", 67 | "type": "address" 68 | }, 69 | { 70 | "indexed": false, 71 | "internalType": "uint256", 72 | "name": "amount", 73 | "type": "uint256" 74 | } 75 | ], 76 | "name": "Minted", 77 | "type": "event", 78 | "signature": "0x30385c845b448a36257a6a1716e6ad2e1bc2cbe333cde1e69fe849ad6511adfe" 79 | }, 80 | { 81 | "anonymous": false, 82 | "inputs": [ 83 | { 84 | "indexed": false, 85 | "internalType": "address", 86 | "name": "oldOwner", 87 | "type": "address" 88 | }, 89 | { 90 | "indexed": false, 91 | "internalType": "address", 92 | "name": "newOwner", 93 | "type": "address" 94 | } 95 | ], 96 | "name": "OwnerChanged", 97 | "type": "event", 98 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 99 | }, 100 | { 101 | "anonymous": false, 102 | "inputs": [ 103 | { 104 | "indexed": false, 105 | "internalType": "address", 106 | "name": "newOwner", 107 | "type": "address" 108 | } 109 | ], 110 | "name": "OwnerNominated", 111 | "type": "event", 112 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 113 | }, 114 | { 115 | "payable": true, 116 | "stateMutability": "payable", 117 | "type": "fallback" 118 | }, 119 | { 120 | "constant": false, 121 | "inputs": [], 122 | "name": "acceptOwnership", 123 | "outputs": [], 124 | "payable": false, 125 | "stateMutability": "nonpayable", 126 | "type": "function", 127 | "signature": "0x79ba5097" 128 | }, 129 | { 130 | "constant": false, 131 | "inputs": [ 132 | { 133 | "internalType": "uint256", 134 | "name": "amount", 135 | "type": "uint256" 136 | } 137 | ], 138 | "name": "burn", 139 | "outputs": [], 140 | "payable": false, 141 | "stateMutability": "nonpayable", 142 | "type": "function", 143 | "signature": "0x42966c68" 144 | }, 145 | { 146 | "constant": true, 147 | "inputs": [], 148 | "name": "isResolverCached", 149 | "outputs": [ 150 | { 151 | "internalType": "bool", 152 | "name": "", 153 | "type": "bool" 154 | } 155 | ], 156 | "payable": false, 157 | "stateMutability": "view", 158 | "type": "function", 159 | "signature": "0x2af64bd3" 160 | }, 161 | { 162 | "constant": false, 163 | "inputs": [], 164 | "name": "mint", 165 | "outputs": [], 166 | "payable": true, 167 | "stateMutability": "payable", 168 | "type": "function", 169 | "signature": "0x1249c58b" 170 | }, 171 | { 172 | "constant": false, 173 | "inputs": [ 174 | { 175 | "internalType": "address", 176 | "name": "_owner", 177 | "type": "address" 178 | } 179 | ], 180 | "name": "nominateNewOwner", 181 | "outputs": [], 182 | "payable": false, 183 | "stateMutability": "nonpayable", 184 | "type": "function", 185 | "signature": "0x1627540c" 186 | }, 187 | { 188 | "constant": true, 189 | "inputs": [], 190 | "name": "nominatedOwner", 191 | "outputs": [ 192 | { 193 | "internalType": "address", 194 | "name": "", 195 | "type": "address" 196 | } 197 | ], 198 | "payable": false, 199 | "stateMutability": "view", 200 | "type": "function", 201 | "signature": "0x53a47bb7" 202 | }, 203 | { 204 | "constant": true, 205 | "inputs": [], 206 | "name": "owner", 207 | "outputs": [ 208 | { 209 | "internalType": "address", 210 | "name": "", 211 | "type": "address" 212 | } 213 | ], 214 | "payable": false, 215 | "stateMutability": "view", 216 | "type": "function", 217 | "signature": "0x8da5cb5b" 218 | }, 219 | { 220 | "constant": false, 221 | "inputs": [], 222 | "name": "rebuildCache", 223 | "outputs": [], 224 | "payable": false, 225 | "stateMutability": "nonpayable", 226 | "type": "function", 227 | "signature": "0x74185360" 228 | }, 229 | { 230 | "constant": true, 231 | "inputs": [], 232 | "name": "resolver", 233 | "outputs": [ 234 | { 235 | "internalType": "contract AddressResolver", 236 | "name": "", 237 | "type": "address" 238 | } 239 | ], 240 | "payable": false, 241 | "stateMutability": "view", 242 | "type": "function", 243 | "signature": "0x04f3bcec" 244 | }, 245 | { 246 | "constant": true, 247 | "inputs": [], 248 | "name": "resolverAddressesRequired", 249 | "outputs": [ 250 | { 251 | "internalType": "bytes32[]", 252 | "name": "addresses", 253 | "type": "bytes32[]" 254 | } 255 | ], 256 | "payable": false, 257 | "stateMutability": "view", 258 | "type": "function", 259 | "signature": "0x899ffef4" 260 | } 261 | ] 262 | -------------------------------------------------------------------------------- /src/wrapper.ts: -------------------------------------------------------------------------------- 1 | import { dataSource, BigInt, DataSourceContext, BigDecimal, Address } from '@graphprotocol/graph-ts'; 2 | import { Wrapper, WrapperMint, WrapperBurn } from '../generated/subgraphs/wrapper/schema'; 3 | import { WrapperTemplate } from '../generated/subgraphs/wrapper/templates'; 4 | import { getLatestRate, strToBytes, toDecimal } from './lib/helpers'; 5 | import { getContractDeployment } from '../generated/addresses'; 6 | import { AddressResolver } from '../generated/subgraphs/wrapper/SystemSettings_0/AddressResolver'; 7 | import { 8 | Burned as BurnedEvent, 9 | Minted as MintedEvent, 10 | } from '../generated/subgraphs/wrapper/templates/WrapperTemplate/Wrapper'; 11 | import { 12 | WrapperMaxTokenAmountUpdated as WrapperMaxTokenAmountUpdatedEvent, 13 | EtherWrapperMaxETHUpdated as EtherWrapperMaxETHUpdatedEvent, 14 | } from '../generated/subgraphs/wrapper/SystemSettings_0/SystemSettings'; 15 | import { Wrapper as WrapperContract } from '../generated/subgraphs/wrapper/templates/WrapperTemplate/Wrapper'; 16 | import { WrapperCreated as WrapperCreatedEvent } from '../generated/subgraphs/wrapper/WrapperFactory_0/WrapperFactory'; 17 | 18 | export function handleWrapperCreated(event: WrapperCreatedEvent): void { 19 | let context = new DataSourceContext(); 20 | context.setString('tokenAddress', event.params.token.toHexString()); 21 | context.setString('currencyKey', event.params.currencyKey.toString()); 22 | WrapperTemplate.createWithContext(event.params.wrapperAddress, context); 23 | } 24 | 25 | export function handleMinted(event: MintedEvent): void { 26 | // Create Mint 27 | let mintEntity = new WrapperMint(event.transaction.hash.toHex() + '-' + event.logIndex.toString()); 28 | mintEntity.account = event.params.account.toHexString(); 29 | mintEntity.principal = toDecimal(event.params.principal); 30 | mintEntity.fee = toDecimal(event.params.fee); 31 | mintEntity.amountIn = toDecimal(event.params.amountIn); 32 | mintEntity.timestamp = event.block.timestamp; 33 | mintEntity.wrapperAddress = event.address.toHexString(); 34 | mintEntity.save(); 35 | 36 | // Update Wrapper 37 | let wrapper = Wrapper.load(event.address.toHexString()); 38 | if (!wrapper) { 39 | wrapper = new Wrapper(event.address.toHexString()); 40 | } 41 | wrapper = initializeWrapper(wrapper, event.address); 42 | 43 | if (wrapper) { 44 | wrapper.amount = wrapper.amount.plus(toDecimal(event.params.amountIn)); 45 | wrapper.totalFees = wrapper.totalFees.plus(toDecimal(event.params.fee)); 46 | 47 | let txHash = event.transaction.hash.toString(); 48 | let latestRate = getLatestRate(wrapper.currencyKey, txHash); 49 | if (latestRate) { 50 | wrapper.amountInUSD = wrapper.amount.times(latestRate); 51 | wrapper.totalFeesInUSD = wrapper.totalFees.times(latestRate); 52 | } 53 | 54 | wrapper.save(); 55 | } 56 | } 57 | 58 | export function handleBurned(event: BurnedEvent): void { 59 | // Create Burn 60 | let burnEntity = new WrapperBurn(event.transaction.hash.toHex() + '-' + event.logIndex.toString()); 61 | burnEntity.account = event.params.account.toHex(); 62 | burnEntity.principal = toDecimal(event.params.principal); 63 | burnEntity.fee = toDecimal(event.params.fee); 64 | burnEntity.amountOut = toDecimal(event.params.amountIn); 65 | burnEntity.timestamp = event.block.timestamp; 66 | burnEntity.wrapperAddress = event.address.toHexString(); 67 | burnEntity.save(); 68 | 69 | // Update Wrapper 70 | let wrapper = Wrapper.load(event.address.toHexString()); 71 | if (!wrapper) { 72 | wrapper = new Wrapper(event.address.toHexString()); 73 | } 74 | wrapper = initializeWrapper(wrapper, event.address); 75 | 76 | if (wrapper) { 77 | wrapper.amount = wrapper.amount.minus(toDecimal(event.params.principal)); 78 | wrapper.totalFees = wrapper.totalFees.plus(toDecimal(event.params.fee)); 79 | 80 | let txHash = event.transaction.hash.toHexString(); 81 | let latestRate = getLatestRate(wrapper.currencyKey, txHash); 82 | if (latestRate) { 83 | wrapper.amountInUSD = wrapper.amount.times(latestRate); 84 | wrapper.totalFeesInUSD = wrapper.totalFees.times(latestRate); 85 | } 86 | 87 | wrapper.save(); 88 | } 89 | } 90 | 91 | export function handleWrapperMaxTokenAmountUpdated(event: WrapperMaxTokenAmountUpdatedEvent): void { 92 | let wrapper = Wrapper.load(event.params.wrapper.toHexString()); 93 | if (wrapper) { 94 | wrapper.maxAmount = toDecimal(event.params.maxTokenAmount); 95 | wrapper.save(); 96 | } 97 | } 98 | 99 | export function handleEtherWrapperMaxETHUpdated(event: EtherWrapperMaxETHUpdatedEvent): void { 100 | let addressResolverAddress = getContractDeployment( 101 | 'AddressResolver', 102 | dataSource.network(), 103 | BigInt.fromI32(1000000000), 104 | )!; 105 | let resolver = AddressResolver.bind(addressResolverAddress); 106 | let etherWrapperAddress = resolver.try_getAddress(strToBytes('EtherWrapper', 32)); 107 | if (etherWrapperAddress.reverted) { 108 | return; 109 | } 110 | let wrapperAddress = etherWrapperAddress.value; 111 | 112 | let wrapper = Wrapper.load(wrapperAddress.toHexString()); 113 | if (wrapper) { 114 | wrapper.maxAmount = toDecimal(event.params.maxETH); 115 | wrapper.save(); 116 | } 117 | } 118 | 119 | function initializeWrapper(wrapper: Wrapper, address: Address): Wrapper { 120 | // See wrapper.js for more context on the pre-regenesis wrappers 121 | // We assume this hasn't been initialized if the maxAmount is 0 122 | if ( 123 | wrapper.amount.toString() == '0' && 124 | (address.toHexString() == '0xad32aa4bff8b61b4ae07e3ba437cf81100af0cd7' || 125 | address.toHexString() == '0x6202a3b0be1d222971e93aab084c6e584c29db70' || 126 | address.toHexString() == '0x8a91e92fdd86e734781c38db52a390e1b99fba7c') 127 | ) { 128 | let wrapperContract = WrapperContract.bind(address); 129 | wrapper.tokenAddress = wrapperContract.token().toHexString(); 130 | wrapper.currencyKey = wrapperContract.currencyKey().toString(); 131 | wrapper.amount = toDecimal(wrapperContract.targetSynthIssued()); 132 | wrapper.maxAmount = toDecimal(wrapperContract.maxTokenAmount()); 133 | wrapper.totalFees = BigDecimal.fromString('0'); // TBD 134 | } 135 | 136 | // If this still doesn't have a currencyKey, this is the ETH wrapper on mainnet 137 | if (!wrapper.currencyKey) { 138 | wrapper.currencyKey = 'ETH'; 139 | } 140 | 141 | // Assign values from context, for template generated Wrapper entities 142 | let context = dataSource.context(); 143 | if (context.get('tokenAddress')) { 144 | let tokenAddress = context.getString('tokenAddress'); 145 | let currencyKey = context.getString('currencyKey'); 146 | if (tokenAddress && tokenAddress.length) { 147 | wrapper.tokenAddress = tokenAddress; 148 | wrapper.currencyKey = currencyKey; 149 | } 150 | } 151 | 152 | return wrapper; 153 | } 154 | -------------------------------------------------------------------------------- /subgraphs/shorts.graphql: -------------------------------------------------------------------------------- 1 | type Short @entity { 2 | " the short id " 3 | id: ID! 4 | 5 | " contract level info for the short position " 6 | contractData: ShortContract! 7 | 8 | " the transaction hash of the short " 9 | txHash: String! 10 | 11 | " the account that created the short " 12 | account: Bytes! 13 | 14 | " the type of collateral locked - sUSD, ETH, renBTC " 15 | collateralLocked: Bytes! 16 | 17 | " the amount of collateral locked in the short " 18 | collateralLockedAmount: BigDecimal! 19 | 20 | " the denomination of the loan repayment - sETH, sBTC " 21 | synthBorrowed: Bytes! 22 | 23 | " the amount owed denominated in the loan repayment synth " 24 | synthBorrowedAmount: BigDecimal! 25 | 26 | " the timestamp the accrued interest was most recently updated " 27 | accruedInterestLastUpdateTimestamp: BigInt! 28 | 29 | " is the short still open? " 30 | isOpen: Boolean! 31 | 32 | " the block the short was created at " 33 | createdAtBlock: BigInt! 34 | 35 | " the timestamp the short was created " 36 | createdAt: BigInt! 37 | 38 | " the timestamp the short was closed " 39 | closedAt: BigInt 40 | 41 | " liquidations that have been made on the short " 42 | liquidations: [ShortLiquidation!] @derivedFrom(field: "short") 43 | 44 | " collateral deposits and withdrawals that have been made on the short " 45 | collateralChanges: [ShortCollateralChange!] @derivedFrom(field: "short") 46 | 47 | " loan changes that have been made on the short - increasing or decreasing the short position " 48 | loanChanges: [ShortLoanChange!]! @derivedFrom(field: "short") 49 | } 50 | 51 | type ShortCollateralChange @entity { 52 | " the event tx hash plus event log index " 53 | id: ID! 54 | 55 | " denotes if the event was a deposit (true) or withdrawal (false)" 56 | isDeposit: Boolean! 57 | 58 | " the amount of collateral deposited or withdrawn " 59 | amount: BigDecimal! 60 | 61 | " the total amount of collateral after the deposit or withdrawal is included " 62 | collateralAfter: BigDecimal! 63 | 64 | " the respective short " 65 | short: Short! 66 | 67 | " the timestamp collateral was deposited or withdrawn " 68 | timestamp: BigInt! 69 | 70 | " the block the collateral was changed " 71 | blockNumber: BigInt! 72 | } 73 | 74 | type ShortLoanChange @entity { 75 | " the event tx hash plus event log index " 76 | id: ID! 77 | 78 | " denotes if the event was a repayment (true) or an increase of the loan (false)" 79 | isRepayment: Boolean! 80 | 81 | " the amount of loan repaid or increased " 82 | amount: BigDecimal! 83 | 84 | " the total amount of loan due after the repayment or increase is included " 85 | loanAfter: BigDecimal! 86 | 87 | " the respective short " 88 | short: Short! 89 | 90 | " the price of the repaid synth in dollars " 91 | rate: BigDecimal! 92 | 93 | " the timestamp of the loan repayment or increase " 94 | timestamp: BigInt! 95 | 96 | " the block the short loan was changed " 97 | blockNumber: BigInt! 98 | } 99 | 100 | type ShortLiquidation @entity { 101 | " the event tx hash plus event log index " 102 | id: ID! 103 | 104 | " the account that liquidated the loan " 105 | liquidator: Bytes! 106 | 107 | " determines if the " 108 | isClosed: Boolean! 109 | 110 | " the amount of the loan that was burned " 111 | liquidatedAmount: BigDecimal! 112 | 113 | " the amount of the collateral that was taken away from the short owner " 114 | liquidatedCollateral: BigDecimal! 115 | 116 | " the respective short " 117 | short: Short! 118 | 119 | " the timestamp of the loan liquidation event " 120 | timestamp: BigInt! 121 | 122 | " the block of the liquidation event " 123 | blockNumber: BigInt! 124 | } 125 | 126 | type ShortContract @entity { 127 | " the address of the shorting contract " 128 | id: ID! 129 | 130 | " a list of shorts attached to each contract " 131 | shorts: [Short!] @derivedFrom(field: "contractData") 132 | 133 | " a list of changes to contract level data " 134 | contractUpdates: [ShortContractUpdate!] @derivedFrom(field: "contractData") 135 | 136 | " the min c-ratio for borrowers below which they can be liquidated " 137 | minCratio: BigInt! 138 | 139 | " the minimum collateral required to open a position " 140 | minCollateral: BigDecimal! 141 | 142 | " the fee for issuing a short " 143 | issueFeeRate: BigDecimal! 144 | 145 | " the max number of loans per account " 146 | maxLoansPerAccount: BigInt! 147 | 148 | " Time in seconds that a user must wait between interacting with a loan. Provides front running and flash loan protection. " 149 | interactionDelay: BigInt! 150 | 151 | " the manager is a contract that ties the shorting contract in with the rest of the Synthetix protocol " 152 | manager: Bytes! 153 | 154 | " a boolean that prevents new loans on the contract when false " 155 | canOpenLoans: Boolean! 156 | } 157 | 158 | type ShortContractUpdate @entity { 159 | " the event tx hash plus event log index " 160 | id: ID! 161 | 162 | " the field that was changed " 163 | field: String! 164 | 165 | " the new value of the field in string format " 166 | value: String! 167 | 168 | " the respective short contract " 169 | contractData: ShortContract! 170 | 171 | " the timestamp of the contract update event " 172 | timestamp: BigInt! 173 | 174 | " the block the short contract was udpated at " 175 | blockNumber: BigInt! 176 | } 177 | 178 | type InversePricingInfo @entity { 179 | " Name of inverse synth. E.g. iETH " 180 | id: ID! 181 | 182 | " whether or not this inverse synth has been frozen " 183 | frozen: Boolean! 184 | 185 | " configured upper limit " 186 | upperLimit: BigDecimal! 187 | 188 | " configured lower limit " 189 | lowerLimit: BigDecimal! 190 | 191 | " matching price point with long synth " 192 | entryPoint: BigDecimal! 193 | } 194 | 195 | type LatestRate @entity { 196 | " Name of synth. E.g. sUSD " 197 | id: ID! 198 | 199 | " Synth USD rate " 200 | rate: BigDecimal! 201 | 202 | " Address of the aggregator which produces current result " 203 | aggregator: Bytes! 204 | } 205 | 206 | " Latest Rates over time " 207 | type RateUpdate @entity { 208 | " - " 209 | id: ID! 210 | " currencyKey for which this this rate update applies " 211 | currencyKey: Bytes! 212 | " currencyKey expressed as a string " 213 | synth: String! 214 | " the rate recorded at this timestamp " 215 | rate: BigDecimal! 216 | " the block which this rate was recorded " 217 | block: BigInt! 218 | " timestamp of the block in which the rate was recorded " 219 | timestamp: BigInt! 220 | } 221 | 222 | " DEPRECATED: See the Candles entity" 223 | type DailyCandle @entity { 224 | " DEPRECATED: See the Candles entity " 225 | id: ID! 226 | synth: String! 227 | open: BigDecimal! 228 | high: BigDecimal! 229 | low: BigDecimal! 230 | close: BigDecimal! 231 | timestamp: BigInt! 232 | } 233 | -------------------------------------------------------------------------------- /abis/AddressResolver.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor", 13 | "signature": "constructor" 14 | }, 15 | { 16 | "anonymous": false, 17 | "inputs": [ 18 | { 19 | "indexed": false, 20 | "internalType": "bytes32", 21 | "name": "name", 22 | "type": "bytes32" 23 | }, 24 | { 25 | "indexed": false, 26 | "internalType": "address", 27 | "name": "destination", 28 | "type": "address" 29 | } 30 | ], 31 | "name": "AddressImported", 32 | "type": "event", 33 | "signature": "0xefe884cc7f82a6cf3cf68f64221519dcf96b5cae9048e1bb008ee32cd05aaa91" 34 | }, 35 | { 36 | "anonymous": false, 37 | "inputs": [ 38 | { 39 | "indexed": false, 40 | "internalType": "address", 41 | "name": "oldOwner", 42 | "type": "address" 43 | }, 44 | { 45 | "indexed": false, 46 | "internalType": "address", 47 | "name": "newOwner", 48 | "type": "address" 49 | } 50 | ], 51 | "name": "OwnerChanged", 52 | "type": "event", 53 | "signature": "0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c" 54 | }, 55 | { 56 | "anonymous": false, 57 | "inputs": [ 58 | { 59 | "indexed": false, 60 | "internalType": "address", 61 | "name": "newOwner", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "OwnerNominated", 66 | "type": "event", 67 | "signature": "0x906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22" 68 | }, 69 | { 70 | "constant": false, 71 | "inputs": [], 72 | "name": "acceptOwnership", 73 | "outputs": [], 74 | "payable": false, 75 | "stateMutability": "nonpayable", 76 | "type": "function", 77 | "signature": "0x79ba5097" 78 | }, 79 | { 80 | "constant": true, 81 | "inputs": [ 82 | { 83 | "internalType": "bytes32[]", 84 | "name": "names", 85 | "type": "bytes32[]" 86 | }, 87 | { 88 | "internalType": "address[]", 89 | "name": "destinations", 90 | "type": "address[]" 91 | } 92 | ], 93 | "name": "areAddressesImported", 94 | "outputs": [ 95 | { 96 | "internalType": "bool", 97 | "name": "", 98 | "type": "bool" 99 | } 100 | ], 101 | "payable": false, 102 | "stateMutability": "view", 103 | "type": "function", 104 | "signature": "0x9f42102f" 105 | }, 106 | { 107 | "constant": true, 108 | "inputs": [ 109 | { 110 | "internalType": "bytes32", 111 | "name": "name", 112 | "type": "bytes32" 113 | } 114 | ], 115 | "name": "getAddress", 116 | "outputs": [ 117 | { 118 | "internalType": "address", 119 | "name": "", 120 | "type": "address" 121 | } 122 | ], 123 | "payable": false, 124 | "stateMutability": "view", 125 | "type": "function", 126 | "signature": "0x21f8a721" 127 | }, 128 | { 129 | "constant": true, 130 | "inputs": [ 131 | { 132 | "internalType": "bytes32", 133 | "name": "key", 134 | "type": "bytes32" 135 | } 136 | ], 137 | "name": "getSynth", 138 | "outputs": [ 139 | { 140 | "internalType": "address", 141 | "name": "", 142 | "type": "address" 143 | } 144 | ], 145 | "payable": false, 146 | "stateMutability": "view", 147 | "type": "function", 148 | "signature": "0x51456061" 149 | }, 150 | { 151 | "constant": false, 152 | "inputs": [ 153 | { 154 | "internalType": "bytes32[]", 155 | "name": "names", 156 | "type": "bytes32[]" 157 | }, 158 | { 159 | "internalType": "address[]", 160 | "name": "destinations", 161 | "type": "address[]" 162 | } 163 | ], 164 | "name": "importAddresses", 165 | "outputs": [], 166 | "payable": false, 167 | "stateMutability": "nonpayable", 168 | "type": "function", 169 | "signature": "0xab0b8f77" 170 | }, 171 | { 172 | "constant": false, 173 | "inputs": [ 174 | { 175 | "internalType": "address", 176 | "name": "_owner", 177 | "type": "address" 178 | } 179 | ], 180 | "name": "nominateNewOwner", 181 | "outputs": [], 182 | "payable": false, 183 | "stateMutability": "nonpayable", 184 | "type": "function", 185 | "signature": "0x1627540c" 186 | }, 187 | { 188 | "constant": true, 189 | "inputs": [], 190 | "name": "nominatedOwner", 191 | "outputs": [ 192 | { 193 | "internalType": "address", 194 | "name": "", 195 | "type": "address" 196 | } 197 | ], 198 | "payable": false, 199 | "stateMutability": "view", 200 | "type": "function", 201 | "signature": "0x53a47bb7" 202 | }, 203 | { 204 | "constant": true, 205 | "inputs": [], 206 | "name": "owner", 207 | "outputs": [ 208 | { 209 | "internalType": "address", 210 | "name": "", 211 | "type": "address" 212 | } 213 | ], 214 | "payable": false, 215 | "stateMutability": "view", 216 | "type": "function", 217 | "signature": "0x8da5cb5b" 218 | }, 219 | { 220 | "constant": false, 221 | "inputs": [ 222 | { 223 | "internalType": "contract MixinResolver[]", 224 | "name": "destinations", 225 | "type": "address[]" 226 | } 227 | ], 228 | "name": "rebuildCaches", 229 | "outputs": [], 230 | "payable": false, 231 | "stateMutability": "nonpayable", 232 | "type": "function", 233 | "signature": "0x766f7815" 234 | }, 235 | { 236 | "constant": true, 237 | "inputs": [ 238 | { 239 | "internalType": "bytes32", 240 | "name": "", 241 | "type": "bytes32" 242 | } 243 | ], 244 | "name": "repository", 245 | "outputs": [ 246 | { 247 | "internalType": "address", 248 | "name": "", 249 | "type": "address" 250 | } 251 | ], 252 | "payable": false, 253 | "stateMutability": "view", 254 | "type": "function", 255 | "signature": "0x187f7935" 256 | }, 257 | { 258 | "constant": true, 259 | "inputs": [ 260 | { 261 | "internalType": "bytes32", 262 | "name": "name", 263 | "type": "bytes32" 264 | }, 265 | { 266 | "internalType": "string", 267 | "name": "reason", 268 | "type": "string" 269 | } 270 | ], 271 | "name": "requireAndGetAddress", 272 | "outputs": [ 273 | { 274 | "internalType": "address", 275 | "name": "", 276 | "type": "address" 277 | } 278 | ], 279 | "payable": false, 280 | "stateMutability": "view", 281 | "type": "function", 282 | "signature": "0xdacb2d01" 283 | } 284 | ] 285 | -------------------------------------------------------------------------------- /abis/WrapperFactory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_owner", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_resolver", 12 | "type": "address" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "constructor" 18 | }, 19 | { 20 | "anonymous": false, 21 | "inputs": [ 22 | { 23 | "indexed": false, 24 | "internalType": "bytes32", 25 | "name": "name", 26 | "type": "bytes32" 27 | }, 28 | { 29 | "indexed": false, 30 | "internalType": "address", 31 | "name": "destination", 32 | "type": "address" 33 | } 34 | ], 35 | "name": "CacheUpdated", 36 | "type": "event" 37 | }, 38 | { 39 | "anonymous": false, 40 | "inputs": [ 41 | { 42 | "indexed": false, 43 | "internalType": "address", 44 | "name": "oldOwner", 45 | "type": "address" 46 | }, 47 | { 48 | "indexed": false, 49 | "internalType": "address", 50 | "name": "newOwner", 51 | "type": "address" 52 | } 53 | ], 54 | "name": "OwnerChanged", 55 | "type": "event" 56 | }, 57 | { 58 | "anonymous": false, 59 | "inputs": [ 60 | { 61 | "indexed": false, 62 | "internalType": "address", 63 | "name": "newOwner", 64 | "type": "address" 65 | } 66 | ], 67 | "name": "OwnerNominated", 68 | "type": "event" 69 | }, 70 | { 71 | "anonymous": false, 72 | "inputs": [ 73 | { 74 | "indexed": true, 75 | "internalType": "address", 76 | "name": "token", 77 | "type": "address" 78 | }, 79 | { 80 | "indexed": true, 81 | "internalType": "bytes32", 82 | "name": "currencyKey", 83 | "type": "bytes32" 84 | }, 85 | { 86 | "indexed": false, 87 | "internalType": "address", 88 | "name": "wrapperAddress", 89 | "type": "address" 90 | } 91 | ], 92 | "name": "WrapperCreated", 93 | "type": "event" 94 | }, 95 | { 96 | "payable": true, 97 | "stateMutability": "payable", 98 | "type": "fallback" 99 | }, 100 | { 101 | "constant": true, 102 | "inputs": [], 103 | "name": "CONTRACT_NAME", 104 | "outputs": [ 105 | { 106 | "internalType": "bytes32", 107 | "name": "", 108 | "type": "bytes32" 109 | } 110 | ], 111 | "payable": false, 112 | "stateMutability": "view", 113 | "type": "function" 114 | }, 115 | { 116 | "constant": false, 117 | "inputs": [], 118 | "name": "acceptOwnership", 119 | "outputs": [], 120 | "payable": false, 121 | "stateMutability": "nonpayable", 122 | "type": "function" 123 | }, 124 | { 125 | "constant": false, 126 | "inputs": [ 127 | { 128 | "internalType": "contract IERC20", 129 | "name": "token", 130 | "type": "address" 131 | }, 132 | { 133 | "internalType": "bytes32", 134 | "name": "currencyKey", 135 | "type": "bytes32" 136 | }, 137 | { 138 | "internalType": "bytes32", 139 | "name": "synthContractName", 140 | "type": "bytes32" 141 | } 142 | ], 143 | "name": "createWrapper", 144 | "outputs": [ 145 | { 146 | "internalType": "address", 147 | "name": "", 148 | "type": "address" 149 | } 150 | ], 151 | "payable": false, 152 | "stateMutability": "nonpayable", 153 | "type": "function" 154 | }, 155 | { 156 | "constant": false, 157 | "inputs": [], 158 | "name": "distributeFees", 159 | "outputs": [], 160 | "payable": false, 161 | "stateMutability": "nonpayable", 162 | "type": "function" 163 | }, 164 | { 165 | "constant": true, 166 | "inputs": [], 167 | "name": "feesEscrowed", 168 | "outputs": [ 169 | { 170 | "internalType": "uint256", 171 | "name": "", 172 | "type": "uint256" 173 | } 174 | ], 175 | "payable": false, 176 | "stateMutability": "view", 177 | "type": "function" 178 | }, 179 | { 180 | "constant": true, 181 | "inputs": [], 182 | "name": "isResolverCached", 183 | "outputs": [ 184 | { 185 | "internalType": "bool", 186 | "name": "", 187 | "type": "bool" 188 | } 189 | ], 190 | "payable": false, 191 | "stateMutability": "view", 192 | "type": "function" 193 | }, 194 | { 195 | "constant": true, 196 | "inputs": [ 197 | { 198 | "internalType": "address", 199 | "name": "possibleWrapper", 200 | "type": "address" 201 | } 202 | ], 203 | "name": "isWrapper", 204 | "outputs": [ 205 | { 206 | "internalType": "bool", 207 | "name": "", 208 | "type": "bool" 209 | } 210 | ], 211 | "payable": false, 212 | "stateMutability": "view", 213 | "type": "function" 214 | }, 215 | { 216 | "constant": false, 217 | "inputs": [ 218 | { 219 | "internalType": "address", 220 | "name": "_owner", 221 | "type": "address" 222 | } 223 | ], 224 | "name": "nominateNewOwner", 225 | "outputs": [], 226 | "payable": false, 227 | "stateMutability": "nonpayable", 228 | "type": "function" 229 | }, 230 | { 231 | "constant": true, 232 | "inputs": [], 233 | "name": "nominatedOwner", 234 | "outputs": [ 235 | { 236 | "internalType": "address", 237 | "name": "", 238 | "type": "address" 239 | } 240 | ], 241 | "payable": false, 242 | "stateMutability": "view", 243 | "type": "function" 244 | }, 245 | { 246 | "constant": true, 247 | "inputs": [], 248 | "name": "owner", 249 | "outputs": [ 250 | { 251 | "internalType": "address", 252 | "name": "", 253 | "type": "address" 254 | } 255 | ], 256 | "payable": false, 257 | "stateMutability": "view", 258 | "type": "function" 259 | }, 260 | { 261 | "constant": false, 262 | "inputs": [], 263 | "name": "rebuildCache", 264 | "outputs": [], 265 | "payable": false, 266 | "stateMutability": "nonpayable", 267 | "type": "function" 268 | }, 269 | { 270 | "constant": true, 271 | "inputs": [], 272 | "name": "resolver", 273 | "outputs": [ 274 | { 275 | "internalType": "contract AddressResolver", 276 | "name": "", 277 | "type": "address" 278 | } 279 | ], 280 | "payable": false, 281 | "stateMutability": "view", 282 | "type": "function" 283 | }, 284 | { 285 | "constant": true, 286 | "inputs": [], 287 | "name": "resolverAddressesRequired", 288 | "outputs": [ 289 | { 290 | "internalType": "bytes32[]", 291 | "name": "addresses", 292 | "type": "bytes32[]" 293 | } 294 | ], 295 | "payable": false, 296 | "stateMutability": "view", 297 | "type": "function" 298 | } 299 | ] 300 | -------------------------------------------------------------------------------- /abis/OneNetAggregatorDebtRatio.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "contract AddressResolver", 6 | "name": "_resolver", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor" 13 | }, 14 | { 15 | "anonymous": false, 16 | "inputs": [ 17 | { 18 | "indexed": false, 19 | "internalType": "address", 20 | "name": "oldOwner", 21 | "type": "address" 22 | }, 23 | { 24 | "indexed": false, 25 | "internalType": "address", 26 | "name": "newOwner", 27 | "type": "address" 28 | } 29 | ], 30 | "name": "OwnerChanged", 31 | "type": "event" 32 | }, 33 | { 34 | "anonymous": false, 35 | "inputs": [ 36 | { 37 | "indexed": false, 38 | "internalType": "address", 39 | "name": "newOwner", 40 | "type": "address" 41 | } 42 | ], 43 | "name": "OwnerNominated", 44 | "type": "event" 45 | }, 46 | { 47 | "anonymous": false, 48 | "inputs": [ 49 | { 50 | "indexed": false, 51 | "internalType": "uint256", 52 | "name": "timestamp", 53 | "type": "uint256" 54 | } 55 | ], 56 | "name": "SetOverrideTimestamp", 57 | "type": "event" 58 | }, 59 | { 60 | "constant": false, 61 | "inputs": [], 62 | "name": "acceptOwnership", 63 | "outputs": [], 64 | "payable": false, 65 | "stateMutability": "nonpayable", 66 | "type": "function" 67 | }, 68 | { 69 | "constant": true, 70 | "inputs": [], 71 | "name": "decimals", 72 | "outputs": [ 73 | { 74 | "internalType": "uint8", 75 | "name": "", 76 | "type": "uint8" 77 | } 78 | ], 79 | "payable": false, 80 | "stateMutability": "view", 81 | "type": "function" 82 | }, 83 | { 84 | "constant": true, 85 | "inputs": [ 86 | { 87 | "internalType": "uint256", 88 | "name": "_roundId", 89 | "type": "uint256" 90 | } 91 | ], 92 | "name": "getAnswer", 93 | "outputs": [ 94 | { 95 | "internalType": "int256", 96 | "name": "answer", 97 | "type": "int256" 98 | } 99 | ], 100 | "payable": false, 101 | "stateMutability": "view", 102 | "type": "function" 103 | }, 104 | { 105 | "constant": true, 106 | "inputs": [ 107 | { 108 | "internalType": "uint80", 109 | "name": "", 110 | "type": "uint80" 111 | } 112 | ], 113 | "name": "getRoundData", 114 | "outputs": [ 115 | { 116 | "internalType": "uint80", 117 | "name": "", 118 | "type": "uint80" 119 | }, 120 | { 121 | "internalType": "int256", 122 | "name": "", 123 | "type": "int256" 124 | }, 125 | { 126 | "internalType": "uint256", 127 | "name": "", 128 | "type": "uint256" 129 | }, 130 | { 131 | "internalType": "uint256", 132 | "name": "", 133 | "type": "uint256" 134 | }, 135 | { 136 | "internalType": "uint80", 137 | "name": "", 138 | "type": "uint80" 139 | } 140 | ], 141 | "payable": false, 142 | "stateMutability": "view", 143 | "type": "function" 144 | }, 145 | { 146 | "constant": true, 147 | "inputs": [ 148 | { 149 | "internalType": "uint256", 150 | "name": "_roundId", 151 | "type": "uint256" 152 | } 153 | ], 154 | "name": "getTimestamp", 155 | "outputs": [ 156 | { 157 | "internalType": "uint256", 158 | "name": "timestamp", 159 | "type": "uint256" 160 | } 161 | ], 162 | "payable": false, 163 | "stateMutability": "view", 164 | "type": "function" 165 | }, 166 | { 167 | "constant": true, 168 | "inputs": [], 169 | "name": "latestRound", 170 | "outputs": [ 171 | { 172 | "internalType": "uint256", 173 | "name": "", 174 | "type": "uint256" 175 | } 176 | ], 177 | "payable": false, 178 | "stateMutability": "view", 179 | "type": "function" 180 | }, 181 | { 182 | "constant": true, 183 | "inputs": [], 184 | "name": "latestRoundData", 185 | "outputs": [ 186 | { 187 | "internalType": "uint80", 188 | "name": "", 189 | "type": "uint80" 190 | }, 191 | { 192 | "internalType": "int256", 193 | "name": "", 194 | "type": "int256" 195 | }, 196 | { 197 | "internalType": "uint256", 198 | "name": "", 199 | "type": "uint256" 200 | }, 201 | { 202 | "internalType": "uint256", 203 | "name": "", 204 | "type": "uint256" 205 | }, 206 | { 207 | "internalType": "uint80", 208 | "name": "", 209 | "type": "uint80" 210 | } 211 | ], 212 | "payable": false, 213 | "stateMutability": "view", 214 | "type": "function" 215 | }, 216 | { 217 | "constant": false, 218 | "inputs": [ 219 | { 220 | "internalType": "address", 221 | "name": "_owner", 222 | "type": "address" 223 | } 224 | ], 225 | "name": "nominateNewOwner", 226 | "outputs": [], 227 | "payable": false, 228 | "stateMutability": "nonpayable", 229 | "type": "function" 230 | }, 231 | { 232 | "constant": true, 233 | "inputs": [], 234 | "name": "nominatedOwner", 235 | "outputs": [ 236 | { 237 | "internalType": "address", 238 | "name": "", 239 | "type": "address" 240 | } 241 | ], 242 | "payable": false, 243 | "stateMutability": "view", 244 | "type": "function" 245 | }, 246 | { 247 | "constant": true, 248 | "inputs": [], 249 | "name": "overrideTimestamp", 250 | "outputs": [ 251 | { 252 | "internalType": "uint256", 253 | "name": "", 254 | "type": "uint256" 255 | } 256 | ], 257 | "payable": false, 258 | "stateMutability": "view", 259 | "type": "function" 260 | }, 261 | { 262 | "constant": true, 263 | "inputs": [], 264 | "name": "owner", 265 | "outputs": [ 266 | { 267 | "internalType": "address", 268 | "name": "", 269 | "type": "address" 270 | } 271 | ], 272 | "payable": false, 273 | "stateMutability": "view", 274 | "type": "function" 275 | }, 276 | { 277 | "constant": true, 278 | "inputs": [], 279 | "name": "resolver", 280 | "outputs": [ 281 | { 282 | "internalType": "contract AddressResolver", 283 | "name": "", 284 | "type": "address" 285 | } 286 | ], 287 | "payable": false, 288 | "stateMutability": "view", 289 | "type": "function" 290 | }, 291 | { 292 | "constant": false, 293 | "inputs": [ 294 | { 295 | "internalType": "uint256", 296 | "name": "timestamp", 297 | "type": "uint256" 298 | } 299 | ], 300 | "name": "setOverrideTimestamp", 301 | "outputs": [], 302 | "payable": false, 303 | "stateMutability": "nonpayable", 304 | "type": "function" 305 | } 306 | ] 307 | -------------------------------------------------------------------------------- /abis/OneNetAggregatorIssuedSynths.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "contract AddressResolver", 6 | "name": "_resolver", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor" 13 | }, 14 | { 15 | "anonymous": false, 16 | "inputs": [ 17 | { 18 | "indexed": false, 19 | "internalType": "address", 20 | "name": "oldOwner", 21 | "type": "address" 22 | }, 23 | { 24 | "indexed": false, 25 | "internalType": "address", 26 | "name": "newOwner", 27 | "type": "address" 28 | } 29 | ], 30 | "name": "OwnerChanged", 31 | "type": "event" 32 | }, 33 | { 34 | "anonymous": false, 35 | "inputs": [ 36 | { 37 | "indexed": false, 38 | "internalType": "address", 39 | "name": "newOwner", 40 | "type": "address" 41 | } 42 | ], 43 | "name": "OwnerNominated", 44 | "type": "event" 45 | }, 46 | { 47 | "anonymous": false, 48 | "inputs": [ 49 | { 50 | "indexed": false, 51 | "internalType": "uint256", 52 | "name": "timestamp", 53 | "type": "uint256" 54 | } 55 | ], 56 | "name": "SetOverrideTimestamp", 57 | "type": "event" 58 | }, 59 | { 60 | "constant": false, 61 | "inputs": [], 62 | "name": "acceptOwnership", 63 | "outputs": [], 64 | "payable": false, 65 | "stateMutability": "nonpayable", 66 | "type": "function" 67 | }, 68 | { 69 | "constant": true, 70 | "inputs": [], 71 | "name": "decimals", 72 | "outputs": [ 73 | { 74 | "internalType": "uint8", 75 | "name": "", 76 | "type": "uint8" 77 | } 78 | ], 79 | "payable": false, 80 | "stateMutability": "view", 81 | "type": "function" 82 | }, 83 | { 84 | "constant": true, 85 | "inputs": [ 86 | { 87 | "internalType": "uint256", 88 | "name": "_roundId", 89 | "type": "uint256" 90 | } 91 | ], 92 | "name": "getAnswer", 93 | "outputs": [ 94 | { 95 | "internalType": "int256", 96 | "name": "answer", 97 | "type": "int256" 98 | } 99 | ], 100 | "payable": false, 101 | "stateMutability": "view", 102 | "type": "function" 103 | }, 104 | { 105 | "constant": true, 106 | "inputs": [ 107 | { 108 | "internalType": "uint80", 109 | "name": "", 110 | "type": "uint80" 111 | } 112 | ], 113 | "name": "getRoundData", 114 | "outputs": [ 115 | { 116 | "internalType": "uint80", 117 | "name": "", 118 | "type": "uint80" 119 | }, 120 | { 121 | "internalType": "int256", 122 | "name": "", 123 | "type": "int256" 124 | }, 125 | { 126 | "internalType": "uint256", 127 | "name": "", 128 | "type": "uint256" 129 | }, 130 | { 131 | "internalType": "uint256", 132 | "name": "", 133 | "type": "uint256" 134 | }, 135 | { 136 | "internalType": "uint80", 137 | "name": "", 138 | "type": "uint80" 139 | } 140 | ], 141 | "payable": false, 142 | "stateMutability": "view", 143 | "type": "function" 144 | }, 145 | { 146 | "constant": true, 147 | "inputs": [ 148 | { 149 | "internalType": "uint256", 150 | "name": "_roundId", 151 | "type": "uint256" 152 | } 153 | ], 154 | "name": "getTimestamp", 155 | "outputs": [ 156 | { 157 | "internalType": "uint256", 158 | "name": "timestamp", 159 | "type": "uint256" 160 | } 161 | ], 162 | "payable": false, 163 | "stateMutability": "view", 164 | "type": "function" 165 | }, 166 | { 167 | "constant": true, 168 | "inputs": [], 169 | "name": "latestRound", 170 | "outputs": [ 171 | { 172 | "internalType": "uint256", 173 | "name": "", 174 | "type": "uint256" 175 | } 176 | ], 177 | "payable": false, 178 | "stateMutability": "view", 179 | "type": "function" 180 | }, 181 | { 182 | "constant": true, 183 | "inputs": [], 184 | "name": "latestRoundData", 185 | "outputs": [ 186 | { 187 | "internalType": "uint80", 188 | "name": "", 189 | "type": "uint80" 190 | }, 191 | { 192 | "internalType": "int256", 193 | "name": "", 194 | "type": "int256" 195 | }, 196 | { 197 | "internalType": "uint256", 198 | "name": "", 199 | "type": "uint256" 200 | }, 201 | { 202 | "internalType": "uint256", 203 | "name": "", 204 | "type": "uint256" 205 | }, 206 | { 207 | "internalType": "uint80", 208 | "name": "", 209 | "type": "uint80" 210 | } 211 | ], 212 | "payable": false, 213 | "stateMutability": "view", 214 | "type": "function" 215 | }, 216 | { 217 | "constant": false, 218 | "inputs": [ 219 | { 220 | "internalType": "address", 221 | "name": "_owner", 222 | "type": "address" 223 | } 224 | ], 225 | "name": "nominateNewOwner", 226 | "outputs": [], 227 | "payable": false, 228 | "stateMutability": "nonpayable", 229 | "type": "function" 230 | }, 231 | { 232 | "constant": true, 233 | "inputs": [], 234 | "name": "nominatedOwner", 235 | "outputs": [ 236 | { 237 | "internalType": "address", 238 | "name": "", 239 | "type": "address" 240 | } 241 | ], 242 | "payable": false, 243 | "stateMutability": "view", 244 | "type": "function" 245 | }, 246 | { 247 | "constant": true, 248 | "inputs": [], 249 | "name": "overrideTimestamp", 250 | "outputs": [ 251 | { 252 | "internalType": "uint256", 253 | "name": "", 254 | "type": "uint256" 255 | } 256 | ], 257 | "payable": false, 258 | "stateMutability": "view", 259 | "type": "function" 260 | }, 261 | { 262 | "constant": true, 263 | "inputs": [], 264 | "name": "owner", 265 | "outputs": [ 266 | { 267 | "internalType": "address", 268 | "name": "", 269 | "type": "address" 270 | } 271 | ], 272 | "payable": false, 273 | "stateMutability": "view", 274 | "type": "function" 275 | }, 276 | { 277 | "constant": true, 278 | "inputs": [], 279 | "name": "resolver", 280 | "outputs": [ 281 | { 282 | "internalType": "contract AddressResolver", 283 | "name": "", 284 | "type": "address" 285 | } 286 | ], 287 | "payable": false, 288 | "stateMutability": "view", 289 | "type": "function" 290 | }, 291 | { 292 | "constant": false, 293 | "inputs": [ 294 | { 295 | "internalType": "uint256", 296 | "name": "timestamp", 297 | "type": "uint256" 298 | } 299 | ], 300 | "name": "setOverrideTimestamp", 301 | "outputs": [], 302 | "payable": false, 303 | "stateMutability": "nonpayable", 304 | "type": "function" 305 | } 306 | ] 307 | -------------------------------------------------------------------------------- /abis/SynthRedeemer.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_resolver", 7 | "type": "address" 8 | } 9 | ], 10 | "payable": false, 11 | "stateMutability": "nonpayable", 12 | "type": "constructor" 13 | }, 14 | { 15 | "anonymous": false, 16 | "inputs": [ 17 | { 18 | "indexed": false, 19 | "internalType": "bytes32", 20 | "name": "name", 21 | "type": "bytes32" 22 | }, 23 | { 24 | "indexed": false, 25 | "internalType": "address", 26 | "name": "destination", 27 | "type": "address" 28 | } 29 | ], 30 | "name": "CacheUpdated", 31 | "type": "event" 32 | }, 33 | { 34 | "anonymous": false, 35 | "inputs": [ 36 | { 37 | "indexed": false, 38 | "internalType": "address", 39 | "name": "synth", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "rateToRedeem", 46 | "type": "uint256" 47 | }, 48 | { 49 | "indexed": false, 50 | "internalType": "uint256", 51 | "name": "totalSynthSupply", 52 | "type": "uint256" 53 | }, 54 | { 55 | "indexed": false, 56 | "internalType": "uint256", 57 | "name": "supplyInsUSD", 58 | "type": "uint256" 59 | } 60 | ], 61 | "name": "SynthDeprecated", 62 | "type": "event" 63 | }, 64 | { 65 | "anonymous": false, 66 | "inputs": [ 67 | { 68 | "indexed": false, 69 | "internalType": "address", 70 | "name": "synth", 71 | "type": "address" 72 | }, 73 | { 74 | "indexed": false, 75 | "internalType": "address", 76 | "name": "account", 77 | "type": "address" 78 | }, 79 | { 80 | "indexed": false, 81 | "internalType": "uint256", 82 | "name": "amountOfSynth", 83 | "type": "uint256" 84 | }, 85 | { 86 | "indexed": false, 87 | "internalType": "uint256", 88 | "name": "amountInsUSD", 89 | "type": "uint256" 90 | } 91 | ], 92 | "name": "SynthRedeemed", 93 | "type": "event" 94 | }, 95 | { 96 | "constant": true, 97 | "inputs": [], 98 | "name": "CONTRACT_NAME", 99 | "outputs": [ 100 | { 101 | "internalType": "bytes32", 102 | "name": "", 103 | "type": "bytes32" 104 | } 105 | ], 106 | "payable": false, 107 | "stateMutability": "view", 108 | "type": "function" 109 | }, 110 | { 111 | "constant": true, 112 | "inputs": [ 113 | { 114 | "internalType": "contract IERC20", 115 | "name": "synthProxy", 116 | "type": "address" 117 | }, 118 | { 119 | "internalType": "address", 120 | "name": "account", 121 | "type": "address" 122 | } 123 | ], 124 | "name": "balanceOf", 125 | "outputs": [ 126 | { 127 | "internalType": "uint256", 128 | "name": "balanceInsUSD", 129 | "type": "uint256" 130 | } 131 | ], 132 | "payable": false, 133 | "stateMutability": "view", 134 | "type": "function" 135 | }, 136 | { 137 | "constant": false, 138 | "inputs": [ 139 | { 140 | "internalType": "contract IERC20", 141 | "name": "synthProxy", 142 | "type": "address" 143 | }, 144 | { 145 | "internalType": "uint256", 146 | "name": "rateToRedeem", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "deprecate", 151 | "outputs": [], 152 | "payable": false, 153 | "stateMutability": "nonpayable", 154 | "type": "function" 155 | }, 156 | { 157 | "constant": true, 158 | "inputs": [], 159 | "name": "isResolverCached", 160 | "outputs": [ 161 | { 162 | "internalType": "bool", 163 | "name": "", 164 | "type": "bool" 165 | } 166 | ], 167 | "payable": false, 168 | "stateMutability": "view", 169 | "type": "function" 170 | }, 171 | { 172 | "constant": false, 173 | "inputs": [], 174 | "name": "rebuildCache", 175 | "outputs": [], 176 | "payable": false, 177 | "stateMutability": "nonpayable", 178 | "type": "function" 179 | }, 180 | { 181 | "constant": false, 182 | "inputs": [ 183 | { 184 | "internalType": "contract IERC20", 185 | "name": "synthProxy", 186 | "type": "address" 187 | } 188 | ], 189 | "name": "redeem", 190 | "outputs": [], 191 | "payable": false, 192 | "stateMutability": "nonpayable", 193 | "type": "function" 194 | }, 195 | { 196 | "constant": false, 197 | "inputs": [ 198 | { 199 | "internalType": "contract IERC20[]", 200 | "name": "synthProxies", 201 | "type": "address[]" 202 | } 203 | ], 204 | "name": "redeemAll", 205 | "outputs": [], 206 | "payable": false, 207 | "stateMutability": "nonpayable", 208 | "type": "function" 209 | }, 210 | { 211 | "constant": false, 212 | "inputs": [ 213 | { 214 | "internalType": "contract IERC20", 215 | "name": "synthProxy", 216 | "type": "address" 217 | }, 218 | { 219 | "internalType": "uint256", 220 | "name": "amountOfSynth", 221 | "type": "uint256" 222 | } 223 | ], 224 | "name": "redeemPartial", 225 | "outputs": [], 226 | "payable": false, 227 | "stateMutability": "nonpayable", 228 | "type": "function" 229 | }, 230 | { 231 | "constant": true, 232 | "inputs": [ 233 | { 234 | "internalType": "address", 235 | "name": "", 236 | "type": "address" 237 | } 238 | ], 239 | "name": "redemptions", 240 | "outputs": [ 241 | { 242 | "internalType": "uint256", 243 | "name": "", 244 | "type": "uint256" 245 | } 246 | ], 247 | "payable": false, 248 | "stateMutability": "view", 249 | "type": "function" 250 | }, 251 | { 252 | "constant": true, 253 | "inputs": [], 254 | "name": "resolver", 255 | "outputs": [ 256 | { 257 | "internalType": "contract AddressResolver", 258 | "name": "", 259 | "type": "address" 260 | } 261 | ], 262 | "payable": false, 263 | "stateMutability": "view", 264 | "type": "function" 265 | }, 266 | { 267 | "constant": true, 268 | "inputs": [], 269 | "name": "resolverAddressesRequired", 270 | "outputs": [ 271 | { 272 | "internalType": "bytes32[]", 273 | "name": "addresses", 274 | "type": "bytes32[]" 275 | } 276 | ], 277 | "payable": false, 278 | "stateMutability": "view", 279 | "type": "function" 280 | }, 281 | { 282 | "constant": true, 283 | "inputs": [ 284 | { 285 | "internalType": "contract IERC20", 286 | "name": "synthProxy", 287 | "type": "address" 288 | } 289 | ], 290 | "name": "totalSupply", 291 | "outputs": [ 292 | { 293 | "internalType": "uint256", 294 | "name": "supplyInsUSD", 295 | "type": "uint256" 296 | } 297 | ], 298 | "payable": false, 299 | "stateMutability": "view", 300 | "type": "function" 301 | } 302 | ] 303 | -------------------------------------------------------------------------------- /subgraphs/issuance.js: -------------------------------------------------------------------------------- 1 | const { getContractDeployments, getCurrentNetwork, createSubgraphManifest } = require('./utils/network'); 2 | 3 | const balances = require('./fragments/balances'); 4 | 5 | const manifest = []; 6 | 7 | const OVERWRITE_HISTORICAL_BLOCK = 5873222; 8 | const HISTORICAL_PROXY_SYNTHETIX = '0xc011a72400e58ecd99ee497cf89e3775d4bd732f'; 9 | 10 | for (const contractName of ['Synthetix', 'ERC20']) { 11 | getContractDeployments('Proxy' + contractName).forEach((a, i) => { 12 | manifest.push({ 13 | kind: 'ethereum/contract', 14 | name: `issuance_${contractName}_${i}`, 15 | network: getCurrentNetwork(), 16 | source: { 17 | address: a.address, 18 | startBlock: Math.max( 19 | parseInt(process.env.SNX_START_BLOCK || '0'), 20 | a.address.toLowerCase() === HISTORICAL_PROXY_SYNTHETIX ? OVERWRITE_HISTORICAL_BLOCK : a.startBlock, 21 | ), 22 | abi: 'Synthetix', 23 | }, 24 | mapping: { 25 | kind: 'ethereum/events', 26 | apiVersion: '0.0.5', 27 | language: 'wasm/assemblyscript', 28 | file: '../src/issuance.ts', 29 | entities: ['SNXTransfer'], 30 | abis: [ 31 | { 32 | name: 'Synthetix', 33 | file: '../abis/SynthetixGlobalDebt.json', 34 | }, 35 | { 36 | name: 'Synthetix4', 37 | file: '../abis/Synthetix_bytes4.json', 38 | }, 39 | { 40 | name: 'Synthetix32', 41 | file: '../abis/Synthetix_bytes32.json', 42 | }, 43 | { 44 | name: 'AddressResolver', 45 | file: '../abis/AddressResolver.json', 46 | }, 47 | { 48 | name: 'SynthetixState', 49 | file: '../abis/SynthetixState.json', 50 | }, 51 | { 52 | // needed to track supply 53 | name: 'Synth', 54 | file: '../abis/Synth.json', 55 | }, 56 | ], 57 | eventHandlers: [ 58 | { 59 | event: 'Transfer(indexed address,indexed address,uint256)', 60 | handler: 'handleTransferSNX', 61 | }, 62 | ], 63 | }, 64 | }); 65 | }); 66 | } 67 | 68 | getContractDeployments('ProxyFeePool').forEach((a, i) => { 69 | manifest.push({ 70 | kind: 'ethereum/contract', 71 | name: `issuance_FeePool_${i}`, 72 | network: getCurrentNetwork(), 73 | source: { 74 | address: a.address, 75 | startBlock: a.startBlock, 76 | abi: 'FeePool', 77 | }, 78 | mapping: { 79 | kind: 'ethereum/events', 80 | apiVersion: '0.0.5', 81 | language: 'wasm/assemblyscript', 82 | file: '../src/issuance.ts', 83 | entities: ['FeesClaimed', 'SNXHolder', 'FeePeriod'], 84 | abis: [ 85 | { 86 | name: 'FeePool', 87 | file: '../abis/FeePool.json', 88 | }, 89 | { 90 | name: 'FeePoolv217', 91 | file: '../abis/FeePool_v2.17.json', 92 | }, 93 | { 94 | name: 'Synthetix4', 95 | file: '../abis/Synthetix_bytes4.json', 96 | }, 97 | { 98 | name: 'Synthetix32', 99 | file: '../abis/Synthetix_bytes32.json', 100 | }, 101 | ], 102 | eventHandlers: [ 103 | { 104 | event: 'FeesClaimed(address,uint256,uint256)', 105 | handler: 'handleFeesClaimed', 106 | }, 107 | { 108 | event: 'FeePeriodClosed(uint256)', 109 | handler: 'handleFeePeriodClosed', 110 | }, 111 | ], 112 | }, 113 | }); 114 | }); 115 | 116 | getContractDeployments('RewardEscrow').forEach((a, i) => { 117 | manifest.push({ 118 | kind: 'ethereum/contract', 119 | name: `issuance_RewardEscrow_${i}`, 120 | network: getCurrentNetwork(), 121 | source: { 122 | address: a.address, 123 | startBlock: a.startBlock, 124 | abi: 'RewardEscrow', 125 | }, 126 | mapping: { 127 | kind: 'ethereum/events', 128 | apiVersion: '0.0.5', 129 | language: 'wasm/assemblyscript', 130 | file: '../src/issuance.ts', 131 | entities: ['RewardEscrowHolder', 'SNXHolder'], 132 | abis: [ 133 | { 134 | name: 'RewardEscrow', 135 | file: '../abis/RewardEscrow.json', 136 | }, 137 | { 138 | name: 'Synthetix', 139 | file: '../abis/SynthetixGlobalDebt.json', 140 | }, 141 | { 142 | name: 'Synthetix4', 143 | file: '../abis/Synthetix_bytes4.json', 144 | }, 145 | { 146 | name: 'Synthetix32', 147 | file: '../abis/Synthetix_bytes32.json', 148 | }, 149 | { 150 | name: 'Synthetix32', 151 | file: '../abis/Synthetix_bytes32.json', 152 | }, 153 | { 154 | name: 'AddressResolver', 155 | file: '../abis/AddressResolver.json', 156 | }, 157 | { 158 | name: 'SynthetixState', 159 | file: '../abis/SynthetixState.json', 160 | }, 161 | ], 162 | eventHandlers: [ 163 | { 164 | event: 'VestingEntryCreated(indexed address,uint256,uint256)', 165 | handler: 'handleRewardVestEvent', 166 | }, 167 | { 168 | event: 'Vested(indexed address,uint256,uint256)', 169 | handler: 'handleRewardVestEvent', 170 | }, 171 | ], 172 | }, 173 | }); 174 | }); 175 | 176 | for (const token of ['sUSD', 'ERC20sUSD']) { 177 | getContractDeployments('Proxy' + token).forEach((a, i) => { 178 | manifest.push({ 179 | kind: 'ethereum/contract', 180 | name: `issuance_Synth${token}_${i}`, 181 | network: getCurrentNetwork(), 182 | source: { 183 | address: a.address, 184 | startBlock: a.startBlock, 185 | abi: 'Synth', 186 | }, 187 | mapping: { 188 | kind: 'ethereum/events', 189 | apiVersion: '0.0.5', 190 | language: 'wasm/assemblyscript', 191 | file: '../src/issuance.ts', 192 | entities: ['Issued', 'Burned', 'DailyIssued', 'DailyBurned'], 193 | abis: [ 194 | { 195 | name: 'Synth', 196 | file: '../abis/Synth.json', 197 | }, 198 | { 199 | name: 'Synthetix', 200 | file: '../abis/SynthetixGlobalDebt.json', 201 | }, 202 | { 203 | name: 'Synthetix4', 204 | file: '../abis/Synthetix_bytes4.json', 205 | }, 206 | { 207 | name: 'Synthetix32', 208 | file: '../abis/Synthetix_bytes32.json', 209 | }, 210 | { 211 | name: 'AddressResolver', 212 | file: '../abis/AddressResolver.json', 213 | }, 214 | { 215 | name: 'SynthetixState', 216 | file: '../abis/SynthetixState.json', 217 | }, 218 | ], 219 | eventHandlers: [ 220 | { 221 | event: 'Issued(indexed address,uint256)', 222 | handler: 'handleIssuedSynths', 223 | }, 224 | { 225 | event: 'Burned(indexed address,uint256)', 226 | handler: 'handleBurnedSynths', 227 | }, 228 | ], 229 | }, 230 | }); 231 | }); 232 | } 233 | 234 | manifest.push(...balances.dataSources); 235 | 236 | module.exports = createSubgraphManifest('issuance', manifest, []); 237 | -------------------------------------------------------------------------------- /src/periodic-updates.ts: -------------------------------------------------------------------------------- 1 | // The latest Synthetix and event invocations 2 | 3 | import { Synthetix as SNX } from '../generated/subgraphs/periodic-updates/periodicUpdates_ProxyERC20_0/Synthetix'; 4 | import { SynthetixDebtShare } from '../generated/subgraphs/periodic-updates/periodicUpdates_ProxyERC20_0/SynthetixDebtShare'; 5 | import { SystemSettings as SystemSettingsContract } from '../generated/subgraphs/periodic-updates/periodicUpdates_ProxyERC20_0/SystemSettings'; 6 | 7 | import { CANDLE_PERIODS, strToBytes, toDecimal, ZERO } from './lib/helpers'; 8 | 9 | // SynthetixState has not changed ABI since deployment 10 | 11 | import { DebtState, SystemSetting } from '../generated/subgraphs/periodic-updates/schema'; 12 | 13 | import { BigInt, ethereum, dataSource, log } from '@graphprotocol/graph-ts'; 14 | import { getContractDeployment } from '../generated/addresses'; 15 | 16 | export function handleBlock(block: ethereum.Block): void { 17 | if (block.number.mod(BigInt.fromI32(6000)).equals(BigInt.fromI32(0))) { 18 | trackSystemSettings(block); 19 | } 20 | if (block.number.mod(BigInt.fromI32(25)).equals(BigInt.fromI32(0))) { 21 | trackGlobalDebt(block); 22 | } 23 | } 24 | 25 | export function trackSystemSettings(block: ethereum.Block): void { 26 | let timeSlot = block.timestamp.minus(block.timestamp.mod(BigInt.fromI32(900))); 27 | 28 | let curSystemSettings = SystemSetting.load(timeSlot.toString()); 29 | 30 | if (curSystemSettings == null) { 31 | let systemSettingsAddress = getContractDeployment('SystemSettings', dataSource.network(), block.number)!; 32 | let systemSettings = SystemSettingsContract.bind(systemSettingsAddress); 33 | let systemSettingsEntity = new SystemSetting(timeSlot.toString()); 34 | systemSettingsEntity.timestamp = block.timestamp; 35 | 36 | let waitingPeriodSecs = systemSettings.try_waitingPeriodSecs(); 37 | if (!waitingPeriodSecs.reverted) { 38 | systemSettingsEntity.waitingPeriodSecs = waitingPeriodSecs.value; 39 | } 40 | 41 | let priceDeviationThresholdFactor = systemSettings.try_priceDeviationThresholdFactor(); 42 | if (!priceDeviationThresholdFactor.reverted) { 43 | systemSettingsEntity.priceDeviationThresholdFactor = toDecimal(priceDeviationThresholdFactor.value); 44 | } 45 | 46 | let issuanceRatio = systemSettings.try_issuanceRatio(); 47 | if (!issuanceRatio.reverted) { 48 | systemSettingsEntity.issuanceRatio = toDecimal(issuanceRatio.value); 49 | } 50 | 51 | let feePeriodDuration = systemSettings.try_feePeriodDuration(); 52 | if (!feePeriodDuration.reverted) { 53 | systemSettingsEntity.feePeriodDuration = feePeriodDuration.value; 54 | } 55 | 56 | let targetThreshold = systemSettings.try_targetThreshold(); 57 | if (!targetThreshold.reverted) { 58 | systemSettingsEntity.targetThreshold = toDecimal(targetThreshold.value); 59 | } 60 | 61 | let liquidationDelay = systemSettings.try_liquidationDelay(); 62 | if (!liquidationDelay.reverted) { 63 | systemSettingsEntity.liquidationDelay = liquidationDelay.value; 64 | } 65 | 66 | let liquidationRatio = systemSettings.try_liquidationRatio(); 67 | if (!liquidationRatio.reverted) { 68 | systemSettingsEntity.liquidationRatio = toDecimal(liquidationRatio.value); 69 | } 70 | 71 | let liquidationPenalty = systemSettings.try_liquidationPenalty(); 72 | if (!liquidationPenalty.reverted) { 73 | systemSettingsEntity.liquidationPenalty = toDecimal(liquidationPenalty.value); 74 | } 75 | 76 | let rateStalePeriod = systemSettings.try_rateStalePeriod(); 77 | if (!rateStalePeriod.reverted) { 78 | systemSettingsEntity.rateStalePeriod = rateStalePeriod.value; 79 | } 80 | 81 | let debtSnapshotStaleTime = systemSettings.try_debtSnapshotStaleTime(); 82 | if (!debtSnapshotStaleTime.reverted) { 83 | systemSettingsEntity.debtSnapshotStaleTime = debtSnapshotStaleTime.value; 84 | } 85 | 86 | let aggregatorWarningFlags = systemSettings.try_aggregatorWarningFlags(); 87 | if (!aggregatorWarningFlags.reverted) { 88 | systemSettingsEntity.aggregatorWarningFlags = aggregatorWarningFlags.value.toHexString(); 89 | } 90 | 91 | let etherWrapperMaxETH = systemSettings.try_etherWrapperMaxETH(); 92 | if (!etherWrapperMaxETH.reverted) { 93 | systemSettingsEntity.etherWrapperMaxETH = toDecimal(etherWrapperMaxETH.value); 94 | } 95 | 96 | let etherWrapperMintFeeRate = systemSettings.try_etherWrapperMintFeeRate(); 97 | if (!etherWrapperMintFeeRate.reverted) { 98 | systemSettingsEntity.etherWrapperMintFeeRate = toDecimal(etherWrapperMintFeeRate.value); 99 | } 100 | 101 | let etherWrapperBurnFeeRate = systemSettings.try_etherWrapperBurnFeeRate(); 102 | if (!etherWrapperBurnFeeRate.reverted) { 103 | systemSettingsEntity.etherWrapperBurnFeeRate = toDecimal(etherWrapperBurnFeeRate.value); 104 | } 105 | 106 | let atomicMaxVolumePerBlock = systemSettings.try_atomicMaxVolumePerBlock(); 107 | if (!atomicMaxVolumePerBlock.reverted) { 108 | systemSettingsEntity.atomicMaxVolumePerBlock = atomicMaxVolumePerBlock.value; 109 | } 110 | 111 | let atomicTwapWindow = systemSettings.try_atomicTwapWindow(); 112 | if (!atomicTwapWindow.reverted) { 113 | systemSettingsEntity.atomicTwapWindow = atomicTwapWindow.value; 114 | } 115 | 116 | systemSettingsEntity.save(); 117 | } 118 | } 119 | 120 | export function trackGlobalDebt(block: ethereum.Block): void { 121 | let timeSlot = block.timestamp.minus(block.timestamp.mod(BigInt.fromI32(900))); 122 | 123 | let curDebtState = DebtState.load(timeSlot.toString()); 124 | 125 | if (curDebtState == null) { 126 | let sdsAddress = getContractDeployment('SynthetixDebtShare', dataSource.network(), block.number)!; 127 | let sds = SynthetixDebtShare.bind(sdsAddress); 128 | 129 | let synthetix = SNX.bind(dataSource.address()); 130 | let issuedSynths = synthetix.try_totalIssuedSynthsExcludeOtherCollateral(strToBytes('sUSD', 32)); 131 | 132 | if (issuedSynths.reverted) { 133 | issuedSynths = synthetix.try_totalIssuedSynthsExcludeEtherCollateral(strToBytes('sUSD', 32)); 134 | 135 | if (issuedSynths.reverted) { 136 | issuedSynths = synthetix.try_totalIssuedSynths(strToBytes('sUSD', 32)); 137 | if (issuedSynths.reverted) { 138 | // for some reason this can happen (not sure how) 139 | log.debug('failed to get issued synths (skip', []); 140 | return; 141 | } 142 | } 143 | } 144 | 145 | let debtSharesSupply = sds.try_totalSupply(); 146 | if (!debtSharesSupply.reverted) { 147 | for (let p = 0; p < CANDLE_PERIODS.length; p++) { 148 | let period = CANDLE_PERIODS[p]; 149 | let periodId = block.timestamp.minus(block.timestamp.mod(period)); 150 | let id = period.toString() + '-' + periodId.toString(); 151 | 152 | let debtStateEntity = new DebtState(id); 153 | 154 | debtStateEntity.debtEntry = toDecimal(debtSharesSupply.value); 155 | debtStateEntity.totalIssuedSynths = toDecimal(issuedSynths.value); 156 | 157 | debtStateEntity.debtRatio = debtStateEntity.debtEntry.equals(toDecimal(ZERO)) 158 | ? toDecimal(ZERO) 159 | : debtStateEntity.totalIssuedSynths.div(debtStateEntity.debtEntry); 160 | 161 | debtStateEntity.timestamp = block.timestamp; 162 | debtStateEntity.period = period; 163 | 164 | debtStateEntity.save(); 165 | } 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /subgraphs/exchanges.graphql: -------------------------------------------------------------------------------- 1 | " Tracks this event from the Synthetix.sol contract " 2 | type SynthExchange @entity { 3 | id: ID! 4 | account: Exchanger! # address 5 | fromSynth: Synth 6 | toSynth: Synth 7 | fromAmount: BigDecimal! # uint256 8 | fromAmountInUSD: BigDecimal! # from Amount including fees 9 | toAmount: BigDecimal! # uint256 10 | toAmountInUSD: BigDecimal! # to Amount without fees 11 | feesInUSD: BigDecimal! 12 | toAddress: Bytes! # address 13 | timestamp: BigInt! 14 | gasPrice: BigInt! 15 | } 16 | 17 | " Tracks this event from the Synthetix.sol contract. (Atomic exchanges also trigger standard SynthExchange events.)" 18 | type AtomicSynthExchange @entity { 19 | id: ID! 20 | account: Exchanger! # address 21 | fromSynth: Synth 22 | toSynth: Synth 23 | fromAmount: BigDecimal! # uint256 24 | fromAmountInUSD: BigDecimal! # from Amount including fees 25 | toAmount: BigDecimal! # uint256 26 | toAmountInUSD: BigDecimal! # to Amount without fees 27 | feesInUSD: BigDecimal! 28 | toAddress: Bytes! # address 29 | timestamp: BigInt! 30 | gasPrice: BigInt! 31 | } 32 | 33 | " Tracks this event from the Synthetix.sol contract " 34 | type ExchangeReclaim @entity { 35 | id: ID! 36 | account: Exchanger! # address 37 | currencyKey: Bytes! # bytes4 38 | amount: BigDecimal! # uint256 39 | amountInUSD: BigDecimal! # uint256 40 | timestamp: BigInt! 41 | gasPrice: BigInt! 42 | block: BigInt! 43 | } 44 | 45 | " Tracks this event from the Synthetix.sol contract " 46 | type ExchangeRebate @entity { 47 | id: ID! 48 | account: Exchanger! 49 | currencyKey: Bytes! # bytes4 50 | amount: BigDecimal! # uint256 51 | amountInUSD: BigDecimal! # uint256 52 | timestamp: BigInt! 53 | gasPrice: BigInt! 54 | block: BigInt! 55 | } 56 | 57 | # Aggrgeations of the synth exchanges by various time/volume groupings 58 | type Total @entity { 59 | " $timestamp-$bucketMagnitude-$synth-$period " 60 | id: ID! 61 | 62 | " timestamp of the beginning of the time period this represents " 63 | timestamp: BigInt! 64 | 65 | " which product the volume came from. Ex 'futures' or 'exchange' " 66 | product: String 67 | 68 | " number of seconds the data covers after `timestamp` " 69 | period: BigInt! 70 | 71 | " minimum power of 10 (in from USD value) the trade must be. ex, 2 means $100 or higher) " 72 | bucketMagnitude: BigInt! 73 | 74 | " synth to filter by " 75 | synth: Synth 76 | 77 | " number of trades completed over period " 78 | trades: BigInt! 79 | 80 | " number of unique traders who were first seen in this period " 81 | newExchangers: BigInt! 82 | 83 | " number of unique traders seen over period " 84 | exchangers: BigInt! 85 | 86 | " synth value exchanged in USD units " 87 | exchangeUSDTally: BigDecimal! 88 | 89 | " synth value received in fees in USD units " 90 | totalFeesGeneratedInUSD: BigDecimal! 91 | } 92 | 93 | " An individual Exchanger aggregated by various time groupings " 94 | type Exchanger @entity { 95 | " hex address in lowercase (and for non global: hex address in lowercase-$timestamp-$period-$bucketMagnitude-$synth " 96 | id: ID! 97 | 98 | " timestamp of the beginning of the time period this represents, or 0 for no period filter " 99 | timestamp: BigInt! 100 | 101 | " number of seconds the data covers after `timestamp`, or 0 for no period filter " 102 | period: BigInt! 103 | 104 | " minimum power of 10 (in from USD value) the trade must be. ex, 2 means $100 or higher) " 105 | bucketMagnitude: BigInt! 106 | 107 | " synth to filter by " 108 | synth: Synth 109 | 110 | " when the user first exchanged " 111 | firstSeen: BigInt! 112 | 113 | " when the user last exchanged " 114 | lastSeen: BigInt! 115 | 116 | " nubmer of trades by account " 117 | trades: BigInt! 118 | 119 | " synth value exchanged in USD units by account " 120 | exchangeUSDTally: BigDecimal! 121 | 122 | " synth value received in fees in USD units from account " 123 | totalFeesGeneratedInUSD: BigDecimal! 124 | 125 | " balances " 126 | balances: [LatestSynthBalance!]! 127 | 128 | " exchanges " 129 | exchanges: [SynthExchange!]! @derivedFrom(field: "account") 130 | } 131 | 132 | type ExchangeFee @entity { 133 | " Name of the synth. E.g. sUSD " 134 | id: ID! 135 | 136 | " Current Fee as a ratio of the trade amount " 137 | fee: BigDecimal! 138 | } 139 | 140 | type Candle @entity { 141 | " synth-period-periodId (periodId is timestamp / period) " 142 | id: ID! 143 | " Ticker for synth (e.g. 'sUSD') or 'SNX'" 144 | synth: String! 145 | open: BigDecimal! 146 | high: BigDecimal! 147 | low: BigDecimal! 148 | close: BigDecimal! 149 | average: BigDecimal! 150 | timestamp: BigInt! 151 | " Duration this candle captures in seconds. Year, quarter, month, week, day, hour, and 15 minutes available. " 152 | period: BigInt! 153 | " Number of RateUpdates aggregated into this candle, mostly useful for the indexer to calculate averages " 154 | aggregatedPrices: BigInt! 155 | } 156 | 157 | " DEPRECATED: See the Candles entity" 158 | type DailyCandle @entity { 159 | " DEPRECATED: See the Candles entity " 160 | id: ID! 161 | synth: String! 162 | open: BigDecimal! 163 | high: BigDecimal! 164 | low: BigDecimal! 165 | close: BigDecimal! 166 | timestamp: BigInt! 167 | } 168 | 169 | type InversePricingInfo @entity { 170 | " Name of inverse synth. E.g. iETH " 171 | id: ID! 172 | 173 | " whether or not this inverse synth has been frozen " 174 | frozen: Boolean! 175 | 176 | " configured upper limit " 177 | upperLimit: BigDecimal! 178 | 179 | " configured lower limit " 180 | lowerLimit: BigDecimal! 181 | 182 | " matching price point with long synth " 183 | entryPoint: BigDecimal! 184 | } 185 | 186 | type LatestRate @entity { 187 | " Name of synth. E.g. sUSD " 188 | id: ID! 189 | 190 | " Synth USD rate " 191 | rate: BigDecimal! 192 | 193 | " Address of the aggregator which produces current result " 194 | aggregator: Bytes! 195 | } 196 | 197 | " Latest Rates over time " 198 | type RateUpdate @entity { 199 | " - " 200 | id: ID! 201 | " currencyKey for which this this rate update applies " 202 | currencyKey: Bytes! 203 | " currencyKey expressed as a string " 204 | synth: String! 205 | " the rate recorded at this timestamp " 206 | rate: BigDecimal! 207 | " the block which this rate was recorded " 208 | block: BigInt! 209 | " timestamp of the block in which the rate was recorded " 210 | timestamp: BigInt! 211 | } 212 | 213 | type SynthByCurrencyKey @entity { 214 | " currency key " 215 | id: ID! 216 | proxyAddress: Bytes! 217 | } 218 | 219 | type Synth @entity { 220 | " lowercase address of the proxy contract for the synth " 221 | id: ID! 222 | name: String! 223 | symbol: String! 224 | 225 | totalSupply: BigDecimal! 226 | } 227 | 228 | type SynthBalance @entity { 229 | " timestamp + account + synth address " 230 | id: ID! 231 | amount: BigDecimal! 232 | address: Bytes! 233 | account: String! # using a string here because its ID compatible 234 | timestamp: BigInt! 235 | synth: Synth 236 | } 237 | 238 | " we dont query this entity but only use it to store aggregate data we need during syncing " 239 | type LatestSynthBalance @entity { 240 | " account + synth address " 241 | id: ID! 242 | amount: BigDecimal! 243 | address: Bytes! 244 | account: String! 245 | timestamp: BigInt! 246 | synth: Synth 247 | } 248 | 249 | type FuturesMarket @entity { 250 | " Address of the market " 251 | id: ID! 252 | } 253 | --------------------------------------------------------------------------------