UNPKG

@synthr/js

Version:

The smart contracts which make up the Synthr system. (synthr.io)

827 lines (761 loc) 24 kB
'use strict'; const w3utils = require('web3-utils'); const abiDecoder = require('abi-decoder'); // load the data in explicitly (not programmatically) so webpack knows what to bundle const data = { mainnet: require('./publish/deployed/mainnet'), rinkeby: require('./publish/deployed/rinkeby'), goerli: require('./publish/deployed/goerli'), fuji: require('./publish/deployed/fuji'), 'goerli-ovm': require('./publish/deployed/goerli-ovm'), 'local-ovm': require('./publish/deployed/local-ovm'), 'mainnet-ovm': require('./publish/deployed/mainnet-ovm'), bsc: require('./publish/deployed/bsc'), 'bsc-testnet': require('./publish/deployed/bsc-testnet'), polygon: require('./publish/deployed/polygon'), mumbai: require('./publish/deployed/mumbai'), arbitrum: require('./publish/deployed/arbitrum'), 'arbitrum-goerli': require('./publish/deployed/arbitrum-goerli'), metis: require('./publish/deployed/metis'), 'metis-goerli': require('./publish/deployed/metis-goerli'), }; const assets = require('./publish/assets.json'); const nonUpgradeable = require('./publish/non-upgradeable.json'); const releases = require('./publish/releases.json'); const networks = [ 'local', 'mainnet', 'rinkeby', 'goerli', 'bsc', 'bsc-testnet', 'polygon', 'mumbai', 'arbitrum', 'arbitrum-goerli', 'fuji', 'metis', 'metis-goerli' ]; const chainIdMapping = Object.entries({ 1: { network: 'mainnet', }, 4: { network: 'rinkeby', }, 5: { network: 'goerli', }, 43113: { network: 'fuji', }, 56: { network: 'bsc', }, 97: { network: 'bsc-testnet', }, 137: { network: 'polygon', }, 80001: { network: 'mumbai', }, 42161: { network: 'arbitrum', }, 421613: { network: 'arbitrum-goerli', }, // Hardhat fork of mainnet: https://hardhat.org/config/#hardhat-network 31337: { network: 'mainnet', fork: true, }, 1088: { network: 'metis', }, 599: { network: 'metis-goerli', }, // OVM networks: see https://github.com/ethereum-optimism/regenesis/ 10: { network: 'mainnet', useOvm: true, }, 420: { network: 'goerli', useOvm: true, }, '-1': { // no chain ID for this currently network: 'unknown', useOvm: true, }, // now append any defaults }).reduce((memo, [id, body]) => { memo[id] = Object.assign({ useOvm: false, fork: false }, body); return memo; }, {}); const getNetworkFromId = ({ id }) => chainIdMapping[id]; const networkToChainId = Object.entries(chainIdMapping).reduce( (memo, [id, { network, useOvm, fork }]) => { memo[network + (useOvm ? '-ovm' : '') + (fork ? '-fork' : '')] = id; return memo; }, {} ); const constants = { BUILD_FOLDER: 'build', CONTRACTS_FOLDER: 'contracts', MIGRATIONS_FOLDER: 'migrations', COMPILED_FOLDER: 'compiled', FLATTENED_FOLDER: 'flattened', AST_FOLDER: 'ast', CONFIG_FILENAME: 'config.json', RELEASES_FILENAME: 'releases.json', PARAMS_FILENAME: 'params.json', SYNTHS_FILENAME: 'synths.json', STAKING_REWARDS_FILENAME: 'rewards.json', SHORTING_REWARDS_FILENAME: 'shorting-rewards.json', OWNER_ACTIONS_FILENAME: 'owner-actions.json', DEPLOYMENT_FILENAME: 'deployment.json', VERSIONS_FILENAME: 'versions.json', FEEDS_FILENAME: 'feeds.json', FUTURES_MARKETS_FILENAME: 'futures-markets.json', AST_FILENAME: 'asts.json', ZERO_ADDRESS: '0x' + '0'.repeat(40), ZERO_BYTES32: '0x' + '0'.repeat(64), inflationStartTimestampInSecs: 1551830400, // 2019-03-06T00:00:00+00:00 }; const knownAccounts = { mainnet: [ { name: 'binance', // Binance 8 Wallet address: '0xF977814e90dA44bFA03b6295A0616a897441aceC', }, { name: 'renBTCWallet', // KeeperDAO wallet (has renBTC and ETH) address: '0x35ffd6e268610e764ff6944d07760d0efe5e40e5', }, { name: 'loansAccount', address: '0x62f7A1F94aba23eD2dD108F8D23Aa3e7d452565B', }, ], rinkeby: [ { name: 'binance', // Binance 8 Wallet address: '0x02aB62496EE0b0dF9ff58f181E237923d8Ad1cBb', }, { name: 'renBTCWallet', // KeeperDAO wallet (has renBTC and ETH) address: '0x02aB62496EE0b0dF9ff58f181E237923d8Ad1cBb', }, { name: 'loansAccount', address: '0x02aB62496EE0b0dF9ff58f181E237923d8Ad1cBb', }, ], }; // The solidity defaults are managed here in the same format they will be stored, hence all // numbers are converted to strings and those with 18 decimals are also converted to wei amounts const defaults = { TEMP_OWNER_DEFAULT_DURATION: 60 * 60 * 24 * 60, // 60 days WAITING_PERIOD_SECS: (60 * 5).toString(), // 5 mins PRICE_DEVIATION_THRESHOLD_FACTOR: w3utils.toWei('3'), TRADING_REWARDS_ENABLED: false, ISSUANCE_RATIO: w3utils .toBN(1) .mul(w3utils.toBN(1e18)) .div(w3utils.toBN(3)) .toString(), // 1/3 = 0.3333333333 // 300% ratio FEE_PERIOD_DURATION: (3600 * 24 * 7).toString(), // 1 week TARGET_THRESHOLD: '1', // 1% target threshold (it will be converted to a decimal when set) LIQUIDATION_DELAY: (3600 * 24).toString(), // 24 hours LIQUIDATION_RATIO: w3utils .toBN(1) .mul(w3utils.toBN(2e18)) .div(w3utils.toBN(3)) .toString(), // 2/3 = 0.6666666667 // 150% ratio LIQUIDATION_ESCROW_DURATION: (3600 * 24 * 365).toString(), // 1 year LIQUIDATION_PENALTY: w3utils.toWei('0.1'), // 10% penalty (used for Collateral liquidations) SNX_LIQUIDATION_PENALTY: w3utils.toWei('0.3'), // 30% penalty (used for SNX Liquidations) SELF_LIQUIDATION_PENALTY: w3utils.toWei('0.2'), // 20% penalty FLAG_REWARD: w3utils.toWei('10'), // 10 SNX LIQUIDATE_REWARD: w3utils.toWei('20'), // 20 SNX RATE_STALE_PERIOD: (3600 * 25).toString(), // 25 hours EXCHANGE_FEE_RATES: { forex: w3utils.toWei('0.003'), commodity: w3utils.toWei('0.003'), equities: w3utils.toWei('0.003'), crypto: w3utils.toWei('0.01'), index: w3utils.toWei('0.01'), }, EXCHANGE_DYNAMIC_FEE_THRESHOLD: w3utils.toWei('0.0025'), EXCHANGE_DYNAMIC_FEE_WEIGHT_DECAY: w3utils.toWei('0.95'), // dynamic fee weight decay for each round EXCHANGE_DYNAMIC_FEE_ROUNDS: '6', // dynamic fee rounds EXCHANGE_MAX_DYNAMIC_FEE: w3utils.toWei('0.015'), // cap max dynamic fee MINIMUM_STAKE_TIME: (3600 * 24).toString(), // 1 days DEBT_SNAPSHOT_STALE_TIME: (43800).toString(), // 12 hour heartbeat + 10 minutes mining time AGGREGATOR_WARNING_FLAGS: { mainnet: '0x4A5b9B4aD08616D11F3A402FF7cBEAcB732a76C6', rinkeby: '0x6292aa9a6650ae14fbf974e5029f36f95a1848fd', goerli: '0x6292aa9a6650ae14fbf974e5029f36f95a1848fd', // TODO: get actual goerli address }, RENBTC_ERC20_ADDRESSES: { mainnet: '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', rinkeby: '0x6292aa9a6650ae14fbf974e5029f36f95a1848fd', goerli: '0x9B2fE385cEDea62D839E4dE89B0A23EF4eacC717', // TODO: get actual goerli address }, WETH_ERC20_ADDRESSES: { mainnet: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', rinkeby: '0x6292aa9a6650ae14fbf974e5029f36f95a1848fd', goerli: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', 'mainnet-ovm': '0x4200000000000000000000000000000000000006', 'goerli-ovm': '0x4200000000000000000000000000000000000006', // TODO: get actual goerli-ovm address }, INITIAL_ISSUANCE: w3utils.toWei(`${100e6}`), CROSS_DOMAIN_DEPOSIT_GAS_LIMIT: `${3e6}`, CROSS_DOMAIN_ESCROW_GAS_LIMIT: `${8e6}`, CROSS_DOMAIN_REWARD_GAS_LIMIT: `${8e6}`, CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT: `${3e6}`, CROSS_DOMAIN_RELAY_GAS_LIMIT: `${8e6}`, CROSS_DOMAIN_FEE_PERIOD_CLOSE_GAS_LIMIT: `${8e6}`, COLLATERAL_MANAGER: { SYNTHS: ['sUSD', 'sBTC', 'sETH'], SHORTS: ['sBTC', 'sETH'], MAX_DEBT: w3utils.toWei('75000000'), // 75 million sUSD MAX_SKEW_RATE: w3utils.toWei('0.2'), BASE_BORROW_RATE: Math.round((0.005 * 1e18) / 31556926).toString(), // 31556926 is CollateralManager seconds per year BASE_SHORT_RATE: Math.round((0.005 * 1e18) / 31556926).toString(), }, COLLATERAL_ETH: { SYNTHS: ['sUSD', 'sETH'], MIN_CRATIO: w3utils.toWei('1.3'), MIN_COLLATERAL: w3utils.toWei('2'), ISSUE_FEE_RATE: w3utils.toWei('0.001'), }, COLLATERAL_RENBTC: { SYNTHS: ['sUSD', 'sBTC'], MIN_CRATIO: w3utils.toWei('1.3'), MIN_COLLATERAL: w3utils.toWei('0.05'), ISSUE_FEE_RATE: w3utils.toWei('0.001'), }, COLLATERAL_SHORT: { SYNTHS: ['sBTC', 'sETH'], MIN_CRATIO: w3utils.toWei('1.2'), MIN_COLLATERAL: w3utils.toWei('1000'), ISSUE_FEE_RATE: w3utils.toWei('0.005'), INTERACTION_DELAY: '3600', // 1 hour in secs COLLAPSE_FEE_RATE: '0', }, ETHER_WRAPPER_MAX_ETH: w3utils.toWei('5000'), ETHER_WRAPPER_MINT_FEE_RATE: w3utils.toWei('0.005'), // 5 bps ETHER_WRAPPER_BURN_FEE_RATE: '0', FUTURES_MIN_KEEPER_FEE: w3utils.toWei('1'), // 1 sUSD min keeper fee FUTURES_LIQUIDATION_FEE_RATIO: w3utils.toWei('0.0035'), // 35 basis points liquidation incentive FUTURES_LIQUIDATION_BUFFER_RATIO: w3utils.toWei('0.0025'), // 25 basis points liquidation buffer FUTURES_MIN_INITIAL_MARGIN: w3utils.toWei('40'), // minimum initial margin for all markets // SIP-120 ATOMIC_MAX_VOLUME_PER_BLOCK: w3utils.toWei(`${2e5}`), // 200k ATOMIC_TWAP_WINDOW: '1800', // 30 mins }; /** * Converts a string into a hex representation of bytes32, with right padding */ const toBytes32 = key => w3utils.rightPad(w3utils.asciiToHex(key), 64); const fromBytes32 = key => w3utils.hexToAscii(key); const getFolderNameForNetwork = ({ network, useOvm = false }) => { if (network.includes('ovm')) { return network; } return useOvm ? `${network}-ovm` : network; }; const getPathToNetwork = ({ network = 'mainnet', file = '', useOvm = false, path } = {}) => path.join(__dirname, 'publish', 'deployed', getFolderNameForNetwork({ network, useOvm }), file); // Pass in fs and path to avoid webpack wrapping those const loadDeploymentFile = ({ network = 'mainnet', path, fs, deploymentPath, useOvm = false }) => { if (!deploymentPath && (!path || !fs)) { return data[getFolderNameForNetwork({ network, useOvm })].deployment; } const pathToDeployment = deploymentPath ? path.join(deploymentPath, constants.DEPLOYMENT_FILENAME) : getPathToNetwork({ network, useOvm, path, file: constants.DEPLOYMENT_FILENAME }); if (!fs.existsSync(pathToDeployment)) { throw Error(`Cannot find deployment for network: ${network}.`); } return JSON.parse(fs.readFileSync(pathToDeployment)); }; /** * Retrieve the list of targets for the network - returning the name, address, source file and link to etherscan */ const getTarget = ({ network = 'mainnet', useOvm = false, contract, path, fs, deploymentPath, } = {}) => { const deployment = loadDeploymentFile({ network, useOvm, path, fs, deploymentPath }); if (contract) return deployment.targets[contract]; else return deployment.targets; }; /** * Retrieve the list of solidity sources for the network - returning the abi and bytecode */ const getSource = ({ network = 'mainnet', useOvm = false, contract, path, fs, deploymentPath, } = {}) => { const deployment = loadDeploymentFile({ network, useOvm, path, fs, deploymentPath }); if (contract) return deployment.sources[contract]; else return deployment.sources; }; /** * Retrieve the ASTs for the source contracts */ const getAST = ({ source, path, fs, match = /^contracts\// } = {}) => { let fullAST; if (path && fs) { const pathToAST = path.resolve( __dirname, constants.BUILD_FOLDER, constants.AST_FOLDER, constants.AST_FILENAME ); if (!fs.existsSync(pathToAST)) { throw Error('Cannot find AST'); } fullAST = JSON.parse(fs.readFileSync(pathToAST)); } else { // Note: The below cannot be required as the build folder is not stored // in code (only in the published module). // The solution involves tracking these after each commit in another file // somewhere persisted in the codebase - JJM // data.ast = require('./build/ast/asts.json'), if (!data.ast) { throw Error('AST currently not supported in browser mode'); } fullAST = data.ast; } // remove anything not matching the pattern const ast = Object.entries(fullAST) .filter(([astEntryKey]) => match.test(astEntryKey)) .reduce((memo, [key, val]) => { memo[key] = val; return memo; }, {}); if (source && source in ast) { return ast[source]; } else if (source) { // try to find the source without a path const [key, entry] = Object.entries(ast).find(([astEntryKey]) => astEntryKey.includes('/' + source)) || []; if (!key || !entry) { throw Error(`Cannot find AST entry for source: ${source}`); } return { [key]: entry }; } else { return ast; } }; const getFeeds = ({ network, path, fs, deploymentPath, useOvm = false } = {}) => { let feeds; if (!deploymentPath && (!path || !fs)) { feeds = data[getFolderNameForNetwork({ network, useOvm })].feeds; } else { const pathToFeeds = deploymentPath ? path.join(deploymentPath, constants.FEEDS_FILENAME) : getPathToNetwork({ network, path, useOvm, file: constants.FEEDS_FILENAME, }); if (!fs.existsSync(pathToFeeds)) { throw Error(`Cannot find feeds file.`); } feeds = JSON.parse(fs.readFileSync(pathToFeeds)); } // now mix in the asset data return Object.entries(feeds).reduce((memo, [asset, entry]) => { memo[asset] = Object.assign(assets[asset], entry); return memo; }, {}); }; /** * Retrieve ths list of synths for the network - returning their names, assets underlying, category, sign, description, and * optional index and inverse properties */ const getSynths = ({ network = 'mainnet', path, fs, deploymentPath, useOvm = false, skipPopulate = false, } = {}) => { let synths; if (!deploymentPath && (!path || !fs)) { synths = data[getFolderNameForNetwork({ network, useOvm })].synths; } else { const pathToSynthList = deploymentPath ? path.join(deploymentPath, constants.SYNTHS_FILENAME) : getPathToNetwork({ network, useOvm, path, file: constants.SYNTHS_FILENAME }); if (!fs.existsSync(pathToSynthList)) { throw Error(`Cannot find synth list.`); } synths = JSON.parse(fs.readFileSync(pathToSynthList)); } if (skipPopulate) { return synths; } const feeds = getFeeds({ network, useOvm, path, fs, deploymentPath }); // copy all necessary index parameters from the longs to the corresponding shorts return synths.map(synth => { // mixin the asset details synth = Object.assign({}, assets[synth.asset], synth); if (feeds[synth.asset]) { const { feed } = feeds[synth.asset]; synth = Object.assign({ feed }, synth); } // replace an index placeholder with the index details if (typeof synth.index === 'string') { const { index } = synths.find(({ name }) => name === synth.index) || {}; if (!index) { throw Error( `While processing ${synth.name}, it's index mapping "${synth.index}" cannot be found - this is an error in the deployment config and should be fixed` ); } synth = Object.assign({}, synth, { index }); } if (synth.index) { synth.index = synth.index.map(indexEntry => { return Object.assign({}, assets[indexEntry.asset], indexEntry); }); } return synth; }); }; const getFuturesMarkets = ({ network = 'mainnet', useOvm = false, path, fs, deploymentPath, } = {}) => { let futuresMarkets; if (!deploymentPath && (!path || !fs)) { futuresMarkets = data[getFolderNameForNetwork({ network, useOvm })].futuresMarkets; } else { const pathToFuturesMarketsList = deploymentPath ? path.join(deploymentPath, constants.FUTURES_MARKETS_FILENAME) : getPathToNetwork({ network, path, useOvm, file: constants.FUTURES_MARKETS_FILENAME, }); if (!fs.existsSync(pathToFuturesMarketsList)) { futuresMarkets = []; } else { futuresMarkets = JSON.parse(fs.readFileSync(pathToFuturesMarketsList)) || []; } } return futuresMarkets.map(futuresMarket => { /** * We expect the asset key to not start with an 's'. ie. AVAX rather than sAVAX * Unfortunately due to some historical reasons 'sBTC', 'sETH' and 'sLINK' does not follow this format * We adjust for that here. */ const marketsWithIncorrectAssetKey = ['sBTC', 'sETH', 'sLINK']; const assetKeyNeedsAdjustment = marketsWithIncorrectAssetKey.includes(futuresMarket.asset); const assetKey = assetKeyNeedsAdjustment ? futuresMarket.asset.slice(1) : futuresMarket.asset; // mixin the asset details return Object.assign({}, assets[assetKey], futuresMarket); }); }; /** * Retrieve the list of staking rewards for the network - returning this names, stakingToken, and rewardToken */ const getStakingRewards = ({ network = 'mainnet', useOvm = false, path, fs, deploymentPath, } = {}) => { if (!deploymentPath && (!path || !fs)) { return data[getFolderNameForNetwork({ network, useOvm })].rewards; } const pathToStakingRewardsList = deploymentPath ? path.join(deploymentPath, constants.STAKING_REWARDS_FILENAME) : getPathToNetwork({ network, path, useOvm, file: constants.STAKING_REWARDS_FILENAME, }); if (!fs.existsSync(pathToStakingRewardsList)) { return []; } return JSON.parse(fs.readFileSync(pathToStakingRewardsList)); }; /** * Retrieve the list of shorting rewards for the network - returning the names and rewardTokens */ const getShortingRewards = ({ network = 'mainnet', useOvm = false, path, fs, deploymentPath, } = {}) => { if (!deploymentPath && (!path || !fs)) { return data[getFolderNameForNetwork({ network, useOvm })]['shorting-rewards']; } const pathToShortingRewardsList = deploymentPath ? path.join(deploymentPath, constants.SHORTING_REWARDS_FILENAME) : getPathToNetwork({ network, path, useOvm, file: constants.SHORTING_REWARDS_FILENAME, }); if (!fs.existsSync(pathToShortingRewardsList)) { return []; } return JSON.parse(fs.readFileSync(pathToShortingRewardsList)); }; /** * Retrieve the list of system user addresses */ const getUsers = ({ network = 'mainnet', user, useOvm = false } = {}) => { const testnetOwner = '0x73570075092502472E4b61A7058Df1A4a1DB12f2'; const base = { owner: testnetOwner, deployer: testnetOwner, marketClosure: testnetOwner, oracle: '0xac1e8B385230970319906C03A1d8567e3996d1d5', fee: '0xfeEFEEfeefEeFeefEEFEEfEeFeefEEFeeFEEFEeF', zero: '0x' + '0'.repeat(40), }; const map = { mainnet: Object.assign({}, base, { owner: '0xEb3107117FEAd7de89Cd14D463D340A2E6917769', deployer: '0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe', marketClosure: '0xC105Ea57Eb434Fbe44690d7Dec2702e4a2FBFCf7', oracle: '0xaC1ED4Fabbd5204E02950D68b6FC8c446AC95362', }), rinkeby: Object.assign({}, base), 'mainnet-ovm': Object.assign({}, base, { owner: '0x6d4a64C57612841c2C6745dB2a4E4db34F002D20', deployer: '0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe', }), goerli: Object.assign({}, base), fuji: Object.assign({}, base), 'goerli-ovm': Object.assign({}, base), local: Object.assign({}, base, { // Deterministic account #0 when using `npx hardhat node` owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', }), 'local-ovm': Object.assign({}, base, { // Deterministic account #0 when using `npx hardhat node` owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', deployer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', oracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', }), bsc: Object.assign({}, base), 'bsc-testnet': Object.assign({}, base), polygon: Object.assign({}, base), mumbai: Object.assign({}, base), arbitrum: Object.assign({}, base), 'arbitrum-goerli': Object.assign({}, base), metis: Object.assign({}, base), 'metis-goerli': Object.assign({}, base), }; const users = Object.entries( map[getFolderNameForNetwork({ network, useOvm })] ).map(([key, value]) => ({ name: key, address: value })); return user ? users.find(({ name }) => name === user) : users; }; const getVersions = ({ network = 'mainnet', path, fs, deploymentPath, useOvm, byContract = false, } = {}) => { let versions; if (!deploymentPath && (!path || !fs)) { versions = data[getFolderNameForNetwork({ network, useOvm })].versions; } else { const pathToVersions = deploymentPath ? path.join(deploymentPath, constants.VERSIONS_FILENAME) : getPathToNetwork({ network, useOvm, path, file: constants.VERSIONS_FILENAME }); if (!fs.existsSync(pathToVersions)) { throw Error(`Cannot find versions for network.`); } versions = JSON.parse(fs.readFileSync(pathToVersions)); } if (byContract) { // compile from the contract perspective return Object.values(versions).reduce( (memo, { tag, release, date, commit, block, contracts }) => { for (const [contract, contractEntry] of Object.entries(contracts)) { memo[contract] = memo[contract] || []; memo[contract].push(Object.assign({ tag, release, date, commit, block }, contractEntry)); } return memo; }, {} ); } return versions; }; const getSuspensionReasons = ({ code = undefined } = {}) => { const suspensionReasonMap = { 1: 'System Upgrade', 2: 'Market Closure', 4: 'iSynth Reprice', 6: 'Index Rebalance', 55: 'Circuit Breaker (Phase one)', // https://sips.synthetix.io/SIPS/sip-55 65: 'Decentralized Circuit Breaker (Phase two)', // https://sips.synthetix.io/SIPS/sip-65 80: 'Futures configuration', // pausing according to deployment configuration 231: 'Latency Breaker', // https://sips.synthetix.io/sips/sip-231/ 99999: 'Emergency', }; return code ? suspensionReasonMap[code] : suspensionReasonMap; }; /** * Retrieve the list of tokens used in the Synthetix protocol */ const getTokens = ({ network = 'mainnet', path, fs, useOvm = false } = {}) => { const synths = getSynths({ network, useOvm, path, fs }); const targets = getTarget({ network, useOvm, path, fs }); const feeds = getFeeds({ network, useOvm, path, fs }); return [ Object.assign( { symbol: 'SNX', asset: 'SNX', name: 'Synthetix', address: targets.ProxySynthetix.address, decimals: 18, }, feeds['SNX'].feed ? { feed: feeds['SNX'].feed } : {} ), ].concat( synths .filter(({ category }) => category !== 'internal') .map(synth => ({ symbol: synth.name, asset: synth.asset, name: synth.description, address: (targets[`Proxy${synth.name}`] || {}).address, index: synth.index, decimals: 18, feed: synth.feed, })) .sort((a, b) => (a.symbol > b.symbol ? 1 : -1)) ); }; const decode = ({ network = 'mainnet', fs, path, data, target, useOvm = false, decodeMigration = false, } = {}) => { const sources = getSource({ network, path, fs, useOvm }); for (const { abi } of Object.values(sources)) { abiDecoder.addABI(abi); } if (decodeMigration) { abiDecoder.addABI([ { constant: false, inputs: [], name: 'migrate', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, ]); } const targets = getTarget({ network, path, fs, useOvm }); let contract; if (target) { contract = Object.values(targets).filter( ({ address }) => address.toLowerCase() === target.toLowerCase() )[0].name; } return { method: abiDecoder.decodeMethod(data), contract }; }; const wrap = ({ network, deploymentPath, fs, path, useOvm = false }) => [ 'decode', 'getAST', 'getPathToNetwork', 'getSource', 'getStakingRewards', 'getShortingRewards', 'getFeeds', 'getSynths', 'getTarget', 'getFuturesMarkets', 'getTokens', 'getUsers', 'getVersions', ].reduce((memo, fnc) => { memo[fnc] = (prop = {}) => module.exports[fnc](Object.assign({ network, deploymentPath, fs, path, useOvm }, prop)); return memo; }, {}); const getNextRelease = ({ useOvm }) => { const release = releases.releases.find(({ released, ovm }) => !released && (useOvm ? ovm : !ovm)); return Object.assign({}, release, { releaseName: release.name.replace(/[^\w]/g, '') }); }; module.exports = { chainIdMapping, constants, decode, defaults, getAST, getNetworkFromId, getNextRelease, getPathToNetwork, getSource, getStakingRewards, getShortingRewards, getSuspensionReasons, getFeeds, getSynths, getFuturesMarkets, getTarget, getTokens, getUsers, getVersions, networks, networkToChainId, toBytes32, fromBytes32, wrap, nonUpgradeable, releases, knownAccounts, };