Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

Commit

Permalink
Fix merge conflicts with dev
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanbennett committed Nov 12, 2019
2 parents 2dc2715 + 311c76a commit 490eeb2
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 36 deletions.
240 changes: 240 additions & 0 deletions packages/dai-plugin-mcd/src/CdpManager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Currency } from '@makerdao/currency';
import { LocalService } from '@makerdao/services-core';
import ethAbi from 'web3-eth-abi';
import tracksTransactions, {
tracksTransactionsWithOptions
} from './utils/tracksTransactions';
Expand All @@ -9,6 +10,9 @@ import ManagedCdp from './ManagedCdp';
import { castAsCurrency, stringToBytes, bytesToString } from './utils';
import has from 'lodash/has';
import padStart from 'lodash/padStart';
import padEnd from 'lodash/padEnd';
import orderBy from 'lodash/orderBy';
import flatten from 'lodash/flatten';
import { MDAI, ETH, GNT } from './index';
const { CDP_MANAGER, CDP_TYPE, SYSTEM_DATA, QUERY_API } = ServiceRoles;
import BigNumber from 'bignumber.js';
Expand Down Expand Up @@ -98,6 +102,18 @@ export default class CdpManager extends LocalService {
return cdp;
}

@tracksTransactions
async reclaimCollateral(id, dink, { promise }) {
dink = castAsCurrency(dink, ETH);
return this.proxyActions.frob(
this._managerAddress,
this.getIdBytes(id),
dink.toFixed('wei'),
0,
{ dsProxy: true, promise }
);
}

// ilk is required if the currency type corresponds to more than one ilk; if
// it's omitted, it is inferred from lockAmount's currency type
@tracksTransactions
Expand Down Expand Up @@ -392,6 +408,230 @@ export default class CdpManager extends LocalService {
if (!this._instanceCache) this._instanceCache = {};
this._instanceCache[id] = instance;
}

