From e27d5740abfd4092ed9f2236c9d644954da6d40e Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 9 Jan 2020 14:39:06 +0000 Subject: [PATCH 001/117] Initial commit --- packages/dai/src/eth/MulticallService.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 3dda955b4..d695406db 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -78,6 +78,8 @@ export default class MulticallService extends PublicService { return this._watcher.start(); } + registerSchema() {} + disconnect() { // TODO } From afa86bb2043113798810a2c5952595336591f83b Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 9 Jan 2020 16:00:39 +0000 Subject: [PATCH 002/117] initial poc for observables schema --- packages/dai/package.json | 1 + packages/dai/src/eth/MulticallService.js | 26 ++++++++++++++++++++++++ yarn.lock | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/packages/dai/package.json b/packages/dai/package.json index e3f321eca..7bbf65a74 100644 --- a/packages/dai/package.json +++ b/packages/dai/package.json @@ -66,6 +66,7 @@ "isomorphic-fetch": "^2.2.1", "lodash": "^4.17.11", "promise-props": "^1.0.0", + "rxjs": "^6.5.4", "toposort": "^2.0.2", "web3": "1.2.1", "web3-provider-engine": "makerdao/provider-engine#kovan-fix-dist" diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index d695406db..3a8a61fd3 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,11 +1,37 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; +import { zip, map } from 'rxjs'; + const log = debug('dai:MulticallService'); +const LOGICAL = 'logical'; +const DERIVED = 'derived'; + export default class MulticallService extends PublicService { constructor(name = 'multicall') { super(name, ['web3', 'smartContract']); + + observableStore = {}; + + observableSchema = { + generatedUserDebt: { + type: LOGICAL, + call: urnId => `vat.urn.${urnId}.art` + }, + debtScalingFactor: { + type: LOGICAL, + call: ilkName => `vat.ilk.${ilkName}.rate` + }, + daiGenerated: { + type: DERIVED, + dependents: ['generatedUserDebt', 'debtScalingFactor'], + fn: ({ generatedUserDebt$, debtScalingFactor$ }) => + zip(generatedUserDebt$, debtScalingFactor$).pipe( + map(([a, b]) => a * b) + ) + } + }; } createWatcher({ diff --git a/yarn.lock b/yarn.lock index 8358944cd..94340bc53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10203,6 +10203,13 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" + integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" From 3421d35e356b77d30e710ffafd38fc03475202de Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 10 Jan 2020 21:51:45 +0000 Subject: [PATCH 003/117] Pushing latest --- packages/dai-plugin-mcd/src/index.js | 3 + packages/dai-plugin-mcd/src/schema.js | 45 +++++++++ packages/dai-plugin-mcd/src/utils.js | 13 +++ packages/dai/src/eth/MulticallService.js | 119 ++++++++++++++++++----- 4 files changed, 156 insertions(+), 24 deletions(-) create mode 100644 packages/dai-plugin-mcd/src/schema.js diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index 5792db2f6..c4a172fbd 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -15,6 +15,7 @@ import SystemDataService from './SystemDataService'; import QueryApiMcdService from './QueryApiMcdService'; import { ServiceRoles as ServiceRoles_ } from './constants'; import BigNumber from 'bignumber.js'; +import schema from './schema'; export const ServiceRoles = ServiceRoles_; const { @@ -75,6 +76,8 @@ export const BAT = createCurrency('BAT'); export const DGD = createCurrency('DGD'); export const GNT = createCurrency('GNT'); +export const mcdSchema = schema; + const defaultCdpTypes = [ { currency: ETH, ilk: 'ETH-A' }, { currency: BAT, ilk: 'BAT-A' } diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js new file mode 100644 index 000000000..b24d8bb1d --- /dev/null +++ b/packages/dai-plugin-mcd/src/schema.js @@ -0,0 +1,45 @@ +import { toHex } from './utils'; + +const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; +const ENCUMBERED_DEBT = 'encumberedDebt'; + +const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; +const DEBT_SCALING_FACTOR = 'debtScalingFactor'; +const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; +const DEBT_CEILING = 'debtCeiling'; +const URN_DEBT_FLOOR = 'urndebtFloor'; + +const DAI_GENERATED = 'daiGenerated'; + +export const urn = (ilkName, urnAddress, vaultId) => ({ + contractName: 'MCD_VAT', + contractCall: 'urns(bytes32,address)(uint256,uint256)', + callArgs: [[ilkName, toHex], [urnAddress]], + callArgsOverrides: [vaultId], + returnKeys: [['ink'], ['art']], + observableKeys: [ + [ENCUMBERED_COLLATERAL, `${vaultId}`], + [ENCUMBERED_DEBT, `${vaultId}`] + ] +}); + +export const ilk = ilkName => ({ + contractName: 'MCD_VAT', + contractCall: 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', + callArgs: [[ilkName, toHex]], + returnKeys: [['Art'], ['rate'], ['spot'], ['line'], ['dust']], + observableKeys: [ + [TOTAL_ENCUMBERED_DEBT, ilkName], + [DEBT_SCALING_FACTOR, ilkName], + [PRICE_WITH_SAFETY_MARGIN, ilkName], + [DEBT_CEILING, ilkName], + [URN_DEBT_FLOOR, ilkName] + ] +}); + +export const derivedSchema = []; + +export default { + urn, + ilk +}; diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index 494577e35..5399a7bd3 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -33,3 +33,16 @@ export function parseWeiNumeric(value, denom = 'ether') { export function numberFromNumeric(value) { return BigNumber(value).toNumber(); } + +export function padRight(string, chars, sign) { + return string + new Array(chars - string.length + 1).join(sign ? sign : '0'); +} + +export function toHex(str, { with0x = true, rightPadding = 64 } = {}) { + let result = ''; + for (let i = 0; i < str.length; i++) { + result += str.charCodeAt(i).toString(16); + } + if (rightPadding > 0) result = padRight(result, rightPadding); + return with0x ? '0x' + result : result; +} diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 3a8a61fd3..f4d36434d 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,37 +1,19 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { zip, map } from 'rxjs'; +import { Observable, ReplaySubject, zip, map } from 'rxjs'; const log = debug('dai:MulticallService'); -const LOGICAL = 'logical'; -const DERIVED = 'derived'; - export default class MulticallService extends PublicService { constructor(name = 'multicall') { super(name, ['web3', 'smartContract']); - observableStore = {}; + this._rootObservable = new ReplaySubject(); + this._rootSubscription = null; - observableSchema = { - generatedUserDebt: { - type: LOGICAL, - call: urnId => `vat.urn.${urnId}.art` - }, - debtScalingFactor: { - type: LOGICAL, - call: ilkName => `vat.ilk.${ilkName}.rate` - }, - daiGenerated: { - type: DERIVED, - dependents: ['generatedUserDebt', 'debtScalingFactor'], - fn: ({ generatedUserDebt$, debtScalingFactor$ }) => - zip(generatedUserDebt$, debtScalingFactor$).pipe( - map(([a, b]) => a * b) - ) - } - }; + this.observableStore = {}; + this.observableSchema = {}; } createWatcher({ @@ -104,7 +86,96 @@ export default class MulticallService extends PublicService { return this._watcher.start(); } - registerSchema() {} + startObservable() { + if (this._watcher === undefined) throw new Error('watcher is not defined'); + + if (this._rootSubscription === null) { + this._rootSubcription = this._watcher.subscribe(updates => + this._rootObservable.next(updates) + ); + } + return this._rootObservable; + } + + destructureContractCall(call) { + const [callFnName, callTypes, returnTypes] = call + .split('(') + .map(s => { + if (s[s.length - 1] === ')') return s.substring(0, s.length - 1); + else return s; + }) + .map(s => s.split(',')) + .map(s => { + if (Array.isArray(s)) return s.filter(x => x !== ''); + else return s; + }); + + if (returnTypes.length < 1) + throw new Error( + 'Invalid contract call specified, call must return at least one argument' + ); + + return { callFnName, callTypes, returnTypes }; + } + + registerLogicalSchema(schemas) { + let addresses = this.get('smartContract').getContractAddresses(); + addresses.MDAI = addresses.MCD_DAI; + addresses.MWETH = addresses.ETH; + + const res = schemas.reduce( + ( + acc, + { contractName, contractCall, callArgs, callArgsOverrides, returnKeys } + ) => { + const { + callFnName, + callTypes, + returnTypes + } = this.destructureContractCall(contractCall); + + if (callTypes.length !== callArgs.length) + throw new Error( + `Invalid contract call specified, number of call types, ${callTypes} does not match number of call arguments, ${callArgs}` + ); + + if (returnTypes.length !== returnKeys.length) + throw new Error( + `Invalid contract call specified, number of return types, ${returnTypes} does not match number of return arguments, ${returnKeys}` + ); + + const callArgsIdentifiers = callArgsOverrides + ? callArgsOverrides.join('.') + : callArgs.map(a => a[0]).join('.'); + + const callKeyStructure = `${contractName}.${callFnName}.${callArgsIdentifiers}`; + + const processedCallArgs = callArgs.map(([arg, cb]) => { + if (cb === undefined) return arg; + return cb(arg); + }); + + const processedReturnKeys = returnKeys.map(([k, cb]) => + [`${callKeyStructure}.${k}`, cb].filter(x => x) + ); + + return { + ...acc, + [contractCall]: { + watcherCall: { + target: addresses[contractName], + call: [contractCall, ...processedCallArgs], + returns: [...processedReturnKeys] + }, + observables: [] + } + }; + }, + {} + ); + + return res; + } disconnect() { // TODO From 8ab293c6399ea0c0c1a28543f47559d9b007534c Mon Sep 17 00:00:00 2001 From: sirromdev Date: Sat, 11 Jan 2020 18:34:13 +0000 Subject: [PATCH 004/117] added example derived schema --- packages/dai-plugin-mcd/package.json | 3 +- packages/dai-plugin-mcd/src/schema.js | 26 +++- packages/dai-plugin-mcd/src/utils.js | 12 ++ packages/dai/src/eth/MulticallService.js | 145 +++++++++++++++-------- 4 files changed, 133 insertions(+), 53 deletions(-) diff --git a/packages/dai-plugin-mcd/package.json b/packages/dai-plugin-mcd/package.json index 30a517784..bdace31bc 100644 --- a/packages/dai-plugin-mcd/package.json +++ b/packages/dai-plugin-mcd/package.json @@ -19,6 +19,7 @@ "dependencies": { "@makerdao/currency": "^0.9.5", "@makerdao/services-core": "^0.9.9", - "bignumber.js": "^8.1.1" + "bignumber.js": "^8.1.1", + "rxjs": "^6.5.4" } } diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index b24d8bb1d..503fc6bcf 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,4 +1,8 @@ -import { toHex } from './utils'; +import { toHex, fromWei, fromRay } from './utils'; +import { combineLatest } from 'rxjs'; +import { map } from 'rxjs/operators'; +const LOGICAL = 'logical'; +const DERIVED = 'derived'; const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; const ENCUMBERED_DEBT = 'encumberedDebt'; @@ -12,11 +16,12 @@ const URN_DEBT_FLOOR = 'urndebtFloor'; const DAI_GENERATED = 'daiGenerated'; export const urn = (ilkName, urnAddress, vaultId) => ({ + type: LOGICAL, contractName: 'MCD_VAT', contractCall: 'urns(bytes32,address)(uint256,uint256)', callArgs: [[ilkName, toHex], [urnAddress]], callArgsOverrides: [vaultId], - returnKeys: [['ink'], ['art']], + returnKeys: [['ink', fromWei], ['art', fromWei]], observableKeys: [ [ENCUMBERED_COLLATERAL, `${vaultId}`], [ENCUMBERED_DEBT, `${vaultId}`] @@ -24,10 +29,11 @@ export const urn = (ilkName, urnAddress, vaultId) => ({ }); export const ilk = ilkName => ({ + type: LOGICAL, contractName: 'MCD_VAT', contractCall: 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', callArgs: [[ilkName, toHex]], - returnKeys: [['Art'], ['rate'], ['spot'], ['line'], ['dust']], + returnKeys: [['Art'], ['rate', fromRay], ['spot'], ['line'], ['dust']], observableKeys: [ [TOTAL_ENCUMBERED_DEBT, ilkName], [DEBT_SCALING_FACTOR, ilkName], @@ -37,9 +43,19 @@ export const ilk = ilkName => ({ ] }); -export const derivedSchema = []; +export const debt = (ilkName, vaultId) => ({ + type: DERIVED, + observableKeys: [DAI_GENERATED, `${vaultId}`], + dependencies: [ + [ENCUMBERED_DEBT, `${vaultId}`], + [DEBT_SCALING_FACTOR, ilkName] + ], + fn: ([obs1$, obs2$]) => + combineLatest(obs1$, obs2$).pipe(map(([art, rate]) => art.times(rate))) +}); export default { urn, - ilk + ilk, + debt }; diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index 5399a7bd3..e41a518f7 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -46,3 +46,15 @@ export function toHex(str, { with0x = true, rightPadding = 64 } = {}) { if (rightPadding > 0) result = padRight(result, rightPadding); return with0x ? '0x' + result : result; } + +export function fromWei(value) { + return BigNumber(value).shiftedBy(-18); +} + +export function fromRay(value) { + return BigNumber(value).shiftedBy(-27); +} + +export function fromRad(value) { + return BigNumber(value).shiftedBy(-45); +} diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index f4d36434d..79bfcc759 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,7 +1,10 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { Observable, ReplaySubject, zip, map } from 'rxjs'; +import { ReplaySubject } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import merge from 'lodash/merge'; +import get from 'lodash/get'; const log = debug('dai:MulticallService'); @@ -13,7 +16,6 @@ export default class MulticallService extends PublicService { this._rootSubscription = null; this.observableStore = {}; - this.observableSchema = {}; } createWatcher({ @@ -90,13 +92,18 @@ export default class MulticallService extends PublicService { if (this._watcher === undefined) throw new Error('watcher is not defined'); if (this._rootSubscription === null) { - this._rootSubcription = this._watcher.subscribe(updates => - this._rootObservable.next(updates) - ); + log('Initialising multicall data flow through root observable'); + this._rootSubcription = this._watcher.subscribe(updates => { + this._rootObservable.next(updates); + }); } return this._rootObservable; } + getObservable(pathString) { + return get(this.observableStore, pathString); + } + destructureContractCall(call) { const [callFnName, callTypes, returnTypes] = call .split('(') @@ -123,58 +130,102 @@ export default class MulticallService extends PublicService { addresses.MDAI = addresses.MCD_DAI; addresses.MWETH = addresses.ETH; - const res = schemas.reduce( - ( - acc, - { contractName, contractCall, callArgs, callArgsOverrides, returnKeys } - ) => { - const { - callFnName, - callTypes, - returnTypes - } = this.destructureContractCall(contractCall); - - if (callTypes.length !== callArgs.length) - throw new Error( - `Invalid contract call specified, number of call types, ${callTypes} does not match number of call arguments, ${callArgs}` - ); - - if (returnTypes.length !== returnKeys.length) - throw new Error( - `Invalid contract call specified, number of return types, ${returnTypes} does not match number of return arguments, ${returnKeys}` + this._watcher.tap(calls => [ + ...calls, + ...schemas.reduce( + ( + acc, + { + contractName, + contractCall, + callArgs, + callArgsOverrides, + returnKeys, + observableKeys + } + ) => { + const { + callFnName, + callTypes, + returnTypes + } = this.destructureContractCall(contractCall); + + if (callTypes.length !== callArgs.length) + throw new Error( + `Invalid contract call specified, number of call types, ${callTypes} does not match number of call arguments, ${callArgs}` + ); + + if (returnTypes.length !== returnKeys.length) + throw new Error( + `Invalid contract call specified, number of return types, ${returnTypes} does not match number of return arguments, ${returnKeys}` + ); + + const callArgsIdentifiers = callArgsOverrides + ? callArgsOverrides.join('.') + : callArgs.map(a => a[0]).join('.'); + + const callKeyStructure = `${contractName}.${callFnName}.${callArgsIdentifiers}`; + + const processedCallArgs = callArgs.map(([arg, cb]) => { + if (cb === undefined) return arg; + return cb(arg); + }); + + const processedReturnKeys = returnKeys.map(([k, cb]) => + [`${callKeyStructure}.${k}`, cb].filter(x => x) ); - const callArgsIdentifiers = callArgsOverrides - ? callArgsOverrides.join('.') - : callArgs.map(a => a[0]).join('.'); + observableKeys.forEach((item, idx) => { + const filteredObservable = this._rootObservable.pipe( + filter(({ type }) => type === processedReturnKeys[idx][0]), + map(({ value }) => value) + ); - const callKeyStructure = `${contractName}.${callFnName}.${callArgsIdentifiers}`; + const entry = item.reduceRight( + (obj, elem) => ({ [elem]: obj }), + filteredObservable + ); - const processedCallArgs = callArgs.map(([arg, cb]) => { - if (cb === undefined) return arg; - return cb(arg); - }); + log(`new observable for call: ${processedReturnKeys[idx][0]}`); - const processedReturnKeys = returnKeys.map(([k, cb]) => - [`${callKeyStructure}.${k}`, cb].filter(x => x) - ); + this.observableStore = merge({}, this.observableStore, entry); + }); - return { - ...acc, - [contractCall]: { - watcherCall: { + return [ + ...acc, + { target: addresses[contractName], call: [contractCall, ...processedCallArgs], returns: [...processedReturnKeys] - }, - observables: [] - } - }; - }, - {} - ); + } + ]; + }, + [] + ) + ]); + return this.observableStore; + } - return res; + registerDerivedSchema(schemas) { + schemas.forEach(({ observableKeys, dependencies, fn }) => { + const dependentObservables = dependencies.map(d => + this.getObservable(d.join('.')) + ); + + const allDependentObservablesExist = !dependentObservables.some( + x => !!x === false + ); + if (allDependentObservablesExist) { + this.observableStore = merge( + {}, + this.observableStore, + observableKeys.reduceRight( + (obj, elem) => ({ [elem]: obj }), + fn(dependentObservables) + ) + ); + } + }); } disconnect() { From 7e1eb78341a0e6fc905c42b0eebeaf624056ef29 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Sun, 12 Jan 2020 17:13:58 +0000 Subject: [PATCH 005/117] Updates --- packages/dai/src/eth/MulticallService.js | 277 +++++++++++++++-------- 1 file changed, 187 insertions(+), 90 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 79bfcc759..3bf17196f 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -6,16 +6,44 @@ import { filter, map } from 'rxjs/operators'; import merge from 'lodash/merge'; import get from 'lodash/get'; +const LOGICAL = 'logical'; +const DERIVED = 'derived'; + const log = debug('dai:MulticallService'); export default class MulticallService extends PublicService { constructor(name = 'multicall') { super(name, ['web3', 'smartContract']); - this._rootObservable = new ReplaySubject(); this._rootSubscription = null; + /* + * The rootObservable is the data sink for all multicall data. Observables + * for individual multicall data, pipe a filter to match the corresponding + * identification key, i.e MCD_VAT.{vaultId}.ink, + */ + this._rootObservable = new ReplaySubject(); + + /* + * The observable store is a store of observables keyed by the + * "observableKeys" entry in a given schema. An observable may be nested + * multiple layers dependent on the complexity of the called function. + * For example, the observable produced for the "debtScalingFactor" + * of a given ilk is stored as: + * + * { + * debtScalingFactor: { [ilkName]: debtScalingFactor$ } + * } + * + */ this.observableStore = {}; + + /* + * Cache containing unregistered observable schemas keyed by logical schemas + * which are not yet existing in the observable store at the time of + * registration + */ + this._unregisteredObservables = {}; } createWatcher({ @@ -125,106 +153,175 @@ export default class MulticallService extends PublicService { return { callFnName, callTypes, returnTypes }; } - registerLogicalSchema(schemas) { + _registerLogicalSchema({ + contractName, + contractCall, + callArgs, + callArgsOverrides, + returnKeys, + observableKeys + }) { let addresses = this.get('smartContract').getContractAddresses(); addresses.MDAI = addresses.MCD_DAI; addresses.MWETH = addresses.ETH; - this._watcher.tap(calls => [ - ...calls, - ...schemas.reduce( - ( - acc, - { - contractName, - contractCall, - callArgs, - callArgsOverrides, - returnKeys, - observableKeys + /* + * destructureContractCall enables us to extract the name of the function as + * defined in the schema. + */ + const { callFnName, callTypes, returnTypes } = this.destructureContractCall( + contractCall + ); + + if (callTypes.length !== callArgs.length) + throw new Error( + `Invalid contract call specified, number of call types, ${callTypes} does not match number of call arguments, ${callArgs}` + ); + + if (returnTypes.length !== returnKeys.length) + throw new Error( + `Invalid contract call specified, number of return types, ${returnTypes} does not match number of return arguments, ${returnKeys}` + ); + + /* + * By default, the callKeyStructure is defined by the contract name, the function name + * to be called and the "callArgsIdentifiers, interleaved by '.' easy parsing. + * + * The "callArgsIdentifiers" can be either the passed arguments to the contract call or + * multiple overrides for custom granularity + */ + const callArgsIdentifiers = callArgsOverrides + ? callArgsOverrides.join('.') + : callArgs.map(a => a[0]).join('.'); + + const callKeyStructure = `${contractName}.${callFnName}${ + callArgsIdentifiers.length > 0 ? `.${callArgsIdentifiers}` : '' + }`; + + const processedCallArgs = callArgs.map(([arg, cb]) => { + if (cb === undefined) return arg; + return cb(arg); + }); + + /* + * Return values and their callbacks are constructed here + */ + const processedReturnKeys = returnKeys.map(([k, cb]) => + [`${callKeyStructure}.${k}`, cb].filter(x => x) + ); + + /* + * For each return value specified in processedReturnKeys, an observable + * will be created which filters on that return value piped from the + * root observable. + * + * When created an object is created nesting the observable as specified by + * the corresponding observableKey array to that return value. This object is + * merged into the observableStore + * + * Finally, a check is ran on the unregisteredObservables cache to check if + * this newly added logical observable is a dependency to a derived observable. + * If one is found, the derived observable is re-registerd + */ + observableKeys.forEach((item, idx) => { + const multicallFilterKey = processedReturnKeys[idx][0]; + const filteredObservable = this._rootObservable.pipe( + filter(({ type }) => type === multicallFilterKey), + map(({ value }) => value) + ); + + const entry = item.reduceRight( + (obj, elem) => ({ [elem]: obj }), + filteredObservable + ); + + this.observableStore = merge({}, this.observableStore, entry); + + // check if added observable is a dependency of an unregisterd observable + Object.entries(this._unregisteredObservables).map( + ([dependency, args]) => { + if (dependency === item.join('.')) { + this._registerDerivedSchema(args); } - ) => { - const { - callFnName, - callTypes, - returnTypes - } = this.destructureContractCall(contractCall); - - if (callTypes.length !== callArgs.length) - throw new Error( - `Invalid contract call specified, number of call types, ${callTypes} does not match number of call arguments, ${callArgs}` - ); - - if (returnTypes.length !== returnKeys.length) - throw new Error( - `Invalid contract call specified, number of return types, ${returnTypes} does not match number of return arguments, ${returnKeys}` - ); - - const callArgsIdentifiers = callArgsOverrides - ? callArgsOverrides.join('.') - : callArgs.map(a => a[0]).join('.'); - - const callKeyStructure = `${contractName}.${callFnName}.${callArgsIdentifiers}`; - - const processedCallArgs = callArgs.map(([arg, cb]) => { - if (cb === undefined) return arg; - return cb(arg); - }); - - const processedReturnKeys = returnKeys.map(([k, cb]) => - [`${callKeyStructure}.${k}`, cb].filter(x => x) - ); + } + ); + }); - observableKeys.forEach((item, idx) => { - const filteredObservable = this._rootObservable.pipe( - filter(({ type }) => type === processedReturnKeys[idx][0]), - map(({ value }) => value) - ); - - const entry = item.reduceRight( - (obj, elem) => ({ [elem]: obj }), - filteredObservable - ); - - log(`new observable for call: ${processedReturnKeys[idx][0]}`); - - this.observableStore = merge({}, this.observableStore, entry); - }); - - return [ - ...acc, - { - target: addresses[contractName], - call: [contractCall, ...processedCallArgs], - returns: [...processedReturnKeys] - } - ]; - }, - [] - ) + /* + * Call is tapped to multicall + */ + this._watcher.tap(calls => [ + ...calls, + { + target: addresses[contractName], + call: [contractCall, ...processedCallArgs], + returns: [...processedReturnKeys] + } ]); - return this.observableStore; } - registerDerivedSchema(schemas) { - schemas.forEach(({ observableKeys, dependencies, fn }) => { - const dependentObservables = dependencies.map(d => - this.getObservable(d.join('.')) - ); + _registerDerivedSchema({ observableKeys, dependencies, fn }) { + /* + * First checks if the dependent observables exist in the observable store + */ + const dependentObservables = dependencies.map( + d => d && this.getObservable(d.join('.')) + ); - const allDependentObservablesExist = !dependentObservables.some( - x => !!x === false + const allDependentObservablesExist = !dependentObservables.some( + x => !!x === false + ); + + /* + * If all observables exist, construct the new observable by passing in + * the dependent observables to the 'fn' callback. Also removes any + * dependency entries from the unregisterdObservables cache if the existed + * before + */ + if (allDependentObservablesExist) { + dependencies.forEach(d => { + const keyString = d.join('.'); + if (!!this._unregisteredObservables[keyString]) { + delete this._unregisteredObservables[keyString]; + } + }); + + this.observableStore = merge( + {}, + this.observableStore, + observableKeys.reduceRight( + (obj, elem) => ({ [elem]: obj }), + fn(dependentObservables) + ) ); - if (allDependentObservablesExist) { - this.observableStore = merge( - {}, - this.observableStore, - observableKeys.reduceRight( - (obj, elem) => ({ [elem]: obj }), - fn(dependentObservables) - ) - ); - } + } else { + /* + * if the dependencies for this schema do not exist in the store + * than this schema is added to the unregisterd cache keyed by the + * observableKeys of each dependency + * + * When those dependencies are added, a check is ran on the unregistered + * cache for schema values which are keyed by a matching observable key. + * Schema values which match are then re ran through this function. + */ + dependencies.forEach((d, idx) => { + if (!dependentObservables[idx]) { + // add the observable to the unregistered cache, keyed by the + // missing dependency string format + this._unregisteredObservables = merge( + {}, + this._unregisteredObservables, + { [d.join('.')]: { observableKeys, dependencies, fn } } + ); + } + }); + } + } + + registerSchemas(schemas) { + schemas.map(schema => { + if (schema.type === LOGICAL) this._registerLogicalSchema(schema); + if (schema.type === DERIVED) this._registerDerivedSchema(schema); }); } From 89e078c587de00e63d05eca27dbc6cefb2c4e1d7 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 13 Jan 2020 21:35:24 +0800 Subject: [PATCH 006/117] Refactor observables and add initial tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Detection of first subscription and last unsubscription - Immediately emit latest value on initial subscription (replay) - Automatically manage schema watching/unwatching (via multicall tap) - Base observables and computed observables with dependencies - Computed observables can be composed of other base observables or computed observables - Computed observables don’t compute value until all dependent observables have emitted at least one value --- packages/dai/src/eth/MulticallService.js | 335 ++++++------------ .../dai/test/eth/MulticallService.spec.js | 33 +- packages/dai/test/helpers/schemas.js | 82 +++++ 3 files changed, 220 insertions(+), 230 deletions(-) create mode 100644 packages/dai/test/helpers/schemas.js diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 3bf17196f..697c0650c 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,49 +1,26 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { ReplaySubject } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import merge from 'lodash/merge'; +import { Observable, ReplaySubject, combineLatest } from 'rxjs'; +import { map } from 'rxjs/operators'; import get from 'lodash/get'; - -const LOGICAL = 'logical'; -const DERIVED = 'derived'; +import set from 'lodash/set'; const log = debug('dai:MulticallService'); +const log2 = debug('dai:MulticallService:observables'); export default class MulticallService extends PublicService { constructor(name = 'multicall') { super(name, ['web3', 'smartContract']); - this._rootSubscription = null; - - /* - * The rootObservable is the data sink for all multicall data. Observables - * for individual multicall data, pipe a filter to match the corresponding - * identification key, i.e MCD_VAT.{vaultId}.ink, - */ - this._rootObservable = new ReplaySubject(); + this._schemas = {}; + this._schemaByObservableKey = {}; + this._subjects = {}; + this._observables = {}; + this._watcherUpdates = null; + this._watchingSchemasTotal = 0; - /* - * The observable store is a store of observables keyed by the - * "observableKeys" entry in a given schema. An observable may be nested - * multiple layers dependent on the complexity of the called function. - * For example, the observable produced for the "debtScalingFactor" - * of a given ilk is stored as: - * - * { - * debtScalingFactor: { [ilkName]: debtScalingFactor$ } - * } - * - */ - this.observableStore = {}; - - /* - * Cache containing unregistered observable schemas keyed by logical schemas - * which are not yet existing in the observable store at the time of - * registration - */ - this._unregisteredObservables = {}; + this.addresses = {}; } createWatcher({ @@ -116,213 +93,113 @@ export default class MulticallService extends PublicService { return this._watcher.start(); } - startObservable() { - if (this._watcher === undefined) throw new Error('watcher is not defined'); - - if (this._rootSubscription === null) { - log('Initialising multicall data flow through root observable'); - this._rootSubcription = this._watcher.subscribe(updates => { - this._rootObservable.next(updates); - }); - } - return this._rootObservable; + schemaByObservableKey(key) { + return this._schemaByObservableKey[key]; } - getObservable(pathString) { - return get(this.observableStore, pathString); - } - - destructureContractCall(call) { - const [callFnName, callTypes, returnTypes] = call - .split('(') - .map(s => { - if (s[s.length - 1] === ')') return s.substring(0, s.length - 1); - else return s; - }) - .map(s => s.split(',')) - .map(s => { - if (Array.isArray(s)) return s.filter(x => x !== ''); - else return s; - }); - - if (returnTypes.length < 1) - throw new Error( - 'Invalid contract call specified, call must return at least one argument' - ); - - return { callFnName, callTypes, returnTypes }; - } - - _registerLogicalSchema({ - contractName, - contractCall, - callArgs, - callArgsOverrides, - returnKeys, - observableKeys - }) { - let addresses = this.get('smartContract').getContractAddresses(); - addresses.MDAI = addresses.MCD_DAI; - addresses.MWETH = addresses.ETH; - - /* - * destructureContractCall enables us to extract the name of the function as - * defined in the schema. - */ - const { callFnName, callTypes, returnTypes } = this.destructureContractCall( - contractCall - ); - - if (callTypes.length !== callArgs.length) - throw new Error( - `Invalid contract call specified, number of call types, ${callTypes} does not match number of call arguments, ${callArgs}` - ); - - if (returnTypes.length !== returnKeys.length) - throw new Error( - `Invalid contract call specified, number of return types, ${returnTypes} does not match number of return arguments, ${returnKeys}` - ); - - /* - * By default, the callKeyStructure is defined by the contract name, the function name - * to be called and the "callArgsIdentifiers, interleaved by '.' easy parsing. - * - * The "callArgsIdentifiers" can be either the passed arguments to the contract call or - * multiple overrides for custom granularity - */ - const callArgsIdentifiers = callArgsOverrides - ? callArgsOverrides.join('.') - : callArgs.map(a => a[0]).join('.'); - - const callKeyStructure = `${contractName}.${callFnName}${ - callArgsIdentifiers.length > 0 ? `.${callArgsIdentifiers}` : '' - }`; - - const processedCallArgs = callArgs.map(([arg, cb]) => { - if (cb === undefined) return arg; - return cb(arg); - }); - - /* - * Return values and their callbacks are constructed here - */ - const processedReturnKeys = returnKeys.map(([k, cb]) => - [`${callKeyStructure}.${k}`, cb].filter(x => x) - ); - - /* - * For each return value specified in processedReturnKeys, an observable - * will be created which filters on that return value piped from the - * root observable. - * - * When created an object is created nesting the observable as specified by - * the corresponding observableKey array to that return value. This object is - * merged into the observableStore - * - * Finally, a check is ran on the unregisteredObservables cache to check if - * this newly added logical observable is a dependency to a derived observable. - * If one is found, the derived observable is re-registerd - */ - observableKeys.forEach((item, idx) => { - const multicallFilterKey = processedReturnKeys[idx][0]; - const filteredObservable = this._rootObservable.pipe( - filter(({ type }) => type === multicallFilterKey), - map(({ value }) => value) - ); - - const entry = item.reduceRight( - (obj, elem) => ({ [elem]: obj }), - filteredObservable - ); - - this.observableStore = merge({}, this.observableStore, entry); - - // check if added observable is a dependency of an unregisterd observable - Object.entries(this._unregisteredObservables).map( - ([dependency, args]) => { - if (dependency === item.join('.')) { - this._registerDerivedSchema(args); - } - } - ); + registerSchemas(schemas) { + // this.addresses = this.get('smartContract').getContractAddresses(); + this._schemas = [...schemas]; + this._schemas.forEach(schema => { + schema.watching = {}; + if (schema.keys) + schema.keys.forEach(key => (this._schemaByObservableKey[key] = schema)); + if (schema.key) this._schemaByObservableKey[schema.key] = schema; }); - - /* - * Call is tapped to multicall - */ - this._watcher.tap(calls => [ - ...calls, - { - target: addresses[contractName], - call: [contractCall, ...processedCallArgs], - returns: [...processedReturnKeys] - } - ]); } - _registerDerivedSchema({ observableKeys, dependencies, fn }) { - /* - * First checks if the dependent observables exist in the observable store - */ - const dependentObservables = dependencies.map( - d => d && this.getObservable(d.join('.')) - ); + watchObservable(key, ...args) { + const path = args.join('.'); + const fullPath = `${key}.${path}`; - const allDependentObservablesExist = !dependentObservables.some( - x => !!x === false - ); + const schema = this.schemaByObservableKey(key); + const generatedSchema = schema.generate(...args); - /* - * If all observables exist, construct the new observable by passing in - * the dependent observables to the 'fn' callback. Also removes any - * dependency entries from the unregisterdObservables cache if the existed - * before - */ - if (allDependentObservablesExist) { - dependencies.forEach(d => { - const keyString = d.join('.'); - if (!!this._unregisteredObservables[keyString]) { - delete this._unregisteredObservables[keyString]; - } + // Subscribe to updates from watcher and emit to subjects + if (!this._watcherUpdates) { + this._watcherUpdates = this._watcher.subscribe(update => { + const subject = get(this._subjects, update.type); + if (subject) subject.next(update.value); }); + } - this.observableStore = merge( - {}, - this.observableStore, - observableKeys.reduceRight( - (obj, elem) => ({ [elem]: obj }), - fn(dependentObservables) - ) - ); - } else { - /* - * if the dependencies for this schema do not exist in the store - * than this schema is added to the unregisterd cache keyed by the - * observableKeys of each dependency - * - * When those dependencies are added, a check is ran on the unregistered - * cache for schema values which are keyed by a matching observable key. - * Schema values which match are then re ran through this function. - */ - dependencies.forEach((d, idx) => { - if (!dependentObservables[idx]) { - // add the observable to the unregistered cache, keyed by the - // missing dependency string format - this._unregisteredObservables = merge( - {}, - this._unregisteredObservables, - { [d.join('.')]: { observableKeys, dependencies, fn } } - ); - } + // This is a computed observable + if (generatedSchema.computed) { + const subs = generatedSchema.dependencies.map(dep => { + const key = dep.shift(); + return this.watchObservable(key, ...dep); + }); + return Observable.create(observer => { + const observerLatest = combineLatest(subs).pipe( + map(result => generatedSchema.computed(...result)) + ); + const sub = observerLatest.subscribe(observer); + return () => { + sub.unsubscribe(); + }; }); } - } - registerSchemas(schemas) { - schemas.map(schema => { - if (schema.type === LOGICAL) this._registerLogicalSchema(schema); - if (schema.type === DERIVED) this._registerDerivedSchema(schema); - }); + // This is a base observable + if (!get(this._subjects, fullPath)) { + const subject = new ReplaySubject(1); + set(this._subjects, fullPath, subject); + + // Create base observable + const observable = Observable.create(observer => { + this._watchingSchemasTotal++; + // If this is the first subscriber to this schema + if (!schema.watching[path]) { + schema.watching[path] = 1; + // Tap multicall to add schema (first subscriber to this schema) + const { id, contractName, call, returns } = generatedSchema; + log2(`Schema added to multicall: ${id}`); + this._watcher.tap(calls => [ + ...calls, + { + id, + target: this.addresses[contractName], + call, + returns + } + ]); + this._watcher.tap(s => { + log2('Current schemas:', s.map(({ id }) => id)); + return s; + }); + } else schema.watching[path]++; + log2('Schema subscribers:', schema.watching[path]); + + const sub = subject.subscribe(observer); + return () => { + // If no schemas are being watched, unsubscribe from watcher updates + this._watchingSchemasTotal--; + if (this._watchingSchemasTotal === 0) this._watcherUpdates.unsub(); + + sub.unsubscribe(); + schema.watching[path]--; + log2('Schema subscribers:', schema.watching[path]); + if (schema.watching[path] === 0) { + // Tap multicall to remove schema (last unsubscriber to this schema) + log2(`Schema removed from multicall: ${generatedSchema.id}`); + this._watcher.tap(schemas => + // TODO: make backwards compatible by checking undefined + schemas.filter(({ id }) => id !== generatedSchema.id) + ); + this._watcher.tap(s => { + log2('Remaining schemas count:', s.length); + return s; + }); + } + }; + }); + + log2(`Created new observable: ${fullPath}`); + set(this._observables, fullPath, observable); + return observable; + } + log2(`Returning existing observable: ${fullPath}`); + return get(this._observables, fullPath); } disconnect() { diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index 949d3429f..986e069dd 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -1,18 +1,25 @@ import TestAccountProvider from '@makerdao/test-helpers/src/TestAccountProvider'; import { buildTestMulticallService } from '../helpers/serviceBuilders'; +import schemas from '../helpers/schemas'; +import addresses from '../../../dai-plugin-mcd/contracts/addresses/testnet.json'; +import { first } from 'rxjs/operators'; let service; let watcher; let address; -beforeAll(async () => { +beforeEach(async () => { service = buildTestMulticallService(); await service.manager().authenticate(); address = TestAccountProvider.nextAddress(); watcher = service.createWatcher({ interval: 'block' }); + service.addresses = addresses; + service.registerSchemas(Object.values(schemas)); + service.start(); }); test('get eth balance via multicall', async () => { + watcher.stop(); const initialBlock = (await service.get('web3').getBlock('latest')).number + 1; const initialEthBalance = service @@ -49,3 +56,27 @@ test('get eth balance via multicall', async () => { expect(ethBalance.toString()).toEqual(initialEthBalance); expect(parseInt(blockNumber)).toEqual(initialBlock); }); + +test('watch 2 base observables from same schema', async () => { + const observable1 = service.watchObservable('debtCeiling', 'ETH-A'); + const observable2 = service.watchObservable('debtScalingFactor', 'ETH-A'); + const debtCeiling = await observable1.pipe(first()).toPromise(); + const debtScalingFactor = await observable2.pipe(first()).toPromise(); + + expect(Number(debtCeiling)).toEqual(100000); + expect(Number(debtScalingFactor)).toEqual(1); +}); + +test('watch computed observable', async () => { + const observable = service.watchObservable('testComputed1', 'ETH-A'); + const testComputed1 = await observable.pipe(first()).toPromise(); + + expect(testComputed1).toEqual(100001); +}); + +test('watch computed observable with computed dependency', async () => { + const observable = service.watchObservable('testComputed2', 5); + const testComputed2 = await observable.pipe(first()).toPromise(); + + expect(testComputed2).toEqual(500005); +}); diff --git a/packages/dai/test/helpers/schemas.js b/packages/dai/test/helpers/schemas.js new file mode 100644 index 000000000..c95c6fe76 --- /dev/null +++ b/packages/dai/test/helpers/schemas.js @@ -0,0 +1,82 @@ +import BigNumber from 'bignumber.js'; + +export function padRight(string, chars, sign) { + return string + new Array(chars - string.length + 1).join(sign ? sign : '0'); +} + +export function toHex(str, { with0x = true, rightPadding = 64 } = {}) { + let result = ''; + for (let i = 0; i < str.length; i++) { + result += str.charCodeAt(i).toString(16); + } + if (rightPadding > 0) result = padRight(result, rightPadding); + return with0x ? '0x' + result : result; +} + +export function fromWei(value) { + return BigNumber(value).shiftedBy(-18); +} + +export function fromRay(value) { + return BigNumber(value).shiftedBy(-27); +} + +export function fromRad(value) { + return BigNumber(value).shiftedBy(-45); +} + +const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; +const DEBT_SCALING_FACTOR = 'debtScalingFactor'; +const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; +const DEBT_CEILING = 'debtCeiling'; +const URN_DEBT_FLOOR = 'urnDebtFloor'; + +const TEST_COMPUTED_1 = 'testComputed1'; +const TEST_COMPUTED_2 = 'testComputed2'; + +export const ilk = { + generate: ilkName => ({ + id: `MCD_VAT.ilks(${ilkName})`, + contractName: 'MCD_VAT', + call: [ + 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', + toHex(ilkName) + ], + returns: [ + [`${TOTAL_ENCUMBERED_DEBT}.${ilkName}`], // Art + [`${DEBT_SCALING_FACTOR}.${ilkName}`, fromRay], // rate + [`${PRICE_WITH_SAFETY_MARGIN}.${ilkName}`], // spot + [`${DEBT_CEILING}.${ilkName}`, fromRad], // line + [`${URN_DEBT_FLOOR}.${ilkName}`] // dust + ] + }), + keys: [ + TOTAL_ENCUMBERED_DEBT, + DEBT_SCALING_FACTOR, + PRICE_WITH_SAFETY_MARGIN, + DEBT_CEILING, + URN_DEBT_FLOOR + ] +}; + +export const testComputed1 = { + generate: ilkName => ({ + dependencies: [[DEBT_SCALING_FACTOR, ilkName], [DEBT_CEILING, ilkName]], + computed: (val1, val2) => Number(val1) + Number(val2) + }), + key: TEST_COMPUTED_1 +}; + +export const testComputed2 = { + generate: multiplyBy => ({ + dependencies: [[TEST_COMPUTED_1, 'ETH-A']], + computed: val1 => Number(val1) * multiplyBy + }), + key: TEST_COMPUTED_2 +}; + +export default { + ilk, + testComputed1, + testComputed2 +}; From bddf39f49387e89311f2af637e05c5297c0e8b97 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 15 Jan 2020 21:25:45 +0800 Subject: [PATCH 007/117] Add additional observable features to multicall --- .prettierignore | 3 + packages/dai/src/eth/MulticallService.js | 211 +++++++++++------- .../dai/test/eth/MulticallService.spec.js | 51 ++++- packages/dai/test/helpers/schemas.js | 89 +++++--- 4 files changed, 237 insertions(+), 117 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..3f2ee15a7 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +packages/dai/src/eth/MulticallService.js +packages/dai/test/eth/MulticallService.spec.js +packages/dai/test/helpers/schemas.js diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 697c0650c..b901dce7f 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,7 +1,7 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { Observable, ReplaySubject, combineLatest } from 'rxjs'; +import { Observable, ReplaySubject, combineLatest, from } from 'rxjs'; import { map } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -13,21 +13,16 @@ export default class MulticallService extends PublicService { constructor(name = 'multicall') { super(name, ['web3', 'smartContract']); - this._schemas = {}; + this._schemas = []; this._schemaByObservableKey = {}; this._subjects = {}; this._observables = {}; this._watcherUpdates = null; this._watchingSchemasTotal = 0; - - this.addresses = {}; + this._addresses = {}; } - createWatcher({ - useWeb3Provider = false, - interval = 'block', - ...config - } = {}) { + createWatcher({ useWeb3Provider = false, interval = 'block', ...config } = {}) { const web3 = this.get('web3'); config = { multicallAddress: this.get('smartContract').getContractAddress( @@ -98,108 +93,162 @@ export default class MulticallService extends PublicService { } registerSchemas(schemas) { - // this.addresses = this.get('smartContract').getContractAddresses(); - this._schemas = [...schemas]; - this._schemas.forEach(schema => { + this._addresses = this.get('smartContract').getContractAddresses(); + + if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); + + // If schemas is key/val object use key as schema key in array object + if (!Array.isArray(schemas)) + schemas = Object.keys(schemas).map(key => ({ key, ...schemas[key] })); + // Clone if array + else schemas = schemas.map(item => ({ ...item })); + + schemas.forEach(schema => { schema.watching = {}; - if (schema.keys) - schema.keys.forEach(key => (this._schemaByObservableKey[key] = schema)); - if (schema.key) this._schemaByObservableKey[schema.key] = schema; + // Automatically use schema key as return key if no return keys specified + if (!schema.return && !schema.returns) schema.returns = [schema.key]; + + if (schema.return && schema.returns) + throw new Error('Ambiguous return key definitions on schema: found both return and returns'); + if (schema.return) schema.returns = [schema.return]; + if (!Array.isArray(schema.returns)) + throw new Error('Schema must contain return/returns key'); + schema.returns.forEach(ret => { + if (Array.isArray(ret)) this._schemaByObservableKey[ret[0]] = schema; + else if (typeof ret === 'string') + this._schemaByObservableKey[ret] = schema; + }); }); + this._schemas = [...this._schemas, ...schemas]; + log2(`Registered ${schemas.length} schemas`); } watchObservable(key, ...args) { const path = args.join('.'); - const fullPath = `${key}.${path}`; + const fullPath = `${key}${path ? '.' : ''}${path}`; const schema = this.schemaByObservableKey(key); + if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); const generatedSchema = schema.generate(...args); + log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); - // Subscribe to updates from watcher and emit to subjects - if (!this._watcherUpdates) { - this._watcherUpdates = this._watcher.subscribe(update => { - const subject = get(this._subjects, update.type); - if (subject) subject.next(update.value); - }); + const existingObservable = get(this._observables, fullPath); + if (existingObservable) { + log2(`Returning existing ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); + return existingObservable; } - // This is a computed observable + // Handle computed observable if (generatedSchema.computed) { - const subs = generatedSchema.dependencies.map(dep => { + // Handle dynamically generated dependencies + const dependencies = typeof generatedSchema.dependencies === 'function' + ? generatedSchema.dependencies() + : generatedSchema.dependencies; + + const dependencySubs = dependencies.map(dep => { + // TODO: This should allow a single string/func as well as a string array const key = dep.shift(); + // This is a promise dependency + if (typeof key === 'function') { + return from(key()); + } return this.watchObservable(key, ...dep); }); - return Observable.create(observer => { - const observerLatest = combineLatest(subs).pipe( - map(result => generatedSchema.computed(...result)) - ); + const observerLatest = combineLatest(dependencySubs).pipe( + map(result => generatedSchema.computed(...result)) + ); + + // Create computed observable + const observable = Observable.create(observer => { const sub = observerLatest.subscribe(observer); return () => { sub.unsubscribe(); }; }); + log2(`Created new computed observable: ${fullPath}`); + set(this._observables, fullPath, observable); + return observable; } // This is a base observable - if (!get(this._subjects, fullPath)) { - const subject = new ReplaySubject(1); - set(this._subjects, fullPath, subject); - - // Create base observable - const observable = Observable.create(observer => { - this._watchingSchemasTotal++; - // If this is the first subscriber to this schema - if (!schema.watching[path]) { - schema.watching[path] = 1; - // Tap multicall to add schema (first subscriber to this schema) - const { id, contractName, call, returns } = generatedSchema; - log2(`Schema added to multicall: ${id}`); - this._watcher.tap(calls => [ - ...calls, - { - id, - target: this.addresses[contractName], - call, - returns - } - ]); + const subject = new ReplaySubject(1); + set(this._subjects, fullPath, subject); + + // Create base observable + const observable = Observable.create(observer => { + // Subscribe to watcher updates and emit them to subjects + if (!this._watcherUpdates) { + log2('Subscribed to watcher updates'); + this._watcherUpdates = this._watcher.subscribe(update => { + // console.log('(MulticallService) Got update:', update) + const subject = get(this._subjects, update.type); + if (subject) subject.next(update.value); + }); + } + this._watchingSchemasTotal++; + + // If this is the first subscriber to this schema + if (!schema.watching[path]) { + schema.watching[path] = 1; + // Tap multicall to add schema (first subscriber to this schema) + let { id, contractName, call, returns } = generatedSchema; + // Automatically generate schema return keys + if (!returns) + returns = schema.returns.map(ret => { + let key = Array.isArray(ret) ? ret[0] : ret; + key = `${key}${path ? '.' : ''}${path}`; + return Array.isArray(ret) && ret.length == 2 ? [key, ret[1]] : [key]; + }); + + if (!this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); + this._watcher.tap(calls => [ + ...calls, + { + id, + target: this._addresses[contractName], + call, + returns + } + ]); + log2(`Schema added to multicall: ${id}`); + this._watcher.tap(s => { + log2('Current schemas:', s.filter(({ id }) => id).map(({ id }) => id)); + return s; + }); + } else schema.watching[path]++; + log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); + + const sub = subject.subscribe(observer); + return () => { + // If no schemas are being watched, unsubscribe from watcher updates + if (--this._watchingSchemasTotal === 0) { + log2('Unsubscribed from watcher updates'); + this._watcherUpdates.unsub(); + this._watcherUpdates = null; + } + sub.unsubscribe(); + schema.watching[path]--; + log2('Schema subscribers:', schema.watching[path]); + if (schema.watching[path] === 0) { + // Tap multicall to remove schema (last unsubscriber to this schema) + log2(`Schema removed from multicall: ${generatedSchema.id}`); + this._watcher.tap(schemas => + // TODO: make backwards compatible by checking undefined + schemas.filter(({ id }) => id !== generatedSchema.id) + ); this._watcher.tap(s => { - log2('Current schemas:', s.map(({ id }) => id)); + log2(`Remaining schemas: ${s.filter(({ id }) => id).length}`); return s; }); - } else schema.watching[path]++; - log2('Schema subscribers:', schema.watching[path]); + } + }; + }); - const sub = subject.subscribe(observer); - return () => { - // If no schemas are being watched, unsubscribe from watcher updates - this._watchingSchemasTotal--; - if (this._watchingSchemasTotal === 0) this._watcherUpdates.unsub(); + log2(`Created new base observable: ${fullPath}`); + set(this._observables, fullPath, observable); + return observable; - sub.unsubscribe(); - schema.watching[path]--; - log2('Schema subscribers:', schema.watching[path]); - if (schema.watching[path] === 0) { - // Tap multicall to remove schema (last unsubscriber to this schema) - log2(`Schema removed from multicall: ${generatedSchema.id}`); - this._watcher.tap(schemas => - // TODO: make backwards compatible by checking undefined - schemas.filter(({ id }) => id !== generatedSchema.id) - ); - this._watcher.tap(s => { - log2('Remaining schemas count:', s.length); - return s; - }); - } - }; - }); - log2(`Created new observable: ${fullPath}`); - set(this._observables, fullPath, observable); - return observable; - } - log2(`Returning existing observable: ${fullPath}`); - return get(this._observables, fullPath); } disconnect() { diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index 986e069dd..3713bc450 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -3,6 +3,7 @@ import { buildTestMulticallService } from '../helpers/serviceBuilders'; import schemas from '../helpers/schemas'; import addresses from '../../../dai-plugin-mcd/contracts/addresses/testnet.json'; import { first } from 'rxjs/operators'; +// import { promiseWait } from '../../src/utils'; let service; let watcher; @@ -13,8 +14,8 @@ beforeEach(async () => { await service.manager().authenticate(); address = TestAccountProvider.nextAddress(); watcher = service.createWatcher({ interval: 'block' }); - service.addresses = addresses; - service.registerSchemas(Object.values(schemas)); + service.registerSchemas(schemas); + service._addresses = addresses; service.start(); }); @@ -57,14 +58,24 @@ test('get eth balance via multicall', async () => { expect(parseInt(blockNumber)).toEqual(initialBlock); }); -test('watch 2 base observables from same schema', async () => { +test('watch multiple base observables from same schema', async () => { const observable1 = service.watchObservable('debtCeiling', 'ETH-A'); - const observable2 = service.watchObservable('debtScalingFactor', 'ETH-A'); - const debtCeiling = await observable1.pipe(first()).toPromise(); - const debtScalingFactor = await observable2.pipe(first()).toPromise(); + const observable3 = service.watchObservable('debtScalingFactor', 'ETH-A'); + const debtCeilingEth = await observable1.pipe(first()).toPromise(); + const debtScalingFactorEth = await observable3.pipe(first()).toPromise(); - expect(Number(debtCeiling)).toEqual(100000); - expect(Number(debtScalingFactor)).toEqual(1); + expect(Number(debtCeilingEth)).toEqual(100000); + expect(Number(debtScalingFactorEth)).toEqual(1); +}); + +test('watch multiple base observables from same schema with different schema params', async () => { + const observable1 = service.watchObservable('debtCeiling', 'ETH-A'); + const observable2 = service.watchObservable('debtCeiling', 'BAT-A'); + const debtCeilingEth = await observable1.pipe(first()).toPromise(); + const debtCeilingBat = await observable2.pipe(first()).toPromise(); + + expect(Number(debtCeilingEth)).toEqual(100000); + expect(Number(debtCeilingBat)).toEqual(5000); }); test('watch computed observable', async () => { @@ -80,3 +91,27 @@ test('watch computed observable with computed dependency', async () => { expect(testComputed2).toEqual(500005); }); + +test('watch computed observable with promise dependency', async () => { + const observable = service.watchObservable('testComputed3', 2); + const testComputed3 = await observable.pipe(first()).toPromise(); + + expect(testComputed3).toEqual(2000020); +}); + +test('watch computed observable with dynamically generated dependencies', async () => { + const observable = service.watchObservable('ilkDebtCeilings', ['ETH-A', 'BAT-A']); + const ilkDebtCeilings = await observable.pipe(first()).toPromise(); + + expect(ilkDebtCeilings).toEqual([100000, 5000]); +}); + +test('watch same computed observable with dynamically generated dependencies more than once', async () => { + const observable1 = service.watchObservable('ilkDebtCeilings', ['ETH-A', 'BAT-A']); + const observable2 = service.watchObservable('ilkDebtCeilings', ['ETH-A', 'BAT-A']); + const ilkDebtCeilings1 = await observable1.pipe(first()).toPromise(); + const ilkDebtCeilings2 = await observable2.pipe(first()).toPromise(); + + expect(ilkDebtCeilings1).toEqual([100000, 5000]); + expect(ilkDebtCeilings2).toEqual([100000, 5000]); +}); diff --git a/packages/dai/test/helpers/schemas.js b/packages/dai/test/helpers/schemas.js index c95c6fe76..7f01da5ee 100644 --- a/packages/dai/test/helpers/schemas.js +++ b/packages/dai/test/helpers/schemas.js @@ -1,5 +1,11 @@ +/* eslint-disable */ import BigNumber from 'bignumber.js'; +function debtValue(art, rate) { + art = fromWei(art); + return art.times(rate).shiftedBy(-27); +} + export function padRight(string, chars, sign) { return string + new Array(chars - string.length + 1).join(sign ? sign : '0'); } @@ -25,14 +31,11 @@ export function fromRad(value) { return BigNumber(value).shiftedBy(-45); } -const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; -const DEBT_SCALING_FACTOR = 'debtScalingFactor'; -const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; -const DEBT_CEILING = 'debtCeiling'; -const URN_DEBT_FLOOR = 'urnDebtFloor'; - -const TEST_COMPUTED_1 = 'testComputed1'; -const TEST_COMPUTED_2 = 'testComputed2'; +const totalEncumberedDebt = 'totalEncumberedDebt'; +const debtScalingFactor = 'debtScalingFactor'; +const priceWithSafetyMargin = 'priceWithSafetyMargin'; +const debtCeiling = 'debtCeiling'; +const urnDebtFloor = 'urnDebtFloor'; export const ilk = { generate: ilkName => ({ @@ -41,42 +44,72 @@ export const ilk = { call: [ 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', toHex(ilkName) - ], - returns: [ - [`${TOTAL_ENCUMBERED_DEBT}.${ilkName}`], // Art - [`${DEBT_SCALING_FACTOR}.${ilkName}`, fromRay], // rate - [`${PRICE_WITH_SAFETY_MARGIN}.${ilkName}`], // spot - [`${DEBT_CEILING}.${ilkName}`, fromRad], // line - [`${URN_DEBT_FLOOR}.${ilkName}`] // dust ] }), - keys: [ - TOTAL_ENCUMBERED_DEBT, - DEBT_SCALING_FACTOR, - PRICE_WITH_SAFETY_MARGIN, - DEBT_CEILING, - URN_DEBT_FLOOR + returns: [ + totalEncumberedDebt, + [debtScalingFactor, fromRay], + priceWithSafetyMargin, + [debtCeiling, fromRad], + urnDebtFloor ] }; +export const ilkDebt = { + generate: ilkName => ({ + dependencies: [ + ['totalEncumberedDebt', ilkName], + ['debtScalingFactor', ilkName] + ], + computed: (art, rate) => { + console.log('ilkDebt computed:', art, rate) + return debtValue(art, rate); + } + }) +}; + export const testComputed1 = { generate: ilkName => ({ - dependencies: [[DEBT_SCALING_FACTOR, ilkName], [DEBT_CEILING, ilkName]], - computed: (val1, val2) => Number(val1) + Number(val2) + dependencies: [ + ['debtScalingFactor', ilkName], + ['debtCeiling', ilkName] + ], + computed: (debtScalingFactor, debtCeiling) => Number(debtScalingFactor) + Number(debtCeiling) }), - key: TEST_COMPUTED_1 }; export const testComputed2 = { generate: multiplyBy => ({ - dependencies: [[TEST_COMPUTED_1, 'ETH-A']], - computed: val1 => Number(val1) * multiplyBy + dependencies: [ + ['testComputed1', 'ETH-A'] + ], + computed: testComputed1 => testComputed1 * multiplyBy }), - key: TEST_COMPUTED_2 +}; + +export const testComputed3 = { + generate: multiplyBy => ({ + dependencies: [ + ['testComputed2', 10], + [() => new Promise(resolve => resolve(multiplyBy))] + ], + computed: (testComputed2, promiseResult) => testComputed2 * promiseResult + }) +}; + +export const ilkDebtCeilings = { + generate: ilks => ({ + // Dynamically generate dependencies + dependencies: () => ilks.map(ilkName => ['debtCeiling', ilkName]), + computed: (...results) => results.map(r => Number(r)) + }) }; export default { ilk, + ilkDebt, testComputed1, - testComputed2 + testComputed2, + testComputed3, + ilkDebtCeilings }; From cd840f3bb9df623ffca7749e7f54cb47ddd4b0a2 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 15 Jan 2020 16:23:18 +0000 Subject: [PATCH 008/117] Adding initial test --- packages/dai-plugin-mcd/src/schema.js | 77 ++++++--------------- packages/dai-plugin-mcd/test/schema.spec.js | 32 +++++++++ 2 files changed, 55 insertions(+), 54 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schema.spec.js diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 503fc6bcf..53f0731ff 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,61 +1,30 @@ -import { toHex, fromWei, fromRay } from './utils'; +import { toHex, fromWei, fromRay, fromRad } from './utils'; import { combineLatest } from 'rxjs'; -import { map } from 'rxjs/operators'; -const LOGICAL = 'logical'; -const DERIVED = 'derived'; -const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; -const ENCUMBERED_DEBT = 'encumberedDebt'; +export const totalEncumberedDebt = 'totalEncumberedDebt'; +export const debtScalingFactor = 'debtScalingFactor'; +export const priceWithSafetyMargin = 'priceWithSafetyMargin'; +export const debtCeiling = 'debtCeiling'; +export const urnDebtFloor = 'urnDebtFloor'; -const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; -const DEBT_SCALING_FACTOR = 'debtScalingFactor'; -const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; -const DEBT_CEILING = 'debtCeiling'; -const URN_DEBT_FLOOR = 'urndebtFloor'; - -const DAI_GENERATED = 'daiGenerated'; - -export const urn = (ilkName, urnAddress, vaultId) => ({ - type: LOGICAL, - contractName: 'MCD_VAT', - contractCall: 'urns(bytes32,address)(uint256,uint256)', - callArgs: [[ilkName, toHex], [urnAddress]], - callArgsOverrides: [vaultId], - returnKeys: [['ink', fromWei], ['art', fromWei]], - observableKeys: [ - [ENCUMBERED_COLLATERAL, `${vaultId}`], - [ENCUMBERED_DEBT, `${vaultId}`] - ] -}); - -export const ilk = ilkName => ({ - type: LOGICAL, - contractName: 'MCD_VAT', - contractCall: 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', - callArgs: [[ilkName, toHex]], - returnKeys: [['Art'], ['rate', fromRay], ['spot'], ['line'], ['dust']], - observableKeys: [ - [TOTAL_ENCUMBERED_DEBT, ilkName], - [DEBT_SCALING_FACTOR, ilkName], - [PRICE_WITH_SAFETY_MARGIN, ilkName], - [DEBT_CEILING, ilkName], - [URN_DEBT_FLOOR, ilkName] +export const ilk = { + generate: ilkName => ({ + id: `MCD_VAT.ilks(${ilkName})`, + contractName: 'MCD_VAT', + call: [ + 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', + toHex(ilkName) + ] + }), + returns: [ + totalEncumberedDebt, + [debtScalingFactor, fromRay], + priceWithSafetyMargin, + [debtCeiling, fromRad], + urnDebtFloor ] -}); - -export const debt = (ilkName, vaultId) => ({ - type: DERIVED, - observableKeys: [DAI_GENERATED, `${vaultId}`], - dependencies: [ - [ENCUMBERED_DEBT, `${vaultId}`], - [DEBT_SCALING_FACTOR, ilkName] - ], - fn: ([obs1$, obs2$]) => - combineLatest(obs1$, obs2$).pipe(map(([art, rate]) => art.times(rate))) -}); +}; export default { - urn, - ilk, - debt + ilk }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js new file mode 100644 index 000000000..8b0c0b057 --- /dev/null +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -0,0 +1,32 @@ +import { mcdMaker } from './helpers'; +import { ETH } from '../src'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import schemas, { totalEncumberedDebt } from '../src/schema'; +import { first } from 'rxjs/operators'; + +let service, maker, watcher, snapshotData; + +beforeAll(async () => { + maker = await mcdMaker({ + cdpTypes: [{ currency: ETH, ilk: 'ETH-A' }], + multicall: true + }); + service = maker.service('multicall'); + watcher = service.createWatcher({ interval: 'block' }); + service.registerSchemas(schemas); + snapshotData = await takeSnapshot(maker); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test('totalEncumberedDebt', async () => { + const totalEncumberedDebt$ = service.watchObservable( + totalEncumberedDebt, + 'ETH-A' + ); + const res = await totalEncumberedDebt$.pipe(first()).toPromise(); + console.log(res); + // res not returned, hangs +}); From f29ae308fc6cf6544114100719205d7ba68aa45a Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 15 Jan 2020 17:40:26 +0000 Subject: [PATCH 009/117] Added setup function to open vaults --- packages/dai-plugin-mcd/test/schema.spec.js | 68 +++++++++++++++++---- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 8b0c0b057..095564146 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -1,19 +1,60 @@ -import { mcdMaker } from './helpers'; -import { ETH } from '../src'; +import { mcdMaker, setupCollateral } from './helpers'; +import { ETH, BAT, MDAI } from '../src'; +import { toHex } from '../src/utils'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import schemas, { totalEncumberedDebt } from '../src/schema'; +import schemas, { totalEncumberedDebt, debtScalingFactor } from '../src/schema'; import { first } from 'rxjs/operators'; +import { ServiceRoles } from '../src/constants'; -let service, maker, watcher, snapshotData; +let mcall, + maker, + watcher, + proxyAddress, + snapshotData, + cdpMgr, + cdpTypeService, + vault; + +const ETH_A_COLLATERAL_AMOUNT = ETH(1); +const ETH_A_DEBT_AMOUNT = MDAI(1); + +const BAT_A_COLLATERAL_AMOUNT = BAT(1); +const BAT_A_DEBT_AMOUNT = MDAI(1); + +const setupFn = async () => { + await setupCollateral(maker, 'ETH-A', { price: 150, debtCeiling: 50 }); + await setupCollateral(maker, 'BAT-A', { price: 5, debtCeiling: 50 }); + + const mgr = await maker.service(ServiceRoles.CDP_MANAGER); + await mgr.openLockAndDraw( + 'ETH-A', + ETH_A_COLLATERAL_AMOUNT, + ETH_A_DEBT_AMOUNT + ); + await mgr.openLockAndDraw( + 'BAT-A', + BAT_A_COLLATERAL_AMOUNT, + BAT_A_DEBT_AMOUNT + ); +}; beforeAll(async () => { maker = await mcdMaker({ - cdpTypes: [{ currency: ETH, ilk: 'ETH-A' }], + cdpTypes: [ + { currency: ETH, ilk: 'ETH-A' }, + { currency: BAT, ilk: 'BAT-A' } + ], multicall: true }); - service = maker.service('multicall'); - watcher = service.createWatcher({ interval: 'block' }); - service.registerSchemas(schemas); + + mcall = maker.service('multicall'); + watcher = mcall.createWatcher({ interval: 'block' }); + mcall.registerSchemas(schemas); + mcall.start(); + cdpMgr = maker.service(ServiceRoles.CDP_MANAGER); + cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); + + vault = await setupFn(); snapshotData = await takeSnapshot(maker); }); @@ -21,12 +62,17 @@ afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); -test('totalEncumberedDebt', async () => { - const totalEncumberedDebt$ = service.watchObservable( +test(totalEncumberedDebt, async () => { + const totalEncumberedDebt$ = mcall.watchObservable( totalEncumberedDebt, 'ETH-A' ); const res = await totalEncumberedDebt$.pipe(first()).toPromise(); + console.log(res.toString()); +}); + +test(debtScalingFactor, async () => { + const debtScalingFactor$ = mcall.watchObservable(debtScalingFactor, 'ETH-A'); + const res = await debtScalingFactor$.pipe(first()).toPromise(); console.log(res); - // res not returned, hangs }); From 719435f5fbf1d31f08dc7ff24be3759cafa60ddf Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 15 Jan 2020 21:04:16 +0000 Subject: [PATCH 010/117] Finished ilks tests and added proxyAddress and totalDaiSupply --- packages/dai-plugin-mcd/src/schema.js | 35 ++++++- packages/dai-plugin-mcd/src/utils.js | 7 ++ packages/dai-plugin-mcd/test/schema.spec.js | 109 ++++++++++++++++---- 3 files changed, 125 insertions(+), 26 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 53f0731ff..144e3f488 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,5 +1,18 @@ import { toHex, fromWei, fromRay, fromRad } from './utils'; import { combineLatest } from 'rxjs'; +import BigNumber from 'bignumber.js'; +import { MDAI } from '..'; + +export const proxyAddress = 'proxyAddress'; + +export const proxies = { + generate: address => ({ + id: `PROXY_REGISTRY.proxies(${address})`, + contractName: 'PROXY_REGISTRY', + call: ['proxies(address)(address)', address] + }), + returns: [[proxyAddress]] +}; export const totalEncumberedDebt = 'totalEncumberedDebt'; export const debtScalingFactor = 'debtScalingFactor'; @@ -7,7 +20,7 @@ export const priceWithSafetyMargin = 'priceWithSafetyMargin'; export const debtCeiling = 'debtCeiling'; export const urnDebtFloor = 'urnDebtFloor'; -export const ilk = { +export const ilks = { generate: ilkName => ({ id: `MCD_VAT.ilks(${ilkName})`, contractName: 'MCD_VAT', @@ -17,14 +30,26 @@ export const ilk = { ] }), returns: [ - totalEncumberedDebt, + [totalEncumberedDebt, BigNumber], [debtScalingFactor, fromRay], - priceWithSafetyMargin, + [priceWithSafetyMargin, fromRay], [debtCeiling, fromRad], - urnDebtFloor + [urnDebtFloor, fromRad] ] }; +export const totalDaiSupply = 'totalDaiSupply'; +export const debt = { + generate: () => ({ + id: `VAT.debt()`, + contractName: 'MCD_VAT', + call: ['debt()(uint256)'] + }), + returns: [[totalDaiSupply, MDAI.rad]] +}; + export default { - ilk + ilks, + proxies, + debt }; diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index e41a518f7..ed86321b4 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -58,3 +58,10 @@ export function fromRay(value) { export function fromRad(value) { return BigNumber(value).shiftedBy(-45); } + +export function isBigNumber(value) { + return BigNumber.isBigNumber(value); +} + +export const isValidAddressString = addressString => + /^0x([A-Fa-f0-9]{40})$/.test(addressString); diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 095564146..d836fc059 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -1,29 +1,47 @@ import { mcdMaker, setupCollateral } from './helpers'; import { ETH, BAT, MDAI } from '../src'; -import { toHex } from '../src/utils'; -import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import schemas, { totalEncumberedDebt, debtScalingFactor } from '../src/schema'; +import { + toHex, + fromRad, + fromRay, + isBigNumber, + isValidAddressString +} from '../src/utils'; +import schemas, { + totalEncumberedDebt, + debtScalingFactor, + priceWithSafetyMargin, + debtCeiling, + urnDebtFloor, + proxyAddress, + totalDaiSupply +} from '../src/schema'; import { first } from 'rxjs/operators'; import { ServiceRoles } from '../src/constants'; - +import BigNumber from 'bignumber.js'; let mcall, maker, + address, watcher, - proxyAddress, snapshotData, cdpMgr, cdpTypeService, + ethAInfo, + batAInfo, vault; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); +const ETH_A_PRICE = 180; const BAT_A_COLLATERAL_AMOUNT = BAT(1); const BAT_A_DEBT_AMOUNT = MDAI(1); const setupFn = async () => { - await setupCollateral(maker, 'ETH-A', { price: 150, debtCeiling: 50 }); - await setupCollateral(maker, 'BAT-A', { price: 5, debtCeiling: 50 }); + await setupCollateral(maker, 'ETH-A', { + price: ETH_A_PRICE + }); + // await setupCollateral(maker, 'BAT-A', { price: 5, debtCeiling: 50 }); const mgr = await maker.service(ServiceRoles.CDP_MANAGER); await mgr.openLockAndDraw( @@ -31,18 +49,18 @@ const setupFn = async () => { ETH_A_COLLATERAL_AMOUNT, ETH_A_DEBT_AMOUNT ); - await mgr.openLockAndDraw( - 'BAT-A', - BAT_A_COLLATERAL_AMOUNT, - BAT_A_DEBT_AMOUNT - ); + // await mgr.openLockAndDraw( + // 'BAT-A', + // BAT_A_COLLATERAL_AMOUNT, + // BAT_A_DEBT_AMOUNT + // ); }; beforeAll(async () => { maker = await mcdMaker({ cdpTypes: [ - { currency: ETH, ilk: 'ETH-A' }, - { currency: BAT, ilk: 'BAT-A' } + { currency: ETH, ilk: 'ETH-A' } + //{ currency: BAT, ilk: 'BAT-A' } ], multicall: true }); @@ -51,28 +69,77 @@ beforeAll(async () => { watcher = mcall.createWatcher({ interval: 'block' }); mcall.registerSchemas(schemas); mcall.start(); + address = maker.service('web3').currentAddress(); cdpMgr = maker.service(ServiceRoles.CDP_MANAGER); cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); - vault = await setupFn(); - snapshotData = await takeSnapshot(maker); -}); -afterAll(async () => { - await restoreSnapshot(snapshotData, maker); + ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); + // batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); test(totalEncumberedDebt, async () => { + const { rate } = ethAInfo; + const debtAmount = ETH_A_DEBT_AMOUNT._amount; const totalEncumberedDebt$ = mcall.watchObservable( totalEncumberedDebt, 'ETH-A' ); const res = await totalEncumberedDebt$.pipe(first()).toPromise(); - console.log(res.toString()); + + expect(isBigNumber(res)).toEqual(true); + expect(res.toString()).toEqual( + debtAmount + .shiftedBy(18) + .div(fromRay(rate)) + .toFixed(0, 0) + ); }); test(debtScalingFactor, async () => { + const { rate } = ethAInfo; const debtScalingFactor$ = mcall.watchObservable(debtScalingFactor, 'ETH-A'); const res = await debtScalingFactor$.pipe(first()).toPromise(); - console.log(res); + expect(isBigNumber(res)).toEqual(true); + expect(res.toString()).toEqual(fromRay(rate).toString()); +}); + +test(priceWithSafetyMargin, async () => { + const priceWithSafetyMargin$ = mcall.watchObservable( + priceWithSafetyMargin, + 'ETH-A' + ); + const res = await priceWithSafetyMargin$.pipe(first()).toPromise(); + expect(isBigNumber(res)).toEqual(true); + expect(res.toString()).toEqual('120'); +}); + +test(debtCeiling, async () => { + const debtCeiling$ = mcall.watchObservable(debtCeiling, 'ETH-A'); + const res = await debtCeiling$.pipe(first()).toPromise(); + expect(isBigNumber(res)).toEqual(true); + expect(res.toString()).toEqual('100000'); +}); + +test(urnDebtFloor, async () => { + const urnDebtFloor$ = mcall.watchObservable(urnDebtFloor, 'ETH-A'); + const res = await urnDebtFloor$.pipe(first()).toPromise(); + expect(isBigNumber(res)).toEqual(true); + expect(res.toString()).toEqual('0'); +}); + +test(proxyAddress, async () => { + const proxyAddress$ = mcall.watchObservable(proxyAddress, address); + const res = await proxyAddress$.pipe(first()).toPromise(); + expect(isValidAddressString(res)).toEqual(true); + expect(res.toString()).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); +}); + +test(totalDaiSupply, async () => { + const { Art, rate } = ethAInfo; + const daiGeneratedFromETHA = MDAI.rad(BigNumber(Art).times(rate)); + const totalDaiSupply$ = mcall.watchObservable(totalDaiSupply); + const res = await totalDaiSupply$.pipe(first()).toPromise(); + expect(res.symbol).toEqual('MDAI'); + expect(res.isEqual(daiGeneratedFromETHA)).toEqual(true); }); From 45d013f65660f7f0eae42fb5d933b9d079ea7f8f Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 16 Jan 2020 19:15:44 +0800 Subject: [PATCH 011/117] Minor multicall refactor --- packages/dai/src/eth/MulticallService.js | 64 ++++++++++++------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index b901dce7f..3f677aaae 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -97,7 +97,7 @@ export default class MulticallService extends PublicService { if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); - // If schemas is key/val object use key as schema key in array object + // If schemas is key/val object use key as schema key and convert to array object if (!Array.isArray(schemas)) schemas = Object.keys(schemas).map(key => ({ key, ...schemas[key] })); // Clone if array @@ -107,16 +107,15 @@ export default class MulticallService extends PublicService { schema.watching = {}; // Automatically use schema key as return key if no return keys specified if (!schema.return && !schema.returns) schema.returns = [schema.key]; - if (schema.return && schema.returns) - throw new Error('Ambiguous return key definitions on schema: found both return and returns'); + throw new Error('Ambiguous return definitions in schema: found both return and returns property'); if (schema.return) schema.returns = [schema.return]; if (!Array.isArray(schema.returns)) - throw new Error('Schema must contain return/returns key'); + throw new Error('Schema must contain return/returns property'); + // Use return keys to create observable keys => schema mapping schema.returns.forEach(ret => { if (Array.isArray(ret)) this._schemaByObservableKey[ret[0]] = schema; - else if (typeof ret === 'string') - this._schemaByObservableKey[ret] = schema; + else if (typeof ret === 'string') this._schemaByObservableKey[ret] = schema; }); }); this._schemas = [...this._schemas, ...schemas]; @@ -187,34 +186,11 @@ export default class MulticallService extends PublicService { } this._watchingSchemasTotal++; - // If this is the first subscriber to this schema + // First subscriber to this schema if (!schema.watching[path]) { schema.watching[path] = 1; // Tap multicall to add schema (first subscriber to this schema) - let { id, contractName, call, returns } = generatedSchema; - // Automatically generate schema return keys - if (!returns) - returns = schema.returns.map(ret => { - let key = Array.isArray(ret) ? ret[0] : ret; - key = `${key}${path ? '.' : ''}${path}`; - return Array.isArray(ret) && ret.length == 2 ? [key, ret[1]] : [key]; - }); - - if (!this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); - this._watcher.tap(calls => [ - ...calls, - { - id, - target: this._addresses[contractName], - call, - returns - } - ]); - log2(`Schema added to multicall: ${id}`); - this._watcher.tap(s => { - log2('Current schemas:', s.filter(({ id }) => id).map(({ id }) => id)); - return s; - }); + this._tapMulticallWithSchema(schema, generatedSchema, path); } else schema.watching[path]++; log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); @@ -247,8 +223,32 @@ export default class MulticallService extends PublicService { log2(`Created new base observable: ${fullPath}`); set(this._observables, fullPath, observable); return observable; + } - + _tapMulticallWithSchema(schema, generatedSchema, path) { + let { id, contractName, call, returns } = generatedSchema; + // Automatically generate return keys if not specified in schema + if (!returns) + returns = schema.returns.map(ret => { + let key = Array.isArray(ret) ? ret[0] : ret; + key = `${key}${path ? '.' : ''}${path}`; + return Array.isArray(ret) && ret.length == 2 ? [key, ret[1]] : [key]; + }); + if (!this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); + this._watcher.tap(calls => [ + ...calls, + { + id, + target: this._addresses[contractName], + call, + returns + } + ]); + log2(`Schema added to multicall: ${id}`); + this._watcher.tap(s => { + log2('Current schemas:', s.filter(({ id }) => id).map(({ id }) => id)); + return s; + }); } disconnect() { From bd633962204f487730afc1637c19595335120c25 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 16 Jan 2020 19:20:31 +0800 Subject: [PATCH 012/117] Add ilkPrices computed observable and test --- .prettierignore | 2 + packages/dai-plugin-mcd/src/schema.js | 63 ++++++++++++++++++++- packages/dai-plugin-mcd/test/schema.spec.js | 9 +++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/.prettierignore b/.prettierignore index 3f2ee15a7..a2342e6f5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ packages/dai/src/eth/MulticallService.js packages/dai/test/eth/MulticallService.spec.js packages/dai/test/helpers/schemas.js +packages/dai-plugin-mcd/src/schema.js +packages/dai-plugin-mcd/test/schema.spec.js diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 144e3f488..cb151e59f 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,7 +1,7 @@ +import { createCurrencyRatio } from '@makerdao/currency'; import { toHex, fromWei, fromRay, fromRad } from './utils'; -import { combineLatest } from 'rxjs'; import BigNumber from 'bignumber.js'; -import { MDAI } from '..'; +import { USD, ETH, BAT, MDAI } from '..'; export const proxyAddress = 'proxyAddress'; @@ -48,8 +48,65 @@ export const debt = { returns: [[totalDaiSupply, MDAI.rad]] }; +export const ilkPrices = { + generate: ilkNames => ({ + // Dynamically generated dependencies + dependencies: () => [ + ['refPerDai'], + ...ilkNames.reduce((acc, ilk) => [...acc, + ['priceWithSafetyMargin', ilk], + ['liquidationRatio', ilk] + ], []) + ], + computed: (refPerDai, ...results) => + results.reduce((acc, r, i) => { + if (i % 2 === 0) { + const currency = ETH; + const priceWithSafetyMargin = results[i]; + const liquidationRatio = results[i + 1]; + const ratio = createCurrencyRatio(USD, currency); + const price = priceWithSafetyMargin.times(refPerDai).times(liquidationRatio.toNumber()); + return [...acc, ratio(price)]; + } + return acc; + }, []) + }) +}; + +export const priceFeedAddress = 'priceFeedAddress'; +export const liquidationRatio = 'liquidationRatio'; +export const spotIlks = { + generate: ilkName => ({ + id: `MCD_SPOT.ilks(${ilkName})`, + contractName: 'MCD_SPOT', + call: [ + 'ilks(bytes32)(address,uint256)', + toHex(ilkName) + ], + }), + returns: [ + priceFeedAddress, + [liquidationRatio, v => createCurrencyRatio(USD, MDAI)(BigNumber(fromRay(v)))] + ] +}; + +export const refPerDai = 'refPerDai'; +export const spotPar = { + generate: () => ({ + id: 'MCD_SPOT.par()', + contractName: 'MCD_SPOT', + call: ['par()(uint256)'], + }), + returns: [ + [refPerDai, fromRay] + ] +}; + export default { ilks, proxies, - debt + debt, + spotIlks, + spotPar, + ilkPrices, }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index d836fc059..b0cb58eda 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -143,3 +143,12 @@ test(totalDaiSupply, async () => { expect(res.symbol).toEqual('MDAI'); expect(res.isEqual(daiGeneratedFromETHA)).toEqual(true); }); + +test('ilkPrices', async () => { + const obs = mcall.watchObservable('ilkPrices', ['ETH-A', 'ETH-B']); + const res = await obs.pipe(first()).toPromise(); + expect(res[0].toNumber()).toEqual(180); + expect(res[0].symbol).toEqual('USD/ETH'); + expect(res[1].toNumber()).toEqual(150); + expect(res[1].symbol).toEqual('USD/ETH'); +}); From f6618f038a1dd2b37b44b486b74aa8c3419768fd Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 16 Jan 2020 13:22:29 +0000 Subject: [PATCH 013/117] Added helper fn, BAT-A ilk, , maker.watch and test changes --- packages/dai-plugin-mcd/src/schema.js | 54 +++++++------- packages/dai-plugin-mcd/test/schema.spec.js | 73 +++++++++++++------ packages/dai/src/Maker.js | 3 +- packages/dai/src/eth/MulticallService.js | 51 +++++++++---- .../dai/test/eth/MulticallService.spec.js | 15 +++- 5 files changed, 130 insertions(+), 66 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index cb151e59f..d20cbb8fe 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -53,23 +53,29 @@ export const ilkPrices = { // Dynamically generated dependencies dependencies: () => [ ['refPerDai'], - ...ilkNames.reduce((acc, ilk) => [...acc, - ['priceWithSafetyMargin', ilk], - ['liquidationRatio', ilk] - ], []) + ...ilkNames.reduce( + (acc, ilk) => [ + ...acc, + ['priceWithSafetyMargin', ilk], + ['liquidationRatio', ilk] + ], + [] + ) ], computed: (refPerDai, ...results) => - results.reduce((acc, r, i) => { - if (i % 2 === 0) { - const currency = ETH; - const priceWithSafetyMargin = results[i]; - const liquidationRatio = results[i + 1]; - const ratio = createCurrencyRatio(USD, currency); - const price = priceWithSafetyMargin.times(refPerDai).times(liquidationRatio.toNumber()); - return [...acc, ratio(price)]; - } - return acc; - }, []) + results.reduce((acc, r, i) => { + if (i % 2 === 0) { + const currency = ETH; + const priceWithSafetyMargin = results[i]; + const liquidationRatio = results[i + 1]; + const ratio = createCurrencyRatio(USD, currency); + const price = priceWithSafetyMargin + .times(refPerDai) + .times(liquidationRatio.toNumber()); + return [...acc, ratio(price)]; + } + return acc; + }, []) }) }; @@ -79,14 +85,14 @@ export const spotIlks = { generate: ilkName => ({ id: `MCD_SPOT.ilks(${ilkName})`, contractName: 'MCD_SPOT', - call: [ - 'ilks(bytes32)(address,uint256)', - toHex(ilkName) - ], + call: ['ilks(bytes32)(address,uint256)', toHex(ilkName)] }), returns: [ priceFeedAddress, - [liquidationRatio, v => createCurrencyRatio(USD, MDAI)(BigNumber(fromRay(v)))] + [ + liquidationRatio, + v => createCurrencyRatio(USD, MDAI)(BigNumber(fromRay(v))) + ] ] }; @@ -95,11 +101,9 @@ export const spotPar = { generate: () => ({ id: 'MCD_SPOT.par()', contractName: 'MCD_SPOT', - call: ['par()(uint256)'], + call: ['par()(uint256)'] }), - returns: [ - [refPerDai, fromRay] - ] + returns: [[refPerDai, fromRay]] }; export default { @@ -108,5 +112,5 @@ export default { debt, spotIlks, spotPar, - ilkPrices, + ilkPrices }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index b0cb58eda..032bba508 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -36,12 +36,13 @@ const ETH_A_PRICE = 180; const BAT_A_COLLATERAL_AMOUNT = BAT(1); const BAT_A_DEBT_AMOUNT = MDAI(1); +const BAT_A_PRICE = 40; const setupFn = async () => { await setupCollateral(maker, 'ETH-A', { price: ETH_A_PRICE }); - // await setupCollateral(maker, 'BAT-A', { price: 5, debtCeiling: 50 }); + await setupCollateral(maker, 'BAT-A', { price: BAT_A_PRICE }); const mgr = await maker.service(ServiceRoles.CDP_MANAGER); await mgr.openLockAndDraw( @@ -49,18 +50,20 @@ const setupFn = async () => { ETH_A_COLLATERAL_AMOUNT, ETH_A_DEBT_AMOUNT ); - // await mgr.openLockAndDraw( - // 'BAT-A', - // BAT_A_COLLATERAL_AMOUNT, - // BAT_A_DEBT_AMOUNT - // ); + await mgr.openLockAndDraw( + 'BAT-A', + BAT_A_COLLATERAL_AMOUNT, + BAT_A_DEBT_AMOUNT + ); }; +const giveLatest = obs$ => obs$.pipe(first()).toPromise(); + beforeAll(async () => { maker = await mcdMaker({ cdpTypes: [ - { currency: ETH, ilk: 'ETH-A' } - //{ currency: BAT, ilk: 'BAT-A' } + { currency: ETH, ilk: 'ETH-A' }, + { currency: BAT, ilk: 'BAT-A' } ], multicall: true }); @@ -69,29 +72,47 @@ beforeAll(async () => { watcher = mcall.createWatcher({ interval: 'block' }); mcall.registerSchemas(schemas); mcall.start(); + address = maker.service('web3').currentAddress(); cdpMgr = maker.service(ServiceRoles.CDP_MANAGER); cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); + vault = await setupFn(); ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); - // batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); + batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); test(totalEncumberedDebt, async () => { - const { rate } = ethAInfo; - const debtAmount = ETH_A_DEBT_AMOUNT._amount; - const totalEncumberedDebt$ = mcall.watchObservable( - totalEncumberedDebt, - 'ETH-A' + // TODO Define hardcoded rates for given ilks outside of the system and test + // against those rather than data extracted from the chain + const { rate: ethARate } = ethAInfo; + const { rate: batARate } = batAInfo; + + const ethADebtAmount = ETH_A_DEBT_AMOUNT._amount; + const batADebtAmount = BAT_A_DEBT_AMOUNT._amount; + + const ethAEncumberedDebt = await giveLatest( + maker.watch(totalEncumberedDebt, 'ETH-A') + ); + const batAEncumberedDebt = await giveLatest( + maker.watch(totalEncumberedDebt, 'BAT-A') ); - const res = await totalEncumberedDebt$.pipe(first()).toPromise(); - expect(isBigNumber(res)).toEqual(true); - expect(res.toString()).toEqual( - debtAmount + expect(isBigNumber(ethAEncumberedDebt)).toEqual(true); + expect(isBigNumber(batAEncumberedDebt)).toEqual(true); + + expect(ethAEncumberedDebt.toString()).toEqual( + ethADebtAmount .shiftedBy(18) - .div(fromRay(rate)) + .div(fromRay(ethARate)) + .toFixed(0, 0) + ); + + expect(batAEncumberedDebt.toString()).toEqual( + batADebtAmount + .shiftedBy(18) + .div(fromRay(batARate)) .toFixed(0, 0) ); }); @@ -136,12 +157,16 @@ test(proxyAddress, async () => { }); test(totalDaiSupply, async () => { - const { Art, rate } = ethAInfo; - const daiGeneratedFromETHA = MDAI.rad(BigNumber(Art).times(rate)); - const totalDaiSupply$ = mcall.watchObservable(totalDaiSupply); - const res = await totalDaiSupply$.pipe(first()).toPromise(); + const { Art: ethAArt, rate: ethARate } = ethAInfo; + const { Art: batAArt, rate: batARate } = batAInfo; + + const ethADaiGenerated = MDAI.rad(BigNumber(ethAArt).times(ethARate)); + const batADaiGenerated = MDAI.rad(BigNumber(batAArt).times(batARate)); + + const res = await giveLatest(maker.watch(totalDaiSupply)); + expect(res.symbol).toEqual('MDAI'); - expect(res.isEqual(daiGeneratedFromETHA)).toEqual(true); + expect(res.isEqual(ethADaiGenerated.plus(batADaiGenerated))).toEqual(true); }); test('ilkPrices', async () => { diff --git a/packages/dai/src/Maker.js b/packages/dai/src/Maker.js index bdf047f67..a4b8d0e2f 100644 --- a/packages/dai/src/Maker.js +++ b/packages/dai/src/Maker.js @@ -51,7 +51,8 @@ export default class Maker { cdp: ['getCdp', 'openCdp', 'getCdpIds'], event: ['on'], proxy: ['currentProxy'], - token: ['getToken'] + token: ['getToken'], + multicall: ['watch'] }); } diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 3f677aaae..287cdb417 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -22,7 +22,15 @@ export default class MulticallService extends PublicService { this._addresses = {}; } - createWatcher({ useWeb3Provider = false, interval = 'block', ...config } = {}) { + initialize() { + this.watch = this.watchObservable; + } + + createWatcher({ + useWeb3Provider = false, + interval = 'block', + ...config + } = {}) { const web3 = this.get('web3'); config = { multicallAddress: this.get('smartContract').getContractAddress( @@ -95,7 +103,8 @@ export default class MulticallService extends PublicService { registerSchemas(schemas) { this._addresses = this.get('smartContract').getContractAddresses(); - if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); + if (typeof schemas !== 'object') + throw new Error('Schemas must be object or array'); // If schemas is key/val object use key as schema key and convert to array object if (!Array.isArray(schemas)) @@ -108,14 +117,17 @@ export default class MulticallService extends PublicService { // Automatically use schema key as return key if no return keys specified if (!schema.return && !schema.returns) schema.returns = [schema.key]; if (schema.return && schema.returns) - throw new Error('Ambiguous return definitions in schema: found both return and returns property'); + throw new Error( + 'Ambiguous return definitions in schema: found both return and returns property' + ); if (schema.return) schema.returns = [schema.return]; if (!Array.isArray(schema.returns)) throw new Error('Schema must contain return/returns property'); // Use return keys to create observable keys => schema mapping schema.returns.forEach(ret => { if (Array.isArray(ret)) this._schemaByObservableKey[ret[0]] = schema; - else if (typeof ret === 'string') this._schemaByObservableKey[ret] = schema; + else if (typeof ret === 'string') + this._schemaByObservableKey[ret] = schema; }); }); this._schemas = [...this._schemas, ...schemas]; @@ -127,22 +139,32 @@ export default class MulticallService extends PublicService { const fullPath = `${key}${path ? '.' : ''}${path}`; const schema = this.schemaByObservableKey(key); - if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); + if (!schema) + throw new Error(`No registered schema found for observable key: ${key}`); const generatedSchema = schema.generate(...args); - log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); + log2( + `watchObservable() called for ${ + generatedSchema.computed ? 'computed ' : 'base ' + }observable: ${fullPath}` + ); const existingObservable = get(this._observables, fullPath); if (existingObservable) { - log2(`Returning existing ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); + log2( + `Returning existing ${ + generatedSchema.computed ? 'computed ' : 'base ' + }observable: ${fullPath}` + ); return existingObservable; } // Handle computed observable if (generatedSchema.computed) { // Handle dynamically generated dependencies - const dependencies = typeof generatedSchema.dependencies === 'function' - ? generatedSchema.dependencies() - : generatedSchema.dependencies; + const dependencies = + typeof generatedSchema.dependencies === 'function' + ? generatedSchema.dependencies() + : generatedSchema.dependencies; const dependencySubs = dependencies.map(dep => { // TODO: This should allow a single string/func as well as a string array @@ -158,7 +180,7 @@ export default class MulticallService extends PublicService { ); // Create computed observable - const observable = Observable.create(observer => { + const observable = Observable.create(observer => { const sub = observerLatest.subscribe(observer); return () => { sub.unsubscribe(); @@ -192,7 +214,9 @@ export default class MulticallService extends PublicService { // Tap multicall to add schema (first subscriber to this schema) this._tapMulticallWithSchema(schema, generatedSchema, path); } else schema.watching[path]++; - log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); + log2( + `Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}` + ); const sub = subject.subscribe(observer); return () => { @@ -234,7 +258,8 @@ export default class MulticallService extends PublicService { key = `${key}${path ? '.' : ''}${path}`; return Array.isArray(ret) && ret.length == 2 ? [key, ret[1]] : [key]; }); - if (!this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); + if (!this._addresses[contractName]) + throw new Error(`Can't find contract address for ${contractName}`); this._watcher.tap(calls => [ ...calls, { diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index 3713bc450..ecaab9536 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -100,15 +100,24 @@ test('watch computed observable with promise dependency', async () => { }); test('watch computed observable with dynamically generated dependencies', async () => { - const observable = service.watchObservable('ilkDebtCeilings', ['ETH-A', 'BAT-A']); + const observable = service.watchObservable('ilkDebtCeilings', [ + 'ETH-A', + 'BAT-A' + ]); const ilkDebtCeilings = await observable.pipe(first()).toPromise(); expect(ilkDebtCeilings).toEqual([100000, 5000]); }); test('watch same computed observable with dynamically generated dependencies more than once', async () => { - const observable1 = service.watchObservable('ilkDebtCeilings', ['ETH-A', 'BAT-A']); - const observable2 = service.watchObservable('ilkDebtCeilings', ['ETH-A', 'BAT-A']); + const observable1 = service.watchObservable('ilkDebtCeilings', [ + 'ETH-A', + 'BAT-A' + ]); + const observable2 = service.watchObservable('ilkDebtCeilings', [ + 'ETH-A', + 'BAT-A' + ]); const ilkDebtCeilings1 = await observable1.pipe(first()).toPromise(); const ilkDebtCeilings2 = await observable2.pipe(first()).toPromise(); From a2a831112fbbcff37d003ea7f19be2ddab5a0980 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 16 Jan 2020 14:21:30 +0000 Subject: [PATCH 014/117] Added maker.latest for one-time calling --- packages/dai-plugin-mcd/test/schema.spec.js | 118 ++++++++++++-------- packages/dai/src/Maker.js | 2 +- packages/dai/src/eth/MulticallService.js | 8 +- 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 032bba508..65db10b9f 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -16,7 +16,6 @@ import schemas, { proxyAddress, totalDaiSupply } from '../src/schema'; -import { first } from 'rxjs/operators'; import { ServiceRoles } from '../src/constants'; import BigNumber from 'bignumber.js'; let mcall, @@ -57,8 +56,6 @@ const setupFn = async () => { ); }; -const giveLatest = obs$ => obs$.pipe(first()).toPromise(); - beforeAll(async () => { maker = await mcdMaker({ cdpTypes: [ @@ -92,68 +89,83 @@ test(totalEncumberedDebt, async () => { const ethADebtAmount = ETH_A_DEBT_AMOUNT._amount; const batADebtAmount = BAT_A_DEBT_AMOUNT._amount; - const ethAEncumberedDebt = await giveLatest( - maker.watch(totalEncumberedDebt, 'ETH-A') - ); - const batAEncumberedDebt = await giveLatest( - maker.watch(totalEncumberedDebt, 'BAT-A') - ); + const ethAEncumberedDebt = await maker.latest(totalEncumberedDebt, 'ETH-A'); + const batAEncumberedDebt = await maker.latest(totalEncumberedDebt, 'BAT-A'); expect(isBigNumber(ethAEncumberedDebt)).toEqual(true); expect(isBigNumber(batAEncumberedDebt)).toEqual(true); - expect(ethAEncumberedDebt.toString()).toEqual( + expect(ethAEncumberedDebt).toEqual( ethADebtAmount .shiftedBy(18) .div(fromRay(ethARate)) - .toFixed(0, 0) + .integerValue(0) ); - expect(batAEncumberedDebt.toString()).toEqual( + expect(batAEncumberedDebt).toEqual( batADebtAmount .shiftedBy(18) .div(fromRay(batARate)) - .toFixed(0, 0) + .integerValue(0) ); }); test(debtScalingFactor, async () => { - const { rate } = ethAInfo; - const debtScalingFactor$ = mcall.watchObservable(debtScalingFactor, 'ETH-A'); - const res = await debtScalingFactor$.pipe(first()).toPromise(); - expect(isBigNumber(res)).toEqual(true); - expect(res.toString()).toEqual(fromRay(rate).toString()); + const { rate: ethARate } = ethAInfo; + const { rate: batARate } = batAInfo; + + const ethADebtScalingFactor = await maker.latest(debtScalingFactor, 'ETH-A'); + const batADebtScalingFactor = await maker.latest(debtScalingFactor, 'BAT-A'); + + expect(isBigNumber(ethADebtScalingFactor)).toEqual(true); + expect(isBigNumber(batADebtScalingFactor)).toEqual(true); + + expect(ethADebtScalingFactor).toEqual(fromRay(ethARate)); + expect(batADebtScalingFactor).toEqual(fromRay(batARate)); }); test(priceWithSafetyMargin, async () => { - const priceWithSafetyMargin$ = mcall.watchObservable( + const ethAPriceWithSafetyMargin = await maker.latest( priceWithSafetyMargin, 'ETH-A' ); - const res = await priceWithSafetyMargin$.pipe(first()).toPromise(); - expect(isBigNumber(res)).toEqual(true); - expect(res.toString()).toEqual('120'); + const batAPriceWithSafetyMargin = await maker.latest( + priceWithSafetyMargin, + 'BAT-A' + ); + + const ethACalculatedMargin = BigNumber(ETH_A_PRICE) + .times(2) + .div(3); + const batACalculatedMargin = BigNumber(BAT_A_PRICE).div(2); + + expect(isBigNumber(ethAPriceWithSafetyMargin)).toEqual(true); + expect(isBigNumber(batAPriceWithSafetyMargin)).toEqual(true); + + expect(ethAPriceWithSafetyMargin).toEqual(ethACalculatedMargin); + expect(batAPriceWithSafetyMargin).toEqual(batACalculatedMargin); }); test(debtCeiling, async () => { - const debtCeiling$ = mcall.watchObservable(debtCeiling, 'ETH-A'); - const res = await debtCeiling$.pipe(first()).toPromise(); - expect(isBigNumber(res)).toEqual(true); - expect(res.toString()).toEqual('100000'); + const ethADebtCeiling = await maker.latest(debtCeiling, 'ETH-A'); + const batADebtCeiling = await maker.latest(debtCeiling, 'BAT-A'); + + expect(isBigNumber(ethADebtCeiling)).toEqual(true); + expect(isBigNumber(batADebtCeiling)).toEqual(true); + + expect(ethADebtCeiling).toEqual(BigNumber('100000')); + expect(batADebtCeiling).toEqual(BigNumber('5000')); }); test(urnDebtFloor, async () => { - const urnDebtFloor$ = mcall.watchObservable(urnDebtFloor, 'ETH-A'); - const res = await urnDebtFloor$.pipe(first()).toPromise(); - expect(isBigNumber(res)).toEqual(true); - expect(res.toString()).toEqual('0'); -}); + const ethAUrnDebtFloor = await maker.latest(urnDebtFloor, 'ETH-A'); + const batAUrnDebtFloor = await maker.latest(urnDebtFloor, 'BAT-A'); -test(proxyAddress, async () => { - const proxyAddress$ = mcall.watchObservable(proxyAddress, address); - const res = await proxyAddress$.pipe(first()).toPromise(); - expect(isValidAddressString(res)).toEqual(true); - expect(res.toString()).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); + expect(isBigNumber(ethAUrnDebtFloor)).toEqual(true); + expect(isBigNumber(batAUrnDebtFloor)).toEqual(true); + + expect(ethAUrnDebtFloor).toEqual(BigNumber('0')); + expect(batAUrnDebtFloor).toEqual(BigNumber('0')); }); test(totalDaiSupply, async () => { @@ -162,18 +174,32 @@ test(totalDaiSupply, async () => { const ethADaiGenerated = MDAI.rad(BigNumber(ethAArt).times(ethARate)); const batADaiGenerated = MDAI.rad(BigNumber(batAArt).times(batARate)); + const sumOfDaiGeneratedFromIlks = ethADaiGenerated.plus(batADaiGenerated); - const res = await giveLatest(maker.watch(totalDaiSupply)); + const totalDaiAmount = await maker.latest(totalDaiSupply); - expect(res.symbol).toEqual('MDAI'); - expect(res.isEqual(ethADaiGenerated.plus(batADaiGenerated))).toEqual(true); + expect(totalDaiAmount.symbol).toEqual('MDAI'); + expect(totalDaiAmount.isEqual(sumOfDaiGeneratedFromIlks)).toEqual(true); }); -test('ilkPrices', async () => { - const obs = mcall.watchObservable('ilkPrices', ['ETH-A', 'ETH-B']); - const res = await obs.pipe(first()).toPromise(); - expect(res[0].toNumber()).toEqual(180); - expect(res[0].symbol).toEqual('USD/ETH'); - expect(res[1].toNumber()).toEqual(150); - expect(res[1].symbol).toEqual('USD/ETH'); +test(proxyAddress, async () => { + const proxy = await maker.latest(proxyAddress, address); + expect(isValidAddressString(proxy)).toEqual(true); + expect(proxy).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); +}); + +test.skip('ilkPrices', async () => { + const [ethAPrice, ethBPrice, batAPrice] = await maker.latest('ilkPrices', [ + 'ETH-A', + 'ETH-B', + 'BAT-A' + ]); + + expect(ethAPrice.toNumber()).toEqual(180); + expect(ethBPrice.toNumber()).toEqual(150); + expect(batAPrice.toNumber()).toEqual(40); + + expect(ethAPrice.symbol).toEqual('USD/ETH'); + expect(ethBPrice.symbol).toEqual('USD/ETH'); + expect(batAPrice.symbol).toEqual('USD/BAT'); }); diff --git a/packages/dai/src/Maker.js b/packages/dai/src/Maker.js index a4b8d0e2f..e96c7bbae 100644 --- a/packages/dai/src/Maker.js +++ b/packages/dai/src/Maker.js @@ -52,7 +52,7 @@ export default class Maker { event: ['on'], proxy: ['currentProxy'], token: ['getToken'], - multicall: ['watch'] + multicall: ['watch', 'latest'] }); } diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 287cdb417..bd847fa80 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -2,7 +2,7 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; import { Observable, ReplaySubject, combineLatest, from } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, first } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -249,6 +249,12 @@ export default class MulticallService extends PublicService { return observable; } + latest(key, ...args) { + return this.watchObservable(key, ...args) + .pipe(first()) + .toPromise(); + } + _tapMulticallWithSchema(schema, generatedSchema, path) { let { id, contractName, call, returns } = generatedSchema; // Automatically generate return keys if not specified in schema From 663a47e6e2b5a802a179cc5db9a3c35f9d6ba58c Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Thu, 16 Jan 2020 20:17:37 +0100 Subject: [PATCH 015/117] CDP-733 schemas for ink, art, unlocked collateral --- packages/dai-plugin-mcd/src/schema.js | 27 +++++++++++++++-- packages/dai-plugin-mcd/test/schema.spec.js | 32 ++++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index d20cbb8fe..2009a2b02 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -41,7 +41,7 @@ export const ilks = { export const totalDaiSupply = 'totalDaiSupply'; export const debt = { generate: () => ({ - id: `VAT.debt()`, + id: 'VAT.debt()', contractName: 'MCD_VAT', call: ['debt()(uint256)'] }), @@ -106,11 +106,34 @@ export const spotPar = { returns: [[refPerDai, fromRay]] }; +export const unlockedCollateral = 'unlockedCollateral';//TODO is it best naming? +export const vatGem = { + generate: (ilkName, urn) => ({ + id: `MCD_Vat.gem(${ilkName},${urn})`, + contractName: 'MCD_VAT', + call: ['gem(bytes32,address)(uint)', toHex(ilkName), urn] + }), + return: [unlockedCollateral, fromWei] +}; + +export const urnInk = 'urnInk'; +export const urnArt = 'urnArt'; +export const urnState = { + generate: (ilkName, urn) => ({ + id: `MCD_Vat.urns(${ilkName},${urn})`, + contractName: 'MCD_VAT', + call: ['urns(bytes32,address)(uint256,uint256)', toHex(ilkName), urn] + }), + returns: [[urnInk, fromWei], [urnArt, fromWei]] +}; + export default { ilks, proxies, debt, spotIlks, spotPar, - ilkPrices + ilkPrices, + vatGem, + urnState }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 65db10b9f..5c641c1c8 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -3,6 +3,7 @@ import { ETH, BAT, MDAI } from '../src'; import { toHex, fromRad, + fromWei, fromRay, isBigNumber, isValidAddressString @@ -14,7 +15,10 @@ import schemas, { debtCeiling, urnDebtFloor, proxyAddress, - totalDaiSupply + totalDaiSupply, + unlockedCollateral, + urnInk, + urnArt } from '../src/schema'; import { ServiceRoles } from '../src/constants'; import BigNumber from 'bignumber.js'; @@ -25,6 +29,7 @@ let mcall, snapshotData, cdpMgr, cdpTypeService, + proxyService, ethAInfo, batAInfo, vault; @@ -73,6 +78,7 @@ beforeAll(async () => { address = maker.service('web3').currentAddress(); cdpMgr = maker.service(ServiceRoles.CDP_MANAGER); cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); + proxyService = maker.service('proxy'); vault = await setupFn(); @@ -203,3 +209,27 @@ test.skip('ilkPrices', async () => { expect(ethBPrice.symbol).toEqual('USD/ETH'); expect(batAPrice.symbol).toEqual('USD/BAT'); }); + +test('unlockedCollateral', async () => { + const cdpId = 1; + const expected = 0; + const col = await maker.latest(unlockedCollateral, 'ETH-A', await cdpMgr.getUrn(cdpId)); + + expect(col).toEqual(fromWei(expected)); +}); + +test('urnInk', async () => { + const cdpId = 1; + const expected = fromWei(1000000000000000000); + const col = await maker.latest(urnInk, 'ETH-A', await cdpMgr.getUrn(cdpId)); + + expect(col).toEqual(expected); +}); + +test('urnArt', async () => { + const cdpId = 1; + const expected = fromWei(995000000000000000); + const col = await maker.latest(urnArt, 'ETH-A', await cdpMgr.getUrn(cdpId)); + + expect(col.toNumber()).toBeCloseTo(expected.toNumber()); +}); \ No newline at end of file From ec58542a0de334f61d142cd671a01eb9585771b9 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 16 Jan 2020 19:53:57 +0000 Subject: [PATCH 016/117] Changed constant names --- packages/dai-plugin-mcd/src/schema.js | 118 ++++++++++-------- packages/dai-plugin-mcd/test/schema.spec.js | 126 +++++++++++++++----- 2 files changed, 160 insertions(+), 84 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index d20cbb8fe..4ae53ef3a 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,9 +1,9 @@ -import { createCurrencyRatio } from '@makerdao/currency'; +import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; import { toHex, fromWei, fromRay, fromRad } from './utils'; import BigNumber from 'bignumber.js'; import { USD, ETH, BAT, MDAI } from '..'; -export const proxyAddress = 'proxyAddress'; +export const PROXY_ADDRESS = 'proxyAddress'; export const proxies = { generate: address => ({ @@ -11,16 +11,16 @@ export const proxies = { contractName: 'PROXY_REGISTRY', call: ['proxies(address)(address)', address] }), - returns: [[proxyAddress]] + returns: [[PROXY_ADDRESS]] }; -export const totalEncumberedDebt = 'totalEncumberedDebt'; -export const debtScalingFactor = 'debtScalingFactor'; -export const priceWithSafetyMargin = 'priceWithSafetyMargin'; -export const debtCeiling = 'debtCeiling'; -export const urnDebtFloor = 'urnDebtFloor'; +export const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; +export const DEBT_SCALING_FACTOR = 'debtScalingFactor'; +export const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; +export const DEBT_CEILING = 'debtCeiling'; +export const URN_DEBT_FLOOR = 'urnDebtFloor'; -export const ilks = { +export const vatIlks = { generate: ilkName => ({ id: `MCD_VAT.ilks(${ilkName})`, contractName: 'MCD_VAT', @@ -30,47 +30,89 @@ export const ilks = { ] }), returns: [ - [totalEncumberedDebt, BigNumber], - [debtScalingFactor, fromRay], - [priceWithSafetyMargin, fromRay], - [debtCeiling, fromRad], - [urnDebtFloor, fromRad] + [TOTAL_ENCUMBERED_DEBT, BigNumber], + [DEBT_SCALING_FACTOR, fromRay], + [PRICE_WITH_SAFETY_MARGIN, fromRay], + [DEBT_CEILING, fromRad], + [URN_DEBT_FLOOR, fromRad] ] }; -export const totalDaiSupply = 'totalDaiSupply'; +export const TOTAL_DAI_SUPPLY = 'totalDaiSupply'; export const debt = { generate: () => ({ id: `VAT.debt()`, contractName: 'MCD_VAT', call: ['debt()(uint256)'] }), - returns: [[totalDaiSupply, MDAI.rad]] + returns: [[TOTAL_DAI_SUPPLY, MDAI.rad]] }; +export const PRICE_FEED_ADDRESS = 'priceFeedAddress'; +export const RAW_LIQUIDATION_RATIO = 'rawLiquidationRatio'; + +export const spotIlks = { + generate: ilkName => ({ + id: `MCD_SPOT.ilks(${ilkName})`, + contractName: 'MCD_SPOT', + call: ['ilks(bytes32)(address,uint256)', toHex(ilkName)] + }), + returns: [PRICE_FEED_ADDRESS, [RAW_LIQUIDATION_RATIO, fromRay]] +}; + +export const LIQUIDATION_RATIO = 'liquidationRatio'; +export const liquidationRatio = { + // The liquidation ratio value is the minimum dollar amount of collateral in + // terms of a single dollar unit amount of debt + // + // In plain english, it is the ratio of the dollar amount of ETH in terms of + // the dollar amount of dai + generate: ilkName => ({ + dependencies: () => [[RAW_LIQUIDATION_RATIO, ilkName]], + computed: liqRatio => + createCurrencyRatio( + createCurrency(`(${ilkName.split('-')[0]}/USD)`), + createCurrency(`(${MDAI.symbol}/USD)`) + )(liqRatio) + }) +}; + +export const RATIO_DAI_USD = 'ratioDaiUsd'; +export const spotPar = { + generate: () => ({ + id: 'MCD_SPOT.par()', + contractName: 'MCD_SPOT', + call: ['par()(uint256)'] + }), + returns: [[RATIO_DAI_USD, v => createCurrencyRatio(MDAI, USD)(fromRay(v))]] +}; + +export const ILK_PRICES = 'ilkPrices'; export const ilkPrices = { generate: ilkNames => ({ // Dynamically generated dependencies dependencies: () => [ - ['refPerDai'], + [RATIO_DAI_USD], ...ilkNames.reduce( (acc, ilk) => [ ...acc, - ['priceWithSafetyMargin', ilk], - ['liquidationRatio', ilk] + [PRICE_WITH_SAFETY_MARGIN, ilk], + [LIQUIDATION_RATIO, ilk] ], [] ) ], - computed: (refPerDai, ...results) => + computed: (ratioDaiUsd, ...results) => results.reduce((acc, r, i) => { if (i % 2 === 0) { - const currency = ETH; const priceWithSafetyMargin = results[i]; const liquidationRatio = results[i + 1]; + const currency = createCurrency( + liquidationRatio.numerator.symbol.substring(1, 4) + ); const ratio = createCurrencyRatio(USD, currency); const price = priceWithSafetyMargin - .times(refPerDai) + .times(ratioDaiUsd.toNumber()) .times(liquidationRatio.toNumber()); return [...acc, ratio(price)]; } @@ -79,38 +121,12 @@ export const ilkPrices = { }) }; -export const priceFeedAddress = 'priceFeedAddress'; -export const liquidationRatio = 'liquidationRatio'; -export const spotIlks = { - generate: ilkName => ({ - id: `MCD_SPOT.ilks(${ilkName})`, - contractName: 'MCD_SPOT', - call: ['ilks(bytes32)(address,uint256)', toHex(ilkName)] - }), - returns: [ - priceFeedAddress, - [ - liquidationRatio, - v => createCurrencyRatio(USD, MDAI)(BigNumber(fromRay(v))) - ] - ] -}; - -export const refPerDai = 'refPerDai'; -export const spotPar = { - generate: () => ({ - id: 'MCD_SPOT.par()', - contractName: 'MCD_SPOT', - call: ['par()(uint256)'] - }), - returns: [[refPerDai, fromRay]] -}; - export default { - ilks, + vatIlks, proxies, debt, spotIlks, spotPar, - ilkPrices + ilkPrices, + liquidationRatio }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 65db10b9f..be65310b1 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -1,5 +1,5 @@ import { mcdMaker, setupCollateral } from './helpers'; -import { ETH, BAT, MDAI } from '../src'; +import { ETH, BAT, MDAI, USD } from '../src'; import { toHex, fromRad, @@ -7,15 +7,6 @@ import { isBigNumber, isValidAddressString } from '../src/utils'; -import schemas, { - totalEncumberedDebt, - debtScalingFactor, - priceWithSafetyMargin, - debtCeiling, - urnDebtFloor, - proxyAddress, - totalDaiSupply -} from '../src/schema'; import { ServiceRoles } from '../src/constants'; import BigNumber from 'bignumber.js'; let mcall, @@ -28,6 +19,22 @@ let mcall, ethAInfo, batAInfo, vault; +import { createCurrencyRatio } from '@makerdao/currency'; + +import schemas, { + TOTAL_ENCUMBERED_DEBT, + DEBT_SCALING_FACTOR, + PRICE_WITH_SAFETY_MARGIN, + DEBT_CEILING, + URN_DEBT_FLOOR, + PROXY_ADDRESS, + TOTAL_DAI_SUPPLY, + PRICE_FEED_ADDRESS, + RAW_LIQUIDATION_RATIO, + RATIO_DAI_USD, + LIQUIDATION_RATIO, + ILK_PRICES +} from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -80,7 +87,7 @@ beforeAll(async () => { batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); -test(totalEncumberedDebt, async () => { +test(TOTAL_ENCUMBERED_DEBT, async () => { // TODO Define hardcoded rates for given ilks outside of the system and test // against those rather than data extracted from the chain const { rate: ethARate } = ethAInfo; @@ -89,8 +96,8 @@ test(totalEncumberedDebt, async () => { const ethADebtAmount = ETH_A_DEBT_AMOUNT._amount; const batADebtAmount = BAT_A_DEBT_AMOUNT._amount; - const ethAEncumberedDebt = await maker.latest(totalEncumberedDebt, 'ETH-A'); - const batAEncumberedDebt = await maker.latest(totalEncumberedDebt, 'BAT-A'); + const ethAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'ETH-A'); + const batAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'BAT-A'); expect(isBigNumber(ethAEncumberedDebt)).toEqual(true); expect(isBigNumber(batAEncumberedDebt)).toEqual(true); @@ -110,12 +117,18 @@ test(totalEncumberedDebt, async () => { ); }); -test(debtScalingFactor, async () => { +test(DEBT_SCALING_FACTOR, async () => { const { rate: ethARate } = ethAInfo; const { rate: batARate } = batAInfo; - const ethADebtScalingFactor = await maker.latest(debtScalingFactor, 'ETH-A'); - const batADebtScalingFactor = await maker.latest(debtScalingFactor, 'BAT-A'); + const ethADebtScalingFactor = await maker.latest( + DEBT_SCALING_FACTOR, + 'ETH-A' + ); + const batADebtScalingFactor = await maker.latest( + DEBT_SCALING_FACTOR, + 'BAT-A' + ); expect(isBigNumber(ethADebtScalingFactor)).toEqual(true); expect(isBigNumber(batADebtScalingFactor)).toEqual(true); @@ -124,13 +137,13 @@ test(debtScalingFactor, async () => { expect(batADebtScalingFactor).toEqual(fromRay(batARate)); }); -test(priceWithSafetyMargin, async () => { +test(PRICE_WITH_SAFETY_MARGIN, async () => { const ethAPriceWithSafetyMargin = await maker.latest( - priceWithSafetyMargin, + PRICE_WITH_SAFETY_MARGIN, 'ETH-A' ); const batAPriceWithSafetyMargin = await maker.latest( - priceWithSafetyMargin, + PRICE_WITH_SAFETY_MARGIN, 'BAT-A' ); @@ -146,9 +159,9 @@ test(priceWithSafetyMargin, async () => { expect(batAPriceWithSafetyMargin).toEqual(batACalculatedMargin); }); -test(debtCeiling, async () => { - const ethADebtCeiling = await maker.latest(debtCeiling, 'ETH-A'); - const batADebtCeiling = await maker.latest(debtCeiling, 'BAT-A'); +test(DEBT_CEILING, async () => { + const ethADebtCeiling = await maker.latest(DEBT_CEILING, 'ETH-A'); + const batADebtCeiling = await maker.latest(DEBT_CEILING, 'BAT-A'); expect(isBigNumber(ethADebtCeiling)).toEqual(true); expect(isBigNumber(batADebtCeiling)).toEqual(true); @@ -157,9 +170,9 @@ test(debtCeiling, async () => { expect(batADebtCeiling).toEqual(BigNumber('5000')); }); -test(urnDebtFloor, async () => { - const ethAUrnDebtFloor = await maker.latest(urnDebtFloor, 'ETH-A'); - const batAUrnDebtFloor = await maker.latest(urnDebtFloor, 'BAT-A'); +test(URN_DEBT_FLOOR, async () => { + const ethAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'ETH-A'); + const batAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'BAT-A'); expect(isBigNumber(ethAUrnDebtFloor)).toEqual(true); expect(isBigNumber(batAUrnDebtFloor)).toEqual(true); @@ -168,7 +181,7 @@ test(urnDebtFloor, async () => { expect(batAUrnDebtFloor).toEqual(BigNumber('0')); }); -test(totalDaiSupply, async () => { +test(TOTAL_DAI_SUPPLY, async () => { const { Art: ethAArt, rate: ethARate } = ethAInfo; const { Art: batAArt, rate: batARate } = batAInfo; @@ -176,20 +189,67 @@ test(totalDaiSupply, async () => { const batADaiGenerated = MDAI.rad(BigNumber(batAArt).times(batARate)); const sumOfDaiGeneratedFromIlks = ethADaiGenerated.plus(batADaiGenerated); - const totalDaiAmount = await maker.latest(totalDaiSupply); + const totalDaiAmount = await maker.latest(TOTAL_DAI_SUPPLY); expect(totalDaiAmount.symbol).toEqual('MDAI'); expect(totalDaiAmount.isEqual(sumOfDaiGeneratedFromIlks)).toEqual(true); }); -test(proxyAddress, async () => { - const proxy = await maker.latest(proxyAddress, address); - expect(isValidAddressString(proxy)).toEqual(true); - expect(proxy).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); +test(PROXY_ADDRESS, async () => { + const proxyAddress = await maker.latest(PROXY_ADDRESS, address); + + expect(isValidAddressString(proxyAddress)).toEqual(true); + expect(proxyAddress).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); +}); + +test(PRICE_FEED_ADDRESS, async () => { + const ethAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'ETH-A'); + const batAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'BAT-A'); + + expect(isValidAddressString(ethAPriceFeedAddress)).toEqual(true); + expect(isValidAddressString(batAPriceFeedAddress)).toEqual(true); + + expect(ethAPriceFeedAddress).toEqual( + '0xaF14E6E871f81BB92f151AfF1bB80936Aa06C6D6' + ); + expect(batAPriceFeedAddress).toEqual( + '0x7eD0d0255153050e9623FfECEeE49a1020503CA3' + ); +}); + +test(RAW_LIQUIDATION_RATIO, async () => { + const ethARawLiquidationRatio = await maker.latest( + RAW_LIQUIDATION_RATIO, + 'ETH-A' + ); + const batARawLiquidationRatio = await maker.latest( + RAW_LIQUIDATION_RATIO, + 'BAT-A' + ); + + expect(ethARawLiquidationRatio).toEqual(BigNumber('1.5')); + expect(batARawLiquidationRatio).toEqual(BigNumber('2.0')); +}); + +test(LIQUIDATION_RATIO, async () => { + const ethALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'ETH-A'); + const batALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'BAT-A'); + + expect(ethALiquidationRatio.symbol).toEqual('(ETH/USD)/(MDAI/USD)'); + expect(batALiquidationRatio.symbol).toEqual('(BAT/USD)/(MDAI/USD)'); + + expect(ethALiquidationRatio.toNumber()).toEqual(1.5); + expect(batALiquidationRatio.toNumber()).toEqual(2.0); +}); + +test(RATIO_DAI_USD, async () => { + const ratio = await maker.latest(RATIO_DAI_USD); + expect(ratio.symbol).toEqual('MDAI/USD'); + expect(ratio.toNumber()).toEqual(1); }); -test.skip('ilkPrices', async () => { - const [ethAPrice, ethBPrice, batAPrice] = await maker.latest('ilkPrices', [ +test(ILK_PRICES, async () => { + const [ethAPrice, ethBPrice, batAPrice] = await maker.latest(ILK_PRICES, [ 'ETH-A', 'ETH-B', 'BAT-A' From fbbae6be90077e84cfc297c7129bc4ddaf2c40de Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 10:28:09 +0800 Subject: [PATCH 017/117] Use local dai package in monorepo for mcdMaker helper --- packages/dai-plugin-mcd/test/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/test/helpers.js b/packages/dai-plugin-mcd/test/helpers.js index 87406d2a0..a312497ed 100644 --- a/packages/dai-plugin-mcd/test/helpers.js +++ b/packages/dai-plugin-mcd/test/helpers.js @@ -1,6 +1,6 @@ import assert from 'assert'; import { createCurrencyRatio } from '@makerdao/currency'; -import Maker from '@makerdao/dai'; +import Maker from '../../dai/src'; import McdPlugin, { ETH, USD, GNT } from '../src'; import { ServiceRoles } from '../src/constants'; import { stringToBytes } from '../src/utils'; From 4e8df7323f5426137d04702535629dc8371cb53b Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 11:14:38 +0800 Subject: [PATCH 018/117] Compose ilkPrices computed observable from ilkPrice --- packages/dai-plugin-mcd/src/schema.js | 48 ++++++++++----------- packages/dai-plugin-mcd/test/schema.spec.js | 16 ++++--- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 459885015..c04812275 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,3 +1,4 @@ +/* eslint-disable */ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; import { toHex, fromWei, fromRay, fromRad } from './utils'; import BigNumber from 'bignumber.js'; @@ -87,37 +88,33 @@ export const spotPar = { returns: [[RATIO_DAI_USD, v => createCurrencyRatio(MDAI, USD)(fromRay(v))]] }; +export const ILK_PRICE = 'ilkPrice'; +export const ilkPrice = { + generate: ilkName => ({ + dependencies: [ + [RATIO_DAI_USD], + [PRICE_WITH_SAFETY_MARGIN, ilkName], + [LIQUIDATION_RATIO, ilkName] + ], + computed: (ratioDaiUsd, priceWithSafetyMargin, liquidationRatio) => { + const currency = createCurrency(liquidationRatio.numerator.symbol.substring(1, 4)); + const ratio = createCurrencyRatio(USD, currency); + const price = priceWithSafetyMargin + .times(ratioDaiUsd.toNumber()) + .times(liquidationRatio.toNumber()); + return ratio(price); + } + }) +}; + export const ILK_PRICES = 'ilkPrices'; export const ilkPrices = { generate: ilkNames => ({ - // Dynamically generated dependencies dependencies: () => [ [RATIO_DAI_USD], - ...ilkNames.reduce( - (acc, ilk) => [ - ...acc, - [PRICE_WITH_SAFETY_MARGIN, ilk], - [LIQUIDATION_RATIO, ilk] - ], - [] - ) + ...ilkNames.map(ilkName => [ILK_PRICE, ilkName]) ], - computed: (ratioDaiUsd, ...results) => - results.reduce((acc, r, i) => { - if (i % 2 === 0) { - const priceWithSafetyMargin = results[i]; - const liquidationRatio = results[i + 1]; - const currency = createCurrency( - liquidationRatio.numerator.symbol.substring(1, 4) - ); - const ratio = createCurrencyRatio(USD, currency); - const price = priceWithSafetyMargin - .times(ratioDaiUsd.toNumber()) - .times(liquidationRatio.toNumber()); - return [...acc, ratio(price)]; - } - return acc; - }, []) + computed: (_, ...prices) => prices }) }; @@ -148,6 +145,7 @@ export default { debt, spotIlks, spotPar, + ilkPrice, ilkPrices, liquidationRatio, vatGem, diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 3f83dd299..2594ef70b 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -1,3 +1,4 @@ +/* eslint-disable */ import { mcdMaker, setupCollateral } from './helpers'; import { ETH, BAT, MDAI, USD } from '../src'; import { @@ -35,6 +36,7 @@ import schemas, { RAW_LIQUIDATION_RATIO, RATIO_DAI_USD, LIQUIDATION_RATIO, + ILK_PRICE, ILK_PRICES, UNLOCKED_COLLATERAL, URN_INK, @@ -215,12 +217,8 @@ test(PRICE_FEED_ADDRESS, async () => { expect(isValidAddressString(ethAPriceFeedAddress)).toEqual(true); expect(isValidAddressString(batAPriceFeedAddress)).toEqual(true); - expect(ethAPriceFeedAddress).toEqual( - '0xaF14E6E871f81BB92f151AfF1bB80936Aa06C6D6' - ); - expect(batAPriceFeedAddress).toEqual( - '0x7eD0d0255153050e9623FfECEeE49a1020503CA3' - ); + expect(ethAPriceFeedAddress).toEqual('0xaF14E6E871f81BB92f151AfF1bB80936Aa06C6D6'); + expect(batAPriceFeedAddress).toEqual('0x7eD0d0255153050e9623FfECEeE49a1020503CA3'); }); test(RAW_LIQUIDATION_RATIO, async () => { @@ -254,6 +252,12 @@ test(RATIO_DAI_USD, async () => { expect(ratio.toNumber()).toEqual(1); }); +test(ILK_PRICE, async () => { + const ethAPrice = await maker.latest(ILK_PRICE, 'ETH-A'); + expect(ethAPrice.toNumber()).toEqual(180); + expect(ethAPrice.symbol).toEqual('USD/ETH'); +}); + test(ILK_PRICES, async () => { const [ethAPrice, ethBPrice, batAPrice] = await maker.latest(ILK_PRICES, [ 'ETH-A', From b70726bf8b0b434a324ab9ac6d7052c0edde3806 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 17:35:56 +0800 Subject: [PATCH 019/117] Return Currency object from debtCeiling base observable --- packages/dai-plugin-mcd/src/schema.js | 2 +- packages/dai-plugin-mcd/src/utils.js | 5 +++++ packages/dai-plugin-mcd/test/schema.spec.js | 9 +++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index c04812275..d902a6d0c 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -34,7 +34,7 @@ export const vatIlks = { [TOTAL_ENCUMBERED_DEBT, BigNumber], [DEBT_SCALING_FACTOR, fromRay], [PRICE_WITH_SAFETY_MARGIN, fromRay], - [DEBT_CEILING, fromRad], + [DEBT_CEILING, MDAI.rad], [URN_DEBT_FLOOR, fromRad] ] }; diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index ed86321b4..3d9ff5b5f 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -1,6 +1,7 @@ import assert from 'assert'; import BigNumber from 'bignumber.js'; import Web3 from 'web3'; +import { Currency } from '@makerdao/currency'; const web3Utils = new Web3().utils; export function stringToBytes(str) { @@ -63,5 +64,9 @@ export function isBigNumber(value) { return BigNumber.isBigNumber(value); } +export function isCurrency(value) { + return value instanceof Currency; +} + export const isValidAddressString = addressString => /^0x([A-Fa-f0-9]{40})$/.test(addressString); diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 2594ef70b..9e6b35318 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -7,6 +7,7 @@ import { fromWei, fromRay, isBigNumber, + isCurrency, isValidAddressString } from '../src/utils'; import { ServiceRoles } from '../src/constants'; @@ -171,11 +172,11 @@ test(DEBT_CEILING, async () => { const ethADebtCeiling = await maker.latest(DEBT_CEILING, 'ETH-A'); const batADebtCeiling = await maker.latest(DEBT_CEILING, 'BAT-A'); - expect(isBigNumber(ethADebtCeiling)).toEqual(true); - expect(isBigNumber(batADebtCeiling)).toEqual(true); + expect(isCurrency(ethADebtCeiling)).toEqual(true); + expect(isCurrency(batADebtCeiling)).toEqual(true); - expect(ethADebtCeiling).toEqual(BigNumber('100000')); - expect(batADebtCeiling).toEqual(BigNumber('5000')); + expect(ethADebtCeiling.isEqual(MDAI(100000))).toEqual(true); + expect(batADebtCeiling.isEqual(MDAI(5000))).toEqual(true); }); test(URN_DEBT_FLOOR, async () => { From 5f0838666ae751d9dcb98c7c153b5e344f1e5959 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 17:38:04 +0800 Subject: [PATCH 020/117] Add vaultUrn schema --- packages/dai-plugin-mcd/src/schema.js | 15 ++++++++++++++- packages/dai-plugin-mcd/test/schema.spec.js | 10 +++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index d902a6d0c..a2f453e02 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -139,6 +139,18 @@ export const urnState = { returns: [[URN_INK, fromWei], [URN_ART, fromWei]] }; +export const VAULT_URN = 'vaultUrn'; +export const vaultUrn = { + generate: id => ({ + id: `CDP_MANAGER.urns(${id})`, + contractName: 'CDP_MANAGER', + call: ['urns(uint256)(address)', parseInt(id)] + }), + returns: [ + VAULT_URN + ] +}; + export default { vatIlks, proxies, @@ -149,5 +161,6 @@ export default { ilkPrices, liquidationRatio, vatGem, - urnState + urnState, + vaultUrn }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 9e6b35318..f55125ee6 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -41,7 +41,8 @@ import schemas, { ILK_PRICES, UNLOCKED_COLLATERAL, URN_INK, - URN_ART + URN_ART, + VAULT_URN } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -302,3 +303,10 @@ test(URN_ART, async () => { expect(col.toNumber()).toBeCloseTo(expected.toNumber()); }); + +test(VAULT_URN, async () => { + const cdpId = 1; + const expected = '0xe8c8C8A68b9dE5cC65aCBF20f4eCc802d71a4EBE'; + const urn = await maker.latest(VAULT_URN, cdpId); + expect(urn).toEqual(expected); +}); From c7dfddb3c2c5b80e5cb5495934d78c8e5aeffcff Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 17:48:23 +0800 Subject: [PATCH 021/117] Add vaultIlk schema --- packages/dai-plugin-mcd/src/schema.js | 15 +++++++++++++-- packages/dai-plugin-mcd/test/schema.spec.js | 10 +++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index a2f453e02..a4b4f85dd 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,6 +1,6 @@ /* eslint-disable */ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; -import { toHex, fromWei, fromRay, fromRad } from './utils'; +import { toHex, bytesToString, fromWei, fromWad, fromRay, fromRad } from './utils'; import BigNumber from 'bignumber.js'; import { USD, ETH, BAT, MDAI } from '..'; @@ -151,6 +151,16 @@ export const vaultUrn = { ] }; +export const VAULT_ILK = 'vaultIlk'; +export const vaultIlk = { + generate: id => ({ + id: `CDP_MANAGER.ilks(${id})`, + contractName: 'CDP_MANAGER', + call: ['ilks(uint256)(bytes32)', parseInt(id)] + }), + returns: [[VAULT_ILK, bytesToString]] +}; + export default { vatIlks, proxies, @@ -162,5 +172,6 @@ export default { liquidationRatio, vatGem, urnState, - vaultUrn + vaultUrn, + vaultIlk }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index f55125ee6..6fb2b0c71 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -42,7 +42,8 @@ import schemas, { UNLOCKED_COLLATERAL, URN_INK, URN_ART, - VAULT_URN + VAULT_URN, + VAULT_ILK } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -310,3 +311,10 @@ test(VAULT_URN, async () => { const urn = await maker.latest(VAULT_URN, cdpId); expect(urn).toEqual(expected); }); + +test(VAULT_ILK, async () => { + const cdpId = 1; + const expected = 'ETH-A'; + const ilk = await maker.latest(VAULT_ILK, cdpId); + expect(ilk).toEqual(expected); +}); From 9f8058ac7e5183cb268262587c0428c3f408d1a1 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 18:00:30 +0800 Subject: [PATCH 022/117] Simplify ilkPrices schema --- packages/dai-plugin-mcd/src/schema.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index a4b4f85dd..ce876b6df 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -111,10 +111,9 @@ export const ILK_PRICES = 'ilkPrices'; export const ilkPrices = { generate: ilkNames => ({ dependencies: () => [ - [RATIO_DAI_USD], ...ilkNames.map(ilkName => [ILK_PRICE, ilkName]) ], - computed: (_, ...prices) => prices + computed: (...prices) => prices }) }; From cc978badd53c4c9bb29cdfe5b77f41e0777b2ead Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 18:01:22 +0800 Subject: [PATCH 023/117] Add vaultIlkAndUrn schema --- packages/dai-plugin-mcd/src/schema.js | 14 +++++++++++++- packages/dai-plugin-mcd/test/schema.spec.js | 12 +++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index ce876b6df..363c4b800 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -160,6 +160,17 @@ export const vaultIlk = { returns: [[VAULT_ILK, bytesToString]] }; +export const VAULT_ILK_AND_URN = 'vaultIlkAndUrn'; +export const vaultIlkAndUrn = { + generate: id => ({ + dependencies: [ + [VAULT_ILK, id], + [VAULT_URN, id] + ], + computed: (ilk, urn) => [ilk, urn] + }) +}; + export default { vatIlks, proxies, @@ -172,5 +183,6 @@ export default { vatGem, urnState, vaultUrn, - vaultIlk + vaultIlk, + vaultIlkAndUrn }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 6fb2b0c71..6adada3e9 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -43,7 +43,8 @@ import schemas, { URN_INK, URN_ART, VAULT_URN, - VAULT_ILK + VAULT_ILK, + VAULT_ILK_AND_URN } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -318,3 +319,12 @@ test(VAULT_ILK, async () => { const ilk = await maker.latest(VAULT_ILK, cdpId); expect(ilk).toEqual(expected); }); + +test(VAULT_ILK_AND_URN, async () => { + const cdpId = 1; + const expectedIlk = 'ETH-A'; + const expectedUrn = '0xe8c8C8A68b9dE5cC65aCBF20f4eCc802d71a4EBE'; + const [ilk, urn] = await maker.latest(VAULT_ILK_AND_URN, cdpId); + expect(ilk).toEqual(expectedIlk); + expect(urn).toEqual(expectedUrn); +}); From eeceb9800143b30074b0ab91ff1156339a3ffcd1 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 19:02:45 +0800 Subject: [PATCH 024/117] Pass watchObservable func to dependencies generator Pass watchObservable func to dependencies generator func for more dynamic dependency generation --- packages/dai/src/eth/MulticallService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index bd847fa80..13bdee428 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -163,7 +163,7 @@ export default class MulticallService extends PublicService { // Handle dynamically generated dependencies const dependencies = typeof generatedSchema.dependencies === 'function' - ? generatedSchema.dependencies() + ? generatedSchema.dependencies(this.watchObservable.bind(this)) : generatedSchema.dependencies; const dependencySubs = dependencies.map(dep => { From d9600660ecff3c639c2fe6eead95b8df29a4d45e Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 19:11:54 +0800 Subject: [PATCH 025/117] Add vaultById computed observable --- packages/dai-plugin-mcd/src/schema.js | 32 ++++++++++++++++++++- packages/dai-plugin-mcd/test/schema.spec.js | 17 ++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 363c4b800..dc31d7e29 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -3,6 +3,7 @@ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; import { toHex, bytesToString, fromWei, fromWad, fromRay, fromRad } from './utils'; import BigNumber from 'bignumber.js'; import { USD, ETH, BAT, MDAI } from '..'; +import { first } from 'rxjs/operators'; export const PROXY_ADDRESS = 'proxyAddress'; @@ -171,6 +172,33 @@ export const vaultIlkAndUrn = { }) }; +export const URN_INK_AND_ART = 'urnInkAndArt'; +export const urnInkAndArt = { + generate: (ilk, urn) => ({ + dependencies: [ + [URN_INK, ilk, urn], + [URN_ART, ilk, urn] + ], + computed: (ink, art) => [ink, art] + }) +}; + +export const VAULT_BY_ID = 'vaultById'; +export const vaultById = { + generate: id => ({ + dependencies: (watch) => ([ + [() => new Promise(resolve => { + watch(VAULT_ILK_AND_URN, id).pipe(first()).toPromise().then(([ilk, urn]) => { + watch(URN_INK_AND_ART, ilk, urn).pipe(first()).toPromise().then(([ink, art]) => { + resolve([ilk, urn, ink, art]) + }) + }); + })] + ]), + computed: ([ilk, urn, ink, art]) => ({ ilk, urn, ink, art }) + }) +}; + export default { vatIlks, proxies, @@ -184,5 +212,7 @@ export default { urnState, vaultUrn, vaultIlk, - vaultIlkAndUrn + vaultIlkAndUrn, + urnInkAndArt, + vaultById }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 6adada3e9..57bcc59fc 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -44,7 +44,8 @@ import schemas, { URN_ART, VAULT_URN, VAULT_ILK, - VAULT_ILK_AND_URN + VAULT_ILK_AND_URN, + VAULT_BY_ID } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -328,3 +329,17 @@ test(VAULT_ILK_AND_URN, async () => { expect(ilk).toEqual(expectedIlk); expect(urn).toEqual(expectedUrn); }); + +test(VAULT_BY_ID, async () => { + const cdpId = 1; + const expectedIlk = 'ETH-A'; + const expectedUrn = '0xe8c8C8A68b9dE5cC65aCBF20f4eCc802d71a4EBE'; + const expectedInk = fromWei(1000000000000000000); + const expectedArt = fromWei(995000000000000000); + const { ilk, urn, ink, art } = await maker.latest(VAULT_BY_ID, cdpId); + + expect(ilk).toEqual(expectedIlk); + expect(urn).toEqual(expectedUrn); + expect(ink).toEqual(expectedInk); + expect(art.toNumber()).toBeCloseTo(expectedArt.toNumber()); +}); From 6fdd69a09977b9f63c1431474f559e53121bd408 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 17 Jan 2020 19:19:09 +0800 Subject: [PATCH 026/117] Add type definitions for multicall --- .../dai-plugin-mcd/types/multicall/index.d.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 packages/dai-plugin-mcd/types/multicall/index.d.ts diff --git a/packages/dai-plugin-mcd/types/multicall/index.d.ts b/packages/dai-plugin-mcd/types/multicall/index.d.ts new file mode 100644 index 000000000..3c0af2b2c --- /dev/null +++ b/packages/dai-plugin-mcd/types/multicall/index.d.ts @@ -0,0 +1,60 @@ +interface Currency {} +interface BigNumber {} + +interface WatchInterface { + + /** Watch the total encumbered debt of a collateral type + * @param ilkName String uniquely identifying an ilk + */ + totalEncumberedDebt( + ilkName: string + ): Currency; + + /** + * @param ilkName String uniquely identifying an ilk + */ + debtScalingFactor( + ilkName: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + */ + priceWithSafetyMargin( + ilkName: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + */ + debtCeiling( + ilkName: string + ): Currency; + + /** + * @param ilkName String uniquely identifying an ilk + */ + urnDebtFloor( + ilkName: string + ): BigNumber; + + /** Watch the price of an ilk + * @param ilkName String uniquely identifying an ilk + */ + ilkPrice( + ilkName: string + ): Currency; + + /** Watch the prices of a list of ilks + * @param ilkName Array of strings uniquely identifying ilks + */ + ilkPrices( + [ilkNames]: string + ): Currency; +} + +declare var watch: WatchInterface; + +declare module 'watch' { + export = watch; +} From e1c5d1598e312e60e45a6b0864fdac74c72bd4fb Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 11:53:26 +0000 Subject: [PATCH 027/117] Update tests --- packages/dai-plugin-mcd/test/schema.spec.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 57bcc59fc..cfd70f14f 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -212,7 +212,7 @@ test(PROXY_ADDRESS, async () => { const proxyAddress = await maker.latest(PROXY_ADDRESS, address); expect(isValidAddressString(proxyAddress)).toEqual(true); - expect(proxyAddress).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); + expect(proxyAddress).toEqual('0x570074CCb147ea3dE2E23fB038D4d78324278886'); }); test(PRICE_FEED_ADDRESS, async () => { @@ -222,8 +222,12 @@ test(PRICE_FEED_ADDRESS, async () => { expect(isValidAddressString(ethAPriceFeedAddress)).toEqual(true); expect(isValidAddressString(batAPriceFeedAddress)).toEqual(true); - expect(ethAPriceFeedAddress).toEqual('0xaF14E6E871f81BB92f151AfF1bB80936Aa06C6D6'); - expect(batAPriceFeedAddress).toEqual('0x7eD0d0255153050e9623FfECEeE49a1020503CA3'); + expect(ethAPriceFeedAddress).toEqual( + '0xb0ae8c0856259C6fe000F8e2C14507E5FC167D48' + ); + expect(batAPriceFeedAddress).toEqual( + '0x80f178c7b47cb635Ceb12aBB891338744e98365C' + ); }); test(RAW_LIQUIDATION_RATIO, async () => { @@ -309,7 +313,7 @@ test(URN_ART, async () => { test(VAULT_URN, async () => { const cdpId = 1; - const expected = '0xe8c8C8A68b9dE5cC65aCBF20f4eCc802d71a4EBE'; + const expected = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const urn = await maker.latest(VAULT_URN, cdpId); expect(urn).toEqual(expected); }); @@ -324,7 +328,7 @@ test(VAULT_ILK, async () => { test(VAULT_ILK_AND_URN, async () => { const cdpId = 1; const expectedIlk = 'ETH-A'; - const expectedUrn = '0xe8c8C8A68b9dE5cC65aCBF20f4eCc802d71a4EBE'; + const expectedUrn = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const [ilk, urn] = await maker.latest(VAULT_ILK_AND_URN, cdpId); expect(ilk).toEqual(expectedIlk); expect(urn).toEqual(expectedUrn); @@ -333,7 +337,7 @@ test(VAULT_ILK_AND_URN, async () => { test(VAULT_BY_ID, async () => { const cdpId = 1; const expectedIlk = 'ETH-A'; - const expectedUrn = '0xe8c8C8A68b9dE5cC65aCBF20f4eCc802d71a4EBE'; + const expectedUrn = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const expectedInk = fromWei(1000000000000000000); const expectedArt = fromWei(995000000000000000); const { ilk, urn, ink, art } = await maker.latest(VAULT_BY_ID, cdpId); From 64489508a59e49841d0356cf43781ceba1ac3498 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 17 Jan 2020 13:11:04 +0100 Subject: [PATCH 028/117] CDP-733 schemas for duty and rho --- packages/dai-plugin-mcd/src/schema.js | 16 ++++++- packages/dai-plugin-mcd/test/schema.spec.js | 46 +++++++++++++-------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 363c4b800..9e80cfc2b 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -3,6 +3,7 @@ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; import { toHex, bytesToString, fromWei, fromWad, fromRay, fromRad } from './utils'; import BigNumber from 'bignumber.js'; import { USD, ETH, BAT, MDAI } from '..'; +import { annualStabilityFee } from './math'; export const PROXY_ADDRESS = 'proxyAddress'; @@ -171,6 +172,18 @@ export const vaultIlkAndUrn = { }) }; +export const DUTY = 'duty'; +export const RHO = 'rho'; +export const jugInfo = { + generate: (name) => ({ + id: `MCD_JUG.ilks(${name})`, + contractName: 'MCD_JUG', + call: ['ilks(bytes32)(uint256,uint48)', toHex(name)], + }), + returns: [[DUTY, annualStabilityFee], [RHO]] +}; + + export default { vatIlks, proxies, @@ -184,5 +197,6 @@ export default { urnState, vaultUrn, vaultIlk, - vaultIlkAndUrn + vaultIlkAndUrn, + jugInfo }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 6adada3e9..99c8be219 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -23,7 +23,6 @@ let mcall, ethAInfo, batAInfo, vault; -import { createCurrencyRatio } from '@makerdao/currency'; import schemas, { TOTAL_ENCUMBERED_DEBT, @@ -44,7 +43,9 @@ import schemas, { URN_ART, VAULT_URN, VAULT_ILK, - VAULT_ILK_AND_URN + VAULT_ILK_AND_URN, + DUTY, + RHO } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -99,6 +100,13 @@ beforeAll(async () => { batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); +test(PROXY_ADDRESS, async () => { + const proxyAddress = await maker.latest(PROXY_ADDRESS, address); + + expect(isValidAddressString(proxyAddress)).toEqual(true); + expect(proxyAddress).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); +}); + test(TOTAL_ENCUMBERED_DEBT, async () => { // TODO Define hardcoded rates for given ilks outside of the system and test // against those rather than data extracted from the chain @@ -207,13 +215,6 @@ test(TOTAL_DAI_SUPPLY, async () => { expect(totalDaiAmount.isEqual(sumOfDaiGeneratedFromIlks)).toEqual(true); }); -test(PROXY_ADDRESS, async () => { - const proxyAddress = await maker.latest(PROXY_ADDRESS, address); - - expect(isValidAddressString(proxyAddress)).toEqual(true); - expect(proxyAddress).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); -}); - test(PRICE_FEED_ADDRESS, async () => { const ethAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'ETH-A'); const batAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'BAT-A'); @@ -280,7 +281,7 @@ test(ILK_PRICES, async () => { test(UNLOCKED_COLLATERAL, async () => { const cdpId = 1; - const expected = 0; + const expected = 0; const col = await maker.latest( UNLOCKED_COLLATERAL, 'ETH-A', @@ -293,17 +294,15 @@ test(UNLOCKED_COLLATERAL, async () => { test(URN_INK, async () => { const cdpId = 1; const expected = fromWei(1000000000000000000); - const col = await maker.latest(URN_INK, 'ETH-A', await cdpMgr.getUrn(cdpId)); - - expect(col).toEqual(expected); + const ink = await maker.latest(URN_INK, 'ETH-A', await cdpMgr.getUrn(cdpId)); + expect(ink).toEqual(expected); }); test(URN_ART, async () => { const cdpId = 1; - const expected = fromWei(995000000000000000); - const col = await maker.latest(URN_ART, 'ETH-A', await cdpMgr.getUrn(cdpId)); - - expect(col.toNumber()).toBeCloseTo(expected.toNumber()); + const expected = fromWei(995000000000000000); + const art = await maker.latest(URN_ART, 'ETH-A', await cdpMgr.getUrn(cdpId)); + expect(art.toNumber()).toBeCloseTo(expected.toNumber()); }); test(VAULT_URN, async () => { @@ -328,3 +327,16 @@ test(VAULT_ILK_AND_URN, async () => { expect(ilk).toEqual(expectedIlk); expect(urn).toEqual(expectedUrn); }); + +test(DUTY, async () => { + const expected = 0.04999999999989363; + const duty = await maker.latest(DUTY, 'ETH-A'); + expect((duty)).toEqual(expected); +}); + +test(RHO, async () => { + var timestamp = Math.round((new Date()).getTime() / 1000); + const rho = await maker.latest(RHO, 'ETH-A'); +// RHO is called in the beforeAll block + expect((timestamp-rho)).toBeLessThanOrEqual(10); +}); From d85e473a4dbfe4f505528d0d92b8c5974052fc34 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 12:17:43 +0000 Subject: [PATCH 029/117] Correct currency object casting which wasn't building properly --- packages/dai-plugin-mcd/src/schema.js | 60 +++++++++++++++------------ 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index dc31d7e29..3f8b2dd59 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -1,6 +1,13 @@ /* eslint-disable */ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; -import { toHex, bytesToString, fromWei, fromWad, fromRay, fromRad } from './utils'; +import { + toHex, + bytesToString, + fromWei, + fromWad, + fromRay, + fromRad +} from './utils'; import BigNumber from 'bignumber.js'; import { USD, ETH, BAT, MDAI } from '..'; import { first } from 'rxjs/operators'; @@ -35,7 +42,7 @@ export const vatIlks = { [TOTAL_ENCUMBERED_DEBT, BigNumber], [DEBT_SCALING_FACTOR, fromRay], [PRICE_WITH_SAFETY_MARGIN, fromRay], - [DEBT_CEILING, MDAI.rad], + [DEBT_CEILING, v => MDAI(v, 'rad')], [URN_DEBT_FLOOR, fromRad] ] }; @@ -47,7 +54,7 @@ export const debt = { contractName: 'MCD_VAT', call: ['debt()(uint256)'] }), - returns: [[TOTAL_DAI_SUPPLY, MDAI.rad]] + returns: [[TOTAL_DAI_SUPPLY, v => MDAI(v, 'rad')]] }; export const PRICE_FEED_ADDRESS = 'priceFeedAddress'; @@ -98,7 +105,9 @@ export const ilkPrice = { [LIQUIDATION_RATIO, ilkName] ], computed: (ratioDaiUsd, priceWithSafetyMargin, liquidationRatio) => { - const currency = createCurrency(liquidationRatio.numerator.symbol.substring(1, 4)); + const currency = createCurrency( + liquidationRatio.numerator.symbol.substring(1, 4) + ); const ratio = createCurrencyRatio(USD, currency); const price = priceWithSafetyMargin .times(ratioDaiUsd.toNumber()) @@ -111,9 +120,7 @@ export const ilkPrice = { export const ILK_PRICES = 'ilkPrices'; export const ilkPrices = { generate: ilkNames => ({ - dependencies: () => [ - ...ilkNames.map(ilkName => [ILK_PRICE, ilkName]) - ], + dependencies: () => [...ilkNames.map(ilkName => [ILK_PRICE, ilkName])], computed: (...prices) => prices }) }; @@ -146,9 +153,7 @@ export const vaultUrn = { contractName: 'CDP_MANAGER', call: ['urns(uint256)(address)', parseInt(id)] }), - returns: [ - VAULT_URN - ] + returns: [VAULT_URN] }; export const VAULT_ILK = 'vaultIlk'; @@ -164,10 +169,7 @@ export const vaultIlk = { export const VAULT_ILK_AND_URN = 'vaultIlkAndUrn'; export const vaultIlkAndUrn = { generate: id => ({ - dependencies: [ - [VAULT_ILK, id], - [VAULT_URN, id] - ], + dependencies: [[VAULT_ILK, id], [VAULT_URN, id]], computed: (ilk, urn) => [ilk, urn] }) }; @@ -175,10 +177,7 @@ export const vaultIlkAndUrn = { export const URN_INK_AND_ART = 'urnInkAndArt'; export const urnInkAndArt = { generate: (ilk, urn) => ({ - dependencies: [ - [URN_INK, ilk, urn], - [URN_ART, ilk, urn] - ], + dependencies: [[URN_INK, ilk, urn], [URN_ART, ilk, urn]], computed: (ink, art) => [ink, art] }) }; @@ -186,15 +185,24 @@ export const urnInkAndArt = { export const VAULT_BY_ID = 'vaultById'; export const vaultById = { generate: id => ({ - dependencies: (watch) => ([ - [() => new Promise(resolve => { - watch(VAULT_ILK_AND_URN, id).pipe(first()).toPromise().then(([ilk, urn]) => { - watch(URN_INK_AND_ART, ilk, urn).pipe(first()).toPromise().then(([ink, art]) => { - resolve([ilk, urn, ink, art]) + dependencies: watch => [ + [ + () => + new Promise(resolve => { + watch(VAULT_ILK_AND_URN, id) + .pipe(first()) + .toPromise() + .then(([ilk, urn]) => { + watch(URN_INK_AND_ART, ilk, urn) + .pipe(first()) + .toPromise() + .then(([ink, art]) => { + resolve([ilk, urn, ink, art]); + }); + }); }) - }); - })] - ]), + ] + ], computed: ([ilk, urn, ink, art]) => ({ ilk, urn, ink, art }) }) }; From d5402d61a057a2ee9e5de063189263b4ad011b35 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 17 Jan 2020 13:28:32 +0100 Subject: [PATCH 030/117] CDP-733 schemas for duty and rho --- packages/dai-plugin-governance/src/utils/helpers.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/dai-plugin-governance/src/utils/helpers.js b/packages/dai-plugin-governance/src/utils/helpers.js index a43c9e484..59ad19d99 100644 --- a/packages/dai-plugin-governance/src/utils/helpers.js +++ b/packages/dai-plugin-governance/src/utils/helpers.js @@ -1,10 +1,5 @@ import { createGetCurrency } from '@makerdao/currency'; -import { - MKR, - STAGING_MAINNET_URL, - KOVAN_URL, - MAINNET_URL -} from './constants'; +import { MKR, STAGING_MAINNET_URL, KOVAN_URL, MAINNET_URL } from './constants'; /** * @desc get network name From 9c0f88746b24e0bcd87e55e7491527fb1d57b08e Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 12:41:49 +0000 Subject: [PATCH 031/117] fix proxy address --- packages/dai-plugin-mcd/test/schema.spec.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index ac0ac4e78..70b2f0bba 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -105,7 +105,7 @@ test(PROXY_ADDRESS, async () => { const proxyAddress = await maker.latest(PROXY_ADDRESS, address); expect(isValidAddressString(proxyAddress)).toEqual(true); - expect(proxyAddress).toEqual('0xC21eDD3d1Ba1bCCD67008B680b362ce6F344DaB3'); + expect(proxyAddress).toEqual('0x570074CCb147ea3dE2E23fB038D4d78324278886'); }); test(TOTAL_ENCUMBERED_DEBT, async () => { @@ -293,7 +293,7 @@ test(ILK_PRICES, async () => { test(UNLOCKED_COLLATERAL, async () => { const cdpId = 1; - const expected = 0; + const expected = 0; const col = await maker.latest( UNLOCKED_COLLATERAL, 'ETH-A', @@ -312,7 +312,7 @@ test(URN_INK, async () => { test(URN_ART, async () => { const cdpId = 1; - const expected = fromWei(995000000000000000); + const expected = fromWei(995000000000000000); const art = await maker.latest(URN_ART, 'ETH-A', await cdpMgr.getUrn(cdpId)); expect(art.toNumber()).toBeCloseTo(expected.toNumber()); }); @@ -357,12 +357,12 @@ test(VAULT_BY_ID, async () => { test(DUTY, async () => { const expected = 0.04999999999989363; const duty = await maker.latest(DUTY, 'ETH-A'); - expect((duty)).toEqual(expected); + expect(duty).toEqual(expected); }); test(RHO, async () => { - var timestamp = Math.round((new Date()).getTime() / 1000); + var timestamp = Math.round(new Date().getTime() / 1000); const rho = await maker.latest(RHO, 'ETH-A'); -// RHO is called in the beforeAll block - expect((timestamp-rho)).toBeLessThanOrEqual(10); -}); \ No newline at end of file + // RHO is called in the beforeAll block + expect(timestamp - rho).toBeLessThanOrEqual(10); +}); From 0439dc7898efab179d84d044b3689306e371d650 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 13:43:53 +0000 Subject: [PATCH 032/117] Name changes --- packages/dai-plugin-mcd/src/schema.js | 45 +++++++++++++-------- packages/dai-plugin-mcd/test/schema.spec.js | 45 +++++++++++++-------- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index ee3025216..a88984e00 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -136,15 +136,15 @@ export const vatGem = { return: [UNLOCKED_COLLATERAL, fromWei] }; -export const URN_INK = 'urnInk'; -export const URN_ART = 'urnArt'; +export const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; +export const ENCUMBERED_DEBT = 'encumberedDebt'; export const urnState = { generate: (ilkName, urn) => ({ id: `MCD_Vat.urns(${ilkName},${urn})`, contractName: 'MCD_VAT', call: ['urns(bytes32,address)(uint256,uint256)', toHex(ilkName), urn] }), - returns: [[URN_INK, fromWei], [URN_ART, fromWei]] + returns: [[ENCUMBERED_COLLATERAL, fromWei], [ENCUMBERED_DEBT, fromWei]] }; export const VAULT_URN = 'vaultUrn'; @@ -175,11 +175,17 @@ export const vaultIlkAndUrn = { }) }; -export const URN_INK_AND_ART = 'urnInkAndArt'; -export const urnInkAndArt = { +export const URN_COLLATERAL_AND_DEBT = 'urnCollateralAndDebt'; +export const urnCollateralAndDebt = { generate: (ilk, urn) => ({ - dependencies: [[URN_INK, ilk, urn], [URN_ART, ilk, urn]], - computed: (ink, art) => [ink, art] + dependencies: [ + [ENCUMBERED_COLLATERAL, ilk, urn], + [ENCUMBERED_DEBT, ilk, urn] + ], + computed: (encumberedCollateral, encumberedDebt) => [ + encumberedCollateral, + encumberedDebt + ] }) }; @@ -194,29 +200,34 @@ export const vaultById = { .pipe(first()) .toPromise() .then(([ilk, urn]) => { - watch(URN_INK_AND_ART, ilk, urn) + watch(URN_COLLATERAL_AND_DEBT, ilk, urn) .pipe(first()) .toPromise() - .then(([ink, art]) => { - resolve([ilk, urn, ink, art]); + .then(([encumberedCollateral, encumberedDebt]) => { + resolve([ilk, urn, encumberedCollateral, encumberedDebt]); }); }); }) ] ], - computed: ([ilk, urn, ink, art]) => ({ ilk, urn, ink, art }) + computed: ([ilk, urn, encumberedCollateral, encumberedDebt]) => ({ + ilk, + urn, + encumberedCollateral, + encumberedDebt + }) }) }; -export const DUTY = 'duty'; -export const RHO = 'rho'; +export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; +export const FEE_UPDATE_TIMESTAMP = 'feeUpdateTimestamp'; export const jugInfo = { - generate: (name) => ({ + generate: name => ({ id: `MCD_JUG.ilks(${name})`, contractName: 'MCD_JUG', - call: ['ilks(bytes32)(uint256,uint48)', toHex(name)], + call: ['ilks(bytes32)(uint256,uint48)', toHex(name)] }), - returns: [[DUTY, annualStabilityFee], [RHO]] + returns: [[ANNUAL_STABILITY_FEE, annualStabilityFee], [FEE_UPDATE_TIMESTAMP]] }; export default { @@ -233,7 +244,7 @@ export default { vaultUrn, vaultIlk, vaultIlkAndUrn, - urnInkAndArt, + urnCollateralAndDebt, vaultById, jugInfo }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 70b2f0bba..5a8aea54c 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -39,14 +39,14 @@ import schemas, { ILK_PRICE, ILK_PRICES, UNLOCKED_COLLATERAL, - URN_INK, - URN_ART, + ENCUMBERED_COLLATERAL, + ENCUMBERED_DEBT, VAULT_URN, VAULT_ILK, VAULT_ILK_AND_URN, VAULT_BY_ID, - DUTY, - RHO + ANNUAL_STABILITY_FEE, + FEE_UPDATE_TIMESTAMP } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -303,18 +303,26 @@ test(UNLOCKED_COLLATERAL, async () => { expect(col).toEqual(fromWei(expected)); }); -test(URN_INK, async () => { +test(ENCUMBERED_COLLATERAL, async () => { const cdpId = 1; const expected = fromWei(1000000000000000000); - const ink = await maker.latest(URN_INK, 'ETH-A', await cdpMgr.getUrn(cdpId)); - expect(ink).toEqual(expected); + const encumberedCollateral = await maker.latest( + ENCUMBERED_COLLATERAL, + 'ETH-A', + await cdpMgr.getUrn(cdpId) + ); + expect(encumberedCollateral).toEqual(expected); }); -test(URN_ART, async () => { +test(ENCUMBERED_DEBT, async () => { const cdpId = 1; const expected = fromWei(995000000000000000); - const art = await maker.latest(URN_ART, 'ETH-A', await cdpMgr.getUrn(cdpId)); - expect(art.toNumber()).toBeCloseTo(expected.toNumber()); + const encumberedDebt = await maker.latest( + ENCUMBERED_DEBT, + 'ETH-A', + await cdpMgr.getUrn(cdpId) + ); + expect(encumberedDebt.toNumber()).toBeCloseTo(expected.toNumber()); }); test(VAULT_URN, async () => { @@ -346,23 +354,26 @@ test(VAULT_BY_ID, async () => { const expectedUrn = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const expectedInk = fromWei(1000000000000000000); const expectedArt = fromWei(995000000000000000); - const { ilk, urn, ink, art } = await maker.latest(VAULT_BY_ID, cdpId); + const { ilk, urn, encumberedCollateral, encumberedDebt } = await maker.latest( + VAULT_BY_ID, + cdpId + ); expect(ilk).toEqual(expectedIlk); expect(urn).toEqual(expectedUrn); - expect(ink).toEqual(expectedInk); - expect(art.toNumber()).toBeCloseTo(expectedArt.toNumber()); + expect(encumberedCollateral).toEqual(expectedInk); + expect(encumberedDebt.toNumber()).toBeCloseTo(expectedArt.toNumber()); }); -test(DUTY, async () => { +test(ANNUAL_STABILITY_FEE, async () => { const expected = 0.04999999999989363; - const duty = await maker.latest(DUTY, 'ETH-A'); + const duty = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); expect(duty).toEqual(expected); }); -test(RHO, async () => { +test(FEE_UPDATE_TIMESTAMP, async () => { var timestamp = Math.round(new Date().getTime() / 1000); - const rho = await maker.latest(RHO, 'ETH-A'); + const rho = await maker.latest(FEE_UPDATE_TIMESTAMP, 'ETH-A'); // RHO is called in the beforeAll block expect(timestamp - rho).toBeLessThanOrEqual(10); }); From 66795c014a025d3ba9777f9f9144ebf4d7500270 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 17 Jan 2020 15:01:41 +0100 Subject: [PATCH 033/117] Revert "CDP-733 schemas for duty and rho" This reverts commit d5402d61a057a2ee9e5de063189263b4ad011b35. --- packages/dai-plugin-governance/src/utils/helpers.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/dai-plugin-governance/src/utils/helpers.js b/packages/dai-plugin-governance/src/utils/helpers.js index 59ad19d99..a43c9e484 100644 --- a/packages/dai-plugin-governance/src/utils/helpers.js +++ b/packages/dai-plugin-governance/src/utils/helpers.js @@ -1,5 +1,10 @@ import { createGetCurrency } from '@makerdao/currency'; -import { MKR, STAGING_MAINNET_URL, KOVAN_URL, MAINNET_URL } from './constants'; +import { + MKR, + STAGING_MAINNET_URL, + KOVAN_URL, + MAINNET_URL +} from './constants'; /** * @desc get network name From 5bd2b7243747042969cb62c66efb2564ddbb7933 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 14:58:38 +0000 Subject: [PATCH 034/117] totalSavingsDai --- packages/dai-plugin-mcd/src/index.js | 3 +++ packages/dai-plugin-mcd/src/schema.js | 20 ++++++++++++---- packages/dai-plugin-mcd/test/schema.spec.js | 26 ++++++++++++++++----- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index c4a172fbd..d0628692e 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -69,6 +69,9 @@ export const USD_ETH = createCurrencyRatio(USD, ETH); export const MWETH = createCurrency('MWETH'); export const MDAI = createCurrency('MDAI'); +// Casting for savings dai +export const CHAI = createCurrency('CHAI'); + export const REP = createCurrency('REP'); export const ZRX = createCurrency('ZRX'); export const OMG = createCurrency('OMG'); diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index a88984e00..161e5b5a7 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -9,7 +9,7 @@ import { fromRad } from './utils'; import BigNumber from 'bignumber.js'; -import { USD, ETH, BAT, MDAI } from '..'; +import { USD, ETH, BAT, MDAI, CHAI } from '..'; import { annualStabilityFee } from './math'; import { first } from 'rxjs/operators'; @@ -72,8 +72,9 @@ export const spotIlks = { export const LIQUIDATION_RATIO = 'liquidationRatio'; export const liquidationRatio = { - // The liquidation ratio value is the minimum dollar amount of collateral in - // terms of a single dollar unit amount of debt + // The liquidation ratio value is the ratio between the minimum dollar amount of a unit of + // collateral in terms of a single dollar unit amount of debt in which the system does not + // deem a vault of that collateral type (ilk) underwater // // In plain english, it is the ratio of the dollar amount of ETH in terms of // the dollar amount of dai @@ -230,6 +231,16 @@ export const jugInfo = { returns: [[ANNUAL_STABILITY_FEE, annualStabilityFee], [FEE_UPDATE_TIMESTAMP]] }; +export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; +export const piePot = { + generate: () => ({ + id: `MCD_POT.Pie`, + contractName: 'MCD_POT', + call: ['Pie()(uint256)'] + }), + returns: [[TOTAL_SAVINGS_DAI, v => CHAI(v, 'wei')]] +}; + export default { vatIlks, proxies, @@ -246,5 +257,6 @@ export default { vaultIlkAndUrn, urnCollateralAndDebt, vaultById, - jugInfo + jugInfo, + piePot }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 5a8aea54c..7053945d8 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -46,11 +46,12 @@ import schemas, { VAULT_ILK_AND_URN, VAULT_BY_ID, ANNUAL_STABILITY_FEE, - FEE_UPDATE_TIMESTAMP + FEE_UPDATE_TIMESTAMP, + TOTAL_SAVINGS_DAI } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); -const ETH_A_DEBT_AMOUNT = MDAI(1); +const ETH_A_DEBT_AMOUNT = MDAI(2); const ETH_A_PRICE = 180; const BAT_A_COLLATERAL_AMOUNT = BAT(1); @@ -64,6 +65,11 @@ const setupFn = async () => { await setupCollateral(maker, 'BAT-A', { price: BAT_A_PRICE }); const mgr = await maker.service(ServiceRoles.CDP_MANAGER); + const sav = await maker.service(ServiceRoles.SAVINGS); + const dai = maker.getToken(MDAI); + const _proxyAddress = await maker.service('proxy').ensureProxy(); + await dai.approveUnlimited(_proxyAddress); + await mgr.openLockAndDraw( 'ETH-A', ETH_A_COLLATERAL_AMOUNT, @@ -74,6 +80,8 @@ const setupFn = async () => { BAT_A_COLLATERAL_AMOUNT, BAT_A_DEBT_AMOUNT ); + + await sav.join(MDAI(1)); }; beforeAll(async () => { @@ -367,13 +375,19 @@ test(VAULT_BY_ID, async () => { test(ANNUAL_STABILITY_FEE, async () => { const expected = 0.04999999999989363; - const duty = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); - expect(duty).toEqual(expected); + const annualStabilityFee = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); + expect(annualStabilityFee).toEqual(expected); }); test(FEE_UPDATE_TIMESTAMP, async () => { var timestamp = Math.round(new Date().getTime() / 1000); - const rho = await maker.latest(FEE_UPDATE_TIMESTAMP, 'ETH-A'); + const feeUpdateTimestamp = await maker.latest(FEE_UPDATE_TIMESTAMP, 'ETH-A'); // RHO is called in the beforeAll block - expect(timestamp - rho).toBeLessThanOrEqual(10); + expect(timestamp - feeUpdateTimestamp).toBeLessThanOrEqual(10); +}); + +test(TOTAL_SAVINGS_DAI, async () => { + const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); + expect(totalSavingsDai.symbol).toEqual('CHAI'); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); }); From 21f447034e3f7a5dde710c78501f84bb5f1169b4 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 17 Jan 2020 16:02:40 +0100 Subject: [PATCH 035/117] SDK-745 update type definitions for some schemas --- packages/dai-plugin-mcd/src/schema.js | 6 +-- .../dai-plugin-mcd/types/multicall/index.d.ts | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index a88984e00..69e544499 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -222,10 +222,10 @@ export const vaultById = { export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; export const FEE_UPDATE_TIMESTAMP = 'feeUpdateTimestamp'; export const jugInfo = { - generate: name => ({ - id: `MCD_JUG.ilks(${name})`, + generate: ilkName => ({ + id: `MCD_JUG.ilks(${ilkName})`, contractName: 'MCD_JUG', - call: ['ilks(bytes32)(uint256,uint48)', toHex(name)] + call: ['ilks(bytes32)(uint256,uint48)', toHex(ilkName)] }), returns: [[ANNUAL_STABILITY_FEE, annualStabilityFee], [FEE_UPDATE_TIMESTAMP]] }; diff --git a/packages/dai-plugin-mcd/types/multicall/index.d.ts b/packages/dai-plugin-mcd/types/multicall/index.d.ts index 3c0af2b2c..4c666419f 100644 --- a/packages/dai-plugin-mcd/types/multicall/index.d.ts +++ b/packages/dai-plugin-mcd/types/multicall/index.d.ts @@ -51,6 +51,49 @@ interface WatchInterface { ilkPrices( [ilkNames]: string ): Currency; + + /** + * @param ilkName String uniquely identifying an ilk + * @param urn String hexidecimal address of the vault handler + */ + unlockedCollateral( + ilkName: string, + urn: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + * @param urn String hexidecimal address of the vault handler + */ + encumberedCollateral( + ilkName: string, + urn: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + * @param urn String hexidecimal address of the vault handler + */ + encumberedDebt( + ilkName: string, + urn: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + */ + annualStabilityFee( + ilkName: string + ): number; + + /** + * @param ilkName String uniquely identifying an ilk + */ + feeUpdateTimestamp( + ilkName: string + ): number; + + } declare var watch: WatchInterface; From 984e1b9e2056c1111953cba6b0b34d5e333dd949 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 15:03:04 +0000 Subject: [PATCH 036/117] beforeEach snapshots --- packages/dai-plugin-mcd/test/schema.spec.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 7053945d8..3a36d3578 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -1,6 +1,7 @@ /* eslint-disable */ import { mcdMaker, setupCollateral } from './helpers'; import { ETH, BAT, MDAI, USD } from '../src'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { toHex, fromRad, @@ -51,7 +52,7 @@ import schemas, { } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); -const ETH_A_DEBT_AMOUNT = MDAI(2); +const ETH_A_DEBT_AMOUNT = MDAI(1); const ETH_A_PRICE = 180; const BAT_A_COLLATERAL_AMOUNT = BAT(1); @@ -109,6 +110,14 @@ beforeAll(async () => { batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); +beforeEach(async () => { + snapshotData = await takeSnapshot(maker); +}); + +afterEach(async () => { + await restoreSnapshot(snapshotData, maker); +}); + test(PROXY_ADDRESS, async () => { const proxyAddress = await maker.latest(PROXY_ADDRESS, address); From f7356e371afc9fbb2c35c2bfa0ec670c118b545d Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 16:15:02 +0000 Subject: [PATCH 037/117] savingsDaiByProxy and savingsDai --- packages/dai-plugin-mcd/src/schema.js | 39 +++++++++++++++++++-- packages/dai-plugin-mcd/test/schema.spec.js | 27 ++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 5124f0688..3ec94cc9a 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -232,7 +232,7 @@ export const jugInfo = { }; export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; -export const piePot = { +export const potPie = { generate: () => ({ id: `MCD_POT.Pie`, contractName: 'MCD_POT', @@ -241,6 +241,39 @@ export const piePot = { returns: [[TOTAL_SAVINGS_DAI, v => CHAI(v, 'wei')]] }; +export const SAVINGS_DAI_BY_PROXY = 'savingsDaiByProxy'; +export const potpie = { + generate: proxyAddress => ({ + id: `MCD_POT.pie(${proxyAddress})`, + contractName: 'MCD_POT', + call: ['pie(address)(uint256)', proxyAddress] + }), + returns: [[SAVINGS_DAI_BY_PROXY, v => CHAI(v, 'wei')]] +}; + +export const SAVINGS_DAI = 'savingsDai'; +export const savingsDai = { + generate: address => ({ + dependencies: watch => [ + [ + () => + new Promise(resolve => { + watch(PROXY_ADDRESS, address) + .pipe(first()) + .toPromise() + .then(proxyAddress => { + watch(SAVINGS_DAI_BY_PROXY, proxyAddress) + .pipe(first()) + .toPromise() + .then(resolve); + }); + }) + ] + ], + computed: savingsDai => savingsDai + }) +}; + export default { vatIlks, proxies, @@ -258,5 +291,7 @@ export default { urnCollateralAndDebt, vaultById, jugInfo, - piePot + potPie, + potpie, + savingsDai }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 3a36d3578..0043d7311 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -48,7 +48,9 @@ import schemas, { VAULT_BY_ID, ANNUAL_STABILITY_FEE, FEE_UPDATE_TIMESTAMP, - TOTAL_SAVINGS_DAI + TOTAL_SAVINGS_DAI, + SAVINGS_DAI_BY_PROXY, + SAVINGS_DAI } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -86,6 +88,7 @@ const setupFn = async () => { }; beforeAll(async () => { + snapshotData = await takeSnapshot(maker); maker = await mcdMaker({ cdpTypes: [ { currency: ETH, ilk: 'ETH-A' }, @@ -103,18 +106,13 @@ beforeAll(async () => { cdpMgr = maker.service(ServiceRoles.CDP_MANAGER); cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); proxyService = maker.service('proxy'); - vault = await setupFn(); ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); -beforeEach(async () => { - snapshotData = await takeSnapshot(maker); -}); - -afterEach(async () => { +afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); @@ -400,3 +398,18 @@ test(TOTAL_SAVINGS_DAI, async () => { expect(totalSavingsDai.symbol).toEqual('CHAI'); expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); }); + +test(SAVINGS_DAI_BY_PROXY, async () => { + const savingsDaiByProxy = await maker.latest( + SAVINGS_DAI_BY_PROXY, + await proxyService.getProxyAddress() + ); + expect(savingsDaiByProxy.symbol).toEqual('CHAI'); + expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); +}); + +test(SAVINGS_DAI, async () => { + const savingsDai = await maker.latest(SAVINGS_DAI, address); + expect(savingsDai.symbol).toEqual('CHAI'); + expect(savingsDai.toNumber()).toBeCloseTo(0.99995); +}); From 68a86aa3b60c3488c2cf49c90aa722c867c36f5e Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 17 Jan 2020 18:08:45 +0100 Subject: [PATCH 038/117] SDK-745 add schemas, tests & types for liquidator state --- packages/dai-plugin-mcd/src/schema.js | 20 ++++++++++++++-- packages/dai-plugin-mcd/test/schema.spec.js | 23 ++++++++++++++++++- .../dai-plugin-mcd/types/multicall/index.d.ts | 21 +++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 5124f0688..9e1cdd552 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -10,7 +10,7 @@ import { } from './utils'; import BigNumber from 'bignumber.js'; import { USD, ETH, BAT, MDAI, CHAI } from '..'; -import { annualStabilityFee } from './math'; +import { annualStabilityFee, liquidationPenalty } from './math'; import { first } from 'rxjs/operators'; export const PROXY_ADDRESS = 'proxyAddress'; @@ -241,6 +241,21 @@ export const piePot = { returns: [[TOTAL_SAVINGS_DAI, v => CHAI(v, 'wei')]] }; +export const LIQUIDATOR_ADDRESS = 'liquidatorAddress'; +export const LIQUIDATION_PENALTY = 'liquidationPenalty'; +export const MAX_AUCTION_LOT_SIZE = 'maxAuctionLotSize'; +export const liquidatorState = { + generate: ilkName => ({ + id: `MCD_CAT.ilks(${ilkName})`, + contractName: 'MCD_CAT', + call: ['ilks(bytes32)(address,uint256,uint256)', toHex(ilkName)] + }), + returns: [ + [LIQUIDATOR_ADDRESS], + [LIQUIDATION_PENALTY, liquidationPenalty], + [MAX_AUCTION_LOT_SIZE, v => BigNumber(v).shiftedBy(-18)]] +}; + export default { vatIlks, proxies, @@ -258,5 +273,6 @@ export default { urnCollateralAndDebt, vaultById, jugInfo, - piePot + piePot, + liquidatorState }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 3a36d3578..8e9d6a6eb 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -48,7 +48,10 @@ import schemas, { VAULT_BY_ID, ANNUAL_STABILITY_FEE, FEE_UPDATE_TIMESTAMP, - TOTAL_SAVINGS_DAI + TOTAL_SAVINGS_DAI, + LIQUIDATOR_ADDRESS, + LIQUIDATION_PENALTY, + MAX_AUCTION_LOT_SIZE } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -400,3 +403,21 @@ test(TOTAL_SAVINGS_DAI, async () => { expect(totalSavingsDai.symbol).toEqual('CHAI'); expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); }); + +test(LIQUIDATOR_ADDRESS, async () => { + const expected = '0x55320248dC50Ef6dABc88ECbc294Fd5e2e1f4eC6'; + const address = await maker.latest(LIQUIDATOR_ADDRESS, 'ETH-A'); + expect(address).toEqual(expected); +}); + +test(LIQUIDATION_PENALTY, async () => { + const expected = 0.05; + const liquidationPenalty = await maker.latest(LIQUIDATION_PENALTY, 'ETH-A'); + expect(liquidationPenalty).toEqual(expected); +}); + +test(MAX_AUCTION_LOT_SIZE, async () => { + const expected = BigNumber('1.5'); + const maxLotSize = await maker.latest(MAX_AUCTION_LOT_SIZE, 'ETH-A'); + expect(maxLotSize).toEqual(expected); +}); diff --git a/packages/dai-plugin-mcd/types/multicall/index.d.ts b/packages/dai-plugin-mcd/types/multicall/index.d.ts index 4c666419f..22a379df4 100644 --- a/packages/dai-plugin-mcd/types/multicall/index.d.ts +++ b/packages/dai-plugin-mcd/types/multicall/index.d.ts @@ -93,6 +93,27 @@ interface WatchInterface { ilkName: string ): number; + /** + * @param ilkName String uniquely identifying an ilk + */ + liquidatorAddress( + ilkName: string + ): string; + + /** + * @param ilkName String uniquely identifying an ilk + */ + liquidationPenalty( + ilkName: string + ): number; + + /** + * @param ilkName String uniquely identifying an ilk + */ + maxAuctionLotSize( + ilkName: string + ): BigNumber; + } From ec22751c4147ef6624887de7a3af913e03116e6d Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 17 Jan 2020 17:12:58 +0000 Subject: [PATCH 039/117] added dateEarningsLastAccrued for pot.rho --- packages/dai-plugin-mcd/src/schema.js | 46 +++++++++++++++++++-- packages/dai-plugin-mcd/test/schema.spec.js | 43 ++++++++++++++++--- 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 3ec94cc9a..285b58aee 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -221,14 +221,17 @@ export const vaultById = { }; export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; -export const FEE_UPDATE_TIMESTAMP = 'feeUpdateTimestamp'; +export const DATE_STABILITY_FEES_LAST_LEVIED = 'dateStabilityFeesLastLevied'; export const jugInfo = { generate: ilkName => ({ id: `MCD_JUG.ilks(${ilkName})`, contractName: 'MCD_JUG', call: ['ilks(bytes32)(uint256,uint48)', toHex(ilkName)] }), - returns: [[ANNUAL_STABILITY_FEE, annualStabilityFee], [FEE_UPDATE_TIMESTAMP]] + returns: [ + [ANNUAL_STABILITY_FEE, annualStabilityFee], + [DATE_STABILITY_FEES_LAST_LEVIED, val => new Date(val * 1000)] + ] }; export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; @@ -274,6 +277,40 @@ export const savingsDai = { }) }; +export const DAI_SAVINGS_RATE = 'daiSavingsRate'; +export const potDsr = { + generate: () => ({ + id: `MCD_POT.dsr`, + contractName: 'MCD_POT', + call: ['dsr()(uint256)'] + }), + returns: [[DAI_SAVINGS_RATE, fromRay]] +}; + +export const ANNUAL_DAI_SAVINGS_RATE = 'annualDaiSavingsRate'; +export const annualDaiSavingsRate = { + generate: () => ({ + dependencies: () => [[DAI_SAVINGS_RATE]], + computed: daiSavingsRate => + daiSavingsRate + .pow(365 * 24 * 60 * 60) + .minus(1) + .times(100) + }) +}; + +export const DATE_EARNINGS_LAST_ACCRUED = 'dateEarningsLastAccrued'; +export const potRho = { + generate: () => ({ + id: `MCD_POT.rho`, + contractName: 'MCD_POT', + call: ['rho()(uint256)'] + }), + returns: [ + [DATE_EARNINGS_LAST_ACCRUED, val => new Date(val.toNumber() * 1000)] + ] +}; + export default { vatIlks, proxies, @@ -293,5 +330,8 @@ export default { jugInfo, potPie, potpie, - savingsDai + savingsDai, + potDsr, + annualDaiSavingsRate, + potRho }; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 0043d7311..c4f2df063 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -47,10 +47,13 @@ import schemas, { VAULT_ILK_AND_URN, VAULT_BY_ID, ANNUAL_STABILITY_FEE, - FEE_UPDATE_TIMESTAMP, + DATE_STABILITY_FEES_LAST_LEVIED, TOTAL_SAVINGS_DAI, SAVINGS_DAI_BY_PROXY, - SAVINGS_DAI + SAVINGS_DAI, + DAI_SAVINGS_RATE, + ANNUAL_DAI_SAVINGS_RATE, + DATE_EARNINGS_LAST_ACCRUED } from '../src/schema'; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -386,11 +389,15 @@ test(ANNUAL_STABILITY_FEE, async () => { expect(annualStabilityFee).toEqual(expected); }); -test(FEE_UPDATE_TIMESTAMP, async () => { +test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { var timestamp = Math.round(new Date().getTime() / 1000); - const feeUpdateTimestamp = await maker.latest(FEE_UPDATE_TIMESTAMP, 'ETH-A'); - // RHO is called in the beforeAll block - expect(timestamp - feeUpdateTimestamp).toBeLessThanOrEqual(10); + const dateStabilityFeesLastLevied = await maker.latest( + DATE_STABILITY_FEES_LAST_LEVIED, + 'ETH-A' + ); + + expect(dateStabilityFeesLastLevied instanceof Date).toEqual(true); + expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); }); test(TOTAL_SAVINGS_DAI, async () => { @@ -413,3 +420,27 @@ test(SAVINGS_DAI, async () => { expect(savingsDai.symbol).toEqual('CHAI'); expect(savingsDai.toNumber()).toBeCloseTo(0.99995); }); + +test(DAI_SAVINGS_RATE, async () => { + const daiSavingsRate = await maker.latest(DAI_SAVINGS_RATE); + expect(daiSavingsRate).toEqual(BigNumber('1.000000000315522921573372069')); +}); + +test(ANNUAL_DAI_SAVINGS_RATE, async () => { + const annualDaiSavingsRate = await maker.latest(ANNUAL_DAI_SAVINGS_RATE); + expect(annualDaiSavingsRate).toEqual( + BigNumber( + '0.999999999999999998903600959584714938425430352632298919434159277685511322388082342817131189583694' + ) + ); +}); + +test(DATE_EARNINGS_LAST_ACCRUED, async () => { + const timestamp = Math.round(new Date().getTime() / 1000); + const dateEarningsLastAccrued = await maker.latest( + DATE_EARNINGS_LAST_ACCRUED + ); + + expect(dateEarningsLastAccrued instanceof Date).toEqual(true); + expect(timestamp - dateEarningsLastAccrued).toBeLessThanOrEqual(10); +}); From 909001bd2ad46f7594e261565b9ceeaca1cac26e Mon Sep 17 00:00:00 2001 From: sirromdev Date: Sun, 19 Jan 2020 19:52:01 +0000 Subject: [PATCH 040/117] Not skipping tests --- packages/dai-plugin-mcd/test/schema.spec.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index c0710a97f..61f628ba1 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -403,22 +403,22 @@ test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); }); -xtest(TOTAL_SAVINGS_DAI, async () => { +test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(totalSavingsDai.symbol).toEqual('CHAI'); expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); }); -xtest(SAVINGS_DAI_BY_PROXY, async () => { +test(SAVINGS_DAI_BY_PROXY, async () => { const savingsDaiByProxy = await maker.latest( SAVINGS_DAI_BY_PROXY, await proxyService.getProxyAddress() - ); - expect(savingsDaiByProxy.symbol).toEqual('CHAI'); - expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); - }); - -xtest(SAVINGS_DAI, async () => { + ); + expect(savingsDaiByProxy.symbol).toEqual('CHAI'); + expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); +}); + +test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); expect(savingsDai.symbol).toEqual('CHAI'); expect(savingsDai.toNumber()).toBeCloseTo(0.99995); From 6d7d2a9d1d851ee330256d8f20581c556dbdfd94 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Sun, 19 Jan 2020 23:16:46 +0000 Subject: [PATCH 041/117] Traversing dependency trees of schemas in computed observables --- packages/dai-plugin-mcd/src/schema.js | 44 ++++-------------- packages/dai-plugin-mcd/test/schema.spec.js | 14 ++++-- packages/dai/src/eth/MulticallService.js | 43 ++++++++++++++--- .../dai/test/eth/MulticallService.spec.js | 8 ++++ packages/dai/test/helpers/schemas.js | 46 ++++++++++++++----- 5 files changed, 98 insertions(+), 57 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js index 4a6cc86ac..c844339fb 100644 --- a/packages/dai-plugin-mcd/src/schema.js +++ b/packages/dai-plugin-mcd/src/schema.js @@ -193,25 +193,13 @@ export const urnCollateralAndDebt = { export const VAULT_BY_ID = 'vaultById'; export const vaultById = { generate: id => ({ - dependencies: watch => [ - [ - () => - new Promise(resolve => { - watch(VAULT_ILK_AND_URN, id) - .pipe(first()) - .toPromise() - .then(([ilk, urn]) => { - watch(URN_COLLATERAL_AND_DEBT, ilk, urn) - .pipe(first()) - .toPromise() - .then(([encumberedCollateral, encumberedDebt]) => { - resolve([ilk, urn, encumberedCollateral, encumberedDebt]); - }); - }); - }) - ] + dependencies: [ + [VAULT_ILK, id], + [VAULT_URN, id], + [ENCUMBERED_COLLATERAL, [VAULT_ILK, id], [VAULT_URN, id]], + [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]] ], - computed: ([ilk, urn, encumberedCollateral, encumberedDebt]) => ({ + computed: (ilk, urn, encumberedCollateral, encumberedDebt) => ({ ilk, urn, encumberedCollateral, @@ -257,22 +245,7 @@ export const potpie = { export const SAVINGS_DAI = 'savingsDai'; export const savingsDai = { generate: address => ({ - dependencies: watch => [ - [ - () => - new Promise(resolve => { - watch(PROXY_ADDRESS, address) - .pipe(first()) - .toPromise() - .then(proxyAddress => { - watch(SAVINGS_DAI_BY_PROXY, proxyAddress) - .pipe(first()) - .toPromise() - .then(resolve); - }); - }) - ] - ], + dependencies: [[SAVINGS_DAI_BY_PROXY, [PROXY_ADDRESS, address]]], computed: savingsDai => savingsDai }) }; @@ -323,7 +296,8 @@ export const liquidatorState = { returns: [ [LIQUIDATOR_ADDRESS], [LIQUIDATION_PENALTY, liquidationPenalty], - [MAX_AUCTION_LOT_SIZE, v => BigNumber(v).shiftedBy(-18)]] + [MAX_AUCTION_LOT_SIZE, v => BigNumber(v).shiftedBy(-18)] + ] }; export default { diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schema.spec.js index 61f628ba1..05e68f5e4 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schema.spec.js @@ -418,11 +418,15 @@ test(SAVINGS_DAI_BY_PROXY, async () => { expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); }); -test(SAVINGS_DAI, async () => { - const savingsDai = await maker.latest(SAVINGS_DAI, address); - expect(savingsDai.symbol).toEqual('CHAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.99995); -}); +test( + SAVINGS_DAI, + async () => { + const savingsDai = await maker.latest(SAVINGS_DAI, address); + expect(savingsDai.symbol).toEqual('CHAI'); + expect(savingsDai.toNumber()).toBeCloseTo(0.99995); + }, + 10000 +); test(DAI_SAVINGS_RATE, async () => { const daiSavingsRate = await maker.latest(DAI_SAVINGS_RATE); diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 13bdee428..4071b9fc5 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -2,7 +2,7 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; import { Observable, ReplaySubject, combineLatest, from } from 'rxjs'; -import { map, first } from 'rxjs/operators'; +import { map, first, flatMap } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -166,15 +166,46 @@ export default class MulticallService extends PublicService { ? generatedSchema.dependencies(this.watchObservable.bind(this)) : generatedSchema.dependencies; - const dependencySubs = dependencies.map(dep => { - // TODO: This should allow a single string/func as well as a string array - const key = dep.shift(); + const recurseDependencyTree = trie => { + let key = trie.shift(); // This is a promise dependency if (typeof key === 'function') { return from(key()); } - return this.watchObservable(key, ...dep); - }); + + let atLeafNode = false; + const [checker] = trie; + if (!Array.isArray(checker)) { + atLeafNode = true; + } + + if (Array.isArray(trie) && !atLeafNode) { + if (trie.length > 1) { + return combineLatest(trie.map(recurseDependencyTree)).pipe( + flatMap(result => { + return this.watchObservable(key, ...result); + }) + ); + } else { + const next = trie.shift(); + console.log(next); + return recurseDependencyTree(next).pipe( + flatMap(result => { + return Array.isArray(result) + ? this.watchObservable(key, ...result) + : this.watchObservable(key, result); + }) + ); + } + } else { + if (Array.isArray(trie) && trie.length === 0) + return this.watchObservable(key); + return this.watchObservable(key, ...trie); + } + }; + + const dependencySubs = dependencies.map(recurseDependencyTree); + const observerLatest = combineLatest(dependencySubs).pipe( map(result => generatedSchema.computed(...result)) ); diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index ecaab9536..976278729 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -8,10 +8,12 @@ import { first } from 'rxjs/operators'; let service; let watcher; let address; +let addressWithProxy; beforeEach(async () => { service = buildTestMulticallService(); await service.manager().authenticate(); + addressWithProxy = service.get('web3').currentAddress(); address = TestAccountProvider.nextAddress(); watcher = service.createWatcher({ interval: 'block' }); service.registerSchemas(schemas); @@ -124,3 +126,9 @@ test('watch same computed observable with dynamically generated dependencies mor expect(ilkDebtCeilings1).toEqual([100000, 5000]); expect(ilkDebtCeilings2).toEqual([100000, 5000]); }); + +test('watch computed observable which chains calls to base observables', async () => { + const observable = service.watchObservable('testComputed4', address); + const res = await observable.pipe(first()).toPromise(); + expect(res).toEqual(0); +}); diff --git a/packages/dai/test/helpers/schemas.js b/packages/dai/test/helpers/schemas.js index 7f01da5ee..ac81c5133 100644 --- a/packages/dai/test/helpers/schemas.js +++ b/packages/dai/test/helpers/schemas.js @@ -62,7 +62,7 @@ export const ilkDebt = { ['debtScalingFactor', ilkName] ], computed: (art, rate) => { - console.log('ilkDebt computed:', art, rate) + console.log('ilkDebt computed:', art, rate); return debtValue(art, rate); } }) @@ -70,21 +70,17 @@ export const ilkDebt = { export const testComputed1 = { generate: ilkName => ({ - dependencies: [ - ['debtScalingFactor', ilkName], - ['debtCeiling', ilkName] - ], - computed: (debtScalingFactor, debtCeiling) => Number(debtScalingFactor) + Number(debtCeiling) - }), + dependencies: [['debtScalingFactor', ilkName], ['debtCeiling', ilkName]], + computed: (debtScalingFactor, debtCeiling) => + Number(debtScalingFactor) + Number(debtCeiling) + }) }; export const testComputed2 = { generate: multiplyBy => ({ - dependencies: [ - ['testComputed1', 'ETH-A'] - ], + dependencies: [['testComputed1', 'ETH-A']], computed: testComputed1 => testComputed1 * multiplyBy - }), + }) }; export const testComputed3 = { @@ -97,6 +93,31 @@ export const testComputed3 = { }) }; +export const proxies = { + generate: address => ({ + id: `PROXY_REGISTRY.proxies(${address})`, + contractName: 'PROXY_REGISTRY', + call: ['proxies(address)(address)', address] + }), + returns: [['proxyAddress']] +}; + +export const potpie = { + generate: proxyAddress => ({ + id: `MCD_POT.pie(${proxyAddress})`, + contractName: 'MCD_POT', + call: ['pie(address)(uint256)', proxyAddress] + }), + returns: [['savingsDaiByProxy']] +}; + +export const testComputed4 = { + generate: address => ({ + dependencies: [['savingsDaiByProxy', ['proxyAddress', address]]], + computed: res => Number(res) + }) +}; + export const ilkDebtCeilings = { generate: ilks => ({ // Dynamically generate dependencies @@ -111,5 +132,8 @@ export default { testComputed1, testComputed2, testComputed3, + proxies, + potpie, + testComputed4, ilkDebtCeilings }; From b1f05819d5ad52cb4a1797c597ae3772a87e4e16 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 20 Jan 2020 13:07:34 +0800 Subject: [PATCH 042/117] Set multicall addresses on service initialize() --- packages/dai/src/eth/MulticallService.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 4071b9fc5..1b1b726f1 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -24,6 +24,7 @@ export default class MulticallService extends PublicService { initialize() { this.watch = this.watchObservable; + this._addresses = this.get('smartContract').getContractAddresses(); } createWatcher({ @@ -101,8 +102,6 @@ export default class MulticallService extends PublicService { } registerSchemas(schemas) { - this._addresses = this.get('smartContract').getContractAddresses(); - if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); From 4e1a4f839d4d705cac02bfe018af727422fce694 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 20 Jan 2020 13:07:58 +0800 Subject: [PATCH 043/117] Add multicall observableKeys getter --- packages/dai/src/eth/MulticallService.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 1b1b726f1..5f686ceae 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -101,6 +101,10 @@ export default class MulticallService extends PublicService { return this._schemaByObservableKey[key]; } + get observableKeys() { + return Object.keys(this._schemaByObservableKey); + } + registerSchemas(schemas) { if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); From 57cbb010d7a4d5557c956dd154351c500341bde3 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 17:32:31 +0000 Subject: [PATCH 044/117] Broke schema.js into own file structure /schema --- packages/dai-plugin-mcd/jest.config.js | 3 +- packages/dai-plugin-mcd/src/index.js | 3 - packages/dai-plugin-mcd/src/schema.js | 327 ------------------ packages/dai-plugin-mcd/src/schemas/cat.js | 26 ++ .../dai-plugin-mcd/src/schemas/cdpManager.js | 25 ++ .../dai-plugin-mcd/src/schemas/computed.js | 93 +++++ .../dai-plugin-mcd/src/schemas/constants.js | 47 +++ packages/dai-plugin-mcd/src/schemas/index.js | 59 ++++ packages/dai-plugin-mcd/src/schemas/jug.js | 23 ++ packages/dai-plugin-mcd/src/schemas/pot.js | 68 ++++ .../src/schemas/proxyRegistry.js | 14 + packages/dai-plugin-mcd/src/schemas/spot.js | 52 +++ packages/dai-plugin-mcd/src/schemas/vat.js | 67 ++++ .../{schema.spec.js => schemas/index.spec.js} | 37 +- packages/dai/src/eth/MulticallService.js | 2 - 15 files changed, 495 insertions(+), 351 deletions(-) delete mode 100644 packages/dai-plugin-mcd/src/schema.js create mode 100644 packages/dai-plugin-mcd/src/schemas/cat.js create mode 100644 packages/dai-plugin-mcd/src/schemas/cdpManager.js create mode 100644 packages/dai-plugin-mcd/src/schemas/computed.js create mode 100644 packages/dai-plugin-mcd/src/schemas/constants.js create mode 100644 packages/dai-plugin-mcd/src/schemas/index.js create mode 100644 packages/dai-plugin-mcd/src/schemas/jug.js create mode 100644 packages/dai-plugin-mcd/src/schemas/pot.js create mode 100644 packages/dai-plugin-mcd/src/schemas/proxyRegistry.js create mode 100644 packages/dai-plugin-mcd/src/schemas/spot.js create mode 100644 packages/dai-plugin-mcd/src/schemas/vat.js rename packages/dai-plugin-mcd/test/{schema.spec.js => schemas/index.spec.js} (98%) diff --git a/packages/dai-plugin-mcd/jest.config.js b/packages/dai-plugin-mcd/jest.config.js index a387061dd..9fc0640f2 100644 --- a/packages/dai-plugin-mcd/jest.config.js +++ b/packages/dai-plugin-mcd/jest.config.js @@ -8,5 +8,6 @@ module.exports = { testPathIgnorePatterns: [ '/node_modules/', '/test/integration/' - ] + ], + moduleFileExtensions: ['js', 'json'] }; diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index d0628692e..19b4217ba 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -15,7 +15,6 @@ import SystemDataService from './SystemDataService'; import QueryApiMcdService from './QueryApiMcdService'; import { ServiceRoles as ServiceRoles_ } from './constants'; import BigNumber from 'bignumber.js'; -import schema from './schema'; export const ServiceRoles = ServiceRoles_; const { @@ -79,8 +78,6 @@ export const BAT = createCurrency('BAT'); export const DGD = createCurrency('DGD'); export const GNT = createCurrency('GNT'); -export const mcdSchema = schema; - const defaultCdpTypes = [ { currency: ETH, ilk: 'ETH-A' }, { currency: BAT, ilk: 'BAT-A' } diff --git a/packages/dai-plugin-mcd/src/schema.js b/packages/dai-plugin-mcd/src/schema.js deleted file mode 100644 index c844339fb..000000000 --- a/packages/dai-plugin-mcd/src/schema.js +++ /dev/null @@ -1,327 +0,0 @@ -/* eslint-disable */ -import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; -import { - toHex, - bytesToString, - fromWei, - fromWad, - fromRay, - fromRad -} from './utils'; -import BigNumber from 'bignumber.js'; -import { USD, ETH, BAT, MDAI, CHAI } from '..'; -import { annualStabilityFee, liquidationPenalty } from './math'; -import { first } from 'rxjs/operators'; - -export const PROXY_ADDRESS = 'proxyAddress'; - -export const proxies = { - generate: address => ({ - id: `PROXY_REGISTRY.proxies(${address})`, - contractName: 'PROXY_REGISTRY', - call: ['proxies(address)(address)', address] - }), - returns: [[PROXY_ADDRESS]] -}; - -export const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; -export const DEBT_SCALING_FACTOR = 'debtScalingFactor'; -export const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; -export const DEBT_CEILING = 'debtCeiling'; -export const URN_DEBT_FLOOR = 'urnDebtFloor'; - -export const vatIlks = { - generate: ilkName => ({ - id: `MCD_VAT.ilks(${ilkName})`, - contractName: 'MCD_VAT', - call: [ - 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', - toHex(ilkName) - ] - }), - returns: [ - [TOTAL_ENCUMBERED_DEBT, BigNumber], - [DEBT_SCALING_FACTOR, fromRay], - [PRICE_WITH_SAFETY_MARGIN, fromRay], - [DEBT_CEILING, v => MDAI(v, 'rad')], - [URN_DEBT_FLOOR, fromRad] - ] -}; - -export const TOTAL_DAI_SUPPLY = 'totalDaiSupply'; -export const debt = { - generate: () => ({ - id: 'VAT.debt()', - contractName: 'MCD_VAT', - call: ['debt()(uint256)'] - }), - returns: [[TOTAL_DAI_SUPPLY, v => MDAI(v, 'rad')]] -}; - -export const PRICE_FEED_ADDRESS = 'priceFeedAddress'; -export const RAW_LIQUIDATION_RATIO = 'rawLiquidationRatio'; - -export const spotIlks = { - generate: ilkName => ({ - id: `MCD_SPOT.ilks(${ilkName})`, - contractName: 'MCD_SPOT', - call: ['ilks(bytes32)(address,uint256)', toHex(ilkName)] - }), - returns: [PRICE_FEED_ADDRESS, [RAW_LIQUIDATION_RATIO, fromRay]] -}; - -export const LIQUIDATION_RATIO = 'liquidationRatio'; -export const liquidationRatio = { - // The liquidation ratio value is the ratio between the minimum dollar amount of a unit of - // collateral in terms of a single dollar unit amount of debt in which the system does not - // deem a vault of that collateral type (ilk) underwater - // - // In plain english, it is the ratio of the dollar amount of ETH in terms of - // the dollar amount of dai - generate: ilkName => ({ - dependencies: () => [[RAW_LIQUIDATION_RATIO, ilkName]], - computed: liqRatio => - createCurrencyRatio( - createCurrency(`(${ilkName.split('-')[0]}/USD)`), - createCurrency(`(${MDAI.symbol}/USD)`) - )(liqRatio) - }) -}; - -export const RATIO_DAI_USD = 'ratioDaiUsd'; -export const spotPar = { - generate: () => ({ - id: 'MCD_SPOT.par()', - contractName: 'MCD_SPOT', - call: ['par()(uint256)'] - }), - returns: [[RATIO_DAI_USD, v => createCurrencyRatio(MDAI, USD)(fromRay(v))]] -}; - -export const ILK_PRICE = 'ilkPrice'; -export const ilkPrice = { - generate: ilkName => ({ - dependencies: [ - [RATIO_DAI_USD], - [PRICE_WITH_SAFETY_MARGIN, ilkName], - [LIQUIDATION_RATIO, ilkName] - ], - computed: (ratioDaiUsd, priceWithSafetyMargin, liquidationRatio) => { - const currency = createCurrency( - liquidationRatio.numerator.symbol.substring(1, 4) - ); - const ratio = createCurrencyRatio(USD, currency); - const price = priceWithSafetyMargin - .times(ratioDaiUsd.toNumber()) - .times(liquidationRatio.toNumber()); - return ratio(price); - } - }) -}; - -export const ILK_PRICES = 'ilkPrices'; -export const ilkPrices = { - generate: ilkNames => ({ - dependencies: () => [...ilkNames.map(ilkName => [ILK_PRICE, ilkName])], - computed: (...prices) => prices - }) -}; - -export const UNLOCKED_COLLATERAL = 'unlockedCollateral'; -export const vatGem = { - generate: (ilkName, urn) => ({ - id: `MCD_Vat.gem(${ilkName},${urn})`, - contractName: 'MCD_VAT', - call: ['gem(bytes32,address)(uint)', toHex(ilkName), urn] - }), - return: [UNLOCKED_COLLATERAL, fromWei] -}; - -export const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; -export const ENCUMBERED_DEBT = 'encumberedDebt'; -export const urnState = { - generate: (ilkName, urn) => ({ - id: `MCD_Vat.urns(${ilkName},${urn})`, - contractName: 'MCD_VAT', - call: ['urns(bytes32,address)(uint256,uint256)', toHex(ilkName), urn] - }), - returns: [[ENCUMBERED_COLLATERAL, fromWei], [ENCUMBERED_DEBT, fromWei]] -}; - -export const VAULT_URN = 'vaultUrn'; -export const vaultUrn = { - generate: id => ({ - id: `CDP_MANAGER.urns(${id})`, - contractName: 'CDP_MANAGER', - call: ['urns(uint256)(address)', parseInt(id)] - }), - returns: [VAULT_URN] -}; - -export const VAULT_ILK = 'vaultIlk'; -export const vaultIlk = { - generate: id => ({ - id: `CDP_MANAGER.ilks(${id})`, - contractName: 'CDP_MANAGER', - call: ['ilks(uint256)(bytes32)', parseInt(id)] - }), - returns: [[VAULT_ILK, bytesToString]] -}; - -export const VAULT_ILK_AND_URN = 'vaultIlkAndUrn'; -export const vaultIlkAndUrn = { - generate: id => ({ - dependencies: [[VAULT_ILK, id], [VAULT_URN, id]], - computed: (ilk, urn) => [ilk, urn] - }) -}; - -export const URN_COLLATERAL_AND_DEBT = 'urnCollateralAndDebt'; -export const urnCollateralAndDebt = { - generate: (ilk, urn) => ({ - dependencies: [ - [ENCUMBERED_COLLATERAL, ilk, urn], - [ENCUMBERED_DEBT, ilk, urn] - ], - computed: (encumberedCollateral, encumberedDebt) => [ - encumberedCollateral, - encumberedDebt - ] - }) -}; - -export const VAULT_BY_ID = 'vaultById'; -export const vaultById = { - generate: id => ({ - dependencies: [ - [VAULT_ILK, id], - [VAULT_URN, id], - [ENCUMBERED_COLLATERAL, [VAULT_ILK, id], [VAULT_URN, id]], - [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]] - ], - computed: (ilk, urn, encumberedCollateral, encumberedDebt) => ({ - ilk, - urn, - encumberedCollateral, - encumberedDebt - }) - }) -}; - -export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; -export const DATE_STABILITY_FEES_LAST_LEVIED = 'dateStabilityFeesLastLevied'; -export const jugInfo = { - generate: ilkName => ({ - id: `MCD_JUG.ilks(${ilkName})`, - contractName: 'MCD_JUG', - call: ['ilks(bytes32)(uint256,uint48)', toHex(ilkName)] - }), - returns: [ - [ANNUAL_STABILITY_FEE, annualStabilityFee], - [DATE_STABILITY_FEES_LAST_LEVIED, val => new Date(val * 1000)] - ] -}; - -export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; -export const potPie = { - generate: () => ({ - id: `MCD_POT.Pie`, - contractName: 'MCD_POT', - call: ['Pie()(uint256)'] - }), - returns: [[TOTAL_SAVINGS_DAI, v => CHAI(v, 'wei')]] -}; - -export const SAVINGS_DAI_BY_PROXY = 'savingsDaiByProxy'; -export const potpie = { - generate: proxyAddress => ({ - id: `MCD_POT.pie(${proxyAddress})`, - contractName: 'MCD_POT', - call: ['pie(address)(uint256)', proxyAddress] - }), - returns: [[SAVINGS_DAI_BY_PROXY, v => CHAI(v, 'wei')]] -}; - -export const SAVINGS_DAI = 'savingsDai'; -export const savingsDai = { - generate: address => ({ - dependencies: [[SAVINGS_DAI_BY_PROXY, [PROXY_ADDRESS, address]]], - computed: savingsDai => savingsDai - }) -}; - -export const DAI_SAVINGS_RATE = 'daiSavingsRate'; -export const potDsr = { - generate: () => ({ - id: `MCD_POT.dsr`, - contractName: 'MCD_POT', - call: ['dsr()(uint256)'] - }), - returns: [[DAI_SAVINGS_RATE, fromRay]] -}; - -export const ANNUAL_DAI_SAVINGS_RATE = 'annualDaiSavingsRate'; -export const annualDaiSavingsRate = { - generate: () => ({ - dependencies: () => [[DAI_SAVINGS_RATE]], - computed: daiSavingsRate => - daiSavingsRate - .pow(365 * 24 * 60 * 60) - .minus(1) - .times(100) - }) -}; - -export const DATE_EARNINGS_LAST_ACCRUED = 'dateEarningsLastAccrued'; -export const potRho = { - generate: () => ({ - id: `MCD_POT.rho`, - contractName: 'MCD_POT', - call: ['rho()(uint256)'] - }), - returns: [ - [DATE_EARNINGS_LAST_ACCRUED, val => new Date(val.toNumber() * 1000)] - ] -}; - -export const LIQUIDATOR_ADDRESS = 'liquidatorAddress'; -export const LIQUIDATION_PENALTY = 'liquidationPenalty'; -export const MAX_AUCTION_LOT_SIZE = 'maxAuctionLotSize'; -export const liquidatorState = { - generate: ilkName => ({ - id: `MCD_CAT.ilks(${ilkName})`, - contractName: 'MCD_CAT', - call: ['ilks(bytes32)(address,uint256,uint256)', toHex(ilkName)] - }), - returns: [ - [LIQUIDATOR_ADDRESS], - [LIQUIDATION_PENALTY, liquidationPenalty], - [MAX_AUCTION_LOT_SIZE, v => BigNumber(v).shiftedBy(-18)] - ] -}; - -export default { - vatIlks, - proxies, - debt, - spotIlks, - spotPar, - ilkPrice, - ilkPrices, - liquidationRatio, - vatGem, - urnState, - vaultUrn, - vaultIlk, - vaultIlkAndUrn, - urnCollateralAndDebt, - vaultById, - jugInfo, - potPie, - potpie, - savingsDai, - potDsr, - annualDaiSavingsRate, - potRho, - liquidatorState -}; diff --git a/packages/dai-plugin-mcd/src/schemas/cat.js b/packages/dai-plugin-mcd/src/schemas/cat.js new file mode 100644 index 000000000..44900438f --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/cat.js @@ -0,0 +1,26 @@ +import { toHex } from '../utils'; +import BigNumber from 'bignumber.js'; +import { liquidationPenalty } from '../math'; + +import { + LIQUIDATOR_ADDRESS, + LIQUIDATION_PENALTY, + MAX_AUCTION_LOT_SIZE +} from './constants'; + +export const catIlks = { + generate: ilkName => ({ + id: `MCD_CAT.ilks(${ilkName})`, + contractName: 'MCD_CAT', + call: ['ilks(bytes32)(address,uint256,uint256)', toHex(ilkName)] + }), + returns: [ + [LIQUIDATOR_ADDRESS], + [LIQUIDATION_PENALTY, liquidationPenalty], + [MAX_AUCTION_LOT_SIZE, v => BigNumber(v).shiftedBy(-18)] + ] +}; + +export default { + catIlks +}; diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js new file mode 100644 index 000000000..b05b4a63c --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -0,0 +1,25 @@ +import { VAULT_URN, VAULT_ILK } from './constants'; +import { bytesToString } from '../utils'; + +export const cdpManagerUrns = { + generate: id => ({ + id: `CDP_MANAGER.urns(${id})`, + contractName: 'CDP_MANAGER', + call: ['urns(uint256)(address)', parseInt(id)] + }), + returns: [VAULT_URN] +}; + +export const cdpManagerIlks = { + generate: id => ({ + id: `CDP_MANAGER.ilks(${id})`, + contractName: 'CDP_MANAGER', + call: ['ilks(uint256)(bytes32)', parseInt(id)] + }), + returns: [[VAULT_ILK, bytesToString]] +}; + +export default { + cdpManagerUrns, + cdpManagerIlks +}; diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js new file mode 100644 index 000000000..39ffeb77b --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -0,0 +1,93 @@ +import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; +import { USD } from '../..'; + +import { + RATIO_DAI_USD, + LIQUIDATION_RATIO, + PRICE_WITH_SAFETY_MARGIN, + ILK_PRICE, + VAULT_ILK, + VAULT_URN, + ENCUMBERED_COLLATERAL, + ENCUMBERED_DEBT, + SAVINGS_DAI_BY_PROXY +} from './constants'; + +export const ilkPrice = { + generate: ilkName => ({ + dependencies: [ + [RATIO_DAI_USD], + [PRICE_WITH_SAFETY_MARGIN, ilkName], + [LIQUIDATION_RATIO, ilkName] + ], + computed: (ratioDaiUsd, priceWithSafetyMargin, liquidationRatio) => { + const currency = createCurrency( + liquidationRatio.numerator.symbol.substring(1, 4) + ); + const ratio = createCurrencyRatio(USD, currency); + const price = priceWithSafetyMargin + .times(ratioDaiUsd.toNumber()) + .times(liquidationRatio.toNumber()); + return ratio(price); + } + }) +}; + +export const ilkPrices = { + generate: ilkNames => ({ + dependencies: () => [...ilkNames.map(ilkName => [ILK_PRICE, ilkName])], + computed: (...prices) => prices + }) +}; + +export const vaultIlkAndUrn = { + generate: id => ({ + dependencies: [[VAULT_ILK, id], [VAULT_URN, id]], + computed: (ilk, urn) => [ilk, urn] + }) +}; + +export const urnCollateralAndDebt = { + generate: (ilk, urn) => ({ + dependencies: [ + [ENCUMBERED_COLLATERAL, ilk, urn], + [ENCUMBERED_DEBT, ilk, urn] + ], + computed: (encumberedCollateral, encumberedDebt) => [ + encumberedCollateral, + encumberedDebt + ] + }) +}; + +export const vaultById = { + generate: id => ({ + dependencies: [ + [VAULT_ILK, id], + [VAULT_URN, id], + [ENCUMBERED_COLLATERAL, [VAULT_ILK, id], [VAULT_URN, id]], + [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]] + ], + computed: (ilk, urn, encumberedCollateral, encumberedDebt) => ({ + ilk, + urn, + encumberedCollateral, + encumberedDebt + }) + }) +}; + +export const savingsDai = { + generate: address => ({ + dependencies: [[SAVINGS_DAI_BY_PROXY, [PROXY_ADDRESS, address]]], + computed: savingsDai => savingsDai + }) +}; + +export default { + ilkPrice, + ilkPrices, + vaultIlkAndUrn, + urnCollateralAndDebt, + vaultById +}; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js new file mode 100644 index 000000000..2cac0016e --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -0,0 +1,47 @@ +// vat +export const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; +export const DEBT_SCALING_FACTOR = 'debtScalingFactor'; +export const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; +export const DEBT_CEILING = 'debtCeiling'; +export const URN_DEBT_FLOOR = 'urnDebtFloor'; +export const TOTAL_DAI_SUPPLY = 'totalDaiSupply'; +export const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; +export const ENCUMBERED_DEBT = 'encumberedDebt'; +export const UNLOCKED_COLLATERAL = 'unlockedCollateral'; + +// spot +export const PRICE_FEED_ADDRESS = 'priceFeedAddress'; +export const RAW_LIQUIDATION_RATIO = 'rawLiquidationRatio'; +export const RATIO_DAI_USD = 'ratioDaiUsd'; + +// jug +export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; +export const DATE_STABILITY_FEES_LAST_LEVIED = 'dateStabilityFeesLastLevied'; + +// proxyRegistry +export const PROXY_ADDRESS = 'proxyAddress'; + +// cdpManager +export const VAULT_URN = 'vaultUrn'; +export const VAULT_ILK = 'vaultIlk'; + +// pot +export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; +export const SAVINGS_DAI_BY_PROXY = 'savingsDaiByProxy'; +export const DAI_SAVINGS_RATE = 'daiSavingsRate'; +export const ANNUAL_DAI_SAVINGS_RATE = 'annualDaiSavingsRate'; +export const DATE_EARNINGS_LAST_ACCRUED = 'dateEarningsLastAccrued'; + +// cat +export const LIQUIDATOR_ADDRESS = 'liquidatorAddress'; +export const LIQUIDATION_PENALTY = 'liquidationPenalty'; +export const MAX_AUCTION_LOT_SIZE = 'maxAuctionLotSize'; + +// computed +export const LIQUIDATION_RATIO = 'liquidationRatio'; +export const ILK_PRICE = 'ilkPrice'; +export const ILK_PRICES = 'ilkPrices'; +export const VAULT_ILK_AND_URN = 'vaultIlkAndUrn'; +export const URN_COLLATERAL_AND_DEBT = 'urnCollateralAndDebt'; +export const VAULT_BY_ID = 'vaultById'; +export const SAVINGS_DAI = 'savingsDai'; diff --git a/packages/dai-plugin-mcd/src/schemas/index.js b/packages/dai-plugin-mcd/src/schemas/index.js new file mode 100644 index 000000000..99afa5109 --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/index.js @@ -0,0 +1,59 @@ +import vat from './vat'; +import spot from './spot'; +import proxyRegistry from './proxyRegistry'; +import cdpManager from './cdpManager'; +import jug from './jug'; +import pot from './pot'; +import cat from './cat'; + +import computed from './computed'; + +export * from './constants'; +export default { + ...vat, + ...spot, + ...proxyRegistry, + ...cdpManager, + ...jug, + ...pot, + ...cat, + ...computed +}; + +/* + * Notes on structure: + * + * Base schemas, those which make a basic function call, such as vat.debt + * should be stored in a file under the name of the contract which is to + * be called from. In this case vat.js + * + * Naming of base observable schemas should follow the template of + * camelCasing the contract name and function call. So following the + * example of vat.debt, the resulting schema should be an exported + * constant, vatDebt + * + * All observable keys should be located in the constants.js file. + * For base observables, there could be 1 or more return values + * using these constant names. + * + * For computed observables, the observable keys should match the name of + * the exported constant. Using 'ilkPrices' as an example, the exported + * constant will be ilkPrices, the corresponding constant key to match it + * will be ILK_PRICES = 'ilkPrices'. + * + * In the majority of cases, computed observables should exist under + * computed.js, especially if they are intending to compose calls to multiple + * functions from multiple contracts. + * + * The exception to this is for instances where a computed observable has a + * relationship with base observable(s) from a single contract such that a + * untransformed and transformed value can be present within the api. An example + * to better illustrate this is rawLiquidationRatio which returns a number value + * and liquidationRatio which returns a currencyRatio object for that number + * value. The baseObservable return value key should be prepended with "raw" + * to strongly indicate we are getting a chain value. The computed key should + * map to what the base observable key would be named should it not exist, + * hence liquidationRatio. Both of the schemas for these should exist in the + * file for the base observable, spot.js in this case. + * + */ diff --git a/packages/dai-plugin-mcd/src/schemas/jug.js b/packages/dai-plugin-mcd/src/schemas/jug.js new file mode 100644 index 000000000..945aeca0e --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/jug.js @@ -0,0 +1,23 @@ +import { annualStabilityFee } from '../math'; +import { toHex } from '../utils'; + +import { + ANNUAL_STABILITY_FEE, + DATE_STABILITY_FEES_LAST_LEVIED +} from './constants'; + +export const jugIlks = { + generate: ilkName => ({ + id: `MCD_JUG.ilks(${ilkName})`, + contractName: 'MCD_JUG', + call: ['ilks(bytes32)(uint256,uint48)', toHex(ilkName)] + }), + returns: [ + [ANNUAL_STABILITY_FEE, annualStabilityFee], + [DATE_STABILITY_FEES_LAST_LEVIED, val => new Date(val * 1000)] + ] +}; + +export default { + jugIlks +}; diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js new file mode 100644 index 000000000..4326c3ef8 --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -0,0 +1,68 @@ +import { fromRay } from '../utils'; +import { CHAI } from '../..'; + +import { + TOTAL_SAVINGS_DAI, + SAVINGS_DAI_BY_PROXY, + DAI_SAVINGS_RATE, + DATE_EARNINGS_LAST_ACCRUED +} from './constants'; + +export const potPie = { + generate: () => ({ + id: `MCD_POT.Pie`, + contractName: 'MCD_POT', + call: ['Pie()(uint256)'] + }), + returns: [[TOTAL_SAVINGS_DAI, v => CHAI(v, 'wei')]] +}; + +export const potpie = { + generate: proxyAddress => ({ + id: `MCD_POT.pie(${proxyAddress})`, + contractName: 'MCD_POT', + call: ['pie(address)(uint256)', proxyAddress] + }), + returns: [[SAVINGS_DAI_BY_PROXY, v => CHAI(v, 'wei')]] +}; + +export const potDsr = { + generate: () => ({ + id: `MCD_POT.dsr`, + contractName: 'MCD_POT', + call: ['dsr()(uint256)'] + }), + returns: [[DAI_SAVINGS_RATE, fromRay]] +}; + +export const potRho = { + generate: () => ({ + id: `MCD_POT.rho`, + contractName: 'MCD_POT', + call: ['rho()(uint256)'] + }), + returns: [ + [DATE_EARNINGS_LAST_ACCRUED, val => new Date(val.toNumber() * 1000)] + ] +}; + +export const annualDaiSavingsRate = { + generate: () => ({ + dependencies: () => [[DAI_SAVINGS_RATE]], + computed: daiSavingsRate => + daiSavingsRate + .pow(365 * 24 * 60 * 60) + .minus(1) + .times(100) + }) +}; + +export default { + potPie, + potpie, + potDsr, + potRho, + + // computed + annualDaiSavingsRate +}; diff --git a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js new file mode 100644 index 000000000..2fff678e0 --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js @@ -0,0 +1,14 @@ +import { PROXY_ADDRESS } from './constants'; + +export const proxyRegistryProxies = { + generate: address => ({ + id: `PROXY_REGISTRY.proxies(${address})`, + contractName: 'PROXY_REGISTRY', + call: ['proxies(address)(address)', address] + }), + returns: [[PROXY_ADDRESS]] +}; + +export default { + proxyRegistryProxies +}; diff --git a/packages/dai-plugin-mcd/src/schemas/spot.js b/packages/dai-plugin-mcd/src/schemas/spot.js new file mode 100644 index 000000000..dc20fbeda --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/spot.js @@ -0,0 +1,52 @@ +import { toHex, fromRay } from '../utils'; +import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; +import { MDAI, USD } from '../..'; + +import { + PRICE_FEED_ADDRESS, + RAW_LIQUIDATION_RATIO, + RATIO_DAI_USD +} from './constants'; + +export const spotIlks = { + generate: ilkName => ({ + id: `MCD_SPOT.ilks(${ilkName})`, + contractName: 'MCD_SPOT', + call: ['ilks(bytes32)(address,uint256)', toHex(ilkName)] + }), + returns: [PRICE_FEED_ADDRESS, [RAW_LIQUIDATION_RATIO, fromRay]] +}; + +export const spotPar = { + generate: () => ({ + id: 'MCD_SPOT.par()', + contractName: 'MCD_SPOT', + call: ['par()(uint256)'] + }), + returns: [[RATIO_DAI_USD, v => createCurrencyRatio(MDAI, USD)(fromRay(v))]] +}; + +export const liquidationRatio = { + // The liquidation ratio value is the ratio between the minimum dollar amount of a unit of + // collateral in terms of a single dollar unit amount of debt in which the system does not + // deem a vault of that collateral type (ilk) underwater + // + // In plain english, it is the ratio of the dollar amount of ETH in terms of + // the dollar amount of dai + generate: ilkName => ({ + dependencies: () => [[RAW_LIQUIDATION_RATIO, ilkName]], + computed: liqRatio => + createCurrencyRatio( + createCurrency(`(${ilkName.split('-')[0]}/USD)`), + createCurrency(`(${MDAI.symbol}/USD)`) + )(liqRatio) + }) +}; + +export default { + spotIlks, + spotPar, + + // computed + liquidationRatio +}; diff --git a/packages/dai-plugin-mcd/src/schemas/vat.js b/packages/dai-plugin-mcd/src/schemas/vat.js new file mode 100644 index 000000000..5bc6d69e2 --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/vat.js @@ -0,0 +1,67 @@ +import { toHex, fromRay, fromRad, fromWei } from '../utils'; +import BigNumber from 'bignumber.js'; +import { MDAI } from '../..'; + +import { + TOTAL_ENCUMBERED_DEBT, + DEBT_SCALING_FACTOR, + PRICE_WITH_SAFETY_MARGIN, + DEBT_CEILING, + URN_DEBT_FLOOR, + TOTAL_DAI_SUPPLY, + ENCUMBERED_COLLATERAL, + ENCUMBERED_DEBT, + UNLOCKED_COLLATERAL +} from './constants'; + +export const vatIlks = { + generate: ilkName => ({ + id: `MCD_VAT.ilks(${ilkName})`, + contractName: 'MCD_VAT', + call: [ + 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', + toHex(ilkName) + ] + }), + returns: [ + [TOTAL_ENCUMBERED_DEBT, BigNumber], + [DEBT_SCALING_FACTOR, fromRay], + [PRICE_WITH_SAFETY_MARGIN, fromRay], + [DEBT_CEILING, v => MDAI(v, 'rad')], + [URN_DEBT_FLOOR, fromRad] + ] +}; + +export const vatDebt = { + generate: () => ({ + id: 'MCD_VAT.debt()', + contractName: 'MCD_VAT', + call: ['debt()(uint256)'] + }), + returns: [[TOTAL_DAI_SUPPLY, v => MDAI(v, 'rad')]] +}; + +export const vatUrns = { + generate: (ilkName, urn) => ({ + id: `MCD_Vat.urns(${ilkName},${urn})`, + contractName: 'MCD_VAT', + call: ['urns(bytes32,address)(uint256,uint256)', toHex(ilkName), urn] + }), + returns: [[ENCUMBERED_COLLATERAL, fromWei], [ENCUMBERED_DEBT, fromWei]] +}; + +export const vatGem = { + generate: (ilkName, urn) => ({ + id: `MCD_Vat.gem(${ilkName},${urn})`, + contractName: 'MCD_VAT', + call: ['gem(bytes32,address)(uint)', toHex(ilkName), urn] + }), + return: [UNLOCKED_COLLATERAL, fromWei] +}; + +export default { + vatIlks, + vatDebt, + vatUrns, + vatGem +}; diff --git a/packages/dai-plugin-mcd/test/schema.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js similarity index 98% rename from packages/dai-plugin-mcd/test/schema.spec.js rename to packages/dai-plugin-mcd/test/schemas/index.spec.js index 05e68f5e4..123230c44 100644 --- a/packages/dai-plugin-mcd/test/schema.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -1,6 +1,6 @@ /* eslint-disable */ -import { mcdMaker, setupCollateral } from './helpers'; -import { ETH, BAT, MDAI, USD } from '../src'; +import { mcdMaker, setupCollateral } from '../helpers'; +import { ETH, BAT, MDAI, USD } from '../../src'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { toHex, @@ -10,20 +10,9 @@ import { isBigNumber, isCurrency, isValidAddressString -} from '../src/utils'; -import { ServiceRoles } from '../src/constants'; +} from '../../src/utils'; +import { ServiceRoles } from '../../src/constants'; import BigNumber from 'bignumber.js'; -let mcall, - maker, - address, - watcher, - snapshotData, - cdpMgr, - cdpTypeService, - proxyService, - ethAInfo, - batAInfo, - vault; import schemas, { TOTAL_ENCUMBERED_DEBT, @@ -31,7 +20,6 @@ import schemas, { PRICE_WITH_SAFETY_MARGIN, DEBT_CEILING, URN_DEBT_FLOOR, - PROXY_ADDRESS, TOTAL_DAI_SUPPLY, PRICE_FEED_ADDRESS, RAW_LIQUIDATION_RATIO, @@ -42,12 +30,13 @@ import schemas, { UNLOCKED_COLLATERAL, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, + PROXY_ADDRESS, VAULT_URN, VAULT_ILK, VAULT_ILK_AND_URN, VAULT_BY_ID, - ANNUAL_STABILITY_FEE, DATE_STABILITY_FEES_LAST_LEVIED, + ANNUAL_STABILITY_FEE, TOTAL_SAVINGS_DAI, SAVINGS_DAI_BY_PROXY, SAVINGS_DAI, @@ -57,7 +46,19 @@ import schemas, { LIQUIDATOR_ADDRESS, LIQUIDATION_PENALTY, MAX_AUCTION_LOT_SIZE -} from '../src/schema'; +} from '../../src/schemas'; + +let mcall, + maker, + address, + watcher, + snapshotData, + cdpMgr, + cdpTypeService, + proxyService, + ethAInfo, + batAInfo, + vault; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 5f686ceae..714148dd1 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -191,7 +191,6 @@ export default class MulticallService extends PublicService { ); } else { const next = trie.shift(); - console.log(next); return recurseDependencyTree(next).pipe( flatMap(result => { return Array.isArray(result) @@ -235,7 +234,6 @@ export default class MulticallService extends PublicService { if (!this._watcherUpdates) { log2('Subscribed to watcher updates'); this._watcherUpdates = this._watcher.subscribe(update => { - // console.log('(MulticallService) Got update:', update) const subject = get(this._subjects, update.type); if (subject) subject.next(update.value); }); From 3e43307e2b23559505db930034a73c67849c7665 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 17:34:12 +0000 Subject: [PATCH 045/117] Include proxyAddress key --- packages/dai-plugin-mcd/src/schemas/computed.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 39ffeb77b..796d2491c 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -10,7 +10,8 @@ import { VAULT_URN, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, - SAVINGS_DAI_BY_PROXY + SAVINGS_DAI_BY_PROXY, + PROXY_ADDRESS } from './constants'; export const ilkPrice = { From 7f3e0a0918594a91bee54d527d5244f575282ccc Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 17:48:54 +0000 Subject: [PATCH 046/117] exporting savingsDai from computed schemas --- packages/dai-plugin-mcd/src/schemas/computed.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 796d2491c..35509fc97 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -90,5 +90,6 @@ export default { ilkPrices, vaultIlkAndUrn, urnCollateralAndDebt, - vaultById + vaultById, + savingsDai }; From b2c1b648bf7c7ee7090fec4799246de46bd90287 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 18:54:40 +0000 Subject: [PATCH 047/117] Moved proxyAddress test to own file --- .../dai-plugin-mcd/test/schemas/index.spec.js | 7 ---- .../test/schemas/proxyRegistry.spec.js | 40 +++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js index 123230c44..ab8a0c6a5 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -123,13 +123,6 @@ afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); -test(PROXY_ADDRESS, async () => { - const proxyAddress = await maker.latest(PROXY_ADDRESS, address); - - expect(isValidAddressString(proxyAddress)).toEqual(true); - expect(proxyAddress).toEqual('0x570074CCb147ea3dE2E23fB038D4d78324278886'); -}); - test(TOTAL_ENCUMBERED_DEBT, async () => { // TODO Define hardcoded rates for given ilks outside of the system and test // against those rather than data extracted from the chain diff --git a/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js b/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js new file mode 100644 index 000000000..8bae9fd29 --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js @@ -0,0 +1,40 @@ +import { mcdMaker } from '../helpers'; +import { + takeSnapshot, + restoreSnapshot, + TestAccountProvider, + mineBlocks +} from '@makerdao/test-helpers'; +import { isValidAddressString } from '../../src/utils'; + +import schemas, { PROXY_ADDRESS } from '../../src/schemas'; + +let maker, address, address2, snapshotData; +beforeAll(async () => { + maker = await mcdMaker({ multicall: true }); + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); + + address = maker.service('web3').currentAddress(); + const account = TestAccountProvider.nextAccount(); + await maker.addAccount({ ...account, type: 'privateKey' }); + maker.useAccount(account.address); + address2 = maker.service('web3').currentAddress(); + await maker.service('proxy').ensureProxy(); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(PROXY_ADDRESS, async () => { + const proxyAddress1 = await maker.latest(PROXY_ADDRESS, address); + expect(isValidAddressString(proxyAddress1)).toEqual(true); + expect(proxyAddress1).toEqual('0x570074CCb147ea3dE2E23fB038D4d78324278886'); + + const proxyAddress2 = await maker.latest(PROXY_ADDRESS, address2); + expect(isValidAddressString(proxyAddress2)).toEqual(true); + expect(proxyAddress2).toEqual('0xa029495c24518097170824CDD905351BdD938e52'); +}); From 0e2ea7c1eef15ea0f4540604424028a1c450fe46 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:22:37 +0000 Subject: [PATCH 048/117] moved tests for vat --- .../dai-plugin-mcd/test/schemas/index.spec.js | 115 ---------- .../test/schemas/proxyRegistry.spec.js | 3 +- .../dai-plugin-mcd/test/schemas/vat.spec.js | 213 ++++++++++++++++++ 3 files changed, 214 insertions(+), 117 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schemas/vat.spec.js diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js index ab8a0c6a5..51e853013 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -123,121 +123,6 @@ afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); -test(TOTAL_ENCUMBERED_DEBT, async () => { - // TODO Define hardcoded rates for given ilks outside of the system and test - // against those rather than data extracted from the chain - const { rate: ethARate } = ethAInfo; - const { rate: batARate } = batAInfo; - - const ethADebtAmount = ETH_A_DEBT_AMOUNT._amount; - const batADebtAmount = BAT_A_DEBT_AMOUNT._amount; - - const ethAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'ETH-A'); - const batAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'BAT-A'); - - expect(isBigNumber(ethAEncumberedDebt)).toEqual(true); - expect(isBigNumber(batAEncumberedDebt)).toEqual(true); - - expect(ethAEncumberedDebt).toEqual( - ethADebtAmount - .shiftedBy(18) - .div(fromRay(ethARate)) - .integerValue(0) - ); - - expect(batAEncumberedDebt).toEqual( - batADebtAmount - .shiftedBy(18) - .div(fromRay(batARate)) - .integerValue(0) - ); -}); - -test(DEBT_SCALING_FACTOR, async () => { - const { rate: ethARate } = ethAInfo; - const { rate: batARate } = batAInfo; - - const ethADebtScalingFactor = await maker.latest( - DEBT_SCALING_FACTOR, - 'ETH-A' - ); - const batADebtScalingFactor = await maker.latest( - DEBT_SCALING_FACTOR, - 'BAT-A' - ); - - expect(isBigNumber(ethADebtScalingFactor)).toEqual(true); - expect(isBigNumber(batADebtScalingFactor)).toEqual(true); - - expect(ethADebtScalingFactor).toEqual(fromRay(ethARate)); - expect(batADebtScalingFactor).toEqual(fromRay(batARate)); -}); - -test(PRICE_WITH_SAFETY_MARGIN, async () => { - const ethAPriceWithSafetyMargin = await maker.latest( - PRICE_WITH_SAFETY_MARGIN, - 'ETH-A' - ); - const batAPriceWithSafetyMargin = await maker.latest( - PRICE_WITH_SAFETY_MARGIN, - 'BAT-A' - ); - - const ethACalculatedMargin = BigNumber(ETH_A_PRICE) - .times(2) - .div(3); - const batACalculatedMargin = BigNumber(BAT_A_PRICE).div(2); - - expect(isBigNumber(ethAPriceWithSafetyMargin)).toEqual(true); - expect(isBigNumber(batAPriceWithSafetyMargin)).toEqual(true); - - expect(ethAPriceWithSafetyMargin).toEqual(ethACalculatedMargin); - expect(batAPriceWithSafetyMargin).toEqual(batACalculatedMargin); -}); - -test(DEBT_CEILING, async () => { - const ethADebtCeiling = await maker.latest(DEBT_CEILING, 'ETH-A'); - const batADebtCeiling = await maker.latest(DEBT_CEILING, 'BAT-A'); - - expect(isCurrency(ethADebtCeiling)).toEqual(true); - expect(isCurrency(batADebtCeiling)).toEqual(true); - - expect(ethADebtCeiling.isEqual(MDAI(100000))).toEqual(true); - expect(batADebtCeiling.isEqual(MDAI(5000))).toEqual(true); -}); - -test(URN_DEBT_FLOOR, async () => { - const ethAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'ETH-A'); - const batAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'BAT-A'); - - expect(isBigNumber(ethAUrnDebtFloor)).toEqual(true); - expect(isBigNumber(batAUrnDebtFloor)).toEqual(true); - - expect(ethAUrnDebtFloor).toEqual(BigNumber('0')); - expect(batAUrnDebtFloor).toEqual(BigNumber('0')); -}); - -test(TOTAL_DAI_SUPPLY, async () => { - const { Art: ethAArt, rate: ethARate } = ethAInfo; - const { Art: batAArt, rate: batARate } = batAInfo; - - const ethADaiGenerated = MDAI.rad(BigNumber(ethAArt).times(ethARate)); - const batADaiGenerated = MDAI.rad(BigNumber(batAArt).times(batARate)); - const sumOfDaiGeneratedFromIlks = ethADaiGenerated.plus(batADaiGenerated); - - const totalDaiAmount = await maker.latest(TOTAL_DAI_SUPPLY); - - expect(totalDaiAmount.symbol).toEqual('MDAI'); - expect(totalDaiAmount.isEqual(sumOfDaiGeneratedFromIlks)).toEqual(true); -}); - -test(PROXY_ADDRESS, async () => { - const proxyAddress = await maker.latest(PROXY_ADDRESS, address); - - expect(isValidAddressString(proxyAddress)).toEqual(true); - expect(proxyAddress).toEqual('0x570074CCb147ea3dE2E23fB038D4d78324278886'); -}); - test(PRICE_FEED_ADDRESS, async () => { const ethAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'ETH-A'); const batAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'BAT-A'); diff --git a/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js b/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js index 8bae9fd29..27fa7e2eb 100644 --- a/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js @@ -2,8 +2,7 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot, - TestAccountProvider, - mineBlocks + TestAccountProvider } from '@makerdao/test-helpers'; import { isValidAddressString } from '../../src/utils'; diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js new file mode 100644 index 000000000..3be3663c1 --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -0,0 +1,213 @@ +import { mcdMaker, setupCollateral } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import { ETH, BAT, MDAI } from '../../src'; +import { ServiceRoles } from '../../src/constants'; +import { fromRay, fromWei, isBigNumber, isCurrency } from '../../src/utils'; +import BigNumber from 'bignumber.js'; + +import schemas, { + TOTAL_ENCUMBERED_DEBT, + DEBT_SCALING_FACTOR, + PRICE_WITH_SAFETY_MARGIN, + DEBT_CEILING, + URN_DEBT_FLOOR, + TOTAL_DAI_SUPPLY, + ENCUMBERED_COLLATERAL, + ENCUMBERED_DEBT, + UNLOCKED_COLLATERAL +} from '../../src/schemas'; + +let maker, snapshotData, ethAInfo, batAInfo, cdpMgr, cdpTypeService; + +const ETH_A_COLLATERAL_AMOUNT = ETH(1); +const ETH_A_DEBT_AMOUNT = MDAI(1); +const ETH_A_PRICE = 180; + +const BAT_A_COLLATERAL_AMOUNT = BAT(1); +const BAT_A_DEBT_AMOUNT = MDAI(1); +const BAT_A_PRICE = 40; + +beforeAll(async () => { + maker = await mcdMaker({ + cdpTypes: [ + { currency: ETH, ilk: 'ETH-A' }, + { currency: BAT, ilk: 'BAT-A' } + ], + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); + await setupCollateral(maker, 'ETH-A', { + price: ETH_A_PRICE + }); + await setupCollateral(maker, 'BAT-A', { price: BAT_A_PRICE }); + + cdpMgr = await maker.service(ServiceRoles.CDP_MANAGER); + const dai = maker.getToken(MDAI); + const _proxyAddress = await maker.service('proxy').ensureProxy(); + await dai.approveUnlimited(_proxyAddress); + + await cdpMgr.openLockAndDraw( + 'ETH-A', + ETH_A_COLLATERAL_AMOUNT, + ETH_A_DEBT_AMOUNT + ); + await cdpMgr.openLockAndDraw( + 'BAT-A', + BAT_A_COLLATERAL_AMOUNT, + BAT_A_DEBT_AMOUNT + ); + cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); + + ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); + batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(TOTAL_ENCUMBERED_DEBT, async () => { + // TODO Define hardcoded rates for given ilks outside of the system and test + // against those rather than data extracted from the chain + const { rate: ethARate } = ethAInfo; + const { rate: batARate } = batAInfo; + + const ethADebtAmount = ETH_A_DEBT_AMOUNT._amount; + const batADebtAmount = BAT_A_DEBT_AMOUNT._amount; + + const ethAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'ETH-A'); + const batAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'BAT-A'); + + expect(isBigNumber(ethAEncumberedDebt)).toEqual(true); + expect(isBigNumber(batAEncumberedDebt)).toEqual(true); + + expect(ethAEncumberedDebt).toEqual( + ethADebtAmount + .shiftedBy(18) + .div(fromRay(ethARate)) + .integerValue(0) + ); + + expect(batAEncumberedDebt).toEqual( + batADebtAmount + .shiftedBy(18) + .div(fromRay(batARate)) + .integerValue(0) + ); +}); + +test(DEBT_SCALING_FACTOR, async () => { + const { rate: ethARate } = ethAInfo; + const { rate: batARate } = batAInfo; + + const ethADebtScalingFactor = await maker.latest( + DEBT_SCALING_FACTOR, + 'ETH-A' + ); + const batADebtScalingFactor = await maker.latest( + DEBT_SCALING_FACTOR, + 'BAT-A' + ); + + expect(isBigNumber(ethADebtScalingFactor)).toEqual(true); + expect(isBigNumber(batADebtScalingFactor)).toEqual(true); + + expect(ethADebtScalingFactor).toEqual(fromRay(ethARate)); + expect(batADebtScalingFactor).toEqual(fromRay(batARate)); +}); + +test(PRICE_WITH_SAFETY_MARGIN, async () => { + const ethAPriceWithSafetyMargin = await maker.latest( + PRICE_WITH_SAFETY_MARGIN, + 'ETH-A' + ); + const batAPriceWithSafetyMargin = await maker.latest( + PRICE_WITH_SAFETY_MARGIN, + 'BAT-A' + ); + + const ethACalculatedMargin = BigNumber(ETH_A_PRICE) + .times(2) + .div(3); + const batACalculatedMargin = BigNumber(BAT_A_PRICE).div(2); + + expect(isBigNumber(ethAPriceWithSafetyMargin)).toEqual(true); + expect(isBigNumber(batAPriceWithSafetyMargin)).toEqual(true); + + expect(ethAPriceWithSafetyMargin).toEqual(ethACalculatedMargin); + expect(batAPriceWithSafetyMargin).toEqual(batACalculatedMargin); +}); + +test(DEBT_CEILING, async () => { + const ethADebtCeiling = await maker.latest(DEBT_CEILING, 'ETH-A'); + const batADebtCeiling = await maker.latest(DEBT_CEILING, 'BAT-A'); + + expect(isCurrency(ethADebtCeiling)).toEqual(true); + expect(isCurrency(batADebtCeiling)).toEqual(true); + + expect(ethADebtCeiling.isEqual(MDAI(100000))).toEqual(true); + expect(batADebtCeiling.isEqual(MDAI(5000))).toEqual(true); +}); + +test(URN_DEBT_FLOOR, async () => { + const ethAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'ETH-A'); + const batAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'BAT-A'); + + expect(isBigNumber(ethAUrnDebtFloor)).toEqual(true); + expect(isBigNumber(batAUrnDebtFloor)).toEqual(true); + + expect(ethAUrnDebtFloor).toEqual(BigNumber('0')); + expect(batAUrnDebtFloor).toEqual(BigNumber('0')); +}); + +test(TOTAL_DAI_SUPPLY, async () => { + const { Art: ethAArt, rate: ethARate } = ethAInfo; + const { Art: batAArt, rate: batARate } = batAInfo; + + const ethADaiGenerated = MDAI.rad(BigNumber(ethAArt).times(ethARate)); + const batADaiGenerated = MDAI.rad(BigNumber(batAArt).times(batARate)); + const sumOfDaiGeneratedFromIlks = ethADaiGenerated.plus(batADaiGenerated); + + const totalDaiAmount = await maker.latest(TOTAL_DAI_SUPPLY); + + expect(totalDaiAmount.symbol).toEqual('MDAI'); + expect(totalDaiAmount.isEqual(sumOfDaiGeneratedFromIlks)).toEqual(true); +}); + +test(ENCUMBERED_COLLATERAL, async () => { + const cdpId = 1; + const expected = fromWei(1000000000000000000); + const encumberedCollateral = await maker.latest( + ENCUMBERED_COLLATERAL, + 'ETH-A', + await cdpMgr.getUrn(cdpId) + ); + expect(encumberedCollateral).toEqual(expected); +}); + +test(ENCUMBERED_DEBT, async () => { + const cdpId = 1; + const expected = fromWei(995000000000000000); + const encumberedDebt = await maker.latest( + ENCUMBERED_DEBT, + 'ETH-A', + await cdpMgr.getUrn(cdpId) + ); + expect(encumberedDebt.toNumber()).toBeCloseTo(expected.toNumber()); +}); + +test(UNLOCKED_COLLATERAL, async () => { + const cdpId = 1; + const expected = 0; + const col = await maker.latest( + UNLOCKED_COLLATERAL, + 'ETH-A', + await cdpMgr.getUrn(cdpId) + ); + + expect(col).toEqual(fromWei(expected)); +}); From 50f52decbb59dfe3a72025ac0daa6074c42a8d24 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:35:42 +0000 Subject: [PATCH 049/117] moved tests for pot --- packages/dai-plugin-mcd/src/schemas/pot.js | 6 +- .../dai-plugin-mcd/test/schemas/index.spec.js | 93 ++----------------- .../dai-plugin-mcd/test/schemas/pot.spec.js | 92 ++++++++++++++++++ 3 files changed, 105 insertions(+), 86 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schemas/pot.spec.js diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js index 4326c3ef8..5f3d80ba1 100644 --- a/packages/dai-plugin-mcd/src/schemas/pot.js +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -10,7 +10,7 @@ import { export const potPie = { generate: () => ({ - id: `MCD_POT.Pie`, + id: 'MCD_POT.Pie', contractName: 'MCD_POT', call: ['Pie()(uint256)'] }), @@ -28,7 +28,7 @@ export const potpie = { export const potDsr = { generate: () => ({ - id: `MCD_POT.dsr`, + id: 'MCD_POT.dsr', contractName: 'MCD_POT', call: ['dsr()(uint256)'] }), @@ -37,7 +37,7 @@ export const potDsr = { export const potRho = { generate: () => ({ - id: `MCD_POT.rho`, + id: 'MCD_POT.rho', contractName: 'MCD_POT', call: ['rho()(uint256)'] }), diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js index 51e853013..9391863d9 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -191,40 +191,6 @@ test(ILK_PRICES, async () => { expect(batAPrice.symbol).toEqual('USD/BAT'); }); -test(UNLOCKED_COLLATERAL, async () => { - const cdpId = 1; - const expected = 0; - const col = await maker.latest( - UNLOCKED_COLLATERAL, - 'ETH-A', - await cdpMgr.getUrn(cdpId) - ); - - expect(col).toEqual(fromWei(expected)); -}); - -test(ENCUMBERED_COLLATERAL, async () => { - const cdpId = 1; - const expected = fromWei(1000000000000000000); - const encumberedCollateral = await maker.latest( - ENCUMBERED_COLLATERAL, - 'ETH-A', - await cdpMgr.getUrn(cdpId) - ); - expect(encumberedCollateral).toEqual(expected); -}); - -test(ENCUMBERED_DEBT, async () => { - const cdpId = 1; - const expected = fromWei(995000000000000000); - const encumberedDebt = await maker.latest( - ENCUMBERED_DEBT, - 'ETH-A', - await cdpMgr.getUrn(cdpId) - ); - expect(encumberedDebt.toNumber()).toBeCloseTo(expected.toNumber()); -}); - test(VAULT_URN, async () => { const cdpId = 1; const expected = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; @@ -265,6 +231,16 @@ test(VAULT_BY_ID, async () => { expect(encumberedDebt.toNumber()).toBeCloseTo(expectedArt.toNumber()); }); +test( + SAVINGS_DAI, + async () => { + const savingsDai = await maker.latest(SAVINGS_DAI, address); + expect(savingsDai.symbol).toEqual('CHAI'); + expect(savingsDai.toNumber()).toBeCloseTo(0.99995); + }, + 10000 +); + test(ANNUAL_STABILITY_FEE, async () => { const expected = 0.04999999999989363; const annualStabilityFee = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); @@ -282,55 +258,6 @@ test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); }); -test(TOTAL_SAVINGS_DAI, async () => { - const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); - expect(totalSavingsDai.symbol).toEqual('CHAI'); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); -}); - -test(SAVINGS_DAI_BY_PROXY, async () => { - const savingsDaiByProxy = await maker.latest( - SAVINGS_DAI_BY_PROXY, - await proxyService.getProxyAddress() - ); - expect(savingsDaiByProxy.symbol).toEqual('CHAI'); - expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); -}); - -test( - SAVINGS_DAI, - async () => { - const savingsDai = await maker.latest(SAVINGS_DAI, address); - expect(savingsDai.symbol).toEqual('CHAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.99995); - }, - 10000 -); - -test(DAI_SAVINGS_RATE, async () => { - const daiSavingsRate = await maker.latest(DAI_SAVINGS_RATE); - expect(daiSavingsRate).toEqual(BigNumber('1.000000000315522921573372069')); -}); - -test(ANNUAL_DAI_SAVINGS_RATE, async () => { - const annualDaiSavingsRate = await maker.latest(ANNUAL_DAI_SAVINGS_RATE); - expect(annualDaiSavingsRate).toEqual( - BigNumber( - '0.999999999999999998903600959584714938425430352632298919434159277685511322388082342817131189583694' - ) - ); -}); - -test(DATE_EARNINGS_LAST_ACCRUED, async () => { - const timestamp = Math.round(new Date().getTime() / 1000); - const dateEarningsLastAccrued = await maker.latest( - DATE_EARNINGS_LAST_ACCRUED - ); - - expect(dateEarningsLastAccrued instanceof Date).toEqual(true); - expect(timestamp - dateEarningsLastAccrued).toBeLessThanOrEqual(10); -}); - test(LIQUIDATOR_ADDRESS, async () => { const expected = '0x55320248dC50Ef6dABc88ECbc294Fd5e2e1f4eC6'; const address = await maker.latest(LIQUIDATOR_ADDRESS, 'ETH-A'); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js new file mode 100644 index 000000000..f617b52ce --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -0,0 +1,92 @@ +import { mcdMaker, setupCollateral } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import { ETH, MDAI } from '../../src'; +import { ServiceRoles } from '../../src/constants'; +import BigNumber from 'bignumber.js'; + +import schemas, { + TOTAL_SAVINGS_DAI, + SAVINGS_DAI_BY_PROXY, + DAI_SAVINGS_RATE, + ANNUAL_DAI_SAVINGS_RATE, + DATE_EARNINGS_LAST_ACCRUED +} from '../../src/schemas'; + +let maker, snapshotData, cdpMgr, saveService; + +const ETH_A_COLLATERAL_AMOUNT = ETH(1); +const ETH_A_DEBT_AMOUNT = MDAI(1); +const ETH_A_PRICE = 180; + +beforeAll(async () => { + maker = await mcdMaker({ + cdpTypes: [{ currency: ETH, ilk: 'ETH-A' }], + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); + await setupCollateral(maker, 'ETH-A', { + price: ETH_A_PRICE + }); + + cdpMgr = await maker.service(ServiceRoles.CDP_MANAGER); + saveService = await maker.service(ServiceRoles.SAVINGS); + + const dai = maker.getToken(MDAI); + const _proxyAddress = await maker.service('proxy').ensureProxy(); + await dai.approveUnlimited(_proxyAddress); + + await cdpMgr.openLockAndDraw( + 'ETH-A', + ETH_A_COLLATERAL_AMOUNT, + ETH_A_DEBT_AMOUNT + ); + + await saveService.join(MDAI(1)); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(TOTAL_SAVINGS_DAI, async () => { + const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); + expect(totalSavingsDai.symbol).toEqual('CHAI'); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); +}); + +test(SAVINGS_DAI_BY_PROXY, async () => { + const savingsDaiByProxy = await maker.latest( + SAVINGS_DAI_BY_PROXY, + await maker.service('proxy').getProxyAddress() + ); + expect(savingsDaiByProxy.symbol).toEqual('CHAI'); + expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); +}); + +test(DAI_SAVINGS_RATE, async () => { + const daiSavingsRate = await maker.latest(DAI_SAVINGS_RATE); + expect(daiSavingsRate).toEqual(BigNumber('1.000000000315522921573372069')); +}); + +test(ANNUAL_DAI_SAVINGS_RATE, async () => { + const annualDaiSavingsRate = await maker.latest(ANNUAL_DAI_SAVINGS_RATE); + expect(annualDaiSavingsRate).toEqual( + BigNumber( + '0.999999999999999998903600959584714938425430352632298919434159277685511322388082342817131189583694' + ) + ); +}); + +test(DATE_EARNINGS_LAST_ACCRUED, async () => { + const timestamp = Math.round(new Date().getTime() / 1000); + const dateEarningsLastAccrued = await maker.latest( + DATE_EARNINGS_LAST_ACCRUED + ); + + expect(dateEarningsLastAccrued instanceof Date).toEqual(true); + expect(timestamp - dateEarningsLastAccrued).toBeLessThanOrEqual(10); +}); From 69e679e02eeae28264f3ae765313ca8335c009ca Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:40:53 +0000 Subject: [PATCH 050/117] moved tests for spot --- .../dai-plugin-mcd/test/schemas/spot.spec.js | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 packages/dai-plugin-mcd/test/schemas/spot.spec.js diff --git a/packages/dai-plugin-mcd/test/schemas/spot.spec.js b/packages/dai-plugin-mcd/test/schemas/spot.spec.js new file mode 100644 index 000000000..d7430a33e --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/spot.spec.js @@ -0,0 +1,74 @@ +import { mcdMaker } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import BigNumber from 'bignumber.js'; +import { isValidAddressString } from '../../src/utils'; + +import schemas, { + PRICE_FEED_ADDRESS, + RAW_LIQUIDATION_RATIO, + LIQUIDATION_RATIO, + RATIO_DAI_USD +} from '../../src/schemas'; + +let maker, snapshotData; + +beforeAll(async () => { + maker = await mcdMaker({ + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(PRICE_FEED_ADDRESS, async () => { + const ethAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'ETH-A'); + const batAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'BAT-A'); + + expect(isValidAddressString(ethAPriceFeedAddress)).toEqual(true); + expect(isValidAddressString(batAPriceFeedAddress)).toEqual(true); + + expect(ethAPriceFeedAddress).toEqual( + '0xb0ae8c0856259C6fe000F8e2C14507E5FC167D48' + ); + expect(batAPriceFeedAddress).toEqual( + '0x80f178c7b47cb635Ceb12aBB891338744e98365C' + ); +}); + +test(RAW_LIQUIDATION_RATIO, async () => { + const ethARawLiquidationRatio = await maker.latest( + RAW_LIQUIDATION_RATIO, + 'ETH-A' + ); + const batARawLiquidationRatio = await maker.latest( + RAW_LIQUIDATION_RATIO, + 'BAT-A' + ); + + expect(ethARawLiquidationRatio).toEqual(BigNumber('1.5')); + expect(batARawLiquidationRatio).toEqual(BigNumber('2.0')); +}); + +test(LIQUIDATION_RATIO, async () => { + const ethALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'ETH-A'); + const batALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'BAT-A'); + + expect(ethALiquidationRatio.symbol).toEqual('(ETH/USD)/(MDAI/USD)'); + expect(batALiquidationRatio.symbol).toEqual('(BAT/USD)/(MDAI/USD)'); + + expect(ethALiquidationRatio.toNumber()).toEqual(1.5); + expect(batALiquidationRatio.toNumber()).toEqual(2.0); +}); + +test(RATIO_DAI_USD, async () => { + const ratio = await maker.latest(RATIO_DAI_USD); + expect(ratio.symbol).toEqual('MDAI/USD'); + expect(ratio.toNumber()).toEqual(1); +}); From 04b739604fbfcd9338e70baffb4fb922030c4800 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:46:17 +0000 Subject: [PATCH 051/117] moved tests for cdpManager --- .../test/schemas/cdpManager.spec.js | 59 ++++++++++++++++++ .../dai-plugin-mcd/test/schemas/index.spec.js | 60 ------------------- 2 files changed, 59 insertions(+), 60 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js new file mode 100644 index 000000000..c0449df7e --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -0,0 +1,59 @@ +import { mcdMaker, setupCollateral } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import { ETH, BAT, MDAI } from '../../src'; +import { ServiceRoles } from '../../src/constants'; + +import schemas, { VAULT_URN, VAULT_ILK } from '../../src/schemas'; + +let maker, snapshotData, cdpMgr; + +const ETH_A_COLLATERAL_AMOUNT = ETH(1); +const ETH_A_DEBT_AMOUNT = MDAI(1); +const ETH_A_PRICE = 180; + +beforeAll(async () => { + maker = await mcdMaker({ + cdpTypes: [ + { currency: ETH, ilk: 'ETH-A' }, + { currency: BAT, ilk: 'BAT-A' } + ], + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); + await setupCollateral(maker, 'ETH-A', { + price: ETH_A_PRICE + }); + + cdpMgr = await maker.service(ServiceRoles.CDP_MANAGER); + const dai = maker.getToken(MDAI); + const _proxyAddress = await maker.service('proxy').ensureProxy(); + await dai.approveUnlimited(_proxyAddress); + + await cdpMgr.openLockAndDraw( + 'ETH-A', + ETH_A_COLLATERAL_AMOUNT, + ETH_A_DEBT_AMOUNT + ); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(VAULT_URN, async () => { + const cdpId = 1; + const expected = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; + const urn = await maker.latest(VAULT_URN, cdpId); + expect(urn).toEqual(expected); +}); + +test(VAULT_ILK, async () => { + const cdpId = 1; + const expected = 'ETH-A'; + const ilk = await maker.latest(VAULT_ILK, cdpId); + expect(ilk).toEqual(expected); +}); diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js index 9391863d9..43abde4b7 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -123,52 +123,6 @@ afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); -test(PRICE_FEED_ADDRESS, async () => { - const ethAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'ETH-A'); - const batAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'BAT-A'); - - expect(isValidAddressString(ethAPriceFeedAddress)).toEqual(true); - expect(isValidAddressString(batAPriceFeedAddress)).toEqual(true); - - expect(ethAPriceFeedAddress).toEqual( - '0xb0ae8c0856259C6fe000F8e2C14507E5FC167D48' - ); - expect(batAPriceFeedAddress).toEqual( - '0x80f178c7b47cb635Ceb12aBB891338744e98365C' - ); -}); - -test(RAW_LIQUIDATION_RATIO, async () => { - const ethARawLiquidationRatio = await maker.latest( - RAW_LIQUIDATION_RATIO, - 'ETH-A' - ); - const batARawLiquidationRatio = await maker.latest( - RAW_LIQUIDATION_RATIO, - 'BAT-A' - ); - - expect(ethARawLiquidationRatio).toEqual(BigNumber('1.5')); - expect(batARawLiquidationRatio).toEqual(BigNumber('2.0')); -}); - -test(LIQUIDATION_RATIO, async () => { - const ethALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'ETH-A'); - const batALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'BAT-A'); - - expect(ethALiquidationRatio.symbol).toEqual('(ETH/USD)/(MDAI/USD)'); - expect(batALiquidationRatio.symbol).toEqual('(BAT/USD)/(MDAI/USD)'); - - expect(ethALiquidationRatio.toNumber()).toEqual(1.5); - expect(batALiquidationRatio.toNumber()).toEqual(2.0); -}); - -test(RATIO_DAI_USD, async () => { - const ratio = await maker.latest(RATIO_DAI_USD); - expect(ratio.symbol).toEqual('MDAI/USD'); - expect(ratio.toNumber()).toEqual(1); -}); - test(ILK_PRICE, async () => { const ethAPrice = await maker.latest(ILK_PRICE, 'ETH-A'); expect(ethAPrice.toNumber()).toEqual(180); @@ -191,20 +145,6 @@ test(ILK_PRICES, async () => { expect(batAPrice.symbol).toEqual('USD/BAT'); }); -test(VAULT_URN, async () => { - const cdpId = 1; - const expected = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; - const urn = await maker.latest(VAULT_URN, cdpId); - expect(urn).toEqual(expected); -}); - -test(VAULT_ILK, async () => { - const cdpId = 1; - const expected = 'ETH-A'; - const ilk = await maker.latest(VAULT_ILK, cdpId); - expect(ilk).toEqual(expected); -}); - test(VAULT_ILK_AND_URN, async () => { const cdpId = 1; const expectedIlk = 'ETH-A'; From c3dd67aeef0bdb7550d526da00bfc3b9314ca53e Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:49:17 +0000 Subject: [PATCH 052/117] moved tests for cat --- .../dai-plugin-mcd/test/schemas/cat.spec.js | 44 +++++++++++++++++++ .../dai-plugin-mcd/test/schemas/index.spec.js | 18 -------- 2 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schemas/cat.spec.js diff --git a/packages/dai-plugin-mcd/test/schemas/cat.spec.js b/packages/dai-plugin-mcd/test/schemas/cat.spec.js new file mode 100644 index 000000000..afb4c7d05 --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/cat.spec.js @@ -0,0 +1,44 @@ +import { mcdMaker } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import BigNumber from 'bignumber.js'; + +import schemas, { + LIQUIDATOR_ADDRESS, + LIQUIDATION_PENALTY, + MAX_AUCTION_LOT_SIZE +} from '../../src/schemas'; + +let maker, snapshotData; + +beforeAll(async () => { + maker = await mcdMaker({ + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(LIQUIDATOR_ADDRESS, async () => { + const expected = '0x55320248dC50Ef6dABc88ECbc294Fd5e2e1f4eC6'; + const address = await maker.latest(LIQUIDATOR_ADDRESS, 'ETH-A'); + expect(address).toEqual(expected); +}); + +test(LIQUIDATION_PENALTY, async () => { + const expected = 0.05; + const liquidationPenalty = await maker.latest(LIQUIDATION_PENALTY, 'ETH-A'); + expect(liquidationPenalty).toEqual(expected); +}); + +test(MAX_AUCTION_LOT_SIZE, async () => { + const expected = BigNumber('1.5'); + const maxLotSize = await maker.latest(MAX_AUCTION_LOT_SIZE, 'ETH-A'); + expect(maxLotSize).toEqual(expected); +}); diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js index 43abde4b7..65cf18250 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -197,21 +197,3 @@ test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { expect(dateStabilityFeesLastLevied instanceof Date).toEqual(true); expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); }); - -test(LIQUIDATOR_ADDRESS, async () => { - const expected = '0x55320248dC50Ef6dABc88ECbc294Fd5e2e1f4eC6'; - const address = await maker.latest(LIQUIDATOR_ADDRESS, 'ETH-A'); - expect(address).toEqual(expected); -}); - -test(LIQUIDATION_PENALTY, async () => { - const expected = 0.05; - const liquidationPenalty = await maker.latest(LIQUIDATION_PENALTY, 'ETH-A'); - expect(liquidationPenalty).toEqual(expected); -}); - -test(MAX_AUCTION_LOT_SIZE, async () => { - const expected = BigNumber('1.5'); - const maxLotSize = await maker.latest(MAX_AUCTION_LOT_SIZE, 'ETH-A'); - expect(maxLotSize).toEqual(expected); -}); From 512dec6856f20ae1f6486336a5075395c0f5255c Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:52:29 +0000 Subject: [PATCH 053/117] moved tests for jug --- .../dai-plugin-mcd/test/schemas/index.spec.js | 36 ---------------- .../dai-plugin-mcd/test/schemas/jug.spec.js | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 36 deletions(-) create mode 100644 packages/dai-plugin-mcd/test/schemas/jug.spec.js diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/index.spec.js index 65cf18250..baaa84b05 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/index.spec.js @@ -15,24 +15,8 @@ import { ServiceRoles } from '../../src/constants'; import BigNumber from 'bignumber.js'; import schemas, { - TOTAL_ENCUMBERED_DEBT, - DEBT_SCALING_FACTOR, - PRICE_WITH_SAFETY_MARGIN, - DEBT_CEILING, - URN_DEBT_FLOOR, - TOTAL_DAI_SUPPLY, - PRICE_FEED_ADDRESS, - RAW_LIQUIDATION_RATIO, - RATIO_DAI_USD, - LIQUIDATION_RATIO, ILK_PRICE, ILK_PRICES, - UNLOCKED_COLLATERAL, - ENCUMBERED_COLLATERAL, - ENCUMBERED_DEBT, - PROXY_ADDRESS, - VAULT_URN, - VAULT_ILK, VAULT_ILK_AND_URN, VAULT_BY_ID, DATE_STABILITY_FEES_LAST_LEVIED, @@ -114,9 +98,6 @@ beforeAll(async () => { cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); proxyService = maker.service('proxy'); vault = await setupFn(); - - ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); - batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); }); afterAll(async () => { @@ -180,20 +161,3 @@ test( }, 10000 ); - -test(ANNUAL_STABILITY_FEE, async () => { - const expected = 0.04999999999989363; - const annualStabilityFee = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); - expect(annualStabilityFee).toEqual(expected); -}); - -test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { - var timestamp = Math.round(new Date().getTime() / 1000); - const dateStabilityFeesLastLevied = await maker.latest( - DATE_STABILITY_FEES_LAST_LEVIED, - 'ETH-A' - ); - - expect(dateStabilityFeesLastLevied instanceof Date).toEqual(true); - expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); -}); diff --git a/packages/dai-plugin-mcd/test/schemas/jug.spec.js b/packages/dai-plugin-mcd/test/schemas/jug.spec.js new file mode 100644 index 000000000..ceb064618 --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/jug.spec.js @@ -0,0 +1,41 @@ +import { mcdMaker } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; + +import schemas, { + ANNUAL_STABILITY_FEE, + DATE_STABILITY_FEES_LAST_LEVIED +} from '../../src/schemas'; + +let maker, snapshotData; + +beforeAll(async () => { + maker = await mcdMaker({ + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(ANNUAL_STABILITY_FEE, async () => { + const expected = 0.04999999999989363; + const annualStabilityFee = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); + expect(annualStabilityFee).toEqual(expected); +}); + +test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { + var timestamp = Math.round(new Date().getTime() / 1000); + const dateStabilityFeesLastLevied = await maker.latest( + DATE_STABILITY_FEES_LAST_LEVIED, + 'ETH-A' + ); + + expect(dateStabilityFeesLastLevied instanceof Date).toEqual(true); + expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); +}); From ecadf86766fd471131951850ef6405b7edec096d Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 19:59:27 +0000 Subject: [PATCH 054/117] moved tests for computed --- .../{index.spec.js => computed.spec.js} | 90 +++++-------------- 1 file changed, 24 insertions(+), 66 deletions(-) rename packages/dai-plugin-mcd/test/schemas/{index.spec.js => computed.spec.js} (71%) diff --git a/packages/dai-plugin-mcd/test/schemas/index.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js similarity index 71% rename from packages/dai-plugin-mcd/test/schemas/index.spec.js rename to packages/dai-plugin-mcd/test/schemas/computed.spec.js index baaa84b05..956911daa 100644 --- a/packages/dai-plugin-mcd/test/schemas/index.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -1,48 +1,18 @@ -/* eslint-disable */ import { mcdMaker, setupCollateral } from '../helpers'; -import { ETH, BAT, MDAI, USD } from '../../src'; +import { ETH, BAT, MDAI } from '../../src'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import { - toHex, - fromRad, - fromWei, - fromRay, - isBigNumber, - isCurrency, - isValidAddressString -} from '../../src/utils'; +import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; -import BigNumber from 'bignumber.js'; import schemas, { ILK_PRICE, ILK_PRICES, VAULT_ILK_AND_URN, VAULT_BY_ID, - DATE_STABILITY_FEES_LAST_LEVIED, - ANNUAL_STABILITY_FEE, - TOTAL_SAVINGS_DAI, - SAVINGS_DAI_BY_PROXY, - SAVINGS_DAI, - DAI_SAVINGS_RATE, - ANNUAL_DAI_SAVINGS_RATE, - DATE_EARNINGS_LAST_ACCRUED, - LIQUIDATOR_ADDRESS, - LIQUIDATION_PENALTY, - MAX_AUCTION_LOT_SIZE + SAVINGS_DAI } from '../../src/schemas'; -let mcall, - maker, - address, - watcher, - snapshotData, - cdpMgr, - cdpTypeService, - proxyService, - ethAInfo, - batAInfo, - vault; +let maker, address, snapshotData; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -52,7 +22,21 @@ const BAT_A_COLLATERAL_AMOUNT = BAT(1); const BAT_A_DEBT_AMOUNT = MDAI(1); const BAT_A_PRICE = 40; -const setupFn = async () => { +beforeAll(async () => { + snapshotData = await takeSnapshot(maker); + maker = await mcdMaker({ + cdpTypes: [ + { currency: ETH, ilk: 'ETH-A' }, + { currency: BAT, ilk: 'BAT-A' } + ], + multicall: true + }); + + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').start(); + + address = maker.service('web3').currentAddress(); await setupCollateral(maker, 'ETH-A', { price: ETH_A_PRICE }); @@ -76,28 +60,6 @@ const setupFn = async () => { ); await sav.join(MDAI(1)); -}; - -beforeAll(async () => { - snapshotData = await takeSnapshot(maker); - maker = await mcdMaker({ - cdpTypes: [ - { currency: ETH, ilk: 'ETH-A' }, - { currency: BAT, ilk: 'BAT-A' } - ], - multicall: true - }); - - mcall = maker.service('multicall'); - watcher = mcall.createWatcher({ interval: 'block' }); - mcall.registerSchemas(schemas); - mcall.start(); - - address = maker.service('web3').currentAddress(); - cdpMgr = maker.service(ServiceRoles.CDP_MANAGER); - cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); - proxyService = maker.service('proxy'); - vault = await setupFn(); }); afterAll(async () => { @@ -152,12 +114,8 @@ test(VAULT_BY_ID, async () => { expect(encumberedDebt.toNumber()).toBeCloseTo(expectedArt.toNumber()); }); -test( - SAVINGS_DAI, - async () => { - const savingsDai = await maker.latest(SAVINGS_DAI, address); - expect(savingsDai.symbol).toEqual('CHAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.99995); - }, - 10000 -); +test(SAVINGS_DAI, async () => { + const savingsDai = await maker.latest(SAVINGS_DAI, address); + expect(savingsDai.symbol).toEqual('CHAI'); + expect(savingsDai.toNumber()).toBeCloseTo(0.99995); +}); From d2d11d2868e9a7920778861abf945ab92a640152 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Mon, 20 Jan 2020 22:22:48 +0000 Subject: [PATCH 055/117] Remove .prettierignore and linting --- .prettierignore | 5 ----- packages/dai/test/eth/MulticallService.spec.js | 2 -- 2 files changed, 7 deletions(-) delete mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index a2342e6f5..000000000 --- a/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -packages/dai/src/eth/MulticallService.js -packages/dai/test/eth/MulticallService.spec.js -packages/dai/test/helpers/schemas.js -packages/dai-plugin-mcd/src/schema.js -packages/dai-plugin-mcd/test/schema.spec.js diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index 976278729..9477d98b3 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -8,12 +8,10 @@ import { first } from 'rxjs/operators'; let service; let watcher; let address; -let addressWithProxy; beforeEach(async () => { service = buildTestMulticallService(); await service.manager().authenticate(); - addressWithProxy = service.get('web3').currentAddress(); address = TestAccountProvider.nextAddress(); watcher = service.createWatcher({ interval: 'block' }); service.registerSchemas(schemas); From 171dfa2a31b2d3ea084107cbb93ff0d303be3508 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Tue, 21 Jan 2020 13:47:53 +0800 Subject: [PATCH 056/117] Fix CHAI import path --- packages/dai-plugin-mcd/src/schemas/pot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js index 5f3d80ba1..9f5ec2599 100644 --- a/packages/dai-plugin-mcd/src/schemas/pot.js +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -1,5 +1,5 @@ import { fromRay } from '../utils'; -import { CHAI } from '../..'; +import { CHAI } from '..'; import { TOTAL_SAVINGS_DAI, From 881084f6dfa126579dcc5c50028e87250ffcdb2c Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 22 Jan 2020 11:09:36 +0800 Subject: [PATCH 057/117] Add multicall result cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save multicall results to a cache when a subject for a base observable doesn’t exist yet so it can be emitted once there’s an initial subscriber for it --- packages/dai/src/eth/MulticallService.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 714148dd1..31413aada 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -19,6 +19,7 @@ export default class MulticallService extends PublicService { this._observables = {}; this._watcherUpdates = null; this._watchingSchemasTotal = 0; + this._multicallResultCache = {}; this._addresses = {}; } @@ -227,6 +228,8 @@ export default class MulticallService extends PublicService { // This is a base observable const subject = new ReplaySubject(1); set(this._subjects, fullPath, subject); + if (this._multicallResultCache[fullPath]) + subject.next(this._multicallResultCache[fullPath]); // Create base observable const observable = Observable.create(observer => { @@ -236,6 +239,7 @@ export default class MulticallService extends PublicService { this._watcherUpdates = this._watcher.subscribe(update => { const subject = get(this._subjects, update.type); if (subject) subject.next(update.value); + else this._multicallResultCache[update.type] = update.value; }); } this._watchingSchemasTotal++; @@ -246,6 +250,7 @@ export default class MulticallService extends PublicService { // Tap multicall to add schema (first subscriber to this schema) this._tapMulticallWithSchema(schema, generatedSchema, path); } else schema.watching[path]++; + log2( `Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}` ); From fd6f9ef743e3c9f716906ee9e5b7ee6b92a06e31 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 22 Jan 2020 13:12:21 +0800 Subject: [PATCH 058/117] Check sufficient args are passed to watchObservable --- packages/dai/src/eth/MulticallService.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 31413aada..cbef3beff 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -145,6 +145,13 @@ export default class MulticallService extends PublicService { const schema = this.schemaByObservableKey(key); if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); + if (args.length < schema.generate.length) + throw new Error( + `Observable ${key} expects at least ${schema.generate.length} argument${ + schema.generate.length > 1 ? 's' : '' + }` + ); + const generatedSchema = schema.generate(...args); log2( `watchObservable() called for ${ @@ -267,10 +274,9 @@ export default class MulticallService extends PublicService { schema.watching[path]--; log2('Schema subscribers:', schema.watching[path]); if (schema.watching[path] === 0) { - // Tap multicall to remove schema (last unsubscriber to this schema) + // Tap multicall to remove schema (last unsubscriber from this schema) log2(`Schema removed from multicall: ${generatedSchema.id}`); this._watcher.tap(schemas => - // TODO: make backwards compatible by checking undefined schemas.filter(({ id }) => id !== generatedSchema.id) ); this._watcher.tap(s => { From 57f3c92a406f0dd6306cd9c310a2cdfdc00f138a Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 22 Jan 2020 16:53:48 +0800 Subject: [PATCH 059/117] Move and refactor watch alias type definitions --- packages/dai-plugin-mcd/package.json | 3 +- .../dai-plugin-mcd/types/multicall/index.d.ts | 124 ------------------ packages/dai-plugin-mcd/typings/index.d.ts | 1 + .../dai-plugin-mcd/typings/multicall.d.ts | 117 +++++++++++++++++ 4 files changed, 120 insertions(+), 125 deletions(-) delete mode 100644 packages/dai-plugin-mcd/types/multicall/index.d.ts create mode 100644 packages/dai-plugin-mcd/typings/index.d.ts create mode 100644 packages/dai-plugin-mcd/typings/multicall.d.ts diff --git a/packages/dai-plugin-mcd/package.json b/packages/dai-plugin-mcd/package.json index c13aea321..44b9ab2a0 100644 --- a/packages/dai-plugin-mcd/package.json +++ b/packages/dai-plugin-mcd/package.json @@ -21,5 +21,6 @@ "@makerdao/services-core": "^0.9.9", "bignumber.js": "^8.1.1", "rxjs": "^6.5.4" - } + }, + "types": "typings/index.d.ts" } diff --git a/packages/dai-plugin-mcd/types/multicall/index.d.ts b/packages/dai-plugin-mcd/types/multicall/index.d.ts deleted file mode 100644 index 22a379df4..000000000 --- a/packages/dai-plugin-mcd/types/multicall/index.d.ts +++ /dev/null @@ -1,124 +0,0 @@ -interface Currency {} -interface BigNumber {} - -interface WatchInterface { - - /** Watch the total encumbered debt of a collateral type - * @param ilkName String uniquely identifying an ilk - */ - totalEncumberedDebt( - ilkName: string - ): Currency; - - /** - * @param ilkName String uniquely identifying an ilk - */ - debtScalingFactor( - ilkName: string - ): BigNumber; - - /** - * @param ilkName String uniquely identifying an ilk - */ - priceWithSafetyMargin( - ilkName: string - ): BigNumber; - - /** - * @param ilkName String uniquely identifying an ilk - */ - debtCeiling( - ilkName: string - ): Currency; - - /** - * @param ilkName String uniquely identifying an ilk - */ - urnDebtFloor( - ilkName: string - ): BigNumber; - - /** Watch the price of an ilk - * @param ilkName String uniquely identifying an ilk - */ - ilkPrice( - ilkName: string - ): Currency; - - /** Watch the prices of a list of ilks - * @param ilkName Array of strings uniquely identifying ilks - */ - ilkPrices( - [ilkNames]: string - ): Currency; - - /** - * @param ilkName String uniquely identifying an ilk - * @param urn String hexidecimal address of the vault handler - */ - unlockedCollateral( - ilkName: string, - urn: string - ): BigNumber; - - /** - * @param ilkName String uniquely identifying an ilk - * @param urn String hexidecimal address of the vault handler - */ - encumberedCollateral( - ilkName: string, - urn: string - ): BigNumber; - - /** - * @param ilkName String uniquely identifying an ilk - * @param urn String hexidecimal address of the vault handler - */ - encumberedDebt( - ilkName: string, - urn: string - ): BigNumber; - - /** - * @param ilkName String uniquely identifying an ilk - */ - annualStabilityFee( - ilkName: string - ): number; - - /** - * @param ilkName String uniquely identifying an ilk - */ - feeUpdateTimestamp( - ilkName: string - ): number; - - /** - * @param ilkName String uniquely identifying an ilk - */ - liquidatorAddress( - ilkName: string - ): string; - - /** - * @param ilkName String uniquely identifying an ilk - */ - liquidationPenalty( - ilkName: string - ): number; - - /** - * @param ilkName String uniquely identifying an ilk - */ - maxAuctionLotSize( - ilkName: string - ): BigNumber; - - -} - -declare var watch: WatchInterface; - -declare module 'watch' { - export = watch; -} diff --git a/packages/dai-plugin-mcd/typings/index.d.ts b/packages/dai-plugin-mcd/typings/index.d.ts new file mode 100644 index 000000000..e2ee923ab --- /dev/null +++ b/packages/dai-plugin-mcd/typings/index.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts new file mode 100644 index 000000000..41469334b --- /dev/null +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -0,0 +1,117 @@ +declare global { + interface WatchInterfaceMcd { + + /** Watch the total encumbered debt of a collateral type + * @param ilkName String uniquely identifying an ilk + */ + static totalEncumberedDebt( + ilkName: string + ): Currency; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static debtScalingFactor( + ilkName: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static priceWithSafetyMargin( + ilkName: string + ): BigNumber; + + /** Get the Dai debt ceiling for a particular collateral type + * @param ilkName String uniquely identifying an ilk + */ + static debtCeiling( + ilkName: string + ): Currency; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static urnDebtFloor( + ilkName: string + ): BigNumber; + + /** Watch the price of an ilk + * @param ilkName String uniquely identifying an ilk + */ + static ilkPrice( + ilkName: string + ): Currency; + + /** Watch the prices of a list of ilks + * @param ilkName Array of strings uniquely identifying ilks + */ + static ilkPrices( + [ilkNames]: string + ): Currency; + + /** + * @param ilkName String uniquely identifying an ilk + * @param urn String hexidecimal address of the vault handler + */ + static unlockedCollateral( + ilkName: string, + urn: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + * @param urn String hexidecimal address of the vault handler + */ + static encumberedCollateral( + ilkName: string, + urn: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + * @param urn String hexidecimal address of the vault handler + */ + static encumberedDebt( + ilkName: string, + urn: string + ): BigNumber; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static annualStabilityFee( + ilkName: string + ): number; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static feeUpdateTimestamp( + ilkName: string + ): number; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static liquidatorAddress( + ilkName: string + ): string; + + /** + * @param ilkName String uniquely identifying an ilk + */ + static liquidationPenalty( + ilkName: string + ): number; + + /** + * @param ilkName String uniquely identifying an ilk + */ + maxAuctionLotSize( + ilkName: string + ): BigNumber; + } +} + +export default WatchInterfaceMcd; From 8a241b12aa4387be0b9182314c03888b4f059677 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 22 Jan 2020 20:31:17 +0800 Subject: [PATCH 060/117] Add initial type definitions for vaultById and VaultResult --- .../dai-plugin-mcd/typings/multicall.d.ts | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts index 41469334b..9a861de3e 100644 --- a/packages/dai-plugin-mcd/typings/multicall.d.ts +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -1,4 +1,47 @@ declare global { + interface Currency {} + interface CurrencyRatio {} + interface VaultResult { + /** + * The urn address of this vault. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + * ```ts + * console.log('test code'); + * ``` + */ + urn: string; + + /** + * The price of this vault's ilk. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + */ + ilkPrice: CurrencyRatio; + + /** + * Lorem ipsum. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + */ + encumberedCollateral: Currency; + + /** + * Lorem ipsum. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + */ + encumberedDebt: Currency; + } + interface WatchInterfaceMcd { /** Watch the total encumbered debt of a collateral type @@ -108,9 +151,25 @@ declare global { /** * @param ilkName String uniquely identifying an ilk */ - maxAuctionLotSize( + static maxAuctionLotSize( ilkName: string ): BigNumber; + + /** + * Get a vault by id. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + * ```js + * const vault = watch.vaultById(2) + * ``` + * + * @param id Numerical id of the vault + */ + static vaultById( + id: number + ): VaultResult; } } From 79d20a9781b3e74fea5830554a5310aee4ee8f30 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 14:11:11 +0000 Subject: [PATCH 061/117] Renaming of keys urnDebtFloor -> debtFloor vaultUrn -> vaultAddress vaultIlk -> vaultType ilkPrice -> collateralTypePrice ilkPrices -> collateralTypesPrices vaultIlkAndUrn -> vaultTypeAndAddress urnCollateralAndDebt -> vaultCollateralAndDebt Also ilkName -> collateralTypeName --- packages/dai-plugin-mcd/src/schemas/cat.js | 6 +- .../dai-plugin-mcd/src/schemas/cdpManager.js | 6 +- .../dai-plugin-mcd/src/schemas/computed.js | 66 +++++----- .../dai-plugin-mcd/src/schemas/constants.js | 16 +-- packages/dai-plugin-mcd/src/schemas/jug.js | 6 +- packages/dai-plugin-mcd/src/schemas/spot.js | 12 +- packages/dai-plugin-mcd/src/schemas/vat.js | 4 +- .../test/schemas/cdpManager.spec.js | 14 +-- .../test/schemas/computed.spec.js | 64 +++++----- .../dai-plugin-mcd/test/schemas/vat.spec.js | 16 +-- .../dai-plugin-mcd/typings/multicall.d.ts | 117 +++++++----------- 11 files changed, 158 insertions(+), 169 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cat.js b/packages/dai-plugin-mcd/src/schemas/cat.js index 44900438f..f6f3cfa20 100644 --- a/packages/dai-plugin-mcd/src/schemas/cat.js +++ b/packages/dai-plugin-mcd/src/schemas/cat.js @@ -9,10 +9,10 @@ import { } from './constants'; export const catIlks = { - generate: ilkName => ({ - id: `MCD_CAT.ilks(${ilkName})`, + generate: collateralTypeName => ({ + id: `MCD_CAT.ilks(${collateralTypeName})`, contractName: 'MCD_CAT', - call: ['ilks(bytes32)(address,uint256,uint256)', toHex(ilkName)] + call: ['ilks(bytes32)(address,uint256,uint256)', toHex(collateralTypeName)] }), returns: [ [LIQUIDATOR_ADDRESS], diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js index b05b4a63c..d6824c12c 100644 --- a/packages/dai-plugin-mcd/src/schemas/cdpManager.js +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -1,4 +1,4 @@ -import { VAULT_URN, VAULT_ILK } from './constants'; +import { VAULT_ADDRESS, VAULT_TYPE } from './constants'; import { bytesToString } from '../utils'; export const cdpManagerUrns = { @@ -7,7 +7,7 @@ export const cdpManagerUrns = { contractName: 'CDP_MANAGER', call: ['urns(uint256)(address)', parseInt(id)] }), - returns: [VAULT_URN] + returns: [VAULT_ADDRESS] }; export const cdpManagerIlks = { @@ -16,7 +16,7 @@ export const cdpManagerIlks = { contractName: 'CDP_MANAGER', call: ['ilks(uint256)(bytes32)', parseInt(id)] }), - returns: [[VAULT_ILK, bytesToString]] + returns: [[VAULT_TYPE, bytesToString]] }; export default { diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 35509fc97..391dd907d 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -5,21 +5,21 @@ import { RATIO_DAI_USD, LIQUIDATION_RATIO, PRICE_WITH_SAFETY_MARGIN, - ILK_PRICE, - VAULT_ILK, - VAULT_URN, + COLLATERAL_TYPE_PRICE, + VAULT_TYPE, + VAULT_ADDRESS, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, SAVINGS_DAI_BY_PROXY, PROXY_ADDRESS } from './constants'; -export const ilkPrice = { - generate: ilkName => ({ +export const collateralTypePrice = { + generate: collateralTypeName => ({ dependencies: [ [RATIO_DAI_USD], - [PRICE_WITH_SAFETY_MARGIN, ilkName], - [LIQUIDATION_RATIO, ilkName] + [PRICE_WITH_SAFETY_MARGIN, collateralTypeName], + [LIQUIDATION_RATIO, collateralTypeName] ], computed: (ratioDaiUsd, priceWithSafetyMargin, liquidationRatio) => { const currency = createCurrency( @@ -34,25 +34,30 @@ export const ilkPrice = { }) }; -export const ilkPrices = { - generate: ilkNames => ({ - dependencies: () => [...ilkNames.map(ilkName => [ILK_PRICE, ilkName])], +export const collateralTypesPrices = { + generate: collateralTypesNames => ({ + dependencies: () => [ + ...collateralTypesNames.map(collateralTypeName => [ + COLLATERAL_TYPE_PRICE, + collateralTypeName + ]) + ], computed: (...prices) => prices }) }; -export const vaultIlkAndUrn = { +export const vaultTypeAndAddress = { generate: id => ({ - dependencies: [[VAULT_ILK, id], [VAULT_URN, id]], - computed: (ilk, urn) => [ilk, urn] + dependencies: [[VAULT_TYPE, id], [VAULT_ADDRESS, id]], + computed: (vaultType, vaultAddress) => [vaultType, vaultAddress] }) }; -export const urnCollateralAndDebt = { - generate: (ilk, urn) => ({ +export const vaultCollateralAndDebt = { + generate: (vaultType, vaultAddress) => ({ dependencies: [ - [ENCUMBERED_COLLATERAL, ilk, urn], - [ENCUMBERED_DEBT, ilk, urn] + [ENCUMBERED_COLLATERAL, vaultType, vaultAddress], + [ENCUMBERED_DEBT, vaultType, vaultAddress] ], computed: (encumberedCollateral, encumberedDebt) => [ encumberedCollateral, @@ -64,14 +69,19 @@ export const urnCollateralAndDebt = { export const vaultById = { generate: id => ({ dependencies: [ - [VAULT_ILK, id], - [VAULT_URN, id], - [ENCUMBERED_COLLATERAL, [VAULT_ILK, id], [VAULT_URN, id]], - [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]] + [VAULT_TYPE, id], + [VAULT_ADDRESS, id], + [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], + [ENCUMBERED_DEBT, [VAULT_TYPE, id], [VAULT_ADDRESS, id]] ], - computed: (ilk, urn, encumberedCollateral, encumberedDebt) => ({ - ilk, - urn, + computed: ( + vaultType, + vaultAddress, + encumberedCollateral, + encumberedDebt + ) => ({ + vaultType, + vaultAddress, encumberedCollateral, encumberedDebt }) @@ -86,10 +96,10 @@ export const savingsDai = { }; export default { - ilkPrice, - ilkPrices, - vaultIlkAndUrn, - urnCollateralAndDebt, + collateralTypePrice, + collateralTypesPrices, + vaultTypeAndAddress, + vaultCollateralAndDebt, vaultById, savingsDai }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 2cac0016e..c3fd8eb36 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -3,7 +3,7 @@ export const TOTAL_ENCUMBERED_DEBT = 'totalEncumberedDebt'; export const DEBT_SCALING_FACTOR = 'debtScalingFactor'; export const PRICE_WITH_SAFETY_MARGIN = 'priceWithSafetyMargin'; export const DEBT_CEILING = 'debtCeiling'; -export const URN_DEBT_FLOOR = 'urnDebtFloor'; +export const DEBT_FLOOR = 'debtFloor'; export const TOTAL_DAI_SUPPLY = 'totalDaiSupply'; export const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; export const ENCUMBERED_DEBT = 'encumberedDebt'; @@ -13,6 +13,7 @@ export const UNLOCKED_COLLATERAL = 'unlockedCollateral'; export const PRICE_FEED_ADDRESS = 'priceFeedAddress'; export const RAW_LIQUIDATION_RATIO = 'rawLiquidationRatio'; export const RATIO_DAI_USD = 'ratioDaiUsd'; +export const LIQUIDATION_RATIO = 'liquidationRatio'; // jug export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; @@ -22,8 +23,8 @@ export const DATE_STABILITY_FEES_LAST_LEVIED = 'dateStabilityFeesLastLevied'; export const PROXY_ADDRESS = 'proxyAddress'; // cdpManager -export const VAULT_URN = 'vaultUrn'; -export const VAULT_ILK = 'vaultIlk'; +export const VAULT_ADDRESS = 'vaultAddress'; +export const VAULT_TYPE = 'vaultType'; // pot export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; @@ -38,10 +39,9 @@ export const LIQUIDATION_PENALTY = 'liquidationPenalty'; export const MAX_AUCTION_LOT_SIZE = 'maxAuctionLotSize'; // computed -export const LIQUIDATION_RATIO = 'liquidationRatio'; -export const ILK_PRICE = 'ilkPrice'; -export const ILK_PRICES = 'ilkPrices'; -export const VAULT_ILK_AND_URN = 'vaultIlkAndUrn'; -export const URN_COLLATERAL_AND_DEBT = 'urnCollateralAndDebt'; +export const COLLATERAL_TYPE_PRICE = 'collateralTypePrice'; +export const COLLATERAL_TYPES_PRICES = 'collateralTypesPrices'; +export const VAULT_TYPE_AND_ADDRESS = 'vaultTypeAndAddress'; +export const VAULT_COLLATERAL_AND_DEBT = 'vaultCollateralAndDebt'; export const VAULT_BY_ID = 'vaultById'; export const SAVINGS_DAI = 'savingsDai'; diff --git a/packages/dai-plugin-mcd/src/schemas/jug.js b/packages/dai-plugin-mcd/src/schemas/jug.js index 945aeca0e..0e9405f5f 100644 --- a/packages/dai-plugin-mcd/src/schemas/jug.js +++ b/packages/dai-plugin-mcd/src/schemas/jug.js @@ -7,10 +7,10 @@ import { } from './constants'; export const jugIlks = { - generate: ilkName => ({ - id: `MCD_JUG.ilks(${ilkName})`, + generate: collateralTypeName => ({ + id: `MCD_JUG.ilks(${collateralTypeName})`, contractName: 'MCD_JUG', - call: ['ilks(bytes32)(uint256,uint48)', toHex(ilkName)] + call: ['ilks(bytes32)(uint256,uint48)', toHex(collateralTypeName)] }), returns: [ [ANNUAL_STABILITY_FEE, annualStabilityFee], diff --git a/packages/dai-plugin-mcd/src/schemas/spot.js b/packages/dai-plugin-mcd/src/schemas/spot.js index dc20fbeda..dcdbb72bd 100644 --- a/packages/dai-plugin-mcd/src/schemas/spot.js +++ b/packages/dai-plugin-mcd/src/schemas/spot.js @@ -9,10 +9,10 @@ import { } from './constants'; export const spotIlks = { - generate: ilkName => ({ - id: `MCD_SPOT.ilks(${ilkName})`, + generate: collateralTypeName => ({ + id: `MCD_SPOT.ilks(${collateralTypeName})`, contractName: 'MCD_SPOT', - call: ['ilks(bytes32)(address,uint256)', toHex(ilkName)] + call: ['ilks(bytes32)(address,uint256)', toHex(collateralTypeName)] }), returns: [PRICE_FEED_ADDRESS, [RAW_LIQUIDATION_RATIO, fromRay]] }; @@ -33,11 +33,11 @@ export const liquidationRatio = { // // In plain english, it is the ratio of the dollar amount of ETH in terms of // the dollar amount of dai - generate: ilkName => ({ - dependencies: () => [[RAW_LIQUIDATION_RATIO, ilkName]], + generate: collateralTypeName => ({ + dependencies: () => [[RAW_LIQUIDATION_RATIO, collateralTypeName]], computed: liqRatio => createCurrencyRatio( - createCurrency(`(${ilkName.split('-')[0]}/USD)`), + createCurrency(`(${collateralTypeName.split('-')[0]}/USD)`), createCurrency(`(${MDAI.symbol}/USD)`) )(liqRatio) }) diff --git a/packages/dai-plugin-mcd/src/schemas/vat.js b/packages/dai-plugin-mcd/src/schemas/vat.js index 5bc6d69e2..48f8fc0e1 100644 --- a/packages/dai-plugin-mcd/src/schemas/vat.js +++ b/packages/dai-plugin-mcd/src/schemas/vat.js @@ -7,7 +7,7 @@ import { DEBT_SCALING_FACTOR, PRICE_WITH_SAFETY_MARGIN, DEBT_CEILING, - URN_DEBT_FLOOR, + DEBT_FLOOR, TOTAL_DAI_SUPPLY, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, @@ -28,7 +28,7 @@ export const vatIlks = { [DEBT_SCALING_FACTOR, fromRay], [PRICE_WITH_SAFETY_MARGIN, fromRay], [DEBT_CEILING, v => MDAI(v, 'rad')], - [URN_DEBT_FLOOR, fromRad] + [DEBT_FLOOR, fromRad] ] }; diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index c0449df7e..378ee4891 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -3,7 +3,7 @@ import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { ETH, BAT, MDAI } from '../../src'; import { ServiceRoles } from '../../src/constants'; -import schemas, { VAULT_URN, VAULT_ILK } from '../../src/schemas'; +import schemas, { VAULT_ADDRESS, VAULT_TYPE } from '../../src/schemas'; let maker, snapshotData, cdpMgr; @@ -44,16 +44,16 @@ afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); -test(VAULT_URN, async () => { +test(VAULT_ADDRESS, async () => { const cdpId = 1; const expected = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; - const urn = await maker.latest(VAULT_URN, cdpId); - expect(urn).toEqual(expected); + const vaultAddress = await maker.latest(VAULT_ADDRESS, cdpId); + expect(vaultAddress).toEqual(expected); }); -test(VAULT_ILK, async () => { +test(VAULT_TYPE, async () => { const cdpId = 1; const expected = 'ETH-A'; - const ilk = await maker.latest(VAULT_ILK, cdpId); - expect(ilk).toEqual(expected); + const vaultType = await maker.latest(VAULT_TYPE, cdpId); + expect(vaultType).toEqual(expected); }); diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 956911daa..23b181937 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -5,9 +5,9 @@ import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; import schemas, { - ILK_PRICE, - ILK_PRICES, - VAULT_ILK_AND_URN, + COLLATERAL_TYPE_PRICE, + COLLATERAL_TYPES_PRICES, + VAULT_TYPE_AND_ADDRESS, VAULT_BY_ID, SAVINGS_DAI } from '../../src/schemas'; @@ -66,18 +66,17 @@ afterAll(async () => { await restoreSnapshot(snapshotData, maker); }); -test(ILK_PRICE, async () => { - const ethAPrice = await maker.latest(ILK_PRICE, 'ETH-A'); +test(COLLATERAL_TYPE_PRICE, async () => { + const ethAPrice = await maker.latest(COLLATERAL_TYPE_PRICE, 'ETH-A'); expect(ethAPrice.toNumber()).toEqual(180); expect(ethAPrice.symbol).toEqual('USD/ETH'); }); -test(ILK_PRICES, async () => { - const [ethAPrice, ethBPrice, batAPrice] = await maker.latest(ILK_PRICES, [ - 'ETH-A', - 'ETH-B', - 'BAT-A' - ]); +test(COLLATERAL_TYPES_PRICES, async () => { + const [ethAPrice, ethBPrice, batAPrice] = await maker.latest( + COLLATERAL_TYPES_PRICES, + ['ETH-A', 'ETH-B', 'BAT-A'] + ); expect(ethAPrice.toNumber()).toEqual(180); expect(ethBPrice.toNumber()).toEqual(150); @@ -88,30 +87,37 @@ test(ILK_PRICES, async () => { expect(batAPrice.symbol).toEqual('USD/BAT'); }); -test(VAULT_ILK_AND_URN, async () => { +test(VAULT_TYPE_AND_ADDRESS, async () => { const cdpId = 1; - const expectedIlk = 'ETH-A'; - const expectedUrn = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; - const [ilk, urn] = await maker.latest(VAULT_ILK_AND_URN, cdpId); - expect(ilk).toEqual(expectedIlk); - expect(urn).toEqual(expectedUrn); + const expectedVaultType = 'ETH-A'; + const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; + const [collateralType, vaultAddress] = await maker.latest( + VAULT_TYPE_AND_ADDRESS, + cdpId + ); + expect(collateralType).toEqual(expectedVaultType); + expect(vaultAddress).toEqual(expectedVaultAddress); }); test(VAULT_BY_ID, async () => { const cdpId = 1; - const expectedIlk = 'ETH-A'; - const expectedUrn = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; - const expectedInk = fromWei(1000000000000000000); - const expectedArt = fromWei(995000000000000000); - const { ilk, urn, encumberedCollateral, encumberedDebt } = await maker.latest( - VAULT_BY_ID, - cdpId + const expectedVaultType = 'ETH-A'; + const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; + const expectedEncumberedCollateral = fromWei(1000000000000000000); + const expectedEncumberedDebt = fromWei(995000000000000000); + const { + vaultType, + vaultAddress, + encumberedCollateral, + encumberedDebt + } = await maker.latest(VAULT_BY_ID, cdpId); + + expect(vaultType).toEqual(expectedVaultType); + expect(vaultAddress).toEqual(expectedVaultAddress); + expect(encumberedCollateral).toEqual(expectedEncumberedCollateral); + expect(encumberedDebt.toNumber()).toBeCloseTo( + expectedEncumberedDebt.toNumber() ); - - expect(ilk).toEqual(expectedIlk); - expect(urn).toEqual(expectedUrn); - expect(encumberedCollateral).toEqual(expectedInk); - expect(encumberedDebt.toNumber()).toBeCloseTo(expectedArt.toNumber()); }); test(SAVINGS_DAI, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index 3be3663c1..4d026c9b2 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -10,7 +10,7 @@ import schemas, { DEBT_SCALING_FACTOR, PRICE_WITH_SAFETY_MARGIN, DEBT_CEILING, - URN_DEBT_FLOOR, + DEBT_FLOOR, TOTAL_DAI_SUPPLY, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, @@ -153,15 +153,15 @@ test(DEBT_CEILING, async () => { expect(batADebtCeiling.isEqual(MDAI(5000))).toEqual(true); }); -test(URN_DEBT_FLOOR, async () => { - const ethAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'ETH-A'); - const batAUrnDebtFloor = await maker.latest(URN_DEBT_FLOOR, 'BAT-A'); +test(DEBT_FLOOR, async () => { + const ethADebtFloor = await maker.latest(DEBT_FLOOR, 'ETH-A'); + const batADebtFloor = await maker.latest(DEBT_FLOOR, 'BAT-A'); - expect(isBigNumber(ethAUrnDebtFloor)).toEqual(true); - expect(isBigNumber(batAUrnDebtFloor)).toEqual(true); + expect(isBigNumber(ethADebtFloor)).toEqual(true); + expect(isBigNumber(batADebtFloor)).toEqual(true); - expect(ethAUrnDebtFloor).toEqual(BigNumber('0')); - expect(batAUrnDebtFloor).toEqual(BigNumber('0')); + expect(ethADebtFloor).toEqual(BigNumber('0')); + expect(batADebtFloor).toEqual(BigNumber('0')); }); test(TOTAL_DAI_SUPPLY, async () => { diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts index 9a861de3e..2225e7293 100644 --- a/packages/dai-plugin-mcd/typings/multicall.d.ts +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -3,7 +3,7 @@ declare global { interface CurrencyRatio {} interface VaultResult { /** - * The urn address of this vault. + * The address of this vault. * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod * tempor incididunt ut labore et dolore magna aliqua. @@ -12,16 +12,16 @@ declare global { * console.log('test code'); * ``` */ - urn: string; + vaultAddress: string; /** - * The price of this vault's ilk. + * The price of this vault's collateral type. * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod * tempor incididunt ut labore et dolore magna aliqua. * */ - ilkPrice: CurrencyRatio; + collateralTypePrice: CurrencyRatio; /** * Lorem ipsum. @@ -43,117 +43,92 @@ declare global { } interface WatchInterfaceMcd { - /** Watch the total encumbered debt of a collateral type - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static totalEncumberedDebt( - ilkName: string - ): Currency; + static totalEncumberedDebt(collateralTypeName: string): Currency; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static debtScalingFactor( - ilkName: string - ): BigNumber; + static debtScalingFactor(collateralTypeName: string): BigNumber; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static priceWithSafetyMargin( - ilkName: string - ): BigNumber; + static priceWithSafetyMargin(collateralTypeName: string): BigNumber; /** Get the Dai debt ceiling for a particular collateral type - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static debtCeiling( - ilkName: string - ): Currency; + static debtCeiling(collateralTypeName: string): Currency; - /** - * @param ilkName String uniquely identifying an ilk + /** Minimum amount of debt that can be generated when opening a vault of that type + * @param collateralTypeName String uniquely identifying a collateral type */ - static urnDebtFloor( - ilkName: string - ): BigNumber; + static debtFloor(collateralTypeName: string): BigNumber; - /** Watch the price of an ilk - * @param ilkName String uniquely identifying an ilk + /** Watch the price of a collateral type + * @param collateralTypeName String uniquely identifying a collateral type */ - static ilkPrice( - ilkName: string - ): Currency; + static collateralTypePrice(collateralTypeName: string): Currency; /** Watch the prices of a list of ilks - * @param ilkName Array of strings uniquely identifying ilks + * @param collateralTypeNames Array of strings uniquely identifying collateral types */ - static ilkPrices( - [ilkNames]: string - ): Currency; + static collateralTypesPrices([collateralTypeNames]: string): Currency; /** - * @param ilkName String uniquely identifying an ilk - * @param urn String hexidecimal address of the vault handler + * @param collateralTypeName String uniquely identifying a collateral type + * @param vaultAddress String hexidecimal address of the vault handler */ static unlockedCollateral( - ilkName: string, - urn: string + collateralTypeName: string, + vaultAddress: string ): BigNumber; /** - * @param ilkName String uniquely identifying an ilk - * @param urn String hexidecimal address of the vault handler + * @param collateralTypeName String uniquely identifying a collateral type + * @param vaultAddress String hexidecimal address of the vault handler */ static encumberedCollateral( - ilkName: string, - urn: string - ): BigNumber; + collateralTypeName: string, + vaultAddress: string + ): BigNumber; /** - * @param ilkName String uniquely identifying an ilk - * @param urn String hexidecimal address of the vault handler + * @param collateralTypeName String uniquely identifying a collateral type + * @param vaultAddress String hexidecimal address of the vault handler */ static encumberedDebt( - ilkName: string, - urn: string - ): BigNumber; + collateralTypeName: string, + vaultAddress: string + ): BigNumber; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static annualStabilityFee( - ilkName: string - ): number; + static annualStabilityFee(collateralTypeName: string): number; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static feeUpdateTimestamp( - ilkName: string - ): number; + static feeUpdateTimestamp(collateralTypeName: string): number; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static liquidatorAddress( - ilkName: string - ): string; + static liquidatorAddress(collateralTypeName: string): string; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static liquidationPenalty( - ilkName: string - ): number; + static liquidationPenalty(collateralTypeName: string): number; /** - * @param ilkName String uniquely identifying an ilk + * @param collateralTypeName String uniquely identifying a collateral type */ - static maxAuctionLotSize( - ilkName: string - ): BigNumber; + static maxAuctionLotSize(collateralTypeName: string): BigNumber; /** * Get a vault by id. @@ -167,9 +142,7 @@ declare global { * * @param id Numerical id of the vault */ - static vaultById( - id: number - ): VaultResult; + static vaultById(id: number): VaultResult; } } From 759c641324981b90313e7a4ba64e398fba8338ac Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 14:35:21 +0000 Subject: [PATCH 062/117] collateralTypesPrices will extract prices from defaultCdpTypes list in index --- packages/dai-plugin-mcd/src/index.js | 2 +- packages/dai-plugin-mcd/src/schemas/computed.js | 7 ++++--- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 7 +------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index 19b4217ba..7fb315ca4 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -78,7 +78,7 @@ export const BAT = createCurrency('BAT'); export const DGD = createCurrency('DGD'); export const GNT = createCurrency('GNT'); -const defaultCdpTypes = [ +export const defaultCdpTypes = [ { currency: ETH, ilk: 'ETH-A' }, { currency: BAT, ilk: 'BAT-A' } ]; diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 391dd907d..c11754513 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -1,5 +1,6 @@ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; -import { USD } from '../..'; +import { defaultCdpTypes } from '../'; +import { USD } from '../'; import { RATIO_DAI_USD, @@ -35,9 +36,9 @@ export const collateralTypePrice = { }; export const collateralTypesPrices = { - generate: collateralTypesNames => ({ + generate: () => ({ dependencies: () => [ - ...collateralTypesNames.map(collateralTypeName => [ + ...defaultCdpTypes.map(({ ilk: collateralTypeName }) => [ COLLATERAL_TYPE_PRICE, collateralTypeName ]) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 23b181937..ccfdd4a40 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -73,17 +73,12 @@ test(COLLATERAL_TYPE_PRICE, async () => { }); test(COLLATERAL_TYPES_PRICES, async () => { - const [ethAPrice, ethBPrice, batAPrice] = await maker.latest( - COLLATERAL_TYPES_PRICES, - ['ETH-A', 'ETH-B', 'BAT-A'] - ); + const [ethAPrice, batAPrice] = await maker.latest(COLLATERAL_TYPES_PRICES); expect(ethAPrice.toNumber()).toEqual(180); - expect(ethBPrice.toNumber()).toEqual(150); expect(batAPrice.toNumber()).toEqual(40); expect(ethAPrice.symbol).toEqual('USD/ETH'); - expect(ethBPrice.symbol).toEqual('USD/ETH'); expect(batAPrice.symbol).toEqual('USD/BAT'); }); From 4deda86442e61495be4c61d5cf272e3ab4b1e6e6 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 16:33:09 +0000 Subject: [PATCH 063/117] Rename CHAI -> DSR_DAI --- packages/dai-plugin-mcd/src/index.js | 2 +- packages/dai-plugin-mcd/src/schemas/pot.js | 6 +++--- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 4 ++-- packages/dai-plugin-mcd/typings/multicall.d.ts | 3 +-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index 7fb315ca4..067340e76 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -69,7 +69,7 @@ export const MWETH = createCurrency('MWETH'); export const MDAI = createCurrency('MDAI'); // Casting for savings dai -export const CHAI = createCurrency('CHAI'); +export const DSR_DAI = createCurrency('DSR-DAI'); export const REP = createCurrency('REP'); export const ZRX = createCurrency('ZRX'); diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js index 9f5ec2599..1fd2ecfce 100644 --- a/packages/dai-plugin-mcd/src/schemas/pot.js +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -1,5 +1,5 @@ import { fromRay } from '../utils'; -import { CHAI } from '..'; +import { DSR_DAI } from '..'; import { TOTAL_SAVINGS_DAI, @@ -14,7 +14,7 @@ export const potPie = { contractName: 'MCD_POT', call: ['Pie()(uint256)'] }), - returns: [[TOTAL_SAVINGS_DAI, v => CHAI(v, 'wei')]] + returns: [[TOTAL_SAVINGS_DAI, v => DSR_DAI(v, 'wei')]] }; export const potpie = { @@ -23,7 +23,7 @@ export const potpie = { contractName: 'MCD_POT', call: ['pie(address)(uint256)', proxyAddress] }), - returns: [[SAVINGS_DAI_BY_PROXY, v => CHAI(v, 'wei')]] + returns: [[SAVINGS_DAI_BY_PROXY, v => DSR_DAI(v, 'wei')]] }; export const potDsr = { diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index ccfdd4a40..003d34cd3 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -117,6 +117,6 @@ test(VAULT_BY_ID, async () => { test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); - expect(savingsDai.symbol).toEqual('CHAI'); + expect(savingsDai.symbol).toEqual('DSR-DAI'); expect(savingsDai.toNumber()).toBeCloseTo(0.99995); }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index f617b52ce..5bb0362a0 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -54,7 +54,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); - expect(totalSavingsDai.symbol).toEqual('CHAI'); + expect(totalSavingsDai.symbol).toEqual('DSR-DAI'); expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); }); @@ -63,7 +63,7 @@ test(SAVINGS_DAI_BY_PROXY, async () => { SAVINGS_DAI_BY_PROXY, await maker.service('proxy').getProxyAddress() ); - expect(savingsDaiByProxy.symbol).toEqual('CHAI'); + expect(savingsDaiByProxy.symbol).toEqual('DSR-DAI'); expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); }); diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts index 2225e7293..99c2774b4 100644 --- a/packages/dai-plugin-mcd/typings/multicall.d.ts +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -74,9 +74,8 @@ declare global { static collateralTypePrice(collateralTypeName: string): Currency; /** Watch the prices of a list of ilks - * @param collateralTypeNames Array of strings uniquely identifying collateral types */ - static collateralTypesPrices([collateralTypeNames]: string): Currency; + static collateralTypesPrices(): Currency; /** * @param collateralTypeName String uniquely identifying a collateral type From eb5155d015d77c05da77b9603139a9232219c8a5 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 16:54:37 +0000 Subject: [PATCH 064/117] Explicitly registering schemas for tests --- .../dai-plugin-mcd/test/schemas/cat.spec.js | 6 +++-- .../test/schemas/cdpManager.spec.js | 6 +++-- .../test/schemas/computed.spec.js | 22 +++++++++++++++++-- .../dai-plugin-mcd/test/schemas/jug.spec.js | 6 +++-- .../dai-plugin-mcd/test/schemas/pot.spec.js | 6 +++-- .../dai-plugin-mcd/test/schemas/spot.spec.js | 6 +++-- .../dai-plugin-mcd/test/schemas/vat.spec.js | 6 +++-- .../dai-plugin-mcd/typings/multicall.d.ts | 4 +++- 8 files changed, 47 insertions(+), 15 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/cat.spec.js b/packages/dai-plugin-mcd/test/schemas/cat.spec.js index afb4c7d05..fdc09982d 100644 --- a/packages/dai-plugin-mcd/test/schemas/cat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cat.spec.js @@ -2,12 +2,14 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import BigNumber from 'bignumber.js'; -import schemas, { +import { LIQUIDATOR_ADDRESS, LIQUIDATION_PENALTY, MAX_AUCTION_LOT_SIZE } from '../../src/schemas'; +import catSchemas from '../../src/schemas/cat'; + let maker, snapshotData; beforeAll(async () => { @@ -17,7 +19,7 @@ beforeAll(async () => { snapshotData = await takeSnapshot(maker); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas(catSchemas); maker.service('multicall').start(); }); diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index 378ee4891..be24cc891 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -3,7 +3,9 @@ import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { ETH, BAT, MDAI } from '../../src'; import { ServiceRoles } from '../../src/constants'; -import schemas, { VAULT_ADDRESS, VAULT_TYPE } from '../../src/schemas'; +import { VAULT_ADDRESS, VAULT_TYPE } from '../../src/schemas'; + +import cdpManagerSchemas from '../../src/schemas/cdpManager'; let maker, snapshotData, cdpMgr; @@ -22,7 +24,7 @@ beforeAll(async () => { snapshotData = await takeSnapshot(maker); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas(cdpManagerSchemas); maker.service('multicall').start(); await setupCollateral(maker, 'ETH-A', { price: ETH_A_PRICE diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 003d34cd3..372953886 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -4,7 +4,7 @@ import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; -import schemas, { +import { COLLATERAL_TYPE_PRICE, COLLATERAL_TYPES_PRICES, VAULT_TYPE_AND_ADDRESS, @@ -12,6 +12,13 @@ import schemas, { SAVINGS_DAI } from '../../src/schemas'; +import { vatIlks, vatUrns } from '../../src/schemas/vat'; +import { cdpManagerUrns, cdpManagerIlks } from '../../src/schemas/cdpManager'; +import { spotIlks, liquidationRatio, spotPar } from '../../src/schemas/spot'; +import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; +import { potpie } from '../../src/schemas/pot'; +import computedSchemas from '../../src/schemas/computed'; + let maker, address, snapshotData; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -33,7 +40,18 @@ beforeAll(async () => { }); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas({ + vatIlks, + vatUrns, + cdpManagerUrns, + cdpManagerIlks, + spotPar, + spotIlks, + proxyRegistryProxies, + potpie, + liquidationRatio, + ...computedSchemas + }); maker.service('multicall').start(); address = maker.service('web3').currentAddress(); diff --git a/packages/dai-plugin-mcd/test/schemas/jug.spec.js b/packages/dai-plugin-mcd/test/schemas/jug.spec.js index ceb064618..d878908db 100644 --- a/packages/dai-plugin-mcd/test/schemas/jug.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/jug.spec.js @@ -1,11 +1,13 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import schemas, { +import { ANNUAL_STABILITY_FEE, DATE_STABILITY_FEES_LAST_LEVIED } from '../../src/schemas'; +import jugSchemas from '../../src/schemas/jug'; + let maker, snapshotData; beforeAll(async () => { @@ -15,7 +17,7 @@ beforeAll(async () => { snapshotData = await takeSnapshot(maker); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas(jugSchemas); maker.service('multicall').start(); }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index 5bb0362a0..f176ba1af 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -4,7 +4,7 @@ import { ETH, MDAI } from '../../src'; import { ServiceRoles } from '../../src/constants'; import BigNumber from 'bignumber.js'; -import schemas, { +import { TOTAL_SAVINGS_DAI, SAVINGS_DAI_BY_PROXY, DAI_SAVINGS_RATE, @@ -12,6 +12,8 @@ import schemas, { DATE_EARNINGS_LAST_ACCRUED } from '../../src/schemas'; +import potSchemas from '../../src/schemas/pot'; + let maker, snapshotData, cdpMgr, saveService; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -26,7 +28,7 @@ beforeAll(async () => { snapshotData = await takeSnapshot(maker); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas(potSchemas); maker.service('multicall').start(); await setupCollateral(maker, 'ETH-A', { price: ETH_A_PRICE diff --git a/packages/dai-plugin-mcd/test/schemas/spot.spec.js b/packages/dai-plugin-mcd/test/schemas/spot.spec.js index d7430a33e..19dac6514 100644 --- a/packages/dai-plugin-mcd/test/schemas/spot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/spot.spec.js @@ -3,13 +3,15 @@ import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import BigNumber from 'bignumber.js'; import { isValidAddressString } from '../../src/utils'; -import schemas, { +import { PRICE_FEED_ADDRESS, RAW_LIQUIDATION_RATIO, LIQUIDATION_RATIO, RATIO_DAI_USD } from '../../src/schemas'; +import spotSchemas from '../../src/schemas/spot'; + let maker, snapshotData; beforeAll(async () => { @@ -19,7 +21,7 @@ beforeAll(async () => { snapshotData = await takeSnapshot(maker); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas(spotSchemas); maker.service('multicall').start(); }); diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index 4d026c9b2..637dacea6 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -5,7 +5,7 @@ import { ServiceRoles } from '../../src/constants'; import { fromRay, fromWei, isBigNumber, isCurrency } from '../../src/utils'; import BigNumber from 'bignumber.js'; -import schemas, { +import { TOTAL_ENCUMBERED_DEBT, DEBT_SCALING_FACTOR, PRICE_WITH_SAFETY_MARGIN, @@ -17,6 +17,8 @@ import schemas, { UNLOCKED_COLLATERAL } from '../../src/schemas'; +import vatSchemas from '../../src/schemas/vat'; + let maker, snapshotData, ethAInfo, batAInfo, cdpMgr, cdpTypeService; const ETH_A_COLLATERAL_AMOUNT = ETH(1); @@ -38,7 +40,7 @@ beforeAll(async () => { snapshotData = await takeSnapshot(maker); maker.service('multicall').createWatcher({ interval: 'block' }); - maker.service('multicall').registerSchemas(schemas); + maker.service('multicall').registerSchemas(vatSchemas); maker.service('multicall').start(); await setupCollateral(maker, 'ETH-A', { price: ETH_A_PRICE diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts index 99c2774b4..68a37ec9c 100644 --- a/packages/dai-plugin-mcd/typings/multicall.d.ts +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -73,7 +73,9 @@ declare global { */ static collateralTypePrice(collateralTypeName: string): Currency; - /** Watch the prices of a list of ilks + /** Watch the prices of ilks defined in the system + * + * Default ilks defined in the dai.js mcd-plugin */ static collateralTypesPrices(): Currency; From 2e53d78702229cf8f3938b8c02d93dfc8fdd3e37 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 17:23:16 +0000 Subject: [PATCH 065/117] test cleanup --- packages/dai-plugin-mcd/test/schemas/cat.spec.js | 5 +++-- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/jug.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/spot.spec.js | 11 +++++------ packages/dai-plugin-mcd/test/schemas/vat.spec.js | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/cat.spec.js b/packages/dai-plugin-mcd/test/schemas/cat.spec.js index fdc09982d..e0c4a0365 100644 --- a/packages/dai-plugin-mcd/test/schemas/cat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cat.spec.js @@ -1,6 +1,7 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import BigNumber from 'bignumber.js'; +import testnetAddresses from '../../contracts/addresses/testnet'; import { LIQUIDATOR_ADDRESS, @@ -28,9 +29,9 @@ afterAll(async () => { }); test(LIQUIDATOR_ADDRESS, async () => { - const expected = '0x55320248dC50Ef6dABc88ECbc294Fd5e2e1f4eC6'; + const { MCD_FLIP_ETH_A: expected } = testnetAddresses; const address = await maker.latest(LIQUIDATOR_ADDRESS, 'ETH-A'); - expect(address).toEqual(expected); + expect(address.toLowerCase()).toEqual(expected); }); test(LIQUIDATION_PENALTY, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 372953886..512ab9066 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -136,5 +136,5 @@ test(VAULT_BY_ID, async () => { test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); expect(savingsDai.symbol).toEqual('DSR-DAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.99995); + expect(savingsDai.toNumber()).toBeCloseTo(0.9997958, 7); }); diff --git a/packages/dai-plugin-mcd/test/schemas/jug.spec.js b/packages/dai-plugin-mcd/test/schemas/jug.spec.js index d878908db..9fe579cdc 100644 --- a/packages/dai-plugin-mcd/test/schemas/jug.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/jug.spec.js @@ -32,7 +32,7 @@ test(ANNUAL_STABILITY_FEE, async () => { }); test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { - var timestamp = Math.round(new Date().getTime() / 1000); + const timestamp = Math.round(new Date().getTime() / 1000); const dateStabilityFeesLastLevied = await maker.latest( DATE_STABILITY_FEES_LAST_LEVIED, 'ETH-A' diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index f176ba1af..654dc7313 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -57,7 +57,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(totalSavingsDai.symbol).toEqual('DSR-DAI'); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.99995); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.9997958, 7); }); test(SAVINGS_DAI_BY_PROXY, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/spot.spec.js b/packages/dai-plugin-mcd/test/schemas/spot.spec.js index 19dac6514..6fe2d69f9 100644 --- a/packages/dai-plugin-mcd/test/schemas/spot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/spot.spec.js @@ -2,6 +2,7 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import BigNumber from 'bignumber.js'; import { isValidAddressString } from '../../src/utils'; +import testnetAddresses from '../../contracts/addresses/testnet'; import { PRICE_FEED_ADDRESS, @@ -33,15 +34,13 @@ test(PRICE_FEED_ADDRESS, async () => { const ethAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'ETH-A'); const batAPriceFeedAddress = await maker.latest(PRICE_FEED_ADDRESS, 'BAT-A'); + const { PIP_ETH, PIP_BAT } = testnetAddresses; + expect(isValidAddressString(ethAPriceFeedAddress)).toEqual(true); expect(isValidAddressString(batAPriceFeedAddress)).toEqual(true); - expect(ethAPriceFeedAddress).toEqual( - '0xb0ae8c0856259C6fe000F8e2C14507E5FC167D48' - ); - expect(batAPriceFeedAddress).toEqual( - '0x80f178c7b47cb635Ceb12aBB891338744e98365C' - ); + expect(ethAPriceFeedAddress.toLowerCase()).toEqual(PIP_ETH); + expect(batAPriceFeedAddress.toLowerCase()).toEqual(PIP_BAT); }); test(RAW_LIQUIDATION_RATIO, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index 637dacea6..494d83572 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -78,8 +78,8 @@ test(TOTAL_ENCUMBERED_DEBT, async () => { const { rate: ethARate } = ethAInfo; const { rate: batARate } = batAInfo; - const ethADebtAmount = ETH_A_DEBT_AMOUNT._amount; - const batADebtAmount = BAT_A_DEBT_AMOUNT._amount; + const ethADebtAmount = ETH_A_DEBT_AMOUNT.toBigNumber(); + const batADebtAmount = BAT_A_DEBT_AMOUNT.toBigNumber(); const ethAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'ETH-A'); const batAEncumberedDebt = await maker.latest(TOTAL_ENCUMBERED_DEBT, 'BAT-A'); From f98c37d55449ed7da25efecb6b3211520d4f5c9e Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 17:32:17 +0000 Subject: [PATCH 066/117] Decrease precision when testing accrued interest --- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 512ab9066..4fbb40ae9 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -136,5 +136,5 @@ test(VAULT_BY_ID, async () => { test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); expect(savingsDai.symbol).toEqual('DSR-DAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.9997958, 7); + expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 6); }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index 654dc7313..e2fc0c2f1 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -57,7 +57,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(totalSavingsDai.symbol).toEqual('DSR-DAI'); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.9997958, 7); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 6); }); test(SAVINGS_DAI_BY_PROXY, async () => { From 7cb24a612b73e9d69a33bdb51e47f1221de931c1 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 17:34:39 +0000 Subject: [PATCH 067/117] Lowered again --- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 4fbb40ae9..b2a2cab74 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -136,5 +136,5 @@ test(VAULT_BY_ID, async () => { test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); expect(savingsDai.symbol).toEqual('DSR-DAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 6); + expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 5); }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index e2fc0c2f1..9041c7c4a 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -57,7 +57,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(totalSavingsDai.symbol).toEqual('DSR-DAI'); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 6); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 5); }); test(SAVINGS_DAI_BY_PROXY, async () => { From 0cc39d081341aff6675cacce3553cfe815c129da Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Wed, 22 Jan 2020 19:33:03 +0100 Subject: [PATCH 068/117] add computed schemas for vault info and tests --- .../dai-plugin-mcd/src/schemas/computed.js | 86 +++++++++++++++++-- .../dai-plugin-mcd/src/schemas/constants.js | 5 +- .../test/schemas/computed.spec.js | 61 ++++++++++--- 3 files changed, 132 insertions(+), 20 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 35509fc97..af9972df9 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -1,5 +1,9 @@ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; -import { USD } from '../..'; +import { USD, MDAI } from '../..'; +import { + collateralValue as calcCollateralValue, + daiAvailable as calcDaiAvailable +} from '../math'; import { RATIO_DAI_USD, @@ -11,7 +15,12 @@ import { ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, SAVINGS_DAI_BY_PROXY, - PROXY_ADDRESS + PROXY_ADDRESS, + DEBT_SCALING_FACTOR, + DEBT_VALUE, + COLLATERAL_VALUE, + DAI_AVAILABLE, + RAW_LIQUIDATION_RATIO } from './constants'; export const ilkPrice = { @@ -61,19 +70,79 @@ export const urnCollateralAndDebt = { }) }; -export const vaultById = { +export const debtValue = { + generate: id => ({ + dependencies: [ + [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]], + [DEBT_SCALING_FACTOR, [VAULT_ILK, id]] + ], + computed: (encumberedDebt, debtScalingFactor) => { + return MDAI(encumberedDebt).times(debtScalingFactor); + } + }) +}; + +export const collateralValue = { + generate: id => ({ + dependencies: [ + [VAULT_ILK, id], + [ENCUMBERED_COLLATERAL, [VAULT_ILK, id], [VAULT_URN, id]], + [ILK_PRICE, [VAULT_ILK, id]] + ], + computed: (ilkName, encumberedCollateral, ilkPrice) => { + // Todo: better way to get collateral name + const currency = createCurrency(ilkName.substring(0, 3)); + // Note: the first arg is collateralAmount calculation + return calcCollateralValue(currency(encumberedCollateral), ilkPrice); + } + }) +}; + +export const daiAvailable = { + generate: id => ({ + dependencies: [ + [COLLATERAL_VALUE, id], + [DEBT_VALUE, id], + [RAW_LIQUIDATION_RATIO, [VAULT_ILK, id]] + ], + computed: (collateralValue, debtValue, rawLiquidationRatio) => { + const ratio = createCurrencyRatio(USD, MDAI); + const liquidationRatio = ratio(rawLiquidationRatio.toNumber()); + return calcDaiAvailable(collateralValue, debtValue, liquidationRatio); + } + }) +}; + +export const vault = { generate: id => ({ dependencies: [ [VAULT_ILK, id], [VAULT_URN, id], [ENCUMBERED_COLLATERAL, [VAULT_ILK, id], [VAULT_URN, id]], - [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]] + [ENCUMBERED_DEBT, [VAULT_ILK, id], [VAULT_URN, id]], + [ILK_PRICE, [VAULT_ILK, id]], + [DEBT_VALUE, id], + [COLLATERAL_VALUE, id], + [DAI_AVAILABLE, id] ], - computed: (ilk, urn, encumberedCollateral, encumberedDebt) => ({ + computed: ( ilk, urn, encumberedCollateral, - encumberedDebt + encumberedDebt, + ilkPrice, + debtValue, + collateralValue, + daiAvailable + ) => ({ + ilk, + urn, + encumberedCollateral, + encumberedDebt, + ilkPrice, + debtValue, + collateralValue, + daiAvailable }) }) }; @@ -90,6 +159,9 @@ export default { ilkPrices, vaultIlkAndUrn, urnCollateralAndDebt, - vaultById, + vault, + collateralValue, + debtValue, + daiAvailable, savingsDai }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 2cac0016e..d7060d95a 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -43,5 +43,8 @@ export const ILK_PRICE = 'ilkPrice'; export const ILK_PRICES = 'ilkPrices'; export const VAULT_ILK_AND_URN = 'vaultIlkAndUrn'; export const URN_COLLATERAL_AND_DEBT = 'urnCollateralAndDebt'; -export const VAULT_BY_ID = 'vaultById'; +export const VAULT = 'vault'; export const SAVINGS_DAI = 'savingsDai'; +export const DEBT_VALUE = 'debtValue'; +export const COLLATERAL_VALUE = 'collateralValue'; +export const DAI_AVAILABLE = 'daiAvailable'; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 956911daa..e20475e1e 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -1,5 +1,5 @@ import { mcdMaker, setupCollateral } from '../helpers'; -import { ETH, BAT, MDAI } from '../../src'; +import { ETH, BAT, MDAI, USD } from '../../src'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; @@ -8,8 +8,11 @@ import schemas, { ILK_PRICE, ILK_PRICES, VAULT_ILK_AND_URN, - VAULT_BY_ID, - SAVINGS_DAI + VAULT, + SAVINGS_DAI, + DEBT_VALUE, + COLLATERAL_VALUE, + DAI_AVAILABLE } from '../../src/schemas'; let maker, address, snapshotData; @@ -97,21 +100,55 @@ test(VAULT_ILK_AND_URN, async () => { expect(urn).toEqual(expectedUrn); }); -test(VAULT_BY_ID, async () => { +test(DEBT_VALUE, async () => { + const cdpId = 1; + const debtValue = await maker.latest(DEBT_VALUE, cdpId); + const expected = MDAI(1); + + expect(debtValue.toNumber()).toEqual(expected.toNumber()); +}); + +test(COLLATERAL_VALUE, async () => { + const cdpId = 1; + const collateralValue = await maker.latest(COLLATERAL_VALUE, cdpId); + const expected = USD(180); + + expect(collateralValue.toString()).toEqual(expected.toString()); +}); + +test(DAI_AVAILABLE, async () => { + const cdpId = 1; + const daiAvailable = await maker.latest(DAI_AVAILABLE, cdpId); + const expected = MDAI(119); + + expect(daiAvailable.toString()).toEqual(expected.toString()); +}); + +test(VAULT, async () => { const cdpId = 1; const expectedIlk = 'ETH-A'; const expectedUrn = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const expectedInk = fromWei(1000000000000000000); const expectedArt = fromWei(995000000000000000); - const { ilk, urn, encumberedCollateral, encumberedDebt } = await maker.latest( - VAULT_BY_ID, - cdpId + const expectedDebtValue = MDAI(1); + const expectedCollateralValue = USD(180); + const expectedDaiAvailable = MDAI(119); + + const vault = await maker.latest(VAULT, cdpId); + + expect(Object.keys(vault).length).toBe(8); + expect(vault.ilk).toEqual(expectedIlk); + expect(vault.urn).toEqual(expectedUrn); + expect(vault.encumberedCollateral).toEqual(expectedInk); + expect(vault.encumberedDebt.toNumber()).toBeCloseTo(expectedArt.toNumber()); + // expect(vault.ilkPrice).toEqual(expectedInk); + expect(vault.debtValue.toString()).toEqual(expectedDebtValue.toString()); + expect(vault.collateralValue.toString()).toEqual( + expectedCollateralValue.toString() + ); + expect(vault.daiAvailable.toString()).toEqual( + expectedDaiAvailable.toString() ); - - expect(ilk).toEqual(expectedIlk); - expect(urn).toEqual(expectedUrn); - expect(encumberedCollateral).toEqual(expectedInk); - expect(encumberedDebt.toNumber()).toBeCloseTo(expectedArt.toNumber()); }); test(SAVINGS_DAI, async () => { From b2c7cf8671077cdc01becd2316e763f8eae28b33 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Wed, 22 Jan 2020 20:19:37 +0100 Subject: [PATCH 069/117] add type defs for vault result and dependencies --- .../test/schemas/computed.spec.js | 6 +- .../dai-plugin-mcd/typings/multicall.d.ts | 63 +++++++++++++++++-- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index d7d307278..1870866c4 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -21,6 +21,7 @@ import { spotIlks, liquidationRatio, spotPar } from '../../src/schemas/spot'; import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; import { potpie } from '../../src/schemas/pot'; import computedSchemas from '../../src/schemas/computed'; +import { createCurrencyRatio } from '@makerdao/currency'; let maker, address, snapshotData; @@ -148,6 +149,7 @@ test(VAULT, async () => { const expectedDebtValue = MDAI(1); const expectedCollateralValue = USD(180); const expectedDaiAvailable = MDAI(119); + const expectedColTypePrice = createCurrencyRatio(USD, ETH)(180); const vault = await maker.latest(VAULT, cdpId); @@ -159,7 +161,9 @@ test(VAULT, async () => { expect(vault.encumberedDebt.toNumber()).toBeCloseTo( expectedEncumberedDebt.toNumber() ); - // expect(vault.ilkPrice).toEqual(expectedInk); + expect(vault.collateralTypePrice.toString()).toEqual( + expectedColTypePrice.toString() + ); expect(vault.debtValue.toString()).toEqual(expectedDebtValue.toString()); expect(vault.collateralValue.toString()).toEqual( expectedCollateralValue.toString() diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts index 68a37ec9c..edb283914 100644 --- a/packages/dai-plugin-mcd/typings/multicall.d.ts +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -2,6 +2,15 @@ declare global { interface Currency {} interface CurrencyRatio {} interface VaultResult { + /** + * The collateral type of this vault. + * + * ```ts + * console.log('test code'); + * ``` + */ + vaultType: string; + /** * The address of this vault. * @@ -14,6 +23,24 @@ declare global { */ vaultAddress: string; + /** + * Lorem ipsum. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + */ + encumberedCollateral: Currency; + + /** + * Lorem ipsum. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + */ + encumberedDebt: Currency; + /** * The price of this vault's collateral type. * @@ -24,22 +51,33 @@ declare global { collateralTypePrice: CurrencyRatio; /** - * Lorem ipsum. + * The value in DAI of this vault's debt. * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod * tempor incididunt ut labore et dolore magna aliqua. * */ - encumberedCollateral: Currency; + debtValue: Currency; /** - * Lorem ipsum. + * The value in USD of this vault's locked collateral. * * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod * tempor incididunt ut labore et dolore magna aliqua. * */ - encumberedDebt: Currency; + collateralValue: Currency; + + /** + * The maximum amount DAI available to generate for this vault. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + * tempor incididunt ut labore et dolore magna aliqua. + * + */ + daiAvailable: Currency; + + } interface WatchInterfaceMcd { @@ -131,6 +169,21 @@ declare global { */ static maxAuctionLotSize(collateralTypeName: string): BigNumber; + /** + * @param id Numerical id of the vault + */ + static debtValue(id: number): Currency; + + /** + * @param id Numerical id of the vault + */ + static collateralValue(id: number): Currency; + + /** + * @param id Numerical id of the vault + */ + static daiAvailable(id: number): Currency; + /** * Get a vault by id. * @@ -143,7 +196,7 @@ declare global { * * @param id Numerical id of the vault */ - static vaultById(id: number): VaultResult; + static vault(id: number): VaultResult; } } From 2f0572d1cb46ef70034e3043436113362bc6e7fc Mon Sep 17 00:00:00 2001 From: sirromdev Date: Wed, 22 Jan 2020 21:48:09 +0000 Subject: [PATCH 070/117] Retrieving token balances --- .../dai-plugin-mcd/src/schemas/constants.js | 4 ++ packages/dai-plugin-mcd/src/schemas/token.js | 31 +++++++++++++ .../dai-plugin-mcd/test/schemas/token.spec.js | 46 +++++++++++++++++++ packages/dai/src/eth/MulticallService.js | 5 +- 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 packages/dai-plugin-mcd/src/schemas/token.js create mode 100644 packages/dai-plugin-mcd/test/schemas/token.spec.js diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index ee4294159..b6c424d57 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -48,3 +48,7 @@ export const SAVINGS_DAI = 'savingsDai'; export const DEBT_VALUE = 'debtValue'; export const COLLATERAL_VALUE = 'collateralValue'; export const DAI_AVAILABLE = 'daiAvailable'; + +// token +export const TOKEN_BALANCE = 'tokenBalance'; +export const BALANCE = 'balance'; diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js new file mode 100644 index 000000000..1b1822859 --- /dev/null +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -0,0 +1,31 @@ +import { TOKEN_BALANCE } from './constants'; +import { fromWei } from '../utils'; + +export const tokenBalance = { + generate: (address, symbol) => ({ + id: `balance.${symbol}.${address}`, + contractName: symbol === 'ETH' ? 'MULTICALL' : symbol, + call: [ + symbol === 'ETH' + ? 'getEthBalance(address)(uint256)' + : 'balanceOf(address)(uint256)', + address + ] + }), + returns: [[TOKEN_BALANCE, fromWei]] +}; + +export const balance = { + generate: symbol => ({ + dependencies: ({ get }) => { + const address = get('web3').currentAddress(); + return [[TOKEN_BALANCE, address, symbol]]; + }, + computed: v => v + }) +}; + +export default { + tokenBalance, + balance +}; diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js new file mode 100644 index 000000000..8e2e451cd --- /dev/null +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -0,0 +1,46 @@ +import { mcdMaker } from '../helpers'; +import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import { ETH, BAT } from '../../src'; +import BigNumber from 'bignumber.js'; + +import { TOKEN_BALANCE, BALANCE } from '../../src/schemas'; + +import tokenSchemas from '../../src/schemas/token'; + +let maker, snapshotData, address; + +beforeAll(async () => { + maker = await mcdMaker({ + cdpTypes: [ + { currency: ETH, ilk: 'ETH-A' }, + { currency: BAT, ilk: 'BAT-A' } + ], + multicall: true + }); + + snapshotData = await takeSnapshot(maker); + maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').registerSchemas(tokenSchemas); + maker.service('multicall').start(); + address = maker.currentAddress(); +}); + +afterAll(async () => { + await restoreSnapshot(snapshotData, maker); +}); + +test(TOKEN_BALANCE, async () => { + const ethBalance = await maker.latest(TOKEN_BALANCE, address, 'ETH'); + const batBalance = await maker.latest(TOKEN_BALANCE, address, 'BAT'); + + expect(ethBalance).toEqual(BigNumber('94.69019922')); + expect(batBalance).toEqual(BigNumber('1000')); +}); + +test(BALANCE, async () => { + const ethBalance = await maker.latest(BALANCE, 'ETH'); + const batBalance = await maker.latest(BALANCE, 'BAT'); + + expect(ethBalance).toEqual(BigNumber('94.69019922')); + expect(batBalance).toEqual(BigNumber('1000')); +}); diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index cbef3beff..301150fd6 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -174,7 +174,10 @@ export default class MulticallService extends PublicService { // Handle dynamically generated dependencies const dependencies = typeof generatedSchema.dependencies === 'function' - ? generatedSchema.dependencies(this.watchObservable.bind(this)) + ? generatedSchema.dependencies({ + watch: this.watchObservable.bind(this), + get: this.get.bind(this) + }) : generatedSchema.dependencies; const recurseDependencyTree = trie => { From 2d6b38d5829c19e0d6a4dec1a494f860cc984239 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 23 Jan 2020 16:34:28 +0800 Subject: [PATCH 071/117] Add throttle to combineLatest updates --- packages/dai/src/eth/MulticallService.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 301150fd6..7c439608b 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,8 +1,8 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { Observable, ReplaySubject, combineLatest, from } from 'rxjs'; -import { map, first, flatMap } from 'rxjs/operators'; +import { Observable, ReplaySubject, combineLatest, from, interval } from 'rxjs'; +import { map, first, flatMap, throttle } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -196,6 +196,7 @@ export default class MulticallService extends PublicService { if (Array.isArray(trie) && !atLeafNode) { if (trie.length > 1) { return combineLatest(trie.map(recurseDependencyTree)).pipe( + throttle(() => interval(1)), flatMap(result => { return this.watchObservable(key, ...result); }) @@ -220,6 +221,7 @@ export default class MulticallService extends PublicService { const dependencySubs = dependencies.map(recurseDependencyTree); const observerLatest = combineLatest(dependencySubs).pipe( + throttle(() => interval(1)), map(result => generatedSchema.computed(...result)) ); From abe049e10da5a044b2bc140602dfa5ecbec8afd1 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 23 Jan 2020 15:05:36 +0000 Subject: [PATCH 072/117] Fixed return values in generated function for base observables --- packages/dai-plugin-mcd/src/index.js | 12 +++++ packages/dai-plugin-mcd/src/schemas/token.js | 49 ++++++++++++++----- packages/dai-plugin-mcd/src/utils.js | 5 ++ .../test/schemas/computed.spec.js | 2 +- .../dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- .../dai-plugin-mcd/test/schemas/token.spec.js | 38 ++++++++++---- packages/dai/src/eth/MulticallService.js | 27 +++++++++- 7 files changed, 111 insertions(+), 24 deletions(-) diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index 067340e76..56e51e9bc 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -83,6 +83,18 @@ export const defaultCdpTypes = [ { currency: BAT, ilk: 'BAT-A' } ]; +export const SAI = createCurrency('SAI'); + +export const defaultTokens = [ + ...new Set([ + ...defaultCdpTypes.map(type => type.currency), + MDAI, + MWETH, + SAI, + DSR_DAI + ]) +]; + export default { addConfig: ( _, diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js index 1b1822859..85f08933d 100644 --- a/packages/dai-plugin-mcd/src/schemas/token.js +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -1,18 +1,35 @@ import { TOKEN_BALANCE } from './constants'; -import { fromWei } from '../utils'; +import { fromWei, getMcdToken } from '../utils'; +import { defaultTokens } from '../'; export const tokenBalance = { - generate: (address, symbol) => ({ - id: `balance.${symbol}.${address}`, - contractName: symbol === 'ETH' ? 'MULTICALL' : symbol, - call: [ - symbol === 'ETH' - ? 'getEthBalance(address)(uint256)' - : 'balanceOf(address)(uint256)', - address - ] - }), - returns: [[TOKEN_BALANCE, fromWei]] + generate: (address, symbol) => { + if (symbol === 'WETH') symbol = 'MWETH'; + if (symbol === 'DAI') symbol = 'MDAI'; + + const currencyToken = getMcdToken(symbol); + const contract = + symbol === 'MDAI' ? 'MCD_DAI' : symbol === 'MWETH' ? 'ETH' : symbol; + if (!currencyToken) + throw new Error(`${symbol} token is not part of the default tokens list`); + if (symbol === 'DSR-DAI') + throw new Error( + "Balance of DAI in savings cannot be retrieved from a token contract call. To get DAI balance in savings call 'balance('DSR-DAI')'" + ); + + return { + id: `balance.${symbol}.${address}`, + contractName: symbol === 'ETH' ? 'MULTICALL' : contract, + call: [ + symbol === 'ETH' + ? 'getEthBalance(address)(uint256)' + : 'balanceOf(address)(uint256)', + address + ], + returns: [TOKEN_BALANCE, v => currencyToken(v, 'wei')] + }; + }, + returns: [TOKEN_BALANCE] }; export const balance = { @@ -25,6 +42,14 @@ export const balance = { }) }; +// export const allowance = { +// generate: (address, proxyAddress, symbol) => { +// if (symbol ===) +// return {} +// } + +export const tokenAllowance = {}; + export default { tokenBalance, balance diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index 3d9ff5b5f..5ff6b1581 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -2,6 +2,8 @@ import assert from 'assert'; import BigNumber from 'bignumber.js'; import Web3 from 'web3'; import { Currency } from '@makerdao/currency'; +import { defaultTokens } from '.'; + const web3Utils = new Web3().utils; export function stringToBytes(str) { @@ -70,3 +72,6 @@ export function isCurrency(value) { export const isValidAddressString = addressString => /^0x([A-Fa-f0-9]{40})$/.test(addressString); + +export const getMcdToken = token => + defaultTokens.find(mcdToken => mcdToken.symbol === token); diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 1870866c4..eff65cbe3 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -176,5 +176,5 @@ test(VAULT, async () => { test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); expect(savingsDai.symbol).toEqual('DSR-DAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 5); + expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 4); }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index 9041c7c4a..8a67e0d93 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -57,7 +57,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(totalSavingsDai.symbol).toEqual('DSR-DAI'); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 5); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 4); }); test(SAVINGS_DAI_BY_PROXY, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js index 8e2e451cd..3c9d3faba 100644 --- a/packages/dai-plugin-mcd/test/schemas/token.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -1,6 +1,6 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import { ETH, BAT } from '../../src'; +import { ETH, BAT, MWETH, MDAI } from '../../src'; import BigNumber from 'bignumber.js'; import { TOKEN_BALANCE, BALANCE } from '../../src/schemas'; @@ -30,17 +30,37 @@ afterAll(async () => { }); test(TOKEN_BALANCE, async () => { + expect.assertions(8); + const ethBalance = await maker.latest(TOKEN_BALANCE, address, 'ETH'); const batBalance = await maker.latest(TOKEN_BALANCE, address, 'BAT'); - expect(ethBalance).toEqual(BigNumber('94.69019922')); - expect(batBalance).toEqual(BigNumber('1000')); -}); + expect(ethBalance.symbol).toEqual('ETH'); + expect(batBalance.symbol).toEqual('BAT'); + expect(ethBalance.toBigNumber()).toEqual(BigNumber('94.69019922')); + expect(batBalance.toBigNumber()).toEqual(BigNumber('1000')); + + const daiBalance = await maker.latest(TOKEN_BALANCE, address, 'DAI'); + const wethBalance = await maker.latest(TOKEN_BALANCE, address, 'WETH'); + + expect(daiBalance.symbol).toEqual('MDAI'); + expect(wethBalance.symbol).toEqual('MWETH'); -test(BALANCE, async () => { - const ethBalance = await maker.latest(BALANCE, 'ETH'); - const batBalance = await maker.latest(BALANCE, 'BAT'); + try { + await maker.latest(TOKEN_BALANCE, address, 'NON_MCD_TOKEN'); + } catch (e) { + expect(e).toEqual( + Error('NON_MCD_TOKEN token is not part of the default tokens list') + ); + } - expect(ethBalance).toEqual(BigNumber('94.69019922')); - expect(batBalance).toEqual(BigNumber('1000')); + try { + await maker.latest(TOKEN_BALANCE, address, 'DSR-DAI'); + } catch (e) { + expect(e).toEqual( + Error( + "Balance of DAI in savings cannot be retrieved from a token contract call. To get DAI balance in savings call 'balance('DSR-DAI')'" + ) + ); + } }); diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 7c439608b..51d66c2ed 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -152,7 +152,32 @@ export default class MulticallService extends PublicService { }` ); - const generatedSchema = schema.generate(...args); + let generatedSchema = schema.generate(...args); + + // check only in base observables + if (!schema.computed) { + if (generatedSchema.returns && schema.returns) { + const isArrayReturn = Array.isArray(generatedSchema.returns[0]); + if ( + !(isArrayReturn && Array.isArray(schema.returns[0])) && + !( + typeof generatedSchema.returns[0] === 'string' && + typeof schema.returns[0] === 'string' + ) + ) { + throw new Error('Both return values must have same structure'); + } + generatedSchema = { + ...generatedSchema, + returns: isArrayReturn + ? generatedSchema.returns.map(([_, cb]) => + [fullPath, cb].filter(x => x) + ) + : [[fullPath, generatedSchema.returns[1]].filter(x => x)] + }; + } + } + log2( `watchObservable() called for ${ generatedSchema.computed ? 'computed ' : 'base ' From 974980f6f5224361551732b3c969e013ab7e67a8 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Thu, 23 Jan 2020 16:00:04 +0000 Subject: [PATCH 073/117] Making calculations for dai locked in dsr + token balances --- .../dai-plugin-mcd/src/schemas/computed.js | 49 ++++++++++++--- .../dai-plugin-mcd/src/schemas/constants.js | 6 +- packages/dai-plugin-mcd/src/schemas/pot.js | 21 +++++-- packages/dai-plugin-mcd/src/schemas/token.js | 24 +------ .../test/schemas/computed.spec.js | 63 ++++++++++++++++--- .../dai-plugin-mcd/test/schemas/pot.spec.js | 14 ++--- 6 files changed, 123 insertions(+), 54 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 084ca47fa..08d59ae8f 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -4,7 +4,7 @@ import { daiAvailable as calcDaiAvailable } from '../math'; import { defaultCdpTypes } from '../'; -import { USD, MDAI } from '../'; +import { USD, MDAI, DSR_DAI } from '../'; import { RATIO_DAI_USD, @@ -15,13 +15,17 @@ import { VAULT_ADDRESS, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, - SAVINGS_DAI_BY_PROXY, + SAVINGS_DAI, + TOTAL_SAVINGS_DAI, PROXY_ADDRESS, DEBT_SCALING_FACTOR, DEBT_VALUE, COLLATERAL_VALUE, DAI_AVAILABLE, - RAW_LIQUIDATION_RATIO + RAW_LIQUIDATION_RATIO, + SAVINGS_RATE_ACCUMULATOR, + DAI_LOCKED_IN_DSR, + TOKEN_BALANCE } from './constants'; export const collateralTypePrice = { @@ -160,10 +164,37 @@ export const vault = { }) }; -export const savingsDai = { - generate: address => ({ - dependencies: [[SAVINGS_DAI_BY_PROXY, [PROXY_ADDRESS, address]]], - computed: savingsDai => savingsDai +export const daiLockedInDsr = { + generate: () => ({ + dependencies: ({ get }) => [ + [SAVINGS_DAI, [PROXY_ADDRESS, get('web3').currentAddress()]], + [SAVINGS_RATE_ACCUMULATOR] + ], + computed: (savingsDai, savingsRateAccumulator) => { + return DSR_DAI(savingsDai.times(savingsRateAccumulator)); + } + }) +}; + +export const totalDaiLockedInDsr = { + generate: () => ({ + dependencies: [[TOTAL_SAVINGS_DAI], [SAVINGS_RATE_ACCUMULATOR]], + computed: (totalSavingsDai, savingsRateAccumulator) => { + return DSR_DAI(totalSavingsDai.times(savingsRateAccumulator)); + } + }) +}; + +export const balance = { + generate: symbol => ({ + dependencies: ({ get }) => { + const address = get('web3').currentAddress(); + if (symbol === 'DSR-DAI') { + return [[DAI_LOCKED_IN_DSR]]; + } + return [[TOKEN_BALANCE, address, symbol]]; + }, + computed: v => v }) }; @@ -176,5 +207,7 @@ export default { collateralValue, debtValue, daiAvailable, - savingsDai + daiLockedInDsr, + totalDaiLockedInDsr, + balance }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index b6c424d57..0b6795189 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -28,10 +28,11 @@ export const VAULT_TYPE = 'vaultType'; // pot export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; -export const SAVINGS_DAI_BY_PROXY = 'savingsDaiByProxy'; +export const SAVINGS_DAI = 'savingsDai'; export const DAI_SAVINGS_RATE = 'daiSavingsRate'; export const ANNUAL_DAI_SAVINGS_RATE = 'annualDaiSavingsRate'; export const DATE_EARNINGS_LAST_ACCRUED = 'dateEarningsLastAccrued'; +export const SAVINGS_RATE_ACCUMUALTOR = 'savingsRateAccumulator'; // cat export const LIQUIDATOR_ADDRESS = 'liquidatorAddress'; @@ -44,7 +45,8 @@ export const COLLATERAL_TYPES_PRICES = 'collateralTypesPrices'; export const VAULT_TYPE_AND_ADDRESS = 'vaultTypeAndAddress'; export const VAULT_COLLATERAL_AND_DEBT = 'vaultCollateralAndDebt'; export const VAULT = 'vault'; -export const SAVINGS_DAI = 'savingsDai'; +export const DAI_LOCKED_IN_DSR = 'daiLockedInDsr'; +export const TOTAL_DAI_LOCKED_IN_DSR = 'totalDaiLockedInDsr'; export const DEBT_VALUE = 'debtValue'; export const COLLATERAL_VALUE = 'collateralValue'; export const DAI_AVAILABLE = 'daiAvailable'; diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js index 1fd2ecfce..344ca338b 100644 --- a/packages/dai-plugin-mcd/src/schemas/pot.js +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -1,11 +1,11 @@ -import { fromRay } from '../utils'; -import { DSR_DAI } from '..'; +import { fromRay, fromWei } from '../utils'; import { TOTAL_SAVINGS_DAI, - SAVINGS_DAI_BY_PROXY, + SAVINGS_DAI, DAI_SAVINGS_RATE, - DATE_EARNINGS_LAST_ACCRUED + DATE_EARNINGS_LAST_ACCRUED, + SAVINGS_RATE_ACCUMULATOR } from './constants'; export const potPie = { @@ -14,7 +14,7 @@ export const potPie = { contractName: 'MCD_POT', call: ['Pie()(uint256)'] }), - returns: [[TOTAL_SAVINGS_DAI, v => DSR_DAI(v, 'wei')]] + returns: [[TOTAL_SAVINGS_DAI, fromWei]] }; export const potpie = { @@ -23,7 +23,7 @@ export const potpie = { contractName: 'MCD_POT', call: ['pie(address)(uint256)', proxyAddress] }), - returns: [[SAVINGS_DAI_BY_PROXY, v => DSR_DAI(v, 'wei')]] + returns: [[SAVINGS_DAI, fromWei]] }; export const potDsr = { @@ -34,6 +34,14 @@ export const potDsr = { }), returns: [[DAI_SAVINGS_RATE, fromRay]] }; +export const potChi = { + generate: () => ({ + id: 'MCD_POT.dsr', + contractName: 'MCD_POT', + call: ['chi()(uint256)'] + }), + returns: [[SAVINGS_RATE_ACCUMULATOR, fromRay]] +}; export const potRho = { generate: () => ({ @@ -62,6 +70,7 @@ export default { potpie, potDsr, potRho, + potChi, // computed annualDaiSavingsRate diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js index 85f08933d..3fb3118d1 100644 --- a/packages/dai-plugin-mcd/src/schemas/token.js +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -1,6 +1,5 @@ import { TOKEN_BALANCE } from './constants'; -import { fromWei, getMcdToken } from '../utils'; -import { defaultTokens } from '../'; +import { getMcdToken } from '../utils'; export const tokenBalance = { generate: (address, symbol) => { @@ -32,25 +31,6 @@ export const tokenBalance = { returns: [TOKEN_BALANCE] }; -export const balance = { - generate: symbol => ({ - dependencies: ({ get }) => { - const address = get('web3').currentAddress(); - return [[TOKEN_BALANCE, address, symbol]]; - }, - computed: v => v - }) -}; - -// export const allowance = { -// generate: (address, proxyAddress, symbol) => { -// if (symbol ===) -// return {} -// } - -export const tokenAllowance = {}; - export default { - tokenBalance, - balance + tokenBalance }; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index eff65cbe3..2b44662ab 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -3,27 +3,31 @@ import { ETH, BAT, MDAI, USD } from '../../src'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; +import BigNumber from 'bignumber.js'; import { COLLATERAL_TYPE_PRICE, COLLATERAL_TYPES_PRICES, VAULT_TYPE_AND_ADDRESS, VAULT, - SAVINGS_DAI, DEBT_VALUE, + DAI_LOCKED_IN_DSR, + TOTAL_DAI_LOCKED_IN_DSR, COLLATERAL_VALUE, - DAI_AVAILABLE + DAI_AVAILABLE, + BALANCE } from '../../src/schemas'; import { vatIlks, vatUrns } from '../../src/schemas/vat'; import { cdpManagerUrns, cdpManagerIlks } from '../../src/schemas/cdpManager'; import { spotIlks, liquidationRatio, spotPar } from '../../src/schemas/spot'; import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; -import { potpie } from '../../src/schemas/pot'; +import { potPie, potpie, potChi } from '../../src/schemas/pot'; +import { tokenBalance } from '../../src/schemas/token'; import computedSchemas from '../../src/schemas/computed'; import { createCurrencyRatio } from '@makerdao/currency'; -let maker, address, snapshotData; +let maker, snapshotData; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -52,13 +56,15 @@ beforeAll(async () => { spotPar, spotIlks, proxyRegistryProxies, + potPie, potpie, + potChi, liquidationRatio, + tokenBalance, ...computedSchemas }); maker.service('multicall').start(); - address = maker.service('web3').currentAddress(); await setupCollateral(maker, 'ETH-A', { price: ETH_A_PRICE }); @@ -173,8 +179,47 @@ test(VAULT, async () => { ); }); -test(SAVINGS_DAI, async () => { - const savingsDai = await maker.latest(SAVINGS_DAI, address); - expect(savingsDai.symbol).toEqual('DSR-DAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 4); +test(DAI_LOCKED_IN_DSR, async () => { + const daiLockedInDsr = await maker.latest(DAI_LOCKED_IN_DSR); + expect(daiLockedInDsr.symbol).toEqual('DSR-DAI'); + expect(daiLockedInDsr.toNumber()).toBeCloseTo(1, 18); +}); + +test(TOTAL_DAI_LOCKED_IN_DSR, async () => { + const totalDaiLockedInDsr = await maker.latest(DAI_LOCKED_IN_DSR); + expect(totalDaiLockedInDsr.symbol).toEqual('DSR-DAI'); + expect(totalDaiLockedInDsr.toNumber()).toBeCloseTo(1, 18); +}); + +test(BALANCE, async () => { + expect.assertions(11); + + const ethBalance = await maker.latest(BALANCE, 'ETH'); + const batBalance = await maker.latest(BALANCE, 'BAT'); + + expect(ethBalance.symbol).toEqual('ETH'); + expect(batBalance.symbol).toEqual('BAT'); + expect(ethBalance.toNumber()).toBeCloseTo(93.675, 2); + expect(batBalance.toBigNumber()).toEqual(BigNumber('999')); + + const daiBalance = await maker.latest(BALANCE, 'DAI'); + const wethBalance = await maker.latest(BALANCE, 'WETH'); + + expect(daiBalance.symbol).toEqual('MDAI'); + expect(daiBalance.toBigNumber()).toEqual(BigNumber('1')); + + expect(wethBalance.symbol).toEqual('MWETH'); + expect(wethBalance.toBigNumber()).toEqual(BigNumber('0')); + + const dsrDaiBalance = await maker.latest(BALANCE, 'DSR-DAI'); + expect(dsrDaiBalance.symbol).toEqual('DSR-DAI'); + expect(dsrDaiBalance.toNumber()).toBeCloseTo(1, 18); + + try { + await maker.latest(BALANCE, 'NON_MCD_TOKEN'); + } catch (e) { + expect(e).toEqual( + Error('NON_MCD_TOKEN token is not part of the default tokens list') + ); + } }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index 8a67e0d93..714f26153 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -6,7 +6,7 @@ import BigNumber from 'bignumber.js'; import { TOTAL_SAVINGS_DAI, - SAVINGS_DAI_BY_PROXY, + SAVINGS_DAI, DAI_SAVINGS_RATE, ANNUAL_DAI_SAVINGS_RATE, DATE_EARNINGS_LAST_ACCRUED @@ -56,17 +56,17 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); - expect(totalSavingsDai.symbol).toEqual('DSR-DAI'); + expect(BigNumber.isBigNumber(totalSavingsDai)).toEqual(true); expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 4); }); -test(SAVINGS_DAI_BY_PROXY, async () => { - const savingsDaiByProxy = await maker.latest( - SAVINGS_DAI_BY_PROXY, +test(SAVINGS_DAI, async () => { + const savingsDai = await maker.latest( + SAVINGS_DAI, await maker.service('proxy').getProxyAddress() ); - expect(savingsDaiByProxy.symbol).toEqual('DSR-DAI'); - expect(savingsDaiByProxy.toNumber()).toBeCloseTo(0.99995); + expect(BigNumber.isBigNumber(savingsDai)).toEqual(true); + expect(savingsDai.toNumber()).toBeCloseTo(0.99995); }); test(DAI_SAVINGS_RATE, async () => { From 017a54b93e648cf83f0efa7e3e4de3164197240c Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Thu, 23 Jan 2020 21:42:09 +0100 Subject: [PATCH 074/117] add additional values to 'vault' schema --- .../dai-plugin-mcd/src/schemas/computed.js | 134 +++++++++++++++--- .../dai-plugin-mcd/src/schemas/constants.js | 6 + .../test/schemas/computed.spec.js | 97 +++++++++++-- 3 files changed, 208 insertions(+), 29 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 084ca47fa..50308a4d7 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -1,7 +1,10 @@ import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; import { collateralValue as calcCollateralValue, - daiAvailable as calcDaiAvailable + daiAvailable as calcDaiAvailable, + collateralizationRatio as calcCollateralizationRatio, + liquidationPrice as calcLiquidationPrice, + minSafeCollateralAmount as calcMinSafeCollateralAmount } from '../math'; import { defaultCdpTypes } from '../'; import { USD, MDAI } from '../'; @@ -19,9 +22,16 @@ import { PROXY_ADDRESS, DEBT_SCALING_FACTOR, DEBT_VALUE, + COLLATERALIZATION_RATIO, + COLLATERAL_AMOUNT, COLLATERAL_VALUE, + LIQUIDATION_PRICE, DAI_AVAILABLE, - RAW_LIQUIDATION_RATIO + MIN_SAFE_COLLATERAL_AMOUNT, + COLLATERAL_AVAILABLE_AMOUNT, + COLLATERAL_AVAILABLE_VALUE, + RAW_LIQUIDATION_RATIO, + UNLOCKED_COLLATERAL } from './constants'; export const collateralTypePrice = { @@ -76,6 +86,31 @@ export const vaultCollateralAndDebt = { }) }; +export const collateralAmount = { + generate: id => ({ + dependencies: [ + [VAULT_TYPE, id], + [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]] + ], + computed: (vaultType, encumberedCollateral) => { + // Todo: better way to get collateral name + const currency = createCurrency(vaultType.substring(0, 3)); + return currency(encumberedCollateral); + } + }) +}; + +export const collateralValue = { + generate: id => ({ + dependencies: [ + [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]], + [COLLATERAL_AMOUNT, id] + ], + computed: (collateralTypePrice, collateralAmount) => + calcCollateralValue(collateralAmount, collateralTypePrice) + }) +}; + export const debtValue = { generate: id => ({ dependencies: [ @@ -88,20 +123,28 @@ export const debtValue = { }) }; -export const collateralValue = { +export const collateralizationRatio = { + generate: id => ({ + dependencies: [[COLLATERAL_VALUE, id], [DEBT_VALUE, id]], + computed: (collateralValue, debtValue) => + calcCollateralizationRatio(collateralValue, debtValue) + }) +}; + +export const liquidationPrice = { generate: id => ({ dependencies: [ - [VAULT_TYPE, id], - [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], - [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]] + [COLLATERAL_AMOUNT, id], + [DEBT_VALUE, id], + [RAW_LIQUIDATION_RATIO, [VAULT_TYPE, id]] ], - computed: (ilkName, encumberedCollateral, collateralTypePrice) => { - // Todo: better way to get collateral name - const currency = createCurrency(ilkName.substring(0, 3)); - // Note: the first arg is collateralAmount calculation - return calcCollateralValue( - currency(encumberedCollateral), - collateralTypePrice + computed: (collateralAmount, debtValue, rawLiquidationRatio) => { + const ratio = createCurrencyRatio(USD, MDAI); + const liquidationRatio = ratio(rawLiquidationRatio.toNumber()); + return calcLiquidationPrice( + collateralAmount, + debtValue, + liquidationRatio ); } }) @@ -122,6 +165,39 @@ export const daiAvailable = { }) }; +export const minSafeCollateralAmount = { + generate: id => ({ + dependencies: [ + [DEBT_VALUE, id], + [RAW_LIQUIDATION_RATIO, [VAULT_TYPE, id]], + [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]] + ], + computed: (debtValue, rawLiquidationRatio, price) => { + const ratio = createCurrencyRatio(USD, MDAI); + const liquidationRatio = ratio(rawLiquidationRatio.toNumber()); + return calcMinSafeCollateralAmount(debtValue, liquidationRatio, price); + } + }) +}; +export const collateralAvailableAmount = { + generate: id => ({ + dependencies: [[COLLATERAL_AMOUNT, id], [MIN_SAFE_COLLATERAL_AMOUNT, id]], + computed: (collateralAmount, minSafeCollateralAmount) => + collateralAmount.minus(minSafeCollateralAmount) + }) +}; + +export const collateralAvailableValue = { + generate: id => ({ + dependencies: [ + [COLLATERAL_AVAILABLE_AMOUNT, id], + [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]] + ], + computed: (collateralAvailableAmount, collateralTypePrice) => + calcCollateralValue(collateralAvailableAmount, collateralTypePrice) + }) +}; + export const vault = { generate: id => ({ dependencies: [ @@ -131,12 +207,14 @@ export const vault = { [ENCUMBERED_DEBT, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]], [DEBT_VALUE, id], + [COLLATERALIZATION_RATIO, id], + [COLLATERAL_AMOUNT, id], [COLLATERAL_VALUE, id], + [LIQUIDATION_PRICE, id], [DAI_AVAILABLE, id], - [VAULT_TYPE, id], - [VAULT_ADDRESS, id], - [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], - [ENCUMBERED_DEBT, [VAULT_TYPE, id], [VAULT_ADDRESS, id]] + [COLLATERAL_AVAILABLE_AMOUNT, id], + [COLLATERAL_AVAILABLE_VALUE, id], + [UNLOCKED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]] ], computed: ( vaultType, @@ -145,8 +223,14 @@ export const vault = { encumberedDebt, collateralTypePrice, debtValue, + collateralizationRatio, + collateralAmount, collateralValue, - daiAvailable + liquidationPrice, + daiAvailable, + collateralAvailableAmount, + collateralAvailableValue, + unlockedCollateral ) => ({ vaultType, vaultAddress, @@ -154,8 +238,14 @@ export const vault = { encumberedDebt, collateralTypePrice, debtValue, + collateralizationRatio, + collateralAmount, collateralValue, - daiAvailable + liquidationPrice, + daiAvailable, + collateralAvailableAmount, + collateralAvailableValue, + unlockedCollateral }) }) }; @@ -173,8 +263,14 @@ export default { vaultTypeAndAddress, vaultCollateralAndDebt, vault, + collateralAmount, collateralValue, debtValue, + collateralizationRatio, + liquidationPrice, daiAvailable, + minSafeCollateralAmount, + collateralAvailableAmount, + collateralAvailableValue, savingsDai }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index b6c424d57..18016accc 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -46,8 +46,14 @@ export const VAULT_COLLATERAL_AND_DEBT = 'vaultCollateralAndDebt'; export const VAULT = 'vault'; export const SAVINGS_DAI = 'savingsDai'; export const DEBT_VALUE = 'debtValue'; +export const COLLATERALIZATION_RATIO = 'collateralizationRatio'; +export const COLLATERAL_AMOUNT = 'collateralAmount'; export const COLLATERAL_VALUE = 'collateralValue'; +export const LIQUIDATION_PRICE = 'liquidationPrice'; export const DAI_AVAILABLE = 'daiAvailable'; +export const MIN_SAFE_COLLATERAL_AMOUNT = 'minSafeCollateralAmount'; +export const COLLATERAL_AVAILABLE_AMOUNT = 'collateralAvailableAmount'; +export const COLLATERAL_AVAILABLE_VALUE = 'collateralAvailableValue'; // token export const TOKEN_BALANCE = 'tokenBalance'; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 1870866c4..2ce476c03 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -11,11 +11,17 @@ import { VAULT, SAVINGS_DAI, DEBT_VALUE, + COLLATERALIZATION_RATIO, + COLLATERAL_AMOUNT, COLLATERAL_VALUE, - DAI_AVAILABLE + LIQUIDATION_PRICE, + DAI_AVAILABLE, + MIN_SAFE_COLLATERAL_AMOUNT, + COLLATERAL_AVAILABLE_AMOUNT, + COLLATERAL_AVAILABLE_VALUE } from '../../src/schemas'; -import { vatIlks, vatUrns } from '../../src/schemas/vat'; +import { vatIlks, vatUrns, vatGem } from '../../src/schemas/vat'; import { cdpManagerUrns, cdpManagerIlks } from '../../src/schemas/cdpManager'; import { spotIlks, liquidationRatio, spotPar } from '../../src/schemas/spot'; import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; @@ -47,6 +53,7 @@ beforeAll(async () => { maker.service('multicall').registerSchemas({ vatIlks, vatUrns, + vatGem, cdpManagerUrns, cdpManagerIlks, spotPar, @@ -116,12 +123,12 @@ test(VAULT_TYPE_AND_ADDRESS, async () => { expect(vaultAddress).toEqual(expectedVaultAddress); }); -test(DEBT_VALUE, async () => { +test(COLLATERAL_AMOUNT, async () => { const cdpId = 1; - const debtValue = await maker.latest(DEBT_VALUE, cdpId); - const expected = MDAI(1); + const collateralAmount = await maker.latest(COLLATERAL_AMOUNT, cdpId); + const expected = ETH(1); - expect(debtValue.toNumber()).toEqual(expected.toNumber()); + expect(collateralAmount.toString()).toEqual(expected.toString()); }); test(COLLATERAL_VALUE, async () => { @@ -132,6 +139,30 @@ test(COLLATERAL_VALUE, async () => { expect(collateralValue.toString()).toEqual(expected.toString()); }); +test(COLLATERALIZATION_RATIO, async () => { + const cdpId = 1; + const colRatio = await maker.latest(COLLATERALIZATION_RATIO, cdpId); + const expected = createCurrencyRatio(USD, MDAI)(180); + + expect(colRatio.toString()).toEqual(expected.toString()); +}); + +test(DEBT_VALUE, async () => { + const cdpId = 1; + const debtValue = await maker.latest(DEBT_VALUE, cdpId); + const expected = MDAI(1); + + expect(debtValue.toNumber()).toEqual(expected.toNumber()); +}); + +test(LIQUIDATION_PRICE, async () => { + const cdpId = 1; + const liqPrice = await maker.latest(LIQUIDATION_PRICE, cdpId); + const expected = createCurrencyRatio(USD, ETH)(1.5); + + expect(liqPrice.toString()).toEqual(expected.toString()); +}); + test(DAI_AVAILABLE, async () => { const cdpId = 1; const daiAvailable = await maker.latest(DAI_AVAILABLE, cdpId); @@ -140,20 +171,50 @@ test(DAI_AVAILABLE, async () => { expect(daiAvailable.toString()).toEqual(expected.toString()); }); +test(MIN_SAFE_COLLATERAL_AMOUNT, async () => { + const cdpId = 1; + const minSafe = await maker.latest(MIN_SAFE_COLLATERAL_AMOUNT, cdpId); + const expected = ETH(0.01); + + expect(minSafe.toString()).toEqual(expected.toString()); +}); + +test(COLLATERAL_AVAILABLE_AMOUNT, async () => { + const cdpId = 1; + const avail = await maker.latest(COLLATERAL_AVAILABLE_AMOUNT, cdpId); + const expected = ETH(0.99); + + expect(avail.toString()).toEqual(expected.toString()); +}); + +test(COLLATERAL_AVAILABLE_VALUE, async () => { + const cdpId = 1; + const value = await maker.latest(COLLATERAL_AVAILABLE_VALUE, cdpId); + const expected = USD(178.5); + + expect(value.toString()).toEqual(expected.toString()); +}); + test(VAULT, async () => { const cdpId = 1; const expectedVaultType = 'ETH-A'; const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const expectedEncumberedCollateral = fromWei(1000000000000000000); const expectedEncumberedDebt = fromWei(995000000000000000); + const expectedColTypePrice = createCurrencyRatio(USD, ETH)(180); const expectedDebtValue = MDAI(1); + const expectedColRatio = createCurrencyRatio(USD, MDAI)(180); + const expectedCollateralAmount = ETH(1); const expectedCollateralValue = USD(180); + const expectedLiquidationPrice = createCurrencyRatio(USD, ETH)(1.5); const expectedDaiAvailable = MDAI(119); - const expectedColTypePrice = createCurrencyRatio(USD, ETH)(180); + const expectedCollateralAvailableAmount = ETH(0.99); + const expectedCollateralAvailableValue = USD(178.5); + const expectedUnlockedCollateral = fromWei(0); const vault = await maker.latest(VAULT, cdpId); - expect(Object.keys(vault).length).toBe(8); + expect(Object.keys(vault).length).toBe(14); expect(vault.vaultType).toEqual(expectedVaultType); expect(vault.vaultAddress).toEqual(expectedVaultAddress); @@ -164,17 +225,33 @@ test(VAULT, async () => { expect(vault.collateralTypePrice.toString()).toEqual( expectedColTypePrice.toString() ); - expect(vault.debtValue.toString()).toEqual(expectedDebtValue.toString()); + expect(vault.collateralAmount.toString()).toEqual( + expectedCollateralAmount.toString() + ); expect(vault.collateralValue.toString()).toEqual( expectedCollateralValue.toString() ); + expect(vault.debtValue.toString()).toEqual(expectedDebtValue.toString()); + expect(vault.collateralizationRatio.toString()).toEqual( + expectedColRatio.toString() + ); + expect(vault.liquidationPrice.toString()).toEqual( + expectedLiquidationPrice.toString() + ); expect(vault.daiAvailable.toString()).toEqual( expectedDaiAvailable.toString() ); + expect(vault.collateralAvailableAmount.toString()).toEqual( + expectedCollateralAvailableAmount.toString() + ); + expect(vault.collateralAvailableValue.toString()).toEqual( + expectedCollateralAvailableValue.toString() + ); + expect(vault.unlockedCollateral).toEqual(expectedUnlockedCollateral); }); test(SAVINGS_DAI, async () => { const savingsDai = await maker.latest(SAVINGS_DAI, address); expect(savingsDai.symbol).toEqual('DSR-DAI'); - expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 5); + expect(savingsDai.toNumber()).toBeCloseTo(0.999795, 4); }); From 34adb4ff5cfd98519f67cbc1aec32d0a68862e2d Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 24 Jan 2020 11:40:22 +0100 Subject: [PATCH 075/117] refactor some spot and jug schemas to return bignumber & add them to vault schema --- packages/dai-plugin-mcd/src/schemas/cat.js | 6 ++-- .../dai-plugin-mcd/src/schemas/computed.js | 34 ++++++++++++++++--- .../dai-plugin-mcd/src/schemas/constants.js | 1 + packages/dai-plugin-mcd/src/schemas/jug.js | 14 ++++++-- .../dai-plugin-mcd/test/schemas/cat.spec.js | 2 +- .../test/schemas/computed.spec.js | 12 ++++++- .../dai-plugin-mcd/test/schemas/jug.spec.js | 2 +- 7 files changed, 58 insertions(+), 13 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cat.js b/packages/dai-plugin-mcd/src/schemas/cat.js index f6f3cfa20..4459bb59a 100644 --- a/packages/dai-plugin-mcd/src/schemas/cat.js +++ b/packages/dai-plugin-mcd/src/schemas/cat.js @@ -1,6 +1,6 @@ -import { toHex } from '../utils'; +import { toHex, fromRay } from '../utils'; import BigNumber from 'bignumber.js'; -import { liquidationPenalty } from '../math'; +import { RAY } from '../constants'; import { LIQUIDATOR_ADDRESS, @@ -16,7 +16,7 @@ export const catIlks = { }), returns: [ [LIQUIDATOR_ADDRESS], - [LIQUIDATION_PENALTY, liquidationPenalty], + [LIQUIDATION_PENALTY, v => fromRay(BigNumber(v).minus(RAY))], [MAX_AUCTION_LOT_SIZE, v => BigNumber(v).shiftedBy(-18)] ] }; diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 863895e34..b397d995f 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -35,7 +35,10 @@ import { UNLOCKED_COLLATERAL, SAVINGS_RATE_ACCUMULATOR, DAI_LOCKED_IN_DSR, - TOKEN_BALANCE + TOKEN_BALANCE, + LIQUIDATION_RATIO_SIMPLE, + LIQUIDATION_PENALTY, + ANNUAL_STABILITY_FEE } from './constants'; export const collateralTypePrice = { @@ -202,6 +205,17 @@ export const collateralAvailableValue = { }) }; +export const liquidationRatioSimple = { + generate: id => ({ + dependencies: [[RAW_LIQUIDATION_RATIO, [VAULT_TYPE, id]]], + computed: rawLiquidationRatio => { + const ratio = createCurrencyRatio(USD, MDAI); + const liquidationRatio = ratio(rawLiquidationRatio.toNumber()); + return liquidationRatio; + } + }) +}; + export const vault = { generate: id => ({ dependencies: [ @@ -218,7 +232,10 @@ export const vault = { [DAI_AVAILABLE, id], [COLLATERAL_AVAILABLE_AMOUNT, id], [COLLATERAL_AVAILABLE_VALUE, id], - [UNLOCKED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]] + [UNLOCKED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], + [LIQUIDATION_RATIO_SIMPLE, id], + [LIQUIDATION_PENALTY, [VAULT_TYPE, id]], + [ANNUAL_STABILITY_FEE, [VAULT_TYPE, id]] ], computed: ( vaultType, @@ -234,7 +251,10 @@ export const vault = { daiAvailable, collateralAvailableAmount, collateralAvailableValue, - unlockedCollateral + unlockedCollateral, + liquidationRatioSimple, + liquidationPenalty, + annualStabilityFee ) => ({ vaultType, vaultAddress, @@ -249,7 +269,10 @@ export const vault = { daiAvailable, collateralAvailableAmount, collateralAvailableValue, - unlockedCollateral + unlockedCollateral, + liquidationRatioSimple, + liquidationPenalty, + annualStabilityFee }) }) }; @@ -305,5 +328,6 @@ export default { collateralAvailableValue, daiLockedInDsr, totalDaiLockedInDsr, - balance + balance, + liquidationRatioSimple }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 1c0ae5599..cf17d502f 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -56,6 +56,7 @@ export const DAI_AVAILABLE = 'daiAvailable'; export const MIN_SAFE_COLLATERAL_AMOUNT = 'minSafeCollateralAmount'; export const COLLATERAL_AVAILABLE_AMOUNT = 'collateralAvailableAmount'; export const COLLATERAL_AVAILABLE_VALUE = 'collateralAvailableValue'; +export const LIQUIDATION_RATIO_SIMPLE = 'liquidationRatioSimple'; // token export const TOKEN_BALANCE = 'tokenBalance'; diff --git a/packages/dai-plugin-mcd/src/schemas/jug.js b/packages/dai-plugin-mcd/src/schemas/jug.js index 0e9405f5f..f65cc1974 100644 --- a/packages/dai-plugin-mcd/src/schemas/jug.js +++ b/packages/dai-plugin-mcd/src/schemas/jug.js @@ -1,11 +1,14 @@ -import { annualStabilityFee } from '../math'; +import BigNumber from 'bignumber.js'; import { toHex } from '../utils'; +import { RAY } from '../constants'; import { ANNUAL_STABILITY_FEE, DATE_STABILITY_FEES_LAST_LEVIED } from './constants'; +const secondsPerYear = 60 * 60 * 24 * 365; + export const jugIlks = { generate: collateralTypeName => ({ id: `MCD_JUG.ilks(${collateralTypeName})`, @@ -13,7 +16,14 @@ export const jugIlks = { call: ['ilks(bytes32)(uint256,uint48)', toHex(collateralTypeName)] }), returns: [ - [ANNUAL_STABILITY_FEE, annualStabilityFee], + [ + ANNUAL_STABILITY_FEE, + v => { + v = new BigNumber(v.toString()).dividedBy(RAY); + BigNumber.config({ POW_PRECISION: 100 }); + return v.pow(secondsPerYear).minus(1); + } + ], [DATE_STABILITY_FEES_LAST_LEVIED, val => new Date(val * 1000)] ] }; diff --git a/packages/dai-plugin-mcd/test/schemas/cat.spec.js b/packages/dai-plugin-mcd/test/schemas/cat.spec.js index e0c4a0365..40246925b 100644 --- a/packages/dai-plugin-mcd/test/schemas/cat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cat.spec.js @@ -35,7 +35,7 @@ test(LIQUIDATOR_ADDRESS, async () => { }); test(LIQUIDATION_PENALTY, async () => { - const expected = 0.05; + const expected = BigNumber('0.05'); const liquidationPenalty = await maker.latest(LIQUIDATION_PENALTY, 'ETH-A'); expect(liquidationPenalty).toEqual(expected); }); diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 727136d72..e4e058878 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -30,6 +30,8 @@ import { spotIlks, liquidationRatio, spotPar } from '../../src/schemas/spot'; import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; import { potPie, potpie, potChi } from '../../src/schemas/pot'; import { tokenBalance } from '../../src/schemas/token'; +import { catIlks } from '../../src/schemas/cat'; +import { jugIlks } from '../../src/schemas/jug'; import computedSchemas from '../../src/schemas/computed'; import { createCurrencyRatio } from '@makerdao/currency'; @@ -68,6 +70,8 @@ beforeAll(async () => { potChi, liquidationRatio, tokenBalance, + catIlks, + jugIlks, ...computedSchemas }); maker.service('multicall').start(); @@ -217,10 +221,13 @@ test(VAULT, async () => { const expectedCollateralAvailableAmount = ETH(0.99); const expectedCollateralAvailableValue = USD(178.5); const expectedUnlockedCollateral = fromWei(0); + const expectedLiqRatio = createCurrencyRatio(USD, MDAI)(1.5); + const expectedLiqPenalty = BigNumber('0.05'); + const expectedAnnStabilityFee = 0.04999999999989363; const vault = await maker.latest(VAULT, cdpId); - expect(Object.keys(vault).length).toBe(14); + expect(Object.keys(vault).length).toBe(17); expect(vault.vaultType).toEqual(expectedVaultType); expect(vault.vaultAddress).toEqual(expectedVaultAddress); @@ -254,6 +261,9 @@ test(VAULT, async () => { expectedCollateralAvailableValue.toString() ); expect(vault.unlockedCollateral).toEqual(expectedUnlockedCollateral); + expect(vault.liquidationRatioSimple).toEqual(expectedLiqRatio); + expect(vault.liquidationPenalty).toEqual(expectedLiqPenalty); + expect(vault.annualStabilityFee.toNumber()).toEqual(expectedAnnStabilityFee); }); test(DAI_LOCKED_IN_DSR, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/jug.spec.js b/packages/dai-plugin-mcd/test/schemas/jug.spec.js index 9fe579cdc..cb95229c5 100644 --- a/packages/dai-plugin-mcd/test/schemas/jug.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/jug.spec.js @@ -28,7 +28,7 @@ afterAll(async () => { test(ANNUAL_STABILITY_FEE, async () => { const expected = 0.04999999999989363; const annualStabilityFee = await maker.latest(ANNUAL_STABILITY_FEE, 'ETH-A'); - expect(annualStabilityFee).toEqual(expected); + expect(annualStabilityFee.toNumber()).toEqual(expected); }); test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { From cabf3778bd24311687672478f2f5ee0f9bde87f1 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 24 Jan 2020 19:39:35 +0800 Subject: [PATCH 076/117] Add support for transform mapping in generate() and refactor --- packages/dai-plugin-mcd/src/schemas/token.js | 4 +- packages/dai/src/eth/MulticallService.js | 116 +++++++------------ 2 files changed, 45 insertions(+), 75 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js index 3fb3118d1..0a16b5454 100644 --- a/packages/dai-plugin-mcd/src/schemas/token.js +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -25,7 +25,9 @@ export const tokenBalance = { : 'balanceOf(address)(uint256)', address ], - returns: [TOKEN_BALANCE, v => currencyToken(v, 'wei')] + transforms: { + [TOKEN_BALANCE]: v => currencyToken(v, 'wei') + } }; }, returns: [TOKEN_BALANCE] diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 51d66c2ed..1596b55aa 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -120,18 +120,16 @@ export default class MulticallService extends PublicService { schema.watching = {}; // Automatically use schema key as return key if no return keys specified if (!schema.return && !schema.returns) schema.returns = [schema.key]; - if (schema.return && schema.returns) - throw new Error( - 'Ambiguous return definitions in schema: found both return and returns property' - ); + if (schema.return && schema.returns) throw new Error('Ambiguous return definitions in schema: found both return and returns property'); // prettier-ignore if (schema.return) schema.returns = [schema.return]; - if (!Array.isArray(schema.returns)) - throw new Error('Schema must contain return/returns property'); - // Use return keys to create observable keys => schema mapping - schema.returns.forEach(ret => { - if (Array.isArray(ret)) this._schemaByObservableKey[ret[0]] = schema; - else if (typeof ret === 'string') - this._schemaByObservableKey[ret] = schema; + if (!Array.isArray(schema.returns)) throw new Error('Schema must contain return/returns property'); // prettier-ignore + // Use return keys to create observable key => schema mapping + // and normalize as array of [key, transform] arrays + schema.returns = schema.returns.map(ret => { + if (!Array.isArray(ret)) ret = [ret]; + this._schemaByObservableKey[ret[0]] = schema; + if (ret.length > 2) throw new Error('Returns array format should be [key, transform]'); // prettier-ignore + return ret; }); }); this._schemas = [...this._schemas, ...schemas]; @@ -141,59 +139,25 @@ export default class MulticallService extends PublicService { watchObservable(key, ...args) { const path = args.join('.'); const fullPath = `${key}${path ? '.' : ''}${path}`; - const schema = this.schemaByObservableKey(key); if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); if (args.length < schema.generate.length) - throw new Error( - `Observable ${key} expects at least ${schema.generate.length} argument${ - schema.generate.length > 1 ? 's' : '' - }` - ); - - let generatedSchema = schema.generate(...args); - - // check only in base observables - if (!schema.computed) { - if (generatedSchema.returns && schema.returns) { - const isArrayReturn = Array.isArray(generatedSchema.returns[0]); - if ( - !(isArrayReturn && Array.isArray(schema.returns[0])) && - !( - typeof generatedSchema.returns[0] === 'string' && - typeof schema.returns[0] === 'string' - ) - ) { - throw new Error('Both return values must have same structure'); - } - generatedSchema = { - ...generatedSchema, - returns: isArrayReturn - ? generatedSchema.returns.map(([_, cb]) => - [fullPath, cb].filter(x => x) - ) - : [[fullPath, generatedSchema.returns[1]].filter(x => x)] - }; - } - } - - log2( - `watchObservable() called for ${ - generatedSchema.computed ? 'computed ' : 'base ' - }observable: ${fullPath}` - ); + throw new Error(`Observable ${key} expects at least ${schema.generate.length} argument${schema.generate.length > 1 ? 's' : ''}`); // prettier-ignore + // Check if an observable already exists for this path (key + args) const existingObservable = get(this._observables, fullPath); if (existingObservable) { - log2( - `Returning existing ${ - generatedSchema.computed ? 'computed ' : 'base ' - }observable: ${fullPath}` - ); + const isComputed = !get(this._subjects, fullPath, subject); + log2(`watchObservable() called for ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + log2(`Returning existing ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore return existingObservable; } + // Generate schema + let generatedSchema = schema.generate(...args); + log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + // Handle computed observable if (generatedSchema.computed) { // Handle dynamically generated dependencies @@ -286,23 +250,17 @@ export default class MulticallService extends PublicService { schema.watching[path] = 1; // Tap multicall to add schema (first subscriber to this schema) this._tapMulticallWithSchema(schema, generatedSchema, path); + this._watcher.tap(s => { + log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); // prettier-ignore + return s; + }); } else schema.watching[path]++; - - log2( - `Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}` - ); + log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore const sub = subject.subscribe(observer); return () => { - // If no schemas are being watched, unsubscribe from watcher updates - if (--this._watchingSchemasTotal === 0) { - log2('Unsubscribed from watcher updates'); - this._watcherUpdates.unsub(); - this._watcherUpdates = null; - } sub.unsubscribe(); schema.watching[path]--; - log2('Schema subscribers:', schema.watching[path]); if (schema.watching[path] === 0) { // Tap multicall to remove schema (last unsubscriber from this schema) log2(`Schema removed from multicall: ${generatedSchema.id}`); @@ -310,9 +268,16 @@ export default class MulticallService extends PublicService { schemas.filter(({ id }) => id !== generatedSchema.id) ); this._watcher.tap(s => { - log2(`Remaining schemas: ${s.filter(({ id }) => id).length}`); + log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); // prettier-ignore return s; }); + } else log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore + + // If no schemas are being watched, unsubscribe from watcher updates + if (--this._watchingSchemasTotal === 0) { + log2('Unsubscribed from watcher updates'); + this._watcherUpdates.unsub(); + this._watcherUpdates = null; } }; }); @@ -329,16 +294,19 @@ export default class MulticallService extends PublicService { } _tapMulticallWithSchema(schema, generatedSchema, path) { - let { id, contractName, call, returns } = generatedSchema; - // Automatically generate return keys if not specified in schema + let { id, contractName, call, returns, transforms = {} } = generatedSchema; + // Automatically generate return keys if not explicitly specified in generated schema if (!returns) returns = schema.returns.map(ret => { - let key = Array.isArray(ret) ? ret[0] : ret; - key = `${key}${path ? '.' : ''}${path}`; - return Array.isArray(ret) && ret.length == 2 ? [key, ret[1]] : [key]; + const key = ret[0]; + const fullPath = `${key}${path ? '.' : ''}${path}`; + return transforms[key] + ? [fullPath, transforms[key]] // Use transform mapping in generated schema if available + : ret.length == 2 + ? [fullPath, ret[1]] + : [fullPath]; }); - if (!this._addresses[contractName]) - throw new Error(`Can't find contract address for ${contractName}`); + if (!this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore this._watcher.tap(calls => [ ...calls, { @@ -350,7 +318,7 @@ export default class MulticallService extends PublicService { ]); log2(`Schema added to multicall: ${id}`); this._watcher.tap(s => { - log2('Current schemas:', s.filter(({ id }) => id).map(({ id }) => id)); + log2('Current schemas: ' + s.filter(({ id }) => id).map(({ id }) => id).join(',')); // prettier-ignore return s; }); } From c135d1c8eed632c9e2810f0d2cd487a99869e48c Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 24 Jan 2020 20:51:49 +0800 Subject: [PATCH 077/117] Implement remove schema delay feature Allows waiting after a schema is no longer being used before removing it from multicall polling --- packages/dai/src/eth/MulticallService.js | 65 +++++++++++++----------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 1596b55aa..30f46a861 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -21,6 +21,8 @@ export default class MulticallService extends PublicService { this._watchingSchemasTotal = 0; this._multicallResultCache = {}; this._addresses = {}; + this._removeSchemaTimers = {}; + this._removeSchemaDelay = 1000; } initialize() { @@ -244,41 +246,18 @@ export default class MulticallService extends PublicService { }); } this._watchingSchemasTotal++; + if (schema.watching[path] === undefined) schema.watching[path] = 0; - // First subscriber to this schema - if (!schema.watching[path]) { - schema.watching[path] = 1; - // Tap multicall to add schema (first subscriber to this schema) - this._tapMulticallWithSchema(schema, generatedSchema, path); - this._watcher.tap(s => { - log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); // prettier-ignore - return s; - }); - } else schema.watching[path]++; + // If first subscriber to this schema add it to multicall + if (++schema.watching[path] === 1) this._addSchemaToMulticall(schema, generatedSchema, path); // prettier-ignore log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore const sub = subject.subscribe(observer); return () => { sub.unsubscribe(); - schema.watching[path]--; - if (schema.watching[path] === 0) { - // Tap multicall to remove schema (last unsubscriber from this schema) - log2(`Schema removed from multicall: ${generatedSchema.id}`); - this._watcher.tap(schemas => - schemas.filter(({ id }) => id !== generatedSchema.id) - ); - this._watcher.tap(s => { - log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); // prettier-ignore - return s; - }); - } else log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore - - // If no schemas are being watched, unsubscribe from watcher updates - if (--this._watchingSchemasTotal === 0) { - log2('Unsubscribed from watcher updates'); - this._watcherUpdates.unsub(); - this._watcherUpdates = null; - } + // If last unsubscriber from this schema remove it from multicall + if (--schema.watching[path] === 0) this._removeSchemaFromMulticall(generatedSchema); + else log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore }; }); @@ -293,8 +272,15 @@ export default class MulticallService extends PublicService { .toPromise(); } - _tapMulticallWithSchema(schema, generatedSchema, path) { + _addSchemaToMulticall(schema, generatedSchema, path) { let { id, contractName, call, returns, transforms = {} } = generatedSchema; + // If this schema is already added but pending removal + if (this._removeSchemaTimers[id]) { + log2(`Cleared pending schema removal for: ${id}`); + clearTimeout(this._removeSchemaTimers[id]); + this._removeSchemaTimers[id] = null; + return; + } // Automatically generate return keys if not explicitly specified in generated schema if (!returns) returns = schema.returns.map(ret => { @@ -319,10 +305,29 @@ export default class MulticallService extends PublicService { log2(`Schema added to multicall: ${id}`); this._watcher.tap(s => { log2('Current schemas: ' + s.filter(({ id }) => id).map(({ id }) => id).join(',')); // prettier-ignore + log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); return s; }); } + _removeSchemaFromMulticall({ id }) { + this._removeSchemaTimers[id] = setTimeout(() => { + this._removeSchemaTimers[id] = null; + log2(`Schema removed from multicall: ${id}`); + this._watcher.tap(schemas => schemas.filter(({ id: id_ }) => id_ !== id)); + this._watcher.tap(s => { + log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); + return s; + }); + // If no schemas are being watched, unsubscribe from watcher updates + if (--this._watchingSchemasTotal === 0) { + log2('Unsubscribed from watcher updates'); + this._watcherUpdates.unsub(); + this._watcherUpdates = null; + } + }, this._removeSchemaDelay); + } + disconnect() { // TODO } From 27a266b58a96145b26f5c4ae97daf38bbf6aaf91 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 24 Jan 2020 13:48:06 +0000 Subject: [PATCH 078/117] Refactored dependency tree recursion + included allowance schemas + tests --- packages/dai-plugin-mcd/src/index.js | 4 + .../dai-plugin-mcd/src/schemas/computed.js | 19 ++++- .../dai-plugin-mcd/src/schemas/constants.js | 4 +- packages/dai-plugin-mcd/src/schemas/token.js | 29 ++++++- .../test/schemas/computed.spec.js | 35 ++++++++- .../dai-plugin-mcd/test/schemas/token.spec.js | 34 ++++++++- packages/dai/src/eth/MulticallService.js | 76 +++++++++++-------- 7 files changed, 155 insertions(+), 46 deletions(-) diff --git a/packages/dai-plugin-mcd/src/index.js b/packages/dai-plugin-mcd/src/index.js index 56e51e9bc..1830b9435 100644 --- a/packages/dai-plugin-mcd/src/index.js +++ b/packages/dai-plugin-mcd/src/index.js @@ -85,6 +85,10 @@ export const defaultCdpTypes = [ export const SAI = createCurrency('SAI'); +export const ALLOWANCE_AMOUNT = BigNumber( + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +); + export const defaultTokens = [ ...new Set([ ...defaultCdpTypes.map(type => type.currency), diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index b397d995f..d5332e7ad 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -6,8 +6,7 @@ import { liquidationPrice as calcLiquidationPrice, minSafeCollateralAmount as calcMinSafeCollateralAmount } from '../math'; -import { defaultCdpTypes } from '../'; -import { USD, MDAI, DSR_DAI } from '../'; +import { USD, MDAI, DSR_DAI, defaultCdpTypes, ALLOWANCE_AMOUNT } from '../'; import { RATIO_DAI_USD, @@ -38,7 +37,8 @@ import { TOKEN_BALANCE, LIQUIDATION_RATIO_SIMPLE, LIQUIDATION_PENALTY, - ANNUAL_STABILITY_FEE + ANNUAL_STABILITY_FEE, + TOKEN_ALLOWANCE } from './constants'; export const collateralTypePrice = { @@ -311,6 +311,16 @@ export const balance = { }) }; +export const allowance = { + generate: symbol => ({ + dependencies: ({ get }) => { + const address = get('web3').currentAddress(); + return [[TOKEN_ALLOWANCE, address, [PROXY_ADDRESS, address], symbol]]; + }, + computed: v => v.isEqualTo(ALLOWANCE_AMOUNT) + }) +}; + export default { collateralTypePrice, collateralTypesPrices, @@ -329,5 +339,6 @@ export default { daiLockedInDsr, totalDaiLockedInDsr, balance, - liquidationRatioSimple + liquidationRatioSimple, + allowance }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index cf17d502f..84804e76a 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -57,7 +57,9 @@ export const MIN_SAFE_COLLATERAL_AMOUNT = 'minSafeCollateralAmount'; export const COLLATERAL_AVAILABLE_AMOUNT = 'collateralAvailableAmount'; export const COLLATERAL_AVAILABLE_VALUE = 'collateralAvailableValue'; export const LIQUIDATION_RATIO_SIMPLE = 'liquidationRatioSimple'; +export const BALANCE = 'balance'; +export const ALLOWANCE = 'allowance'; // token export const TOKEN_BALANCE = 'tokenBalance'; -export const BALANCE = 'balance'; +export const TOKEN_ALLOWANCE = 'tokenAllowance'; diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js index 0a16b5454..b7ed5be90 100644 --- a/packages/dai-plugin-mcd/src/schemas/token.js +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -1,5 +1,7 @@ -import { TOKEN_BALANCE } from './constants'; import { getMcdToken } from '../utils'; +import BigNumber from 'bignumber.js'; + +import { TOKEN_BALANCE, TOKEN_ALLOWANCE } from './constants'; export const tokenBalance = { generate: (address, symbol) => { @@ -33,6 +35,29 @@ export const tokenBalance = { returns: [TOKEN_BALANCE] }; +export const tokenAllowance = { + generate: (address, proxyAddress, symbol) => { + if (symbol === 'WETH') symbol = 'MWETH'; + if (symbol === 'DAI') symbol = 'MDAI'; + if (symbol === 'ETH' || symbol === 'DSR-DAI') + throw new Error(`${symbol} does not require an allowance to be set`); + + const currencyToken = getMcdToken(symbol); + const contract = + symbol === 'MDAI' ? 'MCD_DAI' : symbol === 'MWETH' ? 'ETH' : symbol; + if (!currencyToken) + throw new Error(`${symbol} token is not part of the default tokens list`); + + return { + id: `allowance.${symbol}.${address}`, + contractName: contract, + call: ['allowance(address,address)(uint256)', address, proxyAddress] + }; + }, + returns: [[TOKEN_ALLOWANCE, v => BigNumber(v)]] +}; + export default { - tokenBalance + tokenBalance, + tokenAllowance }; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index e4e058878..cfb61524a 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -1,6 +1,10 @@ import { mcdMaker, setupCollateral } from '../helpers'; import { ETH, BAT, MDAI, USD } from '../../src'; -import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import { + takeSnapshot, + restoreSnapshot, + TestAccountProvider +} from '@makerdao/test-helpers'; import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; import BigNumber from 'bignumber.js'; @@ -21,7 +25,8 @@ import { COLLATERAL_AVAILABLE_VALUE, DAI_LOCKED_IN_DSR, TOTAL_DAI_LOCKED_IN_DSR, - BALANCE + BALANCE, + ALLOWANCE } from '../../src/schemas'; import { vatIlks, vatUrns, vatGem } from '../../src/schemas/vat'; @@ -29,9 +34,9 @@ import { cdpManagerUrns, cdpManagerIlks } from '../../src/schemas/cdpManager'; import { spotIlks, liquidationRatio, spotPar } from '../../src/schemas/spot'; import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; import { potPie, potpie, potChi } from '../../src/schemas/pot'; -import { tokenBalance } from '../../src/schemas/token'; import { catIlks } from '../../src/schemas/cat'; import { jugIlks } from '../../src/schemas/jug'; +import { tokenBalance, tokenAllowance } from '../../src/schemas/token'; import computedSchemas from '../../src/schemas/computed'; import { createCurrencyRatio } from '@makerdao/currency'; @@ -72,6 +77,7 @@ beforeAll(async () => { tokenBalance, catIlks, jugIlks, + tokenAllowance, ...computedSchemas }); maker.service('multicall').start(); @@ -286,7 +292,7 @@ test(BALANCE, async () => { expect(ethBalance.symbol).toEqual('ETH'); expect(batBalance.symbol).toEqual('BAT'); - expect(ethBalance.toNumber()).toBeCloseTo(93.675, 2); + expect(ethBalance.toNumber()).toBeCloseTo(93.677, 2); expect(batBalance.toBigNumber()).toEqual(BigNumber('999')); const daiBalance = await maker.latest(BALANCE, 'DAI'); @@ -310,3 +316,24 @@ test(BALANCE, async () => { ); } }); + +test(ALLOWANCE, async () => { + const nextAccount = TestAccountProvider.nextAccount(); + await maker.addAccount({ ...nextAccount, type: 'privateKey' }); + maker.useAccount(nextAccount.address); + const nextAccountProxy = await maker.service('proxy').ensureProxy(); + + let batAllowance; + batAllowance = await maker.latest(ALLOWANCE, 'BAT'); + expect(batAllowance).toEqual(false); + + await maker + .service('token') + .getToken('BAT') + .approveUnlimited(nextAccountProxy); + + batAllowance = await maker.latest(ALLOWANCE, 'BAT'); + expect(batAllowance).toEqual(true); + + maker.useAccount('default'); +}); diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js index 3c9d3faba..770a8d5c6 100644 --- a/packages/dai-plugin-mcd/test/schemas/token.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -1,13 +1,13 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import { ETH, BAT, MWETH, MDAI } from '../../src'; +import { ETH, BAT, ALLOWANCE_AMOUNT } from '../../src'; import BigNumber from 'bignumber.js'; -import { TOKEN_BALANCE, BALANCE } from '../../src/schemas'; +import { TOKEN_BALANCE, TOKEN_ALLOWANCE } from '../../src/schemas'; import tokenSchemas from '../../src/schemas/token'; -let maker, snapshotData, address; +let maker, snapshotData, address, proxyAddress; beforeAll(async () => { maker = await mcdMaker({ @@ -23,6 +23,7 @@ beforeAll(async () => { maker.service('multicall').registerSchemas(tokenSchemas); maker.service('multicall').start(); address = maker.currentAddress(); + proxyAddress = await maker.service('proxy').ensureProxy(); }); afterAll(async () => { @@ -64,3 +65,30 @@ test(TOKEN_BALANCE, async () => { ); } }); + +test(TOKEN_ALLOWANCE, async () => { + const unsetBatAllowance = await maker.latest( + TOKEN_ALLOWANCE, + address, + proxyAddress, + 'BAT' + ); + + expect(BigNumber.isBigNumber(unsetBatAllowance)).toEqual(true); + expect(unsetBatAllowance).toEqual(BigNumber('0')); + + await maker + .service('token') + .getToken('BAT') + .approveUnlimited(proxyAddress); + + const setBatAllowance = await maker.latest( + TOKEN_ALLOWANCE, + address, + proxyAddress, + 'BAT' + ); + + expect(BigNumber.isBigNumber(setBatAllowance)).toEqual(true); + expect(setBatAllowance).toEqual(BigNumber(ALLOWANCE_AMOUNT)); +}); diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 30f46a861..cfb71ba91 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,8 +1,15 @@ import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { Observable, ReplaySubject, combineLatest, from, interval } from 'rxjs'; -import { map, first, flatMap, throttle } from 'rxjs/operators'; +import { + Observable, + ReplaySubject, + combineLatest, + from, + interval, + timer +} from 'rxjs'; +import { map, first, flatMap, throttle, debounce, take } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -144,8 +151,6 @@ export default class MulticallService extends PublicService { const schema = this.schemaByObservableKey(key); if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); - if (args.length < schema.generate.length) - throw new Error(`Observable ${key} expects at least ${schema.generate.length} argument${schema.generate.length > 1 ? 's' : ''}`); // prettier-ignore // Check if an observable already exists for this path (key + args) const existingObservable = get(this._observables, fullPath); @@ -160,6 +165,9 @@ export default class MulticallService extends PublicService { let generatedSchema = schema.generate(...args); log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + if (args.length < generatedSchema.length) + throw new Error(`Observable ${key} expects at least ${generatedSchema.length} argument${generatedSchema.length > 1 ? 's' : ''}`); // prettier-ignore + // Handle computed observable if (generatedSchema.computed) { // Handle dynamically generated dependencies @@ -178,34 +186,34 @@ export default class MulticallService extends PublicService { return from(key()); } - let atLeafNode = false; - const [checker] = trie; - if (!Array.isArray(checker)) { - atLeafNode = true; - } - - if (Array.isArray(trie) && !atLeafNode) { - if (trie.length > 1) { - return combineLatest(trie.map(recurseDependencyTree)).pipe( - throttle(() => interval(1)), - flatMap(result => { - return this.watchObservable(key, ...result); - }) - ); - } else { - const next = trie.shift(); - return recurseDependencyTree(next).pipe( - flatMap(result => { - return Array.isArray(result) - ? this.watchObservable(key, ...result) - : this.watchObservable(key, result); - }) - ); - } - } else { - if (Array.isArray(trie) && trie.length === 0) - return this.watchObservable(key); + const indexesAtLeafNodes = trie.map(node => !Array.isArray(node)); + const allLeafNodes = indexesAtLeafNodes.every(node => node === true); + + if (Array.isArray(trie) && trie.length === 0) { + // When trie is an empty array, indicates that we only need to return + // watchObservable on the key + return this.watchObservable(key); + } else if (allLeafNodes) { + // If the trie is an array it indicates that the observable is + // expecting arguments. These can be normal values or other + // observables. Where an index in the trie is an array, it is + // assumed that it is syntax for an observable argument. In the case + // where all indexes in the trie array are normal values, we use the + // spread operator to pass them to the returned watchObservable fn return this.watchObservable(key, ...trie); + } else { + // When a trie array has nested observables, recursively call this fn + // on indexes which have an array. + return combineLatest( + trie.map((node, idx) => { + return indexesAtLeafNodes[idx] + ? [node] + : recurseDependencyTree(node); + }) + ).pipe( + throttle(() => interval(1)), + flatMap(result => this.watchObservable(key, ...result)) + ); } }; @@ -267,8 +275,12 @@ export default class MulticallService extends PublicService { } latest(key, ...args) { + // TODO Possible configuration setting for timer? return this.watchObservable(key, ...args) - .pipe(first()) + .pipe( + debounce(() => timer(100)), + take(1) + ) .toPromise(); } From ae97809e850338664d25716d072a0b445e787357 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Fri, 24 Jan 2020 15:31:54 +0100 Subject: [PATCH 079/117] reduce precision on total savings dai test --- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index a8e72cbd2..82785068b 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -283,7 +283,7 @@ test(TOTAL_DAI_LOCKED_IN_DSR, async () => { expect(totalDaiLockedInDsr.toNumber()).toBeCloseTo(1, 18); }); -xtest(BALANCE, async () => { +test(BALANCE, async () => { expect.assertions(11); const ethBalance = await maker.latest(BALANCE, 'ETH'); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index 714f26153..f93c9e309 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -57,7 +57,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(BigNumber.isBigNumber(totalSavingsDai)).toEqual(true); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 4); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 3); }); test(SAVINGS_DAI, async () => { From 2aa5acb38253fae26b59e836a24c043320f3af6a Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 24 Jan 2020 14:46:52 +0000 Subject: [PATCH 080/117] Added demarcate flag for optional extending of observable path to use the currently connected address as a unique identifier --- .../dai-plugin-mcd/src/schemas/computed.js | 2 ++ packages/dai/src/eth/MulticallService.js | 23 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index ce026e907..b3039f0ca 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -302,6 +302,7 @@ export const balance = { } return [[TOKEN_BALANCE, address, symbol]]; }, + demarcate: true, computed: v => v }) }; @@ -312,6 +313,7 @@ export const allowance = { const address = get('web3').currentAddress(); return [[TOKEN_ALLOWANCE, address, [PROXY_ADDRESS, address], symbol]]; }, + demarcate: true, computed: v => v.isEqualTo(ALLOWANCE_AMOUNT) }) }; diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index cfb71ba91..0ca922720 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -37,6 +37,10 @@ export default class MulticallService extends PublicService { this._addresses = this.get('smartContract').getContractAddresses(); } + authenticate() { + this._connectedAddress = this.get('web3').currentAddress(); + } + createWatcher({ useWeb3Provider = false, interval = 'block', @@ -146,12 +150,23 @@ export default class MulticallService extends PublicService { } watchObservable(key, ...args) { - const path = args.join('.'); - const fullPath = `${key}${path ? '.' : ''}${path}`; const schema = this.schemaByObservableKey(key); if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); + // Generate schema + let generatedSchema = schema.generate(...args); + + const path = [ + generatedSchema.demarcate ? this.get('web3').currentAddress() : undefined, + ...args + ] + .filter(x => x) + .join('.'); + const fullPath = `${key}${path ? '.' : ''}${path}`; + + log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + // Check if an observable already exists for this path (key + args) const existingObservable = get(this._observables, fullPath); if (existingObservable) { @@ -161,10 +176,6 @@ export default class MulticallService extends PublicService { return existingObservable; } - // Generate schema - let generatedSchema = schema.generate(...args); - log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore - if (args.length < generatedSchema.length) throw new Error(`Observable ${key} expects at least ${generatedSchema.length} argument${generatedSchema.length > 1 ? 's' : ''}`); // prettier-ignore From 6d4a02740d5bed6887e65940d153c48e0517757c Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 24 Jan 2020 21:10:57 +0000 Subject: [PATCH 081/117] Finished off schemas to replace multicall/reducers --- .../dai-plugin-mcd/src/schemas/cdpManager.js | 31 ++++++++++++++- .../dai-plugin-mcd/src/schemas/constants.js | 5 +++ packages/dai-plugin-mcd/src/schemas/jug.js | 15 ++++++- packages/dai-plugin-mcd/src/schemas/token.js | 26 ++++++++++++- packages/dai-plugin-mcd/src/schemas/vat.js | 15 ++++++- .../test/schemas/cdpManager.spec.js | 25 ++++++++++-- .../dai-plugin-mcd/test/schemas/jug.spec.js | 9 ++++- .../dai-plugin-mcd/test/schemas/token.spec.js | 39 ++++++++++++++++++- .../dai-plugin-mcd/test/schemas/vat.spec.js | 10 ++++- 9 files changed, 160 insertions(+), 15 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js index d6824c12c..928df2a71 100644 --- a/packages/dai-plugin-mcd/src/schemas/cdpManager.js +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -1,5 +1,12 @@ -import { VAULT_ADDRESS, VAULT_TYPE } from './constants'; import { bytesToString } from '../utils'; +import BigNumber from 'bignumber.js'; + +import { + VAULT_ADDRESS, + VAULT_TYPE, + VAULTS_CREATED, + VAULT_OWNER +} from './constants'; export const cdpManagerUrns = { generate: id => ({ @@ -19,7 +26,27 @@ export const cdpManagerIlks = { returns: [[VAULT_TYPE, bytesToString]] }; +export const cdpManagerCdpi = { + generate: () => ({ + id: 'CDP_MANAGER.cdpi', + contractName: 'CDP_MANAGER', + call: ['cdpi()(uint256)'] + }), + returns: [[VAULTS_CREATED, v => BigNumber(v)]] +}; + +export const cdpManagerOwner = { + generate: id => ({ + id: `CDP_MANAGER.owner(${id})`, + contractName: 'CDP_MANAGER', + call: ['owns(uint256)(address)', id] + }), + returns: [[VAULT_OWNER]] +}; + export default { cdpManagerUrns, - cdpManagerIlks + cdpManagerIlks, + cdpManagerCdpi, + cdpManagerOwner }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 84804e76a..312c7e43d 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -8,6 +8,7 @@ export const TOTAL_DAI_SUPPLY = 'totalDaiSupply'; export const ENCUMBERED_COLLATERAL = 'encumberedCollateral'; export const ENCUMBERED_DEBT = 'encumberedDebt'; export const UNLOCKED_COLLATERAL = 'unlockedCollateral'; +export const GLOBAL_DEBT_CEILING = 'globalDebtCeiling'; // spot export const PRICE_FEED_ADDRESS = 'priceFeedAddress'; @@ -18,6 +19,7 @@ export const LIQUIDATION_RATIO = 'liquidationRatio'; // jug export const ANNUAL_STABILITY_FEE = 'annualStabilityFee'; export const DATE_STABILITY_FEES_LAST_LEVIED = 'dateStabilityFeesLastLevied'; +export const BASE_COLLATERAL_FEE = 'baseCollateralFee'; // proxyRegistry export const PROXY_ADDRESS = 'proxyAddress'; @@ -25,6 +27,8 @@ export const PROXY_ADDRESS = 'proxyAddress'; // cdpManager export const VAULT_ADDRESS = 'vaultAddress'; export const VAULT_TYPE = 'vaultType'; +export const VAULTS_CREATED = 'vaultCreated'; +export const VAULT_OWNER = 'vaultOwner'; // pot export const TOTAL_SAVINGS_DAI = 'totalSavingsDai'; @@ -63,3 +67,4 @@ export const ALLOWANCE = 'allowance'; // token export const TOKEN_BALANCE = 'tokenBalance'; export const TOKEN_ALLOWANCE = 'tokenAllowance'; +export const ADAPTER_BALANCE = 'adapterBalance'; diff --git a/packages/dai-plugin-mcd/src/schemas/jug.js b/packages/dai-plugin-mcd/src/schemas/jug.js index f65cc1974..70a7bff5c 100644 --- a/packages/dai-plugin-mcd/src/schemas/jug.js +++ b/packages/dai-plugin-mcd/src/schemas/jug.js @@ -4,7 +4,8 @@ import { RAY } from '../constants'; import { ANNUAL_STABILITY_FEE, - DATE_STABILITY_FEES_LAST_LEVIED + DATE_STABILITY_FEES_LAST_LEVIED, + BASE_COLLATERAL_FEE } from './constants'; const secondsPerYear = 60 * 60 * 24 * 365; @@ -28,6 +29,16 @@ export const jugIlks = { ] }; +export const jugBase = { + generate: () => ({ + id: 'MCD_JUG.base', + contractName: 'MCD_JUG', + call: ['base()(uint256)'] + }), + returns: [[BASE_COLLATERAL_FEE, v => BigNumber(v)]] +}; + export default { - jugIlks + jugIlks, + jugBase }; diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js index b7ed5be90..681ac420f 100644 --- a/packages/dai-plugin-mcd/src/schemas/token.js +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -57,7 +57,31 @@ export const tokenAllowance = { returns: [[TOKEN_ALLOWANCE, v => BigNumber(v)]] }; +export const adapterBalance = { + generate: collateralTypeName => ({ + dependencies: ({ get }) => { + collateralTypeName = + collateralTypeName === 'MDAI' ? 'DAI' : collateralTypeName; + let tokenSymbol = collateralTypeName.split('-')[0]; + tokenSymbol = tokenSymbol === 'ETH' ? 'MWETH' : tokenSymbol; + return [ + [ + TOKEN_BALANCE, + get('smartContract').getContractAddress( + `MCD_JOIN_${collateralTypeName.replace('-', '_')}` + ), + tokenSymbol + ] + ]; + }, + computed: v => v + }) +}; + export default { tokenBalance, - tokenAllowance + tokenAllowance, + + // computed + adapterBalance }; diff --git a/packages/dai-plugin-mcd/src/schemas/vat.js b/packages/dai-plugin-mcd/src/schemas/vat.js index 48f8fc0e1..1e5798af1 100644 --- a/packages/dai-plugin-mcd/src/schemas/vat.js +++ b/packages/dai-plugin-mcd/src/schemas/vat.js @@ -11,7 +11,8 @@ import { TOTAL_DAI_SUPPLY, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, - UNLOCKED_COLLATERAL + UNLOCKED_COLLATERAL, + GLOBAL_DEBT_CEILING } from './constants'; export const vatIlks = { @@ -59,9 +60,19 @@ export const vatGem = { return: [UNLOCKED_COLLATERAL, fromWei] }; +export const vatLine = { + generate: () => ({ + id: 'MCD_VAT.Line', + contractName: 'MCD_VAT', + call: ['Line()(uint256)'] + }), + returns: [[GLOBAL_DEBT_CEILING, v => MDAI(v, 'rad')]] +}; + export default { vatIlks, vatDebt, vatUrns, - vatGem + vatGem, + vatLine }; diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index be24cc891..3db5dd4ec 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -2,12 +2,19 @@ import { mcdMaker, setupCollateral } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; import { ETH, BAT, MDAI } from '../../src'; import { ServiceRoles } from '../../src/constants'; +import BigNumber from 'bignumber.js'; -import { VAULT_ADDRESS, VAULT_TYPE } from '../../src/schemas'; +import { + VAULT_ADDRESS, + VAULT_TYPE, + VAULTS_CREATED, + VAULT_OWNER, + TOTAL_OWNED_VAULTS +} from '../../src/schemas'; import cdpManagerSchemas from '../../src/schemas/cdpManager'; -let maker, snapshotData, cdpMgr; +let maker, snapshotData, cdpMgr, proxyAddress; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -32,8 +39,8 @@ beforeAll(async () => { cdpMgr = await maker.service(ServiceRoles.CDP_MANAGER); const dai = maker.getToken(MDAI); - const _proxyAddress = await maker.service('proxy').ensureProxy(); - await dai.approveUnlimited(_proxyAddress); + proxyAddress = await maker.service('proxy').ensureProxy(); + await dai.approveUnlimited(proxyAddress); await cdpMgr.openLockAndDraw( 'ETH-A', @@ -59,3 +66,13 @@ test(VAULT_TYPE, async () => { const vaultType = await maker.latest(VAULT_TYPE, cdpId); expect(vaultType).toEqual(expected); }); + +test(VAULTS_CREATED, async () => { + const vaultsCreated = await maker.latest(VAULTS_CREATED); + expect(vaultsCreated).toEqual(BigNumber('1')); +}); + +test(VAULT_OWNER, async () => { + const vaultOwner = await maker.latest(VAULT_OWNER, 1); + expect(vaultOwner).toEqual(proxyAddress); +}); diff --git a/packages/dai-plugin-mcd/test/schemas/jug.spec.js b/packages/dai-plugin-mcd/test/schemas/jug.spec.js index cb95229c5..cbc1c693c 100644 --- a/packages/dai-plugin-mcd/test/schemas/jug.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/jug.spec.js @@ -1,9 +1,11 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import BigNumber from 'bignumber.js'; import { ANNUAL_STABILITY_FEE, - DATE_STABILITY_FEES_LAST_LEVIED + DATE_STABILITY_FEES_LAST_LEVIED, + BASE_COLLATERAL_FEE } from '../../src/schemas'; import jugSchemas from '../../src/schemas/jug'; @@ -41,3 +43,8 @@ test(DATE_STABILITY_FEES_LAST_LEVIED, async () => { expect(dateStabilityFeesLastLevied instanceof Date).toEqual(true); expect(timestamp - dateStabilityFeesLastLevied).toBeLessThanOrEqual(10); }); + +test(BASE_COLLATERAL_FEE, async () => { + const baseCollateralFee = await maker.latest(BASE_COLLATERAL_FEE); + expect(baseCollateralFee).toEqual(BigNumber('0')); +}); diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js index 770a8d5c6..cbf12955a 100644 --- a/packages/dai-plugin-mcd/test/schemas/token.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -1,14 +1,25 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; -import { ETH, BAT, ALLOWANCE_AMOUNT } from '../../src'; +import { ETH, BAT, MDAI, MWETH, ALLOWANCE_AMOUNT } from '../../src'; import BigNumber from 'bignumber.js'; +import { ServiceRoles } from '../../src/constants'; -import { TOKEN_BALANCE, TOKEN_ALLOWANCE } from '../../src/schemas'; +import { + TOKEN_BALANCE, + TOKEN_ALLOWANCE, + ADAPTER_BALANCE +} from '../../src/schemas'; import tokenSchemas from '../../src/schemas/token'; let maker, snapshotData, address, proxyAddress; +const ETH_A_COLLATERAL_AMOUNT = ETH(1); +const ETH_A_DEBT_AMOUNT = MDAI(1); + +const BAT_A_COLLATERAL_AMOUNT = BAT(1); +const BAT_A_DEBT_AMOUNT = MDAI(1); + beforeAll(async () => { maker = await mcdMaker({ cdpTypes: [ @@ -92,3 +103,27 @@ test(TOKEN_ALLOWANCE, async () => { expect(BigNumber.isBigNumber(setBatAllowance)).toEqual(true); expect(setBatAllowance).toEqual(BigNumber(ALLOWANCE_AMOUNT)); }); + +test(ADAPTER_BALANCE, async () => { + const mgr = await maker.service(ServiceRoles.CDP_MANAGER); + await mgr.openLockAndDraw( + 'ETH-A', + ETH_A_COLLATERAL_AMOUNT, + ETH_A_DEBT_AMOUNT + ); + await mgr.openLockAndDraw( + 'BAT-A', + BAT_A_COLLATERAL_AMOUNT, + BAT_A_DEBT_AMOUNT + ); + + const ethAdapterBalance = await maker.latest(ADAPTER_BALANCE, 'ETH-A'); + const batAdapterBalance = await maker.latest(ADAPTER_BALANCE, 'BAT-A'); + + expect(ethAdapterBalance.symbol).toEqual(MWETH.symbol); + expect(batAdapterBalance.symbol).toEqual(BAT.symbol); + + expect(ethAdapterBalance.toBigNumber()).toEqual(BigNumber('1')); + expect(batAdapterBalance.toBigNumber()).toEqual(BigNumber('1')); + await restoreSnapshot(snapshotData, maker); +}); diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index 494d83572..6af3253de 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -14,7 +14,8 @@ import { TOTAL_DAI_SUPPLY, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, - UNLOCKED_COLLATERAL + UNLOCKED_COLLATERAL, + GLOBAL_DEBT_CEILING } from '../../src/schemas'; import vatSchemas from '../../src/schemas/vat'; @@ -213,3 +214,10 @@ test(UNLOCKED_COLLATERAL, async () => { expect(col).toEqual(fromWei(expected)); }); + +test(GLOBAL_DEBT_CEILING, async () => { + const globalDebtCeiling = await maker.latest(GLOBAL_DEBT_CEILING); + + expect(globalDebtCeiling.symbol).toEqual(MDAI.symbol); + expect(globalDebtCeiling.toBigNumber()).toEqual(BigNumber('1000000')); +}); From 298575a430681c1d5c78efc4a4033cbc76bcc104 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Mon, 27 Jan 2020 13:15:31 +0100 Subject: [PATCH 082/117] add debtFloor to vault computed observable --- packages/dai-plugin-mcd/src/schemas/computed.js | 12 ++++++++---- .../dai-plugin-mcd/test/schemas/computed.spec.js | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index b3039f0ca..ce3781b17 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -37,7 +37,8 @@ import { LIQUIDATION_RATIO_SIMPLE, LIQUIDATION_PENALTY, ANNUAL_STABILITY_FEE, - TOKEN_ALLOWANCE + TOKEN_ALLOWANCE, + DEBT_FLOOR } from './constants'; export const collateralTypePrice = { @@ -230,7 +231,8 @@ export const vault = { [UNLOCKED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], [LIQUIDATION_RATIO_SIMPLE, id], [LIQUIDATION_PENALTY, [VAULT_TYPE, id]], - [ANNUAL_STABILITY_FEE, [VAULT_TYPE, id]] + [ANNUAL_STABILITY_FEE, [VAULT_TYPE, id]], + [DEBT_FLOOR, id] ], computed: ( vaultType, @@ -249,7 +251,8 @@ export const vault = { unlockedCollateral, liquidationRatioSimple, liquidationPenalty, - annualStabilityFee + annualStabilityFee, + debtFloor ) => ({ vaultType, vaultAddress, @@ -267,7 +270,8 @@ export const vault = { unlockedCollateral, liquidationRatioSimple, liquidationPenalty, - annualStabilityFee + annualStabilityFee, + debtFloor }) }) }; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 82785068b..995d1f774 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -229,10 +229,11 @@ test(VAULT, async () => { const expectedLiqRatio = createCurrencyRatio(USD, MDAI)(1.5); const expectedLiqPenalty = BigNumber('0.05'); const expectedAnnStabilityFee = 0.04999999999989363; + const expectedDebtFloor = BigNumber('0'); const vault = await maker.latest(VAULT, cdpId); - expect(Object.keys(vault).length).toBe(17); + expect(Object.keys(vault).length).toBe(18); expect(vault.vaultType).toEqual(expectedVaultType); expect(vault.vaultAddress).toEqual(expectedVaultAddress); @@ -269,6 +270,7 @@ test(VAULT, async () => { expect(vault.liquidationRatioSimple).toEqual(expectedLiqRatio); expect(vault.liquidationPenalty).toEqual(expectedLiqPenalty); expect(vault.annualStabilityFee.toNumber()).toEqual(expectedAnnStabilityFee); + expect(vault.debtFloor).toEqual(expectedDebtFloor); }); test(DAI_LOCKED_IN_DSR, async () => { From b11d90b59bd864f0a97d45dbe2562a70651a4250 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Tue, 28 Jan 2020 23:54:40 +0800 Subject: [PATCH 083/117] Add helper funcs to vault computed observable Add calculateLiquidationPrice and calculateCollateralizationRatio helper functions to vault computed observable result --- .../dai-plugin-mcd/src/schemas/computed.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index ce3781b17..f5c36a8f4 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -271,7 +271,28 @@ export const vault = { liquidationRatioSimple, liquidationPenalty, annualStabilityFee, - debtFloor + debtFloor, + calculateLiquidationPrice({ + collateralAmount = this.collateralAmount, + debtValue = this.debtValue, + liquidationRatioSimple = this.liquidationRatioSimple + } = {}) { + if (!collateralAmount || !debtValue || !liquidationRatioSimple) return; + return calcLiquidationPrice( + collateralAmount, + debtValue, + liquidationRatioSimple + ); + }, + calculateCollateralizationRatio({ + collateralValue = this.collateralValue, + debtValue = this.debtValue + } = {}) { + if (!collateralValue || !debtValue) return; + return calcCollateralizationRatio(collateralValue, debtValue) + .times(100) + .toNumber(); + } }) }) }; From 6ea24e36a6b995e677bd9d3d98032e4f626f5611 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 15:00:21 +0800 Subject: [PATCH 084/117] Add ownerAddress to vault observable --- packages/dai-plugin-mcd/src/schemas/computed.js | 4 ++++ .../test/schemas/computed.spec.js | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index f5c36a8f4..bdbf25a30 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -15,6 +15,7 @@ import { COLLATERAL_TYPE_PRICE, VAULT_TYPE, VAULT_ADDRESS, + VAULT_OWNER, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, SAVINGS_DAI, @@ -217,6 +218,7 @@ export const vault = { dependencies: [ [VAULT_TYPE, id], [VAULT_ADDRESS, id], + [VAULT_OWNER, id], [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], [ENCUMBERED_DEBT, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]], @@ -237,6 +239,7 @@ export const vault = { computed: ( vaultType, vaultAddress, + ownerAddress, encumberedCollateral, encumberedDebt, collateralTypePrice, @@ -256,6 +259,7 @@ export const vault = { ) => ({ vaultType, vaultAddress, + ownerAddress, encumberedCollateral, encumberedDebt, collateralTypePrice, diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 995d1f774..60dba52d2 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -30,7 +30,11 @@ import { } from '../../src/schemas'; import { vatIlks, vatUrns, vatGem } from '../../src/schemas/vat'; -import { cdpManagerUrns, cdpManagerIlks } from '../../src/schemas/cdpManager'; +import { + cdpManagerUrns, + cdpManagerIlks, + cdpManagerOwner +} from '../../src/schemas/cdpManager'; import { spotIlks, spotPar } from '../../src/schemas/spot'; import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; import { potPie, potpie, potChi } from '../../src/schemas/pot'; @@ -40,7 +44,7 @@ import { tokenBalance, tokenAllowance } from '../../src/schemas/token'; import computedSchemas from '../../src/schemas/computed'; import { createCurrencyRatio } from '@makerdao/currency'; -let maker, snapshotData; +let maker, snapshotData, proxyAddress; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -67,6 +71,7 @@ beforeAll(async () => { vatGem, cdpManagerUrns, cdpManagerIlks, + cdpManagerOwner, spotPar, spotIlks, proxyRegistryProxies, @@ -89,8 +94,8 @@ beforeAll(async () => { const mgr = await maker.service(ServiceRoles.CDP_MANAGER); const sav = await maker.service(ServiceRoles.SAVINGS); const dai = maker.getToken(MDAI); - const _proxyAddress = await maker.service('proxy').ensureProxy(); - await dai.approveUnlimited(_proxyAddress); + proxyAddress = await maker.service('proxy').ensureProxy(); + await dai.approveUnlimited(proxyAddress); await mgr.openLockAndDraw( 'ETH-A', @@ -214,6 +219,7 @@ test(VAULT, async () => { const cdpId = 1; const expectedVaultType = 'ETH-A'; const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; + const expectedOwner = proxyAddress; const expectedEncumberedCollateral = fromWei(1000000000000000000); const expectedEncumberedDebt = fromWei(995000000000000000); const expectedColTypePrice = createCurrencyRatio(USD, ETH)(180); @@ -233,10 +239,11 @@ test(VAULT, async () => { const vault = await maker.latest(VAULT, cdpId); - expect(Object.keys(vault).length).toBe(18); + expect(Object.keys(vault).length).toBe(21); expect(vault.vaultType).toEqual(expectedVaultType); expect(vault.vaultAddress).toEqual(expectedVaultAddress); + expect(vault.ownerAddress).toEqual(expectedOwner); expect(vault.encumberedCollateral).toEqual(expectedEncumberedCollateral); expect(vault.encumberedDebt.toNumber()).toBeCloseTo( expectedEncumberedDebt.toNumber() From e855b15cf8d1b4862f53b16864876ade4c147ce1 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 15:55:10 +0800 Subject: [PATCH 085/117] Refactor multicall service createWatcher() --- packages/dai/src/eth/MulticallService.js | 25 +++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 0ca922720..30fe63462 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -44,6 +44,7 @@ export default class MulticallService extends PublicService { createWatcher({ useWeb3Provider = false, interval = 'block', + rpcUrl, ...config } = {}) { const web3 = this.get('web3'); @@ -54,23 +55,21 @@ export default class MulticallService extends PublicService { ...config }; - let onNewBlockPolling; - if (interval === 'block') { - onNewBlockPolling = true; - config.interval = 60000; // 1 min polling fallback safeguard - } + const onNewBlockPolling = interval === 'block'; + if (onNewBlockPolling) interval = 60000; // 1 min polling fallback safeguard + if (useWeb3Provider) config.web3 = web3._web3; - else { - if (!web3.rpcUrl) throw new Error('Unable to get rpcUrl for multicall'); - config.rpcUrl = web3.rpcUrl; + else if (!rpcUrl) { + if (!web3.rpcUrl) new Error('Unable to get rpcUrl for multicall'); + rpcUrl = web3.rpcUrl; } - this._watcher = createWatcher([], config); + this._watcher = createWatcher([], { ...config, interval, rpcUrl }); if (onNewBlockPolling) { log( `Watcher created with poll on new block mode using ${ - config.rpcUrl ? `rpcUrl: ${config.rpcUrl}` : 'web3 provider' + rpcUrl ? `rpcUrl: ${rpcUrl}` : 'web3 provider' }` ); web3.onNewBlock(blockNumber => { @@ -79,10 +78,8 @@ export default class MulticallService extends PublicService { }); } else { log( - `Watcher created with ${ - config.interval ? config.interval + 'ms' : 'default' - } polling interval using ${ - config.rpcUrl ? `rpcUrl: ${config.rpcUrl}` : 'web3 provider' + `Watcher created with ${interval}ms polling interval using ${ + useWeb3Provider ? 'web3 provider' : `rpcUrl: ${rpcUrl}` }` ); } From f5412eeef6088e62dfa7f58396bbc8805a6d2845 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 15:58:18 +0800 Subject: [PATCH 086/117] Allow target to be specified in schema --- packages/dai/src/eth/MulticallService.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 30fe63462..53349f846 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -293,7 +293,14 @@ export default class MulticallService extends PublicService { } _addSchemaToMulticall(schema, generatedSchema, path) { - let { id, contractName, call, returns, transforms = {} } = generatedSchema; + let { + id, + target, + contractName, + call, + returns, + transforms = {} + } = generatedSchema; // If this schema is already added but pending removal if (this._removeSchemaTimers[id]) { log2(`Cleared pending schema removal for: ${id}`); @@ -312,12 +319,13 @@ export default class MulticallService extends PublicService { ? [fullPath, ret[1]] : [fullPath]; }); - if (!this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore + if (!target && !contractName) throw new Error(`Schema must specify target or contractName`); // prettier-ignore + if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore this._watcher.tap(calls => [ ...calls, { id, - target: this._addresses[contractName], + target: target || this._addresses[contractName], call, returns } From 53ade6093a9bf6b7b75d18cc231ab8a04fce0c38 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 16:10:30 +0800 Subject: [PATCH 087/117] Add DSProxy proxyGetOwner schema --- .../dai-plugin-mcd/src/schemas/constants.js | 1 + .../src/schemas/proxyRegistry.js | 14 ++++++++-- .../test/schemas/proxyRegistry.spec.js | 28 ++++++++++++------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 312c7e43d..bf3edf65a 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -23,6 +23,7 @@ export const BASE_COLLATERAL_FEE = 'baseCollateralFee'; // proxyRegistry export const PROXY_ADDRESS = 'proxyAddress'; +export const PROXY_OWNER = 'proxyOwner'; // cdpManager export const VAULT_ADDRESS = 'vaultAddress'; diff --git a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js index 2fff678e0..24954b7e9 100644 --- a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js +++ b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js @@ -1,4 +1,4 @@ -import { PROXY_ADDRESS } from './constants'; +import { PROXY_ADDRESS, PROXY_OWNER } from './constants'; export const proxyRegistryProxies = { generate: address => ({ @@ -9,6 +9,16 @@ export const proxyRegistryProxies = { returns: [[PROXY_ADDRESS]] }; +export const proxyGetOwner = { + generate: address => ({ + id: `DS_PROXY.owner(${address})`, + target: address, + call: ['owner()(address)'] + }), + returns: [[PROXY_OWNER]] +}; + export default { - proxyRegistryProxies + proxyRegistryProxies, + proxyGetOwner }; diff --git a/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js b/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js index 27fa7e2eb..7e8c56bda 100644 --- a/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/proxyRegistry.spec.js @@ -6,22 +6,24 @@ import { } from '@makerdao/test-helpers'; import { isValidAddressString } from '../../src/utils'; -import schemas, { PROXY_ADDRESS } from '../../src/schemas'; +import schemas, { PROXY_ADDRESS, PROXY_OWNER } from '../../src/schemas'; + +let maker, snapshotData, address, address2, proxyAddress, proxyAddress2; -let maker, address, address2, snapshotData; beforeAll(async () => { maker = await mcdMaker({ multicall: true }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(schemas); maker.service('multicall').start(); address = maker.service('web3').currentAddress(); + proxyAddress = await maker.service('proxy').currentProxy(); const account = TestAccountProvider.nextAccount(); await maker.addAccount({ ...account, type: 'privateKey' }); maker.useAccount(account.address); address2 = maker.service('web3').currentAddress(); - await maker.service('proxy').ensureProxy(); + proxyAddress2 = await maker.service('proxy').ensureProxy(); }); afterAll(async () => { @@ -29,11 +31,17 @@ afterAll(async () => { }); test(PROXY_ADDRESS, async () => { - const proxyAddress1 = await maker.latest(PROXY_ADDRESS, address); - expect(isValidAddressString(proxyAddress1)).toEqual(true); - expect(proxyAddress1).toEqual('0x570074CCb147ea3dE2E23fB038D4d78324278886'); + const proxy1 = await maker.latest(PROXY_ADDRESS, address); + expect(isValidAddressString(proxy1)).toEqual(true); + expect(proxy1).toEqual(proxyAddress); + + const proxy2 = await maker.latest(PROXY_ADDRESS, address2); + expect(isValidAddressString(proxy2)).toEqual(true); + expect(proxy2).toEqual(proxyAddress2); +}); - const proxyAddress2 = await maker.latest(PROXY_ADDRESS, address2); - expect(isValidAddressString(proxyAddress2)).toEqual(true); - expect(proxyAddress2).toEqual('0xa029495c24518097170824CDD905351BdD938e52'); +test(PROXY_OWNER, async () => { + const proxyOwner = await maker.latest(PROXY_OWNER, proxyAddress); + expect(isValidAddressString(proxyOwner)).toEqual(true); + expect(proxyOwner.toLowerCase()).toEqual(address); }); From eece510a9bea1760b44e56ff53a9d96889775bb7 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 16:46:03 +0800 Subject: [PATCH 088/117] Make vaultType observable return null if invalid vault id --- packages/dai-plugin-mcd/src/schemas/cdpManager.js | 4 ++-- packages/dai-plugin-mcd/src/utils.js | 4 ++++ .../dai-plugin-mcd/test/schemas/cdpManager.spec.js | 13 +++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js index 928df2a71..dd4e83ad1 100644 --- a/packages/dai-plugin-mcd/src/schemas/cdpManager.js +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -1,4 +1,4 @@ -import { bytesToString } from '../utils'; +import { bytesToString, nullIfEmpty } from '../utils'; import BigNumber from 'bignumber.js'; import { @@ -23,7 +23,7 @@ export const cdpManagerIlks = { contractName: 'CDP_MANAGER', call: ['ilks(uint256)(bytes32)', parseInt(id)] }), - returns: [[VAULT_TYPE, bytesToString]] + returns: [[VAULT_TYPE, v => nullIfEmpty(bytesToString(v))]] }; export const cdpManagerCdpi = { diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index 5ff6b1581..f41c65ea0 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -18,6 +18,10 @@ export function bytesToString(hex) { .replace(/\x00/g, ''); // eslint-disable-line no-control-regex } +export function nullIfEmpty(value) { + return value === '' ? null : value; +} + export function castAsCurrency(value, currency) { if (currency.isInstance(value)) return value; if (typeof value === 'string' || typeof value === 'number') diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index 3db5dd4ec..ae77b8b6e 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -61,10 +61,15 @@ test(VAULT_ADDRESS, async () => { }); test(VAULT_TYPE, async () => { - const cdpId = 1; - const expected = 'ETH-A'; - const vaultType = await maker.latest(VAULT_TYPE, cdpId); - expect(vaultType).toEqual(expected); + const cdpId1 = 1; + const expected1 = 'ETH-A'; + const vaultType1 = await maker.latest(VAULT_TYPE, cdpId1); + expect(vaultType1).toEqual(expected1); + + const cdpId2 = 1000; + const expected2 = null; + const vaultType2 = await maker.latest(VAULT_TYPE, cdpId2); + expect(vaultType2).toEqual(expected2); }); test(VAULTS_CREATED, async () => { From 7890021dee7181013ba32c4242f23a17d934c7dc Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 17:02:06 +0800 Subject: [PATCH 089/117] Re-emit cached value if existing value is not undefined --- packages/dai/src/eth/MulticallService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 53349f846..5e6c12795 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -247,7 +247,7 @@ export default class MulticallService extends PublicService { // This is a base observable const subject = new ReplaySubject(1); set(this._subjects, fullPath, subject); - if (this._multicallResultCache[fullPath]) + if (this._multicallResultCache[fullPath] !== undefined) subject.next(this._multicallResultCache[fullPath]); // Create base observable From e151455e57edd9ac8cc60df2f891355f13287689 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 29 Jan 2020 17:25:48 +0800 Subject: [PATCH 090/117] Add logging for watcher update --- packages/dai/src/eth/MulticallService.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 5e6c12795..6663b8e12 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -257,8 +257,10 @@ export default class MulticallService extends PublicService { log2('Subscribed to watcher updates'); this._watcherUpdates = this._watcher.subscribe(update => { const subject = get(this._subjects, update.type); - if (subject) subject.next(update.value); - else this._multicallResultCache[update.type] = update.value; + if (subject) { + log2('Got watcher update for ' + update.type + ':', update.value); + subject.next(update.value); + } else this._multicallResultCache[update.type] = update.value; }); } this._watchingSchemasTotal++; From de5a3c02a272fb16d5e08f98e2d503bb98325979 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 30 Jan 2020 16:34:41 +0800 Subject: [PATCH 091/117] Minor MulticallService refactor Improve logging and move subscribing to watcher updates into func --- packages/dai/src/eth/MulticallService.js | 46 ++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 6663b8e12..d4cde40fa 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -9,7 +9,7 @@ import { interval, timer } from 'rxjs'; -import { map, first, flatMap, throttle, debounce, take } from 'rxjs/operators'; +import { map, flatMap, throttle, debounce, take } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -168,7 +168,6 @@ export default class MulticallService extends PublicService { const existingObservable = get(this._observables, fullPath); if (existingObservable) { const isComputed = !get(this._subjects, fullPath, subject); - log2(`watchObservable() called for ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore log2(`Returning existing ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore return existingObservable; } @@ -252,30 +251,22 @@ export default class MulticallService extends PublicService { // Create base observable const observable = Observable.create(observer => { - // Subscribe to watcher updates and emit them to subjects - if (!this._watcherUpdates) { - log2('Subscribed to watcher updates'); - this._watcherUpdates = this._watcher.subscribe(update => { - const subject = get(this._subjects, update.type); - if (subject) { - log2('Got watcher update for ' + update.type + ':', update.value); - subject.next(update.value); - } else this._multicallResultCache[update.type] = update.value; - }); - } this._watchingSchemasTotal++; - if (schema.watching[path] === undefined) schema.watching[path] = 0; - + // Subscribe to watcher updates and emit them to subjects + if (!this._watcherUpdates) this._subscribeToWatcherUpdates(); // If first subscriber to this schema add it to multicall + if (schema.watching[path] === undefined) schema.watching[path] = 0; if (++schema.watching[path] === 1) this._addSchemaToMulticall(schema, generatedSchema, path); // prettier-ignore - log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore - + // Subscribe this observer to the subject for this base observable const sub = subject.subscribe(observer); + log2(`Observer subscribed to ${generatedSchema.id} (${schema.watching[path]} subscribers)`); // prettier-ignore + // Return the function to call when this observer unsubscribes return () => { - sub.unsubscribe(); // If last unsubscriber from this schema remove it from multicall - if (--schema.watching[path] === 0) this._removeSchemaFromMulticall(generatedSchema); - else log2(`Subscriber count for schema ${generatedSchema.id}: ${schema.watching[path]}`); // prettier-ignore + if (--schema.watching[path] === 0) this._removeSchemaFromMulticall(generatedSchema); // prettier-ignore + // Unsubscribe this observer from the subject for this base observable + sub.unsubscribe(); + log2(`Observer unsubscribed from ${generatedSchema.id} (${schema.watching[path]} subscribers)`); // prettier-ignore }; }); @@ -305,7 +296,7 @@ export default class MulticallService extends PublicService { } = generatedSchema; // If this schema is already added but pending removal if (this._removeSchemaTimers[id]) { - log2(`Cleared pending schema removal for: ${id}`); + log2(`Cancelled pending schema removal for: ${id}`); clearTimeout(this._removeSchemaTimers[id]); this._removeSchemaTimers[id] = null; return; @@ -321,7 +312,7 @@ export default class MulticallService extends PublicService { ? [fullPath, ret[1]] : [fullPath]; }); - if (!target && !contractName) throw new Error(`Schema must specify target or contractName`); // prettier-ignore + if (!target && !contractName) throw new Error('Schema must specify target or contractName'); // prettier-ignore if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore this._watcher.tap(calls => [ ...calls, @@ -358,6 +349,17 @@ export default class MulticallService extends PublicService { }, this._removeSchemaDelay); } + _subscribeToWatcherUpdates() { + log2('Subscribed to watcher updates'); + this._watcherUpdates = this._watcher.subscribe(update => { + const subject = get(this._subjects, update.type); + if (subject) { + log2('Got watcher update for ' + update.type + ':', update.value); + subject.next(update.value); + } else this._multicallResultCache[update.type] = update.value; + }); + } + disconnect() { // TODO } From 8c93a88898a9ba2434b44b5c4f3b2fadcdf9d819 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 30 Jan 2020 16:45:41 +0800 Subject: [PATCH 092/117] Add promiseWait to utils --- packages/dai-plugin-mcd/src/utils.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/dai-plugin-mcd/src/utils.js b/packages/dai-plugin-mcd/src/utils.js index f41c65ea0..ee88ffe21 100644 --- a/packages/dai-plugin-mcd/src/utils.js +++ b/packages/dai-plugin-mcd/src/utils.js @@ -79,3 +79,7 @@ export const isValidAddressString = addressString => export const getMcdToken = token => defaultTokens.find(mcdToken => mcdToken.symbol === token); + +export function promiseWait(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} From 27622a367b0be123f314b59d6918dddbb0aa1d08 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 31 Jan 2020 10:38:10 +0800 Subject: [PATCH 093/117] Add test:debug npm script --- packages/dai-plugin-mcd/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dai-plugin-mcd/package.json b/packages/dai-plugin-mcd/package.json index 44b9ab2a0..d76e8f0dc 100644 --- a/packages/dai-plugin-mcd/package.json +++ b/packages/dai-plugin-mcd/package.json @@ -10,6 +10,7 @@ "testchain": "../../scripts/run-testchain.sh", "coverage": "yarn test --coverage", "test": "yarn testchain --ci jest --runInBand", + "test:debug": "yarn testchain --ci node --inspect-brk ../../node_modules/.bin/jest --runInBand", "test:kovan": "export NETWORK='kovan' && yarn test --config ./test/config/jestIntegrationConfig.json", "test:integration": "export NETWORK='test' && yarn test --config ./test/config/jestIntegrationConfig.json" }, From 08f141b4c3786e1b4102abb0f60ce63424c63bfc Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 31 Jan 2020 12:24:14 +0800 Subject: [PATCH 094/117] Add optional-chaining babel plugin --- package.json | 1 + packages/dai-plugin-mcd/babel.config.js | 1 + packages/dai/babel.config.js | 1 + 3 files changed, 3 insertions(+) diff --git a/package.json b/package.json index 126fca3ae..5507ae624 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@babel/plugin-proposal-export-namespace-from": "^7.2.0", "@babel/plugin-proposal-function-sent": "^7.2.0", "@babel/plugin-proposal-numeric-separator": "^7.2.0", + "@babel/plugin-proposal-optional-chaining": "^7.6.0", "@babel/plugin-proposal-throw-expressions": "^7.2.0", "@babel/plugin-transform-runtime": "^7.3.4", "@babel/preset-env": "^7.4.5", diff --git a/packages/dai-plugin-mcd/babel.config.js b/packages/dai-plugin-mcd/babel.config.js index 374dbaa2f..58a45428b 100644 --- a/packages/dai-plugin-mcd/babel.config.js +++ b/packages/dai-plugin-mcd/babel.config.js @@ -3,6 +3,7 @@ module.exports = function(api) { const presets = ['@babel/preset-env']; const plugins = [ + '@babel/plugin-proposal-optional-chaining', ['@babel/plugin-proposal-decorators', { legacy: true }], '@babel/plugin-proposal-function-sent', '@babel/plugin-proposal-export-namespace-from', diff --git a/packages/dai/babel.config.js b/packages/dai/babel.config.js index 374dbaa2f..58a45428b 100644 --- a/packages/dai/babel.config.js +++ b/packages/dai/babel.config.js @@ -3,6 +3,7 @@ module.exports = function(api) { const presets = ['@babel/preset-env']; const plugins = [ + '@babel/plugin-proposal-optional-chaining', ['@babel/plugin-proposal-decorators', { legacy: true }], '@babel/plugin-proposal-function-sent', '@babel/plugin-proposal-export-namespace-from', From 6ebd8f9512fce7a48fa1c796997deb5ab0a965e7 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 31 Jan 2020 16:17:21 +0800 Subject: [PATCH 095/117] Implement schema param and result validation --- packages/dai/src/eth/MulticallService.js | 31 ++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index d4cde40fa..fcd37525e 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -151,6 +151,9 @@ export default class MulticallService extends PublicService { if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); + // Validate schema params + if (schema.validateParams) schema.validateParams(...args); + // Generate schema let generatedSchema = schema.generate(...args); @@ -246,7 +249,15 @@ export default class MulticallService extends PublicService { // This is a base observable const subject = new ReplaySubject(1); set(this._subjects, fullPath, subject); - if (this._multicallResultCache[fullPath] !== undefined) + // Emit initial value if cached result from multicall exists + if ( + this._multicallResultCache[fullPath] !== undefined && + this._validateResult( + subject, + fullPath, + this._multicallResultCache[fullPath] + ) + ) subject.next(this._multicallResultCache[fullPath]); // Create base observable @@ -349,13 +360,29 @@ export default class MulticallService extends PublicService { }, this._removeSchemaDelay); } + _validateResult(subject, type, value) { + const [observableKey] = type.split('.'); + const schema = this._schemaByObservableKey[observableKey]; + if (!schema.validateReturns?.hasOwnProperty(observableKey)) return true; + const validator = schema.validateReturns[observableKey]; + try { + validator(value); + return true; + } catch (err) { + log2('Validation error for ' + type + ' result:', value); + subject.error(err); + return false; + } + } + _subscribeToWatcherUpdates() { log2('Subscribed to watcher updates'); this._watcherUpdates = this._watcher.subscribe(update => { const subject = get(this._subjects, update.type); if (subject) { log2('Got watcher update for ' + update.type + ':', update.value); - subject.next(update.value); + if (this._validateResult(subject, update.type, update.value)) + subject.next(update.value); } else this._multicallResultCache[update.type] = update.value; }); } From 262f0bd69254e8157d545cfbeb4c697d8cde08ec Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 31 Jan 2020 16:34:04 +0800 Subject: [PATCH 096/117] Add validation logic to various schemas --- packages/dai-plugin-mcd/src/schemas/cat.js | 4 ++++ packages/dai-plugin-mcd/src/schemas/cdpManager.js | 9 +++++++++ .../dai-plugin-mcd/src/schemas/proxyRegistry.js | 2 ++ packages/dai-plugin-mcd/src/schemas/spot.js | 4 ++++ packages/dai-plugin-mcd/test/schemas/cat.spec.js | 4 ++++ .../dai-plugin-mcd/test/schemas/cdpManager.spec.js | 14 ++++++++++---- .../dai-plugin-mcd/test/schemas/computed.spec.js | 13 +++++++++++++ 7 files changed, 46 insertions(+), 4 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cat.js b/packages/dai-plugin-mcd/src/schemas/cat.js index 4459bb59a..b32a087ea 100644 --- a/packages/dai-plugin-mcd/src/schemas/cat.js +++ b/packages/dai-plugin-mcd/src/schemas/cat.js @@ -14,6 +14,10 @@ export const catIlks = { contractName: 'MCD_CAT', call: ['ilks(bytes32)(address,uint256,uint256)', toHex(collateralTypeName)] }), + validateParams: collateralTypeName => { + if (collateralTypeName === null) + throw new Error('Invalid collateral type name'); + }, returns: [ [LIQUIDATOR_ADDRESS], [LIQUIDATION_PENALTY, v => fromRay(BigNumber(v).minus(RAY))], diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js index dd4e83ad1..2411dc1a1 100644 --- a/packages/dai-plugin-mcd/src/schemas/cdpManager.js +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -23,6 +23,15 @@ export const cdpManagerIlks = { contractName: 'CDP_MANAGER', call: ['ilks(uint256)(bytes32)', parseInt(id)] }), + validateParams(id) { + if (!/^\d+$/.test(id)) + throw new Error('Invalid vault id: must be a positive integer'); + }, + validateReturns: { + [VAULT_TYPE](vaultType) { + if (vaultType === null) throw new Error('Vault does not exist'); + } + }, returns: [[VAULT_TYPE, v => nullIfEmpty(bytesToString(v))]] }; diff --git a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js index 24954b7e9..7470efd19 100644 --- a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js +++ b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js @@ -9,6 +9,8 @@ export const proxyRegistryProxies = { returns: [[PROXY_ADDRESS]] }; +// TODO: throw an error if the DSProxy contract doesn't exist +// could use new multicall onError event listener export const proxyGetOwner = { generate: address => ({ id: `DS_PROXY.owner(${address})`, diff --git a/packages/dai-plugin-mcd/src/schemas/spot.js b/packages/dai-plugin-mcd/src/schemas/spot.js index b0c6b1136..7d07d910b 100644 --- a/packages/dai-plugin-mcd/src/schemas/spot.js +++ b/packages/dai-plugin-mcd/src/schemas/spot.js @@ -27,6 +27,10 @@ export const spotIlks = { )(fromRay(liqRatio)) } }), + validateParams: collateralTypeName => { + if (collateralTypeName === null) + throw new Error('Invalid collateral type name'); + }, returns: [[PRICE_FEED_ADDRESS], [LIQUIDATION_RATIO]] }; diff --git a/packages/dai-plugin-mcd/test/schemas/cat.spec.js b/packages/dai-plugin-mcd/test/schemas/cat.spec.js index 40246925b..ef0df9fa6 100644 --- a/packages/dai-plugin-mcd/test/schemas/cat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cat.spec.js @@ -32,6 +32,10 @@ test(LIQUIDATOR_ADDRESS, async () => { const { MCD_FLIP_ETH_A: expected } = testnetAddresses; const address = await maker.latest(LIQUIDATOR_ADDRESS, 'ETH-A'); expect(address.toLowerCase()).toEqual(expected); + + expect(() => { + maker.latest(LIQUIDATOR_ADDRESS, null); + }).toThrow(/invalid/i); }); test(LIQUIDATION_PENALTY, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index ae77b8b6e..b24e6fd62 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -61,15 +61,21 @@ test(VAULT_ADDRESS, async () => { }); test(VAULT_TYPE, async () => { + expect.assertions(3); + const cdpId1 = 1; const expected1 = 'ETH-A'; const vaultType1 = await maker.latest(VAULT_TYPE, cdpId1); expect(vaultType1).toEqual(expected1); - const cdpId2 = 1000; - const expected2 = null; - const vaultType2 = await maker.latest(VAULT_TYPE, cdpId2); - expect(vaultType2).toEqual(expected2); + const cdpId2 = 9000; + const vaultType2 = maker.latest(VAULT_TYPE, cdpId2); + await expect(vaultType2).rejects.toThrow(/does not exist/i); + + const cdpId3 = -9000; + expect(() => { + maker.latest(VAULT_TYPE, cdpId3); + }).toThrow(/invalid vault id/i); }); test(VAULTS_CREATED, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 60dba52d2..06520cc79 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -280,6 +280,19 @@ test(VAULT, async () => { expect(vault.debtFloor).toEqual(expectedDebtFloor); }); +test('vault with non-existent id', async () => { + const cdpId = 9000; + const vault = maker.latest(VAULT, cdpId); + await expect(vault).rejects.toThrow(/not exist/i); +}); + +test('vault with invalid id', async () => { + const cdpId = -9000; + expect(() => { + maker.latest(VAULT, cdpId); + }).toThrow(/invalid vault id/i); +}); + test(DAI_LOCKED_IN_DSR, async () => { const daiLockedInDsr = await maker.latest(DAI_LOCKED_IN_DSR); expect(daiLockedInDsr.symbol).toEqual('DSR-DAI'); From 3c22030fb7d3022a50e5821cfbf910f5e724eb33 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 31 Jan 2020 16:40:23 +0800 Subject: [PATCH 097/117] Add id and externalOwnerAddress to vault observable --- .../dai-plugin-mcd/src/schemas/computed.js | 17 +++++++++++++- .../dai-plugin-mcd/src/schemas/constants.js | 1 + .../test/schemas/computed.spec.js | 23 ++++++++++++++++--- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index bdbf25a30..0469bc9d6 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -16,6 +16,7 @@ import { VAULT_TYPE, VAULT_ADDRESS, VAULT_OWNER, + VAULT_EXTERNAL_OWNER, ENCUMBERED_COLLATERAL, ENCUMBERED_DEBT, SAVINGS_DAI, @@ -39,7 +40,8 @@ import { LIQUIDATION_PENALTY, ANNUAL_STABILITY_FEE, TOKEN_ALLOWANCE, - DEBT_FLOOR + DEBT_FLOOR, + PROXY_OWNER } from './constants'; export const collateralTypePrice = { @@ -81,6 +83,14 @@ export const vaultTypeAndAddress = { }) }; +export const vaultExternalOwner = { + generate: id => ({ + dependencies: [[PROXY_OWNER, [VAULT_OWNER, id]]], + // TODO: throw error if no owner (DSProxy contract doesn't exist) + computed: owner => owner + }) +}; + export const vaultCollateralAndDebt = { generate: (vaultType, vaultAddress) => ({ dependencies: [ @@ -219,6 +229,7 @@ export const vault = { [VAULT_TYPE, id], [VAULT_ADDRESS, id], [VAULT_OWNER, id], + [VAULT_EXTERNAL_OWNER, id], [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], [ENCUMBERED_DEBT, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]], @@ -240,6 +251,7 @@ export const vault = { vaultType, vaultAddress, ownerAddress, + externalOwnerAddress, encumberedCollateral, encumberedDebt, collateralTypePrice, @@ -257,9 +269,11 @@ export const vault = { annualStabilityFee, debtFloor ) => ({ + id: parseInt(id), vaultType, vaultAddress, ownerAddress, + externalOwnerAddress, encumberedCollateral, encumberedDebt, collateralTypePrice, @@ -351,6 +365,7 @@ export default { collateralTypePrice, collateralTypesPrices, vaultTypeAndAddress, + vaultExternalOwner, vaultCollateralAndDebt, vault, collateralAmount, diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index bf3edf65a..2db879f73 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -48,6 +48,7 @@ export const MAX_AUCTION_LOT_SIZE = 'maxAuctionLotSize'; export const COLLATERAL_TYPE_PRICE = 'collateralTypePrice'; export const COLLATERAL_TYPES_PRICES = 'collateralTypesPrices'; export const VAULT_TYPE_AND_ADDRESS = 'vaultTypeAndAddress'; +export const VAULT_EXTERNAL_OWNER = 'vaultExternalOwner'; export const VAULT_COLLATERAL_AND_DEBT = 'vaultCollateralAndDebt'; export const VAULT = 'vault'; export const DAI_LOCKED_IN_DSR = 'daiLockedInDsr'; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 06520cc79..68f293446 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -13,6 +13,7 @@ import { COLLATERAL_TYPE_PRICE, COLLATERAL_TYPES_PRICES, VAULT_TYPE_AND_ADDRESS, + VAULT_EXTERNAL_OWNER, VAULT, DEBT_VALUE, COLLATERALIZATION_RATIO, @@ -36,7 +37,10 @@ import { cdpManagerOwner } from '../../src/schemas/cdpManager'; import { spotIlks, spotPar } from '../../src/schemas/spot'; -import { proxyRegistryProxies } from '../../src/schemas/proxyRegistry'; +import { + proxyRegistryProxies, + proxyGetOwner +} from '../../src/schemas/proxyRegistry'; import { potPie, potpie, potChi } from '../../src/schemas/pot'; import { catIlks } from '../../src/schemas/cat'; import { jugIlks } from '../../src/schemas/jug'; @@ -44,7 +48,7 @@ import { tokenBalance, tokenAllowance } from '../../src/schemas/token'; import computedSchemas from '../../src/schemas/computed'; import { createCurrencyRatio } from '@makerdao/currency'; -let maker, snapshotData, proxyAddress; +let maker, snapshotData, address, proxyAddress; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -63,6 +67,7 @@ beforeAll(async () => { ], multicall: true }); + address = maker.service('web3').currentAddress(); maker.service('multicall').createWatcher({ interval: 'block' }); maker.service('multicall').registerSchemas({ @@ -75,6 +80,7 @@ beforeAll(async () => { spotPar, spotIlks, proxyRegistryProxies, + proxyGetOwner, potPie, potpie, potChi, @@ -143,6 +149,12 @@ test(VAULT_TYPE_AND_ADDRESS, async () => { expect(vaultAddress).toEqual(expectedVaultAddress); }); +test(VAULT_EXTERNAL_OWNER, async () => { + const cdpId = 1; + const owner = await maker.latest(VAULT_EXTERNAL_OWNER, cdpId); + expect(owner.toLowerCase()).toEqual(address); +}); + test(COLLATERAL_AMOUNT, async () => { const cdpId = 1; const collateralAmount = await maker.latest(COLLATERAL_AMOUNT, cdpId); @@ -220,6 +232,7 @@ test(VAULT, async () => { const expectedVaultType = 'ETH-A'; const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const expectedOwner = proxyAddress; + const expectedExternalOwner = address; const expectedEncumberedCollateral = fromWei(1000000000000000000); const expectedEncumberedDebt = fromWei(995000000000000000); const expectedColTypePrice = createCurrencyRatio(USD, ETH)(180); @@ -239,11 +252,15 @@ test(VAULT, async () => { const vault = await maker.latest(VAULT, cdpId); - expect(Object.keys(vault).length).toBe(21); + expect(Object.keys(vault).length).toBe(23); + expect(vault.id).toEqual(cdpId); expect(vault.vaultType).toEqual(expectedVaultType); expect(vault.vaultAddress).toEqual(expectedVaultAddress); expect(vault.ownerAddress).toEqual(expectedOwner); + expect(vault.externalOwnerAddress.toLowerCase()).toEqual( + expectedExternalOwner + ); expect(vault.encumberedCollateral).toEqual(expectedEncumberedCollateral); expect(vault.encumberedDebt.toNumber()).toBeCloseTo( expectedEncumberedDebt.toNumber() From c7450343536200ab0a8e3139dfe76e4e63a76afc Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 31 Jan 2020 17:05:15 +0800 Subject: [PATCH 098/117] Remove magic addresses from tests --- packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js | 8 ++++---- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index b24e6fd62..0a6e46e08 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -14,7 +14,7 @@ import { import cdpManagerSchemas from '../../src/schemas/cdpManager'; -let maker, snapshotData, cdpMgr, proxyAddress; +let maker, snapshotData, cdpMgr, proxyAddress, expectedVaultAddress; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -42,11 +42,12 @@ beforeAll(async () => { proxyAddress = await maker.service('proxy').ensureProxy(); await dai.approveUnlimited(proxyAddress); - await cdpMgr.openLockAndDraw( + const vault = await cdpMgr.openLockAndDraw( 'ETH-A', ETH_A_COLLATERAL_AMOUNT, ETH_A_DEBT_AMOUNT ); + expectedVaultAddress = await cdpMgr.getUrn(vault.id); }); afterAll(async () => { @@ -55,9 +56,8 @@ afterAll(async () => { test(VAULT_ADDRESS, async () => { const cdpId = 1; - const expected = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const vaultAddress = await maker.latest(VAULT_ADDRESS, cdpId); - expect(vaultAddress).toEqual(expected); + expect(vaultAddress).toEqual(expectedVaultAddress); }); test(VAULT_TYPE, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 68f293446..17213e9da 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -48,7 +48,7 @@ import { tokenBalance, tokenAllowance } from '../../src/schemas/token'; import computedSchemas from '../../src/schemas/computed'; import { createCurrencyRatio } from '@makerdao/currency'; -let maker, snapshotData, address, proxyAddress; +let maker, snapshotData, address, proxyAddress, expectedVaultAddress; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -103,11 +103,13 @@ beforeAll(async () => { proxyAddress = await maker.service('proxy').ensureProxy(); await dai.approveUnlimited(proxyAddress); - await mgr.openLockAndDraw( + const vault = await mgr.openLockAndDraw( 'ETH-A', ETH_A_COLLATERAL_AMOUNT, ETH_A_DEBT_AMOUNT ); + expectedVaultAddress = await mgr.getUrn(vault.id); + await mgr.openLockAndDraw( 'BAT-A', BAT_A_COLLATERAL_AMOUNT, @@ -140,7 +142,6 @@ test(COLLATERAL_TYPES_PRICES, async () => { test(VAULT_TYPE_AND_ADDRESS, async () => { const cdpId = 1; const expectedVaultType = 'ETH-A'; - const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const [collateralType, vaultAddress] = await maker.latest( VAULT_TYPE_AND_ADDRESS, cdpId @@ -230,7 +231,6 @@ test(COLLATERAL_AVAILABLE_VALUE, async () => { test(VAULT, async () => { const cdpId = 1; const expectedVaultType = 'ETH-A'; - const expectedVaultAddress = '0x6D43e8f5A6D2b5aD2b242A1D3CF957C71AfC48a1'; const expectedOwner = proxyAddress; const expectedExternalOwner = address; const expectedEncumberedCollateral = fromWei(1000000000000000000); From f2e8aab5fa3dd42d00ea51f0bd60c6326a79cdfc Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Mon, 3 Feb 2020 13:14:55 +0100 Subject: [PATCH 099/117] add some type defs and clean up file --- .../dai-plugin-mcd/typings/multicall.d.ts | 123 +++++++++++------- 1 file changed, 78 insertions(+), 45 deletions(-) diff --git a/packages/dai-plugin-mcd/typings/multicall.d.ts b/packages/dai-plugin-mcd/typings/multicall.d.ts index edb283914..ac9c31a20 100644 --- a/packages/dai-plugin-mcd/typings/multicall.d.ts +++ b/packages/dai-plugin-mcd/typings/multicall.d.ts @@ -2,82 +2,110 @@ declare global { interface Currency {} interface CurrencyRatio {} interface VaultResult { + /** + * The id of this vault. + */ + id: number; + /** * The collateral type of this vault. - * - * ```ts - * console.log('test code'); - * ``` */ vaultType: string; /** * The address of this vault. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * - * ```ts - * console.log('test code'); - * ``` */ vaultAddress: string; /** - * Lorem ipsum. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * + * The address of the proxy that owns this vault. + */ + ownerAddress: string; + + /** + * The address of the owner of the proxy, or ownerAddress. + */ + externalOwnerAddress: string; + + /** + * The amount of collateral locked for this vault. */ encumberedCollateral: Currency; /** - * Lorem ipsum. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * + * The amount of stablecoin debt owed for this vault. */ encumberedDebt: Currency; /** * The price of this vault's collateral type. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * */ collateralTypePrice: CurrencyRatio; /** * The value in DAI of this vault's debt. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * */ debtValue: Currency; + /** + * The ratio of collateral price to stablecoin debt. + */ + collateralizationRatio: CurrencyRatio; + + /** + * The amount of this vault's locked collateral. + */ + collateralAmount: Currency; + /** * The value in USD of this vault's locked collateral. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * */ collateralValue: Currency; + /** + * The minimum price of the collateral possible to remain above the liquidation ratio. + */ + liquidationPrice: CurrencyRatio; + /** * The maximum amount DAI available to generate for this vault. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * */ daiAvailable: Currency; + /** + * The amount of collateral available to withdraw. + */ + collateralAvailableAmount: Currency; + + /** + * The value in USD of the collateral available to withdraw. + */ + collateralAvailableValue: Currency; + + /** + * The amount of collateral that has been joined, but not yet locked. + */ + unlockedCollateral: Currency; + + /** + * The minimum ratio of collateral price to debt allowed. + */ + liquidationRatioSimple: CurrencyRatio; + + /** + * The penalty incurred for liquidation of this vault. + */ + liquidationPenalty: BigNumber; + + /** + * The stability fee for this vault. + */ + annualStabilityFee: number; + /** + * The minimum amount of stablecoin debt can exist this vault. + */ + debtFloor: BigNumber; } interface WatchInterfaceMcd { @@ -186,17 +214,22 @@ declare global { /** * Get a vault by id. - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod - * tempor incididunt ut labore et dolore magna aliqua. - * - * ```js - * const vault = watch.vaultById(2) - * ``` - * * @param id Numerical id of the vault */ static vault(id: number): VaultResult; + + /** + * @param id Numerical id of the vault + */ + static minSafeCollateralAmount(id: number): Currency; + /** + * @param id Numerical id of the vault + */ + static collateralAvailableAmount(id: number): Currency; + /** + * @param id Numerical id of the vault + */ + static collateralAvailableValue(id: number): Currency; } } From a2586b539c104bac36b3b4344e3f4862f3613f10 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 5 Feb 2020 18:25:24 +0800 Subject: [PATCH 100/117] Use latest multicall.js --- packages/dai/package.json | 2 +- yarn.lock | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/dai/package.json b/packages/dai/package.json index a3f07b741..eec8b7a75 100644 --- a/packages/dai/package.json +++ b/packages/dai/package.json @@ -52,7 +52,7 @@ "dependencies": { "@babel/runtime": "^7.3.4", "@makerdao/currency": "^0.9.5", - "@makerdao/multicall": "^0.9.0-rc.2", + "@makerdao/multicall": "^0.10.0-rc.1", "@makerdao/services-core": "^0.9.9", "assert": "^1.4.1", "bignumber.js": "^7.2.1", diff --git a/yarn.lock b/yarn.lock index b87348f6e..dc5c204ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -171,6 +171,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== +"@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" + integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== + "@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2" @@ -334,6 +339,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" +"@babel/plugin-proposal-optional-chaining@^7.6.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543" + integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-proposal-throw-expressions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.2.0.tgz#2d9e452d370f139000e51db65d0a85dc60c64739" @@ -407,6 +420,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + "@babel/plugin-syntax-throw-expressions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.2.0.tgz#79001ee2afe1b174b1733cdc2fc69c9a46a0f1f8" @@ -755,6 +775,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.7.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" + integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" @@ -1707,14 +1734,16 @@ babel-runtime "^6.26.0" ramda "^0.26.1" -"@makerdao/multicall@^0.9.0-rc.2": - version "0.9.0-rc.2" - resolved "https://registry.yarnpkg.com/@makerdao/multicall/-/multicall-0.9.0-rc.2.tgz#2a005df4f41f5097784781be077f09d9c82776e2" - integrity sha512-WRiMqugAIDvPFAadNg3+dgV16G94K5EGIJUUs2Ee9tzprX8+lPOhytVxQpnkAiLTP3ac108QtjTiKpKYLdOhpw== +"@makerdao/multicall@^0.10.0-rc.1": + version "0.10.0-rc.1" + resolved "https://registry.yarnpkg.com/@makerdao/multicall/-/multicall-0.10.0-rc.1.tgz#14732a2a6f31d8025ccc1d3e50560380980d0dd2" + integrity sha512-Axsq25vFImexQ+4MeTJP2BdqAHzzedGTUmVLsV385nXL1qa+92EgmM/8zP8/Wo57G0oSlGXnLTEnI0WrVNK1tQ== dependencies: + "@babel/runtime" "^7.7.4" cross-fetch "^3.0.4" debug "^4.1.1" ethers "^4.0.27" + invariant "^2.2.4" isomorphic-ws "^4.0.1" js-sha3 "^0.8.0" lodash "^4.17.11" From ac4a916585f3ae05ae232a38052d3ff0446c7d2e Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 5 Feb 2020 18:27:15 +0800 Subject: [PATCH 101/117] Add test:debug npm script for attaching a debugger --- packages/dai/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dai/package.json b/packages/dai/package.json index eec8b7a75..ff9875a8c 100644 --- a/packages/dai/package.json +++ b/packages/dai/package.json @@ -38,6 +38,7 @@ "testchain": "../../scripts/run-testchain.sh", "coverage": "yarn test --coverage", "test": "yarn testchain --ci jest --runInBand", + "test:debug": "yarn testchain --ci node --inspect-brk ../../node_modules/.bin/jest --runInBand", "build:frontend": "./scripts/build-frontend.sh", "build:backend": "./scripts/build-backend.sh", "build:backend:watch": "sane ./scripts/build-backend.sh src --wait=10", From b02e910a5f733af65bf17c9821371f99ee1bb874 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 5 Feb 2020 18:29:55 +0800 Subject: [PATCH 102/117] Add multicall to contracts --- packages/dai/contracts/abis.js | 4 +++- packages/dai/contracts/abis/Multicall.json | 1 + packages/dai/contracts/contracts.js | 3 ++- packages/dai/contracts/networks.js | 9 +++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 packages/dai/contracts/abis/Multicall.json diff --git a/packages/dai/contracts/abis.js b/packages/dai/contracts/abis.js index 142115fac..caabe4ef7 100644 --- a/packages/dai/contracts/abis.js +++ b/packages/dai/contracts/abis.js @@ -21,6 +21,8 @@ import vox from './abis/SaiVox.json'; import mom from './abis/SaiMom.json'; import pit from './abis/GemPit.json'; +import multicall from './abis/Multicall.json'; + const daiV1 = { saiTop, tub, @@ -55,4 +57,4 @@ const proxies = { proxyRegistry }; -export { daiV1, dappHub, exchangesV1, general, proxies }; +export { daiV1, dappHub, exchangesV1, general, proxies, multicall }; diff --git a/packages/dai/contracts/abis/Multicall.json b/packages/dai/contracts/abis/Multicall.json new file mode 100644 index 000000000..8520016c0 --- /dev/null +++ b/packages/dai/contracts/abis/Multicall.json @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentBlockGasLimit","outputs":[{"internalType":"uint256","name":"gaslimit","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/packages/dai/contracts/contracts.js b/packages/dai/contracts/contracts.js index cf9eb639f..95b44ac49 100644 --- a/packages/dai/contracts/contracts.js +++ b/packages/dai/contracts/contracts.js @@ -14,5 +14,6 @@ export default { SAI_PROXY: 'SAI_PROXY', PROXY_REGISTRY: 'PROXY_REGISTRY', DS_PROXY_FACTORY: 'DS_PROXY_FACTORY', - DS_PROXY: 'DS_PROXY' + DS_PROXY: 'DS_PROXY', + MULTICALL: 'MULTICALL' }; diff --git a/packages/dai/contracts/networks.js b/packages/dai/contracts/networks.js index d07dfc05a..167661f03 100644 --- a/packages/dai/contracts/networks.js +++ b/packages/dai/contracts/networks.js @@ -96,6 +96,15 @@ export function contractInfo(network) { } ], + // Multicall + [contracts.MULTICALL]: [ + { + version: 1, + address: addresses.MULTICALL, + abi: abis.multicall + } + ], + // Proxies [contracts.SAI_PROXY]: [ { From c636820209b193e107a875496856b3c0d3bcedeb Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 5 Feb 2020 18:50:34 +0800 Subject: [PATCH 103/117] Change multicall tests to use SCD testchain state only - Multicall schema tests now only use SCD testchain state - Added removeSchemaDelay service config option - Added computedThrottleTime service config option - Flush pending schema removals when watcher stopped - Removed buildTestMulticallService service builder --- packages/dai/.prettierrc | 13 ++ packages/dai/src/eth/MulticallService.js | 216 +++++++++--------- packages/dai/src/utils/conversion.js | 26 +++ .../dai/test/eth/MulticallService.spec.js | 180 ++++++--------- packages/dai/test/helpers/schemas.js | 175 +++++--------- packages/dai/test/helpers/serviceBuilders.js | 15 -- 6 files changed, 279 insertions(+), 346 deletions(-) create mode 100644 packages/dai/.prettierrc diff --git a/packages/dai/.prettierrc b/packages/dai/.prettierrc new file mode 100644 index 000000000..49bfe6bfa --- /dev/null +++ b/packages/dai/.prettierrc @@ -0,0 +1,13 @@ +{ + "singleQuote": true, + "overrides": [ + { + "files": [ + "MulticallService.*" + ], + "options": { + "printWidth": 100 + } + } + ] +} diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index fcd37525e..dc16be9f1 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,15 +1,9 @@ +import BigNumber from 'bignumber.js'; import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; -import { - Observable, - ReplaySubject, - combineLatest, - from, - interval, - timer -} from 'rxjs'; -import { map, flatMap, throttle, debounce, take } from 'rxjs/operators'; +import { Observable, ReplaySubject, combineLatest, from } from 'rxjs'; +import { map, flatMap, throttleTime, debounceTime, take } from 'rxjs/operators'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -29,29 +23,23 @@ export default class MulticallService extends PublicService { this._multicallResultCache = {}; this._addresses = {}; this._removeSchemaTimers = {}; - this._removeSchemaDelay = 1000; } - initialize() { - this.watch = this.watchObservable; - this._addresses = this.get('smartContract').getContractAddresses(); + initialize(settings = {}) { + this._addresses = settings.addresses || this.get('smartContract').getContractAddresses(); + this._removeSchemaDelay = settings.removeSchemaDelay || 1000; + this._debounceTime = settings.debounceTime || 100; + this._computedThrottleTime = settings.computedThrottleTime || 1; } authenticate() { this._connectedAddress = this.get('web3').currentAddress(); } - createWatcher({ - useWeb3Provider = false, - interval = 'block', - rpcUrl, - ...config - } = {}) { + createWatcher({ useWeb3Provider = false, interval = 'block', rpcUrl, ...config } = {}) { const web3 = this.get('web3'); config = { - multicallAddress: this.get('smartContract').getContractAddress( - 'MULTICALL' - ), + multicallAddress: this.get('smartContract').getContractAddress('MULTICALL'), ...config }; @@ -67,11 +55,7 @@ export default class MulticallService extends PublicService { this._watcher = createWatcher([], { ...config, interval, rpcUrl }); if (onNewBlockPolling) { - log( - `Watcher created with poll on new block mode using ${ - rpcUrl ? `rpcUrl: ${rpcUrl}` : 'web3 provider' - }` - ); + log(`Watcher created with poll on new block mode using ${rpcUrl ? `rpcUrl: ${rpcUrl}` : 'web3 provider'}`); // prettier-ignore web3.onNewBlock(blockNumber => { log(`Polling after new block detected (${blockNumber})`); this._watcher.poll(); @@ -84,16 +68,11 @@ export default class MulticallService extends PublicService { ); } - this._watcher.onPoll(({ id, latestBlockNumber }) => - log( - `Sending eth_call network request #${id}${ - latestBlockNumber ? ` (latestBlockNumber: ${latestBlockNumber})` : '' - }` - ) - ); - this._watcher.onNewBlock(blockHeight => - log(`Latest block: ${blockHeight}`) + this._watcher.onPoll(({ id, latestBlockNumber: block }) => + log(`Sending network request #${id}${block ? ` (latest block: ${block})` : ''}`) ); + this._watcher.onNewBlock(block => log(`Latest block: ${block}`)); + this._watcher.onError(err => console.error('Multicall error:', err)); return this._watcher; } @@ -108,6 +87,17 @@ export default class MulticallService extends PublicService { return this._watcher.start(); } + stop() { + this._flushPendingSchemaRemovals(); + log('Watcher stopped'); + return this._watcher.stop(); + } + + restart() { + this.stop(); + this.start(); + } + schemaByObservableKey(key) { return this._schemaByObservableKey[key]; } @@ -116,9 +106,24 @@ export default class MulticallService extends PublicService { return Object.keys(this._schemaByObservableKey); } + get watcher() { + return this._watcher; + } + + get activeSchemas() { + return this._watcher.schemas.filter(({ id }) => id); // Filter only schemas with id + } + + get activeSchemaIds() { + return this.activeSchemas.map(({ id }) => id); + } + + get activeSchemasCount() { + return this._watchingSchemasTotal; + } + registerSchemas(schemas) { - if (typeof schemas !== 'object') - throw new Error('Schemas must be object or array'); + if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); // If schemas is key/val object use key as schema key and convert to array object if (!Array.isArray(schemas)) @@ -132,13 +137,16 @@ export default class MulticallService extends PublicService { if (!schema.return && !schema.returns) schema.returns = [schema.key]; if (schema.return && schema.returns) throw new Error('Ambiguous return definitions in schema: found both return and returns property'); // prettier-ignore if (schema.return) schema.returns = [schema.return]; - if (!Array.isArray(schema.returns)) throw new Error('Schema must contain return/returns property'); // prettier-ignore + if (!Array.isArray(schema.returns)) + throw new Error('Schema must contain return/returns property'); // Use return keys to create observable key => schema mapping // and normalize as array of [key, transform] arrays schema.returns = schema.returns.map(ret => { if (!Array.isArray(ret)) ret = [ret]; + if (this._schemaByObservableKey[ret[0]] !== undefined) + throw new Error(`Observable with key ${ret[0]} already registered`); this._schemaByObservableKey[ret[0]] = schema; - if (ret.length > 2) throw new Error('Returns array format should be [key, transform]'); // prettier-ignore + if (ret.length > 2) throw new Error('Returns array format should be [key, transform]'); return ret; }); }); @@ -146,10 +154,9 @@ export default class MulticallService extends PublicService { log2(`Registered ${schemas.length} schemas`); } - watchObservable(key, ...args) { + watch(key, ...args) { const schema = this.schemaByObservableKey(key); - if (!schema) - throw new Error(`No registered schema found for observable key: ${key}`); + if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); // Validate schema params if (schema.validateParams) schema.validateParams(...args); @@ -165,13 +172,13 @@ export default class MulticallService extends PublicService { .join('.'); const fullPath = `${key}${path ? '.' : ''}${path}`; - log2(`watchObservable() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + log2(`watch() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore // Check if an observable already exists for this path (key + args) const existingObservable = get(this._observables, fullPath); if (existingObservable) { const isComputed = !get(this._subjects, fullPath, subject); - log2(`Returning existing ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + log2(`Returning existing ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); return existingObservable; } @@ -184,7 +191,7 @@ export default class MulticallService extends PublicService { const dependencies = typeof generatedSchema.dependencies === 'function' ? generatedSchema.dependencies({ - watch: this.watchObservable.bind(this), + watch: this.watch.bind(this), get: this.get.bind(this) }) : generatedSchema.dependencies; @@ -201,28 +208,26 @@ export default class MulticallService extends PublicService { if (Array.isArray(trie) && trie.length === 0) { // When trie is an empty array, indicates that we only need to return - // watchObservable on the key - return this.watchObservable(key); + // watch on the key + return this.watch(key); } else if (allLeafNodes) { // If the trie is an array it indicates that the observable is // expecting arguments. These can be normal values or other // observables. Where an index in the trie is an array, it is // assumed that it is syntax for an observable argument. In the case // where all indexes in the trie array are normal values, we use the - // spread operator to pass them to the returned watchObservable fn - return this.watchObservable(key, ...trie); + // spread operator to pass them to the returned watch fn + return this.watch(key, ...trie); } else { // When a trie array has nested observables, recursively call this fn // on indexes which have an array. return combineLatest( trie.map((node, idx) => { - return indexesAtLeafNodes[idx] - ? [node] - : recurseDependencyTree(node); + return indexesAtLeafNodes[idx] ? [node] : recurseDependencyTree(node); }) ).pipe( - throttle(() => interval(1)), - flatMap(result => this.watchObservable(key, ...result)) + throttleTime(this._computedThrottleTime), + flatMap(result => this.watch(key, ...result)) ); } }; @@ -230,7 +235,7 @@ export default class MulticallService extends PublicService { const dependencySubs = dependencies.map(recurseDependencyTree); const observerLatest = combineLatest(dependencySubs).pipe( - throttle(() => interval(1)), + throttleTime(this._computedThrottleTime), map(result => generatedSchema.computed(...result)) ); @@ -252,11 +257,7 @@ export default class MulticallService extends PublicService { // Emit initial value if cached result from multicall exists if ( this._multicallResultCache[fullPath] !== undefined && - this._validateResult( - subject, - fullPath, - this._multicallResultCache[fullPath] - ) + this._validateResult(subject, fullPath, this._multicallResultCache[fullPath]) ) subject.next(this._multicallResultCache[fullPath]); @@ -267,14 +268,14 @@ export default class MulticallService extends PublicService { if (!this._watcherUpdates) this._subscribeToWatcherUpdates(); // If first subscriber to this schema add it to multicall if (schema.watching[path] === undefined) schema.watching[path] = 0; - if (++schema.watching[path] === 1) this._addSchemaToMulticall(schema, generatedSchema, path); // prettier-ignore + if (++schema.watching[path] === 1) this._addSchemaToMulticall(schema, generatedSchema, path); // Subscribe this observer to the subject for this base observable const sub = subject.subscribe(observer); - log2(`Observer subscribed to ${generatedSchema.id} (${schema.watching[path]} subscribers)`); // prettier-ignore + log2(`Observer subscribed to ${generatedSchema.id} (${schema.watching[path]} subscribers)`); // Return the function to call when this observer unsubscribes return () => { // If last unsubscriber from this schema remove it from multicall - if (--schema.watching[path] === 0) this._removeSchemaFromMulticall(generatedSchema); // prettier-ignore + if (--schema.watching[path] === 0) this._removeSchemaFromMulticall(generatedSchema); // Unsubscribe this observer from the subject for this base observable sub.unsubscribe(); log2(`Observer unsubscribed from ${generatedSchema.id} (${schema.watching[path]} subscribers)`); // prettier-ignore @@ -287,27 +288,19 @@ export default class MulticallService extends PublicService { } latest(key, ...args) { - // TODO Possible configuration setting for timer? - return this.watchObservable(key, ...args) + return this.watch(key, ...args) .pipe( - debounce(() => timer(100)), + debounceTime(this._debounceTime), take(1) ) .toPromise(); } _addSchemaToMulticall(schema, generatedSchema, path) { - let { - id, - target, - contractName, - call, - returns, - transforms = {} - } = generatedSchema; + let { id, target, contractName, call, returns, transforms = {} } = generatedSchema; // If this schema is already added but pending removal if (this._removeSchemaTimers[id]) { - log2(`Cancelled pending schema removal for: ${id}`); + log2(`Cancelled pending schema removal: ${id}`); clearTimeout(this._removeSchemaTimers[id]); this._removeSchemaTimers[id] = null; return; @@ -323,7 +316,7 @@ export default class MulticallService extends PublicService { ? [fullPath, ret[1]] : [fullPath]; }); - if (!target && !contractName) throw new Error('Schema must specify target or contractName'); // prettier-ignore + if (!target && !contractName) throw new Error('Schema must specify target or contractName'); if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore this._watcher.tap(calls => [ ...calls, @@ -335,29 +328,41 @@ export default class MulticallService extends PublicService { } ]); log2(`Schema added to multicall: ${id}`); - this._watcher.tap(s => { - log2('Current schemas: ' + s.filter(({ id }) => id).map(({ id }) => id).join(',')); // prettier-ignore - log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); - return s; - }); + log2(`Active schemas: ${this.activeSchemaIds.join(',')} (${this.activeSchemasCount} total)`); } - _removeSchemaFromMulticall({ id }) { - this._removeSchemaTimers[id] = setTimeout(() => { - this._removeSchemaTimers[id] = null; - log2(`Schema removed from multicall: ${id}`); - this._watcher.tap(schemas => schemas.filter(({ id: id_ }) => id_ !== id)); - this._watcher.tap(s => { - log2(`Total schemas in multicall: ${s.filter(({ id }) => id).length}`); - return s; - }); - // If no schemas are being watched, unsubscribe from watcher updates - if (--this._watchingSchemasTotal === 0) { - log2('Unsubscribed from watcher updates'); - this._watcherUpdates.unsub(); - this._watcherUpdates = null; - } - }, this._removeSchemaDelay); + _removeSchemaImmediately(id) { + if (this._removeSchemaTimers[id] !== undefined) delete this._removeSchemaTimers[id]; + log2(`Schema removed from multicall: ${id}`); + this._watcher.tap(schemas => schemas.filter(({ id: id_ }) => id_ !== id)); + // If no schemas are being watched, unsubscribe from watcher updates + if (--this._watchingSchemasTotal === 0) { + log2('No remaining active schemas'); + log2('Unsubscribed from watcher updates'); + this._watcherUpdates.unsub(); + this._watcherUpdates = null; + } + else log2(`Active schemas: ${this.activeSchemaIds.join(',')} (${this.activeSchemasCount} remaining)`); // prettier-ignore + } + + _removeSchemaFromMulticall({ id, immediate = false }) { + if (immediate) this._removeSchemaImmediately(id); + else + this._removeSchemaTimers[id] = setTimeout( + () => this._removeSchemaImmediately(id), + this._removeSchemaDelay + ); + } + + _flushPendingSchemaRemovals() { + const schemaTimers = Object.keys(this._removeSchemaTimers); + if (schemaTimers.length === 0) return; + log2(`Flushing ${schemaTimers.length} pending schema removals`); + for (let id of schemaTimers) { + log2(`Forcing schema removal: ${id}`); + clearTimeout(this._removeSchemaTimers[id]); + this._removeSchemaImmediately(id); + } } _validateResult(subject, type, value) { @@ -380,18 +385,11 @@ export default class MulticallService extends PublicService { this._watcherUpdates = this._watcher.subscribe(update => { const subject = get(this._subjects, update.type); if (subject) { - log2('Got watcher update for ' + update.type + ':', update.value); - if (this._validateResult(subject, update.type, update.value)) - subject.next(update.value); + let logValue = update.value; + if (logValue instanceof BigNumber) logValue = logValue.toFormat(); + log2('Got watcher update for ' + update.type + ':', logValue); + if (this._validateResult(subject, update.type, update.value)) subject.next(update.value); } else this._multicallResultCache[update.type] = update.value; }); } - - disconnect() { - // TODO - } - - get watcher() { - return this._watcher; - } } diff --git a/packages/dai/src/utils/conversion.js b/packages/dai/src/utils/conversion.js index d39bd36e7..44715c1f1 100644 --- a/packages/dai/src/utils/conversion.js +++ b/packages/dai/src/utils/conversion.js @@ -1,3 +1,4 @@ +import BigNumber from 'bignumber.js'; import { utils as ethersUtils } from 'ethers'; export function numberToBytes32(num) { @@ -17,3 +18,28 @@ export function stringToBytes32(text, pad = true) { if (pad) data = ethersUtils.padZeros(data, 32); return ethersUtils.hexlify(data); } + +export function padRight(string, chars, sign) { + return string + new Array(chars - string.length + 1).join(sign ? sign : '0'); +} + +export function toHex(str, { with0x = true, rightPadding = 64 } = {}) { + let result = ''; + for (let i = 0; i < str.length; i++) { + result += str.charCodeAt(i).toString(16); + } + if (rightPadding > 0) result = padRight(result, rightPadding); + return with0x ? '0x' + result : result; +} + +export function fromWei(value) { + return BigNumber(value).shiftedBy(-18); +} + +export function fromRay(value) { + return BigNumber(value).shiftedBy(-27); +} + +export function fromRad(value) { + return BigNumber(value).shiftedBy(-45); +} diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index 9477d98b3..1fbd8cfaa 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -1,132 +1,104 @@ import TestAccountProvider from '@makerdao/test-helpers/src/TestAccountProvider'; -import { buildTestMulticallService } from '../helpers/serviceBuilders'; -import schemas from '../helpers/schemas'; -import addresses from '../../../dai-plugin-mcd/contracts/addresses/testnet.json'; -import { first } from 'rxjs/operators'; -// import { promiseWait } from '../../src/utils'; +import Maker from '../../src/index'; +import BigNumber from 'bignumber.js'; + +import schemas, { + TOTAL_CDP_DEBT, + ETH_PRICE, + CDP_COLLATERAL, + CDP_DEBT, + CDP_COLLATERAL_VALUE, + LAST_CREATED_CDP_COLLATERAL_VALUE +} from '../helpers/schemas'; + +let maker, multicall, watcher, address, cdpId; + +beforeAll(async () => { + maker = await Maker.create('test', { + multicall: { + debounceTime: 1 + }, + log: false + }); + await maker.authenticate(); + address = TestAccountProvider.nextAddress(); + multicall = maker.service('multicall'); + watcher = multicall.createWatcher(); + multicall.registerSchemas(schemas); + // Lock 5 ETH and draw 100 DAI + const proxyAddress = await maker.service('proxy').ensureProxy(); + ({ id: cdpId } = {}) = await maker.service('cdp').openProxyCdpLockEthAndDrawDai(5, 100, proxyAddress); // prettier-ignore +}); -let service; -let watcher; -let address; +beforeEach(() => { + multicall.start(); +}); -beforeEach(async () => { - service = buildTestMulticallService(); - await service.manager().authenticate(); - address = TestAccountProvider.nextAddress(); - watcher = service.createWatcher({ interval: 'block' }); - service.registerSchemas(schemas); - service._addresses = addresses; - service.start(); +afterEach(() => { + multicall.stop(); }); test('get eth balance via multicall', async () => { + const web3 = multicall.get('web3'); + const fromWei = web3._web3.utils.fromWei; watcher.stop(); - const initialBlock = - (await service.get('web3').getBlock('latest')).number + 1; - const initialEthBalance = service - .get('web3') - ._web3.utils.fromWei( - (await service.get('web3').getBalance(address)).toString() - ); - - service.tap(() => [ + const initialBlock = (await web3.getBlock('latest')).number + 1; + const initialEthBalance = fromWei(await web3.getBalance(address)).toString(); + watcher.tap(() => [ { call: ['getEthBalance(address)(uint256)', address], - returns: [ - [ - 'ETH_BALANCE', - v => service.get('web3')._web3.utils.fromWei(v.toString()) - ] - ] + returns: [['ETH_BALANCE', v => fromWei(v.toString())]] } ]); - service.start(); - - let ethBalance = ''; - const batchSub = watcher.subscribe(update => { - if (update.type === 'ETH_BALANCE') ethBalance = update.value; - }); - let blockNumber; - const newBlockSub = watcher.onNewBlock(number => (blockNumber = number)); - + watcher.start(); + const results = {}; + const batchSub = watcher.subscribe(update => (results[update.type] = update.value.toString())); + const newBlockSub = watcher.onNewBlock(number => (results.blockNumber = number)); await watcher.awaitInitialFetch(); - batchSub.unsub(); newBlockSub.unsub(); - expect(ethBalance.toString()).toEqual(initialEthBalance); - expect(parseInt(blockNumber)).toEqual(initialBlock); -}); - -test('watch multiple base observables from same schema', async () => { - const observable1 = service.watchObservable('debtCeiling', 'ETH-A'); - const observable3 = service.watchObservable('debtScalingFactor', 'ETH-A'); - const debtCeilingEth = await observable1.pipe(first()).toPromise(); - const debtScalingFactorEth = await observable3.pipe(first()).toPromise(); - - expect(Number(debtCeilingEth)).toEqual(100000); - expect(Number(debtScalingFactorEth)).toEqual(1); -}); - -test('watch multiple base observables from same schema with different schema params', async () => { - const observable1 = service.watchObservable('debtCeiling', 'ETH-A'); - const observable2 = service.watchObservable('debtCeiling', 'BAT-A'); - const debtCeilingEth = await observable1.pipe(first()).toPromise(); - const debtCeilingBat = await observable2.pipe(first()).toPromise(); - - expect(Number(debtCeilingEth)).toEqual(100000); - expect(Number(debtCeilingBat)).toEqual(5000); + expect(results.ETH_BALANCE).toEqual(initialEthBalance); + expect(parseInt(results.blockNumber)).toEqual(initialBlock); }); -test('watch computed observable', async () => { - const observable = service.watchObservable('testComputed1', 'ETH-A'); - const testComputed1 = await observable.pipe(first()).toPromise(); - - expect(testComputed1).toEqual(100001); +test('base observable', async () => { + const expectedTotalCdpDebt = BigNumber(100); + const totalCdpDebt = await maker.latest(TOTAL_CDP_DEBT); + expect(totalCdpDebt).toEqual(expectedTotalCdpDebt); }); -test('watch computed observable with computed dependency', async () => { - const observable = service.watchObservable('testComputed2', 5); - const testComputed2 = await observable.pipe(first()).toPromise(); - - expect(testComputed2).toEqual(500005); +test('base observable with arg', async () => { + const expectedCdpCollateral = BigNumber(5); + const cdpCollateral = await maker.latest(CDP_COLLATERAL, cdpId); + expect(cdpCollateral).toEqual(expectedCdpCollateral); }); -test('watch computed observable with promise dependency', async () => { - const observable = service.watchObservable('testComputed3', 2); - const testComputed3 = await observable.pipe(first()).toPromise(); +test('multiple base observables', async () => { + const expectedCdpCollateral = BigNumber(5); + const expectedCdpDebt = BigNumber(100); + const expectedEthPrice = BigNumber(400); - expect(testComputed3).toEqual(2000020); -}); - -test('watch computed observable with dynamically generated dependencies', async () => { - const observable = service.watchObservable('ilkDebtCeilings', [ - 'ETH-A', - 'BAT-A' - ]); - const ilkDebtCeilings = await observable.pipe(first()).toPromise(); + const cdpCollateral = await maker.latest(CDP_COLLATERAL, cdpId); + const cdpDebt = await maker.latest(CDP_DEBT, cdpId); + const ethPrice = await maker.latest(ETH_PRICE); - expect(ilkDebtCeilings).toEqual([100000, 5000]); + expect(cdpCollateral).toEqual(expectedCdpCollateral); + expect(cdpDebt).toEqual(expectedCdpDebt); + expect(ethPrice).toEqual(expectedEthPrice); + expect(multicall.activeSchemasCount).toEqual(3); }); -test('watch same computed observable with dynamically generated dependencies more than once', async () => { - const observable1 = service.watchObservable('ilkDebtCeilings', [ - 'ETH-A', - 'BAT-A' - ]); - const observable2 = service.watchObservable('ilkDebtCeilings', [ - 'ETH-A', - 'BAT-A' - ]); - const ilkDebtCeilings1 = await observable1.pipe(first()).toPromise(); - const ilkDebtCeilings2 = await observable2.pipe(first()).toPromise(); - - expect(ilkDebtCeilings1).toEqual([100000, 5000]); - expect(ilkDebtCeilings2).toEqual([100000, 5000]); +test('computed observable', async () => { + const expectedCdpCollateralValue = BigNumber(2000); + const cdpCollateralValue = await maker.latest(CDP_COLLATERAL_VALUE, cdpId); + expect(cdpCollateralValue).toEqual(expectedCdpCollateralValue); + expect(multicall.activeSchemasCount).toEqual(2); }); -test('watch computed observable which chains calls to base observables', async () => { - const observable = service.watchObservable('testComputed4', address); - const res = await observable.pipe(first()).toPromise(); - expect(res).toEqual(0); +test('computed observable with nested dependencies', async () => { + const expectedLastCreatedCdpDebt = BigNumber(2000); + const lastCreatedCdpDebt = await maker.latest(LAST_CREATED_CDP_COLLATERAL_VALUE); + expect(lastCreatedCdpDebt).toEqual(expectedLastCreatedCdpDebt); + expect(multicall.activeSchemasCount).toEqual(3); }); diff --git a/packages/dai/test/helpers/schemas.js b/packages/dai/test/helpers/schemas.js index ac81c5133..30b7c1360 100644 --- a/packages/dai/test/helpers/schemas.js +++ b/packages/dai/test/helpers/schemas.js @@ -1,139 +1,78 @@ -/* eslint-disable */ -import BigNumber from 'bignumber.js'; - -function debtValue(art, rate) { - art = fromWei(art); - return art.times(rate).shiftedBy(-27); -} - -export function padRight(string, chars, sign) { - return string + new Array(chars - string.length + 1).join(sign ? sign : '0'); -} - -export function toHex(str, { with0x = true, rightPadding = 64 } = {}) { - let result = ''; - for (let i = 0; i < str.length; i++) { - result += str.charCodeAt(i).toString(16); - } - if (rightPadding > 0) result = padRight(result, rightPadding); - return with0x ? '0x' + result : result; -} - -export function fromWei(value) { - return BigNumber(value).shiftedBy(-18); -} - -export function fromRay(value) { - return BigNumber(value).shiftedBy(-27); -} - -export function fromRad(value) { - return BigNumber(value).shiftedBy(-45); -} - -const totalEncumberedDebt = 'totalEncumberedDebt'; -const debtScalingFactor = 'debtScalingFactor'; -const priceWithSafetyMargin = 'priceWithSafetyMargin'; -const debtCeiling = 'debtCeiling'; -const urnDebtFloor = 'urnDebtFloor'; - -export const ilk = { - generate: ilkName => ({ - id: `MCD_VAT.ilks(${ilkName})`, - contractName: 'MCD_VAT', - call: [ - 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', - toHex(ilkName) - ] +import { fromWei, numberToBytes32 } from '../../src/utils/conversion'; + +export const TOTAL_CDP_DEBT = 'totalCdpDebt'; +export const ETH_PRICE = 'ethPrice'; +export const CDP_COLLATERAL = 'cdpCollateral'; +export const CDP_DEBT = 'cdpDebt'; +export const CDP_COUNT = 'cdpCount'; +export const CDP_COLLATERAL_VALUE = 'cdpCollateralValue'; +export const LAST_CREATED_CDP_COLLATERAL_VALUE = 'lastCreatedCdpCollateralValue'; // prettier-ignore + +export const tubRum = { + generate: () => ({ + id: 'SAI_TUB.rum', + contractName: 'SAI_TUB', + call: ['rum()(uint256)'] }), - returns: [ - totalEncumberedDebt, - [debtScalingFactor, fromRay], - priceWithSafetyMargin, - [debtCeiling, fromRad], - urnDebtFloor - ] -}; - -export const ilkDebt = { - generate: ilkName => ({ - dependencies: [ - ['totalEncumberedDebt', ilkName], - ['debtScalingFactor', ilkName] - ], - computed: (art, rate) => { - console.log('ilkDebt computed:', art, rate); - return debtValue(art, rate); - } - }) -}; - -export const testComputed1 = { - generate: ilkName => ({ - dependencies: [['debtScalingFactor', ilkName], ['debtCeiling', ilkName]], - computed: (debtScalingFactor, debtCeiling) => - Number(debtScalingFactor) + Number(debtCeiling) - }) + returns: [[TOTAL_CDP_DEBT, fromWei]] }; -export const testComputed2 = { - generate: multiplyBy => ({ - dependencies: [['testComputed1', 'ETH-A']], - computed: testComputed1 => testComputed1 * multiplyBy - }) +export const tubCupi = { + generate: () => ({ + id: 'SAI_TUB.cupi', + contractName: 'SAI_TUB', + call: ['cupi()(uint256)'] + }), + returns: [[CDP_COUNT, parseInt]] }; -export const testComputed3 = { - generate: multiplyBy => ({ - dependencies: [ - ['testComputed2', 10], - [() => new Promise(resolve => resolve(multiplyBy))] - ], - computed: (testComputed2, promiseResult) => testComputed2 * promiseResult - }) +export const tubInk = { + generate: id => ({ + id: `SAI_TUB.ink(${id})`, + contractName: 'SAI_TUB', + call: ['ink(bytes32)(uint256)', numberToBytes32(id)] + }), + returns: [[CDP_COLLATERAL, fromWei]] }; -export const proxies = { - generate: address => ({ - id: `PROXY_REGISTRY.proxies(${address})`, - contractName: 'PROXY_REGISTRY', - call: ['proxies(address)(address)', address] +export const tubTab = { + generate: id => ({ + id: `SAI_TUB.tab(${id})`, + contractName: 'SAI_TUB', + call: ['tab(bytes32)(uint256)', numberToBytes32(id)] }), - returns: [['proxyAddress']] + returns: [[CDP_DEBT, fromWei]] }; -export const potpie = { - generate: proxyAddress => ({ - id: `MCD_POT.pie(${proxyAddress})`, - contractName: 'MCD_POT', - call: ['pie(address)(uint256)', proxyAddress] +export const pipRead = { + generate: () => ({ + id: 'SAI_PIP.read', + contractName: 'SAI_PIP', + call: ['read()(uint256)'] }), - returns: [['savingsDaiByProxy']] + returns: [[ETH_PRICE, fromWei]] }; -export const testComputed4 = { - generate: address => ({ - dependencies: [['savingsDaiByProxy', ['proxyAddress', address]]], - computed: res => Number(res) +export const cdpCollateralValue = { + generate: id => ({ + dependencies: () => [[CDP_COLLATERAL, id], [ETH_PRICE]], + computed: (cdpCollateral, ethPrice) => cdpCollateral.times(ethPrice) }) }; -export const ilkDebtCeilings = { - generate: ilks => ({ - // Dynamically generate dependencies - dependencies: () => ilks.map(ilkName => ['debtCeiling', ilkName]), - computed: (...results) => results.map(r => Number(r)) +export const lastCreatedCdpCollateralValue = { + generate: () => ({ + dependencies: () => [[CDP_COLLATERAL, [CDP_COUNT]], [ETH_PRICE]], + computed: (cdpCollateral, ethPrice) => cdpCollateral.times(ethPrice) }) }; export default { - ilk, - ilkDebt, - testComputed1, - testComputed2, - testComputed3, - proxies, - potpie, - testComputed4, - ilkDebtCeilings + tubRum, + tubInk, + tubTab, + tubCupi, + pipRead, + cdpCollateralValue, + lastCreatedCdpCollateralValue }; diff --git a/packages/dai/test/helpers/serviceBuilders.js b/packages/dai/test/helpers/serviceBuilders.js index eb94b7fd4..0dfd1387f 100644 --- a/packages/dai/test/helpers/serviceBuilders.js +++ b/packages/dai/test/helpers/serviceBuilders.js @@ -2,7 +2,6 @@ import DefaultServiceProvider from '../../src/config/DefaultServiceProvider'; import ProviderType from '../../src/eth/web3/ProviderType'; import has from 'lodash/has'; import merge from 'lodash/merge'; -import testnetAddresses from '../../contracts/addresses/testnet.json'; export const infuraProjectId = 'c3f0f26a4c1742e0949d8eedfc47be67'; //dai.js project id @@ -75,17 +74,3 @@ export function buildTestSmartContractService(settings = {}) { export function buildTestEventService(settings = {}) { return buildTestService('event', { ...settings, event: true }); } - -export function buildTestMulticallService(settings = {}) { - return buildTestService('multicall', { - ...settings, - smartContract: { - addContracts: { - MULTICALL: { - address: testnetAddresses.MULTICALL - } - } - }, - multicall: true - }); -} From 42a3f4aafc118cf137c8997f939838577b9a2770 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 6 Feb 2020 12:26:07 +0800 Subject: [PATCH 104/117] Rename getters and separately track total schema subscribers --- packages/dai/package.json | 2 +- packages/dai/src/eth/MulticallService.js | 33 ++++++++++++------- .../dai/test/eth/MulticallService.spec.js | 6 ++-- yarn.lock | 9 +++-- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/dai/package.json b/packages/dai/package.json index ff9875a8c..6579c6879 100644 --- a/packages/dai/package.json +++ b/packages/dai/package.json @@ -53,7 +53,7 @@ "dependencies": { "@babel/runtime": "^7.3.4", "@makerdao/currency": "^0.9.5", - "@makerdao/multicall": "^0.10.0-rc.1", + "@makerdao/multicall": "^0.10.0-rc.3", "@makerdao/services-core": "^0.9.9", "assert": "^1.4.1", "bignumber.js": "^7.2.1", diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index dc16be9f1..2cb7cdb68 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -1,4 +1,3 @@ -import BigNumber from 'bignumber.js'; import { PublicService } from '@makerdao/services-core'; import { createWatcher } from '@makerdao/multicall'; import debug from 'debug'; @@ -19,7 +18,8 @@ export default class MulticallService extends PublicService { this._subjects = {}; this._observables = {}; this._watcherUpdates = null; - this._watchingSchemasTotal = 0; + this._totalSchemaSubscribers = 0; + this._totalActiveSchemas = 0; this._multicallResultCache = {}; this._addresses = {}; this._removeSchemaTimers = {}; @@ -118,8 +118,12 @@ export default class MulticallService extends PublicService { return this.activeSchemas.map(({ id }) => id); } - get activeSchemasCount() { - return this._watchingSchemasTotal; + get totalActiveSchemas() { + return this._totalActiveSchemas; + } + + get totalSchemaSubscribers() { + return this._totalSchemaSubscribers; } registerSchemas(schemas) { @@ -263,7 +267,7 @@ export default class MulticallService extends PublicService { // Create base observable const observable = Observable.create(observer => { - this._watchingSchemasTotal++; + this._totalSchemaSubscribers++; // Subscribe to watcher updates and emit them to subjects if (!this._watcherUpdates) this._subscribeToWatcherUpdates(); // If first subscriber to this schema add it to multicall @@ -302,7 +306,7 @@ export default class MulticallService extends PublicService { if (this._removeSchemaTimers[id]) { log2(`Cancelled pending schema removal: ${id}`); clearTimeout(this._removeSchemaTimers[id]); - this._removeSchemaTimers[id] = null; + delete this._removeSchemaTimers[id]; return; } // Automatically generate return keys if not explicitly specified in generated schema @@ -318,6 +322,7 @@ export default class MulticallService extends PublicService { }); if (!target && !contractName) throw new Error('Schema must specify target or contractName'); if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore + this._totalActiveSchemas++; this._watcher.tap(calls => [ ...calls, { @@ -328,21 +333,24 @@ export default class MulticallService extends PublicService { } ]); log2(`Schema added to multicall: ${id}`); - log2(`Active schemas: ${this.activeSchemaIds.join(',')} (${this.activeSchemasCount} total)`); + if (process?.browser) log2('Active schemas (' + this._totalActiveSchemas + ' total):', this.activeSchemaIds); + else log2(`Active schemas (${this._totalActiveSchemas} total): ${this.activeSchemaIds.join(',')}`); // prettier-ignore } _removeSchemaImmediately(id) { if (this._removeSchemaTimers[id] !== undefined) delete this._removeSchemaTimers[id]; log2(`Schema removed from multicall: ${id}`); this._watcher.tap(schemas => schemas.filter(({ id: id_ }) => id_ !== id)); + if (--this._totalActiveSchemas === 0) log2('No remaining active schemas'); // If no schemas are being watched, unsubscribe from watcher updates - if (--this._watchingSchemasTotal === 0) { - log2('No remaining active schemas'); + if (--this._totalSchemaSubscribers === 0) { log2('Unsubscribed from watcher updates'); this._watcherUpdates.unsub(); this._watcherUpdates = null; + } else { + if (process?.browser) log2('Active schemas (' + this._totalActiveSchemas + ' total):', this.activeSchemaIds); + else log2(`Active schemas (${this._totalActiveSchemas} remaining): ${this.activeSchemaIds.join(',')}`); // prettier-ignore } - else log2(`Active schemas: ${this.activeSchemaIds.join(',')} (${this.activeSchemasCount} remaining)`); // prettier-ignore } _removeSchemaFromMulticall({ id, immediate = false }) { @@ -385,8 +393,9 @@ export default class MulticallService extends PublicService { this._watcherUpdates = this._watcher.subscribe(update => { const subject = get(this._subjects, update.type); if (subject) { - let logValue = update.value; - if (logValue instanceof BigNumber) logValue = logValue.toFormat(); + const logValue = update.value?._isBigNumber + ? `${update.value.toString()} (BigNumber)` + : update.value; log2('Got watcher update for ' + update.type + ':', logValue); if (this._validateResult(subject, update.type, update.value)) subject.next(update.value); } else this._multicallResultCache[update.type] = update.value; diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index 1fbd8cfaa..f0930ddf9 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -86,19 +86,19 @@ test('multiple base observables', async () => { expect(cdpCollateral).toEqual(expectedCdpCollateral); expect(cdpDebt).toEqual(expectedCdpDebt); expect(ethPrice).toEqual(expectedEthPrice); - expect(multicall.activeSchemasCount).toEqual(3); + expect(multicall.totalActiveSchemas).toEqual(3); }); test('computed observable', async () => { const expectedCdpCollateralValue = BigNumber(2000); const cdpCollateralValue = await maker.latest(CDP_COLLATERAL_VALUE, cdpId); expect(cdpCollateralValue).toEqual(expectedCdpCollateralValue); - expect(multicall.activeSchemasCount).toEqual(2); + expect(multicall.totalActiveSchemas).toEqual(2); }); test('computed observable with nested dependencies', async () => { const expectedLastCreatedCdpDebt = BigNumber(2000); const lastCreatedCdpDebt = await maker.latest(LAST_CREATED_CDP_COLLATERAL_VALUE); expect(lastCreatedCdpDebt).toEqual(expectedLastCreatedCdpDebt); - expect(multicall.activeSchemasCount).toEqual(3); + expect(multicall.totalActiveSchemas).toEqual(3); }); diff --git a/yarn.lock b/yarn.lock index dc5c204ee..2ebdf9705 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1734,10 +1734,10 @@ babel-runtime "^6.26.0" ramda "^0.26.1" -"@makerdao/multicall@^0.10.0-rc.1": - version "0.10.0-rc.1" - resolved "https://registry.yarnpkg.com/@makerdao/multicall/-/multicall-0.10.0-rc.1.tgz#14732a2a6f31d8025ccc1d3e50560380980d0dd2" - integrity sha512-Axsq25vFImexQ+4MeTJP2BdqAHzzedGTUmVLsV385nXL1qa+92EgmM/8zP8/Wo57G0oSlGXnLTEnI0WrVNK1tQ== +"@makerdao/multicall@^0.10.0-rc.3": + version "0.10.0-rc.3" + resolved "https://registry.yarnpkg.com/@makerdao/multicall/-/multicall-0.10.0-rc.3.tgz#b93ef3d75a6621ff7110edce5e240e8168ccd6f2" + integrity sha512-sQHR8tVJpT2e3OdOmPIfFfzmJNniA+1aedQ1078V76MMGvkDXI8rVT6XSgsutBPwbveQbSyBqyc32A5HplEpmQ== dependencies: "@babel/runtime" "^7.7.4" cross-fetch "^3.0.4" @@ -1745,7 +1745,6 @@ ethers "^4.0.27" invariant "^2.2.4" isomorphic-ws "^4.0.1" - js-sha3 "^0.8.0" lodash "^4.17.11" ws "^7.2.0" From 5fa94ef0bc6217716db08d8633df03ebc9f0032b Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Thu, 6 Feb 2020 15:07:12 +0800 Subject: [PATCH 105/117] Update multicall.js and fix token tests --- packages/dai-plugin-mcd/test/schemas/cat.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 6 ++++-- packages/dai-plugin-mcd/test/schemas/jug.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/spot.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/token.spec.js | 9 +++++++-- packages/dai-plugin-mcd/test/schemas/vat.spec.js | 2 +- packages/dai/package.json | 2 +- yarn.lock | 8 ++++---- 10 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/cat.spec.js b/packages/dai-plugin-mcd/test/schemas/cat.spec.js index ef0df9fa6..29f5340d8 100644 --- a/packages/dai-plugin-mcd/test/schemas/cat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cat.spec.js @@ -19,7 +19,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(catSchemas); maker.service('multicall').start(); }); diff --git a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js index 0a6e46e08..10bead233 100644 --- a/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/cdpManager.spec.js @@ -30,7 +30,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(cdpManagerSchemas); maker.service('multicall').start(); await setupCollateral(maker, 'ETH-A', { diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 17213e9da..30b6baccb 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -3,7 +3,8 @@ import { ETH, BAT, MDAI, USD } from '../../src'; import { takeSnapshot, restoreSnapshot, - TestAccountProvider + TestAccountProvider, + mineBlocks } from '@makerdao/test-helpers'; import { fromWei } from '../../src/utils'; import { ServiceRoles } from '../../src/constants'; @@ -69,7 +70,7 @@ beforeAll(async () => { }); address = maker.service('web3').currentAddress(); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas({ vatIlks, vatUrns, @@ -369,6 +370,7 @@ test(ALLOWANCE, async () => { .service('token') .getToken('BAT') .approveUnlimited(nextAccountProxy); + await mineBlocks(maker.service('token'), 1); batAllowance = await maker.latest(ALLOWANCE, 'BAT'); expect(batAllowance).toEqual(true); diff --git a/packages/dai-plugin-mcd/test/schemas/jug.spec.js b/packages/dai-plugin-mcd/test/schemas/jug.spec.js index cbc1c693c..47a238f71 100644 --- a/packages/dai-plugin-mcd/test/schemas/jug.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/jug.spec.js @@ -18,7 +18,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(jugSchemas); maker.service('multicall').start(); }); diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index f93c9e309..516e405f2 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -27,7 +27,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(potSchemas); maker.service('multicall').start(); await setupCollateral(maker, 'ETH-A', { diff --git a/packages/dai-plugin-mcd/test/schemas/spot.spec.js b/packages/dai-plugin-mcd/test/schemas/spot.spec.js index 982361038..4e8811c7e 100644 --- a/packages/dai-plugin-mcd/test/schemas/spot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/spot.spec.js @@ -19,7 +19,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(spotSchemas); maker.service('multicall').start(); }); diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js index cbf12955a..6ed5cbe72 100644 --- a/packages/dai-plugin-mcd/test/schemas/token.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -1,5 +1,9 @@ import { mcdMaker } from '../helpers'; -import { takeSnapshot, restoreSnapshot } from '@makerdao/test-helpers'; +import { + takeSnapshot, + restoreSnapshot, + mineBlocks +} from '@makerdao/test-helpers'; import { ETH, BAT, MDAI, MWETH, ALLOWANCE_AMOUNT } from '../../src'; import BigNumber from 'bignumber.js'; import { ServiceRoles } from '../../src/constants'; @@ -30,7 +34,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(tokenSchemas); maker.service('multicall').start(); address = maker.currentAddress(); @@ -92,6 +96,7 @@ test(TOKEN_ALLOWANCE, async () => { .service('token') .getToken('BAT') .approveUnlimited(proxyAddress); + await mineBlocks(maker.service('token'), 1); const setBatAllowance = await maker.latest( TOKEN_ALLOWANCE, diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index 6af3253de..895b637f7 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -40,7 +40,7 @@ beforeAll(async () => { }); snapshotData = await takeSnapshot(maker); - maker.service('multicall').createWatcher({ interval: 'block' }); + maker.service('multicall').createWatcher(); maker.service('multicall').registerSchemas(vatSchemas); maker.service('multicall').start(); await setupCollateral(maker, 'ETH-A', { diff --git a/packages/dai/package.json b/packages/dai/package.json index 6579c6879..df3ec6f80 100644 --- a/packages/dai/package.json +++ b/packages/dai/package.json @@ -53,7 +53,7 @@ "dependencies": { "@babel/runtime": "^7.3.4", "@makerdao/currency": "^0.9.5", - "@makerdao/multicall": "^0.10.0-rc.3", + "@makerdao/multicall": "^0.10.0-rc.4", "@makerdao/services-core": "^0.9.9", "assert": "^1.4.1", "bignumber.js": "^7.2.1", diff --git a/yarn.lock b/yarn.lock index 2ebdf9705..c273eef5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1734,10 +1734,10 @@ babel-runtime "^6.26.0" ramda "^0.26.1" -"@makerdao/multicall@^0.10.0-rc.3": - version "0.10.0-rc.3" - resolved "https://registry.yarnpkg.com/@makerdao/multicall/-/multicall-0.10.0-rc.3.tgz#b93ef3d75a6621ff7110edce5e240e8168ccd6f2" - integrity sha512-sQHR8tVJpT2e3OdOmPIfFfzmJNniA+1aedQ1078V76MMGvkDXI8rVT6XSgsutBPwbveQbSyBqyc32A5HplEpmQ== +"@makerdao/multicall@^0.10.0-rc.4": + version "0.10.0-rc.4" + resolved "https://registry.yarnpkg.com/@makerdao/multicall/-/multicall-0.10.0-rc.4.tgz#05cd24b3db34ba46f9173ede1c34347a2001e560" + integrity sha512-TTHXf78SPnBkNUGMhDKs42TnLCa8aoNBy4cMO/QzqWB+a0+/H8wI1SlFxD7mHLMjTdxVdncZ/99/p0Mk375kVg== dependencies: "@babel/runtime" "^7.7.4" cross-fetch "^3.0.4" From 80e20fb2a6626b013664985e4de7a1b9bec0a433 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Thu, 6 Feb 2020 15:44:50 +0100 Subject: [PATCH 106/117] liquidation ratio refactor --- .../dai-plugin-mcd/src/schemas/computed.js | 49 +++++-------------- packages/dai-plugin-mcd/src/schemas/spot.js | 30 +----------- .../test/schemas/computed.spec.js | 4 +- .../dai-plugin-mcd/test/schemas/spot.spec.js | 4 +- 4 files changed, 20 insertions(+), 67 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 0469bc9d6..a63a1bfa8 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -36,7 +36,6 @@ import { SAVINGS_RATE_ACCUMULATOR, DAI_LOCKED_IN_DSR, TOKEN_BALANCE, - LIQUIDATION_RATIO_SIMPLE, LIQUIDATION_PENALTY, ANNUAL_STABILITY_FEE, TOKEN_ALLOWANCE, @@ -52,9 +51,8 @@ export const collateralTypePrice = { [LIQUIDATION_RATIO, collateralTypeName] ], computed: (ratioDaiUsd, priceWithSafetyMargin, liquidationRatio) => { - const currency = createCurrency( - liquidationRatio.numerator.symbol.substring(1, 4) - ); + const [symbol] = collateralTypeName.split('-'); + const currency = createCurrency(symbol); const ratio = createCurrencyRatio(USD, currency); const price = priceWithSafetyMargin .times(ratioDaiUsd.toNumber()) @@ -111,8 +109,8 @@ export const collateralAmount = { [ENCUMBERED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]] ], computed: (vaultType, encumberedCollateral) => { - // Todo: better way to get collateral name - const currency = createCurrency(vaultType.substring(0, 3)); + const [symbol] = vaultType.split('-'); + const currency = createCurrency(symbol); return currency(encumberedCollateral); } }) @@ -157,11 +155,7 @@ export const liquidationPrice = { [LIQUIDATION_RATIO, [VAULT_TYPE, id]] ], computed: (collateralAmount, debtValue, liquidationRatio) => - calcLiquidationPrice( - collateralAmount, - debtValue, - createCurrencyRatio(USD, MDAI)(liquidationRatio.toNumber()) - ) + calcLiquidationPrice(collateralAmount, debtValue, liquidationRatio) }) }; @@ -173,11 +167,7 @@ export const daiAvailable = { [LIQUIDATION_RATIO, [VAULT_TYPE, id]] ], computed: (collateralValue, debtValue, liquidationRatio) => - calcDaiAvailable( - collateralValue, - debtValue, - createCurrencyRatio(USD, MDAI)(liquidationRatio.toNumber()) - ) + calcDaiAvailable(collateralValue, debtValue, liquidationRatio) }) }; @@ -189,11 +179,7 @@ export const minSafeCollateralAmount = { [COLLATERAL_TYPE_PRICE, [VAULT_TYPE, id]] ], computed: (debtValue, liquidationRatio, price) => - calcMinSafeCollateralAmount( - debtValue, - createCurrencyRatio(USD, MDAI)(liquidationRatio.toNumber()), - price - ) + calcMinSafeCollateralAmount(debtValue, liquidationRatio, price) }) }; export const collateralAvailableAmount = { @@ -215,14 +201,6 @@ export const collateralAvailableValue = { }) }; -export const liquidationRatioSimple = { - generate: id => ({ - dependencies: [[LIQUIDATION_RATIO, [VAULT_TYPE, id]]], - computed: liquidationRatio => - createCurrencyRatio(USD, MDAI)(liquidationRatio.toNumber()) - }) -}; - export const vault = { generate: id => ({ dependencies: [ @@ -242,7 +220,7 @@ export const vault = { [COLLATERAL_AVAILABLE_AMOUNT, id], [COLLATERAL_AVAILABLE_VALUE, id], [UNLOCKED_COLLATERAL, [VAULT_TYPE, id], [VAULT_ADDRESS, id]], - [LIQUIDATION_RATIO_SIMPLE, id], + [LIQUIDATION_RATIO, [VAULT_TYPE, id]], [LIQUIDATION_PENALTY, [VAULT_TYPE, id]], [ANNUAL_STABILITY_FEE, [VAULT_TYPE, id]], [DEBT_FLOOR, id] @@ -264,7 +242,7 @@ export const vault = { collateralAvailableAmount, collateralAvailableValue, unlockedCollateral, - liquidationRatioSimple, + liquidationRatio, liquidationPenalty, annualStabilityFee, debtFloor @@ -286,20 +264,20 @@ export const vault = { collateralAvailableAmount, collateralAvailableValue, unlockedCollateral, - liquidationRatioSimple, + liquidationRatio, liquidationPenalty, annualStabilityFee, debtFloor, calculateLiquidationPrice({ collateralAmount = this.collateralAmount, debtValue = this.debtValue, - liquidationRatioSimple = this.liquidationRatioSimple + liquidationRatio = this.liquidationRatio } = {}) { - if (!collateralAmount || !debtValue || !liquidationRatioSimple) return; + if (!collateralAmount || !debtValue || !liquidationRatio) return; return calcLiquidationPrice( collateralAmount, debtValue, - liquidationRatioSimple + liquidationRatio ); }, calculateCollateralizationRatio({ @@ -380,6 +358,5 @@ export default { daiLockedInDsr, totalDaiLockedInDsr, balance, - liquidationRatioSimple, allowance }; diff --git a/packages/dai-plugin-mcd/src/schemas/spot.js b/packages/dai-plugin-mcd/src/schemas/spot.js index 7d07d910b..4c17faff5 100644 --- a/packages/dai-plugin-mcd/src/schemas/spot.js +++ b/packages/dai-plugin-mcd/src/schemas/spot.js @@ -1,5 +1,5 @@ import { toHex, fromRay } from '../utils'; -import { createCurrency, createCurrencyRatio } from '@makerdao/currency'; +import { createCurrencyRatio } from '@makerdao/currency'; import { MDAI, USD } from '../..'; import { @@ -8,12 +8,6 @@ import { RATIO_DAI_USD } from './constants'; -// The liquidation ratio value is the ratio between the minimum dollar amount of a unit of -// collateral in terms of a single dollar unit amount of debt in which the system does not -// deem a vault of that collateral type (ilk) underwater -// -// In plain english, it is the ratio of the dollar amount of ETH in terms of -// the dollar amount of dai export const spotIlks = { generate: collateralTypeName => ({ id: `MCD_SPOT.ilks(${collateralTypeName})`, @@ -21,10 +15,7 @@ export const spotIlks = { call: ['ilks(bytes32)(address,uint256)', toHex(collateralTypeName)], transforms: { [LIQUIDATION_RATIO]: liqRatio => - createCurrencyRatio( - createCurrency(`(${collateralTypeName.split('-')[0]}/USD)`), - createCurrency(`(${MDAI.symbol}/USD)`) - )(fromRay(liqRatio)) + createCurrencyRatio(USD, MDAI)(fromRay(liqRatio)) } }), validateParams: collateralTypeName => { @@ -43,23 +34,6 @@ export const spotPar = { returns: [[RATIO_DAI_USD, v => createCurrencyRatio(MDAI, USD)(fromRay(v))]] }; -// export const liquidationRatio = { -// // The liquidation ratio value is the ratio between the minimum dollar amount of a unit of -// // collateral in terms of a single dollar unit amount of debt in which the system does not -// // deem a vault of that collateral type (ilk) underwater -// // -// // In plain english, it is the ratio of the dollar amount of ETH in terms of -// // the dollar amount of dai -// generate: collateralTypeName => ({ -// dependencies: () => [[RAW_LIQUIDATION_RATIO, collateralTypeName]], -// computed: liqRatio => -// createCurrencyRatio( -// createCurrency(`(${collateralTypeName.split('-')[0]}/USD)`), -// createCurrency(`(${MDAI.symbol}/USD)`) -// )(liqRatio) -// }) -// }; - export default { spotIlks, spotPar diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 30b6baccb..684780b99 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -292,7 +292,9 @@ test(VAULT, async () => { expectedCollateralAvailableValue.toString() ); expect(vault.unlockedCollateral).toEqual(expectedUnlockedCollateral); - expect(vault.liquidationRatioSimple).toEqual(expectedLiqRatio); + expect(vault.liquidationRatio.toString()).toEqual( + expectedLiqRatio.toString() + ); expect(vault.liquidationPenalty).toEqual(expectedLiqPenalty); expect(vault.annualStabilityFee.toNumber()).toEqual(expectedAnnStabilityFee); expect(vault.debtFloor).toEqual(expectedDebtFloor); diff --git a/packages/dai-plugin-mcd/test/schemas/spot.spec.js b/packages/dai-plugin-mcd/test/schemas/spot.spec.js index 4e8811c7e..a701c35c6 100644 --- a/packages/dai-plugin-mcd/test/schemas/spot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/spot.spec.js @@ -45,8 +45,8 @@ test(LIQUIDATION_RATIO, async () => { const ethALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'ETH-A'); const batALiquidationRatio = await maker.latest(LIQUIDATION_RATIO, 'BAT-A'); - expect(ethALiquidationRatio.symbol).toEqual('(ETH/USD)/(MDAI/USD)'); - expect(batALiquidationRatio.symbol).toEqual('(BAT/USD)/(MDAI/USD)'); + expect(ethALiquidationRatio.symbol).toEqual('USD/MDAI'); + expect(batALiquidationRatio.symbol).toEqual('USD/MDAI'); expect(ethALiquidationRatio.toNumber()).toEqual(1.5); expect(batALiquidationRatio.toNumber()).toEqual(2.0); From 2f6408751e4b4d21fcc607b7392797d3a917a45b Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Fri, 7 Feb 2020 13:59:08 +0800 Subject: [PATCH 107/117] Fix constant typo and validate observable key --- packages/dai-plugin-mcd/src/schemas/constants.js | 2 +- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai/src/eth/MulticallService.js | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 2db879f73..cfbf3d742 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -37,7 +37,7 @@ export const SAVINGS_DAI = 'savingsDai'; export const DAI_SAVINGS_RATE = 'daiSavingsRate'; export const ANNUAL_DAI_SAVINGS_RATE = 'annualDaiSavingsRate'; export const DATE_EARNINGS_LAST_ACCRUED = 'dateEarningsLastAccrued'; -export const SAVINGS_RATE_ACCUMUALTOR = 'savingsRateAccumulator'; +export const SAVINGS_RATE_ACCUMULATOR = 'savingsRateAccumulator'; // cat export const LIQUIDATOR_ADDRESS = 'liquidatorAddress'; diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 684780b99..2894c1a07 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -320,7 +320,7 @@ test(DAI_LOCKED_IN_DSR, async () => { }); test(TOTAL_DAI_LOCKED_IN_DSR, async () => { - const totalDaiLockedInDsr = await maker.latest(DAI_LOCKED_IN_DSR); + const totalDaiLockedInDsr = await maker.latest(TOTAL_DAI_LOCKED_IN_DSR); expect(totalDaiLockedInDsr.symbol).toEqual('DSR-DAI'); expect(totalDaiLockedInDsr.toNumber()).toBeCloseTo(1, 18); }); diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 2cb7cdb68..64903218d 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -159,6 +159,8 @@ export default class MulticallService extends PublicService { } watch(key, ...args) { + if (!key) throw new Error('Invalid observable key'); + const schema = this.schemaByObservableKey(key); if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); From 40b5cc70f3e6ff9e8192fab2b1ddccb8dfde6332 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 7 Feb 2020 16:50:15 +0000 Subject: [PATCH 108/117] added savings schema --- .../dai-plugin-mcd/src/schemas/computed.js | 39 ++++++++++++++++--- .../dai-plugin-mcd/src/schemas/constants.js | 1 + 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index a63a1bfa8..621e15f96 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -40,7 +40,10 @@ import { ANNUAL_STABILITY_FEE, TOKEN_ALLOWANCE, DEBT_FLOOR, - PROXY_OWNER + PROXY_OWNER, + ANNUAL_DAI_SAVINGS_RATE, + DAI_SAVINGS_RATE, + DATE_EARNINGS_LAST_ACCRUED } from './constants'; export const collateralTypePrice = { @@ -294,9 +297,9 @@ export const vault = { }; export const daiLockedInDsr = { - generate: () => ({ - dependencies: ({ get }) => [ - [SAVINGS_DAI, [PROXY_ADDRESS, get('web3').currentAddress()]], + generate: address => ({ + dependencies: () => [ + [SAVINGS_DAI, [PROXY_ADDRESS, address]], [SAVINGS_RATE_ACCUMULATOR] ], computed: (savingsDai, savingsRateAccumulator) => { @@ -339,6 +342,31 @@ export const allowance = { }) }; +export const savings = { + generate: address => ({ + dependencies: [ + [ANNUAL_DAI_SAVINGS_RATE], + [DAI_SAVINGS_RATE], + [DATE_EARNINGS_LAST_ACCRUED], + [DAI_LOCKED_IN_DSR, address], + [PROXY_ADDRESS, address] + ], + computed: ( + annualDaiSavingsRate, + daiSavingsRate, + dateEarningsLastAccrued, + daiLockedInDsr, + proxyAddress + ) => ({ + annualDaiSavingsRate, + daiSavingsRate, + dateEarningsLastAccrued, + daiLockedInDsr, + proxyAddress + }) + }) +}; + export default { collateralTypePrice, collateralTypesPrices, @@ -358,5 +386,6 @@ export default { daiLockedInDsr, totalDaiLockedInDsr, balance, - allowance + allowance, + savings }; diff --git a/packages/dai-plugin-mcd/src/schemas/constants.js b/packages/dai-plugin-mcd/src/schemas/constants.js index 2db879f73..5cdc0e1df 100644 --- a/packages/dai-plugin-mcd/src/schemas/constants.js +++ b/packages/dai-plugin-mcd/src/schemas/constants.js @@ -65,6 +65,7 @@ export const COLLATERAL_AVAILABLE_VALUE = 'collateralAvailableValue'; export const LIQUIDATION_RATIO_SIMPLE = 'liquidationRatioSimple'; export const BALANCE = 'balance'; export const ALLOWANCE = 'allowance'; +export const SAVINGS = 'savings'; // token export const TOKEN_BALANCE = 'tokenBalance'; From 6bfded98abf71d5f0d29f7a70c7ba3f2330e0f2d Mon Sep 17 00:00:00 2001 From: sirromdev Date: Fri, 7 Feb 2020 17:29:27 +0000 Subject: [PATCH 109/117] Added savings schema --- packages/dai-plugin-mcd/src/schemas/computed.js | 16 +++++++--------- .../dai-plugin-mcd/test/schemas/computed.spec.js | 16 ++++++++-------- packages/dai-plugin-mcd/test/schemas/vat.spec.js | 4 ++-- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/computed.js b/packages/dai-plugin-mcd/src/schemas/computed.js index 621e15f96..10f2cd70e 100644 --- a/packages/dai-plugin-mcd/src/schemas/computed.js +++ b/packages/dai-plugin-mcd/src/schemas/computed.js @@ -318,11 +318,10 @@ export const totalDaiLockedInDsr = { }; export const balance = { - generate: symbol => ({ - dependencies: ({ get }) => { - const address = get('web3').currentAddress(); + generate: (symbol, address) => ({ + dependencies: () => { if (symbol === 'DSR-DAI') { - return [[DAI_LOCKED_IN_DSR]]; + return [[DAI_LOCKED_IN_DSR, address]]; } return [[TOKEN_BALANCE, address, symbol]]; }, @@ -332,11 +331,10 @@ export const balance = { }; export const allowance = { - generate: symbol => ({ - dependencies: ({ get }) => { - const address = get('web3').currentAddress(); - return [[TOKEN_ALLOWANCE, address, [PROXY_ADDRESS, address], symbol]]; - }, + generate: (symbol, address) => ({ + dependencies: [ + [TOKEN_ALLOWANCE, address, [PROXY_ADDRESS, address], symbol] + ], demarcate: true, computed: v => v.isEqualTo(ALLOWANCE_AMOUNT) }) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index 2894c1a07..e095bbd41 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -314,7 +314,7 @@ test('vault with invalid id', async () => { }); test(DAI_LOCKED_IN_DSR, async () => { - const daiLockedInDsr = await maker.latest(DAI_LOCKED_IN_DSR); + const daiLockedInDsr = await maker.latest(DAI_LOCKED_IN_DSR, address); expect(daiLockedInDsr.symbol).toEqual('DSR-DAI'); expect(daiLockedInDsr.toNumber()).toBeCloseTo(1, 18); }); @@ -328,16 +328,16 @@ test(TOTAL_DAI_LOCKED_IN_DSR, async () => { test(BALANCE, async () => { expect.assertions(11); - const ethBalance = await maker.latest(BALANCE, 'ETH'); - const batBalance = await maker.latest(BALANCE, 'BAT'); + const ethBalance = await maker.latest(BALANCE, 'ETH', address); + const batBalance = await maker.latest(BALANCE, 'BAT', address); expect(ethBalance.symbol).toEqual('ETH'); expect(batBalance.symbol).toEqual('BAT'); expect(ethBalance.toNumber()).toBeCloseTo(93.677, 2); expect(batBalance.toBigNumber()).toEqual(BigNumber('999')); - const daiBalance = await maker.latest(BALANCE, 'DAI'); - const wethBalance = await maker.latest(BALANCE, 'WETH'); + const daiBalance = await maker.latest(BALANCE, 'DAI', address); + const wethBalance = await maker.latest(BALANCE, 'WETH', address); expect(daiBalance.symbol).toEqual('MDAI'); expect(daiBalance.toBigNumber()).toEqual(BigNumber('1')); @@ -345,7 +345,7 @@ test(BALANCE, async () => { expect(wethBalance.symbol).toEqual('MWETH'); expect(wethBalance.toBigNumber()).toEqual(BigNumber('0')); - const dsrDaiBalance = await maker.latest(BALANCE, 'DSR-DAI'); + const dsrDaiBalance = await maker.latest(BALANCE, 'DSR-DAI', address); expect(dsrDaiBalance.symbol).toEqual('DSR-DAI'); expect(dsrDaiBalance.toNumber()).toBeCloseTo(1, 18); @@ -365,7 +365,7 @@ test(ALLOWANCE, async () => { const nextAccountProxy = await maker.service('proxy').ensureProxy(); let batAllowance; - batAllowance = await maker.latest(ALLOWANCE, 'BAT'); + batAllowance = await maker.latest(ALLOWANCE, 'BAT', nextAccount.address); expect(batAllowance).toEqual(false); await maker @@ -374,7 +374,7 @@ test(ALLOWANCE, async () => { .approveUnlimited(nextAccountProxy); await mineBlocks(maker.service('token'), 1); - batAllowance = await maker.latest(ALLOWANCE, 'BAT'); + batAllowance = await maker.latest(ALLOWANCE, 'BAT', nextAccount.address); expect(batAllowance).toEqual(true); maker.useAccount('default'); diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index 895b637f7..dd6701614 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -64,10 +64,10 @@ beforeAll(async () => { BAT_A_DEBT_AMOUNT ); cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); - ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); -}); + console.log(ethAInfo); +}, 10000); afterAll(async () => { await restoreSnapshot(snapshotData, maker); From 7462bd04342a864f69567a69f8765e66ca3581e1 Mon Sep 17 00:00:00 2001 From: sirromdev Date: Sun, 9 Feb 2020 16:58:44 +0000 Subject: [PATCH 110/117] change id for chi --- packages/dai-plugin-mcd/src/schemas/pot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js index 344ca338b..78c012b7f 100644 --- a/packages/dai-plugin-mcd/src/schemas/pot.js +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -36,7 +36,7 @@ export const potDsr = { }; export const potChi = { generate: () => ({ - id: 'MCD_POT.dsr', + id: 'MCD_POT.chi', contractName: 'MCD_POT', call: ['chi()(uint256)'] }), From ee3940ec0671948b345e011df6037407aeb99e73 Mon Sep 17 00:00:00 2001 From: Josh Levine Date: Sun, 9 Feb 2020 19:51:28 -0500 Subject: [PATCH 111/117] dai-plugin-governance-v0.9.2 --- packages/dai-plugin-governance/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai-plugin-governance/package.json b/packages/dai-plugin-governance/package.json index 556a0db35..db3011d28 100644 --- a/packages/dai-plugin-governance/package.json +++ b/packages/dai-plugin-governance/package.json @@ -1,7 +1,7 @@ { "name": "@makerdao/dai-plugin-governance", "description": "A dai.js plugin for adding MKR governance support to dapps.", - "version": "0.9.1-rc.2", + "version": "0.9.2", "license": "MIT", "repository": { "type": "git", From ca249e07b4436d2d299eea38509c991a4627fb50 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 10 Feb 2020 13:56:15 +0800 Subject: [PATCH 112/117] Minor multicall schema test fixes --- packages/dai-plugin-mcd/test/schemas/computed.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/pot.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/token.spec.js | 2 +- packages/dai-plugin-mcd/test/schemas/vat.spec.js | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/computed.spec.js b/packages/dai-plugin-mcd/test/schemas/computed.spec.js index e095bbd41..e344eb191 100644 --- a/packages/dai-plugin-mcd/test/schemas/computed.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/computed.spec.js @@ -350,7 +350,7 @@ test(BALANCE, async () => { expect(dsrDaiBalance.toNumber()).toBeCloseTo(1, 18); try { - await maker.latest(BALANCE, 'NON_MCD_TOKEN'); + await maker.latest(BALANCE, 'NON_MCD_TOKEN', address); } catch (e) { expect(e).toEqual( Error('NON_MCD_TOKEN token is not part of the default tokens list') diff --git a/packages/dai-plugin-mcd/test/schemas/pot.spec.js b/packages/dai-plugin-mcd/test/schemas/pot.spec.js index 516e405f2..ba7fa55a2 100644 --- a/packages/dai-plugin-mcd/test/schemas/pot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/pot.spec.js @@ -57,7 +57,7 @@ afterAll(async () => { test(TOTAL_SAVINGS_DAI, async () => { const totalSavingsDai = await maker.latest(TOTAL_SAVINGS_DAI); expect(BigNumber.isBigNumber(totalSavingsDai)).toEqual(true); - expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795, 3); + expect(totalSavingsDai.toNumber()).toBeCloseTo(0.999795); }); test(SAVINGS_DAI, async () => { diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js index 6ed5cbe72..951285420 100644 --- a/packages/dai-plugin-mcd/test/schemas/token.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -96,7 +96,7 @@ test(TOKEN_ALLOWANCE, async () => { .service('token') .getToken('BAT') .approveUnlimited(proxyAddress); - await mineBlocks(maker.service('token'), 1); + await mineBlocks(maker.service('token')); const setBatAllowance = await maker.latest( TOKEN_ALLOWANCE, diff --git a/packages/dai-plugin-mcd/test/schemas/vat.spec.js b/packages/dai-plugin-mcd/test/schemas/vat.spec.js index dd6701614..f1acafe67 100644 --- a/packages/dai-plugin-mcd/test/schemas/vat.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/vat.spec.js @@ -66,7 +66,6 @@ beforeAll(async () => { cdpTypeService = maker.service(ServiceRoles.CDP_TYPE); ethAInfo = await cdpTypeService.getCdpType(ETH, 'ETH-A').ilkInfo(); batAInfo = await cdpTypeService.getCdpType(BAT, 'BAT-A').ilkInfo(); - console.log(ethAInfo); }, 10000); afterAll(async () => { From 02d2a80f85758d59e3bd5c7b5edb1344e8c3c59c Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 10 Feb 2020 14:36:28 +0800 Subject: [PATCH 113/117] Refactor multicall service and harden tests --- packages/dai-plugin-mcd/src/schemas/cat.js | 8 +- .../dai-plugin-mcd/src/schemas/cdpManager.js | 21 +- packages/dai-plugin-mcd/src/schemas/spot.js | 22 +- .../dai-plugin-mcd/test/schemas/spot.spec.js | 14 ++ packages/dai/.prettierrc | 3 +- packages/dai/src/eth/MulticallService.js | 216 ++++++++++-------- packages/dai/src/utils/conversion.js | 12 + .../dai/test/eth/MulticallService.spec.js | 62 +++-- packages/dai/test/helpers/schemas.js | 56 +++-- 9 files changed, 277 insertions(+), 137 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cat.js b/packages/dai-plugin-mcd/src/schemas/cat.js index b32a087ea..5aea575f0 100644 --- a/packages/dai-plugin-mcd/src/schemas/cat.js +++ b/packages/dai-plugin-mcd/src/schemas/cat.js @@ -8,15 +8,17 @@ import { MAX_AUCTION_LOT_SIZE } from './constants'; +const validateCollateralTypeName = name => + !name && 'Invalid collateral type name'; + export const catIlks = { generate: collateralTypeName => ({ id: `MCD_CAT.ilks(${collateralTypeName})`, contractName: 'MCD_CAT', call: ['ilks(bytes32)(address,uint256,uint256)', toHex(collateralTypeName)] }), - validateParams: collateralTypeName => { - if (collateralTypeName === null) - throw new Error('Invalid collateral type name'); + validate: { + args: validateCollateralTypeName }, returns: [ [LIQUIDATOR_ADDRESS], diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js index 2411dc1a1..8e4eac845 100644 --- a/packages/dai-plugin-mcd/src/schemas/cdpManager.js +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -1,4 +1,4 @@ -import { bytesToString, nullIfEmpty } from '../utils'; +import { bytesToString } from '../utils'; import BigNumber from 'bignumber.js'; import { @@ -17,22 +17,23 @@ export const cdpManagerUrns = { returns: [VAULT_ADDRESS] }; +const validateVaultId = id => + !/^\d+$/.test(id) && 'Invalid vault id: must be a positive integer'; + +const validateVaultTypeResult = vaultType => + !vaultType && 'Vault does not exist'; + export const cdpManagerIlks = { generate: id => ({ id: `CDP_MANAGER.ilks(${id})`, contractName: 'CDP_MANAGER', call: ['ilks(uint256)(bytes32)', parseInt(id)] }), - validateParams(id) { - if (!/^\d+$/.test(id)) - throw new Error('Invalid vault id: must be a positive integer'); - }, - validateReturns: { - [VAULT_TYPE](vaultType) { - if (vaultType === null) throw new Error('Vault does not exist'); - } + validate: { + args: validateVaultId, + [VAULT_TYPE]: validateVaultTypeResult }, - returns: [[VAULT_TYPE, v => nullIfEmpty(bytesToString(v))]] + returns: [[VAULT_TYPE, bytesToString]] }; export const cdpManagerCdpi = { diff --git a/packages/dai-plugin-mcd/src/schemas/spot.js b/packages/dai-plugin-mcd/src/schemas/spot.js index 4c17faff5..324627bcd 100644 --- a/packages/dai-plugin-mcd/src/schemas/spot.js +++ b/packages/dai-plugin-mcd/src/schemas/spot.js @@ -8,6 +8,17 @@ import { RATIO_DAI_USD } from './constants'; +const validateCollateralTypeName = name => + !name && 'Invalid collateral type name'; + +const validatePriceFeedAddressResult = (result, [name]) => + result === '0x0000000000000000000000000000000000000000' && + `No collateral type with name ${name} found`; + +const validateLiquidationRatioResult = (result, [name]) => { + return !result && `No collateral type with name ${name} found`; +}; + export const spotIlks = { generate: collateralTypeName => ({ id: `MCD_SPOT.ilks(${collateralTypeName})`, @@ -15,12 +26,15 @@ export const spotIlks = { call: ['ilks(bytes32)(address,uint256)', toHex(collateralTypeName)], transforms: { [LIQUIDATION_RATIO]: liqRatio => - createCurrencyRatio(USD, MDAI)(fromRay(liqRatio)) + liqRatio.toString() !== '0' + ? createCurrencyRatio(USD, MDAI)(fromRay(liqRatio)) + : null } }), - validateParams: collateralTypeName => { - if (collateralTypeName === null) - throw new Error('Invalid collateral type name'); + validate: { + args: validateCollateralTypeName, + [PRICE_FEED_ADDRESS]: validatePriceFeedAddressResult, + [LIQUIDATION_RATIO]: validateLiquidationRatioResult }, returns: [[PRICE_FEED_ADDRESS], [LIQUIDATION_RATIO]] }; diff --git a/packages/dai-plugin-mcd/test/schemas/spot.spec.js b/packages/dai-plugin-mcd/test/schemas/spot.spec.js index a701c35c6..f38e0b367 100644 --- a/packages/dai-plugin-mcd/test/schemas/spot.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/spot.spec.js @@ -39,6 +39,13 @@ test(PRICE_FEED_ADDRESS, async () => { expect(ethAPriceFeedAddress.toLowerCase()).toEqual(PIP_ETH); expect(batAPriceFeedAddress.toLowerCase()).toEqual(PIP_BAT); + + await expect(maker.latest(PRICE_FEED_ADDRESS, 'FOO')).rejects.toThrow( + /no collateral type/i + ); + expect(() => { + maker.latest(PRICE_FEED_ADDRESS, ''); + }).toThrow(/invalid collateral/i); }); test(LIQUIDATION_RATIO, async () => { @@ -50,6 +57,13 @@ test(LIQUIDATION_RATIO, async () => { expect(ethALiquidationRatio.toNumber()).toEqual(1.5); expect(batALiquidationRatio.toNumber()).toEqual(2.0); + + await expect(maker.latest(LIQUIDATION_RATIO, 'FOO')).rejects.toThrow( + /no collateral type/i + ); + expect(() => { + maker.latest(LIQUIDATION_RATIO, ''); + }).toThrow(/invalid collateral/i); }); test(RATIO_DAI_USD, async () => { diff --git a/packages/dai/.prettierrc b/packages/dai/.prettierrc index 49bfe6bfa..8b558c59d 100644 --- a/packages/dai/.prettierrc +++ b/packages/dai/.prettierrc @@ -3,7 +3,8 @@ "overrides": [ { "files": [ - "MulticallService.*" + "MulticallService.*", + "schemas.js" ], "options": { "printWidth": 100 diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index 64903218d..b368ed3d1 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -15,9 +15,11 @@ export default class MulticallService extends PublicService { this._schemas = []; this._schemaByObservableKey = {}; + this._schemaInstances = {}; this._subjects = {}; this._observables = {}; this._watcherUpdates = null; + this._schemaSubscribers = {}; this._totalSchemaSubscribers = 0; this._totalActiveSchemas = 0; this._multicallResultCache = {}; @@ -28,7 +30,7 @@ export default class MulticallService extends PublicService { initialize(settings = {}) { this._addresses = settings.addresses || this.get('smartContract').getContractAddresses(); this._removeSchemaDelay = settings.removeSchemaDelay || 1000; - this._debounceTime = settings.debounceTime || 100; + this._debounceTime = settings.debounceTime || 1; this._computedThrottleTime = settings.computedThrottleTime || 1; } @@ -99,6 +101,9 @@ export default class MulticallService extends PublicService { } schemaByObservableKey(key) { + if (!key) throw new Error('Invalid observable key'); + if (!this._schemaByObservableKey[key]) + throw new Error(`No registered schema definition found with observable key: ${key}`); return this._schemaByObservableKey[key]; } @@ -126,17 +131,17 @@ export default class MulticallService extends PublicService { return this._totalSchemaSubscribers; } + // Register schema definitions registerSchemas(schemas) { if (typeof schemas !== 'object') throw new Error('Schemas must be object or array'); // If schemas is key/val object use key as schema key and convert to array object - if (!Array.isArray(schemas)) - schemas = Object.keys(schemas).map(key => ({ key, ...schemas[key] })); + if (!Array.isArray(schemas)) schemas = Object.keys(schemas).map(key => ({ key, ...schemas[key] })); // prettier-ignore // Clone if array else schemas = schemas.map(item => ({ ...item })); schemas.forEach(schema => { - schema.watching = {}; + if (!schema.key) throw new Error('Schema definitions must have a unique key'); // Automatically use schema key as return key if no return keys specified if (!schema.return && !schema.returns) schema.returns = [schema.key]; if (schema.return && schema.returns) throw new Error('Ambiguous return definitions in schema: found both return and returns property'); // prettier-ignore @@ -159,48 +164,41 @@ export default class MulticallService extends PublicService { } watch(key, ...args) { - if (!key) throw new Error('Invalid observable key'); - - const schema = this.schemaByObservableKey(key); - if (!schema) throw new Error(`No registered schema found for observable key: ${key}`); - - // Validate schema params - if (schema.validateParams) schema.validateParams(...args); - - // Generate schema - let generatedSchema = schema.generate(...args); - - const path = [ - generatedSchema.demarcate ? this.get('web3').currentAddress() : undefined, - ...args - ] - .filter(x => x) - .join('.'); - const fullPath = `${key}${path ? '.' : ''}${path}`; + // Find schema definition associated with this observable key + const schemaDefinition = this.schemaByObservableKey(key); + const expectedArgs = schemaDefinition.generate.length; + if (args.length < expectedArgs) + throw new Error(`Observable ${key} expects at least ${expectedArgs} argument(s)`); + + // Validate arguments using schema args validator + if (schemaDefinition?.validate?.args) { + const validate = schemaDefinition.validate.args(...args); + if (validate) throw new Error(validate); + } - log2(`watch() called for ${generatedSchema.computed ? 'computed ' : 'base '}observable: ${fullPath}`); // prettier-ignore + // Create or get existing schema instance for this instance path (schema definition + args) + const schemaInstance = this._createSchemaInstance(schemaDefinition, ...args); + const obsPath = `${key}${args.length > 0 ? '.' : ''}${args.join('.')}`; + const { computed } = schemaInstance; + log2(`watch() called for ${computed ? 'computed ' : 'base '}observable: ${obsPath}`); - // Check if an observable already exists for this path (key + args) - const existingObservable = get(this._observables, fullPath); + // Return existing observable if one already exists for this observer path (key + args) + const existingObservable = get(this._observables, obsPath); if (existingObservable) { - const isComputed = !get(this._subjects, fullPath, subject); - log2(`Returning existing ${isComputed ? 'computed ' : 'base '}observable: ${fullPath}`); + log2(`Returning existing ${computed ? 'computed ' : 'base '}observable: ${obsPath}`); return existingObservable; } - if (args.length < generatedSchema.length) - throw new Error(`Observable ${key} expects at least ${generatedSchema.length} argument${generatedSchema.length > 1 ? 's' : ''}`); // prettier-ignore - // Handle computed observable - if (generatedSchema.computed) { + if (computed) { // Handle dynamically generated dependencies const dependencies = - typeof generatedSchema.dependencies === 'function' - ? generatedSchema.dependencies({ + typeof schemaInstance.dependencies === 'function' + ? schemaInstance.dependencies({ watch: this.watch.bind(this), get: this.get.bind(this) }) - : generatedSchema.dependencies; + : schemaInstance.dependencies; const recurseDependencyTree = trie => { let key = trie.shift(); @@ -242,30 +240,29 @@ export default class MulticallService extends PublicService { const observerLatest = combineLatest(dependencySubs).pipe( throttleTime(this._computedThrottleTime), - map(result => generatedSchema.computed(...result)) + map(result => computed(...result)) ); - // Create computed observable + // Create and return computed observable const observable = Observable.create(observer => { const sub = observerLatest.subscribe(observer); return () => { sub.unsubscribe(); }; }); - log2(`Created new computed observable: ${fullPath}`); - set(this._observables, fullPath, observable); + log2(`Created new computed observable: ${obsPath}`); + set(this._observables, obsPath, observable); return observable; } // This is a base observable + const { id, path } = schemaInstance; + if (this._schemaSubscribers[path] === undefined) this._schemaSubscribers[path] = 0; const subject = new ReplaySubject(1); - set(this._subjects, fullPath, subject); - // Emit initial value if cached result from multicall exists - if ( - this._multicallResultCache[fullPath] !== undefined && - this._validateResult(subject, fullPath, this._multicallResultCache[fullPath]) - ) - subject.next(this._multicallResultCache[fullPath]); + set(this._subjects, obsPath, subject); + // Handle initial value if cached result from multicall exists + if (this._multicallResultCache[obsPath] !== undefined) + this._handleResult(subject, obsPath, this._multicallResultCache[obsPath]); // Create base observable const observable = Observable.create(observer => { @@ -273,23 +270,24 @@ export default class MulticallService extends PublicService { // Subscribe to watcher updates and emit them to subjects if (!this._watcherUpdates) this._subscribeToWatcherUpdates(); // If first subscriber to this schema add it to multicall - if (schema.watching[path] === undefined) schema.watching[path] = 0; - if (++schema.watching[path] === 1) this._addSchemaToMulticall(schema, generatedSchema, path); + if (++this._schemaSubscribers[path] === 1) this._addSchemaToMulticall(schemaInstance); // Subscribe this observer to the subject for this base observable const sub = subject.subscribe(observer); - log2(`Observer subscribed to ${generatedSchema.id} (${schema.watching[path]} subscribers)`); + log2(`Observer subscribed to ${id} (${this._schemaSubscribers[path]} subscribers)`); // Return the function to call when this observer unsubscribes return () => { + this._totalSchemaSubscribers--; // If last unsubscriber from this schema remove it from multicall - if (--schema.watching[path] === 0) this._removeSchemaFromMulticall(generatedSchema); + if (--this._schemaSubscribers[path] === 0) + this._removeSchemaFromMulticall(schemaInstance.id); // Unsubscribe this observer from the subject for this base observable sub.unsubscribe(); - log2(`Observer unsubscribed from ${generatedSchema.id} (${schema.watching[path]} subscribers)`); // prettier-ignore + log2(`Observer unsubscribed from ${id} (${this._schemaSubscribers[path]} subscribers)`); // prettier-ignore }; }); - log2(`Created new base observable: ${fullPath}`); - set(this._observables, fullPath, observable); + log2(`Created new base observable: ${obsPath}`); + set(this._observables, obsPath, observable); return observable; } @@ -302,34 +300,59 @@ export default class MulticallService extends PublicService { .toPromise(); } - _addSchemaToMulticall(schema, generatedSchema, path) { - let { id, target, contractName, call, returns, transforms = {} } = generatedSchema; - // If this schema is already added but pending removal + _createSchemaInstance(schemaDefinition, ...args) { + const path = args.join('.'); + const instancePath = `${schemaDefinition.key}${path ? '.' : ''}${path}`; + + // Return existing schema if found for this instance path (schema key + args) + if (this._schemaInstances[instancePath]) return this._schemaInstances[instancePath]; + + // Generate schema instance + const schemaInstance = schemaDefinition.generate(...args); + this._schemaInstances[instancePath] = schemaInstance; + schemaInstance.args = [...args]; + + // Auto generate some fields if this is a base schema + if (!schemaInstance.computed) { + const { returns, transforms = {} } = schemaInstance; + schemaInstance.path = instancePath; + // Auto generate return keys for schema instance if not provided by generate() + if (!returns) { + schemaInstance.returns = schemaDefinition.returns.map(ret => { + const key = ret[0]; + const fullPath = `${key}${path ? '.' : ''}${path}`; + return transforms[key] + ? [fullPath, transforms[key]] // Use transform mapping in generated schema instance if available + : ret.length == 2 + ? [fullPath, ret[1]] + : [fullPath]; + }); + } + // Resolve target contract address if contract string is provided + const { target, contractName } = schemaInstance; + if (!target && !contractName) throw new Error('Schema must specify target or contractName'); + if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore + schemaInstance.target = target || this._addresses[contractName]; + } + + return schemaInstance; + } + + _addSchemaToMulticall(schemaInstance) { + const { id, target, call, returns } = schemaInstance; + // If schema already added but pending removal then cancel pending removal if (this._removeSchemaTimers[id]) { log2(`Cancelled pending schema removal: ${id}`); clearTimeout(this._removeSchemaTimers[id]); delete this._removeSchemaTimers[id]; return; } - // Automatically generate return keys if not explicitly specified in generated schema - if (!returns) - returns = schema.returns.map(ret => { - const key = ret[0]; - const fullPath = `${key}${path ? '.' : ''}${path}`; - return transforms[key] - ? [fullPath, transforms[key]] // Use transform mapping in generated schema if available - : ret.length == 2 - ? [fullPath, ret[1]] - : [fullPath]; - }); - if (!target && !contractName) throw new Error('Schema must specify target or contractName'); - if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore this._totalActiveSchemas++; this._watcher.tap(calls => [ ...calls, { id, - target: target || this._addresses[contractName], + target, call, returns } @@ -343,9 +366,9 @@ export default class MulticallService extends PublicService { if (this._removeSchemaTimers[id] !== undefined) delete this._removeSchemaTimers[id]; log2(`Schema removed from multicall: ${id}`); this._watcher.tap(schemas => schemas.filter(({ id: id_ }) => id_ !== id)); - if (--this._totalActiveSchemas === 0) log2('No remaining active schemas'); - // If no schemas are being watched, unsubscribe from watcher updates - if (--this._totalSchemaSubscribers === 0) { + // If there are no active schemas unsubscribe from watcher updates + if (--this._totalActiveSchemas === 0) { + log2('No remaining active schemas'); log2('Unsubscribed from watcher updates'); this._watcherUpdates.unsub(); this._watcherUpdates = null; @@ -355,13 +378,11 @@ export default class MulticallService extends PublicService { } } - _removeSchemaFromMulticall({ id, immediate = false }) { - if (immediate) this._removeSchemaImmediately(id); - else - this._removeSchemaTimers[id] = setTimeout( - () => this._removeSchemaImmediately(id), - this._removeSchemaDelay - ); + _removeSchemaFromMulticall(id) { + this._removeSchemaTimers[id] = setTimeout( + () => this._removeSchemaImmediately(id), + this._removeSchemaDelay + ); } _flushPendingSchemaRemovals() { @@ -375,18 +396,33 @@ export default class MulticallService extends PublicService { } } - _validateResult(subject, type, value) { - const [observableKey] = type.split('.'); - const schema = this._schemaByObservableKey[observableKey]; - if (!schema.validateReturns?.hasOwnProperty(observableKey)) return true; - const validator = schema.validateReturns[observableKey]; + _handleResult(subject, obsPath, value) { + const err = this._validateResult(subject, obsPath, value); + // Trigger error on observable or emit result value to observable + if (err) subject.error(err); + else subject.next(value); + } + + _validateResult(subject, obsPath, value) { + let [observableKey, ...args] = obsPath.split('.'); + const schemaDefinition = this._schemaByObservableKey[observableKey]; + const instancePath = `${schemaDefinition.key}${args.length > 0 ? '.' : ''}${args.join('.')}`; + const schemaInstance = this._schemaInstances[instancePath]; + // Pass validation if no validator found for this schema definition + if (!schemaDefinition.validate?.hasOwnProperty(observableKey)) return; try { - validator(value); - return true; + // Call validation func on schema definition for result value and pass schema instance args + // as 2nd param and also this context + const validate = schemaDefinition.validate[observableKey].call( + { args: schemaInstance.args }, + value, + schemaInstance.args + ); + if (validate) throw new Error(validate); + return; // Pass validation } catch (err) { - log2('Validation error for ' + type + ' result:', value); - subject.error(err); - return false; + log2('Validation error for ' + obsPath + ' result:', value); + return err; // Fail validation } } @@ -399,7 +435,7 @@ export default class MulticallService extends PublicService { ? `${update.value.toString()} (BigNumber)` : update.value; log2('Got watcher update for ' + update.type + ':', logValue); - if (this._validateResult(subject, update.type, update.value)) subject.next(update.value); + this._handleResult(subject, update.type, update.value); } else this._multicallResultCache[update.type] = update.value; }); } diff --git a/packages/dai/src/utils/conversion.js b/packages/dai/src/utils/conversion.js index 44715c1f1..d5b286f2c 100644 --- a/packages/dai/src/utils/conversion.js +++ b/packages/dai/src/utils/conversion.js @@ -1,5 +1,6 @@ import BigNumber from 'bignumber.js'; import { utils as ethersUtils } from 'ethers'; +import assert from 'assert'; export function numberToBytes32(num) { const bn = ethersUtils.bigNumberify(num); @@ -19,6 +20,17 @@ export function stringToBytes32(text, pad = true) { return ethersUtils.hexlify(data); } +export function stringToBytes(str) { + assert(!!str, 'argument is falsy'); + assert(typeof str === 'string', 'argument is not a string'); + return '0x' + Buffer.from(str).toString('hex'); +} + +export function bytesToString(hex) { + return Buffer.from(hex.replace(/^0x/, ''), 'hex') + .toString() + .replace(/\x00/g, ''); // eslint-disable-line no-control-regex +} export function padRight(string, chars, sign) { return string + new Array(chars - string.length + 1).join(sign ? sign : '0'); } diff --git a/packages/dai/test/eth/MulticallService.spec.js b/packages/dai/test/eth/MulticallService.spec.js index f0930ddf9..27c4bd5c8 100644 --- a/packages/dai/test/eth/MulticallService.spec.js +++ b/packages/dai/test/eth/MulticallService.spec.js @@ -8,13 +8,17 @@ import schemas, { CDP_COLLATERAL, CDP_DEBT, CDP_COLLATERAL_VALUE, - LAST_CREATED_CDP_COLLATERAL_VALUE + LAST_CREATED_CDP_COLLATERAL_VALUE, + CDP_OWNER } from '../helpers/schemas'; -let maker, multicall, watcher, address, cdpId; +let maker, multicall, watcher, address, cdpId1, cdpId2; beforeAll(async () => { maker = await Maker.create('test', { + web3: { + pollingInterval: 100 + }, multicall: { debounceTime: 1 }, @@ -25,9 +29,11 @@ beforeAll(async () => { multicall = maker.service('multicall'); watcher = multicall.createWatcher(); multicall.registerSchemas(schemas); - // Lock 5 ETH and draw 100 DAI + const proxyAddress = await maker.service('proxy').ensureProxy(); - ({ id: cdpId } = {}) = await maker.service('cdp').openProxyCdpLockEthAndDrawDai(5, 100, proxyAddress); // prettier-ignore + ({ id: cdpId1 } = {}) = await maker.service('cdp').openProxyCdpLockEthAndDrawDai(1, 100, proxyAddress); // prettier-ignore + ({ id: cdpId2 } = {}) = await maker.service('cdp').openProxyCdpLockEthAndDrawDai(5, 100, proxyAddress); // prettier-ignore + await maker.service('cdp').openProxyCdpLockEthAndDrawDai(1, 50, proxyAddress); }); beforeEach(() => { @@ -63,42 +69,70 @@ test('get eth balance via multicall', async () => { }); test('base observable', async () => { - const expectedTotalCdpDebt = BigNumber(100); + const expectedTotalCdpDebt = BigNumber(250); const totalCdpDebt = await maker.latest(TOTAL_CDP_DEBT); expect(totalCdpDebt).toEqual(expectedTotalCdpDebt); }); test('base observable with arg', async () => { - const expectedCdpCollateral = BigNumber(5); - const cdpCollateral = await maker.latest(CDP_COLLATERAL, cdpId); + const expectedCdpCollateral = BigNumber(1); + const cdpCollateral = await maker.latest(CDP_COLLATERAL, cdpId1); expect(cdpCollateral).toEqual(expectedCdpCollateral); }); test('multiple base observables', async () => { - const expectedCdpCollateral = BigNumber(5); + const expectedCdpCollateral = BigNumber(1); const expectedCdpDebt = BigNumber(100); const expectedEthPrice = BigNumber(400); - const cdpCollateral = await maker.latest(CDP_COLLATERAL, cdpId); - const cdpDebt = await maker.latest(CDP_DEBT, cdpId); + const cdpCollateral = await maker.latest(CDP_COLLATERAL, cdpId1); + const cdpDebt = await maker.latest(CDP_DEBT, cdpId1); const ethPrice = await maker.latest(ETH_PRICE); expect(cdpCollateral).toEqual(expectedCdpCollateral); expect(cdpDebt).toEqual(expectedCdpDebt); expect(ethPrice).toEqual(expectedEthPrice); - expect(multicall.totalActiveSchemas).toEqual(3); + expect(multicall.totalActiveSchemas).toEqual(2); }); test('computed observable', async () => { - const expectedCdpCollateralValue = BigNumber(2000); - const cdpCollateralValue = await maker.latest(CDP_COLLATERAL_VALUE, cdpId); + const expectedCdpCollateralValue = BigNumber(400); + const cdpCollateralValue = await maker.latest(CDP_COLLATERAL_VALUE, cdpId1); expect(cdpCollateralValue).toEqual(expectedCdpCollateralValue); expect(multicall.totalActiveSchemas).toEqual(2); }); test('computed observable with nested dependencies', async () => { - const expectedLastCreatedCdpDebt = BigNumber(2000); + const expectedLastCreatedCdpDebt = BigNumber(400); const lastCreatedCdpDebt = await maker.latest(LAST_CREATED_CDP_COLLATERAL_VALUE); expect(lastCreatedCdpDebt).toEqual(expectedLastCreatedCdpDebt); expect(multicall.totalActiveSchemas).toEqual(3); }); + +test('observable throws args validation error', () => { + expect(() => { + maker.latest(CDP_COLLATERAL, -9000); + }).toThrow(/invalid cdp id/i); +}); + +test('observable throws invalid key error', () => { + expect(() => { + maker.latest(null); + }).toThrow(/invalid observable key/i); +}); + +test('observable throws insufficient args error', () => { + expect(() => { + maker.latest(CDP_OWNER); + }).toThrow(/expects.*argument/i); +}); + +test('observable throws result validation error', async () => { + const cdpCollateral = maker.latest(CDP_COLLATERAL, cdpId2); + await expect(cdpCollateral).rejects.toThrow(/Φ/); +}); + +test('observable throws result validation error 2', async () => { + const cdpOwner = maker.latest(CDP_OWNER, 9000); + await expect(cdpOwner).rejects.toThrow(); +}); diff --git a/packages/dai/test/helpers/schemas.js b/packages/dai/test/helpers/schemas.js index 30b7c1360..8dcf6539b 100644 --- a/packages/dai/test/helpers/schemas.js +++ b/packages/dai/test/helpers/schemas.js @@ -1,12 +1,28 @@ -import { fromWei, numberToBytes32 } from '../../src/utils/conversion'; +import { fromWei, numberToBytes32, bytesToString } from '../../src/utils/conversion'; +import BigNumber from 'bignumber.js'; export const TOTAL_CDP_DEBT = 'totalCdpDebt'; -export const ETH_PRICE = 'ethPrice'; +export const CDP_COUNT = 'cdpCount'; +export const CDP_OWNER = 'cdpOwner'; export const CDP_COLLATERAL = 'cdpCollateral'; export const CDP_DEBT = 'cdpDebt'; -export const CDP_COUNT = 'cdpCount'; +export const CDP_IRE = 'cdpIre'; +export const DEBT_CEILING = 'debtCeiling'; +export const ETH_PRICE = 'ethPrice'; export const CDP_COLLATERAL_VALUE = 'cdpCollateralValue'; -export const LAST_CREATED_CDP_COLLATERAL_VALUE = 'lastCreatedCdpCollateralValue'; // prettier-ignore +export const LAST_CREATED_CDP_COLLATERAL_VALUE = 'lastCreatedCdpCollateralValue'; + +const Φ = BigNumber( + '1.6180339887498948482045868343656381177203091798057628621354486227052604628189' +); + +const validateArgsCdpId = id => !/^\d+$/.test(id) && 'Invalid cdp id: must be a positive integer'; + +const validateResultCdpCollateral = collateral => + collateral.gt(Φ) && 'Collateral amounts greater than Φ throw this error'; + +const validateResultCdpOwner = (owner, [id]) => + !owner && `Not found: cdp with id ${id} does not exist`; export const tubRum = { generate: () => ({ @@ -26,22 +42,32 @@ export const tubCupi = { returns: [[CDP_COUNT, parseInt]] }; -export const tubInk = { +export const tubCups = { generate: id => ({ - id: `SAI_TUB.ink(${id})`, + id: `SAI_TUB.cups(${id})`, contractName: 'SAI_TUB', - call: ['ink(bytes32)(uint256)', numberToBytes32(id)] + call: ['cups(bytes32)(address,uint256,uint256,uint256)', numberToBytes32(id)] }), - returns: [[CDP_COLLATERAL, fromWei]] + validate: { + args: validateArgsCdpId, + [CDP_OWNER]: validateResultCdpOwner, + [CDP_COLLATERAL]: validateResultCdpCollateral + }, + returns: [ + [CDP_OWNER, bytesToString], + [CDP_COLLATERAL, fromWei], + [CDP_DEBT, fromWei], + [CDP_IRE, fromWei] + ] }; -export const tubTab = { - generate: id => ({ - id: `SAI_TUB.tab(${id})`, +export const tubCap = { + generate: () => ({ + id: 'SAI_TUB.cap', contractName: 'SAI_TUB', - call: ['tab(bytes32)(uint256)', numberToBytes32(id)] + call: ['cap()(uint256)'] }), - returns: [[CDP_DEBT, fromWei]] + returns: [[DEBT_CEILING, fromWei]] }; export const pipRead = { @@ -69,9 +95,9 @@ export const lastCreatedCdpCollateralValue = { export default { tubRum, - tubInk, - tubTab, + tubCups, tubCupi, + tubCap, pipRead, cdpCollateralValue, lastCreatedCdpCollateralValue From eb74ee9149abed5a85d6ecef0f778687d2d00e83 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 10 Feb 2020 14:44:48 +0800 Subject: [PATCH 114/117] Rename contractName schema field to contract --- packages/dai-plugin-mcd/src/schemas/cat.js | 2 +- packages/dai-plugin-mcd/src/schemas/cdpManager.js | 8 ++++---- packages/dai-plugin-mcd/src/schemas/jug.js | 4 ++-- packages/dai-plugin-mcd/src/schemas/pot.js | 10 +++++----- packages/dai-plugin-mcd/src/schemas/proxyRegistry.js | 2 +- packages/dai-plugin-mcd/src/schemas/spot.js | 4 ++-- packages/dai-plugin-mcd/src/schemas/token.js | 4 ++-- packages/dai-plugin-mcd/src/schemas/vat.js | 10 +++++----- packages/dai/src/eth/MulticallService.js | 8 ++++---- packages/dai/test/helpers/schemas.js | 10 +++++----- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/dai-plugin-mcd/src/schemas/cat.js b/packages/dai-plugin-mcd/src/schemas/cat.js index 5aea575f0..99eab85b9 100644 --- a/packages/dai-plugin-mcd/src/schemas/cat.js +++ b/packages/dai-plugin-mcd/src/schemas/cat.js @@ -14,7 +14,7 @@ const validateCollateralTypeName = name => export const catIlks = { generate: collateralTypeName => ({ id: `MCD_CAT.ilks(${collateralTypeName})`, - contractName: 'MCD_CAT', + contract: 'MCD_CAT', call: ['ilks(bytes32)(address,uint256,uint256)', toHex(collateralTypeName)] }), validate: { diff --git a/packages/dai-plugin-mcd/src/schemas/cdpManager.js b/packages/dai-plugin-mcd/src/schemas/cdpManager.js index 8e4eac845..e12a312f4 100644 --- a/packages/dai-plugin-mcd/src/schemas/cdpManager.js +++ b/packages/dai-plugin-mcd/src/schemas/cdpManager.js @@ -11,7 +11,7 @@ import { export const cdpManagerUrns = { generate: id => ({ id: `CDP_MANAGER.urns(${id})`, - contractName: 'CDP_MANAGER', + contract: 'CDP_MANAGER', call: ['urns(uint256)(address)', parseInt(id)] }), returns: [VAULT_ADDRESS] @@ -26,7 +26,7 @@ const validateVaultTypeResult = vaultType => export const cdpManagerIlks = { generate: id => ({ id: `CDP_MANAGER.ilks(${id})`, - contractName: 'CDP_MANAGER', + contract: 'CDP_MANAGER', call: ['ilks(uint256)(bytes32)', parseInt(id)] }), validate: { @@ -39,7 +39,7 @@ export const cdpManagerIlks = { export const cdpManagerCdpi = { generate: () => ({ id: 'CDP_MANAGER.cdpi', - contractName: 'CDP_MANAGER', + contract: 'CDP_MANAGER', call: ['cdpi()(uint256)'] }), returns: [[VAULTS_CREATED, v => BigNumber(v)]] @@ -48,7 +48,7 @@ export const cdpManagerCdpi = { export const cdpManagerOwner = { generate: id => ({ id: `CDP_MANAGER.owner(${id})`, - contractName: 'CDP_MANAGER', + contract: 'CDP_MANAGER', call: ['owns(uint256)(address)', id] }), returns: [[VAULT_OWNER]] diff --git a/packages/dai-plugin-mcd/src/schemas/jug.js b/packages/dai-plugin-mcd/src/schemas/jug.js index 70a7bff5c..a725b1bfd 100644 --- a/packages/dai-plugin-mcd/src/schemas/jug.js +++ b/packages/dai-plugin-mcd/src/schemas/jug.js @@ -13,7 +13,7 @@ const secondsPerYear = 60 * 60 * 24 * 365; export const jugIlks = { generate: collateralTypeName => ({ id: `MCD_JUG.ilks(${collateralTypeName})`, - contractName: 'MCD_JUG', + contract: 'MCD_JUG', call: ['ilks(bytes32)(uint256,uint48)', toHex(collateralTypeName)] }), returns: [ @@ -32,7 +32,7 @@ export const jugIlks = { export const jugBase = { generate: () => ({ id: 'MCD_JUG.base', - contractName: 'MCD_JUG', + contract: 'MCD_JUG', call: ['base()(uint256)'] }), returns: [[BASE_COLLATERAL_FEE, v => BigNumber(v)]] diff --git a/packages/dai-plugin-mcd/src/schemas/pot.js b/packages/dai-plugin-mcd/src/schemas/pot.js index 78c012b7f..4bf607c3e 100644 --- a/packages/dai-plugin-mcd/src/schemas/pot.js +++ b/packages/dai-plugin-mcd/src/schemas/pot.js @@ -11,7 +11,7 @@ import { export const potPie = { generate: () => ({ id: 'MCD_POT.Pie', - contractName: 'MCD_POT', + contract: 'MCD_POT', call: ['Pie()(uint256)'] }), returns: [[TOTAL_SAVINGS_DAI, fromWei]] @@ -20,7 +20,7 @@ export const potPie = { export const potpie = { generate: proxyAddress => ({ id: `MCD_POT.pie(${proxyAddress})`, - contractName: 'MCD_POT', + contract: 'MCD_POT', call: ['pie(address)(uint256)', proxyAddress] }), returns: [[SAVINGS_DAI, fromWei]] @@ -29,7 +29,7 @@ export const potpie = { export const potDsr = { generate: () => ({ id: 'MCD_POT.dsr', - contractName: 'MCD_POT', + contract: 'MCD_POT', call: ['dsr()(uint256)'] }), returns: [[DAI_SAVINGS_RATE, fromRay]] @@ -37,7 +37,7 @@ export const potDsr = { export const potChi = { generate: () => ({ id: 'MCD_POT.chi', - contractName: 'MCD_POT', + contract: 'MCD_POT', call: ['chi()(uint256)'] }), returns: [[SAVINGS_RATE_ACCUMULATOR, fromRay]] @@ -46,7 +46,7 @@ export const potChi = { export const potRho = { generate: () => ({ id: 'MCD_POT.rho', - contractName: 'MCD_POT', + contract: 'MCD_POT', call: ['rho()(uint256)'] }), returns: [ diff --git a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js index 7470efd19..7bb21fcde 100644 --- a/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js +++ b/packages/dai-plugin-mcd/src/schemas/proxyRegistry.js @@ -3,7 +3,7 @@ import { PROXY_ADDRESS, PROXY_OWNER } from './constants'; export const proxyRegistryProxies = { generate: address => ({ id: `PROXY_REGISTRY.proxies(${address})`, - contractName: 'PROXY_REGISTRY', + contract: 'PROXY_REGISTRY', call: ['proxies(address)(address)', address] }), returns: [[PROXY_ADDRESS]] diff --git a/packages/dai-plugin-mcd/src/schemas/spot.js b/packages/dai-plugin-mcd/src/schemas/spot.js index 324627bcd..36f2fc966 100644 --- a/packages/dai-plugin-mcd/src/schemas/spot.js +++ b/packages/dai-plugin-mcd/src/schemas/spot.js @@ -22,7 +22,7 @@ const validateLiquidationRatioResult = (result, [name]) => { export const spotIlks = { generate: collateralTypeName => ({ id: `MCD_SPOT.ilks(${collateralTypeName})`, - contractName: 'MCD_SPOT', + contract: 'MCD_SPOT', call: ['ilks(bytes32)(address,uint256)', toHex(collateralTypeName)], transforms: { [LIQUIDATION_RATIO]: liqRatio => @@ -42,7 +42,7 @@ export const spotIlks = { export const spotPar = { generate: () => ({ id: 'MCD_SPOT.par()', - contractName: 'MCD_SPOT', + contract: 'MCD_SPOT', call: ['par()(uint256)'] }), returns: [[RATIO_DAI_USD, v => createCurrencyRatio(MDAI, USD)(fromRay(v))]] diff --git a/packages/dai-plugin-mcd/src/schemas/token.js b/packages/dai-plugin-mcd/src/schemas/token.js index 681ac420f..8251b9882 100644 --- a/packages/dai-plugin-mcd/src/schemas/token.js +++ b/packages/dai-plugin-mcd/src/schemas/token.js @@ -20,7 +20,7 @@ export const tokenBalance = { return { id: `balance.${symbol}.${address}`, - contractName: symbol === 'ETH' ? 'MULTICALL' : contract, + contract: symbol === 'ETH' ? 'MULTICALL' : contract, call: [ symbol === 'ETH' ? 'getEthBalance(address)(uint256)' @@ -50,7 +50,7 @@ export const tokenAllowance = { return { id: `allowance.${symbol}.${address}`, - contractName: contract, + contract: contract, call: ['allowance(address,address)(uint256)', address, proxyAddress] }; }, diff --git a/packages/dai-plugin-mcd/src/schemas/vat.js b/packages/dai-plugin-mcd/src/schemas/vat.js index 1e5798af1..1c888679f 100644 --- a/packages/dai-plugin-mcd/src/schemas/vat.js +++ b/packages/dai-plugin-mcd/src/schemas/vat.js @@ -18,7 +18,7 @@ import { export const vatIlks = { generate: ilkName => ({ id: `MCD_VAT.ilks(${ilkName})`, - contractName: 'MCD_VAT', + contract: 'MCD_VAT', call: [ 'ilks(bytes32)(uint256,uint256,uint256,uint256,uint256)', toHex(ilkName) @@ -36,7 +36,7 @@ export const vatIlks = { export const vatDebt = { generate: () => ({ id: 'MCD_VAT.debt()', - contractName: 'MCD_VAT', + contract: 'MCD_VAT', call: ['debt()(uint256)'] }), returns: [[TOTAL_DAI_SUPPLY, v => MDAI(v, 'rad')]] @@ -45,7 +45,7 @@ export const vatDebt = { export const vatUrns = { generate: (ilkName, urn) => ({ id: `MCD_Vat.urns(${ilkName},${urn})`, - contractName: 'MCD_VAT', + contract: 'MCD_VAT', call: ['urns(bytes32,address)(uint256,uint256)', toHex(ilkName), urn] }), returns: [[ENCUMBERED_COLLATERAL, fromWei], [ENCUMBERED_DEBT, fromWei]] @@ -54,7 +54,7 @@ export const vatUrns = { export const vatGem = { generate: (ilkName, urn) => ({ id: `MCD_Vat.gem(${ilkName},${urn})`, - contractName: 'MCD_VAT', + contract: 'MCD_VAT', call: ['gem(bytes32,address)(uint)', toHex(ilkName), urn] }), return: [UNLOCKED_COLLATERAL, fromWei] @@ -63,7 +63,7 @@ export const vatGem = { export const vatLine = { generate: () => ({ id: 'MCD_VAT.Line', - contractName: 'MCD_VAT', + contract: 'MCD_VAT', call: ['Line()(uint256)'] }), returns: [[GLOBAL_DEBT_CEILING, v => MDAI(v, 'rad')]] diff --git a/packages/dai/src/eth/MulticallService.js b/packages/dai/src/eth/MulticallService.js index b368ed3d1..9042201bb 100644 --- a/packages/dai/src/eth/MulticallService.js +++ b/packages/dai/src/eth/MulticallService.js @@ -329,10 +329,10 @@ export default class MulticallService extends PublicService { }); } // Resolve target contract address if contract string is provided - const { target, contractName } = schemaInstance; - if (!target && !contractName) throw new Error('Schema must specify target or contractName'); - if (!target && !this._addresses[contractName]) throw new Error(`Can't find contract address for ${contractName}`); // prettier-ignore - schemaInstance.target = target || this._addresses[contractName]; + const { target, contract } = schemaInstance; + if (!target && !contract) throw new Error('Schema must specify target address or contract'); + if (!target && !this._addresses[contract]) throw new Error(`Can't find contract address for ${contract}`); // prettier-ignore + schemaInstance.target = target || this._addresses[contract]; } return schemaInstance; diff --git a/packages/dai/test/helpers/schemas.js b/packages/dai/test/helpers/schemas.js index 8dcf6539b..868d9d5eb 100644 --- a/packages/dai/test/helpers/schemas.js +++ b/packages/dai/test/helpers/schemas.js @@ -27,7 +27,7 @@ const validateResultCdpOwner = (owner, [id]) => export const tubRum = { generate: () => ({ id: 'SAI_TUB.rum', - contractName: 'SAI_TUB', + contract: 'SAI_TUB', call: ['rum()(uint256)'] }), returns: [[TOTAL_CDP_DEBT, fromWei]] @@ -36,7 +36,7 @@ export const tubRum = { export const tubCupi = { generate: () => ({ id: 'SAI_TUB.cupi', - contractName: 'SAI_TUB', + contract: 'SAI_TUB', call: ['cupi()(uint256)'] }), returns: [[CDP_COUNT, parseInt]] @@ -45,7 +45,7 @@ export const tubCupi = { export const tubCups = { generate: id => ({ id: `SAI_TUB.cups(${id})`, - contractName: 'SAI_TUB', + contract: 'SAI_TUB', call: ['cups(bytes32)(address,uint256,uint256,uint256)', numberToBytes32(id)] }), validate: { @@ -64,7 +64,7 @@ export const tubCups = { export const tubCap = { generate: () => ({ id: 'SAI_TUB.cap', - contractName: 'SAI_TUB', + contract: 'SAI_TUB', call: ['cap()(uint256)'] }), returns: [[DEBT_CEILING, fromWei]] @@ -73,7 +73,7 @@ export const tubCap = { export const pipRead = { generate: () => ({ id: 'SAI_PIP.read', - contractName: 'SAI_PIP', + contract: 'SAI_PIP', call: ['read()(uint256)'] }), returns: [[ETH_PRICE, fromWei]] From 698f57b238c045165a5a52eeae370b55f4146f10 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 10 Feb 2020 16:40:45 +0800 Subject: [PATCH 115/117] Fix token balance schema test --- packages/dai-plugin-mcd/test/schemas/token.spec.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/dai-plugin-mcd/test/schemas/token.spec.js b/packages/dai-plugin-mcd/test/schemas/token.spec.js index 951285420..98672160f 100644 --- a/packages/dai-plugin-mcd/test/schemas/token.spec.js +++ b/packages/dai-plugin-mcd/test/schemas/token.spec.js @@ -2,7 +2,8 @@ import { mcdMaker } from '../helpers'; import { takeSnapshot, restoreSnapshot, - mineBlocks + mineBlocks, + TestAccountProvider } from '@makerdao/test-helpers'; import { ETH, BAT, MDAI, MWETH, ALLOWANCE_AMOUNT } from '../../src'; import BigNumber from 'bignumber.js'; @@ -16,7 +17,7 @@ import { import tokenSchemas from '../../src/schemas/token'; -let maker, snapshotData, address, proxyAddress; +let maker, snapshotData, address, address2, proxyAddress; const ETH_A_COLLATERAL_AMOUNT = ETH(1); const ETH_A_DEBT_AMOUNT = MDAI(1); @@ -38,6 +39,7 @@ beforeAll(async () => { maker.service('multicall').registerSchemas(tokenSchemas); maker.service('multicall').start(); address = maker.currentAddress(); + address2 = TestAccountProvider.nextAccount().address; proxyAddress = await maker.service('proxy').ensureProxy(); }); @@ -48,12 +50,12 @@ afterAll(async () => { test(TOKEN_BALANCE, async () => { expect.assertions(8); - const ethBalance = await maker.latest(TOKEN_BALANCE, address, 'ETH'); + const ethBalance = await maker.latest(TOKEN_BALANCE, address2, 'ETH'); const batBalance = await maker.latest(TOKEN_BALANCE, address, 'BAT'); expect(ethBalance.symbol).toEqual('ETH'); expect(batBalance.symbol).toEqual('BAT'); - expect(ethBalance.toBigNumber()).toEqual(BigNumber('94.69019922')); + expect(ethBalance.toBigNumber()).toEqual(BigNumber('100')); expect(batBalance.toBigNumber()).toEqual(BigNumber('1000')); const daiBalance = await maker.latest(TOKEN_BALANCE, address, 'DAI'); From 0326e4ac7345672e80920f3f94bca19100e83cc1 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 10 Feb 2020 16:57:15 +0800 Subject: [PATCH 116/117] dai-v0.24.0-rc.1 --- packages/dai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai/package.json b/packages/dai/package.json index df3ec6f80..e559658c6 100644 --- a/packages/dai/package.json +++ b/packages/dai/package.json @@ -1,6 +1,6 @@ { "name": "@makerdao/dai", - "version": "0.23.1", + "version": "0.24.0-rc.1", "contributors": [ "Wouter Kampmann ", "Sean Brennan ", From 682b3283b0b9a354d9e0bcf5dd171670c2264c94 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Mon, 10 Feb 2020 17:05:20 +0800 Subject: [PATCH 117/117] dai-plugin-mcd-v1.1.0-rc.1 --- packages/dai-plugin-mcd/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dai-plugin-mcd/package.json b/packages/dai-plugin-mcd/package.json index d76e8f0dc..25558dc28 100644 --- a/packages/dai-plugin-mcd/package.json +++ b/packages/dai-plugin-mcd/package.json @@ -1,7 +1,7 @@ { "name": "@makerdao/dai-plugin-mcd", "description": "Plugin to add Multi-Collateral Dai support to dai.js", - "version": "1.0.6", + "version": "1.1.0-rc.1", "license": "MIT", "main": "dist", "scripts": {