This repository has been archived by the owner on Aug 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 46
Bump to m7, Remove Sudo Pallet, Clean up Chain Spec #256
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,3 +38,6 @@ lcov.info | |
|
||
# Releases | ||
releases | ||
|
||
# Dev Chain | ||
chains/dev/* |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
#!env node | ||
const child_process = require('child_process'); | ||
const fs = require('fs').promises; | ||
const path = require('path'); | ||
const getopts = require('getopts'); | ||
const chalk = require('chalk'); | ||
const types = require('@polkadot/types'); | ||
const Contract = require('web3-eth-contract'); | ||
|
||
function getEthProvider(network) { | ||
if (['ropsten'].includes(network)) { | ||
return `https://${network}-eth.compound.finance`; | ||
} else { | ||
throw new Error(`Unknown or unsupported eth network: "${network}"`); | ||
} | ||
} | ||
|
||
async function readFile(chain, file) { | ||
return await fs.readFile(path.join(__dirname, chain, file), 'utf8'); | ||
} | ||
|
||
async function writeFile(chain, file, contents) { | ||
let filePath = path.join(__dirname, chain, file); | ||
await fs.writeFile(filePath, contents); | ||
return filePath; | ||
} | ||
|
||
async function fetchCompoundConfig(network) { | ||
return JSON.parse(await fs.readFile(path.join(__dirname, 'node_modules', 'compound-config', 'networks', `${network}.json`), 'utf8')); | ||
} | ||
|
||
async function fetchChainDeployment(network) { | ||
return JSON.parse(await fs.readFile(path.join(__dirname, '..', 'ethereum','networks', `${network}.json`), 'utf8')); | ||
} | ||
|
||
async function fetchChainDeploymentABI(network) { | ||
return JSON.parse(await fs.readFile(path.join(__dirname, '..', 'ethereum','networks', `${network}-abi.json`), 'utf8')); | ||
} | ||
|
||
function exec(target, args, opts = {}) { | ||
return new Promise((resolve, reject) => { | ||
let proc = child_process.spawn(target, args, opts); | ||
let res = ""; | ||
|
||
proc.stdout.on('data', (data) => { | ||
res += data; | ||
}); | ||
|
||
proc.stderr.on('data', (data) => { | ||
console.log(`[${target}]: ${data}`); | ||
}); | ||
|
||
proc.on('close', (code) => { | ||
if (code === 0) { | ||
resolve(res); | ||
} else { | ||
reject(`Process \`${target}\` exited with error code: ${code}`); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
async function buildGateway() { | ||
console.log(chalk.yellow("Building Gateway release build...")); | ||
return await exec("cargo", ["build", "--release"]); | ||
} | ||
|
||
async function execGateway(args) { | ||
console.log(chalk.yellow(`Running Gateway with args ${JSON.stringify(args)}...`)); | ||
let bin = process.env['CHAIN_BIN'] || path.join(__dirname, '..', 'target', 'release', 'gateway'); | ||
return await exec(bin, args); | ||
} | ||
|
||
async function setAuthorities(chainSpec, chainConfig, opts) { | ||
let registry = new types.TypeRegistry(); | ||
let validators = chainConfig.validators.map(({substrate_id, eth_address}) => { | ||
let substrateId = (new types.GenericAccountId(registry, substrate_id)); | ||
|
||
return { | ||
substrate_id: [...substrateId.toU8a()], | ||
eth_address | ||
}; | ||
}); | ||
|
||
let sessionKeys = chainConfig.validators.filter((s) => s.hasOwnProperty('session_keys')).map(({substrate_id, session_keys}) => { | ||
return [ | ||
substrate_id, | ||
substrate_id, | ||
session_keys | ||
]; | ||
}); | ||
|
||
chainSpec.genesis.runtime.palletCash.validators = validators; | ||
chainSpec.genesis.runtime.palletSession.keys = sessionKeys; | ||
} | ||
|
||
async function setStarport(chainSpec, chainConfig, opts) { | ||
let chainDeployment = await fetchChainDeployment(chainConfig.eth_network); | ||
let starportAddress = must(chainDeployment.Contracts, 'Starport'); | ||
chainSpec.properties.eth_starport_address = starportAddress; | ||
} | ||
|
||
async function setInitialYield(chainSpec, chainConfig, opts) { | ||
let chainDeployment = await fetchChainDeployment(chainConfig.eth_network); | ||
let chainDeploymentABI = await fetchChainDeploymentABI(chainConfig.eth_network); | ||
let cashTokenAddress = chainDeployment.Contracts.Cash; | ||
if (!cashTokenAddress) { | ||
throw new Error(`Missing cash token address for network ${chainConfig.eth_network}`); | ||
} | ||
let cashTokenABI = chainDeploymentABI.Cash; | ||
if (!cashTokenABI) { | ||
throw new Error(`Missing cash token ABI for network ${chainConfig.eth_network}`); | ||
} | ||
let cashToken = new Contract(cashTokenABI, cashTokenAddress); | ||
cashToken.setProvider(getEthProvider(chainConfig.eth_network)); | ||
let yieldStart = await cashToken.methods.cashYieldStart().call(); | ||
let cashYield = (await cashToken.methods.cashYieldAndIndex().call()).yield; | ||
|
||
chainSpec.genesis.runtime.palletCash.lastYieldTimestamp = Number(yieldStart) * 1000; | ||
chainSpec.genesis.runtime.palletCash.cashYield = Number(cashYield); | ||
} | ||
|
||
function must(hash, key, validator = (x) => x !== undefined) { | ||
if (hash.hasOwnProperty(key) || validator(undefined)) { | ||
let val = hash[key]; | ||
if (!validator(val)) { | ||
throw new Error(`Error validating ${key} with val ${val}`); | ||
} | ||
return val; | ||
} else { | ||
throw new Error(`Could not find key ${key} for object with keys ${JSON.stringify(Object.keys(hash))}`); | ||
} | ||
} | ||
|
||
function upper(obj) { | ||
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toUpperCase(), v])) | ||
} | ||
|
||
async function setAssetInfo(chainSpec, chainConfig, opts) { | ||
let compoundConfig = await fetchCompoundConfig(chainConfig.eth_network); | ||
let contracts = upper(compoundConfig['Contracts']); | ||
let tokenInfos = upper(compoundConfig['Tokens']); | ||
|
||
let rateModels = chainConfig.rate_models; | ||
let assets = Object.entries(chainConfig.tokens).map(([symbol, info]) => { | ||
let tokenAddress = contracts[symbol]; | ||
if (!tokenAddress) { | ||
throw new Error(`Missing contract address on ${chainConfig.eth_network} for ${symbol} from compound-config`); | ||
} | ||
|
||
let tokenInfo = tokenInfos[symbol]; | ||
if (!tokenInfo) { | ||
throw new Error(`Missing token info on ${chainConfig.eth_network} for ${symbol} from compound-config`); | ||
} | ||
let asset = `ETH:${tokenAddress}`; | ||
let decimals = must(tokenInfo, 'decimals', (d) => typeof(d) === 'number'); | ||
let liquidity_factor = must(info, 'liquidity_factor', (d) => typeof(d) === 'number' && d > 0 && d <= 1) * 1e18; | ||
let rate_model; | ||
let rateModelRaw = must(info, 'rate_model'); | ||
if (typeof(rateModelRaw) === 'string') { | ||
rate_model = rateModels[rateModelRaw]; | ||
if (!rate_model) { | ||
throw new Error(`Unknown or missing rate model: ${rate_model}`) | ||
} | ||
} else { | ||
rate_model = rateModelRaw; | ||
} | ||
let miner_shares = must(info, 'miner_shares', (d) => typeof(d) === 'number' && d >= 0 && d <= 1) * 1e18; | ||
let supply_cap = must(info, 'supply_cap', (d) => typeof(d) === 'undefined' || typeof(d) === 'number') || 0; | ||
let ticker = must(info, 'ticker', (d) => typeof(d) === 'undefined' || typeof(d) === 'string') || symbol; | ||
|
||
return { | ||
asset, | ||
decimals, | ||
liquidity_factor, | ||
rate_model, | ||
miner_shares, | ||
supply_cap, | ||
symbol, | ||
ticker | ||
}; | ||
}); | ||
|
||
chainSpec.genesis.runtime.palletCash.assets = assets; | ||
} | ||
|
||
async function setReporters(chainSpec, chainConfig, opts) { | ||
chainSpec.genesis.runtime.palletOracle.reporters = chainConfig.reporters; | ||
} | ||
|
||
async function buildSpec(opts) { | ||
if (!opts.skip_build) { | ||
await buildGateway(); | ||
} | ||
let chain = opts.chain; | ||
|
||
let chainConfig = JSON.parse(await readFile(chain, 'chain-config.json')); | ||
let chainSpec = JSON.parse(await execGateway(["build-spec", "--disable-default-bootnode", "--chain", chainConfig.base_chain])); | ||
await setAuthorities(chainSpec, chainConfig, opts); | ||
await setAssetInfo(chainSpec, chainConfig, opts); | ||
await setReporters(chainSpec, chainConfig, opts); | ||
await setStarport(chainSpec, chainConfig, opts); | ||
await setInitialYield(chainSpec, chainConfig, opts); | ||
|
||
await writeFile(chain, 'chain-spec.json', JSON.stringify(chainSpec, null, 2)); | ||
|
||
let chainSpecFile = await writeFile(chain, 'chain-spec.json', JSON.stringify(chainSpec, null, 2)); | ||
|
||
// Next, build raw spec | ||
let raw = JSON.parse(await execGateway(["build-spec", "--chain", chainSpecFile, "--raw", "--disable-default-bootnode"])); | ||
|
||
// Next, build | ||
await writeFile(chain, 'chain-spec-raw.json', JSON.stringify(raw, null, 2)); | ||
} | ||
|
||
const options = getopts(process.argv.slice(2), { | ||
alias: { | ||
chain: "c", | ||
skip_build: "s" | ||
} | ||
}); | ||
|
||
if (!options.chain) { | ||
throw new Error(`Must choose chain with -c`); | ||
} | ||
|
||
buildSpec({ | ||
skip_build: false, | ||
...options | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "chains", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@polkadot/types": "^4.1.1", | ||
"chalk": "^4.1.0", | ||
"compound-config": "https://github.com/compound-finance/compound-config.git", | ||
"getopts": "^2.3.0", | ||
"web3-eth-contract": "^1.3.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the chain-specific info used to build the chain-spec. I try to limit the data that needs to be here, e.g. by pulling the actual token config from |
||
"eth_network": "ropsten", | ||
"base_chain": "testnet", | ||
"validators": [ | ||
{ | ||
"substrate_id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", | ||
"eth_address": "0x55413a2d4908d130c908ccf2f298b235bacd427a", | ||
"session_keys": { | ||
"aura": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", | ||
"grandpa": "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu" | ||
} | ||
} | ||
], | ||
"reporters": [ | ||
"0xfceadafab14d46e20144f48824d0c09b1a03f2bc" | ||
], | ||
"rate_models": { | ||
"Kink_500_2000": { | ||
"Kink": { | ||
"zero_rate": 0, | ||
"kink_rate": 500, | ||
"kink_utilization": 8000, | ||
"full_rate": 2000 | ||
} | ||
} | ||
}, | ||
"tokens": { | ||
"ZRX": { | ||
"liquidity_factor": 0.5, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"WBTC": { | ||
"ticker": "BTC", | ||
"liquidity_factor": 0.6, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"USDT": { | ||
"ticker": "USD", | ||
"liquidity_factor": 0.7, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"USDC": { | ||
"ticker": "USD", | ||
"liquidity_factor": 0.8, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"DAI": { | ||
"ticker": "DAI", | ||
"liquidity_factor": 0.7, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"BAT": { | ||
"liquidity_factor": 0.6, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"COMP": { | ||
"liquidity_factor": 0.8, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"UNI": { | ||
"liquidity_factor": 0.8, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
}, | ||
"ETH": { | ||
"liquidity_factor": 0.8, | ||
"rate_model": "Kink_500_2000", | ||
"miner_shares": 0.01 | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a script that builds
chains/{chain}/chain-spec.js
andchains/{chain}/chain-spec-raw.js
based on data fromchains/{chain}/chain-config.json
and other sources (e.g.compound-config
andethereum/networks/ropsten.json
)