async getEventHistory(managedCdp) {
const MCD_JOIN_DAI = this.get('smartContract').getContractAddress('MCD_JOIN_DAI');
const CDP_MANAGER = this.get('smartContract').getContractAddress('CDP_MANAGER');
const MCD_VAT = this.get('smartContract').getContractAddress('MCD_VAT');

const id = managedCdp.id;
const fromBlock = 1;
const web3 = this.get('smartContract').get('web3');

const formatAddress = v => '0x' + v.slice(26);
const funcSigTopic = v => padEnd(ethAbi.encodeFunctionSignature(v), 66, '0');
const fromWei = v => web3._web3.utils.fromWei(v.toString());
const fromHexWei = v => web3._web3.utils.fromWei(web3._web3.utils.toBN(v.toString()).toString()).toString();
const numberFromHex = v => web3._web3.utils.toBN(v.toString()).toNumber();

const EVENT_GIVE = funcSigTopic('give(uint256,address)');
const EVENT_DAI_ADAPTER_EXIT = funcSigTopic('exit(address,uint256)');
const EVENT_DAI_ADAPTER_JOIN = funcSigTopic('join(address,uint256)');
const EVENT_VAT_FROB = funcSigTopic('frob(bytes32,address,address,address,int256,int256)');
const EVENT_MANAGER_FROB = funcSigTopic('frob(uint256,int256,int256)');

const promisesBlockTimestamp = {};
const getBlockTimestamp = block => {
if (promisesBlockTimestamp.hasOwnProperty(block)) return promisesBlockTimestamp[block];
promisesBlockTimestamp[block] = web3.getBlock(block, false);
return promisesBlockTimestamp[block];
};

const decodeManagerFrob = data => {
const sig = ethAbi.encodeFunctionSignature('frob(uint256,int256,int256)').slice(2);
const decoded = ethAbi.decodeParameters([
'uint256', // id
'int256', // dink
'int256' // dart
], '0x' + data.replace(new RegExp('^.+?' + sig), ''));
return {
id: decoded[0].toString(),
dink: decoded[1],
dart: decoded[2] // can't be used directly because would need to be scaled up using vat.ilks[ilk].rate
};
};

const decodeVatFrob = data => {
const sig = ethAbi.encodeFunctionSignature('frob(bytes32,address,address,address,int256,int256)').slice(2);
const decoded = ethAbi.decodeParameters([
'bytes32', // ilk
'address', // u (urnHandler)
'address', // v (urnHandler)
'address', // w (urnHandler)
'int256', // dink
'int256' // dart
], '0x' + data.replace(new RegExp('^.+?' + sig), ''));
return {
ilk: bytesToString(decoded[0].toString()),
urnHandler: decoded[1].toString(),
dink: decoded[4].toString(),
dart: decoded[5].toString()
};
};

const urnHandler = (await this.getUrn(id)).toLowerCase();
const ilk = managedCdp.ilk;

const lookups = [
{
request: web3.getPastLogs({
address: CDP_MANAGER,
topics: [
EVENT_MANAGER_FROB,
null,
'0x' + padStart(id.toString(16), 64, '0')
],
fromBlock
}),
result: async r => {
// For now, use an approach where we find the oldest frob
// (until we can get an indexed cdp param added to event NewCdp)
const events1 = r.reduce(
(acc, { blockNumber: block }) => {
// Consider the earliest block to be the block the vault was opened
if (acc === null || acc.block > block) {
return {
type: 'OPEN',
order: 0,
block,
id,
ilk
};
}
return acc;
},
null
);
const events2 = r.reduce(
async (acc, { address, blockNumber: block, data, topics }) => {
let { dart } = decodeManagerFrob(data);
acc = await acc;
dart = new BigNumber(dart);

// Imprecise debt amount frobbed (not scaled by vat.ilks[ilk].rate)
if (dart.lt(0) || dart.gt(0)) {
// Lookup the dai join events on this block for this proxy address
const proxy = topics[1];
const joinDaiEvents = await web3.getPastLogs({
address: MCD_JOIN_DAI,
topics: [
dart.lt(0)
? EVENT_DAI_ADAPTER_JOIN
: EVENT_DAI_ADAPTER_EXIT,
proxy
],
fromBlock: block,
toBlock: block
});
acc.push(
...joinDaiEvents.map(
({ address, blockNumber: block, topics }) => ({
type: dart.lt(0) ? 'PAY_BACK' : 'GENERATE',
order: 2,
address,
block,
id,
ilk,
proxy: formatAddress(topics[1]),
recipient: formatAddress(topics[2]),
amount: fromHexWei(topics[3])
})
)
);
}
return acc;
},
[]
);
return flatten([events1, await events2]);
}
},
{
request: web3.getPastLogs({
address: MCD_VAT,
topics: [
EVENT_VAT_FROB,
null,
'0x' + padStart(urnHandler.slice(2), 64, '0')
],
fromBlock
}),
result: r => {
return r.reduce(
(acc, { address, blockNumber: block, data, topics }) => {
let { ilk, dink } = decodeVatFrob(data);
dink = new BigNumber(dink);
// Gem withdrawal
if (dink.lt(0)) {
acc.push({
type: 'WITHDRAW',
order: 3,
block,
id,
ilk,
adapter: address.toLowerCase(),
amount: Math.abs(fromWei(dink.toString())).toString(),
proxy: formatAddress(topics[1])
});
}
// Gem deposit
if (dink.gt(0)) {
acc.push({
type: 'DEPOSIT',
order: 1,
block,
id,
ilk,
adapter: address.toLowerCase(),
amount: fromWei(dink.toString()),
proxy: formatAddress(topics[1])
});
}
return acc;
},
[]
);
}
},
{
request: web3.getPastLogs({
address: CDP_MANAGER,
topics: [EVENT_GIVE, null, '0x' + padStart(id.toString(16), 64, '0')],
fromBlock
}),
result: r => r.map(({ address, blockNumber: block, data, topics }) => ({
type: 'GIVE',
block,
prevOwner: formatAddress(topics[1]),
id: numberFromHex(topics[2]),
newOwner: formatAddress(topics[3])
}))
}
];

const results = await Promise.all(lookups.map(l => l.request));
const events = orderBy(
await Promise.all(
flatten(
await Promise.all(
results.map(async (r, i) => await lookups[i].result(r))
)
)
.filter(r => r !== null)
.map(async e => {
e.address && e.address.toLowerCase();
e.timestamp = (await getBlockTimestamp(e.block)).timestamp;
return e;
})
),
['block', 'order'],
['desc', 'desc']
).map(e => {
delete e.order;
return e;
});
return events;
}
}

export function setMethod(isEth, isGnt, id) {
Expand Down
33 changes: 33 additions & 0 deletions packages/dai-plugin-mcd/test/CdpManager.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import findIndex from 'lodash/findIndex';
import { mcdMaker, setupCollateral } from './helpers';
import { setMethod, transferToBag } from '../src/CdpManager';
import { ServiceRoles } from '../src/constants';
Expand Down Expand Up @@ -197,3 +198,35 @@ describe('using a different account', () => {
}
});
});

