diff --git a/README.md b/README.md index df5d521e..44a8e219 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ module.exports = { | measureModifierCoverage | *boolean* | `true` | Computes each modifier invocation as a code branch. [More...][34] | | modifierWhitelist | *String[]* | `[]` | List of modifier names (ex: "onlyOwner") to exclude from branch measurement. (Useful for modifiers which prepare something instead of acting as a gate.)) | | matrixOutputPath | *String* | `./testMatrix.json` | Relative path to write test matrix JSON object to. [More...][38] | +| abiOutputPath | *String* | `./humanReadableAbis.json` | Relative path to write diff-able ABI data to. [More...][38] | | istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. | | istanbulReporter | *Array* | `['html', 'lcov', 'text', 'json']` | [Istanbul coverage reporters][2] | | mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions.| diff --git a/lib/abi.js b/lib/abi.js new file mode 100644 index 00000000..2b8455e7 --- /dev/null +++ b/lib/abi.js @@ -0,0 +1,99 @@ +const ethersABI = require("@ethersproject/abi"); +const difflib = require('difflib'); + +class AbiUtils { + + diff(orig={}, cur={}){ + let plus = 0; + let minus = 0; + + const unifiedDiff = difflib.unifiedDiff( + orig.humanReadableAbiList, + cur.humanReadableAbiList, + { + fromfile: orig.contractName, + tofile: cur.contractName, + fromfiledate: `sha: ${orig.sha}`, + tofiledate: `sha: ${cur.sha}`, + lineterm: '' + } + ); + + // Count changes (unified diff always has a plus & minus in header); + if (unifiedDiff.length){ + plus = -1; + minus = -1; + } + + unifiedDiff.forEach(line => { + if (line[0] === `+`) plus++; + if (line[0] === `-`) minus++; + }) + + return { + plus, + minus, + unifiedDiff + } + } + + toHumanReadableFunctions(contract){ + const human = []; + const ethersOutput = new ethersABI.Interface(contract.abi).functions; + const signatures = Object.keys(ethersOutput); + + for (const sig of signatures){ + const method = ethersOutput[sig]; + let returns = ''; + + method.outputs.forEach(output => { + (returns.length) + ? returns += `, ${output.type}` + : returns += output.type; + }); + + let readable = `${method.type} ${sig} ${method.stateMutability}`; + + if (returns.length){ + readable += ` returns (${returns})` + } + + human.push(readable); + } + + return human; + } + + toHumanReadableEvents(contract){ + const human = []; + const ethersOutput = new ethersABI.Interface(contract.abi).events; + const signatures = Object.keys(ethersOutput); + + for (const sig of signatures){ + const method = ethersOutput[sig]; + const readable = `${ethersOutput[sig].type} ${sig}`; + human.push(readable); + } + + return human; + } + + generateHumanReadableAbiList(_artifacts, sha){ + const list = []; + if (_artifacts.length){ + for (const item of _artifacts){ + const fns = this.toHumanReadableFunctions(item); + const evts = this.toHumanReadableEvents(item); + const all = fns.concat(evts); + list.push({ + contractName: item.contractName, + sha: sha, + humanReadableAbiList: all + }) + } + } + return list; + } +} + +module.exports = AbiUtils; \ No newline at end of file diff --git a/lib/api.js b/lib/api.js index aab4caa5..57d8ae23 100644 --- a/lib/api.js +++ b/lib/api.js @@ -12,13 +12,15 @@ const Instrumenter = require('./instrumenter'); const Coverage = require('./coverage'); const DataCollector = require('./collector'); const AppUI = require('./ui').AppUI; +const AbiUtils = require('./abi'); /** * Coverage Runner */ class API { constructor(config={}) { - this.validator = new ConfigValidator() + this.validator = new ConfigValidator(); + this.abiUtils = new AbiUtils(); this.config = config || {}; this.testMatrix = {}; @@ -31,6 +33,7 @@ class API { this.testsErrored = false; this.cwd = config.cwd || process.cwd(); + this.abiOutputPath = config.abiOutputPath || "humanReadableAbis.json"; this.matrixOutputPath = config.matrixOutputPath || "testMatrix.json"; this.matrixReporterPath = config.matrixReporterPath || "solidity-coverage/plugins/resources/matrix.js" @@ -357,6 +360,11 @@ class API { fs.writeFileSync(matrixPath, JSON.stringify(mapping, null, ' ')); } + saveHumanReadableAbis(data){ + const abiPath = path.join(this.cwd, this.abiOutputPath); + fs.writeFileSync(abiPath, JSON.stringify(data, null, ' ')); + } + // ===== // Paths // ===== diff --git a/lib/validator.js b/lib/validator.js index 1cf4f9ec..1e1fdf02 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -14,7 +14,9 @@ const configSchema = { client: {type: "object"}, cwd: {type: "string"}, host: {type: "string"}, - + abiOutputPath: {type: "string"}, + matrixOutputPath: {type: "string"}, + matrixReporterPath: {type: "string"}, port: {type: "number"}, providerOptions: {type: "object"}, silent: {type: "boolean"}, diff --git a/package.json b/package.json index 2b2b0df7..bf05beaa 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,13 @@ "author": "", "license": "ISC", "dependencies": { + "@ethersproject/abi": "^5.0.9", "@solidity-parser/parser": "^0.10.1", "@truffle/provider": "^0.2.24", "chalk": "^2.4.2", "death": "^1.1.0", "detect-port": "^1.3.0", + "difflib": "^0.2.4", "fs-extra": "^8.1.0", "ganache-cli": "^6.11.0", "ghost-testrpc": "^0.0.2", diff --git a/plugins/resources/nomiclabs.utils.js b/plugins/resources/nomiclabs.utils.js index f7a1958f..c8fb0b31 100644 --- a/plugins/resources/nomiclabs.utils.js +++ b/plugins/resources/nomiclabs.utils.js @@ -180,6 +180,35 @@ function collectTestMatrixData(args, env, api){ } } +/** + * Returns all Hardhat artifacts. + * @param {HRE} env + * @return {Artifact[]} + */ +async function getAllArtifacts(env){ + const all = []; + const qualifiedNames = await env.artifacts.getArtifactPaths(); + for (const name of qualifiedNames){ + all.push(await env.artifacts.readArtifact(name)); + } + return all; +} + +/** + * Compiles project + * Collects all artifacts from Hardhat project, + * Converts them to a format that can be consumed by api.abiUtils.diff + * Saves them to `api.abiOutputPath` + * @param {HRE} env + * @param {SolidityCoverageAPI} api + */ +async function generateHumanReadableAbiList(env, api){ + await env.run(TASK_COMPILE); + const _artifacts = getAllArtifacts(env); + const list = api.abiUtils.generateHumanReadableAbiList(_artifacts) + api.saveHumanReadableAbis(list); +} + /** * Sets the default `from` account field in the network that will be used. * This needs to be done after accounts are fetched from the launched client. @@ -231,6 +260,7 @@ module.exports = { setupHardhatNetwork, getTestFilePaths, setNetworkFrom, - collectTestMatrixData + collectTestMatrixData, + getAllArtifacts } diff --git a/plugins/resources/truffle.utils.js b/plugins/resources/truffle.utils.js index 36d8b0bf..9f535ef0 100644 --- a/plugins/resources/truffle.utils.js +++ b/plugins/resources/truffle.utils.js @@ -35,6 +35,39 @@ async function getTestFilePaths(config){ return target.filter(f => f.match(testregex) != null); } +/** + * Returns all Truffle artifacts. + * @param {TruffleConfig} config + * @return {Artifact[]} + */ +function getAllArtifacts(config){ + const all = []; + const artifactsGlob = path.join(config.artifactsDir, '/**/*.json'); + const files = globby.sync([artifactsGlob]) + for (const file of files){ + const candidate = require(file); + if (candidate.contractName && candidate.abi){ + all.push(candidate); + } + } + return all; +} + +/** + * Compiles project + * Collects all artifacts from Truffle project, + * Converts them to a format that can be consumed by api.abiUtils.diff + * Saves them to `api.abiOutputPath` + * @param {TruffleConfig} config + * @param {TruffleAPI} truffle + * @param {SolidityCoverageAPI} api + */ +async function generateHumanReadableAbiList(config, truffle, api){ + await truffle.compile(config); + const _artifacts = getAllArtifacts(config); + const list = api.abiUtils.generateHumanReadableAbiList(_artifacts) + api.saveHumanReadableAbis(list); +} /** * Configures the network. Runs before the server is launched. diff --git a/test/sources/solidity/contracts/diff/addition.sol b/test/sources/solidity/contracts/diff/addition.sol new file mode 100644 index 00000000..2820dd8e --- /dev/null +++ b/test/sources/solidity/contracts/diff/addition.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.7.0; + +contract Old { + uint public y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } +} + +contract New { + uint public y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } + + function d() external { + bool x = true; + } +} + diff --git a/test/sources/solidity/contracts/diff/events.sol b/test/sources/solidity/contracts/diff/events.sol new file mode 100644 index 00000000..131828e6 --- /dev/null +++ b/test/sources/solidity/contracts/diff/events.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.7.0; + +contract Old { + uint y; + + event Evt(uint x, bytes8 y); + + function a() public { + bool x = true; + } +} + +contract New { + uint y; + + function a() public { + bool x = true; + } + + event aEvt(bytes8); + event _Evt(bytes8 x, bytes8 y); +} + diff --git a/test/sources/solidity/contracts/diff/no-change.sol b/test/sources/solidity/contracts/diff/no-change.sol new file mode 100644 index 00000000..7cc9ce23 --- /dev/null +++ b/test/sources/solidity/contracts/diff/no-change.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.7.0; + +contract Old { + uint y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } +} + +contract New { + uint y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } +} + diff --git a/test/sources/solidity/contracts/diff/param-change.sol b/test/sources/solidity/contracts/diff/param-change.sol new file mode 100644 index 00000000..87a048e0 --- /dev/null +++ b/test/sources/solidity/contracts/diff/param-change.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.7.0; + +contract Old { + uint y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } +} + +contract New { + uint y; + + function a() public { + bool x = true; + } + + function b(bytes8 z) external { + bool x = true; + } + + function c(uint q, uint r) external { + bool x = true; + } +} diff --git a/test/sources/solidity/contracts/diff/removal.sol b/test/sources/solidity/contracts/diff/removal.sol new file mode 100644 index 00000000..53a29e4b --- /dev/null +++ b/test/sources/solidity/contracts/diff/removal.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.7.0; + +contract Old { + uint y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } +} + +contract New { + uint y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } +} diff --git a/test/sources/solidity/contracts/diff/reorder.sol b/test/sources/solidity/contracts/diff/reorder.sol new file mode 100644 index 00000000..ff9e8c91 --- /dev/null +++ b/test/sources/solidity/contracts/diff/reorder.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.7.0; + +contract Old { + uint y; + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } + + function c() external { + bool x = true; + } +} + +contract New { + uint y; + + function c() external { + bool x = true; + } + + function a() public { + bool x = true; + } + + function b() external { + bool x = true; + } +} diff --git a/test/sources/solidity/contracts/diff/return-sig.sol b/test/sources/solidity/contracts/diff/return-sig.sol new file mode 100644 index 00000000..b49cce68 --- /dev/null +++ b/test/sources/solidity/contracts/diff/return-sig.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.7.0; + +contract Old { + function a() public view returns (uint) { + return 1; + } +} + +contract New { + function a() public view returns (bool) { + return true; + } + + function e() public view returns (uint8[2] memory) { + return [5,7]; + } + + function f() public view returns (uint8[2] memory, uint) { + return ([5,7], 7); + } + + function g() public view returns (uint8[3] memory) { + return [5,7,8]; + } +} diff --git a/test/sources/solidity/contracts/diff/state-mod-change.sol b/test/sources/solidity/contracts/diff/state-mod-change.sol new file mode 100644 index 00000000..36a451cc --- /dev/null +++ b/test/sources/solidity/contracts/diff/state-mod-change.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.7.0; + +contract Old { + function a() public { + bool x = true; + } +} + +contract New { + function a() public view returns (bool) { + return true; + } +} diff --git a/test/units/diff.js b/test/units/diff.js new file mode 100644 index 00000000..3b184acb --- /dev/null +++ b/test/units/diff.js @@ -0,0 +1,128 @@ +const assert = require('assert'); +const util = require('./../util/util.js'); +const Api = require('./../../lib/api') + +describe('abi diffs', function(){ + const api = new Api(); + + function setUp(source){ + const abis = util.getDiffABIs(source); + const orig = api.abiUtils.generateHumanReadableAbiList([abis.original], abis.original.sha); + const cur = api.abiUtils.generateHumanReadableAbiList([abis.current], abis.current.sha); + return api.abiUtils.diff(orig[0], cur[0]); + } + + function validate(result, expectPlus, expectMinus, expectDiff){ + assert.equal(result.plus, expectPlus); + assert.equal(result.minus, expectMinus); + assert.deepEqual(result.unifiedDiff, expectDiff); + } + + it('when methods are added', function() { + const expectPlus = 1; + const expectMinus = 0; + const expectDiff = [ + "--- Test\tsha: d8b26d8", + "+++ Test\tsha: e77e29d", + "@@ -1,4 +1,5 @@", + " function a() nonpayable", + " function b() nonpayable", + " function c() nonpayable", + "+function d() nonpayable", + " function y() view returns (uint256)" + ]; + + validate(setUp('diff/addition'), expectPlus, expectMinus, expectDiff); + }); + + it('when there are events', function() { + const expectPlus = 2; + const expectMinus = 1; + const expectDiff = [ + "--- Test\tsha: d8b26d8", + "+++ Test\tsha: e77e29d", + "@@ -1,2 +1,3 @@", + " function a() nonpayable", + "-event Evt(uint256,bytes8)", + "+event _Evt(bytes8,bytes8)", + "+event aEvt(bytes8)" + ]; + + validate(setUp('diff/events'), expectPlus, expectMinus, expectDiff); + }); + + it('when parameters change', function() { + const expectPlus = 2; + const expectMinus = 2; + const expectDiff = [ + "--- Test\tsha: d8b26d8", + "+++ Test\tsha: e77e29d", + "@@ -1,3 +1,3 @@", + " function a() nonpayable", + "-function b() nonpayable", + "-function c() nonpayable", + "+function b(bytes8) nonpayable", + "+function c(uint256,uint256) nonpayable" + ]; + + validate(setUp('diff/param-change'), expectPlus, expectMinus, expectDiff); + }); + + it('when there is no change', function() { + const expectPlus = 0; + const expectMinus = 0; + const expectDiff = []; + validate(setUp('diff/no-change'), expectPlus, expectMinus, expectDiff); + }); + + it('when methods are removed', function() { + const expectPlus = 0; + const expectMinus = 1; + const expectDiff = [ + '--- Test\tsha: d8b26d8', + '+++ Test\tsha: e77e29d', + '@@ -1,3 +1,2 @@', + ' function a() nonpayable', + ' function b() nonpayable', + '-function c() nonpayable' + ]; + + validate(setUp('diff/removal'), expectPlus, expectMinus, expectDiff); + }); + + it('when methods are reordered', function() { + const expectPlus = 0; + const expectMinus = 0; + const expectDiff = []; + validate(setUp('diff/reorder'), expectPlus, expectMinus, expectDiff); + }); + + it('when return signatures change', function() { + const expectPlus = 4; + const expectMinus = 1; + const expectDiff = [ + '--- Test\tsha: d8b26d8', + '+++ Test\tsha: e77e29d', + '@@ -1 +1,4 @@', + '-function a() view returns (uint256)', + '+function a() view returns (bool)', + '+function e() view returns (uint8[2])', + '+function f() view returns (uint8[2], uint256)', + '+function g() view returns (uint8[3])' + ]; + validate(setUp('diff/return-sig'), expectPlus, expectMinus, expectDiff); + }); + + it('when state modifiablility changes', function() { + const expectPlus = 1; + const expectMinus = 1; + const expectDiff = [ + '--- Test\tsha: d8b26d8', + '+++ Test\tsha: e77e29d', + '@@ -1 +1 @@', + '-function a() nonpayable', + '+function a() view returns (bool)' + ]; + validate(setUp('diff/state-mod-change'), expectPlus, expectMinus, expectDiff); + }); +}); diff --git a/test/units/validator.js b/test/units/validator.js index 1cb74c0e..2479aed7 100644 --- a/test/units/validator.js +++ b/test/units/validator.js @@ -22,7 +22,9 @@ describe('config validation', () => { const options = [ "cwd", "host", - "istanbulFolder" + "istanbulFolder", + "abiOutputPath", + "matrixOutputPath", ] options.forEach(name => { diff --git a/test/util/integration.js b/test/util/integration.js index 6a9b70ac..d6fdcc46 100644 --- a/test/util/integration.js +++ b/test/util/integration.js @@ -50,7 +50,6 @@ function decacheConfigs(){ function clean() { shell.config.silent = true; - shell.rm('-Rf', temp); shell.rm('-Rf', 'coverage'); shell.rm('coverage.json'); diff --git a/test/util/util.js b/test/util/util.js index aa306a9d..2e0ba91e 100644 --- a/test/util/util.js +++ b/test/util/util.js @@ -68,6 +68,26 @@ function codeToCompilerInput(code) { }); } +// =========== +// Diff tests +// =========== +function getDiffABIs(sourceName, testFile="test.sol", original="Old", current="New"){ + const contract = getCode(`${sourceName}.sol`) + const solcOutput = compile(contract) + return { + original: { + contractName: "Test", + sha: "d8b26d8", + abi: solcOutput.contracts[testFile][original].abi, + }, + current: { + contractName: "Test", + sha: "e77e29d", + abi: solcOutput.contracts[testFile][current].abi, + } + } +} + // ============================ // Instrumentation Correctness // ============================ @@ -118,11 +138,12 @@ function initializeProvider(ganache){ } module.exports = { - getCode: getCode, - pathPrefix: pathPrefix, - filePath: filePath, - report: report, - instrumentAndCompile: instrumentAndCompile, - bootstrapCoverage: bootstrapCoverage, - initializeProvider: initializeProvider, + getCode, + pathPrefix, + filePath, + report, + instrumentAndCompile, + bootstrapCoverage, + initializeProvider, + getDiffABIs } diff --git a/yarn.lock b/yarn.lock index f0207cef..e9445f29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -108,6 +108,45 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" +"@ethersproject/abi@^5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.9.tgz#738c1c557e56d8f395a5a27caef9b0449bc85a10" + integrity sha512-ily2OufA2DTrxkiHQw5GqbkMSnNKuwZBqKsajtT0ERhZy1r9w2CpW1bmtRMIGzaqQxCdn/GEoFogexk72cBBZQ== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abstract-provider@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.7.tgz#04ee3bfe43323384e7fecf6c774975b8dec4bdc9" + integrity sha512-NF16JGn6M0zZP5ZS8KtDL2Rh7yHxZbUjBIHLNHMm/0X0BephhjUWy8jqs/Zks6kDJRzNthgmPVy41Ec0RYWPYA== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + +"@ethersproject/abstract-signer@^5.0.6": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.9.tgz#238ddc06031aeb9dfceee2add965292d7dd1acbf" + integrity sha512-CM5UNmXQaA03MyYARFDDRjHWBxujO41tVle7glf5kHcQsDDULgqSVpkliLJMtPzZjOKFeCVZBHybTZDEZg5zzg== + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/address@>=5.0.0-beta.128": version "5.0.0-beta.134" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.0-beta.134.tgz#9c1790c87b763dc547ac12e2dbc9fa78d0799a71" @@ -130,6 +169,24 @@ "@ethersproject/rlp" "^5.0.3" bn.js "^4.4.0" +"@ethersproject/address@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.8.tgz#0c551659144a5a7643c6bea337149d410825298f" + integrity sha512-V87DHiZMZR6hmFYmoGaHex0D53UEbZpW75uj8AqPbjYUmi65RB4N2LPRcJXuWuN2R0Y2CxkvW6ArijWychr5FA== + dependencies: + "@ethersproject/bignumber" "^5.0.10" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/rlp" "^5.0.3" + +"@ethersproject/base64@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.6.tgz#26311ebf29ea3d0b9c300ccf3e1fdc44b7481516" + integrity sha512-HwrGn8YMiUf7bcdVvB4NJ+eWT0BtEFpDtrYxVXEbR7p/XBSJjwiR7DEggIiRvxbualMKg+EZijQWJ3az2li0uw== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/bignumber@>=5.0.0-beta.130": version "5.0.0-beta.138" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.0-beta.138.tgz#a635f2f9a6f1b262cc38e1c7ee561fb13d79fda4" @@ -139,6 +196,15 @@ "@ethersproject/properties" ">=5.0.0-beta.131" bn.js "^4.4.0" +"@ethersproject/bignumber@^5.0.10", "@ethersproject/bignumber@^5.0.8": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.12.tgz#fe4a78667d7cb01790f75131147e82d6ea7e7cba" + integrity sha512-mbFZjwthx6vFlHG9owXP/C5QkNvsA+xHpDCkPPPdG2n1dS9AmZAL5DI0InNLid60rQWL3MXpEl19tFmtL7Q9jw== + dependencies: + "@ethersproject/bytes" "^5.0.8" + "@ethersproject/logger" "^5.0.5" + bn.js "^4.4.0" + "@ethersproject/bignumber@^5.0.7": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.8.tgz#cee33bd8eb0266176def0d371b45274b1d2c4ec0" @@ -159,6 +225,13 @@ dependencies: "@ethersproject/logger" "^5.0.5" +"@ethersproject/bytes@^5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.8.tgz#cf1246a6a386086e590063a4602b1ffb6cc43db1" + integrity sha512-O+sJNVGzzuy51g+EMK8BegomqNIg+C2RO6vOt0XP6ac4o4saiq69FnjlsrNslaiMFVO7qcEHBsWJ9hx1tj1lMw== + dependencies: + "@ethersproject/logger" "^5.0.5" + "@ethersproject/constants@>=5.0.0-beta.128": version "5.0.0-beta.133" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.0-beta.133.tgz#af4ccd7232f3ed73aebe066a695ede32c497a394" @@ -180,6 +253,20 @@ "@ethersproject/logger" ">=5.0.0-beta.129" "@ethersproject/strings" ">=5.0.0-beta.130" +"@ethersproject/hash@^5.0.4": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.9.tgz#81252a848185b584aa600db4a1a68cad9229a4d4" + integrity sha512-e8/i2ZDeGSgCxXT0vocL54+pMbw5oX5fNjb2E3bAIvdkh5kH29M7zz1jHu1QDZnptIuvCZepIbhUH8lxKE2/SQ== + dependencies: + "@ethersproject/abstract-signer" "^5.0.6" + "@ethersproject/address" "^5.0.5" + "@ethersproject/bignumber" "^5.0.8" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.4" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/keccak256@>=5.0.0-beta.127": version "5.0.0-beta.131" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.0-beta.131.tgz#b5778723ee75208065b9b9ad30c71d480f41bb31" @@ -202,6 +289,13 @@ version "5.0.6" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.6.tgz#faa484203e86e08be9e07fef826afeef7183fe88" +"@ethersproject/networks@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.6.tgz#4d6586bbebfde1c027504ebf6dfb783b29c3803a" + integrity sha512-2Cg1N5109zzFOBfkyuPj+FfF7ioqAsRffmybJ2lrsiB5skphIAE72XNSCs4fqktlf+rwSh/5o/UXRjXxvSktZw== + dependencies: + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties@>=5.0.0-beta.131": version "5.0.0-beta.139" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.0-beta.139.tgz#b56c494bbeb47b2d1bab95a52bbf1ee0a8040274" @@ -214,6 +308,13 @@ dependencies: "@ethersproject/logger" "^5.0.5" +"@ethersproject/properties@^5.0.4": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.6.tgz#44d82aaa294816fd63333e7def42426cf0e87b3b" + integrity sha512-a9DUMizYhJ0TbtuDkO9iYlb2CDlpSKqGPDr+amvlZhRspQ6jbl5Eq8jfu4SCcGlcfaTbguJmqGnyOGn1EFt6xA== + dependencies: + "@ethersproject/logger" "^5.0.5" + "@ethersproject/rlp@>=5.0.0-beta.126": version "5.0.0-beta.132" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.0-beta.132.tgz#f7d31e0ee8792180ffd5c73969aa5b2f8804e967" @@ -245,6 +346,15 @@ "@ethersproject/constants" ">=5.0.0-beta.128" "@ethersproject/logger" ">=5.0.0-beta.129" +"@ethersproject/strings@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.7.tgz#8dc68f794c9e2901f3b75e53b2afbcb6b6c15037" + integrity sha512-a+6T80LvmXGMOOWQTZHtGGQEg1z4v8rm8oX70KNs55YtPXI/5J3LBbVf5pyqCKSlmiBw5IaepPvs5XGalRUSZQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/transactions@^5.0.0-beta.135": version "5.0.6" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.6.tgz#b8b27938be6e9ed671dbdd35fe98af8b14d0df7c" @@ -259,6 +369,32 @@ "@ethersproject/rlp" "^5.0.3" "@ethersproject/signing-key" "^5.0.4" +"@ethersproject/transactions@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.8.tgz#3b4d7041e13b957a9c4f131e0aea9dae7b6f5a23" + integrity sha512-i7NtOXVzUe+YSU6QufzlRrI2WzHaTmULAKHJv4duIZMLqzehCBXGA9lTpFgFdqGYcQJ7vOtNFC2BB2mSjmuXqg== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + +"@ethersproject/web@^5.0.6": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.11.tgz#d47da612b958b4439e415782a53c8f8461522d68" + integrity sha512-x03ihbPoN1S8Gsh9WSwxkYxUIumLi02ZEKJku1C43sxBfe+mdprWyvujzYlpuoRNfWRgNhdRDKMP8JbG6MwNGA== + dependencies: + "@ethersproject/base64" "^5.0.3" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@nodelib/fs.scandir@2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz#1f981cd5b83e85cfdeb386fc693d4baab392fa54" @@ -1898,6 +2034,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha1-teMDYabbAjF21WKJLbhZQKcY9H4= + dependencies: + heap ">= 0.2.0" + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3344,6 +3487,11 @@ he@1.2.0, he@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" +"heap@>= 0.2.0": + version "0.2.6" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" + integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= + highlight.js@^9.12.0, highlight.js@^9.15.8: version "9.18.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.3.tgz#a1a0a2028d5e3149e2380f8a865ee8516703d634"