test('get event history via web3', async () => {
await setupCollateral(maker, 'ETH-A', { price: 150, debtCeiling: 50 });
const cdp = await cdpMgr.openLockAndDraw('ETH-A', ETH(1), MDAI(3));
await cdp.freeCollateral(ETH(0.5));
await cdpMgr.give(cdp.id, '0x1000000000000000000000000000000000000000');
const events = await cdpMgr.getEventHistory(cdp);

const openEventIdx = findIndex(events, { type: 'OPEN', id: cdp.id });
const depositEventIdx = findIndex(events, { type: 'DEPOSIT', id: cdp.id });
const generateEventIdx = findIndex(events, { type: 'GENERATE', id: cdp.id });
const withdrawEventIdx = findIndex(events, { type: 'WITHDRAW', id: cdp.id });
const giveEventIdx = findIndex(events, { type: 'GIVE', id: cdp.id });

expect(openEventIdx).toBeGreaterThan(-1);
expect(events[openEventIdx].ilk).toEqual('ETH-A');

expect(depositEventIdx).toBeGreaterThan(-1);
expect(events[depositEventIdx].ilk).toEqual('ETH-A');
expect(events[depositEventIdx].amount).toEqual('1');

expect(generateEventIdx).toBeGreaterThan(-1);
expect(events[generateEventIdx].ilk).toEqual('ETH-A');
expect(events[generateEventIdx].amount).toEqual('3');

expect(withdrawEventIdx).toBeGreaterThan(-1);
expect(events[withdrawEventIdx].ilk).toEqual('ETH-A');
expect(events[withdrawEventIdx].amount).toEqual('0.5');

expect(giveEventIdx).toBeGreaterThan(-1);
expect(events[giveEventIdx].newOwner).toEqual('0x1000000000000000000000000000000000000000');
});
2 changes: 1 addition & 1 deletion packages/dai-plugin-migrations/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@makerdao/dai-plugin-migrations",
"description": "Plugin to add migrations to dai.js",
"version": "0.1.6",
"version": "0.1.7",
"license": "MIT",
"main": "dist",
"scripts": {
Expand Down
14 changes: 7 additions & 7 deletions packages/dai-plugin-migrations/src/migrations/SaiToDai.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import tracksTransactions from '@makerdao/dai/dist/src/utils/tracksTransactions';
import { SAI } from '..';

export default class SaiToDai {
Expand All @@ -11,7 +12,8 @@ export default class SaiToDai {
return this._sai.balance();
}

async execute(amount) {
@tracksTransactions
async execute(amount, { promise }) {
const formattedAmount = SAI(amount).toFixed('wei');
const address = this._manager.get('web3').currentAddress();
const migrationContract = this._manager
Expand All @@ -25,13 +27,11 @@ export default class SaiToDai {
console.log(formattedAmount * 1.5);
console.log(migrationContract.address);
if (allowance.toNumber() < amount) {
await this._sai.approve(migrationContract.address, (formattedAmount * 1.5));
console.log(await this._sai.allowance(
address,
migrationContract.address
));
await this._sai.approve(migrationContract.address, formattedAmount, {
promise
});
}

return migrationContract.swapSaiToDai(formattedAmount);
return migrationContract.swapSaiToDai(formattedAmount, { promise });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ export default class SingleToMultiCdp {
return this;
}

get newCdpIds() {
return this._newCdpIds;
}

async check() {
const address = this._manager.get('accounts').currentAddress();
const proxyAddress = await this._manager.get('proxy').currentProxy();
Expand All @@ -42,7 +38,9 @@ export default class SingleToMultiCdp {
);

await this._requireAllowance(cupId);
return migrationProxy[method](...args, { dsProxy: true, promise });
return migrationProxy[method](...args, { dsProxy: true, promise }).then(
txo => this._getNewCdpId(txo)
);
}

async _requireAllowance(cupId) {
Expand All @@ -58,7 +56,7 @@ export default class SingleToMultiCdp {
if (allowance.lt(fee)) await mkr.approve(proxyAddress, fee.times(1.5));
}

async getNewCdpId(txo) {
_getNewCdpId(txo) {
const logs = txo.receipt.logs;
const manager = this._manager
.get('smartContract')
Expand Down
Loading

0 comments on commit 490eeb2

Please sign in to comment.